diff --git a/FEATURES/109790 b/FEATURES/109790 new file mode 100644 index 0000000000000000000000000000000000000000..3f967268726de15958d791eb825604432a961aa1 --- /dev/null +++ b/FEATURES/109790 @@ -0,0 +1,10 @@ + '109790' => + ['Label' => $this->_('Retrait des documents sur rendez-vous (Drive)'), + 'Desc' => $this->_('Les abonnés peuvent prendre rendez-vous pour retirer leurs documents à des horaires pré-définies'), + 'Image' => '', + 'Video' => '', + 'Category' => $this->_('Circulation'), + 'Right' => function($feature_description, $user) {return true;}, + 'Wiki' => 'http://wiki.bokeh-library-portal.org/index.php?title=Cat%C3%A9gorie:Drive', + 'Test' => '', + 'Date' => '2020-05-04'], \ No newline at end of file diff --git a/VERSIONS_WIP/109790 b/VERSIONS_WIP/109790 new file mode 100644 index 0000000000000000000000000000000000000000..4c0ae20eb2cca2e9bd2d30338e564d35e6d17f84 --- /dev/null +++ b/VERSIONS_WIP/109790 @@ -0,0 +1 @@ + - ticket #109790 : Retrait des documents sur rendez-vous (Drive) \ No newline at end of file diff --git a/application/modules/admin/controllers/DriveCheckoutController.php b/application/modules/admin/controllers/DriveCheckoutController.php new file mode 100644 index 0000000000000000000000000000000000000000..161a15f2df4033b1ee105d30ebcdbff401cc1156 --- /dev/null +++ b/application/modules/admin/controllers/DriveCheckoutController.php @@ -0,0 +1,172 @@ +<?php +/** + * Copyright (c) 2012-2020, 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 Admin_DriveCheckoutController extends ZendAfi_Controller_Action { + use Trait_TimeSource; + + public function indexAction() { + $this->view->titre = $this->view->_('Drive : rendez-vous'); + $this->view->libraries = Class_Bib::findAllBy(['enable_drive' => true, + 'order' => 'libelle']); + $this->view->disabled_libraries = Class_Bib::findAllBy(['enable_drive' => false, + 'order' => 'libelle']); + } + + + public function listAction() { + $this->view->library = Class_Bib::find($this->_getParam('id_bib')); + $this->view->date = $this->_getParam('date', $this->getCurrentDate()); + $this->view->titre = $this->view->_('Drive : rendez-vous : %s, %s', + $this->view->library->getLibelle(), + strftime('%d %B %Y', strtotime($this->view->date))); + $this->view->checkouts = Class_DriveCheckout::findAllBy(['role' => 'library', + 'model' => $this->view->library, + 'left(start_at,10)' => $this->view->date, + 'order' => 'start_at']); + $this->view->table_description = new Class_TableDescription_DriveCheckout_ListWithActions('checkouts'); + } + + + public function deleteAction() { + $checkout = Class_DriveCheckout::find($this->_getParam('id')); + $checkout->delete(); + + $this->_helper->notify($this->_('Rendez-vous pour %s supprimé', + $checkout->getNomComplet())); + $this->_redirect(sprintf('/admin/drive-checkout/list/id_bib/%s/date/%s', + $checkout->getLibraryId(), + substr($checkout->getStartAt(), 0, 10))); + } + + + public function icalAction() { + if (!$checkout = Class_DriveCheckout::find($this->_getParam('id'))) { + $this->getHelper('ViewRenderer')->setNoRender(); + $this->_helper->notify($this->_('Impossible d\'importer un retrait inconnu.')); + $this->_redirectToIndex(); + return; + } + + $content = (new Class_ICal_DriveCheckout($checkout)) + ->renderCalendar(Class_Profil::getCurrentProfil()); + + $this->_helper->ical('calendar.ics', $content); + } + + + public function listHoldsAction() { + $this->view->checkout = Class_DriveCheckout::find($this->_getParam('id')); + $this->view->titre = sprintf('%s, %s, %s, %s', + $this->view->checkout->getNomComplet(), + $this->view->checkout->getIdAbon(), + strftime('%d %B %H:%M', + strtotime($this->view->checkout->getStartAt())), + $this->view->checkout->getLibraryLabel()); + } + + + public function listAllHoldsAction() { + $this->view->user = Class_Users::find($this->_getParam('id_user')); + foreach($this->view->user->getReservations() as $hold) + $hold->getExemplaireOPAC($this->view->user);//cache initialization + + $this->view->titre = $this->_('Réservations pour %s, %s', + $this->view->user->getNomComplet(), + $this->view->user->getIdabon()); + } + + + public function listCsvAction() { + $this->listAction(); + + $filename = implode(' ', [$this->view->date, + $this->view->library->getLibelle(), + $this->view->_('rendez-vous')]) . '.csv'; + $this + ->_helper + ->csv($filename, + $this->view->renderCsv(new Class_TableDescription_DriveCheckout_List('checkouts'), + $this->view->checkouts)); + } + + + public function itemsCsvAction() { + $library = Class_Bib::find($this->_getParam('id_bib')); + $date = $this->_getParam('date', $this->getCurrentDate()); + + $checkouts = Class_DriveCheckout::findAllBy(['role' => 'library', + 'model' => $library, + 'left(start_at,10)' => $date, + 'order' => 'start_at']); + + $holds = (new Storm_Collection($checkouts)) + ->injectInto(new Storm_Collection(), + function($holds, $checkout) + { + $holds->addAll($checkout->getLibraryHolds()); + return $holds; + }); + + + $filename = implode(' ', [$date, + $library->getLibelle(), + $this->view->_('documents')]) . '.csv'; + $this + ->_helper + ->csv($filename, + $this->view->renderCsv(new Class_TableDescription_DriveCheckout_HoldsWithCheckouts('holds'), + $holds->getArrayCopy())); + } + + + public function planAction() { + $user = Class_Users::find($this->_getParam('id_user')); + + $plan = new Class_DriveCheckout_Plan($this->_request->getParams(), $user); + $plan->doNotFilterLibraries(); + + if (!$plan->isValid()) { + $this->getHelper('ViewRenderer')->setNoRender(); + $this->_helper->notify($plan->getLastMessage()); + $this->_redirect($this->view->url($plan->fallbackUrl())); + return; + } + + if ($this->_request->isPost() + && ($checkout = $plan->persist())) { + $this->getHelper('ViewRenderer')->setNoRender(); + $this->_helper->notify($this->view->_('Retrait planifié pour %s, le %s, %s', + $checkout->getNomComplet(), + strftime('%d %B %H:%M', strtotime($checkout->getStartAt())), + $checkout->getLibraryLabel())); + $this->_redirect(sprintf('/admin/drive-checkout/list/id_bib/%s/date/%s', + $checkout->getLibraryId(), + substr($checkout->getStartAt(), 0, 10))); + return; + } + + $this->view->titre = $this->view->_('Planifier un retrait pour %s', + $user->getNomComplet()); + $this->view->plan = $plan; + $this->view->user = $user; + } +} diff --git a/application/modules/admin/controllers/OuverturesController.php b/application/modules/admin/controllers/OuverturesController.php index 6bc5361db7e1e5048570502c36ef3b878ff3f643..cb3acc02480e74ffc156e5861de70aad4fd40b76 100644 --- a/application/modules/admin/controllers/OuverturesController.php +++ b/application/modules/admin/controllers/OuverturesController.php @@ -20,7 +20,7 @@ */ class Admin_OuverturesController extends ZendAfi_Controller_Action { - protected $_library, $_is_multimedia = false; + protected $_library, $_is_used_for = null; public function getPlugins() { return ['ZendAfi_Controller_Plugin_ResourceDefinition_Opening', @@ -29,13 +29,15 @@ class Admin_OuverturesController extends ZendAfi_Controller_Action { public function init() { + $this->_used_for = (int)$this->_getParam('used_for', null); + if ((!$this->_library = $this->_getLibrary()) - || ($this->_getParam('multimedia') && !Class_AdminVar::isMultimediaEnabled())) { + || (($this->_used_for === Class_Ouverture::USED_FOR_MULTIMEDIA) + && !Class_AdminVar::isMultimediaEnabled())) { $this->_redirect('/admin/bib'); return; } - $this->_is_multimedia = $this->_isMultimedia(); parent::init(); } @@ -45,11 +47,6 @@ class Admin_OuverturesController extends ZendAfi_Controller_Action { } - protected function _isMultimedia() { - return null !== $this->_getParam('multimedia'); - } - - public function acceptVisitor($visitor) { parent::acceptVisitor($visitor); $visitor @@ -57,10 +54,10 @@ class Admin_OuverturesController extends ZendAfi_Controller_Action { { return $this->_getLibrary(); }) - ->visitIsMultimedia(function() - { - return $this->_isMultimedia(); - }); + ->visitUsedFor(function() + { + return (int)$this->_getParam('used_for'); + }); return $this; } @@ -76,7 +73,7 @@ class Admin_OuverturesController extends ZendAfi_Controller_Action { if ($this->_response->isRedirect()) return; - $this->view->multimedia = $this->_is_multimedia; + $this->view->used_for = $this->_used_for; $this->view->model_name = 'library'; $this->view->library = $this->_library; diff --git a/application/modules/admin/views/scripts/drive-checkout/index.phtml b/application/modules/admin/views/scripts/drive-checkout/index.phtml new file mode 100644 index 0000000000000000000000000000000000000000..35b777151695baf1f6548bad96e8be4dff2e5f3c --- /dev/null +++ b/application/modules/admin/views/scripts/drive-checkout/index.phtml @@ -0,0 +1,20 @@ +<?php +$render_libraries = function($libraries) +{ + return $this->tagUlLi( + array_map( + function($library) + { + return $this->tagAnchor(['action' => 'list', + 'id_bib' => $library->getId()], + $library->getLibelle()); + }, + $libraries)); +}; + + +echo $render_libraries($this->libraries); +echo $this->tag('h3', $this->_('Drive désactivé')); +echo $render_libraries($this->disabled_libraries); + +?> diff --git a/application/modules/admin/views/scripts/drive-checkout/list-all-holds.phtml b/application/modules/admin/views/scripts/drive-checkout/list-all-holds.phtml new file mode 100644 index 0000000000000000000000000000000000000000..536a343170793a1c14bfa933651bee9ca6bae597 --- /dev/null +++ b/application/modules/admin/views/scripts/drive-checkout/list-all-holds.phtml @@ -0,0 +1,8 @@ +<?php +echo $this->renderTable((new Class_TableDescription('holds')) + ->addColumn($this->_('Bibliothèque'), function($hold) { return $hold->getBibliotheque(); }) + ->addColumn($this->_('Code-barres'), function($hold) { return $hold->getCodeBarre(); }) + ->addColumn($this->_('Etat'), function($hold) { return $hold->getEtat(); }) + ->addColumn($this->_('Titre'), function($hold) { return $hold->getTitre(); }), + + $this->user->getReservations()); diff --git a/application/modules/admin/views/scripts/drive-checkout/list-holds.phtml b/application/modules/admin/views/scripts/drive-checkout/list-holds.phtml new file mode 100644 index 0000000000000000000000000000000000000000..661ce491d617982e1c29b3c5ce79263fa8789415 --- /dev/null +++ b/application/modules/admin/views/scripts/drive-checkout/list-holds.phtml @@ -0,0 +1,14 @@ +<?php +echo $this->renderTable(new Class_TableDescription_DriveCheckout_Holds('holds'), + $this->checkout->getLibraryHolds()); + +echo $this->tagAnchor($this->url(['module' => 'admin', + 'controller' => 'drive-checkout', + 'action' => 'list-all-holds', + 'id_user' => $this->checkout->getUser()->getId()], + null, + true), + $this->_('Voir toutes les réservations de %s', + $this->checkout->getNomComplet()), + + ['data-popup' => 'true']); diff --git a/application/modules/admin/views/scripts/drive-checkout/list.phtml b/application/modules/admin/views/scripts/drive-checkout/list.phtml new file mode 100644 index 0000000000000000000000000000000000000000..89ac4e7de436abe227d087420b9cbb61c0ab965a --- /dev/null +++ b/application/modules/admin/views/scripts/drive-checkout/list.phtml @@ -0,0 +1,27 @@ +<?php +$skin = Class_Admin_Skin::current(); +echo $this->Button((new Class_Entity()) + ->setUrl($this->url(['action' => 'items-csv'])) + ->setText($this->_('Exporter les documents (.csv)')) + ->setImage($this->tagImg($skin->getIconUrl('actions', 'test'), + ['style' => 'filter: invert();'])) + ->setAttribs(['style' => 'float:right'])); + + +echo $this->Button((new Class_Entity()) + ->setUrl($this->url(['action' => 'list-csv'])) + ->setText($this->_('Exporter les rendez-vous (.csv)')) + ->setImage($this->tagImg($skin->getIconUrl('actions', 'test'), + ['style' => 'filter: invert();'])) + ->setAttribs(['style' => 'float:right'])); + + +echo $this->tag('label', $this->_('Date'), ['for' => 'date', 'style' => 'margin-right: 5px']); + +$date_url = $this->url(['date' => null]) . '/date/'; +echo $this->formDate('date', + $this->date, + ['onchange' => 'window.location=\'' . $date_url . '\' + this.value']); + + +echo $this->renderTable($this->table_description, $this->checkouts); diff --git a/application/modules/admin/views/scripts/drive-checkout/plan.phtml b/application/modules/admin/views/scripts/drive-checkout/plan.phtml new file mode 100644 index 0000000000000000000000000000000000000000..34d26a02c581375f1fd4869dd1ea63aba7f8674d --- /dev/null +++ b/application/modules/admin/views/scripts/drive-checkout/plan.phtml @@ -0,0 +1,14 @@ +<?php + +echo $this->tagAnchor($this->url(['module' => 'admin', + 'controller' => 'drive-checkout', + 'action' => 'list-all-holds', + 'id_user' => $this->user->getId()], + null, + true), + $this->_('Voir toutes les réservations de %s', + $this->user->getNomComplet()), + + ['data-popup' => 'true']); + +echo $this->driveCheckoutPlan($this->plan); diff --git a/application/modules/admin/views/scripts/ouvertures/list.phtml b/application/modules/admin/views/scripts/ouvertures/list.phtml index 7b65e0cc396e760943b7ff10600cfc23cbff655b..33001fad8bca608fdfd613fba73bb219dec184b2 100644 --- a/application/modules/admin/views/scripts/ouvertures/list.phtml +++ b/application/modules/admin/views/scripts/ouvertures/list.phtml @@ -1,10 +1,17 @@ <?php -echo $this->renderForm($this->form); +if ($this->used_for === Class_Ouverture::USED_FOR_LIBRARY) + echo $this->renderForm($this->form); + +$button_text = $this->_('Ajouter une plage d\'ouverture'); +if ($this->used_for === Class_Ouverture::USED_FOR_MULTIMEDIA) + $button_text = $this->_('Ajouter une plage horaire de réservation multimédia'); + +if ($this->used_for === Class_Ouverture::USED_FOR_DRIVE) + $button_text = $this->_('Ajouter une plage d\'ouverture du drive'); + echo $this->button_New( - (new Class_Entity())->setText($this->multimedia - ? $this->_('Ajouter une plage horaire de réservation multimedia') - : $this->_('Ajouter une plage d\'ouverture')) + (new Class_Entity())->setText( $button_text) ->setUrl($this->url(['action' => 'add', 'id' => null]))); echo $this->button_Back( @@ -13,4 +20,4 @@ echo $this->button_Back( 'controller' => 'bib', 'action' => 'index'], null, true))); -echo $this->libraryOpeningsAdmin($this->library, $this->multimedia); +echo $this->libraryOpeningsAdmin($this->library, $this->used_for); diff --git a/application/modules/opac/controllers/DriveCheckoutController.php b/application/modules/opac/controllers/DriveCheckoutController.php new file mode 100644 index 0000000000000000000000000000000000000000..334fe48d4f3b4e67a43d5b0eb4aacd4bfea8b41a --- /dev/null +++ b/application/modules/opac/controllers/DriveCheckoutController.php @@ -0,0 +1,104 @@ +<?php +/** + * Copyright (c) 2012-2020, 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 DriveCheckoutController extends ZendAfi_Controller_Action { + protected $_user = null; + + public function init() { + parent::init(); + $this->_user = $this->view->user = Class_Users::getIdentity(); + } + + + public function planAction() { + if (!$this->_isActive()) + return; + + $plan = new Class_DriveCheckout_Plan($this->_request->getParams(), $this->_user); + + if (!$plan->isValid()) { + $this->getHelper('ViewRenderer')->setNoRender(); + $this->_helper->notify($plan->getLastMessage()); + $this->_redirect($this->view->url($plan->fallbackUrl())); + return; + } + + if ($this->_request->isPost() + && ($checkout = $plan->persist())) { + $this->getHelper('ViewRenderer')->setNoRender(); + $this->_helper->notify($this->_('Le retrait de vos documents de %s est planifié pour %s.', + $checkout->getLibraryLabel(), + $checkout->getDateTimeLabel())); + $this->_redirect($this->view->url($plan->resetUrl())); + return; + } + + $this->view->plan = $plan; + } + + + public function deleteAction() { + if (!$this->_isActive()) + return; + + $this->getHelper('ViewRenderer')->setNoRender(); + $message = $this->_('Impossible de supprimer un retrait inconnu.'); + if ($checkout = Class_DriveCheckout::findFor($this->_getParam('id'), $this->_user)) { + $checkout->delete(); + $message = $this->_('Le retrait de vos documents de %s planifié pour %s a été supprimé.', + $checkout->getLibraryLabel(), + $checkout->getDateTimeLabel()); + } + + $this->_helper->notify($message); + $this->_redirect('/opac/drive-checkout/plan'); + } + + + public function icalAction() { + if (!$this->_isActive()) + return; + + if (!$checkout = Class_DriveCheckout::findFor($this->_getParam('id'), $this->_user)) { + $this->getHelper('ViewRenderer')->setNoRender(); + $this->_helper->notify($this->_('Impossible d\'importer un retrait inconnu.')); + $this->_redirect('/opac/drive-checkout/plan'); + return; + } + + $content = (new Class_ICal_DriveCheckout($checkout)) + ->renderCalendar(Class_Profil::getCurrentProfil()); + + $this->_helper->ical('calendar.ics', $content); + } + + + protected function _isActive() { + if (Class_AdminVar::isDriveCheckoutEnabled()) + return true; + + $this->getHelper('ViewRenderer')->setNoRender(); + $this->_helper->notify($this->_('La planification du retrait des réservations est désactivée.')); + $this->_redirect($this->view->url([], null, true)); + return false; + } +} diff --git a/application/modules/opac/views/scripts/drive-checkout/plan.phtml b/application/modules/opac/views/scripts/drive-checkout/plan.phtml new file mode 100644 index 0000000000000000000000000000000000000000..aafd6c5ca8d346f2ae4222896d110c997985e3f5 --- /dev/null +++ b/application/modules/opac/views/scripts/drive-checkout/plan.phtml @@ -0,0 +1,2 @@ +<?php +echo $this->driveCheckoutPlan($this->plan); diff --git a/cosmogramme/sql/patch/patch_388.php b/cosmogramme/sql/patch/patch_388.php new file mode 100644 index 0000000000000000000000000000000000000000..7550f20493be3e8e1610640d18f56c9170c3d488 --- /dev/null +++ b/cosmogramme/sql/patch/patch_388.php @@ -0,0 +1,16 @@ +<?php +$adapter = Zend_Db_Table::getDefaultAdapter(); + +try { + $adapter->query('create table if not exists `drive_checkout` (' + . 'id int(11) unsigned not null auto_increment,' + . 'user_id int(11) not null,' + . 'library_id int(11) not null,' + . 'start_at datetime not null,' + . 'primary key (id),' + . 'key user_id (user_id),' + . 'key library_id (library_id),' + . 'key start_at (start_at)' + . ') engine=MyISAM default charset=utf8'); +} catch (Exception $e) { +} \ No newline at end of file diff --git a/cosmogramme/sql/patch/patch_389.php b/cosmogramme/sql/patch/patch_389.php new file mode 100644 index 0000000000000000000000000000000000000000..0692ddc52122fa2ddca96abbbe42b8cdf09e9b4f --- /dev/null +++ b/cosmogramme/sql/patch/patch_389.php @@ -0,0 +1,23 @@ +<?php +$adapter = Zend_Db_Table::getDefaultAdapter(); + +try { + $adapter->query('alter table ouvertures change multimedia used_for int(11) default 0'); +} +catch (Exception $e){} + +try { + $adapter->query('alter table ouvertures add index used_for (used_for)'); +} +catch (Exception $e){} + +try { + $adapter->query('alter table bib_c_site add enable_drive tinyint(1) default 0'); +} +catch (Exception $e){} + +try { + $adapter->query('alter table ouvertures add column max_per_period_matin int(11) default null, +add column max_per_period_apres_midi int(11) default null;'); +} +catch (Exception $e){} \ No newline at end of file diff --git a/library/Class/AdminVar.php b/library/Class/AdminVar.php index 14e33346b9bc2d2a3088ee3d808d62e3fbe063b3..c8d2f8c801157bf0abfa9cd19bcbd3f33a7b130a 100644 --- a/library/Class/AdminVar.php +++ b/library/Class/AdminVar.php @@ -137,6 +137,7 @@ class Class_AdminVarLoader extends Storm_Model_Loader { 'usergroup-agenda' => $this->_getRendezVousVars(), 'templating' => $this->_getTemplatingVars(), 'identity-providers' => $this->_getIdentityProvidersVars(), + 'drive-checkout' => $this->_getDriveCheckoutVars() ]; } @@ -550,6 +551,17 @@ class Class_AdminVarLoader extends Storm_Model_Loader { } + protected function _getDriveCheckoutVars() { + return + ['ENABLE_DRIVE_CHECKOUT' => Class_AdminVar_Meta::newOnOff($this->_('Activer la prise de rendez-vous pour récupérer des réservations (Drive)')), + 'DRIVE_TEMPLATE_NEW_RDV_SUBJECT' => Class_AdminVar_Meta::newDefault($this->_('Sujet des courriels de confirmation de création de rendez-vous drive.'), + ['value'=> 'Confirmation de rendez-vous à {library.libelle}']), + 'DRIVE_TEMPLATE_NEW_RDV_CONTENT' => Class_AdminVar_Meta::newEditor($this->_('Modèle utilisé pour les courriels de confirmation de création de rendez-vous drive.'), + ['value'=> '<p>Bonjour {user.nom_complet},</p> <p>nous vous confirmons votre rendez-vous <strong> à {library.libelle} {rendez_vous.date_time_label}</strong>.</p>']), + ]; + } + + public function allVarsValues() { if (null !== static::$_all_vars_values) return static::$_all_vars_values; @@ -1131,6 +1143,11 @@ class Class_AdminVarLoader extends Storm_Model_Loader { public function isIdentityProvidersEnabled() { return Class_AdminVar::isModuleEnabled('ENABLE_IDENTITY_PROVIDERS'); } + + + public function isDriveCheckoutEnabled() { + return Class_AdminVar::isModuleEnabled('ENABLE_DRIVE_CHECKOUT'); + } } diff --git a/library/Class/Bib.php b/library/Class/Bib.php index 57a69a360f17fccbd841fdfd444510741b8230bd..f76e32fd965347a78e0bde0e592d7029c8b18911 100644 --- a/library/Class/Bib.php +++ b/library/Class/Bib.php @@ -273,7 +273,7 @@ class Class_Bib extends Storm_Model_Abstract { 'ouvertures_multimedia' => ['model' => 'Class_Ouverture', 'role' => 'bib', - 'scope' => ['multimedia' => 1], + 'scope' => ['used_for' => Class_Ouverture::USED_FOR_MULTIMEDIA], 'order' => ['jour', 'validity_end desc', 'validity_start desc', @@ -917,19 +917,62 @@ class Class_Bib extends Storm_Model_Abstract { } - public function getOuvertureOnDate($time) { - if($this->hasHoraire()) + public function isDriveEnabled() { + return !$this->isAttributeEmpty('enable_drive') && 1 == (int)$this->getEnableDrive(); + } + + + public function hasDriveOuvertures() { + return $this->isDriveEnabled() && Class_Ouverture::hasDriveFor($this); + } + + + /** + * @param $date DateTime + * @return Class_Ouverture or null + */ + public function getDriveOuvertureOnDate($date) { + return $this->isDriveEnabled() && $date + ? $this->getOuvertureOnDate($date->getTimeStamp(), Class_Ouverture::USED_FOR_DRIVE) + : null; + } + + + public function hasOuverturesFor($used_for) { + return null !== Class_Ouverture::findFirstBy(['id_site' => $this->getId(), + 'used_for' => $used_for]); + } + + + public function getOuvertureOnDate($time, $used_for = Class_Ouverture::USED_FOR_LIBRARY) { + if ($this->hasHoraire()) return null; if ($ouverture = Class_Ouverture::findFirstBy(['jour' => date('Y-m-d', $time), - 'id_site' => $this->getId()])) + 'id_site' => $this->getId(), + 'used_for' => $used_for])) return $ouverture; if ($this->isClosedOnHolidays() && Class_Date_Holiday::isHoliday($time)) return null; $day_of_week = Class_Date::dayOfWeek($time); - $openings = new Storm_Model_Collection($this->getOuvertures()); + $openings = new Storm_Model_Collection( + Class_Ouverture::findAllBy + ( + [ + 'id_site' => $this->getId(), + 'used_for'=> $used_for, + 'order' => [ + 'jour', + 'validity_end desc', + 'validity_start desc', + 'jour_semaine', + 'debut_matin' + ], + ] + ) + ); $blessed = $openings->select('hasValidityRange') ->detect( diff --git a/library/Class/Bib/DriveOpening.php b/library/Class/Bib/DriveOpening.php new file mode 100644 index 0000000000000000000000000000000000000000..fd4953e4ec77b04b793099cfc27b45c9ba61be81 --- /dev/null +++ b/library/Class/Bib/DriveOpening.php @@ -0,0 +1,85 @@ +<?php +/** + * Copyright (c) 2012-2020, 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_Bib_DriveOpening { + protected $_morning, $_afternoon; + + /** + * @param $start DateTime + * @param $end DateTime + * @param $library Class_Bib + * + * @return Storm_Collection of DateTime on which library has an available drive opening + */ + public static function allBetweenFor($start, $end, $library) { + $dates = new Storm_Collection(); + while ($start <= $end) { + (new static($library, $start))->appendIfOpenTo($dates); + $start->modify('+1 day'); + } + + return $dates; + } + + + /** + * @param $library Class_Bib + * @param $date DateTime + * + * @return Storm_Collection of DateTime + */ + public static function timesFor($library, $date) { + return (new static($library, $date))->times(); + } + + + /** + * @param $library Class_Bib + * @param $date DateTime + */ + public function __construct($library, $date) { + $opening = $library->getDriveOuvertureOnDate($date); + $this->_morning = new Class_Bib_DriveOpening_MorningPeriod($library, $date, $opening); + $this->_afternoon = new Class_Bib_DriveOpening_AfternoonPeriod($library, $date, $opening); + } + + + public function appendIfOpenTo($collection) { + $period = (new Storm_Collection([$this->_morning, $this->_afternoon])) + ->detect(function($period) + { + return $period->isAvailable(); + }); + + if ($period) + $collection->append(new DateTime($period->format('c'))); + } + + + public function times() { + $times = new Storm_Collection; + foreach([$this->_morning, $this->_afternoon] as $period) + $period->injectTimesInto($times); + + return $times; + } +} diff --git a/library/Class/Bib/DriveOpening/AfternoonPeriod.php b/library/Class/Bib/DriveOpening/AfternoonPeriod.php new file mode 100644 index 0000000000000000000000000000000000000000..40a10c90fff4a2df8059ecb88da30ea100f574d8 --- /dev/null +++ b/library/Class/Bib/DriveOpening/AfternoonPeriod.php @@ -0,0 +1,29 @@ +<?php +/** + * Copyright (c) 2012-2020, 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_Bib_DriveOpening_AfternoonPeriod extends Class_Bib_DriveOpening_Period { + protected + $_closed_method = 'isClosedPm', + $_start_method = 'getDebutApresMidi', + $_end_method = 'getFinApresMidi', + $_quota_method = 'getMaxPerPeriodApresMidi'; +} \ No newline at end of file diff --git a/library/Class/Bib/DriveOpening/MorningPeriod.php b/library/Class/Bib/DriveOpening/MorningPeriod.php new file mode 100644 index 0000000000000000000000000000000000000000..31d0b095e990994786311f93a7671d87406d2e60 --- /dev/null +++ b/library/Class/Bib/DriveOpening/MorningPeriod.php @@ -0,0 +1,29 @@ +<?php +/** + * Copyright (c) 2012-2020, 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_Bib_DriveOpening_MorningPeriod extends Class_Bib_DriveOpening_Period { + protected + $_closed_method = 'isClosedAm', + $_start_method = 'getDebutMatin', + $_end_method = 'getFinMatin', + $_quota_method = 'getMaxPerPeriodMatin'; +} diff --git a/library/Class/Bib/DriveOpening/Period.php b/library/Class/Bib/DriveOpening/Period.php new file mode 100644 index 0000000000000000000000000000000000000000..1f8a6775fb6d76bfe919aba5a12d213a9e50b647 --- /dev/null +++ b/library/Class/Bib/DriveOpening/Period.php @@ -0,0 +1,129 @@ +<?php +/** + * Copyright (c) 2012-2020, 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_Bib_DriveOpening_Period { + const CHECKOUT_TIME_SLOT = '30'; + + + protected + $_closed_method, $_start_method, $_end_method, $_quota_method, + $_opening, $_date, $_library, + $_times; + + public function __construct($library, $date, $opening) { + $this->_opening = $opening; + $this->_library = $library; + $this->_date = $date; + } + + + public function isAvailable() { + return $this->_isClosed() + ? false + : $this->_quotaByPeriod() > Class_DriveCheckout::countBetweenForLibrary($this->startDateTime(), + $this->endDateTime(), + $this->_library); + } + + + public function injectTimesInto($collection) { + $collection->addAll($this->_times()->select([$this, 'isTimeAvailable'])); + } + + + public function isTimeAvailable($time) { + return ($quota = $this->quota()) + ? $quota > Class_DriveCheckout::countAtTimeForLibrary($time, $this->_library) + : false; + } + + + public function format($format) { + return $this->_date->format($format); + } + + + public function isClosed() { + return call_user_func([$this->_opening, $this->_closed_method]); + } + + + public function start() { + return call_user_func([$this->_opening, $this->_start_method]); + } + + + public function startDateTime() { + return $this->_asDateTime($this->start()); + } + + + public function end() { + return call_user_func([$this->_opening, $this->_end_method]); + } + + + public function endDateTime() { + return $this->_asDateTime($this->end()); + } + + + public function quota() { + return call_user_func([$this->_opening, $this->_quota_method]); + } + + + protected function _isClosed() { + return !$this->_opening || $this->_opening->isClosed() || $this->isClosed(); + } + + + protected function _asDateTime($time) { + return new DateTime($this->_date->format('Y-m-d') . ' ' . $time . ':00'); + } + + + protected function _times() { + if ($this->_isClosed()) + return new Storm_Collection(); + + if (null !== $this->_times) + return $this->_times; + + $day = $this->format('Y-m-d'); + $period = new DatePeriod(new DateTime($day . ' ' . $this->start()), + new DateInterval('PT' . static::CHECKOUT_TIME_SLOT . 'M'), + new DateTime($day . ' ' . $this->end())); + + $times = new Storm_Collection(); + foreach($period as $step) + $times->append($step); + + return $this->_times = $times; + } + + + protected function _quotaByPeriod() { + return $this->_times()->isEmpty() || (!$quota = $this->quota()) + ? 0 + : $quota * $this->_times()->count(); + } +} \ No newline at end of file diff --git a/library/Class/CodifAnnexe.php b/library/Class/CodifAnnexe.php index 61aed280e3ccc0323d54d14deeacfc6c1c103e07..8a4241e40f37e59598b489a8c50c66e4315896b9 100644 --- a/library/Class/CodifAnnexe.php +++ b/library/Class/CodifAnnexe.php @@ -99,6 +99,13 @@ class Class_CodifAnnexe extends Storm_Model_Abstract { } + public function getLibraryId() { + return $this->hasBib() + ? $this->getBib()->getId() + : null; + } + + public function isVisible() { return (1 != $this->getInvisible()); } diff --git a/library/Class/DriveCheckout.php b/library/Class/DriveCheckout.php new file mode 100644 index 0000000000000000000000000000000000000000..927abc4414de305d7f3116ea8fcd02a3fff57eba --- /dev/null +++ b/library/Class/DriveCheckout.php @@ -0,0 +1,217 @@ +<?php +/** + * Copyright (c) 2012-2020, 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_DriveCheckoutLoader extends Storm_Model_Loader { + public function findFutureFor($library, $user) { + if (!$library || !$user) + return; + + return Class_DriveCheckout::findFirstBy(['library_id' => $library->getId(), + 'user_id' => $user->getId(), + 'where' => 'date(start_at) >= curdate()', + 'order' => 'start_at']); + } + + + public function findFor($id, $user) { + return $user + ? Class_DriveCheckout::findFirstBy(['id' => (int)$id, + 'user_id' => $user->getId()]) + : null; + } + + + public function countBetweenForLibrary($start, $end, $library) { + return $library && $start && $end + ? Class_DriveCheckout::countBy(['library_id' => $library->getId(), + 'where' => sprintf('start_at >= "%s" and start_at < "%s"', + $this->_dateAsSql($start), + $this->_dateAsSql($end))]) + : 0; + } + + + public function countAtTimeForLibrary($time, $library) { + return $library && $time + ? Class_DriveCheckout::countBy(['library_id' => $library->getId(), + 'start_at' => $this->_dateAsSql($time)]) + : 0; + } + + + protected function _dateAsSql($date) { + return $date->format('Y-m-d H:i:s'); + } +} + + + + +class Class_DriveCheckout extends Storm_Model_Abstract { + use Trait_Translator; + + protected + $_loader_class = 'Class_DriveCheckoutLoader', + $_table_name = 'drive_checkout', + $_belongs_to = ['user' => ['model' => 'Class_Users'], + 'library' => ['model' => 'Class_Bib']], + + $_default_attribute_values = ['start_at' => null, + 'user_id' => null, + 'library_id' => null]; + + + public function getLibraryLabel() { + return ($library = $this->getLibrary()) + ? $library->getLibelle() + : ''; + } + + + public function getDateTimeLabel() { + return strftime($this->_('le %A %d %B à %Hh%M'), + strtotime($this->getStartAt())); + } + + + public function getDateTimeStartAt() { + return DateTime::createFromFormat('Y-m-d H:i:s', $this->getStartAt()); + } + + + public function getIdAbon() { + return ($user = $this->getUser()) + ? $user->getIdabon() + : ''; + } + + + public function getNomComplet() { + return ($user = $this->getUser()) + ? $user->getNomComplet() + : ''; + } + + + public function getHolds() { + if (!$user = $this->getUser()) + return []; + + + return Class_DriveCheckout_Holds::newFor($user) + ->selectWaitingToBePulledIn($this->getLibrary()) + ->collect(function($hold) + { + return new Class_DriveCheckout_Hold($this, $hold); + }); + } + + + public function getLibraryHolds() { + if (!$user = $this->getUser()) + return []; + + + return Class_DriveCheckout_Holds::newFor($user) + ->selectWithLibraryInfo() + ->selectLibrary($this->getLibrary()) + ->collect(function($hold) + { + return new Class_DriveCheckout_Hold($this, $hold); + }); + } + + + public function getMailRecipient() { + return ($user = $this->getUser()) + ? $user->getMail() + : ''; + } + + + public function notify() { + return (new Class_DriveCheckout_Notification($this))->send(); + } +} + + + + +class Class_DriveCheckout_Hold { + protected + $_checkout, + $_hold, + $_item; + + public function __construct($checkout, $hold) { + $this->_checkout = $checkout; + $this->_hold = $hold; + $this->_item = $hold->getExemplaireOPAC($checkout->getUser()); + } + + + public function getStartAt() { + return $this->_checkout->getStartAt(); + } + + + public function getIdAbon() { + return $this->_checkout->getIdAbon(); + } + + + public function getNomComplet() { + return $this->_checkout->getNomComplet(); + } + + + public function getCote() { + return $this->_item + ? $this->_item->getCote() + : $this->_hold->getCote(); + } + + + public function getStatus() { + return $this->_hold->getEtat(); + } + + + public function getCodeBarres() { + return $this->_item + ? $this->_item->getCodeBarres() + : $this->_hold->getCodeBarre(); + } + + + public function getTitle() { + return $this->_item + ? $this->_item->getTitrePrincipal() + : $this->_hold->getTitre(); + } + + + public function getRecord() { + return $this->_item + ? $this->_item->getNotice() + : null; + } +} \ No newline at end of file diff --git a/library/Class/DriveCheckout/Holds.php b/library/Class/DriveCheckout/Holds.php new file mode 100644 index 0000000000000000000000000000000000000000..3566117b996e2c0fa86546875c04cec7fb02db11 --- /dev/null +++ b/library/Class/DriveCheckout/Holds.php @@ -0,0 +1,98 @@ +<?php +/** + * Copyright (c) 2012-2020, 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_DriveCheckout_Holds extends Storm_Collection { + public static function newFor($user) { + return new static($user ? $user->getReservations() : []); + } + + + public function selectWaitingToBePulledIn($library) { + return $this + ->selectWaitingToBePulledInAnyLibrary() + ->selectLibrary($library); + } + + + public function selectWaitingToBePulledInAnyLibrary() { + return $this + ->selectWaitingToBePulled() + ->selectWithLibraryInfo(); + } + + + public function selectWaitingToBePulled() { + return $this->select(function($each) + { + return $each->isWaitingToBePulled(); + }); + } + + + public function countWaitingToBePulled() { + return $this->selectWaitingToBePulled() + ->count(); + } + + + public function selectLibrary($library) { + return $library + ? $this->select(function($each) use($library) + { + return ($each->getLocationId() == $library->getId()) + || ($each->getBibliotheque() == $library->getLibelle()); + }) + : $this->newInstance([]); + } + + + public function selectWithLibraryInfo() { + return $this->select(function($each) + { + return $each->getLocationId() || $each->getBibliotheque(); + }); + } + + + public function maxAvailabilityEndDateIn($library) { + return $this + ->selectWaitingToBePulledIn($library) + ->collect(function($each) + { + return $each->getAvailabilityEndDate(); + }) + ->injectInto(null, + function($current, $date) + { + if (!$date) + return $current; + + if (false === ($datetime = DateTime::createFromFormat('!Y-m-d', $date))) + return $current; + + if (null == $current) + return $datetime; + + return $current < $datetime ? $current : $datetime; + }); + } +} diff --git a/library/Class/DriveCheckout/Notification.php b/library/Class/DriveCheckout/Notification.php new file mode 100644 index 0000000000000000000000000000000000000000..dfa2a0eadda60c17f3a39dca31305246b4876a33 --- /dev/null +++ b/library/Class/DriveCheckout/Notification.php @@ -0,0 +1,83 @@ +<?php +/** + * Copyright (c) 2012-2020, 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_DriveCheckout_Notification { + protected $_checkout; + + public function __construct($checkout) { + $this->_checkout = $checkout; + } + + + public function getMailRecipient() { + return $this->_checkout + ? $this->_checkout->getMailRecipient() + : ''; + } + + + public function getLibrary() { + return $this->_checkout + ? $this->_checkout->getLibrary() + : null; + } + + + public function getUser() { + return $this->_checkout + ? $this->_checkout->getUser() + : null; + } + + + public function send() { + if ((!$library = $this->getLibrary()) + || (!$user = $this->getUser()) + || (!$recipient = $this->getMailRecipient())) + return; + + $mailer = new Class_MailHtml(); + if (!$mailer->isMailValid($recipient)) + return; + + $data_source = ['rendez_vous' => $this->_checkout, + 'user' => $user, + 'library' => $library]; + + $body = (new Class_ModeleFusion()) + ->setContenu(Class_AdminVar::getValueOrDefault('DRIVE_TEMPLATE_NEW_RDV_CONTENT')) + ->setDataSource($data_source) + ->getContenuFusionne(); + + $subject = (new Class_ModeleFusion()) + ->setContenu(Class_AdminVar::getValueOrDefault('DRIVE_TEMPLATE_NEW_RDV_SUBJECT')) + ->setDataSource($data_source) + ->getContenuFusionne(); + + $mail = $mailer->prepare($recipient, $subject, $body, true); + $attachment = $mail + ->createAttachment((new Class_ICal_DriveCheckout($this->_checkout))->renderCalendar(Class_Profil::getCurrentProfil()), + Class_ICal_DriveCheckout::MIME_TYPE); + $attachment->filename = 'calendar.ics'; + + $mailer->send($mail); + } +} diff --git a/library/Class/DriveCheckout/Plan.php b/library/Class/DriveCheckout/Plan.php new file mode 100644 index 0000000000000000000000000000000000000000..c4bedf95a6a5a71e677b30ee3ca35d4a94bf02df --- /dev/null +++ b/library/Class/DriveCheckout/Plan.php @@ -0,0 +1,305 @@ +<?php +/** + * Copyright (c) 2012-2020, 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_DriveCheckout_Plan { + use Trait_TimeSource, Trait_Translator, Trait_LastMessage; + + const + LIBRARY_PARAM = 'id_bib', + DATE_PARAM = 'checkout_date', + TIME_PARAM = 'checkout_time'; + + protected + $_params = [], + $_libraries, + $_openings, + $_times, + $_holds, + $_user, + $_library, + $_date, + $_time, + $_should_filter_libraries = true; + + + public function __construct($params, $user) { + $this->_params = $params; + $this->_user = $user; + } + + + public function doNotFilterLibraries() { + $this->_should_filter_libraries = false; + return $this; + } + + + public function getId() { + return $this->_user->getId(); + } + + + public function hasLibraries() { + return !$this->_getLibraries()->isEmpty(); + } + + + public function injectIntoLibraries($value, $closure) { + return $this->_getLibraries()->injectInto($value, $closure); + } + + + public function selectedLibrary() { + if ($this->_library) + return $this->_library; + + return ($library = Class_Bib::find((int)$this->_getParam(static::LIBRARY_PARAM))) + && $this->_getLibraries()->includes($library) + ? $this->_library = $library + : null; + } + + + public function injectIntoOpenings($value, $closure) { + return $this->_getOpenings()->injectInto($value, $closure); + } + + + public function selectedDate() { + if (!$this->_hasParam(static::DATE_PARAM)) + return; + + if ($this->_date) + return $this->_date; + + return (false !== $date = DateTime::createFromFormat('Y-m-d', + $this->_getParam(static::DATE_PARAM))) + && $this->_getOpenings()->includes($date->modify('midnight')) + ? $this->_date = $date + : null; + } + + + public function injectIntoTimes($value, $closure) { + return $this->_getTimes()->injectInto($value, $closure); + } + + + public function selectedTime() { + if (!$this->_hasParam(static::TIME_PARAM) + || (!$date = $this->selectedDate())) + return; + + if ($this->_time) + return $this->_time; + + $time = DateTime::createFromFormat('Y-m-d H:i:s', + sprintf('%s %s:00', + $date->format('Y-m-d'), + $this->_getParam(static::TIME_PARAM))); + + return (false !== $time) && $this->_getTimes()->includes($time) + ? $this->_time = $time + : null; + } + + + public function isValid() { + if (!$this->_user) + return $this->_error($this->_('Vous devez vous connecter pour planifier le retrait de vos documents')); + + if ($this->_hasParam(static::LIBRARY_PARAM) && (!$library = $this->selectedLibrary())) + return $this->_error($this->_('Le site de retrait choisi est invalide')); + + if ($this->_hasParam(static::DATE_PARAM) && !$this->selectedDate()) + return $this->_error($this->_('La date choisie n\'est pas disponible')); + + if ($this->_hasParam(static::TIME_PARAM) && !$this->selectedTime()) + return $this->_error($this->_('L\'horaire choisi n\'est pas disponible')); + + return true; + } + + + /** + * @return Class_DriveCheckout created for this plan or null if something has gone wrong + */ + public function persist() { + if (!$this->_user + || (!$library = $this->selectedLibrary()) + || (!$time = $this->selectedTime())) + return; + + $params = ['start_at' => $time->format('Y-m-d H:i:s'), + 'user' => $this->_user, + 'library' => $library]; + + $checkout = Class_DriveCheckout::newInstance($params); + if ($checkout->save()) + $checkout->notify(); + + return $checkout; + } + + + public function findFutureFor($library) { + return Class_DriveCheckout::findFutureFor($library, $this->_user); + } + + + public function holds() { + return Class_DriveCheckout_Holds::newFor($this->_user); + } + + + protected function _getParam($name) { + return isset($this->_params[$name]) ? $this->_params[$name] : null; + } + + + protected function _hasParam($name) { + return null !== $this->_getParam($name); + } + + + protected function _getHolds() { + if ($this->_holds) + return $this->_holds; + + return $this->_holds = Class_DriveCheckout_Holds::newFor($this->_user) + ->select(function($hold) + { + return $hold->isWaitingToBePulled() + && ($hold->getLocationId() || $hold->getBibliotheque()); + }) + ; + } + + + protected function _getLibraries() { + if ($this->_libraries) + return $this->_libraries; + + if (!$this->_should_filter_libraries) + return $this->_libraries = new Storm_Model_Collection(Class_Bib::findAllBy(['enable_drive' => 1])); + + $holds = $this->_getHolds(); + if ($holds->isEmpty()) + return $this->_libraries = new Storm_Model_Collection(); + + $ids = $holds->collect(function($hold) { return $hold->getLocationId(); }) + ->reject(function($id) { return null === $id; }); + + if (!$ids->isEmpty()) + return $this->_libraries = $this->_getLibrariesByParams(['id_site' => $ids->getArrayCopy()]); + + $labels = $holds->collect(function($hold) + { + return $hold->getBibliotheque(); + }) + ->reject(function($label) + { + return !$label; + }); + + if (!$labels->isEmpty()) + return $this->_libraries = $this->_getLibrariesByParams(['libelle' => $labels->getArrayCopy()]); + + return $this->_libraries = new Storm_Model_Collection(); + } + + + protected function _getLibrariesByParams($params) { + $params = array_merge(['enable_drive' => 1, + 'order' => 'libelle'], + $params); + + return new Storm_Model_Collection(Class_Bib::findAllBy($params)); + } + + + protected function _getOpenings() { + if ($this->_openings) + return $this->_openings; + + if ((!$library = $this->selectedLibrary()) + || !$library->hasDriveOuvertures()) + return $this->_openings = new Storm_Collection(); + + $start = $this->getTimeSource()->asDateTime()->modify('tomorrow'); + $end = $this->_maxOpeningDateFor($library); + + return $this->_openings = Class_Bib_DriveOpening::allBetweenFor($start, $end, $library); + } + + + protected function _maxOpeningDateFor($library) { + return null !== ($max = $this->_getHolds()->maxAvailabilityEndDateIn($library)) + ? $max + : $this->getTimeSource()->asDateTime()->modify('+2 weeks midnight'); + } + + + protected function _getTimes() { + if ($this->_times) + return $this->_times; + + return $this->_times = ($library = $this->selectedLibrary()) && ($date = $this->selectedDate()) + ? Class_Bib_DriveOpening::timesFor($library, $date) + : new Storm_Collection(); + } + + + public function libraryUrlFor($library) { + return [static::LIBRARY_PARAM => $library->getId(), + static::DATE_PARAM => null, + static::TIME_PARAM => null]; + } + + + public function dateUrlFor($date) { + if (!$library = $this->selectedLibrary()) + return $this->resetUrl(); + + return [static::LIBRARY_PARAM => $library->getId(), + static::DATE_PARAM => $date->format('Y-m-d'), + static::TIME_PARAM => null]; + } + + + public function resetUrl() { + return [static::LIBRARY_PARAM => null, + static::DATE_PARAM => null, + static::TIME_PARAM => null]; + } + + + public function fallbackUrl() { + if ($date = $this->selectedDate()) + return $this->dateUrlFor($date); + + if ($library = $this->selectedLibrary()) + return $this->libraryUrlFor($library); + + return $this->resetUrl(); + } +} diff --git a/library/Class/ICal/Abstract.php b/library/Class/ICal/Abstract.php new file mode 100644 index 0000000000000000000000000000000000000000..ee1251b2ccf9d8bb522e3f4a40adbbee71cc6227 --- /dev/null +++ b/library/Class/ICal/Abstract.php @@ -0,0 +1,38 @@ +<?php +/** + * Copyright (c) 2012-2020, 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 + */ + + +abstract class Class_ICal_Abstract { + use Trait_Translator; + + const + MIME_TYPE = 'text/calendar;charset=utf-8', + PRODUCT_ID = 'http://bokeh-library-portal.org'; + + protected $_model; + + public function __construct($model) { + $this->_model = $model; + } + + + abstract public function renderCalendar($profil); +} diff --git a/library/Class/ICal/Autoloader.php b/library/Class/ICal/Autoloader.php new file mode 100644 index 0000000000000000000000000000000000000000..48940296cc05245e204efd534e6adb7586ab9199 --- /dev/null +++ b/library/Class/ICal/Autoloader.php @@ -0,0 +1,50 @@ +<?php +/** + * Copyright (c) 2012-2020, 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_ICal_Autoloader { + use Trait_Singleton; + + protected $_autoloaded = false; + + public function ensureAutoload() { + if ($this->_autoloaded) + return; + + $loader = function ($class) { + if (false === strpos($class, 'Eluceo\\iCal')) + return; + + if ('\\' == substr($class, 0, 1)) + $class = substr($class, 1); + + $class = str_replace('Eluceo\\iCal\\', '', $class); + $class = str_replace('\\', '/', $class); + $path = __DIR__ . '/../../iCal/src/' . $class . '.php'; + + if (file_exists($path)) + require_once $path; + }; + + spl_autoload_register($loader); + $this->_autoloaded = true; + } +} diff --git a/library/Class/ICal/DriveCheckout.php b/library/Class/ICal/DriveCheckout.php new file mode 100644 index 0000000000000000000000000000000000000000..c859a861b1ea568c2de9d612cd4b1a324efc44be --- /dev/null +++ b/library/Class/ICal/DriveCheckout.php @@ -0,0 +1,59 @@ +<?php +/** + * Copyright (c) 2012-2020, 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_ICal_DriveCheckout extends Class_ICal_Abstract { + const PREFIX_ID = '//drive-checkout:'; + + public function renderCalendar($profil) { + if (!$this->_model || !$profil) + return ''; + + Class_ICal_Autoloader::getInstance()->ensureAutoload(); + + $event = $this->_newEvent(); + $event->setUseTimezone(true); + $event->setDtStart($this->_model->getDateTimeStartAt()); + $event->setSummary($this->_('Retrait des réservations à %s', $this->_model->getLibraryLabel())); + $event->setLocation($this->_model->getLibraryLabel()); + if (($library = $this->_model->getLibrary()) + && ($latitude = $library->getLatitude()) + && ($longitude = $library->getLongitude())) + $event->setGeoLocation(new \Eluceo\iCal\Property\Event\Geo($latitude, $longitude)); + + $event->setUrl(Class_Url::absolute(['controller' => 'drive-checkout', + 'action' => 'plan', + 'id_profil' => $profil->getId()], + null, true)); + + $calendar = new \Eluceo\iCal\Component\Calendar(static::PRODUCT_ID); + $calendar->addComponent($event); + + return $calendar->render(); + } + + + protected function _newEvent() { + return new \Eluceo\iCal\Component\Event(Class_Url::absolute([], null, true) + . static::PREFIX_ID + . $this->_model->getId()); + } +} diff --git a/library/Class/Mail.php b/library/Class/Mail.php index eea9b6839c236c41f21c096349ace578a23e38c7..9c0315f465b4ab813b272326707f28c92c636258 100644 --- a/library/Class/Mail.php +++ b/library/Class/Mail.php @@ -40,6 +40,14 @@ class Class_Mail { public function mail($destinataire, $sujet, $body, $send_bcc = false) { + return $this->send($this->prepare($destinataire, $sujet, $body, $send_bcc)); + } + + + /** + * @return ZendAfi_Mail + */ + public function prepare($destinataire, $sujet, $body, $send_bcc=false) { $mail = new ZendAfi_Mail('utf8'); $mail ->setSubject($sujet) @@ -50,6 +58,16 @@ class Class_Mail { $this->_setBodyIn($body, $mail); + return $mail; + } + + + /** + * @param $mail ZendAfi_Mail from this prepare() + * + * @return bool true or error string + */ + public function send($mail) { try { $mail->send(); return true; diff --git a/library/Class/Ouverture.php b/library/Class/Ouverture.php index a464754d0f962c601cc08350682abc3806ab513d..cda49f4e6089013a32cdf08edcf0bbeb2ed06431 100644 --- a/library/Class/Ouverture.php +++ b/library/Class/Ouverture.php @@ -21,7 +21,9 @@ class OuvertureLoader extends Storm_Model_Loader { use Trait_Translator; - const CLOSED_VALUE = '00:00'; + const + CLOSED_VALUE = '00:00', + CLOSED_VALUE_SQL = '00:00:00'; public function compare($a, $b) { if ($a->getJourSemaine() && $b->getJourSemaine() && $a->getJourSemaine() > $b->getJourSemaine()) @@ -82,6 +84,14 @@ class OuvertureLoader extends Storm_Model_Loader { return Class_Ouverture::newInstance(['horaires' => array_fill(0, 4, static::CLOSED_VALUE), 'jour' => $day]); } + + + public function hasDriveFor($library) { + return $library + ? 0 < Class_Ouverture::countBy(['id_site' => $library->getId(), + 'used_for' => Class_Ouverture::USED_FOR_DRIVE]) + : false; + } } @@ -97,7 +107,10 @@ class Class_Ouverture extends Storm_Model_Abstract { JEUDI = 4, VENDREDI = 5, SAMEDI = 6, - DIMANCHE = 7; + DIMANCHE = 7, + USED_FOR_LIBRARY = 0, + USED_FOR_MULTIMEDIA = 1, + USED_FOR_DRIVE = 2; protected $_table_name = 'ouvertures'; protected $_loader_class = 'OuvertureLoader'; @@ -106,11 +119,13 @@ class Class_Ouverture extends Storm_Model_Abstract { 'jour' => null, 'debut_matin' => '10:00', 'fin_matin' => '12:00', + 'max_per_period_matin' => null, 'debut_apres_midi' => '12:00', 'fin_apres_midi' => '18:00', + 'max_per_period_apres_midi' => null, 'validity_start' => null, 'validity_end' => null, - 'multimedia' => 0]; + 'used_for' => 0]; protected $_belongs_to = ['bib' => ['model' => 'Class_Bib', 'referenced_in' => 'id_site']]; @@ -125,6 +140,26 @@ class Class_Ouverture extends Storm_Model_Abstract { } + public function getMaxPerPeriodMatin() { + if (!$this->isDrive()) + return null; + + return is_null($value = parent::_get('max_per_period_matin')) + ? 1 + : (int)$value; + } + + + public function getMaxPerPeriodApresMidi() { + if (!$this->isDrive()) + return null; + + return is_null($value = parent::_get('max_per_period_apres_midi')) + ? 1 + : (int)$value; + } + + public function beforeSave() { if ($this->getJour()) $this->setJour(Class_Ouverture::getLoader()->humanDateToDate($this->getJour())); @@ -140,6 +175,27 @@ class Class_Ouverture extends Storm_Model_Abstract { } + + public function beMultimedia() { + return $this->setUsedFor(static::USED_FOR_MULTIMEDIA); + } + + + public function beDrive() { + return $this->setUsedFor(static::USED_FOR_DRIVE); + } + + + public function isDrive() { + return (int)$this->getUsedFor() === static::USED_FOR_DRIVE; + } + + + public function isMultimedia() { + return (int)$this->getUsedFor() === static::USED_FOR_MULTIMEDIA; + } + + public function getLibelle() { return $this->getLabel(); } @@ -225,13 +281,11 @@ class Class_Ouverture extends Storm_Model_Abstract { } - public function validate() { $this->checkAttribute('validity_range', Class_Date::isEndDateAfterStartDate($this->getValidityStart(), $this->getValidityEnd()), - "La date de début doit être plus récente que la date de fin"); - + $this->_('La date de début doit être plus récente que la date de fin')); } @@ -252,7 +306,6 @@ class Class_Ouverture extends Storm_Model_Abstract { } - public function isValidDuring($from, $to) { if ($this->isValidOnDate($from) || $this->isValidOnDate($to)) return true; @@ -307,7 +360,6 @@ class Class_Ouverture extends Storm_Model_Abstract { } - public function hasValidityRange() { return $this->hasValidityStart() || $this->hasValidityEnd(); } diff --git a/library/Class/Ouverture/Visitor.php b/library/Class/Ouverture/Visitor.php index c0b08314d04e2f204f639413b436b3b402f352ac..4e6ff16555ec18774095d87056cc2f4dcd0a384a 100644 --- a/library/Class/Ouverture/Visitor.php +++ b/library/Class/Ouverture/Visitor.php @@ -33,7 +33,7 @@ class Class_Ouverture_Visitor { $_content, $_openings, $_should_keep_all = false, - $_multimedia = false; + $_used_for = null; public function __construct() { $this->_openings = [static::DEFAULT_KEY => [], @@ -50,10 +50,7 @@ class Class_Ouverture_Visitor { public function visitOpening($opening) { - if ($this->_multimedia && !$opening->getMultimedia()) - return $this; - - if (!$this->_multimedia && $opening->getMultimedia()) + if ((int)$opening->getUsedFor() !== (int)$this->_used_for) return $this; if (!$opening->hasValidityRange() && $opening->isRecurrent()) @@ -189,8 +186,8 @@ class Class_Ouverture_Visitor { } - public function setMultimedia($flag=true) { - $this->_multimedia = $flag; + public function setUsedFor($used_for) { + $this->_used_for = $used_for; return $this; } } \ No newline at end of file diff --git a/library/Class/SearchCriteria/DateRange.php b/library/Class/SearchCriteria/DateRange.php index 8142c03541493c9db44f49d34a6b4786262b1e43..b829d5b0648b05a5dfd1492fed38332b668efdd9 100644 --- a/library/Class/SearchCriteria/DateRange.php +++ b/library/Class/SearchCriteria/DateRange.php @@ -109,7 +109,7 @@ class Class_SearchCriteria_DateRange extends Class_SearchCriteria_Abstract { if ('' === $value) return ''; - if (!(new ZendAfi_Validate_DateFormat())->isValid($value, static::DATE_FORMAT)) { + if (!(new ZendAfi_Validate_DateFormat())->setFormat(static::DATE_FORMAT)->isValid($value)) { $this->_element->addError($this->_('Les dates doivent être au format JJ/MM/AAAA')); return; } diff --git a/library/Class/TableDescription.php b/library/Class/TableDescription.php index 9412d0466ca8565cd0c13790a9ed8e43b6633ab3..a523d8e647fb5ed4889249203dc69e6b53378055 100644 --- a/library/Class/TableDescription.php +++ b/library/Class/TableDescription.php @@ -123,7 +123,25 @@ class Class_TableDescription { } + public function prependColumn($label, $description) { + $this + ->_columns + ->prepend($this->newColumn($label, $description)); + + return $this; + } + + public function addColumn($label, $description) { + $this + ->_columns + ->add($this->newColumn($label, $description)); + + return $this; + } + + + public function newColumn($label, $description) { if ($description instanceof Closure) $description = ['callback' => $description]; @@ -136,19 +154,12 @@ class Class_TableDescription { 'sortable' => true, 'sort_attribute' => ''], $description); - $this - ->_columns - ->add($this->newColumn($label, $description) - ->setOptions($description['options'])); - - return $this; - } - - public function newColumn($label, $description) { - return $description['callback'] + $column = $description['callback'] ? new Class_TableDescription_ColumnForCallback($label, $description, $this) : new Class_TableDescription_ColumnForAttribute($label, $description, $this); + + return $column->setOptions($description['options']); } @@ -267,6 +278,15 @@ class Class_TableDescription_Columns { } + /** @return Class_TableDescription_ColumnAbstract subclass */ + public function prepend($column) { + $new_array = $this->_columns->getArrayCopy(); + array_unshift($new_array, $column); + $this->_columns->exchangeArray($new_array); + return $column; + } + + public function acceptVisitor($visitor) { $this->_columns->eachDo(function($column) use($visitor) { diff --git a/library/Class/TableDescription/DriveCheckout/Holds.php b/library/Class/TableDescription/DriveCheckout/Holds.php new file mode 100644 index 0000000000000000000000000000000000000000..7fcfd881fd621ab9b108aa44f8a3dc3edabaec59 --- /dev/null +++ b/library/Class/TableDescription/DriveCheckout/Holds.php @@ -0,0 +1,51 @@ +<?php +/** + * Copyright (c) 2012-2020, 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_DriveCheckout_Holds extends Class_TableDescription { + public function init() { + $this + ->addColumn($this->_('Cote'), + function($hold) { return $hold->getCote(); }) + + ->addColumn($this->_('Code-barres'), + function($hold) { return $hold->getCodeBarres(); }) + + ->addColumn($this->_('État'), + function($hold){ return $hold->getStatus(); }) + + ->addColumn($this->_('Titre'), + function($hold){ return $hold->getTitle(); }) + + ->addRowAction(['canvas_callback' => function($hold, $canvas) + { + if (!$record = $hold->getRecord()) + return ''; + $view = $canvas->getView(); + return $view->tagAnchor($view->urlNotice($record, [], null, true), + Class_Admin_Skin::current() + ->renderActionIconOn('view', $view), + ['target' => '_blank', + 'title' => $this->_('Voir la notice %s', + $record->getTitrePrincipal())]); + }]); + } +} diff --git a/library/Class/TableDescription/DriveCheckout/HoldsWithCheckouts.php b/library/Class/TableDescription/DriveCheckout/HoldsWithCheckouts.php new file mode 100644 index 0000000000000000000000000000000000000000..62257a5fb78f223fb5f558db8552a5362260eb3e --- /dev/null +++ b/library/Class/TableDescription/DriveCheckout/HoldsWithCheckouts.php @@ -0,0 +1,43 @@ +<?php +/** + * Copyright (c) 2012-2020, 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_DriveCheckout_HoldsWithCheckouts extends Class_TableDescription { + public function init() { + $this + ->addColumn($this->_('Jour'), + function($hold) { return strftime('%d %B',strtotime($hold->getStartAt())); }) + ->addColumn($this->_('Heure'), + function($hold) { return strftime('%H:%M',strtotime($hold->getStartAt())); }) + ->addColumn($this->_('Carte'), + function($hold) { return $hold->getIdabon(); }) + ->addColumn($this->_('Abonné'), + function($hold) { return $hold->getNomComplet(); }) + ->addColumn($this->_('Cote'), + function($hold) { return $hold->getCote(); }) + + ->addColumn($this->_('Code-barres'), + function($hold) { return $hold->getCodeBarres(); }) + + ->addColumn($this->_('Titre'), + function($hold){ return strip_tags($hold->getTitle()); }); + } +} diff --git a/library/Class/TableDescription/DriveCheckout/List.php b/library/Class/TableDescription/DriveCheckout/List.php new file mode 100644 index 0000000000000000000000000000000000000000..5caf56025d8585c724e02241bd0adcbbdfb48802 --- /dev/null +++ b/library/Class/TableDescription/DriveCheckout/List.php @@ -0,0 +1,35 @@ +<?php +/** + * Copyright (c) 2012-2020, 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_DriveCheckout_List extends Class_TableDescription { + public function init() { + $this + ->addColumn($this->_('Heure'), + function($checkout) + { + return strftime('%H:%M', + strtotime($checkout->getStartAt())); + }) + ->addColumn($this->_('Carte'), 'id_abon') + ->addColumn($this->_('Abonné'), 'nom_complet'); + } +} \ No newline at end of file diff --git a/library/Class/TableDescription/DriveCheckout/ListWithActions.php b/library/Class/TableDescription/DriveCheckout/ListWithActions.php new file mode 100644 index 0000000000000000000000000000000000000000..a1c2971cad301b23750a623f98d85a2a4a1e2226 --- /dev/null +++ b/library/Class/TableDescription/DriveCheckout/ListWithActions.php @@ -0,0 +1,81 @@ +<?php +/** + * Copyright (c) 2012-2020, 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_DriveCheckout_ListWithActions extends Class_TableDescription_DriveCheckout_List { + public function init() { + parent::init(); + $this + ->addRowAction(['canvas_callback' => [$this, 'listHolds']]) + ->addRowAction(['canvas_callback' => [$this, 'viewUser']]) + ->addRowAction(['canvas_callback' => [$this, 'deleteCheckout']]); + } + + + public function deleteCheckout($checkout, $canvas) { + $view = $canvas->getView(); + return $view->tagAnchor(['action' => 'delete', + 'id' => $checkout->getId()], + + Class_Admin_Skin::current()->renderActionIconOn('delete', + $view), + + ['title' => $this->_('Supprimer le rendez-vous pour %s', + $checkout->getNomComplet()), + 'onclick' => sprintf('return confirm(\'%s\')', + $this->_('Etes-vous sûr de vouloir supprimer le rendez-vous pour %s ?', + $checkout->getNomComplet()))]); + } + + + public function listHolds($checkout, $canvas) { + $view = $canvas->getView(); + return $view->tagAnchor(['action' => 'list-holds', + 'id' => $checkout->getId()], + + Class_Admin_Skin::current()->renderActionIconOn('view', + $view), + + ['data-popup' => 'true', + 'title' => $this->_('Voir les réservations pour %s', + $checkout->getNomComplet())]); + } + + + public function viewUser($checkout, $canvas) { + if (!$user = $checkout->getUser()) + return ''; + + $view = $canvas->getView(); + return $view->tagAnchor($view->url(['module' => 'admin', + 'controller' => 'users', + 'action' => 'edit', + 'id' => $user->getId()], + null, + true), + + Class_Admin_Skin::current()->renderActionIconOn('user', + $view), + + ['title' => $this->_('Voir la fiche de %s', + $checkout->getNomComplet())]); + } +} \ No newline at end of file diff --git a/library/Class/TableDescription/Openings.php b/library/Class/TableDescription/Openings.php index 9dd982a22ecf94e0221ae45a07af0f0563d68a09..1a9749fe3d39058d46a9c2d05da3502a0d8c9e5d 100644 --- a/library/Class/TableDescription/Openings.php +++ b/library/Class/TableDescription/Openings.php @@ -31,18 +31,8 @@ class Class_TableDescription_Openings extends Class_TableDescription { ->addColumn($this->_('Jour'), function($model) { return $this->_humanDateRange($model); }) - ->addColumn($this->_('Matinée'), - function($model) - { - return $this->_timeSegment($model->getDebutMatin(), $model->getFinMatin()); - }) - - ->addColumn($this->_('Après-midi'), - function($model) - { - return $this->_timeSegment($model->getDebutApresMidi(), $model->getFinApresMidi()); - }) - + ->_addMorningColumns() + ->_addAfternoonColumns() ->addRowAction(['url' => ['module' => 'admin', 'controller' => 'ouvertures', 'action' => 'edit', @@ -69,6 +59,26 @@ class Class_TableDescription_Openings extends Class_TableDescription { } + protected function _addMorningColumns() { + return $this->addColumn($this->_('Matinée'), + function($model) + { + return $this->_timeSegment($model->getDebutMatin(), + $model->getFinMatin()); + }); + } + + + protected function _addAfternoonColumns() { + return $this->addColumn($this->_('Après-midi'), + function($model) + { + return $this->_timeSegment($model->getDebutApresMidi(), + $model->getFinApresMidi()); + }); + } + + public function setView($view) { $this->_view = $view; return $this; diff --git a/library/Class/TableDescription/Openings/Drive.php b/library/Class/TableDescription/Openings/Drive.php new file mode 100644 index 0000000000000000000000000000000000000000..2c4c786eff7dba4a27dbea0c7ee02b512fc0268c --- /dev/null +++ b/library/Class/TableDescription/Openings/Drive.php @@ -0,0 +1,46 @@ +<?php +/** + * Copyright (c) 2012-2019, 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_Openings_Drive extends Class_TableDescription_Openings { + protected function _addMorningColumns() { + return + parent::_addMorningColumns() + ->addColumn($this->_('Quota matin'), + function($model) + { + return $this->_('%s pers.', + $model->getMaxPerPeriodMatin()); + }); + } + + + protected function _addAfternoonColumns() { + return + parent::_addAfternoonColumns() + ->addColumn($this->_('Quota après-midi'), + function($model) + { + return $this->_('%s pers.', + $model->getMaxPerPeriodApresMidi()); + }); + } +} \ No newline at end of file diff --git a/library/Class/User/Cards.php b/library/Class/User/Cards.php index d0c304ba22490d6186340b752cf748b1e5117504..bc7207a3799146d9a5e4c3a8ade0da6d60e23779 100644 --- a/library/Class/User/Cards.php +++ b/library/Class/User/Cards.php @@ -208,12 +208,23 @@ class Class_User_Cards extends Storm_Model_Collection { 'Vous avez %d documents en retard.', $late_loans_count)); - if ($pullable_items_count = $this->countWaitingToBePulled()) + $actions = Class_AdminVar::isModuleEnabled('ENABLE_DRIVE_CHECKOUT') + ? ['actions' => + [ + Class_Url::assemble(['module' => 'opac', + 'controller' => 'drive-checkout', + 'action' => 'plan']) => $this->_('Planifier le retrait de mes documents')]] + : []; + + + if ($pullable_items_count = $this->countWaitingToBePulled()) { $notifiable->notify($this->_plural($pullable_items_count, '', 'Vous avez %d document en attente de retrait.', 'Vous avez %d documents en attente de retrait.', - $pullable_items_count)); + $pullable_items_count), + $actions); + } } catch(Exception $e) { $notifiable->notify($this->_('Une erreur est survenue nous empêchant de lister vos prêts.')); $notifiable->notify($this->_('Erreur : "%s"', $e->getMessage())); diff --git a/library/Class/Users.php b/library/Class/Users.php index 4e9cacfda163fce6986b30b8a9d6999418b91652..4627a8434259cc33862e5d61b7b9353b94272278 100644 --- a/library/Class/Users.php +++ b/library/Class/Users.php @@ -452,11 +452,14 @@ class Class_Users extends Storm_Model_Abstract { 'bookmarked_searches' => ['model' => 'Class_User_BookmarkedSearch', 'role' => 'user', 'dependents' => 'delete'], + 'user_identities' => ['model' => 'Class_User_Identity', 'role' => 'user', - 'dependents' => 'delete'] - + 'dependents' => 'delete'], + 'drive_checkouts' => ['model' => 'Class_DriveCheckout', + 'role' => 'user', + 'dependents' => 'delete'] ], diff --git a/library/Class/WebService/SIGB/Exemplaire.php b/library/Class/WebService/SIGB/Exemplaire.php index 6a71c43dcdbe12c552c4171875b3d266783985ca..bebd4ff9d1607af8f7951af97155c73bc740851e 100644 --- a/library/Class/WebService/SIGB/Exemplaire.php +++ b/library/Class/WebService/SIGB/Exemplaire.php @@ -112,13 +112,15 @@ class Class_WebService_SIGB_Exemplaire { /** * @return Class_Exemplaire */ - public function getExemplaireOPAC() { + public function getExemplaireOPAC($user=null) { if($this->_exemplaire_opac) return $this->_exemplaire_opac; $operation = (new Class_Entity())->updateAttributes(['CodeBarre' => $this->code_barre, 'NoNotice' => $this->no_notice]); - return $this->_exemplaire_opac = Class_Exemplaire::findFirstBySIGBOperation(Class_Users::getIdentity(), $operation); + + if (!$user) $user = Class_Users::getIdentity(); + return $this->_exemplaire_opac = Class_Exemplaire::findFirstBySIGBOperation($user, $operation); } @@ -251,6 +253,7 @@ class Class_WebService_SIGB_Exemplaire { public function getCodeBarre(){ if (!$this->code_barre && ($ex_opac = $this->getExemplaireOPAC())) $this->code_barre = $ex_opac->getCodeBarres(); + return $this->code_barre; } @@ -268,6 +271,9 @@ class Class_WebService_SIGB_Exemplaire { public function getCote() { + if (!$this->_cote && ($item = $this->getExemplaireOPAC())) + $this->_cote = $item->getCote(); + return $this->_cote; } diff --git a/library/Class/WebService/SIGB/ExemplaireOperation.php b/library/Class/WebService/SIGB/ExemplaireOperation.php index 9cfdb3e881783747cc52a6bfb30e58cf2186a611..867194cb73cf4a95a130699df859a2e0742bd385 100644 --- a/library/Class/WebService/SIGB/ExemplaireOperation.php +++ b/library/Class/WebService/SIGB/ExemplaireOperation.php @@ -112,6 +112,20 @@ abstract class Class_WebService_SIGB_ExemplaireOperation { } + public function setCote($cote) { + $this->_exemplaire->setCote($cote); + return $this; + } + + + /** + * @return string + */ + public function getCote() { + return $this->_exemplaire->getCote(); + } + + /** * @return Class_WebService_SIGB_Exemplaire */ @@ -190,8 +204,8 @@ abstract class Class_WebService_SIGB_ExemplaireOperation { /** * @return Class_Exemplaire */ - public function getExemplaireOPAC() { - return $this->_exemplaire->getExemplaireOPAC(); + public function getExemplaireOPAC($user = null) { + return $this->_exemplaire->getExemplaireOPAC($user); } diff --git a/library/Class/WebService/SIGB/Koha/PatronInfoReader.php b/library/Class/WebService/SIGB/Koha/PatronInfoReader.php index d7c2f84da8288d4f9b8ac62fc9a5d6714c6e6991..0f75c707c5a7f9105f388afe27980925b1b71e30 100644 --- a/library/Class/WebService/SIGB/Koha/PatronInfoReader.php +++ b/library/Class/WebService/SIGB/Koha/PatronInfoReader.php @@ -19,13 +19,15 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -class Class_WebService_SIGB_Koha_PatronInfoReader extends Class_WebService_SIGB_AbstractILSDIPatronInfoReader { +class Class_WebService_SIGB_Koha_PatronInfoReader + extends Class_WebService_SIGB_AbstractILSDIPatronInfoReader { use Class_WebService_SIGB_Koha_TraitFormat, Trait_Translator; protected $_current_exemplaire_operation, $_grouped_holds_itypes = []; + public static function newInstance() { return new self(); } @@ -61,13 +63,20 @@ class Class_WebService_SIGB_Koha_PatronInfoReader extends Class_WebService_SIGB_ public function endBarcode($code) { - $this->_current_operation->getExemplaire()->setCodeBarre($code); + $this->_current_operation->setCodeBarre($code); + } + + + public function endItemcallnumber($data) { + if ($data) + $this->_current_operation->setCote($data); } public function endItemNumber($id) { if ($this->_xml_parser->inParents('loan')) $this->_current_operation->setId($id); + $this->_current_operation->getExemplaire()->setId($id); } @@ -128,7 +137,8 @@ class Class_WebService_SIGB_Koha_PatronInfoReader extends Class_WebService_SIGB_ $this->_current_operation->getExemplaire()->setBibliotheque($site->getLibelle()); if ($this->_currentHold) - $this->_currentHold->setPickupLocationLabel($site->getLibelle()); + $this->_currentHold->setPickupLocationLabel($site->getLibelle()) + ->setLocationId($site->getLibraryId()); } @@ -166,7 +176,9 @@ class Class_WebService_SIGB_Koha_PatronInfoReader extends Class_WebService_SIGB_ public function endExpirationDate($data) { - $this->_currentHold->setExpirationDate($data); + $this->_currentHold + ->setAvailabilityEndDate($data) + ->setExpirationDate($data); } @@ -180,5 +192,3 @@ class Class_WebService_SIGB_Koha_PatronInfoReader extends Class_WebService_SIGB_ $this->getEmprunteur()->setNbEmprunts((int)$data); } } - -?> \ No newline at end of file diff --git a/library/Class/WebService/SIGB/Nanook/PatronInfoReader.php b/library/Class/WebService/SIGB/Nanook/PatronInfoReader.php index 93f09e9e715ea69bc984a6c56b43e22032b1d7bc..8d7021846226cfd5df05a6be8d165016662142ff 100644 --- a/library/Class/WebService/SIGB/Nanook/PatronInfoReader.php +++ b/library/Class/WebService/SIGB/Nanook/PatronInfoReader.php @@ -83,6 +83,11 @@ class Class_WebService_SIGB_Nanook_PatronInfoReader extends Class_WebService_SIG * @param string $data */ public function endBarcode($data) { + if ($this->_xml_parser->inParents('hold')) { + $this->_currentHold->setCodeBarre($data); + return; + } + $this->_emprunteur->setCodeBarres($data); } @@ -201,6 +206,24 @@ class Class_WebService_SIGB_Nanook_PatronInfoReader extends Class_WebService_SIG } + public function endAvailabilityEndDate($data) { + if ($data && $this->_xml_parser->inParents('hold')) + $this->_currentHold->setAvailabilityEndDate($data); + } + + + public function endLocationId($data) { + if ($data && $this->_xml_parser->inParents('hold')) + $this->_currentHold->setLocationId($data); + } + + + public function endCote($data) { + if ($data && $this->_xml_parser->inParents('hold')) + $this->_currentHold->setCote($data); + } + + public function startSuggest() { $this->_current_suggest = (new Class_WebService_SIGB_Nanook_Suggestion) ->setUser($this->_user); diff --git a/library/Class/WebService/SIGB/Reservation.php b/library/Class/WebService/SIGB/Reservation.php index 5a36cc4ce3c3cd0982a9ef3d0227bd6e3d60dacf..f3640c20c11f4bd95e767343479bf8d5d17e210a 100644 --- a/library/Class/WebService/SIGB/Reservation.php +++ b/library/Class/WebService/SIGB/Reservation.php @@ -20,10 +20,13 @@ */ class Class_WebService_SIGB_Reservation extends Class_WebService_SIGB_ExemplaireOperation { - protected $rang; - protected $etat; - protected $pickup_location_label; - protected $waiting_to_be_pulled = false; + protected + $rang, + $etat, + $pickup_location_label, + $waiting_to_be_pulled = false, + $_availability_end_date, + $_location_id; public function getRang() { @@ -84,9 +87,30 @@ class Class_WebService_SIGB_Reservation extends Class_WebService_SIGB_Exemplaire } + public function setAvailabilityEndDate($value) { + $this->_availability_end_date = $value; + return $this; + } + + + public function getAvailabilityEndDate() { + return $this->_availability_end_date; + } + + + public function setLocationId($id) { + $this->_location_id = $id; + return $this; + } + + + public function getLocationId() { + return $this->_location_id; + } + + /** @codeCoverageIgnore */ public function __toString(){ return parent::__toString().", Rang:".$this->getRang(); } } -?> diff --git a/library/ZendAfi/Acl/AdminControllerGroup.php b/library/ZendAfi/Acl/AdminControllerGroup.php index ea1d5397bc0cf51ca14450f1de199b5ec3a8b6c7..857dadb41761bce6b0220226868d9b66d363779a 100644 --- a/library/ZendAfi/Acl/AdminControllerGroup.php +++ b/library/ZendAfi/Acl/AdminControllerGroup.php @@ -87,6 +87,7 @@ class ZendAfi_Acl_AdminControllerGroup { 'rendez-vous' => Class_AdminVar::isRendezVousEnabled(), 'federation-reviews' => Class_AdminVar::isFederationEnabled(), 'identity-providers' => Class_AdminVar::isIdentityProvidersEnabled(), + 'drive-checkout' => Class_AdminVar::isDriveCheckoutEnabled(), ]; $this->_activated = array_merge($this->_activated, diff --git a/library/ZendAfi/Acl/AdminControllerRoles.php b/library/ZendAfi/Acl/AdminControllerRoles.php index b3f6c534d6077826f19a2baa9e38cb89f049a0c3..01cb776ffe39acb6ca23175744229c0e5c90c934 100644 --- a/library/ZendAfi/Acl/AdminControllerRoles.php +++ b/library/ZendAfi/Acl/AdminControllerRoles.php @@ -102,6 +102,7 @@ class ZendAfi_Acl_AdminControllerRoles extends Zend_Acl { $this->add(new Zend_Acl_Resource('rendez-vous')); $this->add(new Zend_Acl_Resource('journal')); $this->add(new Zend_Acl_Resource('identity-providers')); + $this->add(new Zend_Acl_Resource('drive-checkout')); $codifications = ['codification-browser', 'thesauri', @@ -156,6 +157,7 @@ class ZendAfi_Acl_AdminControllerRoles extends Zend_Acl { $this->allow('modo_bib','users/change-admin-skin'); $this->allow('modo_bib','users/settings'); $this->allow('modo_bib','file-manager'); + $this->allow('modo_bib','drive-checkout'); $this->allow('admin_bib','rss'); $this->allow('admin_bib','catalogue'); diff --git a/library/ZendAfi/Controller/Action/Helper/Ical.php b/library/ZendAfi/Controller/Action/Helper/Ical.php new file mode 100644 index 0000000000000000000000000000000000000000..23ca6ffc485541a533580c51f917183a11cf3688 --- /dev/null +++ b/library/ZendAfi/Controller/Action/Helper/Ical.php @@ -0,0 +1,34 @@ +<?php +/** + * Copyright (c) 2020, 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_Ical extends Zend_Controller_Action_Helper_Abstract { + public function direct($filename, $content) { + $this->_actionController->getHelper('ViewRenderer')->setNoRender(); + + $response = $this->getResponse(); + $response->clearAllHeaders(); + + $response->setHeader('Content-Type', Class_ICal_Abstract::MIME_TYPE, true); + $response->setHeader('Content-Disposition', 'attachment;filename="' . $filename . '"', true); + $response->setBody($content); + } +} diff --git a/library/ZendAfi/Controller/Action/Helper/RenderIcalArticles.php b/library/ZendAfi/Controller/Action/Helper/RenderIcalArticles.php index a43dfe844d59c12420a32b9a18c6c72d5dfd934f..2718ba2cc30189401e376b0c4bd2b8075bda4c28 100644 --- a/library/ZendAfi/Controller/Action/Helper/RenderIcalArticles.php +++ b/library/ZendAfi/Controller/Action/Helper/RenderIcalArticles.php @@ -23,10 +23,9 @@ class ZendAfi_Controller_Action_Helper_RenderIcalArticles extends Zend_Controller_Action_Helper_Abstract { const PRODID = 'http://bokeh-library-portal.org'; - static protected $_autoloaded = false; public function direct($profil, $articles=[]) { - static::ensureAutoload(); + Class_ICal_Autoloader::getInstance()->ensureAutoload(); $vCalendar = new \Eluceo\iCal\Component\Calendar(static::PRODID); @@ -46,28 +45,4 @@ class ZendAfi_Controller_Action_Helper_RenderIcalArticles echo $vCalendar->render(); } - - - public static function ensureAutoload() { - if (static::$_autoloaded) - return; - - $loader = function ($class) { - if (false === strpos($class, 'Eluceo\\iCal')) - return; - - if ('\\' == substr($class, 0, 1)) - $class = substr($class, 1); - - $class = str_replace('Eluceo\\iCal\\', '', $class); - $class = str_replace('\\', '/', $class); - $path = __DIR__ . '/../../../../iCal/src/' . $class . '.php'; - - if (file_exists($path)) - require_once $path; - }; - - spl_autoload_register($loader); - static::$_autoloaded = true; - } } diff --git a/library/ZendAfi/Controller/Plugin/AdminAuth.php b/library/ZendAfi/Controller/Plugin/AdminAuth.php index dd63656e84ae20d4e2335d35d89c09c68d1576a8..abe917b3d60a0121041df5b8df570c7e642e594b 100644 --- a/library/ZendAfi/Controller/Plugin/AdminAuth.php +++ b/library/ZendAfi/Controller/Plugin/AdminAuth.php @@ -63,7 +63,7 @@ class ZendAfi_Controller_Plugin_AdminAuth extends Zend_Controller_Plugin_Abstrac if ((!$user = Class_Users::getIdentity()) && $action !== "authenticate" - && in_array($controller, ["abonne", 'bookmarked-searches'])) { + && in_array($controller, ["abonne", 'bookmarked-searches', 'drive-checkout'])) { $request->setParam('redirect', $this->_getRedirect($request, $action)); $controller = 'auth'; $action = ($request->getParam('render') == 'popup') ? 'popup-login' : 'login'; diff --git a/library/ZendAfi/Controller/Plugin/Manager/Library.php b/library/ZendAfi/Controller/Plugin/Manager/Library.php index 3837c4d03c1334adb5c15cb1286864bf2b4e7016..f3ce96675d52ba281b99606985f8b08313cd2fbe 100644 --- a/library/ZendAfi/Controller/Plugin/Manager/Library.php +++ b/library/ZendAfi/Controller/Plugin/Manager/Library.php @@ -109,12 +109,20 @@ class ZendAfi_Controller_Plugin_Manager_Library extends ZendAfi_Controller_Plugi 'icon' => 'calendar', 'label' => $this->_('Planification des ouvertures')], - ['url' => '/admin/ouvertures/index/id_site/%s/multimedia/1', + ['url' => '/admin/ouvertures/index/id_site/%s/used_for/'.Class_Ouverture::USED_FOR_MULTIMEDIA, 'icon' => 'computers', 'label' => $this->_('Planification des ouvertures multimédia'), 'condition' => function($model) { return Class_AdminVar::isMultimediaEnabled(); + }], + + ['url' => '/admin/ouvertures/index/id_site/%s/used_for/'.Class_Ouverture::USED_FOR_DRIVE, + 'icon' => 'shopping', + 'label' => $this->_('Planification des ouvertures du drive'), + 'condition' => function($library) + { + return Class_AdminVar::isDriveCheckoutEnabled() && $library->isDriveEnabled(); }]]; } } diff --git a/library/ZendAfi/Controller/Plugin/Manager/Opening.php b/library/ZendAfi/Controller/Plugin/Manager/Opening.php index 60e7a96b1a2bec92236301740a0d7a9d2ff5bf89..d46e3d339b07592081bdb8f2ee46cd30915b2899 100644 --- a/library/ZendAfi/Controller/Plugin/Manager/Opening.php +++ b/library/ZendAfi/Controller/Plugin/Manager/Opening.php @@ -53,6 +53,16 @@ class ZendAfi_Controller_Plugin_Manager_Opening extends ZendAfi_Controller_Plugi } + protected function _getForm($model){ + $form = parent::_getForm($model); + if ((int)$this->_getParam('used_for') !== Class_Ouverture::USED_FOR_DRIVE){ + $form->removeElement('max_per_period_matin'); + $form->removeElement('max_per_period_apres_midi'); + } + return $form; + } + + public function duplicateAction() { if (!$this->_canAdd()) { $this->_helper->notify($this->_view->_('Vous n\'avez pas la permission "%s"', @@ -88,8 +98,8 @@ class ZendAfi_Controller_Plugin_Manager_Opening extends ZendAfi_Controller_Plugi protected function _updateNewModel($model) { - if(null !== $this->_getParam('multimedia')) - $model->setMultimedia(1); + if ($module = $this->_getParam('used_for')) + $model->setUsedFor($module); return $this; } diff --git a/library/ZendAfi/Controller/Plugin/Manager/User.php b/library/ZendAfi/Controller/Plugin/Manager/User.php index c584377b6590fd04e6149e43ed13863cc18f7d4e..e03c89998ad16e05bf4e9664cd2bcaf3c1a27e47 100644 --- a/library/ZendAfi/Controller/Plugin/Manager/User.php +++ b/library/ZendAfi/Controller/Plugin/Manager/User.php @@ -65,6 +65,13 @@ class ZendAfi_Controller_Plugin_Manager_User extends ZendAfi_Controller_Plugin_M } ], + ['url' => Class_Url::assemble(['module' => 'admin', + 'controller' => 'drive-checkout', + 'action' => 'plan']) . '/id_user/%s', + 'icon' => 'shopping', + 'condition' => function() { return Class_AdminVar::isDriveCheckoutEnabled(); }, + 'label' => $this->_('Planifier un retrait de réservations')], + ['url' => ['module' => 'opac', 'controller' => 'auth', 'action' => 'become', diff --git a/library/ZendAfi/Controller/Plugin/ResourceDefinition/Opening.php b/library/ZendAfi/Controller/Plugin/ResourceDefinition/Opening.php index e7e24dcfc710a16370e6ffe008e32dffa8681e7c..74a6501af686e013df6a794abf18aab46077b332 100644 --- a/library/ZendAfi/Controller/Plugin/ResourceDefinition/Opening.php +++ b/library/ZendAfi/Controller/Plugin/ResourceDefinition/Opening.php @@ -24,7 +24,7 @@ class ZendAfi_Controller_Plugin_ResourceDefinition_Opening extends ZendAfi_Contr public function getDefinitions() { return ['model' => ['class' => 'Class_Ouverture', 'name' => 'ouverture', - 'scope' => ['id_site', 'multimedia'], + 'scope' => ['id_site', 'used_for'], 'order' => 'jour desc, jour_semaine, validity_start'], 'sort' => ['Class_Ouverture', 'compare'], @@ -47,14 +47,19 @@ class ZendAfi_Controller_Plugin_ResourceDefinition_Opening extends ZendAfi_Contr $lib_label = $this->_library->getLibelle(); - return $this->_isMultimedia() - ? ['successful_add' => $this->_('Plage horaire de réservation multimedia %s ajoutée', $lib_label), - 'successful_save' => $this->_('Plage horaire de réservation multimedia %s sauvegardée', $lib_label), - 'successful_delete' => $this->_('Plage horaire de réservation multimedia %s supprimée', $lib_label)] + if ($this->_getUsedFor() === Class_Ouverture::USED_FOR_MULTIMEDIA) + return ['successful_add' => $this->_('Plage horaire de réservation multimedia %s ajoutée', $lib_label), + 'successful_save' => $this->_('Plage horaire de réservation multimedia %s sauvegardée', $lib_label), + 'successful_delete' => $this->_('Plage horaire de réservation multimedia %s supprimée', $lib_label)]; - : ['successful_add' => $this->_('Plage d\'ouverture %s ajoutée', $lib_label), - 'successful_save' => $this->_('Plage d\'ouverture %s sauvegardée', $lib_label), - 'successful_delete' => $this->_('Plage d\'ouverture %s supprimée', $lib_label)]; + if ($this->_getUsedFor() === Class_Ouverture::USED_FOR_DRIVE) + return ['successful_add' => $this->_('Plage d\'ouverture du drive %s ajoutée', $lib_label), + 'successful_save' => $this->_('Plage d\'ouverture du drive %s sauvegardée', $lib_label), + 'successful_delete' => $this->_('Plage d\'ouverture du drive %s supprimée', $lib_label)]; + + return ['successful_add' => $this->_('Plage d\'ouverture %s ajoutée', $lib_label), + 'successful_save' => $this->_('Plage d\'ouverture %s sauvegardée', $lib_label), + 'successful_delete' => $this->_('Plage d\'ouverture %s supprimée', $lib_label)]; } @@ -64,14 +69,19 @@ class ZendAfi_Controller_Plugin_ResourceDefinition_Opening extends ZendAfi_Contr $lib_label = $this->_library->getLibelle(); - return $this->_isMultimedia() - ? ['edit' => ['title' => $this->_('%s : modifier une plage horaire de réservation multimedia', $lib_label)], - 'add' => ['title' => $this->_('%s : ajouter une plage horaire de réservation multimedia', $lib_label)], - 'index' => ['title' => $this->_('%s : plages horaire de réservation multimedia', $lib_label)]] + if ($this->_getUsedFor() === Class_Ouverture::USED_FOR_MULTIMEDIA) + return ['edit' => ['title' => $this->_('%s : modifier une plage horaire de réservation multimedia', $lib_label)], + 'add' => ['title' => $this->_('%s : ajouter une plage horaire de réservation multimedia', $lib_label)], + 'index' => ['title' => $this->_('%s : plages horaire de réservation multimedia', $lib_label)]]; + + if ($this->_getUsedFor() === Class_Ouverture::USED_FOR_DRIVE) + return ['edit' => ['title' => $this->_('%s : modifier une plage d\'ouverture du drive', $lib_label)], + 'add' => ['title' => $this->_('%s : ajouter une plage d\'ouverture du drive', $lib_label)], + 'index' => ['title' => $this->_('%s : plages d\'ouverture du drive', $lib_label)]]; - : ['edit' => ['title' => $this->_('%s : modifier une plage d\'ouverture', $lib_label)], - 'add' => ['title' => $this->_('%s : ajouter une plage d\'ouverture', $lib_label)], - 'index' => ['title' => $this->_('%s : plages d\'ouverture', $lib_label)]]; + return ['edit' => ['title' => $this->_('%s : modifier une plage d\'ouverture', $lib_label)], + 'add' => ['title' => $this->_('%s : ajouter une plage d\'ouverture', $lib_label)], + 'index' => ['title' => $this->_('%s : plages d\'ouverture', $lib_label)]]; } @@ -86,14 +96,14 @@ class ZendAfi_Controller_Plugin_ResourceDefinition_Opening extends ZendAfi_Contr } - public function visitIsMultimedia($callback) { - $this->_is_multimedia_callback = $callback; + public function visitUsedFor($callback) { + $this->_used_for_callback = $callback; return $this; } - protected function _isMultimedia() { - return $this->_is_multimedia = call_user_func($this->_is_multimedia_callback); + protected function _getUsedFor() { + return call_user_func($this->_used_for_callback); } } ?> \ No newline at end of file diff --git a/library/ZendAfi/Form.php b/library/ZendAfi/Form.php index 8d589b0d6e025ee21e4e470753b7618c879dc36e..277b0a00a57935adc39a390983b4805f0e3a7d78 100644 --- a/library/ZendAfi/Form.php +++ b/library/ZendAfi/Form.php @@ -138,7 +138,7 @@ class ZendAfi_Form extends Zend_Form { } /** - * @param Storm_Model_Abstrict $model + * @param Storm_Model_Abstract $model */ public function addModelErrors($model) { $model->validate(); diff --git a/library/ZendAfi/Form/Admin/Library.php b/library/ZendAfi/Form/Admin/Library.php index efe44da536715c49dd9c885cce85dbf69f674169..6af1bc8a88bef8a2335f9733f9acd0d8725ccebe 100644 --- a/library/ZendAfi/Form/Admin/Library.php +++ b/library/ZendAfi/Form/Admin/Library.php @@ -155,7 +155,12 @@ class ZendAfi_Form_Admin_Library extends ZendAfi_Form { "1" => "oui"], ]) ->addElement('checkbox', 'notify_on_new_resa', ['label' => $this->_('Notifier la bibliothèque au dépôt d\'une nouvelle réservation')]) - ->addElement('checkbox', 'notify_on_new_user', ['label' => $this->_('Notifier la bibliothèque lors de l\'inscription d\'un nouvel utilisateur')]) + ->addElement('checkbox', 'notify_on_new_user', ['label' => $this->_('Notifier la bibliothèque lors de l\'inscription d\'un nouvel utilisateur')]); + + if (Class_AdminVar::isDriveCheckoutEnabled()) + $this->addElement('checkbox', 'enable_drive', ['label' => $this->_('Activer le drive')]); + + $this ->addDisplayGroup(['libelle', 'responsable', 'rewrite_url', @@ -164,7 +169,6 @@ class ZendAfi_Form_Admin_Library extends ZendAfi_Form { 'library', ['legend' => $this->_('Bibliothèque')]) - ->addDisplayGroup(['id_lieu'], 'location', ['legend' => $this->_('Lieu')]) @@ -195,6 +199,7 @@ class ZendAfi_Form_Admin_Library extends ZendAfi_Form { ->addDisplayGroup(['gln', 'visibilite', + 'enable_drive', 'url_web', 'interdire_resa', 'notify_on_new_resa', diff --git a/library/ZendAfi/Form/Admin/Ouverture.php b/library/ZendAfi/Form/Admin/Ouverture.php index 88ccaad739dd67e267128cfa505d512c38476037..2dc2a8b0188e61207f7b7481ba51fe3026cf7adc 100644 --- a/library/ZendAfi/Form/Admin/Ouverture.php +++ b/library/ZendAfi/Form/Admin/Ouverture.php @@ -27,8 +27,8 @@ class ZendAfi_Form_Admin_Ouverture extends ZendAfi_Form { Class_ScriptLoader::getInstance() ->addJqueryReady('formSelectToggleVisibilityForElement("#jour_semaine", "#fieldset-plage_ouverture tr:nth-child(3)", ["0"]);' . 'formSelectToggleVisibilityForElement("#jour_semaine", "#fieldset-plage_ouverture tr:nth-child(4)", ["1", "2", "3", "4", "5", "6", "7"]);' - . 'checkBoxToggleVisibilityForElement("#closed_am", $("#fieldset-plage_ouverture select[name$=\'_matin\']").parents("tr"), false);' - . 'checkBoxToggleVisibilityForElement("#closed_pm", $("#fieldset-plage_ouverture select[name$=\'_apres_midi\']").parents("tr"), false);'); + . 'checkBoxToggleVisibilityForElement("#closed_am", $("#fieldset-plage_ouverture_matin select[name$=\'_matin\']").parents("tr"), false);' + . 'checkBoxToggleVisibilityForElement("#closed_pm", $("#fieldset-plage_ouverture_apres_midi select[name$=\'_apres_midi\']").parents("tr"), false);'); $possible_hours = Class_Multimedia_Location::getPossibleHours(5); @@ -56,33 +56,51 @@ class ZendAfi_Form_Admin_Ouverture extends ZendAfi_Form { ->addElement('select', 'debut_matin', - ['label' => $this->_('Début matinée'), + ['label' => $this->_('Début'), 'multiOptions' => $possible_hours]) ->addElement('select', 'fin_matin', - ['label' => $this->_('Fin matinée'), + ['label' => $this->_('Fin'), 'multiOptions' => $possible_hours]) - ->addElement('checkbox', 'closed_am', ['label' => $this->_('Fermé matinée')]) + ->addElement('number', + 'max_per_period_matin', + ['label' => $this->_('Nombre de personnes pour %s mn', + Class_Bib_DriveOpening_Period::CHECKOUT_TIME_SLOT), + 'value' =>1]) + + ->addElement('checkbox', 'closed_am', ['label' => $this->_('Fermé')]) ->addElement('select', 'debut_apres_midi', - ['label' => $this->_('Début après-midi'), + ['label' => $this->_('Début'), 'multiOptions' => $possible_hours]) ->addElement('select', 'fin_apres_midi', - ['label' => $this->_('Fin après-midi'), + ['label' => $this->_('Fin'), 'multiOptions' => $possible_hours]) - ->addElement('checkbox', 'closed_pm', ['label' => $this->_('Fermé après-midi')]) + ->addElement('number', + 'max_per_period_apres_midi', + ['label' => $this->_('Nombre de personnes pour %s mn', + Class_Bib_DriveOpening_Period::CHECKOUT_TIME_SLOT), + 'value' => 1]) - ; + ->addElement('checkbox', 'closed_pm', ['label' => $this->_('Fermé')]); - $this->addDisplayGroup($this->getElementsNames(), + $this->addDisplayGroup(['label', 'jour_semaine', 'jour', 'validity_range'], 'plage_ouverture', - ['legend' => $this->_('Plage d\'ouverture')]); + ['legend' => $this->_('Général')]); + + $this->addDisplayGroup(['debut_matin', 'fin_matin','closed_am','max_per_period_matin'], + 'plage_ouverture_matin', + ['legend' => $this->_('Matinée')]); + + $this->addDisplayGroup(['debut_apres_midi', 'fin_apres_midi','closed_pm','max_per_period_apres_midi'], + 'plage_ouverture_apres_midi', + ['legend' => $this->_('Après-Midi')]); $isValid = function($value) { $validator = (new Class_Entity())->updateAttributes(['Valid' => true, diff --git a/library/Class/TableDescription/OpeningsLabelled.php b/library/ZendAfi/Form/Element/Date.php similarity index 75% rename from library/Class/TableDescription/OpeningsLabelled.php rename to library/ZendAfi/Form/Element/Date.php index 2cb18e9c55d5e126ddb0abdd92e1dffb600dca2f..c9500756809b5bf995e7e3f7d4e98cd0e23cbb93 100644 --- a/library/Class/TableDescription/OpeningsLabelled.php +++ b/library/ZendAfi/Form/Element/Date.php @@ -1,6 +1,6 @@ <?php /** - * Copyright (c) 2012-2019, Agence Française Informatique (AFI). All rights reserved. + * Copyright (c) 2012-2020, 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 @@ -19,10 +19,15 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +class ZendAfi_Form_Element_Date extends Zend_Form_Element_Text { + /** + * HTML5 input type = Date + * @var string + */ + public $helper = 'formDate'; + -class Class_TableDescription_OpeningsLabelled extends Class_TableDescription_Openings { public function init() { - $this->addColumn($this->_('Libellé'), 'label'); - parent::init(); + $this->addValidator(new ZendAfi_Validate_DateFormat()); } -} +} \ No newline at end of file diff --git a/library/ZendAfi/Validate/DateFormat.php b/library/ZendAfi/Validate/DateFormat.php index d5ddd78099090838f2787a465ce89d8a435c4e2f..7f97a0efd98c6aaa4b62666a19bfb77f57d73603 100644 --- a/library/ZendAfi/Validate/DateFormat.php +++ b/library/ZendAfi/Validate/DateFormat.php @@ -21,12 +21,19 @@ class ZendAfi_Validate_DateFormat extends Zend_Validate_Abstract { + protected $_format = 'Y-m-d'; - public function isValid($string, $format = 'Y-m-d') { + public function setFormat($format) { + $this->_format = $format; + return $this; + } + + + public function isValid($string) { if (!$string) return false; - $date = DateTime::createFromFormat($format, $string); - return $date && ($date->format($format) === $string); + $date = DateTime::createFromFormat($this->_format, $string); + return $date && ($date->format($this->_format) === $string); } } \ No newline at end of file diff --git a/library/ZendAfi/View/Helper/Abonne/HoldsBoard.php b/library/ZendAfi/View/Helper/Abonne/HoldsBoard.php index 62363f20e942c037ad46db748164eb2be2d893a9..49d6f2a7c9d6f99ac54a86f927fce2abd726e2d1 100644 --- a/library/ZendAfi/View/Helper/Abonne/HoldsBoard.php +++ b/library/ZendAfi/View/Helper/Abonne/HoldsBoard.php @@ -31,11 +31,17 @@ class ZendAfi_View_Helper_Abonne_HoldsBoard extends ZendAfi_View_Helper_BaseHelp $html = [$this->view->openBoiteContent($this->_('Réservations en cours')), $this->view->div(['class' => 'abonneTitre'], - $user->getNomAff()), + $user->getNomAff())]; - $this->view->abonne_ReservationsTable($cards->getHolds(), $fiche), + if (Class_AdminVar::isModuleEnabled('ENABLE_DRIVE_CHECKOUT')) + $html []= $this->view->tagAnchor(['controller' => 'drive-checkout', + 'action' => 'plan'], + $this->_('Planifier le retrait de mes documents'), + ['class' => 'checkout-plan-link']); - $this->view->closeBoiteContent()]; + $html []= $this->view->abonne_ReservationsTable($cards->getHolds(), $fiche); + + $html []= $this->view->closeBoiteContent(); if (!empty($consultations_reservations)) { $html [] = $this->view->openBoiteContent($this->_('Réservations pour la consultation sur place')); diff --git a/library/ZendAfi/View/Helper/Accueil/Base.php b/library/ZendAfi/View/Helper/Accueil/Base.php index 517378ee6fd78d52766d00c6365a83649abc7a0b..b3a9d032f572d3927a94298e9c4fa92e1bb678f2 100644 --- a/library/ZendAfi/View/Helper/Accueil/Base.php +++ b/library/ZendAfi/View/Helper/Accueil/Base.php @@ -209,7 +209,7 @@ class ZendAfi_View_Helper_Accueil_Base extends ZendAfi_View_Helper_ModuleAbstrac $profil = Class_Profil::getCurrentProfil(); - if(!array_key_exists('boite', $this->preferences) || !$this->preferences['boite']) + if(!array_key_exists('boite', $this->preferences) || !$this->preferences['boite'] || !is_string($this->preferences['boite'])) $this->preferences['boite'] = $profil->isTelephone() ? 'boite_telephone' : $style_boite[$this->getDivision()]; return $profil->getPathBoites() . $this->preferences['boite'] . '.html'; diff --git a/library/ZendAfi/View/Helper/Admin/ContentNav.php b/library/ZendAfi/View/Helper/Admin/ContentNav.php index a94a7acdb05ed16df178101a22c4b2cf8151e699..55d1333992ae3c042f9c8e5bafa2222cc8047cae 100644 --- a/library/ZendAfi/View/Helper/Admin/ContentNav.php +++ b/library/ZendAfi/View/Helper/Admin/ContentNav.php @@ -69,7 +69,8 @@ class ZendAfi_View_Helper_Admin_ContentNav extends ZendAfi_View_Helper_BaseHelpe ['newsletters', $this->_("Lettres d'information"), '/admin/newsletter'], ['trainings', $this->_('Activités'), '/admin/activity'], ['places', $this->_('Lieux'), '/admin/lieu'], - ['meeting', $this->_('Rendez-vous'), '/admin/usergroup-agenda'], + ['meeting', $this->_('Rendez-vous'), '/admin/usergroup-agenda'], + ['drive-checkout', $this->_('Drive : rendez-vous'), '/admin/drive-checkout'], ['filebrowser', $this->_('Explorateur de fichiers'), '/admin/file-manager'], ]); } diff --git a/library/ZendAfi/View/Helper/Admin/HelpLink.php b/library/ZendAfi/View/Helper/Admin/HelpLink.php index 0076068b6ece804c6b0dcd94686029d0f68bc3e6..fb1dd64c22576dc0da1b1e71eef19f45b863099f 100644 --- a/library/ZendAfi/View/Helper/Admin/HelpLink.php +++ b/library/ZendAfi/View/Helper/Admin/HelpLink.php @@ -128,6 +128,8 @@ class ZendAfi_View_Helper_Admin_HelpLinkBokehWiki { 'genre' => ['index' => 'Codification_des_genres,_emplacements,_annexes,_...'], 'emplacement' => ['index' => 'Codification_des_genres,_emplacements,_annexes,_...'], 'section' => ['index' => 'Codification_des_genres,_emplacements,_annexes,_...'], + 'drive-checkout' => ['index' => 'Gérer_les_listes_de_rendez-vous_en_mode_drive', + 'plan' => 'Prise_de_rendez-vous_par_les_professionnels'] ]; diff --git a/library/ZendAfi/View/Helper/Admin/SubscribeUsers.php b/library/ZendAfi/View/Helper/Admin/SubscribeUsers.php index 752cd3ee36bbb12e68c15c0388e81103299e9273..161c079f9efc12d0a6adf7c4553db3c8fe89fd62 100644 --- a/library/ZendAfi/View/Helper/Admin/SubscribeUsers.php +++ b/library/ZendAfi/View/Helper/Admin/SubscribeUsers.php @@ -108,7 +108,7 @@ class ZendAfi_View_Helper_Admin_SubscribeUsers extends ZendAfi_View_Helper_BaseH $user->getPrenom(), $user->getLogin(), $user->getMail(), - $this->_deleteLinkOf($user)); + $this->_editUserLink($user) . $this->_deleteLinkOf($user)); } @@ -121,6 +121,17 @@ class ZendAfi_View_Helper_Admin_SubscribeUsers extends ZendAfi_View_Helper_BaseH } + protected function _editUserLink($user) { + return $this->view->tagAnchor($this->view->url(['module' => 'admin', + 'controller' => 'users', + 'action' => 'edit', + 'id' => $user->getId()], + null, + true), + $this->view->boutonIco('type=edit')); + } + + protected function _subscribeUsersForm($search) { $users_found = Class_Users::getLoader()->findAllLike($search, $this->_by_right); diff --git a/library/ZendAfi/View/Helper/DriveCheckoutPlan.php b/library/ZendAfi/View/Helper/DriveCheckoutPlan.php new file mode 100644 index 0000000000000000000000000000000000000000..923129fba9d9c9c138580f2293046ba9e4fd60cb --- /dev/null +++ b/library/ZendAfi/View/Helper/DriveCheckoutPlan.php @@ -0,0 +1,203 @@ +<?php +/** + * Copyright (c) 2012-2020, 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_DriveCheckoutPlan extends ZendAfi_View_Helper_BaseHelper { + protected + $_plan, + $_user; + + /** + * @param $plan Class_DriveCheckout_Plan + * @return string + */ + public function driveCheckoutPlan($plan) { + if (!$plan) + return ''; + + $this->_plan = $plan; + + return + $this->view->openBoiteContent($this->_('Planifier le retrait de mes documents')) + . $this->_tag('h2', $this->_('Site de retrait')) + . $this->_libraries() + . $this->_dates() + . $this->_times() + . $this->view->closeBoiteContent(); + } + + + protected function _libraries() { + if ($library = $this->_plan->selectedLibrary()) + return $this->_selectedLibrary($library); + + if (!$this->_plan->hasLibraries()) + return $this->_('Aucun document à retirer pour l\'instant'); + + $list = $this->_plan + ->injectIntoLibraries([], + function($value, $library) + { + $value[] = $this->_library($library); + return $value; + }); + + return $this->_tag('ul', implode($list)); + } + + + protected function _selectedLibrary($library) { + return $this + ->_tag('h3', + $library->getLibelle() + . ' ' . $this->_tagAnchor($this->_plan->resetUrl(), + $this->_('modifier'), + ['title' => $this->_('retour à la liste des sites de retrait')]) + . BR . $this->_holdsInfoFor($library)); + } + + + protected function _library($library) { + if ($existing = $this->_plan->findFutureFor($library)) { + return $this + ->_tag('li', + $library->getLibelle() + . BR . $this->_('Retrait planifié %s', $existing->getDateTimeLabel()) + . ' ' + . $this->_tagAnchor(['action' => 'ical', + 'id' => $existing->getId()], + $this->_('Ajouter à mon agenda'), + ['title' => $this->_('Importer le retrait de %s pour %s au format ICal', + $library->getLibelle(), + $existing->getDateTimeLabel()), + ]) + . ' / ' + . $this->_tagAnchor(['action' => 'delete', + 'id' => $existing->getId()], + $this->_('Supprimer'), + ['title' => $this->_('Supprimer le retrait de %s pour %s', + $library->getLibelle(), + $existing->getDateTimeLabel()), + 'onclick' => 'return confirm(\''. $this->_escapeJsAttrib($this->_('Etes vous sûr de vouloir supprimer ce retrait ?')) . '\')']) + . BR . $this->_holdsInfoFor($library)); + } + + return $this + ->_tag('li', + $this->_tagAnchor($this->_plan->libraryUrlFor($library), + $library->getLibelle(), + ['title' => $this->_('Choisir de retirer les documents de %s', + $library->getLibelle())]) + . BR . $this->_holdsInfoFor($library)); + } + + + protected function _holdsInfoFor($library) { + $holds = $this->_plan->holds()->selectLibrary($library); + $total = $holds->count(); + $ready = $holds->countWaitingToBePulled(); + $not_ready = $total - $ready; + + return sprintf('%s, %s, %s', + $this->_plural($total, + 'Aucune réservation', + '%s réservation', + '%s réservations', + $total), + $this->_('%s à retirer', $ready), + $this->_('%s en attente de traitement', $not_ready)); + } + + + protected function _dates() { + if (!$this->_plan->selectedLibrary()) + return ''; + + if ($date = $this->_plan->selectedDate()) + return $this->_selectedDate($date); + + $list = $this->_plan + ->injectIntoOpenings([], + function($value, $opening) + { + $value[] = $this->_opening($opening); + return $value; + }); + + return $this->_tag('h2', $this->_('Date de retrait')) + . $this->_tag('ul', implode($list)); + } + + + protected function _opening($opening) { + $label = $this->_dateLabel($opening); + + return $this + ->_tag('li', + $this->_tagAnchor($this->_plan->dateUrlFor($opening), + $label, + ['title' => $this->_('Choisir de retirer les documets %s', + $label)])); + } + + + protected function _selectedDate($date) { + $library = $this->_plan->selectedLibrary(); + + return + $this->_tag('h2', $this->_('Date de retrait')) + . $this->_tag('h3', + $this->_dateLabel($date) + . ' ' . $this->_tagAnchor($this->_plan->libraryUrlFor($library), + $this->_('modifier'), + ['title' => $this->_('retour à la liste des dates de retrait pour %s', $library->getLibelle())])); + } + + + protected function _dateLabel($date) { + return strftime($this->_('le %A %d %b'), $date->getTimeStamp()); + } + + + protected function _times() { + if (!$this->_plan->selectedLibrary() + || (!$selected_date = $this->_plan->selectedDate())) + return ''; + + $multiOptions = $this->_plan + ->injectIntoTimes(['' => ''], + function($value, $time) + { + $value[$time->format('H:i')] = $time->format('H\hi'); + return $value; + }); + + $form = (new ZendAfi_Form(['data-backurl' => $this->view->url($this->_plan->dateUrlFor($selected_date))])) + ->addElement('select', 'checkout_time', + ['label' => $this->_('Retirer les documents à :'), + 'multiOptions' => $multiOptions]) + ->addUniqDisplayGroup('default') + ; + + return $this->_tag('h2', $this->_('Heure de retrait')) + . $this->view->renderForm($form); + } +} diff --git a/library/ZendAfi/View/Helper/FormDate.php b/library/ZendAfi/View/Helper/FormDate.php new file mode 100644 index 0000000000000000000000000000000000000000..e1951dc0cb8685eb6f9b9137688c965ed77d0c21 --- /dev/null +++ b/library/ZendAfi/View/Helper/FormDate.php @@ -0,0 +1,31 @@ +<?php +/** + * Copyright (c) 2012-2020, 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_FormDate extends ZendAfi_View_Helper_FormHTML5 { + public function formDate($name, $value = null, $attribs = null) { + return $this->renderElement($name, $value, $attribs); + } + + + public function inputType() { + return 'date'; + } +} \ No newline at end of file diff --git a/library/ZendAfi/View/Helper/LibraryOpeningsAdmin.php b/library/ZendAfi/View/Helper/LibraryOpeningsAdmin.php index 342c62553d173ed42d5abc974302c586b541729e..d746165213cba75fc3d8d573189eb63b0501f56b 100644 --- a/library/ZendAfi/View/Helper/LibraryOpeningsAdmin.php +++ b/library/ZendAfi/View/Helper/LibraryOpeningsAdmin.php @@ -22,10 +22,10 @@ class ZendAfi_View_Helper_LibraryOpeningsAdmin extends ZendAfi_View_Helper_LibraryOpenings { - protected $_multimedia; + protected $_used_for; - public function libraryOpeningsAdmin($library, $multimedia=false) { - $this->_multimedia = $multimedia; + public function libraryOpeningsAdmin($library, $used_for = null) { + $this->_used_for = $used_for; return $this->libraryOpenings($library); } @@ -34,7 +34,7 @@ class ZendAfi_View_Helper_LibraryOpeningsAdmin public function _getVisitor() { return (new Class_Ouverture_Visitor()) ->beExhaustive() - ->setMultimedia($this->_multimedia); + ->setUsedFor($this->_used_for); } @@ -72,14 +72,24 @@ class ZendAfi_View_Helper_LibraryOpeningsAdmin foreach($openings as $opening) $label = (!$label) ? $opening->getLabel() : $label; - $description = new Class_TableDescription_Openings('ouvertures'); + $description = $this->_newTableDescription(); return $this->_renderAdminSection($closure($label), $this->_renderTagModelTable($openings, $description)); } + protected function _newTableDescription() { + $table_description_class = ($this->_used_for === Class_Ouverture::USED_FOR_DRIVE) + ? Class_TableDescription_Openings_Drive::class + : Class_TableDescription_Openings::class; + + return new $table_description_class('ouvertures'); + } + + protected function _renderSectionLabelled($label, $openings) { - $description = (new Class_TableDescription_OpeningsLabelled('ouvertures')); + $description = $this->_newTableDescription() + ->prependColumn($this->_('Libellé'), 'label'); return $this->_renderAdminSection($label, $this->_renderTagModelTable($openings, $description)); diff --git a/library/ZendAfi/View/Helper/RenderLibraryOpening.php b/library/ZendAfi/View/Helper/RenderLibraryOpening.php index 01622651cbfe80a5c5bfe15b339125d1d055300f..37d5d97f65e48b339efda67e4bfc8f34fb1f8dc1 100644 --- a/library/ZendAfi/View/Helper/RenderLibraryOpening.php +++ b/library/ZendAfi/View/Helper/RenderLibraryOpening.php @@ -25,7 +25,7 @@ class ZendAfi_View_Helper_RenderLibraryOpening extends ZendAfi_View_Helper_BaseH public function renderLibraryOpening($library) { - if (!$library->hasOuvertures() || $library->hasHoraire()) + if (!$library->hasOuverturesFor(Class_Ouverture::USED_FOR_LIBRARY) || $library->hasHoraire()) return ''; return $this->renderOuverturesForLibrary($library); diff --git a/library/storm b/library/storm index 7aaada2df1e203d1fb50685dafe371de1ac034a5..67e32fa4f2114f69c7a3d9b96d9419186cb2e7bf 160000 --- a/library/storm +++ b/library/storm @@ -1 +1 @@ -Subproject commit 7aaada2df1e203d1fb50685dafe371de1ac034a5 +Subproject commit 67e32fa4f2114f69c7a3d9b96d9419186cb2e7bf diff --git a/library/templates/Intonation/Library/View/Wrapper/DriveCheckoutPlan.php b/library/templates/Intonation/Library/View/Wrapper/DriveCheckoutPlan.php new file mode 100644 index 0000000000000000000000000000000000000000..1340b09a17ae29d5003235d0736bcb1ef415c81f --- /dev/null +++ b/library/templates/Intonation/Library/View/Wrapper/DriveCheckoutPlan.php @@ -0,0 +1,109 @@ +<?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 Intonation_Library_View_Wrapper_DriveCheckoutPlan + extends Intonation_Library_View_Wrapper_Abstract { + + public function getMainTitle() { + return $this->_('Planifier le retrait de mes documents'); + } + + + public function getSecondaryTitle() { + return ''; + } + + + public function getDocType() { + return ''; + } + + + public function getDocTypeLabel() { + return ''; + } + + + public function getMainLink() { + return; + } + + + public function getPicture() { + return ''; + } + + + public function getPictureAction() { + return ''; + } + + + public function getFullDescription() { + return ''; + } + + + public function getDescription() { + return ''; + } + + + public function getDescriptionTitle() { + return ''; + } + + + public function getSecondaryIco() { + return; + } + + + public function getSecondaryLink() { + return; + } + + + public function getBadges() { + return '';; + } + + + public function getActions() { + return []; + } + + + public function getEmbedMedia() { + return ''; + } + + + public function getHtmlPicture() { + return ''; + } + + + public function getOsmData() { + return null; + } +} \ No newline at end of file diff --git a/library/templates/Intonation/Library/View/Wrapper/DriveCheckoutPlan/Library.php b/library/templates/Intonation/Library/View/Wrapper/DriveCheckoutPlan/Library.php new file mode 100644 index 0000000000000000000000000000000000000000..7363e127c044fc983e62843f607c8921bd3c5ef8 --- /dev/null +++ b/library/templates/Intonation/Library/View/Wrapper/DriveCheckoutPlan/Library.php @@ -0,0 +1,163 @@ +<?php +/** + * Copyright (c) 2012-2020, 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 Intonation_Library_View_Wrapper_DriveCheckoutPlan_Library + extends Intonation_Library_View_Wrapper_Library { + + protected + $_plan, + $_existing; + + public function setPlan($plan) { + $this->_plan = $plan; + return $this; + } + + + public function getMainLink() { + return; + } + + + public function getSecondaryLink() { + return $this->_getExisting() + ? null + : new Intonation_Library_Link(['Url' => $this->_view->url($this->_plan->libraryUrlFor($this->_model)), + 'Image' => Class_Template::current()->getIco($this->_view, + 'available', + 'library'), + + 'Text' => $this->_('Choisir ce site'), + 'Title' => $this->_('Choisir de retirer les documents de %s', + $this->_model->getLibelle())]); + } + + + public function getActions() { + return ($existing = $this->_getExisting()) + ? [new Intonation_Library_Link(['Url' => $this->_view->url(['action' => 'ical', + 'id' => $existing->getId()]), + 'Image' => Class_Template::current()->getIco($this->_view, + 'extend-loan', + 'library'), + + 'Text' => $this->_('Ajouter à mon agenda'), + 'Title' => $this->_('Importer le retrait de %s pour %s au format ICal', + $this->_model->getLibelle(), + $existing->getDateTimeLabel()), + ]), + new Intonation_Library_Link(['Url' => $this->_view->url(['action' => 'delete', + 'id' => $existing->getId()]), + 'Image' => Class_Template::current()->getIco($this->_view, + 'clean', + 'utils'), + + 'Text' => $this->_('Supprimer'), + 'Title' => $this->_('Supprimer le retrait de %s pour %s', + $this->_model->getLibelle(), + $existing->getDateTimeLabel()), + 'Class' => 'text-danger', + 'Attribs' => ['onclick' => $this->_view->confirm($this->_('Etes vous sûr de vouloir supprimer ce retrait ?'))]])] + : []; + } + + + public function getDescription() { + $existing = ($existing = $this->_getExisting()) + ? $this->_view->tag('p', + Class_Template::current()->getIco($this->_view, 'available', 'library') + . ' ' + . $this->_('Retrait planifié %s', $existing->getDateTimeLabel()), + ['class' => 'text-success']) + : ''; + + return $existing . $this->getBadges(); + } + + + public function getBadges() { + return + $this->_view->renderBadges($this->_holdsBadges(), $this) + . $this->_view->renderBadges($this->_localFieldsBadges(), $this); + } + + + protected function _holdsBadges() { + $holds = $this->_plan->holds()->selectLibrary($this->_model); + $total = $holds->count(); + $ready = $holds->countWaitingToBePulled(); + $not_ready = $total - $ready; + + return [ + ((new Intonation_Library_Badge) + ->setTag('span') + ->setClass('primary') + ->setText($this->_plural($total, + 'Aucune réservation', + '%s réservation', + '%s réservations', + $total)) + ->setImage(Class_Template::current()->getIco($this->_view, + 'loan', + 'library')) + ->setTitle($this->_plural($total, + 'Aucun document réservé', + '%s document réservé', + '%s documents réservés', + $total))), + + ((new Intonation_Library_Badge) + ->setTag('span') + ->setClass('success') + ->setText($this->_('%s à retirer', $ready)) + ->setImage(Class_Template::current()->getIco($this->_view, + 'loan', + 'library')) + ->setTitle($this->_plural($ready, + 'Aucun document ne peut être retiré', + '%s document peut être retiré', + '%s documents peuvent être retirés', + $ready))), + + ((new Intonation_Library_Badge) + ->setTag('span') + ->setClass('secondary') + ->setText($this->_('%s en attente', $not_ready)) + ->setImage(Class_Template::current()->getIco($this->_view, + 'loan', + 'library')) + ->setTitle($this->_plural($not_ready, + 'Aucun document en attente de traitement', + '%s document est en attente de traitement', + '%s documents sont en attente de traitement', + $not_ready))), + ]; + } + + + protected function _getExisting() { + if ($this->_existing) + return $this->_existing; + + return $this->_existing = $this->_plan->findFutureFor($this->_model); + } +} diff --git a/library/templates/Intonation/Library/View/Wrapper/DriveCheckoutPlan/LibrarySelected.php b/library/templates/Intonation/Library/View/Wrapper/DriveCheckoutPlan/LibrarySelected.php new file mode 100644 index 0000000000000000000000000000000000000000..f1dbc107e8381a5386833688e2d3b72b0b374abf --- /dev/null +++ b/library/templates/Intonation/Library/View/Wrapper/DriveCheckoutPlan/LibrarySelected.php @@ -0,0 +1,37 @@ +<?php +/** + * Copyright (c) 2012-2020, 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 Intonation_Library_View_Wrapper_DriveCheckoutPlan_LibrarySelected + extends Intonation_Library_View_Wrapper_DriveCheckoutPlan_Library { + + + public function getSecondaryLink() { + return new Intonation_Library_Link(['Url' => $this->_view->url($this->_plan->resetUrl()), + 'Image' => Class_Template::current()->getIco($this->_view, + 'refresh', + 'utils'), + + 'Text' => $this->_('Choisir un autre site'), + 'Title' => $this->_('Retourner à la liste des sites de retrait'), + 'Class' => 'text-danger']); + } +} diff --git a/library/templates/Intonation/Library/View/Wrapper/DriveCheckoutPlan/RichContent.php b/library/templates/Intonation/Library/View/Wrapper/DriveCheckoutPlan/RichContent.php new file mode 100644 index 0000000000000000000000000000000000000000..02d3aab0668746ce619f80db7cb771298ac1ef9b --- /dev/null +++ b/library/templates/Intonation/Library/View/Wrapper/DriveCheckoutPlan/RichContent.php @@ -0,0 +1,51 @@ +<?php +/** + * Copyright (c) 2012-2019, 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 Intonation_Library_View_Wrapper_DriveCheckoutPlan_RichContent + extends Intonation_Library_View_Wrapper_RichContent_Abstract { + + public function getNavigation() { + return; + } + + + public function getActions() { + return ''; + } + + + public function getRowActions() { + return ''; + } + + + protected function _getSectionsInstances() { + return [new Intonation_Library_View_Wrapper_DriveCheckoutPlan_RichContent_Library, + new Intonation_Library_View_Wrapper_DriveCheckoutPlan_RichContent_Date, + new Intonation_Library_View_Wrapper_DriveCheckoutPlan_RichContent_Time]; + } + + + protected function _getWrapper() { + return 'Intonation_Library_View_Wrapper_DriveCheckoutPlan'; + } +} \ No newline at end of file diff --git a/library/templates/Intonation/Library/View/Wrapper/DriveCheckoutPlan/RichContent/Date.php b/library/templates/Intonation/Library/View/Wrapper/DriveCheckoutPlan/RichContent/Date.php new file mode 100644 index 0000000000000000000000000000000000000000..13116fca5be5c171312d612c591b925d8032d912 --- /dev/null +++ b/library/templates/Intonation/Library/View/Wrapper/DriveCheckoutPlan/RichContent/Date.php @@ -0,0 +1,121 @@ +<?php +/** + * Copyright (c) 2012-2020, 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 Intonation_Library_View_Wrapper_DriveCheckoutPlan_RichContent_Date + extends Intonation_Library_View_Wrapper_RichContent_Section{ + + public function getTitle() { + return $this->_('Date de retrait'); + } + + + public function getContent() { + if ($this->_content) + return $this->_content; + + if (!$this->_model->selectedLibrary()) + return $this->_content = ''; + + $html = $this->_getDates()->injectInto('', [$this, 'renderDate']); + + return $this->_content = $this->_view->tag('ul', $html, ['class' => 'list-group']); + } + + + public function _getDates() { + return ($selected = $this->_model->selectedDate()) + ? new Storm_Model_Collection([$selected]) + : $this->_model->injectIntoOpenings(new Storm_Model_Collection(), + function($collection, $library) + { + $collection->append($library); + return $collection; + }); + } + + + public function renderDate($html, $date) { + return $html . $this->_view->tag('li', + ($this->_model->selectedDate() == $date) + ? $this->_renderSelectedDate($date) + : $this->_renderDate($date), + ['class' => 'list-group-item']); + } + + + protected function _renderDate($date) { + $label = $this->_dateLabel($date); + $action = new Intonation_Library_Link(['Url' => $this->_view->url($this->_model->dateUrlFor($date)), + 'Image' => Class_Template::current()->getIco($this->_view, + 'available', + 'library'), + + 'Text' => $label, + 'Title' => $this->_('Choisir de retirer les documents %s', + $label)]); + + return $this->_view->tagAction($action); + } + + + protected function _renderSelectedDate($date) { + $label = $this->_dateLabel($date); + $action = new Intonation_Library_Link(['Url' => $this->_view->url($this->getNavUrl()), + 'Image' => Class_Template::current()->getIco($this->_view, + 'refresh', + 'utils'), + + 'Text' => $this->_('Choisir une autre date'), + 'Title' => $this->_('Retourner à la liste des dates de retrait pour %s', + $this->_model->selectedLibrary()->getLibelle()), + 'Class' => 'text-danger']); + + return $label . BR . $this->_view->tagAction($action); + } + + + protected function _dateLabel($date) { + return strftime($this->_('le %A %d %b'), $date->getTimeStamp()); + } + + + public function getClass() { + return 'drivecheckout_plan_date'; + } + + + public function getNavUrl() { + return ($library = $this->_model->selectedLibrary()) + ? $this->_model->libraryUrlFor($library) + : []; + } + + + public function getNavIco() { + return 'class fas fa-calendar-alt'; + } + + + public function getNavTitle() { + return $this->_('Choisir une date de retrait'); + } +} diff --git a/library/templates/Intonation/Library/View/Wrapper/DriveCheckoutPlan/RichContent/Library.php b/library/templates/Intonation/Library/View/Wrapper/DriveCheckoutPlan/RichContent/Library.php new file mode 100644 index 0000000000000000000000000000000000000000..950046b7296e3d3ac35099005e5159f33f6b5542 --- /dev/null +++ b/library/templates/Intonation/Library/View/Wrapper/DriveCheckoutPlan/RichContent/Library.php @@ -0,0 +1,92 @@ +<?php +/** + * Copyright (c) 2012-2020, 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 Intonation_Library_View_Wrapper_DriveCheckoutPlan_RichContent_Library + extends Intonation_Library_View_Wrapper_RichContent_Section{ + + public function getTitle() { + return $this->_('Site de retrait'); + } + + + public function getContent() { + if ($this->_content) + return $this->_content; + + $librairies = $this->_getLibraries(); + + if ($librairies->isEmpty()) + return $this->_content = $this->_view + ->tag('p', $this->_('Aucun document à retirer pour l\'instant')); + + $wrapper_class = $this->_wrapperClass(); + + return $this->_content = $this->_view + ->renderList($librairies, + function($library) use($wrapper_class) + { + return $this->_view->cardifyHorizontal((new $wrapper_class()) + ->setPlan($this->_model) + ->setModel($library) + ->setView($this->_view)); + }); + } + + + protected function _wrapperClass() { + return $this->_model->selectedLibrary() + ? 'Intonation_Library_View_Wrapper_DriveCheckoutPlan_LibrarySelected' + : 'Intonation_Library_View_Wrapper_DriveCheckoutPlan_Library'; + } + + + public function _getLibraries() { + return ($selected = $this->_model->selectedLibrary()) + ? new Storm_Model_Collection([$selected]) + : $this->_model->injectIntoLibraries(new Storm_Model_Collection(), + function($collection, $library) + { + $collection->append($library); + return $collection; + }); + } + + + public function getClass() { + return 'drivecheckout_plan_library'; + } + + + public function getNavUrl() { + return $this->_model->resetUrl(); + } + + + public function getNavIco() { + return 'class fas fa-map-marker-alt'; + } + + + public function getNavTitle() { + return $this->_('Choisir un site de retrait'); + } +} diff --git a/library/templates/Intonation/Library/View/Wrapper/DriveCheckoutPlan/RichContent/Time.php b/library/templates/Intonation/Library/View/Wrapper/DriveCheckoutPlan/RichContent/Time.php new file mode 100644 index 0000000000000000000000000000000000000000..a142bed07c130a6e0fc33ec305454362370ef1b6 --- /dev/null +++ b/library/templates/Intonation/Library/View/Wrapper/DriveCheckoutPlan/RichContent/Time.php @@ -0,0 +1,77 @@ +<?php +/** + * Copyright (c) 2012-2020, 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 Intonation_Library_View_Wrapper_DriveCheckoutPlan_RichContent_Time + extends Intonation_Library_View_Wrapper_RichContent_Section{ + + public function getTitle() { + return $this->_('Heure de retrait'); + } + + + public function getContent() { + if ($this->_content) + return $this->_content; + + if (!$this->_model->selectedDate()) + return $this->_content = ''; + + $multiOptions = $this->_model + ->injectIntoTimes(['' => ''], + function($value, $time) + { + $value[$time->format('H:i')] = $time->format('H\hi'); + return $value; + }); + + $form = (new ZendAfi_Form(['data-backurl' => $this->_view->url($this->getNavUrl())])) + ->addElement('select', 'checkout_time', + ['label' => $this->_('Retirer les documents à :'), + 'multiOptions' => $multiOptions]) + ->addUniqDisplayGroup('default') + ; + + return $this->_content = $this->_view->renderForm($form); + } + + + public function getClass() { + return 'drivecheckout_plan_time'; + } + + + public function getNavUrl() { + return ($date= $this->_model->selectedDate()) + ? $this->_model->dateUrlFor($date) + : []; + } + + + public function getNavIco() { + return 'class fas fa-clock'; + } + + + public function getNavTitle() { + return $this->_('Choisir une heure de retrait'); + } +} diff --git a/library/templates/Intonation/Library/View/Wrapper/Library.php b/library/templates/Intonation/Library/View/Wrapper/Library.php index 80e62075b22bede90dbaca909fc993bebd1e7f7e..21800425ef8328e8679cec16764315477c0f7207 100644 --- a/library/templates/Intonation/Library/View/Wrapper/Library.php +++ b/library/templates/Intonation/Library/View/Wrapper/Library.php @@ -106,56 +106,7 @@ class Intonation_Library_View_Wrapper_Library extends Intonation_Library_View_Wr public function getBadges() { - $badges = [ - ((new Intonation_Library_Badge) - ->setTag('span') - ->setClass('secondary') - ->setText($this->_model->getAdresse()) - ->setTitle($this->_('%s adresse de la bibliothèque %s', - $this->_model->getAdresse(), - $this->_model->getLibelle()))), - - ((new Intonation_Library_Badge) - ->setTag('span') - ->setClass('secondary') - ->setText($this->_model->getCp()) - ->setTitle($this->_('%s code postale de la bibliothèque %s', - $this->_model->getCp(), - $this->_model->getLibelle()))), - - ((new Intonation_Library_Badge) - ->setTag('span') - ->setClass('secondary') - ->setText($this->_model->getVille()) - ->setTitle($this->_('%s ville de la bibliothèque %s', - $this->_model->getVille(), - $this->_model->getLibelle()))), - - ((new Intonation_Library_Badge) - ->setTag('a') - ->setClass('primary text-light') - ->setText(str_replace([' ', '.', ','], ' ', $this->_model->getTelephone())) - ->setUrl(sprintf('tel:%s', - str_replace([' ', '.', ',',], '', $this->_model->getTelephone()))) - ->setImage(Class_Template::current()->getIco($this->_view, - 'phone', - 'utils')) - ->setTitle($this->_('%s numéro de téléphone de la bibliothèque %s', - $this->_model->getTelephone(), - $this->_model->getLibelle()))), - - ((new Intonation_Library_Badge) - ->setTag('a') - ->setClass('primary text-light') - ->setUrl(sprintf('mailto:%s', $this->_model->getMail())) - ->setImage(Class_Template::current()->getIco($this->_view, - 'email', - 'utils')) - ->setText($this->_model->getMail()) - ->setTitle($this->_('%s adresse e-mail de la bibliothèque %s', - $this->_model->getMail(), - $this->_model->getLibelle()))) - ]; + $badges = $this->_localFieldsBadges(); foreach($this->_model->getAllCustomFields()->getBadgeableFieldValues() as $field) { $label = $field->getLabel(); @@ -179,6 +130,60 @@ class Intonation_Library_View_Wrapper_Library extends Intonation_Library_View_Wr } + protected function _localFieldsBadges() { + return [ + ((new Intonation_Library_Badge) + ->setTag('span') + ->setClass('secondary') + ->setText($this->_model->getAdresse()) + ->setTitle($this->_('%s adresse de la bibliothèque %s', + $this->_model->getAdresse(), + $this->_model->getLibelle()))), + + ((new Intonation_Library_Badge) + ->setTag('span') + ->setClass('secondary') + ->setText($this->_model->getCp()) + ->setTitle($this->_('%s code postale de la bibliothèque %s', + $this->_model->getCp(), + $this->_model->getLibelle()))), + + ((new Intonation_Library_Badge) + ->setTag('span') + ->setClass('secondary') + ->setText($this->_model->getVille()) + ->setTitle($this->_('%s ville de la bibliothèque %s', + $this->_model->getVille(), + $this->_model->getLibelle()))), + + ((new Intonation_Library_Badge) + ->setTag('a') + ->setClass('primary text-light') + ->setText(str_replace([' ', '.', ','], ' ', $this->_model->getTelephone())) + ->setUrl(sprintf('tel:%s', + str_replace([' ', '.', ',',], '', $this->_model->getTelephone()))) + ->setImage(Class_Template::current()->getIco($this->_view, + 'phone', + 'utils')) + ->setTitle($this->_('%s numéro de téléphone de la bibliothèque %s', + $this->_model->getTelephone(), + $this->_model->getLibelle()))), + + ((new Intonation_Library_Badge) + ->setTag('a') + ->setClass('primary text-light') + ->setUrl(sprintf('mailto:%s', $this->_model->getMail())) + ->setImage(Class_Template::current()->getIco($this->_view, + 'email', + 'utils')) + ->setText($this->_model->getMail()) + ->setTitle($this->_('%s adresse e-mail de la bibliothèque %s', + $this->_model->getMail(), + $this->_model->getLibelle()))) + ]; + } + + public function getActions() { return []; } diff --git a/library/templates/Intonation/Library/View/Wrapper/SearchHistory.php b/library/templates/Intonation/Library/View/Wrapper/SearchHistory.php index c44c9640d88eee17a13c49c3f81c071c32ac63b8..549bb162e74339e5c61c10f26412fcbe73eb4b2c 100644 --- a/library/templates/Intonation/Library/View/Wrapper/SearchHistory.php +++ b/library/templates/Intonation/Library/View/Wrapper/SearchHistory.php @@ -23,7 +23,8 @@ class Intonation_Library_View_Wrapper_SearchHistory extends Intonation_Library_View_Wrapper_Search { public function getMainLink() { - return reset(parent::getActions()); + $actions = parent::getActions(); + return reset($actions); } diff --git a/library/templates/Intonation/System/Abstract.php b/library/templates/Intonation/System/Abstract.php index a493477b9b77cf6f0f4400421226ba4250cb3b6f..2e9ffbfc1918c4a15e367ab883366d3187ce3fe6 100644 --- a/library/templates/Intonation/System/Abstract.php +++ b/library/templates/Intonation/System/Abstract.php @@ -191,8 +191,10 @@ abstract class Intonation_System_Abstract { $controllers [] = 'widget'; } - if($this->isVisibleForUserController()) + if($this->isVisibleForUserController()) { $controllers [] = 'abonne'; + $controllers [] = 'drive-checkout'; + } if($this->isVisibleForCmsController()) { $controllers [] = 'cms'; diff --git a/library/templates/Intonation/View/Abonne/Holds.php b/library/templates/Intonation/View/Abonne/Holds.php index c412502edb3007c1e9da2035fd74dde760e4caf8..6b2b388f4cb767c2b88efa1a0ab8d7bad60948aa 100644 --- a/library/templates/Intonation/View/Abonne/Holds.php +++ b/library/templates/Intonation/View/Abonne/Holds.php @@ -36,6 +36,16 @@ class Intonation_View_Abonne_Holds extends ZendAfi_View_Helper_BaseHelper { return $this->view->cardifyHorizontal($wrapped); }; - return $this->view->renderTruncateList(new Storm_Collection($holds), $callback); + $actions = Class_AdminVar::isModuleEnabled('ENABLE_DRIVE_CHECKOUT') + ? [new Intonation_Library_Link(['Url' => $this->view->url(['controller' => 'drive-checkout', + 'action' => 'plan']), + 'Text' => $this->_('Planifier le retrait de mes documents'), + 'Title' => $this->_('Prendre ou lister mes rendez-vous pour le retrait de mes documents'), + 'Image' => Class_Template::current()->getIco($this->view, + 'agenda', + 'library')])] + : []; + + return $this->view->renderCollection(new Storm_Collection($holds), $actions); } } diff --git a/library/templates/Intonation/View/DriveCheckoutPlan.php b/library/templates/Intonation/View/DriveCheckoutPlan.php new file mode 100644 index 0000000000000000000000000000000000000000..1b55a8767737a021b9d41fa76f638b9b3e120536 --- /dev/null +++ b/library/templates/Intonation/View/DriveCheckoutPlan.php @@ -0,0 +1,37 @@ +<?php +/** + * Copyright (c) 2012-2020, 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 Intonation_View_DriveCheckoutPlan extends Intonation_View_Jumbotron_Abstract { + public function driveCheckoutPlan($plan) { + return $this->_core($plan); + } + + + protected function _getWrappedInstance() { + return new Intonation_Library_View_Wrapper_DriveCheckoutPlan; + } + + + protected function _getRichContentInstance() { + return new Intonation_Library_View_Wrapper_DriveCheckoutPlan_RichContent; + } +} diff --git a/public/admin/skins/bokeh72/config.json b/public/admin/skins/bokeh72/config.json index 41a3cc44d5738ea38f6d436ba8d5e62e1442b2f4..fc165ddf0473a110f1b36d8289f27ea115731494 100644 --- a/public/admin/skins/bokeh72/config.json +++ b/public/admin/skins/bokeh72/config.json @@ -67,7 +67,8 @@ "books": "../../images/picto/books.png", "tag": "../../images/picto/tag_blue.png", "suggestion": "../../images/picto/traductions_16.png", - "meeting": "../../images/picto/meeting_24.png" + "meeting": "../../images/picto/meeting_24.png", + "drive-checkout": "../../images/picto/paniers_16.png" }, "actions": @@ -116,6 +117,7 @@ "images": "../../images/ico/album_images.png", "test": "../../images/ico/tester.gif", "basket": "../../images/picto/paniers_16.png", + "shopping": "../../images/picto/paniers_16.png", "permalink": "../../images/reseaux/permalink.gif", "mail": "../../images/ico/mail.png", "validate": "../../images/ico/coche_verte.gif", diff --git a/public/admin/skins/bokeh74/config.json b/public/admin/skins/bokeh74/config.json index 7a2989a908c7f233d9585865803ce11fe4ec0656..15f4733bbca3648f3de91558bbd5ec31057ba23b 100644 --- a/public/admin/skins/bokeh74/config.json +++ b/public/admin/skins/bokeh74/config.json @@ -72,7 +72,8 @@ "tag": "icons/menu/tag_24.png", "suggestion": "icons/menu/suggestion_achat_24.png", "meeting": "icons/menu/meeting_24.png", - "identity_providers": "icons/menu/demande_inscri_24.png" + "identity_providers": "icons/menu/demande_inscri_24.png", + "drive-checkout": "icons/menu/shopping_24.png" }, "actions": @@ -121,6 +122,7 @@ "images": "icons/actions/album_images_16.png", "test": "icons/actions/tester_16.png", "basket": "icons/actions/panier_24.png", + "shopping": "icons/actions/shopping_16.png", "permalink": "icons/actions/permalink_16.png", "mail": "icons/actions/mail_16.png", "validate": "icons/actions/coche_16.png", diff --git a/public/admin/skins/bokeh74/icons/actions/shopping_16.png b/public/admin/skins/bokeh74/icons/actions/shopping_16.png new file mode 100644 index 0000000000000000000000000000000000000000..8f758bb4d49dd52f6478701e6b1ba3139cf288aa Binary files /dev/null and b/public/admin/skins/bokeh74/icons/actions/shopping_16.png differ diff --git a/public/admin/skins/bokeh74/icons/actions/shopping_24.png b/public/admin/skins/bokeh74/icons/actions/shopping_24.png new file mode 100644 index 0000000000000000000000000000000000000000..c40bbe2426f9d5a91eeba0064bafdee7661fa410 Binary files /dev/null and b/public/admin/skins/bokeh74/icons/actions/shopping_24.png differ diff --git a/public/admin/skins/bokeh74/icons/menu/shopping_24.png b/public/admin/skins/bokeh74/icons/menu/shopping_24.png new file mode 100644 index 0000000000000000000000000000000000000000..c40bbe2426f9d5a91eeba0064bafdee7661fa410 Binary files /dev/null and b/public/admin/skins/bokeh74/icons/menu/shopping_24.png differ diff --git a/public/admin/skins/bokeh74/icons/menu/shopping_48.png b/public/admin/skins/bokeh74/icons/menu/shopping_48.png new file mode 100644 index 0000000000000000000000000000000000000000..90fcb8974d22112d483f92fd8ef6ea33f1afa60c Binary files /dev/null and b/public/admin/skins/bokeh74/icons/menu/shopping_48.png differ diff --git a/public/admin/skins/noel/config.json b/public/admin/skins/noel/config.json index 01e9f770ac8c205d90b48117344f5e64d9242ca3..ce45549444fd15e70dab9ac680d0f6e5844ea0c6 100644 --- a/public/admin/skins/noel/config.json +++ b/public/admin/skins/noel/config.json @@ -70,7 +70,8 @@ "books": "icons/menu/books_24.png", "tag": "icons/menu/tag_24.png", "suggestion": "icons/menu/suggestion_achat_24.png", - "meeting": "icons/menu/meeting_24.png" + "meeting": "icons/menu/meeting_24.png", + "drive-checkout": "icons/menu/shopping_24.png" }, "actions": @@ -119,6 +120,7 @@ "images": "icons/actions/album_images_16.png", "test": "icons/actions/tester_16.png", "basket": "icons/actions/panier_24.png", + "shopping": "icons/actions/shopping_16.png", "permalink": "icons/actions/permalink_16.png", "mail": "icons/actions/mail_16.png", "validate": "icons/actions/coche_16.png", diff --git a/public/admin/skins/noel/icons/actions/shopping_16.png b/public/admin/skins/noel/icons/actions/shopping_16.png new file mode 100644 index 0000000000000000000000000000000000000000..8f758bb4d49dd52f6478701e6b1ba3139cf288aa Binary files /dev/null and b/public/admin/skins/noel/icons/actions/shopping_16.png differ diff --git a/public/admin/skins/noel/icons/actions/shopping_24.png b/public/admin/skins/noel/icons/actions/shopping_24.png new file mode 100644 index 0000000000000000000000000000000000000000..c40bbe2426f9d5a91eeba0064bafdee7661fa410 Binary files /dev/null and b/public/admin/skins/noel/icons/actions/shopping_24.png differ diff --git a/public/admin/skins/noel/icons/menu/shopping_24.png b/public/admin/skins/noel/icons/menu/shopping_24.png new file mode 100644 index 0000000000000000000000000000000000000000..c40bbe2426f9d5a91eeba0064bafdee7661fa410 Binary files /dev/null and b/public/admin/skins/noel/icons/menu/shopping_24.png differ diff --git a/public/admin/skins/noel/icons/menu/shopping_48.png b/public/admin/skins/noel/icons/menu/shopping_48.png new file mode 100644 index 0000000000000000000000000000000000000000..90fcb8974d22112d483f92fd8ef6ea33f1afa60c Binary files /dev/null and b/public/admin/skins/noel/icons/menu/shopping_48.png differ diff --git a/public/admin/skins/retro/config.json b/public/admin/skins/retro/config.json index 140ff78b5c2b7b1fadd6e488560b2b465a2ccc48..91889c2c808c2a7552bd5a72664bcd2fb4d7da21 100644 --- a/public/admin/skins/retro/config.json +++ b/public/admin/skins/retro/config.json @@ -70,7 +70,8 @@ "books": "icons/menu/books_24.png", "tag": "icons/menu/tag_24.png", "suggestion": "icons/menu/suggestion_achat_24.png", - "meeting": "icons/menu/meeting_24.png" + "meeting": "icons/menu/meeting_24.png", + "drive-checkout": "icons/menu/shopping_24.png" }, "actions": @@ -120,6 +121,8 @@ "images": "icons/actions/album_images_16.png", "test": "icons/actions/tester_16.png", "basket": "icons/actions/panier_16.png", + "shopping": "icons/actions/shopping_16.png", + "shopping": "icons/actions/shopping_16.png", "permalink": "icons/actions/permalink_16.png", "mail": "icons/actions/mail_16.png", "validate": "icons/actions/coche_16.png", diff --git a/public/admin/skins/retro/icons/actions/shopping_16.png b/public/admin/skins/retro/icons/actions/shopping_16.png new file mode 100644 index 0000000000000000000000000000000000000000..8f758bb4d49dd52f6478701e6b1ba3139cf288aa Binary files /dev/null and b/public/admin/skins/retro/icons/actions/shopping_16.png differ diff --git a/public/admin/skins/retro/icons/menu/shopping_24.png b/public/admin/skins/retro/icons/menu/shopping_24.png new file mode 100644 index 0000000000000000000000000000000000000000..c40bbe2426f9d5a91eeba0064bafdee7661fa410 Binary files /dev/null and b/public/admin/skins/retro/icons/menu/shopping_24.png differ diff --git a/public/admin/skins/retro/icons/menu/shopping_48.png b/public/admin/skins/retro/icons/menu/shopping_48.png new file mode 100644 index 0000000000000000000000000000000000000000..90fcb8974d22112d483f92fd8ef6ea33f1afa60c Binary files /dev/null and b/public/admin/skins/retro/icons/menu/shopping_48.png differ diff --git a/tests/application/modules/admin/controllers/BibControllerTest.php b/tests/application/modules/admin/controllers/BibControllerTest.php index 5dfb73001d9d649ea190b4ea3fbd092755fa59b7..bfe6f4e34da1ac5ec804189cbedd77f7ab7ee8b9 100644 --- a/tests/application/modules/admin/controllers/BibControllerTest.php +++ b/tests/application/modules/admin/controllers/BibControllerTest.php @@ -195,7 +195,7 @@ class BibControllerIndexWidthAdminPortailWithMultimediaTest extends BibControlle /** @test */ public function shouldHaveActionToOuverturesForMultimedia() { - $this->assertXPath('//tr[1]//a[contains(@href, "ouvertures/index/id_site/2/multimedia/1")]'); + $this->assertXPath('//tr[1]//a[contains(@href, "ouvertures/index/id_site/2/used_for/1")]'); } } diff --git a/tests/application/modules/admin/controllers/OuverturesControllerTest.php b/tests/application/modules/admin/controllers/OuverturesControllerTest.php index a9cfa65dc5cea84066b2a8d982011a9594ae9983..9e88dbe36917a03d20aec7131590c97908b8c51e 100644 --- a/tests/application/modules/admin/controllers/OuverturesControllerTest.php +++ b/tests/application/modules/admin/controllers/OuverturesControllerTest.php @@ -257,7 +257,7 @@ class OuverturesControllerIndexActionSiteCranMultimediaEnabledTest extends Ouver parent::setUp(); Class_AdminVar::set('MULTIMEDIA_KEY', 'SECRET_KEY'); - $this->dispatch('/admin/ouvertures/index/id_site/1/multimedia/1', true); + $this->dispatch('/admin/ouvertures/index/id_site/1/used_for/1', true); } @@ -269,7 +269,7 @@ class OuverturesControllerIndexActionSiteCranMultimediaEnabledTest extends Ouver /** @test */ public function addButtonLabelShouldBeAjouterUnePlageHoraire(){ - $this->assertXPathContentContains('//button','Ajouter une plage horaire de réservation multimedia'); + $this->assertXPathContentContains('//button','Ajouter une plage horaire de réservation multimédia'); } /** @test */ @@ -283,7 +283,7 @@ class OuverturesControllerIndexActionSiteCranMultimediaEnabledTest extends Ouver class OuverturesControllerIndexActionSiteCranMultimediaDisabledTest extends OuverturesControllerTestCase { public function setUp() { parent::setUp(); - $this->dispatch('/admin/ouvertures/index/id_site/1/multimedia/1', true); + $this->dispatch('/admin/ouvertures/index/id_site/1/used_for/1', true); } @@ -328,9 +328,9 @@ class OuverturesControllerPostIndexActionSiteCranTest extends OuverturesControll class OuverturesControllerPostIndexActionSiteCranWithMultimediaParamTest extends OuverturesControllerTestCase { public function setUp() { parent::setUp(); - $_SERVER['HTTP_REFERER'] = '/admin/ouvertures/index/id_site/1/multimedia/1'; + $_SERVER['HTTP_REFERER'] = '/admin/ouvertures/index/id_site/1/used_for/1'; Class_AdminVar::set('MULTIMEDIA_KEY', 'SECRET_KEY'); - $this->postDispatch('/admin/ouvertures/index/id_site/1/multimedia/1', + $this->postDispatch('/admin/ouvertures/index/id_site/1/used_for/1', ['closed_on_holidays' => '1']); Class_Bib::clearCache(); } @@ -338,7 +338,7 @@ class OuverturesControllerPostIndexActionSiteCranWithMultimediaParamTest extends /** @test */ public function responseShouldRedirectToIndexIdSiteOne() { - $this->assertRedirectTo('/admin/ouvertures/index/id_site/1/multimedia/1'); + $this->assertRedirectTo('/admin/ouvertures/index/id_site/1/used_for/1'); } } @@ -588,7 +588,7 @@ class OuverturesControllerAddOuvertureCranTest extends OuverturesControllerTestC class OuverturesControllerAddOuvertureCranMultimediaDisabledTest extends OuverturesControllerTestCase { public function setUp() { parent::setUp(); - $this->dispatch('/admin/ouvertures/add/id_site/1/multimedia/1', true); + $this->dispatch('/admin/ouvertures/add/id_site/1/used_for/1', true); } /** @test */ @@ -604,7 +604,7 @@ class OuverturesControllerAddOuvertureCranMultimediaEnabledTest extends Ouvertur parent::setUp(); Class_AdminVar::set('MULTIMEDIA_KEY', 'SECRET_KEY'); - $this->dispatch('/admin/ouvertures/add/id_site/1/multimedia/1', true); + $this->dispatch('/admin/ouvertures/add/id_site/1/used_for/1', true); } @@ -705,7 +705,7 @@ class OuverturesControllerPostAddOuvertureCranMultimediaEnabledTest extends Ouve public function setUp() { parent::setUp(); Class_AdminVar::set('MULTIMEDIA_KEY', 'SECRET_KEY'); - $this->postDispatch('/admin/ouvertures/add/multimedia/1', + $this->postDispatch('/admin/ouvertures/add/used_for/1', ['debut_matin' => '10:30', 'fin_matin' => '11:30', 'debut_apres_midi' => '14:00', @@ -721,13 +721,13 @@ class OuverturesControllerPostAddOuvertureCranMultimediaEnabledTest extends Ouve /** @test */ public function responseShouldRedirectToOuverturesIndexSiteThree() { - $this->assertRedirectTo('/admin/ouvertures/index/id_site/3/multimedia/1'); + $this->assertRedirectTo('/admin/ouvertures/index/id_site/3/used_for/1'); } /** @test */ public function newOuvertureShouldBeMultimedia(){ - $this->assertEquals(1, $this->_new_ouverture->getMultimedia()); + $this->assertTrue($this->_new_ouverture->isMultimedia()); } } @@ -839,6 +839,17 @@ class OuverturesControllerEditWinterTuesdayOpeningsInCranTest extends Ouvertures public function inputValidityEndValueShouldBe2018_01_01() { $this->assertXPath('//form//input[@name="validity_end"][@value="01/01/2018"]'); } + + /** @test */ + public function pageShouldNotContainsInputTextForMaxPerPeriodMatin() { + $this->assertNotXPath('//input[@type="text"][@name="max_per_period_matin"]'); + } + + +/** @test */ + public function pageShouldNotContainsInputTextForMaxPerPeriodApresMidi() { + $this->assertNotXPath('//input[@type="text"][@name="max_per_period_apres_midi"]'); + } } diff --git a/tests/application/modules/admin/controllers/UserGroupControllerTest.php b/tests/application/modules/admin/controllers/UserGroupControllerTest.php index 9a1e33b9e51d9f4249558359137d069c34a3745c..348e871343d6596cb99cc2b74eeb31a154b60517 100644 --- a/tests/application/modules/admin/controllers/UserGroupControllerTest.php +++ b/tests/application/modules/admin/controllers/UserGroupControllerTest.php @@ -1412,6 +1412,18 @@ class Admin_UserGroupControllerEditMembersWithPaginationTest public function nb29UsersShouldBeDisplay() { $this->assertXPathContentContains('//div', '29 utilisateurs', $this->_response->getBody()); } + + + /** @test */ + public function tableShouldContainsLinkToDeleteUserTwo() { + $this->assertXPath('//table//tr//td//a[contains(@href, "admin/usergroup/editmembers/id/1/delete/2")]'); + } + + + /** @test */ + public function tableShouldContainsLinkToEditUserTwo() { + $this->assertXPath('//table//tr//td//a[contains(@href, "admin/users/edit/id/2")]'); + } } diff --git a/tests/application/modules/opac/controllers/AbonneControllerMultimediaTest.php b/tests/application/modules/opac/controllers/AbonneControllerMultimediaTest.php index a13440d7070195fb831b50029416c363db623167..c63a62975a177b07234ea89aab5be7bbf2db9184 100644 --- a/tests/application/modules/opac/controllers/AbonneControllerMultimediaTest.php +++ b/tests/application/modules/opac/controllers/AbonneControllerMultimediaTest.php @@ -209,7 +209,7 @@ abstract class AbonneControllerMultimediaHoldTestCase extends AbstractController protected function _beMultimediaOpening($opening) { - $opening->setMultimedia(1)->assertSave(); + $opening->beMultimedia()->assertSave(); return $opening; } @@ -331,7 +331,7 @@ class AbonneControllerMultimediaHoldLocationTest extends AbonneControllerMultime parent::setUp(); $lundi = Class_Ouverture::chaqueLundi('8:00', '12:00', '13:00', '18:00') - ->setMultimedia(1); + ->beMultimedia(); $lundi->assertSave(); $this->fixture('Class_Bib', @@ -340,7 +340,7 @@ class AbonneControllerMultimediaHoldLocationTest extends AbonneControllerMultime 'ouvertures' => [$lundi]]); $mercredi = Class_Ouverture::chaqueMercredi('8:00', '12:00', '13:00', '18:00') - ->setMultimedia(1); + ->beMultimedia(); $mercredi->assertSave(); $this->fixture('Class_Bib', diff --git a/tests/application/modules/opac/controllers/BibControllerTest.php b/tests/application/modules/opac/controllers/BibControllerTest.php index 2e2cb69dfd4cf236e1c359f47b1971ef737aa003..8353e822ae7d3d1e5ff4a39b02f4ff3f4c865182 100644 --- a/tests/application/modules/opac/controllers/BibControllerTest.php +++ b/tests/application/modules/opac/controllers/BibControllerTest.php @@ -520,7 +520,7 @@ class BibControllerBibViewAnnecyTest extends BibControllerBibViewTestCase { ['id' => 8, 'id_site' => 4, 'jour' => '2016-07-07', - 'multimedia' => 1]); + 'used_for' => Class_Ouverture::USED_FOR_MULTIMEDIA]); $this->dispatch('bib/bibview/id/4', true); } @@ -631,7 +631,7 @@ class BibControllerBibViewAnnecyTest extends BibControllerBibViewTestCase { /** @test */ public function openingsShouldNotContainsMultimediaDate() { $this->assertNotXPathContentContains('//div[contains(@class, "library_schedule")]//li', - 'le jeudi 07 juillet : 10h - 18h'); + 'le jeudi 07 juillet : 10h - 18h'); } diff --git a/tests/application/modules/opac/controllers/MultimediaControllerTest.php b/tests/application/modules/opac/controllers/MultimediaControllerTest.php index 22fa897a70ec6efea747767a3e705b9d6428a83a..7371ad0c89306c8902298192d24767d393139472 100644 --- a/tests/application/modules/opac/controllers/MultimediaControllerTest.php +++ b/tests/application/modules/opac/controllers/MultimediaControllerTest.php @@ -27,7 +27,7 @@ trait TMultimediaControllerAbonneFixtureHoldSuccessOnSept12 { ['id' => 1, 'Jour' => '2012-09-12', 'horaires' => ['08:00', '12:00', '12:00', '18:00'], - 'multimedia' => 1])]]); + 'used_for' => Class_Ouverture::USED_FOR_MULTIMEDIA])]]); $this->fixture('Class_Multimedia_Device', ['id' => 1, diff --git a/tests/bootstrap.php b/tests/bootstrap.php index c292309689335f359d03f7f04998de1d8dbc147e..0dc28b4a65bd7066df080657076a8c35cd5f0d22 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -63,7 +63,7 @@ $_SERVER['HTTP_HOST'] = 'localhost'; setupOpac(); -ZendAfi_Controller_Action_Helper_RenderIcalArticles::ensureAutoload(); +Class_ICal_Autoloader::getInstance()->ensureAutoload(); require_once __DIR__ . '/../library/PhpParser/lib/bootstrap.php'; (new Storm_Cache())->getCache()->setOption('caching', true); diff --git a/tests/db/UpgradeDBTest.php b/tests/db/UpgradeDBTest.php index 2ce84cb7d498409e59145eb6dca5e7dfa243e94c..c5338f69a40f5a750c11c3d8efbc5154d162a0cf 100644 --- a/tests/db/UpgradeDBTest.php +++ b/tests/db/UpgradeDBTest.php @@ -3229,3 +3229,97 @@ class UpgradeDB_387_Test extends UpgradeDBTestCase { $this->assertIndex('notices_avis', 'source_key'); } } + + + + +class UpgradeDB_388_Test extends UpgradeDBTestCase { + public function prepare() { + $this->dropTable('drive_checkout'); + } + + + /** @test */ + public function tableDriveCheckoutShouldExist() { + $this->assertTable('drive_checkout'); + } + + + public function fields() { + return [['id', 'int(11) unsigned'], + ['user_id', 'int(11)'], + ['library_id', 'int(11)'], + ['start_at', 'datetime'], + ]; + } + + + /** + * @test + * @dataProvider fields + */ + public function fieldShouldBeOfType($field, $type) { + $this->assertFieldType('drive_checkout', $field, $type); + } + + + public function indices() { + return [['PRIMARY'], + ['user_id'], + ['library_id'], + ['start_at'], + ]; + } + + + /** + * @test + * @dataProvider indices + */ + public function fieldShouldBeIndexed($index) { + $this->assertIndex('drive_checkout', $index); + } +} + + + + +class UpgradeDB_389_Test extends UpgradeDBTestCase { + public function prepare(){ + $this->silentQuery('alter table ouvertures change used_for multimedia boolean'); + $this->silentQuery('alter table ouvertures drop index used_for'); + $this->silentQuery('alter table bib_c_site drop column enable_drive'); + $this->silentQuery('alter table ouvertures drop column max_per_period_matin'); + $this->silentQuery('alter table ouvertures drop column max_per_period_apres_midi'); + } + + + /** @test */ + public function tableouverturesShouldContainsColumnUsedForAsInt(){ + $this->assertFieldType('ouvertures', 'used_for', 'int(11)'); + } + + + /** @test */ + public function tableouverturesShouldContainsIndexUsedFor(){ + $this->assertIndex('ouvertures', 'used_for'); + } + + + /** @test */ + public function tableBibCSiteShouldContainsColumnEnableDriveAsTinyInt(){ + $this->assertFieldType('bib_c_site', 'enable_drive', 'tinyint(1)'); + } + + + /** @test */ + public function tableOuverturesShouldContainsColumnMaxPerPeriodMatinAsInt(){ + $this->assertFieldType('ouvertures', 'max_per_period_matin', 'int(11)'); + } + + + /** @test */ + public function tableOuverturesShouldContainsColumnMaxPerPeriodApresMidiAsInt(){ + $this->assertFieldType('ouvertures', 'max_per_period_apres_midi', 'int(11)'); + } +} \ No newline at end of file diff --git a/tests/fixtures/KohaFixtures.php b/tests/fixtures/KohaFixtures.php index 6fac4ea0fd43d75436ef75498f1168199da9f0c7..ddc8a16b8d19ba98c748810b24f23f95d34ac21b 100644 --- a/tests/fixtures/KohaFixtures.php +++ b/tests/fixtures/KohaFixtures.php @@ -920,7 +920,6 @@ class KohaFixtures { <lowestPriority>0</lowestPriority> <title>Harry Potter et la chambre des secrets</title> </hold> - <hold> <priority>3</priority> <reservenotes /> @@ -935,8 +934,12 @@ class KohaFixtures { </hold> <hold> <priority>0</priority> - <item></item> + <item> + <barcode>3512099938</barcode> + <itemcallnumber>HH TU 3</itemcallnumber> + </item> <reservedate>2014-05-02</reservedate> + <expirationdate>2014-05-17</expirationdate> <timestamp>2014-05-02 15:40:35</timestamp> <biblionumber>235572</biblionumber> <borrowernumber>17448</borrowernumber> diff --git a/tests/fixtures/NanookFixtures.php b/tests/fixtures/NanookFixtures.php index 124e5e3f51e734c68eec3134402996c06085caf4..722b59f86fdebe7343d1c2c2741fb52be6a48a13 100644 --- a/tests/fixtures/NanookFixtures.php +++ b/tests/fixtures/NanookFixtures.php @@ -292,6 +292,20 @@ class NanookFixtures { <author>Charles Le Blanc</author> <locationLabel>Site Principal</locationLabel> </hold> + <hold> + <bibId>88329</bibId> + <itemId>88928</itemId> + <barcode>80072946</barcode> + <cote>BD BUC</cote> + <title>Sillage. 18, psycholocauste</title> + <author>Philippe Buchet</author> + <locationLabel>Site Principal</locationLabel> + <locationId>12</locationId> + <priority>1</priority> + <available>1</available> + <availabilityDate>15/06/2020</availabilityDate> + <availabilityEndDate>2020-06-30</availabilityEndDate> + </hold> </holds> <suggests> <suggest> diff --git a/tests/library/Class/Multimedia/DeviceTest.php b/tests/library/Class/Multimedia/DeviceTest.php index fad36552c37e2199091c50dd46f60b215f369adf..6cbeeb7e8505ccecf12a01547d36c4f7180237cc 100644 --- a/tests/library/Class/Multimedia/DeviceTest.php +++ b/tests/library/Class/Multimedia/DeviceTest.php @@ -210,11 +210,11 @@ class Multimedia_DeviceCurrentHoldForUserWithoutHoldAndMaxSlotsAfterCloseHoursTe ->save(); $lundi = Class_Ouverture::chaqueLundi('08:00', '12:00', '14:00', '18:00') - ->setMultimedia(1); + ->beMultimedia(); $lundi->assertSave(); $mardi = Class_Ouverture::chaqueMardi('08:00', '12:00', '12:00', '16:00') - ->setMultimedia(1); + ->beMultimedia(); $mardi->assertSave(); @@ -285,7 +285,7 @@ abstract class Multimedia_DeviceCurrentHoldForUserWithoutHoldAndMaxSlotsAfterNex ->setOuvertures([$this->fixture('Class_Ouverture', ['id' => 5, 'jour_semaine' => date('w'), - 'multimedia' => 1, + 'used_for' => Class_Ouverture::USED_FOR_MULTIMEDIA, 'horaires' => ['08:00', '12:00', '14:00', '23:00']])]) ->assertSave(); @@ -384,7 +384,7 @@ class Multimedia_DeviceCreateHoldTest extends ModelTestCase { 'password' => 'jcupwqjms']); $jeudi = Class_Ouverture::chaqueJeudi('08:00', '12:00', '14:00', '18:00') - ->setMultimedia(1); + ->beMultimedia(); $jeudi->assertSave(); $this->fixture('Class_Bib', diff --git a/tests/library/Class/Multimedia/LocationTest.php b/tests/library/Class/Multimedia/LocationTest.php index d5d0e625326714c6cbc9e777b52813d239298dc6..88e7de94c7c8fa55ff12a1e402f0bf00d1022ee7 100644 --- a/tests/library/Class/Multimedia/LocationTest.php +++ b/tests/library/Class/Multimedia/LocationTest.php @@ -35,14 +35,14 @@ abstract class Multimedia_LocationTestCase extends ModelTestCase { ['id' => 3, 'bib' => $antibe, 'jour_semaine' => Class_Ouverture::MERCREDI, - 'multimedia' => 1, + 'used_for' => Class_Ouverture::USED_FOR_MULTIMEDIA, 'horaires' => ['08:30', '12:00', '12:00', '17:45']]); $this->fixture('Class_Ouverture', ['id' => 4, 'bib' => $antibe, 'jour_semaine' => Class_Ouverture::JEUDI, - 'multimedia' => 1, + 'used_for' => Class_Ouverture::USED_FOR_MULTIMEDIA, 'horaires' => ['10:00', '12:00', '14:00', '19:00']]); $this->fixture('Class_Ouverture', @@ -52,7 +52,7 @@ abstract class Multimedia_LocationTestCase extends ModelTestCase { 'validity_start' => '2016-10-01', 'validity_end' => '2017-03-01', 'jour_semaine' => Class_Ouverture::JEUDI, - 'multimedia' => 1, + 'used_for' => Class_Ouverture::USED_FOR_MULTIMEDIA, 'horaires' => ['00:00', '00:00', '14:00', '15:00']]); $this->fixture('Class_Ouverture', @@ -62,21 +62,21 @@ abstract class Multimedia_LocationTestCase extends ModelTestCase { 'validity_start' => '2016-10-01', 'validity_end' => '2017-03-01', 'jour_semaine' => Class_Ouverture::MARDI, - 'multimedia' => 1, + 'used_for' => Class_Ouverture::USED_FOR_MULTIMEDIA, 'horaires' => ['00:00', '00:00', '14:00', '15:00']]); $this->fixture('Class_Ouverture', ['id' => 5, 'bib' => $antibe, 'jour' => '2012-09-19', - 'multimedia' => 1, + 'used_for' => Class_Ouverture::USED_FOR_MULTIMEDIA, 'horaires' => ['09:00', '12:00', '12:00', '18:00']]); $this->fixture('Class_Ouverture', ['id' => 15, 'bib' => $antibe, 'jour' => '2012-09-09', - 'multimedia' => 1, + 'used_for' => Class_Ouverture::USED_FOR_MULTIMEDIA, 'horaires' => ['09:00', '12:00', '12:00', '18:00']]); // closed on september 20 @@ -84,7 +84,7 @@ abstract class Multimedia_LocationTestCase extends ModelTestCase { ['id' => 16, 'bib' => $antibe, 'jour' => '2012-09-20', - 'multimedia' => 1, + 'used_for' => Class_Ouverture::USED_FOR_MULTIMEDIA, 'horaires' => ['00:00', '00:00', '00:00', '00:00']]); diff --git a/tests/library/Class/WebService/SIGB/KohaTest.php b/tests/library/Class/WebService/SIGB/KohaTest.php index 20f8fc948d4b3fe4310ff41e53e6f5f73b68999d..e6c78064481ef49c012d9c3f564d47ab8f804652 100644 --- a/tests/library/Class/WebService/SIGB/KohaTest.php +++ b/tests/library/Class/WebService/SIGB/KohaTest.php @@ -461,9 +461,12 @@ class KohaGetEmprunteurLaureAfondTest extends KohaTestCase { 'libelle' => 'Montmedy', 'id_origine' => 'MON']); + $this->fixture('Class_Bib', ['id' => 88]); + $this->fixture('Class_CodifAnnexe', ['id' => 35, 'libelle' => 'Médiathèque publique', - 'id_origine' => 'MPU']); + 'id_origine' => 'MPU', + 'id_bib' => 88]); $this->mock_web_client ->whenCalled('postData') @@ -536,28 +539,56 @@ class KohaGetEmprunteurLaureAfondTest extends KohaTestCase { /** @test */ - public function firstHoldWaitingToBePulledPickUpLocationShouldBeMontmedy() { + public function firstHoldWaitingToBePulledPickUpLocationShouldBeMediathequePublique() { $waiting_holds = $this->laurent->getHoldsWaitingToBePulled(); $this->assertEquals('Médiathèque publique', $waiting_holds[0]->getPickupLocationLabel()); } /** @test */ - public function secondHoldWaitingToBePulledTitleShouldBeHarryPotterEtLePrisonnierDAzkaban() { + public function firstHoldWaitingToBePulledLocationIdShouldBe88() { + $waiting_holds = $this->laurent->getHoldsWaitingToBePulled(); + $this->assertEquals(88, $waiting_holds[0]->getLocationId()); + } + + + /** @test */ + public function firstHoldWaitingToBePulledBarcodeShouldBe3512099938() { + $this->assertEquals('3512099938', + $this->laurent->getHoldsWaitingToBePulled()[0]->getCodeBarre()); + } + + + /** @test */ + public function firstHoldWaitingToBePulledCoteShouldBeHH_TU_3() { + $this->assertEquals('HH TU 3', + $this->laurent->getHoldsWaitingToBePulled()[0]->getCote()); + } + + + /** @test */ + public function firstHoldWaitingToBePulledAvailabilityEndDateShouldBe2014_05_17() { + $this->assertEquals('2014-05-17', + $this->laurent->getHoldsWaitingToBePulled()[0]->getAvailabilityEndDate()); + } + + + /** @test */ + public function secondHoldWaitingToBePulledTitleShouldBeHarryPotterEtLOrdreDuPhenix() { $waiting_holds = $this->laurent->getHoldsWaitingToBePulled(); $this->assertEquals('Harry Potter et l\'ordre du Phénix', $waiting_holds[1]->getTitre()); } /** @test */ - public function secondHoldWaitingToBePulledPickUpLocationShouldBeMediathequePublique() { + public function secondHoldWaitingToBePulledPickUpLocationShouldBeMontmedy() { $waiting_holds = $this->laurent->getHoldsWaitingToBePulled(); $this->assertEquals('Montmedy', $waiting_holds[1]->getPickupLocationLabel()); } /** @test */ - public function nbReservationsShouldReturnThree() { + public function nbReservationsShouldReturnFour() { $this->assertEquals(4, $this->laurent->getNbReservations()); } diff --git a/tests/library/Class/WebService/SIGB/NanookTest.php b/tests/library/Class/WebService/SIGB/NanookTest.php index 5b7901831f9f5cdf651b3c6f47d6b4ad36d70b76..678e6789eed405d08e742f325dda1e1113e69bd7 100644 --- a/tests/library/Class/WebService/SIGB/NanookTest.php +++ b/tests/library/Class/WebService/SIGB/NanookTest.php @@ -678,14 +678,14 @@ class NanookGetEmprunteurChristelDelpeyrouxTest extends NanookTestCase { /** @test */ - public function nbReservationShouldBeThree() { - $this->assertEquals(3, $this->_emprunteur->getNbReservations()); + public function nbReservationShouldBeFour() { + $this->assertEquals(4, $this->_emprunteur->getNbReservations()); } /** @test */ - public function nbWaitingToBePulledShouldBeOne() { - $this->assertCount(1, $this->_emprunteur->getHoldsWaitingToBePulled()); + public function nbWaitingToBePulledShouldBeTwo() { + $this->assertCount(2, $this->_emprunteur->getHoldsWaitingToBePulled()); } @@ -765,6 +765,37 @@ class NanookGetEmprunteurChristelDelpeyrouxTest extends NanookTestCase { public function secondReservationEtatShouldBeDisponible() { $this->assertEquals('Disponible', $this->_emprunteur->getReservationAt(1)->getEtat()); } + + + /** @test */ + public function fourthHoldShouldBeWaitingToBePulled() { + $this->assertTrue($this->_emprunteur->getReservationAt(3)->isWaitingToBePulled()); + } + + + /** @test */ + public function fourthHoldAvailabilityEndDateShouldBe2020_06_30() { + $this->assertEquals('2020-06-30', + $this->_emprunteur->getReservationAt(3)->getAvailabilityEndDate()); + } + + + /** @test */ + public function fourthHoldLocationIdShouldBe12() { + $this->assertEquals('12', $this->_emprunteur->getReservationAt(3)->getLocationId()); + } + + + /** @test */ + public function fourthHoldBarcodeShouldBe80072946() { + $this->assertEquals('80072946', $this->_emprunteur->getReservationAt(3)->getCodeBarre()); + } + + + /** @test */ + public function fourthHoldCodeShouldBD_BUC() { + $this->assertEquals('BD BUC', $this->_emprunteur->getReservationAt(3)->getCote()); + } } diff --git a/tests/library/ZendAfi/View/Helper/RenderLibraryOpeningTest.php b/tests/library/ZendAfi/View/Helper/RenderLibraryOpeningTest.php index 9a192100f492899b52656ca24c292df063b6e2e5..746e051bebbcb2055e4d45db21917c4436d95e6b 100644 --- a/tests/library/ZendAfi/View/Helper/RenderLibraryOpeningTest.php +++ b/tests/library/ZendAfi/View/Helper/RenderLibraryOpeningTest.php @@ -30,8 +30,10 @@ abstract class ZendAfi_View_Helper_RenderLibraryOpeningTestCase extends ViewHelp $this->annecy = $this->fixture('Class_Bib', ['id' => 1, 'libelle' => 'Annecy', - 'ouvertures' => [ - Class_Ouverture::chaqueLundi('10:00', '12:00', '14:00', '18:00')]]); + 'enable_drive' => true, + 'ouvertures' => [Class_Ouverture::chaqueLundi('08:00','13:00','14:00','17:00')->beDrive(), + Class_Ouverture::chaqueLundi('10:00', '12:00', '14:00', '18:00') + ]]); $this->cran = $this->fixture('Class_Bib', ['id' => 2, @@ -43,7 +45,11 @@ abstract class ZendAfi_View_Helper_RenderLibraryOpeningTestCase extends ViewHelp $this->seynod = $this->fixture('Class_Bib', ['id' => 3, - 'libelle' => 'Seynod']); + 'libelle' => 'Seynod', + 'ouvertures' => [Class_Ouverture::chaqueLundi('08:00','13:00','14:00','17:00')->beDrive(), + Class_Ouverture::chaqueLundi('08:00','15:00','18:00','20:00')->beMultimedia() + ] + ]); $this->meythet = $this->fixture('Class_Bib', ['id' => 4, @@ -400,8 +406,8 @@ class ZendAfi_View_Helper_RenderLibraryOpeningsOnValidityRangeTest /** @test */ - public function ouverturesOrderedIdShouldBe348_349_350_351_2() { - $this->assertEquals([348, 349 , 350, 351, 2], + public function ouverturesOrderedIdShouldBe348_349_350_351_3() { + $this->assertEquals([348, 349 , 350, 351, 3], (new Storm_Model_Collection($this->cran->getOuvertures())) ->collect('id') ->getArrayCopy()); diff --git a/tests/scenarios/DriveCheckOut/DriveCheckOutBookingTest.php b/tests/scenarios/DriveCheckOut/DriveCheckOutBookingTest.php new file mode 100644 index 0000000000000000000000000000000000000000..ef9f3018744a98070a76e507a9ed23dd49e2dde6 --- /dev/null +++ b/tests/scenarios/DriveCheckOut/DriveCheckOutBookingTest.php @@ -0,0 +1,842 @@ +<?php +/** + * Copyright (c) 2012-2020, 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 DriveCheckOutBookingNotActiveTest extends AbstractControllerTestCase { + protected $_storm_default_to_volatile = true; + + + public function setUp() { + parent::setUp(); + Class_AdminVar::set('ENABLE_DRIVE_CHECKOUT', 0); + $this->dispatch('/opac/drive-checkout/plan'); + } + + + /** @test */ + public function shouldRedirectToHome() { + $this->assertRedirectTo('/'); + } + + + /** @test */ + public function notificationShouldContainsNotActiveMessage() { + $this->assertFlashMessengerContentContains('La planification du retrait des réservations est désactivée.'); + } +} + + + + +abstract class DriveCheckOutBookingTestCase extends AbstractControllerTestCase { + protected + $_storm_default_to_volatile = true, + $_marcus, + $_mail_transport; + + + public function setUp() { + parent::setUp(); + + Class_AdminVar::set('ENABLE_DRIVE_CHECKOUT', 1); + + $timesource = new TimeSourceForTest('2020-05-05 11:30'); + Class_DriveCheckout_Plan::setTimeSource($timesource); + + $this->_mail_transport = new MockMailTransport(); + Zend_Mail::setDefaultTransport($this->_mail_transport); + + $this->_setupProfile() + ->_setupLibraries() + ->_setupUser(); + } + + + public function tearDown() { + Class_DriveCheckout_Plan::setTimeSource(null); + Class_Systeme_ModulesAccueil::reset(); + parent::tearDown(); + } + + + protected function _setupProfile() { + Class_Systeme_ModulesAccueil::reset(); + Class_AdminVar::set('TEMPLATING', 1); + + $profile = $this->fixture('Class_Profil', + ['id' => 23, + 'mail_site' => 'zemail@mabib.st']); + + $template = new Intonation_Template; + $template->applyOn($profile); + (new Class_Profil_Promoter())->promote($profile); + + return $this; + } + + + protected function _setupLibraries() { + $lib_hotel_dieu = $this + ->fixture('Class_Bib', + ['id' => 1, + 'libelle' => 'Hotel-Dieu', + 'closed_on_holidays' => false, + 'enable_drive' => 1, + 'ouvertures' => [ + Class_Ouverture::chaqueLundi('00:00', '00:00', '12:00', '18:00') + ->beDrive(), + Class_Ouverture::chaqueMardi('09:00', '12:00', '14:00', '18:00') + ->beDrive(), + Class_Ouverture::chaqueMercredi('00:00', '00:00', '12:00', '17:00') + ->beDrive(), + ]]); + + $this->fixture('Class_Bib', + ['id' => 2, + 'enable_drive' => 1, + 'libelle' => 'Albert Camus']); + + $this->fixture('Class_Bib', + ['id' => 3, + 'enable_drive' => 1, + 'libelle' => 'Mauricette-Rafin']); + + $this->fixture('Class_Bib', + ['id' => 4, + 'enable_drive' => 0, + 'libelle' => 'Le Turbomoteur']); + + return $this; + } + + + protected function _setupUser() { + $this->_marcus = $this->fixture('Class_Users', + ['id' => 10, + 'login' => 'MC', + 'password' => 'US', + 'prenom' => 'Marcus', + 'nom' => 'Miller', + 'pseudo' => '', + 'role_level' => ZendAfi_Acl_AdminControllerRoles::ABONNE_SIGB, + 'idabon' => 'mc123', + 'id_site' => 1, + 'mail' => 'mm@any-serveur.eu']); + + $this + ->_marcus + ->setFicheSigb(['type_comm' => Class_IntBib::COM_NANOOK, + 'fiche' => (new Class_WebService_SIGB_Emprunteur('10', 'Marcus')) + ->reservationsAddAll( + [$this->_holdIn(1)->setAvailabilityEndDate('2020-05-12'), + $this->_holdIn(1)->setAvailabilityEndDate('2020-05-13'), + $this->_holdIn(2), + $this->_holdIn(4)])]); + + + ZendAfi_Auth::getInstance()->logUser($this->_marcus); + return $this; + } + + + protected function _holdIn($library) { + return Class_WebService_SIGB_Reservation::newInstanceWithEmptyExemplaire() + ->setLocationId($library) + ->setWaitingToBePulled(); + } +} + + + + +class DriveCheckOutPatronHoldsTest extends DriveCheckOutBookingTestCase { + public function setUp() { + parent::setUp(); + $this->fixture('Class_Profil', + ['id' => 23, + 'libelle' => 'vintage']); + } + + + /** @test */ + public function pageWithBootstrapShouldContainsLinkToDriveCheckoutPlan() { + $this->dispatch('/abonne/reservations/id_profil/1'); + $this->assertXPathContentContains('//a[contains(@href, "/drive-checkout/plan")]', + 'Planifier le retrait de mes documents'); + } + + + /** @test */ + public function pageWithHistoricProfileShouldContainsLinkToDriveCheckoutPlan() { + $this->dispatch('/abonne/reservations/id_profil/23'); + $this->assertXPathContentContains('//a[contains(@href, "/drive-checkout/plan")]', + 'Planifier le retrait de mes documents'); + } + + + /** @test */ + public function disabledDrivePageWithBootstrapShouldNotContainsLinkToDriveCheckoutPlan() { + Class_AdminVar::set('ENABLE_DRIVE_CHECKOUT', 0); + $this->dispatch('/abonne/reservations/id_profil/1'); + $this->assertNotXPath('//a[contains(@href, "/drive-checkout/plan")]'); + } + + + /** @test */ + public function disabledDrivePageWithHistoricProfileShouldNotContainsLinkToDriveCheckoutPlan() { + Class_AdminVar::set('ENABLE_DRIVE_CHECKOUT', 0); + $this->dispatch('/abonne/reservations/id_profil/23'); + $this->assertNotXPath('//a[contains(@href, "/drive-checkout/plan")]'); + } +} + + + + +class DriveCheckOutUserNotificationsTest extends DriveCheckOutBookingTestCase { + protected $_actions; + + public function notify($message, $actions = []) { + if (!isset($actions['actions'])) + return $this; + $this->_actions []= $actions['actions']; + } + + + /** @test */ + public function marcusNotificationActionsShouldContainsLinkToDriveCheckoutPlan() { + $this->_marcus->registerNotificationsOn($this); + $url = Class_Url::assemble(['module' => 'opac', + 'controller' => 'drive-checkout', + 'action' => 'plan']); + $this->assertEquals([$url => 'Planifier le retrait de mes documents'], + $this->_actions[0]); + } + + + /** @test */ + public function withDisabledDriveMarcusNotificationActionsShouldNotContainsLinkToDriveCheckoutPlan() { + Class_AdminVar::set('ENABLE_DRIVE_CHECKOUT', 0); + $this->_marcus->registerNotificationsOn($this); + $this->assertEmpty($this->_actions); + } +} + + + + +class DriveCheckOutBookingPlanTest extends DriveCheckOutBookingTestCase { + public function setUp() { + parent::setUp(); + $this->dispatch('/opac/drive-checkout/plan'); + } + + + /** @test */ + public function pageTitleShouldBePlanifierLeRetraitDeMesDocuments() { + $this->assertXPathContentContains('//title', 'Planifier le retrait de mes documents'); + } + + + /** @test */ + public function pageShouldContainsLinkToChooseAlbertCamus() { + $this->assertXPath('//a[contains(@href, "/plan/id_bib/2")]'); + } + + + /** @test */ + public function pageShouldContainsCardForAlbertCamus() { + $this->assertXPathContentContains('//div[@class="card-title"]', 'Albert Camus'); + } + + + /** @test */ + public function pageShouldContainsBadge2Holds() { + $this->assertXPathContentContains('//span[contains(@class, "badge-primary")]', + '2 réservations'); + } + + + /** @test */ + public function pageShouldContainsBadge2WaitingToBePulled() { + $this->assertXPathContentContains('//span[contains(@class, "badge-success")]', + '2 à retirer'); + } + + + /** @test */ + public function pageShouldContainsBadgeNoneNotReady() { + $this->assertXPathContentContains('//span[contains(@class, "badge-secondary")]', + '0 en attente'); + } + + + /** @test */ + public function pageShouldContainsLinkToChooseHotelDieu() { + $this->assertXPath('//a[contains(@href, "/plan/id_bib/1")]'); + } + + + /** @test */ + public function pageShouldContainsCardForHotelDieu() { + $this->assertXPathContentContains('//div[@class="card-title"]', 'Hotel-Dieu'); + } + + + /** @test */ + public function pageShouldNotContainsLinkToChooseMauricette() { + $this->assertNotXPath('//a[contains(@href, "/plan/id_bib/3")]'); + } + + + /** @test */ + public function pageShouldNotContainsLinkToChooseLeTurbomoteur() { + $this->assertNotXPath('//a[contains(@href, "/plan/id_bib/4")]'); + } +} + + + + +class DriveCheckOutBookingPlanWithFuturExistingTest extends DriveCheckOutBookingTestCase { + public function setUp() { + parent::setUp(); + $this->onLoaderOfModel('Class_DriveCheckout') + ->whenCalled('findFuturefor')->with(Class_Bib::find(2), $this->_marcus) + ->answers($this->fixture('Class_DriveCheckout', + ['id' => 2, + 'library_id' => 2, + 'user_id' => $this->_marcus->getId(), + 'start_at' => '2020-05-12 09:00:00'])); + + $this->dispatch('/opac/drive-checkout/plan'); + } + + + /** @test */ + public function pageShouldNotContainsLinkToChooseAlbertCamus() { + $this->assertNotXPath('//a[contains(@href, "/plan/id_bib/2")]'); + } + + + /** @test */ + public function pageShouldContainsAlreadyPlannedCheckout() { + $this->assertXPathContentContains('//p', + 'Retrait planifié le mardi 12 mai à 09h00'); + } + + + /** @test */ + public function pageShouldContainsLinkToDeletePlannedCheckout() { + $this->assertXPathContentContains('//a[contains(@href, "/drive-checkout/delete/id/2")]', + 'Supprimer'); + } + + + /** @test */ + public function pageShouldContainsLinkToDownloadPlannedCheckout() { + $this->assertXPathContentContains('//a[contains(@href, "/drive-checkout/ical/id/2")]', + 'Ajouter à mon agenda'); + } +} + + + + +class DriveCheckOutBookingPlanDeleteExistingTest extends DriveCheckOutBookingTestCase { + public function setUp() { + parent::setUp(); + + $this->fixture('Class_DriveCheckout', + ['id' => 2, + 'library_id' => 2, + 'user_id' => $this->_marcus->getId(), + 'start_at' => '2020-05-12 09:00:00']); + + $this->dispatch('/opac/drive-checkout/delete/id/2'); + } + + + /** @test */ + public function existingShouldBeDeleted() { + $this->assertNull(Class_DriveCheckout::find(2)); + } + + + /** @test */ + public function notificationShouldContainsDeletionDetails() { + $this->assertFlashMessengerContentContains('Le retrait de vos documents de Albert Camus planifié pour le mardi 12 mai à 09h00 a été supprimé.'); + } +} + + + + +class DriveCheckOutBookingPlanDownloadExistingTest extends DriveCheckOutBookingTestCase { + public function setUp() { + parent::setUp(); + + $this->fixture('Class_DriveCheckout', + ['id' => 2, + 'library_id' => 2, + 'user_id' => $this->_marcus->getId(), + 'start_at' => '2020-05-12 09:00:00']); + + Class_Bib::find(2)->setLieu($this->fixture('Class_Lieu', + ['id' => 233, + 'libelle' => 'Albert Camus', + 'latitude' => '-38.812239', + 'longitude' => '177.137570'])); + + $this->dispatch('/opac/drive-checkout/ical/id/2'); + } + + + /** @test */ + public function contentTypeShouldBeTextCalendar() { + $this->assertHeaderContains('Content-Type', 'text/calendar;charset=utf-8'); + } + + + /** @test */ + public function contentDispositionShouldBeAttachment() { + $this->assertHeaderContains('Content-Disposition', 'attachment;filename="calendar.ics'); + } + + + /** @test */ + public function uidShouldBeDriveCheckout2() { + $this->assertContains('drive-checkout:2', $this->_response->getBody()); + } + + + /** @test */ + public function dateTimeShouldBe20200512T090000WithTimezone() { + $this->assertContains('DTSTART;TZID=Europe/Paris:20200512T090000', $this->_response->getBody()); + } + + + /** @test */ + public function urlToPlanInCurrentProfilShouldBePresent() { + $this->assertContains('/drive-checkout/plan/id_profil/1', $this->_response->getBody()); + } + + + /** @test */ + public function summaryShouldBeRetraitDesReservations() { + $this->assertContains('SUMMARY:Retrait des réservations à Albert Camus', + $this->_response->getBody()); + } + + + /** @test */ + public function locationShouldBeAlbertCamus() { + $this->assertContains('LOCATION:Albert Camus', $this->_response->getBody()); + } + + + /** @test */ + public function geoShouldBeLat38Long177() { + $this->assertContains('GEO:-38.812239;177.137570', $this->_response->getBody()); + } +} + + + + +class DriveCheckOutBookingPlanBibHotelDieuTest extends DriveCheckOutBookingTestCase { + public function setUp() { + parent::setUp(); + $this->dispatch('/opac/drive-checkout/plan/id_bib/1'); + } + + + /** @test */ + public function selectedLibrayShouldBeHotelDieu() { + $this->assertXPathContentContains('//div[@class="card-title"]', 'Hotel-Dieu'); + } + + + /** @test */ + public function linkToModifySelectedLibraryShouldBePresent() { + $this->assertXPathContentContains('//a[not(contains(@href, "/id_bib"))][contains(@href, "/plan")]', + 'Choisir un autre site'); + } + + + /** @test */ + public function pageShouldContainsLinkToChoose2020_05_12() { + $this->assertXPathContentContains('//a[contains(@href, "/checkout_date/2020-05-12")]', + 'le mardi 12 mai'); + } + + + /** @test */ + public function pageShouldNotContainsLinkToChoosePossibleButAfterMaxHold() { + $this->assertNotXPath('//a[contains(@href, "/checkout_date/2020-05-13")]'); + } + + + /** @test */ + public function pageShouldNotContainsLinkToChooseNotOpened() { + $this->assertNotXPath('//a[contains(@href, "/checkout_date/2020-05-16")]'); + } +} + + + + +class DriveCheckOutBookingPlanBibHotelDieuQuotaFullOn2020_05_12Test + extends DriveCheckOutBookingTestCase { + + public function setUp() { + parent::setUp(); + $this->onLoaderOfModel('Class_DriveCheckout') + ->whenCalled('countBetweenForLibrary') + ->willDo(function($start, $end, $library) + { + return '2020-05-12' == $start->format('Y-m-d') ? 600 : 0; + }); + + $this->dispatch('/opac/drive-checkout/plan/id_bib/1'); + } + + + /** @test */ + public function selectedLibrayShouldBeHotelDieu() { + $this->assertXPathContentContains('//div[@class="card-title"]', 'Hotel-Dieu'); + } + + + /** @test */ + public function pageShouldContainslinkToModifySelectedLibrary() { + $this->assertXPathContentContains('//a[not(contains(@href, "/id_bib"))][contains(@href, "/plan")]', + 'Choisir un autre site'); + } + + + /** @test */ + public function pageShouldNotContainsLinkToChooseFull2020_05_12() { + $this->assertNotXPath('//a[contains(@href, "/checkout_date/2020-05-12")]'); + } +} + + + + +class DriveCheckOutBookingPlanBibHotelDieuAt2020_05_16Test extends DriveCheckOutBookingTestCase { + public function setUp() { + parent::setUp(); + $this->dispatch('/opac/drive-checkout/plan/id_bib/1/checkout_date/2020-05-16'); + } + + + /** @test */ + public function shouldRedirectToHotelDieuSelected() { + $this->assertRedirectTo('/drive-checkout/plan/id_bib/1'); + } + + + /** @test */ + public function notificationShouldContainsUnavailableDate() { + $this->assertFlashMessengerContentContains('La date choisie n\'est pas disponible'); + } +} + + + + +class DriveCheckOutBookingPlanBibHotelDieuAt2020_05_12Test extends DriveCheckOutBookingTestCase { + public function setUp() { + parent::setUp(); + $this->dispatch('/opac/drive-checkout/plan/id_bib/1/checkout_date/2020-05-12'); + } + + + /** @test */ + public function selectedShouldBeHotelDieu() { + $this->assertXPathContentContains('//div[@class="card-title"]', 'Hotel-Dieu'); + } + + + /** @test */ + public function selectedDateShouldBeMardi12Mai() { + $this->assertXPathContentContains('//li', 'le mardi 12 mai'); + } + + + /** @test */ + public function linkToModifySelectedDateShouldBePresent() { + $this->assertXPathContentContains('//a[not(contains(@href, "/checkout_date"))][contains(@href, "/plan/id_bib/1")]', + 'Choisir une autre date'); + } + + + /** @test */ + public function pageShouldContainsFormSelectToChooseTimes() { + $this->assertXPathContentContains('//form//select[@name="checkout_time"]//option[@value="09:00"]', + '09h00'); + } +} + + + + +class DriveCheckOutBookingPlanBibHotelDieuAt2020_05_12QuotaFullAt09_00Test + extends DriveCheckOutBookingTestCase { + + public function setUp() { + parent::setUp(); + + $this->fixture('Class_DriveCheckout', + ['id' => 1, + 'user_id' => 8987383, + 'library' => Class_Bib::find(1), + 'start_at' => '2020-05-12 09:00:00']); + + $this->fixture('Class_DriveCheckout', + ['id' => 2, + 'user_id' => 9987374, + 'library' => Class_Bib::find(1), + 'start_at' => '2020-05-12 14:00:00']); + + $this->fixture('Class_DriveCheckout', + ['id' => 3, + 'user_id' => 897890, + 'library' => Class_Bib::find(1), + 'start_at' => '2020-05-12 09:00:00']); + + $this->dispatch('/opac/drive-checkout/plan/id_bib/1/checkout_date/2020-05-12'); + } + + + /** @test */ + public function selectedShouldBeHotelDieu() { + $this->assertXPathContentContains('//div[@class="card-title"]', 'Hotel-Dieu'); + } + + + /** @test */ + public function selectedDateShouldBeMardi12Mai() { + $this->assertXPathContentContains('//li', 'le mardi 12 mai'); + } + + + /** @test */ + public function linkToModifySelectedDateShouldBePresent() { + $this->assertXPathContentContains('//a[not(contains(@href, "/checkout_date"))][contains(@href, "/plan/id_bib/1")]', + 'Choisir une autre date'); + } + + + /** @test */ + public function pageShouldContainsFormSelectToChooseTimes() { + $this->assertXPath('//form//select[@name="checkout_time"]'); + } + + + /** @test */ + public function selectToChooseTimeShouldNotContains09_00() { + $this->assertNotXPath('//form//select[@name="checkout_time"]//option[@value="09:00"]'); + } + + + /** @test */ + public function selectToChooseTimeShouldContains09_30() { + $this->assertXPath('//form//select[@name="checkout_time"]//option[@value="09:30"]'); + } + + + /** @test */ + public function selectToChooseTimeShouldNotContains14_00() { + $this->assertNotXPath('//form//select[@name="checkout_time"]//option[@value="14:00"]'); + } + + + /** @test */ + public function selectToChooseTimeShouldContains14_30() { + $this->assertXPath('//form//select[@name="checkout_time"]//option[@value="14:30"]'); + } +} + + + + +class DriveCheckOutBookingPlanBibHotelDieuAt2020_05_12_09_00Test + extends DriveCheckOutBookingTestCase { + protected $_mail; + + public function setUp() { + parent::setUp(); + $this->postDispatch('/opac/drive-checkout/plan/id_bib/1/checkout_date/2020-05-12', + ['checkout_time' => '09:00']); + $this->_mail = $this->_mail_transport->sent_mail; + } + + + /** @test */ + public function checkoutShouldBePlanned() { + $this->assertNotNull(Class_DriveCheckout::findFirstBy(['user_id' => $this->_marcus->getId(), + 'library_id' => 1, + 'start_at' => '2020-05-12 09:00:00'])); + } + + + /** @test */ + public function shouldRedirectToConfirm() { + $this->assertRedirectContains('/drive-checkout/plan'); + } + + + /** @test */ + public function notificationShouldContainsRendezVousDetails() { + $this->assertFlashMessengerContentContains('Le retrait de vos documents de Hotel-Dieu est planifié pour le mardi 12 mai à 09h00.'); + } + + + /** @test */ + public function mailShouldBeSent() { + $this->assertNotNull($this->_mail); + } + + + /** @test */ + public function mailSubjectShouldBeConfirmationDeRdv() { + $this->assertContains('Confirmation de rendez-vous à Hotel-Dieu', + quoted_printable_decode($this->_mail->getSubject())); + } + + + /** @test */ + public function mailBodyShouldContainsCheckoutDetails() { + $this->assertContains('le mardi 12 mai à 09h00', + quoted_printable_decode($this->_mail->getBodyHtml(true))); + } + + + /** @test */ + public function mailFirstRecipientShouldBeMarus() { + $this->assertEquals('mm@any-serveur.eu', $this->_mail->getRecipients()[0]); + } + + + /** @test */ + public function mailSiteShouldBeInBcc() { + $this->assertContains('zemail@mabib.st', $this->_mail->getHeaders()['Bcc']); + } + + + /** @test */ + public function mailFromShouldBeMailSite() { + $this->assertEquals('zemail@mabib.st', $this->_mail->getFrom()); + } + + + /** @test */ + public function mailShouldHaveAttachment() { + $this->assertTrue($this->_mail->hasAttachments); + } + + + /** @test */ + public function mailShouldHaveCalendarIcsPart() { + $part = (new Storm_Collection($this->_mail->getParts())) + ->detect(function($each) + { + return Class_ICal_DriveCheckout::MIME_TYPE === $each->type + && 'calendar.ics' === $each->filename; + }); + + $this->assertNotNull($part); + } +} + + + + +class DriveCheckOutBookingPlanBibHotelDieuAt2020_05_12_05_00Test + extends DriveCheckOutBookingTestCase { + + public function setUp() { + parent::setUp(); + $this->postDispatch('/opac/drive-checkout/plan/id_bib/1/checkout_date/2020-05-12', + ['checkout_time' => '05:00']); + } + + + /** @test */ + public function checkoutShouldNotBePlanned() { + $this->assertNull(Class_DriveCheckout::findFirstBy(['user_id' => $this->_marcus->getId(), + 'library_id' => 1, + 'start_at' => '2020-05-12 05:00:00'])); + } + + + /** @test */ + public function shouldRedirectToTimeSelection() { + $this->assertRedirectTo('/drive-checkout/plan/id_bib/1/checkout_date/2020-05-12'); + } + + + /** @test */ + public function notificationShouldContainsUnavailableTime() { + $this->assertFlashMessengerContentContains('L\'horaire choisi n\'est pas disponible'); + } +} + + + + +class DriveCheckOutBookingPlanNotLoggedTest extends DriveCheckOutBookingTestCase { + public function setUp() { + parent::setUp(); + ZendAfi_Auth::getInstance()->clearIdentity(); + $this->dispatch('/opac/drive-checkout/plan'); + } + + + /** @test */ + public function pageShouldContainLoginForm() { + $this->assertXPath('//div[@class="contenu"]//form[contains(@action, "/auth/login")]'); + } +} + + + + +class DriveCheckOutBookingPlanWithoutHoldsTest extends DriveCheckOutBookingTestCase { + public function setUp() { + parent::setUp(); + + $this + ->_marcus + ->setFicheSigb(['type_comm' => Class_IntBib::COM_NANOOK, + 'fiche' => (new Class_WebService_SIGB_Emprunteur('10', 'Marcus'))]); + + $this->dispatch('/opac/drive-checkout/plan'); + } + + + /** @test */ + public function pageShouldContainsNoHoldMessage() { + $this->assertXPathContentContains('//div//p', 'Aucun document à retirer pour l\'instant'); + } +} \ No newline at end of file diff --git a/tests/scenarios/DriveCheckOut/DriveCheckoutAdminControllerTest.php b/tests/scenarios/DriveCheckOut/DriveCheckoutAdminControllerTest.php new file mode 100644 index 0000000000000000000000000000000000000000..a19005ead070fff82c2afee64f2c795334e21cc1 --- /dev/null +++ b/tests/scenarios/DriveCheckOut/DriveCheckoutAdminControllerTest.php @@ -0,0 +1,617 @@ +<?php +/** + * Copyright (c) 2012-2020, 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 + */ + + +require_once('application/modules/admin/controllers/DriveCheckoutController.php'); + +abstract class DriveCheckOutAdminControllerTestCase extends Admin_AbstractControllerTestCase { + protected + $_storm_default_to_volatile = true, + $_mail_transport; + + public function setUp() { + parent::setUp(); + + $timesource = new TimeSourceForTest('2020-05-12 10:00:00'); + Admin_DriveCheckoutController::setTimeSource($timesource); + Class_DriveCheckout_Plan::setTimeSource($timesource); + + Class_AdminVar::set('ENABLE_DRIVE_CHECKOUT', 1); + + $this->_mail_transport = new MockMailTransport(); + Zend_Mail::setDefaultTransport($this->_mail_transport); + + $lib_hotel_dieu = $this->fixture('Class_Bib', + ['id' => 1, + 'libelle' => 'Hotel-Dieu', + 'enable_drive' => true, + 'ouvertures' => [ + Class_Ouverture::chaqueLundi('00:00', '00:00', '12:00', '18:00') + ->beDrive(), + Class_Ouverture::chaqueMardi('09:00', '12:00', '14:00', '18:00') + ->beDrive()]]); + + $lib_camus = $this->fixture('Class_Bib', + ['id' => 2, + 'libelle' => 'Albert Camus', + 'enable_drive' => true]); + + $lib_maurissette = $this->fixture('Class_Bib', + ['id' => 3, + 'libelle' => 'Maurissette', + 'enable_drive' => false]); + + + $emilie = $this->fixture('Class_Users', + ['id' => 2, + 'login' => 'emilie', + 'password' => 'secret', + 'role_level' => ZendAfi_Acl_AdminControllerRoles::ABONNE_SIGB, + 'idabon' => 'A121', + 'bib' => $lib_hotel_dieu]); + + $bernard = $this->fixture('Class_Users', + ['id' => 3, + 'login' => 'bernard', + 'password' => 'secret', + 'role_level' => ZendAfi_Acl_AdminControllerRoles::ABONNE_SIGB, + 'idabon' => 'A123', + 'bib' => $lib_hotel_dieu]); + + $maurice = $this->fixture('Class_Users', + ['id' => 4, + 'login' => 'maurice', + 'password' => 'secret', + 'role_level' => ZendAfi_Acl_AdminControllerRoles::ABONNE_SIGB, + 'idabon' => 'A124', + 'bib' => $lib_hotel_dieu]) + ->setFicheSigb(['type_comm' => Class_IntBib::COM_NANOOK, + 'fiche' => (new Class_WebService_SIGB_Emprunteur(4, 'Maurice')) + ->reservationsAddAll( + [Class_WebService_SIGB_Reservation::newInstanceWithEmptyExemplaire() + ->setExemplaireOPAC($this->fixture('Class_Exemplaire', + ['id' => 2, + 'code_barres' => 'tintin123', + 'notice' => $this->fixture('Class_Notice', + ['id' => 123, + 'titre_principal' => 'Tintin à Dole']), + 'cote' => 'BD2'])) + ->setBibliotheque($lib_camus->getLibelle()) + ->setCodeBarre('tintin123') + ->setTitre('Tintin à Dole') + ->setEtat('On le cherche') + ->setWaitingToBePulled(), + + Class_WebService_SIGB_Reservation::newInstanceWithEmptyExemplaire() + ->setBibliotheque($lib_camus->getLibelle()) + ->setCodeBarre('milou123') + ->setTitre('Milou à Dole') + ->setWaitingToBePulled(), + + Class_WebService_SIGB_Reservation::newInstanceWithEmptyExemplaire() + ->setBibliotheque($lib_maurissette->getLibelle()) + ->setCodeBarre('tournesol123') + ->setEtat('Disponible') + ->setTitre('Tournesol à Maurissette') + ->setWaitingToBePulled(), + + Class_WebService_SIGB_Reservation::newInstanceWithEmptyExemplaire() + ->setBibliotheque($lib_camus->getLibelle()) + ->setCodeBarre('dupont123') + ->setTitre('Dupont à Dole') + ])]); + + + $this->fixture('Class_DriveCheckout', + ['id' => 1, + 'user' => $emilie, + 'library' => $lib_hotel_dieu, + 'start_at' => '2020-05-12 09:00:00']); + + $this->fixture('Class_DriveCheckout', + ['id' => 2, + 'user' => $bernard, + 'library' => $lib_hotel_dieu, + 'start_at' => '2020-05-12 14:00:00']); + + $this->fixture('Class_DriveCheckout', + ['id' => 3, + 'user' => $maurice, + 'library' => $lib_camus, + 'start_at' => '2020-05-14 09:00:00']); + + $this->fixture('Class_DriveCheckout', + ['id' => 4, + 'user' => $emilie, + 'library' => $lib_camus, + 'start_at' => '2020-05-14 09:15:00']); + + $this->fixture('Class_DriveCheckout', + ['id' => 5, + 'user' => $bernard, + 'library' => $lib_camus, + 'start_at' => '2020-05-15 10:00:00']); + + } +} + + + +class DriveCheckoutAdminControllerAdminVarTest extends DriveCheckOutAdminControllerTestCase { + /** @test */ + public function withAdminVarEnableDriveFalseShouldNotDisplayMenuEntry() { + Class_AdminVar::set('ENABLE_DRIVE_CHECKOUT', 0); + $this->dispatch('/admin/index'); + $this->assertNotXPath('//a[contains(@href, "/admin/drive-checkout")]'); + } + + + /** @test */ + public function withAdminVarEnableDriveFalseShouldRedirectToIndexOnAdminDriveCheckout() { + Class_AdminVar::set('ENABLE_DRIVE_CHECKOUT', 0); + $this->dispatch('/admin/drive-checkout'); + $this->assertRedirectTo('/opac/index/index/id_profil/1'); + } + + + /** @test */ + public function withAdminVarEnabledUsersEditMauriceShouldContainsLinkToPlanCheckoutForMaurice() { + $this->dispatch('/admin/users/edit/id/4'); + $this->assertXPath('//a[contains(@href, "/admin/drive-checkout/plan/id_user/4")]'); + } + + + /** @test */ + public function withAdminVarEnabledFalseUsersEditMauriceShouldNotContainsLinkToPlanCheckoutForMaurice() { + Class_AdminVar::set('ENABLE_DRIVE_CHECKOUT', 0); + $this->dispatch('/admin/users/edit/id/4'); + $this->assertNotXPath('//a[contains(@href, "/admin/drive-checkout/plan/id_user/4")]'); + } + + + /** @test */ + public function withAdminVarEnabledShouldDisplayMenuEntry() { + $this->dispatch('/admin/index'); + $this->assertXPath('//a[contains(@href, "/admin/drive-checkout")]'); + } +} + + + + +class DriveCheckoutAdminControllerIndexTest extends DriveCheckOutAdminControllerTestCase { + public function setUp() { + parent::setUp(); + $this->dispatch('/admin/drive-checkout'); + } + + + /** @test */ + public function pageTitleShouldBeDrive() { + $this->assertXPathContentContains('//h1', 'Drive'); + } + + + /** @test */ + public function pageShouldContainsLinkToLibraryHotelDieu() { + $this->assertXPathContentContains('//a[contains(@href, "/admin/drive-checkout/list/id_bib/1")]', + 'Hotel-Dieu'); + } + + + /** @test */ + public function pageShouldContainsLinkToLibraryMaurissetteWithDriveDisabled() { + $this->assertXPathContentContains('//h3[text()="Drive désactivé"]/following-sibling::ul/li/a[contains(@href, "/admin/drive-checkout/list/id_bib/3")]', + 'Maurissette'); + } +} + + + + +class DriveCheckoutAdminControllerListHotelDieuTest extends DriveCheckOutAdminControllerTestCase { + public function setUp() { + parent::setUp(); + $this->dispatch('/admin/drive-checkout/list/id_bib/1'); + } + + + /** @test */ + public function pageTitleShouldBeRendezVousHotelDieu() { + $this->assertXPathContentContains('//h1', 'Drive : rendez-vous : Hotel-Dieu, 12 mai 2020'); + } + + + /** @test */ + public function pageShouldContainsInputForDateValue2020_05_12() { + $this->assertXPath('//input[@name="date"][@type="date"][@value="2020-05-12"][@onchange]'); + } + + + /** @test */ + public function pageShouldContainsTableWithCheckout2020_05_12_at_9_00_For_Emilie() { + $this->assertXPath('//table//td[text()="09:00"]/following-sibling::td[text()="A121"]/following-sibling::td[text()="emilie"]'); + } + + + /** @test */ + public function pageShouldContainsTableWithCheckout2020_05_12_at_14_00_For_Bernard() { + $this->assertXPath('//table//td[text()="14:00"]/following-sibling::td[text()="A123"]/following-sibling::td[text()="bernard"]'); + } + + + /** @test */ + public function pageShouldContainsTwoCheckouts() { + $this->assertXPathCount('//table[@id="checkouts"]/tbody/tr', 2); + } +} + + + + +class DriveCheckoutAdminControllerListCamusOnMayFourteenthTest extends DriveCheckOutAdminControllerTestCase { + public function setUp() { + parent::setUp(); + $this->dispatch('/admin/drive-checkout/list/id_bib/2/date/2020-05-14'); + } + + + /** @test */ + public function pageTitleShouldBeRendezVousHotelDieu() { + $this->assertXPathContentContains('//h1', 'Drive : rendez-vous : Albert Camus, 14 mai 2020'); + } + + + /** @test */ + public function pageShouldContainsTableWithCheckout2020_05_14_at_9_00_For_Maurice() { + $this->assertXPath('//table//td[text()="09:00"]/following-sibling::td[text()="A124"]/following-sibling::td[text()="maurice"]', $this->_response->getBody()); + } + + + /** @test */ + public function tableShouldContainsLinkToListHoldsAssociatedToCheckoutThree() { + $this->assertXPath('//table[@id="checkouts"]//td/a[contains(@href, "/admin/drive-checkout/list-holds/id_bib/2/date/2020-05-14/id/3")][@data-popup="true"]'); + } + + + /** @test */ + public function tableShouldContainsLinkToDeleteCheckoutThree() { + $this->assertXPath('//table[@id="checkouts"]//td/a[contains(@href, "/admin/drive-checkout/delete/id_bib/2/date/2020-05-14/id/3")]'); + } + + + /** @test */ + public function tableShouldContainsLinkToEditUserMauriceIdFour() { + $this->assertXPath('//table[@id="checkouts"]//td/a[contains(@href, "/admin/users/edit/id/4")]'); + } + + + /** @test */ + public function pageShouldContainsTwoCheckouts() { + $this->assertXPathCount('//table[@id="checkouts"]/tbody/tr', 2); + } + + + /** @test */ + public function pageShouldContainsLinkToListCSVIdBib2Date2020_05_14() { + $this->assertXPathContentContains('//button[contains(@data-url, "/admin/drive-checkout/list-csv/id_bib/2/date/2020-05-14")]', 'Exporter les rendez-vous (.csv)'); + } + + + /** @test */ + public function pageShouldContainsLinkToItemsCSVIdBib2Date2020_05_14() { + $this->assertXPathContentContains('//button[contains(@data-url, "/admin/drive-checkout/items-csv/id_bib/2/date/2020-05-14")]', 'Exporter les documents (.csv)'); + } +} + + + + +class DriveCheckoutAdminControllerExportCSVCamusOnMayFourteenthTest extends DriveCheckOutAdminControllerTestCase { + public function setUp() { + parent::setUp(); + $this->dispatch('/admin/drive-checkout/list-csv/id_bib/2/date/2020-05-14'); + } + + + /** @test */ + public function filenameShouldBe2020_05_14_Albert_Camus_Rendez_Vous() { + $this->assertContains(['name' => 'Content-Type', + 'value' => 'text/csv; name="2020-05-14 Albert Camus rendez-vous.csv"', + 'replace' => true], $this->_response->getHeaders()); + } + + + /** @test */ + public function csvShouldContainsCheckouts() { + $this->assertEquals("Heure;Carte;Abonné\n" + ."09:00;A124;maurice\n" + ."09:15;A121;emilie\n", + $this->_response->getBody()); + } +} + + + + +class DriveCheckoutAdminControllerListHoldsForCheckoutThreeTest extends DriveCheckOutAdminControllerTestCase { + public function setUp() { + parent::setUp(); + $this->dispatch('/admin/drive-checkout/list-holds/id/3'); + } + + + /** @test */ + public function pageTitleShouldBeDocumentsForMaurice() { + $this->assertXPathContentContains('//title', 'maurice, A124, 14 mai 09:00, Albert Camus'); + } + + + /** @test */ + public function tableShouldContainsHoldOnTintin() { + $this->assertXPath('//table//td[text()="BD2"]/following-sibling::td[text()="tintin123"]/following-sibling::td[text()="On le cherche"]/following-sibling::td[text()="Tintin à Dole"]/following-sibling::td/a[contains(@href, "/recherche/viewnotice/id/123")]'); + } + + + /** @test */ + public function tableShouldNotContainsHoldOnTournesol() { + $this->assertNotXPath('//table//td[contains(text(),"Tournesol")]'); + } + + + /** @test */ + public function tableShouldContainsHoldOnDupont() { + $this->assertXPath('//table//td[contains(text(), "Dupont")]'); + } + + + /** @test */ + public function pageShouldContainsLinkToShowAllHolds() { + $this->assertXPathContentContains('//a[contains(@href, "/admin/drive-checkout/list-all-holds/id_user/4")]', + 'Voir toutes les réservations de maurice'); + } +} + + + + +class DriveCheckoutAdminControllerListAllHoldsForMauriceIdFourTest extends DriveCheckOutAdminControllerTestCase { + public function setUp() { + parent::setUp(); + $this->dispatch('/admin/drive-checkout/list-all-holds/id_user/4'); + } + + + /** @test */ + public function tableShouldContainsHoldOnTournesol() { + $this->assertXPath('//table//td[text()="Maurissette"]/following-sibling::td[text()="tournesol123"]/following-sibling::td[text()="Disponible"]/following-sibling::td[text()="Tournesol à Maurissette"]'); + } + + + /** @test */ + public function tableShouldContainsHoldOnTintin() { + $this->assertXPath('//table//td[text()="Tintin à Dole"]'); + } +} + + + + +class DriveCheckoutAdminControllerExportItemsCSVCamusOnMayFourteenthTest extends DriveCheckOutAdminControllerTestCase { + public function setUp() { + parent::setUp(); + $this->dispatch('/admin/drive-checkout/items-csv/id_bib/2/date/2020-05-14'); + } + + + /** @test */ + public function filenameShouldBe2020_05_14_Albert_Camus_Rendez_Vous() { + $this->assertContains(['name' => 'Content-Type', + 'value' => 'text/csv; name="2020-05-14 Albert Camus documents.csv"', + 'replace' => true], $this->_response->getHeaders()); + } + + + /** @test */ + public function csvShouldContainsTintinMilouAndDupontItems() { + $this->assertEquals("Jour;Heure;Carte;Abonné;Cote;Code-barres;Titre\n" + . "\"14 mai\";09:00;A124;maurice;BD2;tintin123;\"Tintin à Dole\"\n" + . "\"14 mai\";09:00;A124;maurice;;milou123;\"Milou à Dole\"\n" + . "\"14 mai\";09:00;A124;maurice;;dupont123;\"Dupont à Dole\"\n", + $this->_response->getBody()); + } +} + + + + +class DriveCheckoutAdminControllerDeleteCheckoutThreeTest extends DriveCheckOutAdminControllerTestCase { + public function setUp() { + parent::setUp(); + $this->dispatch('/admin/drive-checkout/delete/id/3'); + } + + + /** @test */ + public function responseShouldRedirectToDriveCheckoutIdBib2Date2020_05_14() { + $this->assertRedirectTo('/admin/drive-checkout/list/id_bib/2/date/2020-05-14'); + } + + + /** @test */ + public function responsShouldNotifyCheckoutDeleted() { + $this->assertFlashMessengerContentContains('Rendez-vous pour maurice supprimé'); + } + + + /** @test */ + public function checkoutShouldHaveBeenDeleted() { + Class_DriveCheckout::clearCache(); + $this->assertNull(Class_DriveCheckout::find(3)); + } +} + + + + +class DriveCheckoutAdminControllerPlanNewCheckoutForMauriceTest extends DriveCheckOutAdminControllerTestCase { + public function setUp() { + parent::setUp(); + Class_DriveCheckout::find(3)->delete(); + $this->dispatch('/admin/drive-checkout/plan/id_user/4'); + } + + + /** @test */ + public function pageTitleShouldBePlanCheckout() { + $this->assertXPathContentContains('//title', 'Planifier un retrait pour maurice'); + } + + + /** @test */ + public function pageShouldContainsLinkToChooseHotelDieu() { + $this->assertXPath('//a[contains(@href, "/plan/id_user/4/id_bib/1")]'); + } + + + /** @test */ + public function pageShouldContainsLinkToShowAllHolds() { + $this->assertXPathContentContains('//a[contains(@href, "/admin/drive-checkout/list-all-holds/id_user/4")]', + 'Voir toutes les réservations de maurice'); + } +} + + + + +class DriveCheckoutAdminControllerDownloadCheckoutThreeTest + extends DriveCheckOutAdminControllerTestCase { + public function setUp() { + parent::setUp(); + + Class_DriveCheckout::find(3) + ->getLibrary() + ->setLieu($this->fixture('Class_Lieu', + ['id' => 233, + 'libelle' => 'Quelque part', + 'latitude' => '-38.812239', + 'longitude' => '177.137570'])); + + $this->dispatch('/admin/drive-checkout/ical/id/3'); + } + + + /** @test */ + public function contentTypeShouldBeTextCalendar() { + $this->assertHeaderContains('Content-Type', 'text/calendar;charset=utf-8'); + } + + + /** @test */ + public function contentDispositionShouldBeAttachment() { + $this->assertHeaderContains('Content-Disposition', 'attachment;filename="calendar.ics'); + } + + + /** @test */ + public function uidShouldBeDriveCheckout3() { + $this->assertContains('drive-checkout:3', $this->_response->getBody()); + } + + + /** @test */ + public function dateTimeShouldBe20200514T090000WithTimezone() { + $this->assertContains('DTSTART;TZID=Europe/Paris:20200514T090000', $this->_response->getBody()); + } + + + /** @test */ + public function urlToPlanInCurrentProfilShouldBePresent() { + $this->assertContains('/drive-checkout/plan/id_profil/2', $this->_response->getBody()); + } + + + /** @test */ + public function summaryShouldBeRetraitDesReservations() { + $this->assertContains('SUMMARY:Retrait des réservations à Albert Camus', + $this->_response->getBody()); + } + + + /** @test */ + public function locationShouldBeAlbertCamus() { + $this->assertContains('LOCATION:Albert Camus', $this->_response->getBody()); + } + + + /** @test */ + public function geoShouldBeLat38Long177() { + $this->assertContains('GEO:-38.812239;177.137570', $this->_response->getBody()); + } +} + + + + +class DriveCheckoutAdminControllerPlanInvalidCheckoutForMauriceHotelDieuOnMayFourteenthTest extends DriveCheckOutAdminControllerTestCase { + public function setUp() { + parent::setUp(); + Class_DriveCheckout::find(3)->delete(); + $this->dispatch('/admin/drive-checkout/plan/id_user/4/id_bib/1/checkout_date/2020-05-14'); + } + + + /** @test */ + public function pageShouldRedirectToDriveCheckoutPlanIdUserFourIdBibOne() { + $this->assertRedirectTo('/admin/drive-checkout/plan/id_user/4/id_bib/1'); + } + + + /** @test */ + public function pageShouldNotifyInvalidDate() { + $this->assertFlashMessengerContentContains('La date choisie n\'est pas disponible'); + } +} + + + + +class DriveCheckoutAdminControllerPlanPostNewCheckoutForMauriceHotelDieuOnMayNineteenthTest + extends DriveCheckOutAdminControllerTestCase { + public function setUp() { + parent::setUp(); + Class_DriveCheckout::find(3)->delete(); + $this->postDispatch('/admin/drive-checkout/plan/id_user/4/id_bib/1/checkout_date/2020-05-19', + ['checkout_time' => '10:00']); + } + + + /** @test */ + public function pageShouldRedirectToDriveCheckoutListIdBibOneDate2020_05_19() { + $this->assertRedirectTo('/admin/drive-checkout/list/id_bib/1/date/2020-05-19'); + } + + + /** @test */ + public function pageShouldNotifyCheckoutSaved() { + $this->assertFlashMessengerContentContains('Retrait planifié pour maurice, le 19 mai 10:00, Hotel-Dieu'); + } +} \ No newline at end of file diff --git a/tests/scenarios/DriveCheckOut/DriveCheckoutOpeningsTest.php b/tests/scenarios/DriveCheckOut/DriveCheckoutOpeningsTest.php new file mode 100644 index 0000000000000000000000000000000000000000..e6f30ff9901d1f70b795147da2c8dee3ea3f4a4b --- /dev/null +++ b/tests/scenarios/DriveCheckOut/DriveCheckoutOpeningsTest.php @@ -0,0 +1,287 @@ +<?php +/** + * Copyright (c) 2012-2020, 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 + */ + +abstract class DriveCheckoutOpeningsTestCase extends Admin_AbstractControllerTestCase { + protected $_storm_default_to_volatile = true; + + public function setUp(){ + parent::setUp(); + Class_AdminVar::set('ENABLE_DRIVE_CHECKOUT', 1); + $this->fixture('Class_Bib', + ['id' => 3, + 'libelle' => 'Carthage', + 'enable_drive' => true + ]); + + $this->fixture('Class_Bib', + ['id' => 5, + 'libelle' => 'Alexandrie', + 'enable_drive' => false + ]); + + $this->fixture('Class_Ouverture', + ['id' => 1, + 'debut_matin' => '10:30', + 'fin_matin' => '11:30', + 'debut_apres_midi' => '14:00', + 'fin_apres_midi' => '15:00', + 'id_site' => 3, + 'used_for' => Class_Ouverture::USED_FOR_LIBRARY, + 'jour_semaine' => Class_Ouverture::VENDREDI, + 'jour' => '01/01/2020', + 'validity_start' => '00/00/0000', + 'validity_end' => '' + ] + ); + + $this->fixture('Class_Ouverture', + ['id'=> 10, + 'debut_matin' => '09:00', + 'fin_matin' => '11:00', + 'debut_apres_midi' => '16:00', + 'fin_apres_midi' => '17:00', + 'id_site' => 3, + 'used_for' => Class_Ouverture::USED_FOR_MULTIMEDIA, + 'jour_semaine' => Class_Ouverture::MARDI, + 'jour' => '23/10/2020', + 'validity_start' => '00/00/0000', + 'validity_end' => '' + ] + ); + + + $this->fixture('Class_Ouverture', + ['id'=> 12, + 'debut_matin' => '09:00', + 'fin_matin' => '11:00', + 'max_per_period_matin' => 3, + 'debut_apres_midi' => '16:00', + 'fin_apres_midi' => '17:00', + 'max_per_period_apres_midi' => 10, + 'id_site' => 3, + 'used_for' => Class_Ouverture::USED_FOR_DRIVE, + 'jour_semaine' => Class_Ouverture::MARDI, + 'jour' => '23/08/2020', + 'validity_start' => '00/00/0000', + 'validity_end' => '' + ] + ); + + $this->fixture('Class_Ouverture', + ['id'=> 13, + 'debut_matin' => '09:00', + 'fin_matin' => '11:00', + 'debut_apres_midi' => '16:00', + 'fin_apres_midi' => '17:00', + 'id_site' => 5, + 'used_for' => Class_Ouverture::USED_FOR_DRIVE, + 'jour_semaine' => Class_Ouverture::LUNDI, + 'jour' => '23/08/2020', + 'validity_start' => '00/00/0000', + 'validity_end' => '' + ] + ); + + } +} + + + + +class DriveCheckoutOpeningsDeactivatedTest extends DriveCheckoutOpeningsTestCase { + public function setUp() { + parent::setUp(); + Class_AdminVar::set('ENABLE_DRIVE_CHECKOUT', 0); + } + + + /** @test */ + public function openingsDriveLinkShouldNotBePresent() { + $this->dispatch('/admin/ouvertures/index/id_site/3'); + $this->assertNotXPath('//a[contains(@href, "/admin/ouvertures/index/id_site/3/used_for/2")]'); + } + + + /** @test */ + public function carthageLibraryFormShouldNotContainsCheckBoxEnableDrive() { + $this->dispatch('/admin/bib/edit/id/3'); + $this->assertNotXPath('//form//input[@name="enable_drive"]'); + } +} + + + + +class DriveCheckoutOpeningsAddNewTest extends DriveCheckoutOpeningsTestCase { + public function setUp() { + parent::setUp(); + + $this->postDispatch('/admin/ouvertures/add/used_for/2', + ['debut_matin' => '10:30', + 'fin_matin' => '11:30', + 'debut_apres_midi' => '14:00', + 'fin_apres_midi' => '15:00', + 'id_site' => 3, + 'jour_semaine' => Class_Ouverture::MARDI, + 'jour' => '23/10/2012', + 'validity_start' => '00/00/0000', + 'validity_end' => '' + ] + ); + } + + + /** @test */ + public function responseShouldRedirectToOuverturesIndexSiteThreeUsed_ForTwo() { + $this->assertRedirectTo('/admin/ouvertures/index/id_site/3/used_for/2'); + } + + + /** @test */ + public function ouvertureUsed_ForShouldBeDRIVE() { + $this->assertEquals(Class_Ouverture::USED_FOR_DRIVE, + Class_Ouverture::find(13)->getUsedFor()); + } +} + + + + +class DriveCheckoutOpeningsIndexTest extends DriveCheckoutOpeningsTestCase { + public function setUp(){ + parent::setUp(); + $this->dispatch('/admin/ouvertures/index/id_site/3/used_for/2'); + } + + + /** @test */ + public function pageShouldNotContainsLinkToEditOpening1() { + $this->assertNotXPath('//a[@href="/admin/ouvertures/edit/id_site/3/used_for/2/id/1"]'); + } + + + /** @test */ + public function pageShouldNotContainsLinkToEditOpening10() { + $this->assertNotXPath('//a[contains(@href, "/ouvertures/edit/id_site/3/used_for/2/id/10")]'); + } + + + /** @test */ + public function pageShouldNotContainsLinkToEditOpening13() { + $this->assertNotXPath('//a[contains(@href, "/ouvertures/edit/id_site/3/used_for/2/id/13")]'); + } + + + /** @test */ + public function pageShouldContainsLinkToEditOpening12() { + $this->assertXPath('//a[@href="/admin/ouvertures/edit/id_site/3/used_for/2/id/12"]', $this->_response->getBody()); + } + + + /** @test */ + public function tdDataOpening12ShouldContains9h00dash11h00And3PersonsPerSlot() { + $this->assertXPath('//td[text()="09h00 - 11h00"]/following-sibling::td[text()="3 pers."]', $this->_response->getBody()); + } + + + /** @test */ + public function pageShouldContainsActionButtonToAddDriveOpening() { + $this->assertXPathContentContains('//button[@data-url="/admin/ouvertures/add/id_site/3/used_for/2"]', 'Ajouter une plage d\'ouverture du drive'); + } + + + /** @test */ + public function pageShouldContainsLinkToOuverturesIndexIdSite3() { + $this->assertXPathContentContains('//a[contains(@href, "/admin/ouvertures/index/id_site/3/used_for/2")]', + 'Planification des ouvertures du drive'); + } + + + /** @test */ + public function pageShouldNotContainsCheckboxClosedOnHolidays() { + $this->assertNotXPath('//form//input[@name="closed_on_holidays"]'); + } + +} + + + + +class DriveCheckoutOpeningsEditTest extends DriveCheckoutOpeningsTestCase { + protected $_storm_default_to_volatile = true; + + public function setUp(){ + parent::setUp(); + $this->dispatch('/admin/ouvertures/edit/id_site/3/used_for/2/id/12'); + } + + + /** @test */ + public function pageShouldContainsInputTextForMaxPerPeriodMatin() { + $this->assertXPath('//input[@type="number"][@name="max_per_period_matin"][@value="3"]', $this->_response->getBody()); + } + + +/** @test */ + public function pageShouldContainsInputTextForMaxPerPeriodApresMidi() { + $this->assertXPath('//input[@type="number"][@name="max_per_period_apres_midi"][@value="10"]'); + } +} + + + + +class DriveCheckoutOpeningsAddGetTest extends DriveCheckoutOpeningsTestCase { + public function setUp(){ + parent::setUp(); + $this->dispatch('/admin/ouvertures/add/id_site/3/used_for/2/id/12'); + } + + + /** @test */ + public function pageShouldContainsInputTextForMaxPerPeriodMatin() { + $this->assertXPath('//input[@type="number"][@name="max_per_period_matin"][@value="1"]', $this->_response->getBody()); + } + + +/** @test */ + public function pageShouldContainsInputTextForMaxPerPeriodApresMidi() { + $this->assertXPath('//input[@type="number"][@name="max_per_period_apres_midi"][@value="1"]'); + } +} + + + + +class DriveCheckoutOpeningsEditLibraryCarthageTest extends DriveCheckoutOpeningsTestCase { + /** @test */ + public function carthageLibraryFormShouldContainsCheckBoxEnableDriveChecked() { + $this->dispatch('/admin/bib/edit/id/3'); + $this->assertXPath('//form//input[@type="checkbox"][@name="enable_drive"][@checked]',$this->_response->getBody()); + } + + + /** @test */ + public function alexandrieLibraryFormShouldContainsCheckBoxEnableDriveNotChecked() { + $this->dispatch('/admin/bib/edit/id/5'); + $this->assertXPath('//form//input[@type="checkbox"][@name="enable_drive"][not(@checked)]',$this->_response->getBody()); + } +}