diff --git a/VERSIONS_WIP/111127 b/VERSIONS_WIP/111127 new file mode 100644 index 0000000000000000000000000000000000000000..a2f406ad2b5dc4d48f80bafac0c87d52113716bc --- /dev/null +++ b/VERSIONS_WIP/111127 @@ -0,0 +1,10 @@ + - ticket #111127 : Drive : + - administration : ajout des colonnes Section et Emplacement dans l'export des documents + - administration : possibilité de visualiser plusieurs journées de rendez-vous + - confirmation de rendez-vous : utilisation du mail renseigné dans la fiche bibliothèque en priorité + - popup de connexion : n'affiche le bouton "planifier le retrait de mes documents" seulement si un document en attente de retrait dans une bibliothèque avec drive activé + - prise de rendez-vous : s'il n'y a qu'un site de retrait possible l'abonné est dirigé directement sur le choix d'une date pour ce site + - prise de rendez-vous : nouvelle variable "DRIVE_DISABLE_HOLD_CHECK" permettant la prise de rendez-vous sans vérification de l'existence ou de l'état des réservations de l'abonné + - prise de rendez-vous : modification du libellé de suppression pour "Annuler ou replanifier", ajout de détail dans le message de confirmation de suppression + - prise de rendez-vous : ajout d'une notification par mail lors de la suppression + - prise de rendez-vous : enregistrement de la date de création du rendez-vous qui est affichée en administration et présentée dans les exports csv \ No newline at end of file diff --git a/application/modules/admin/controllers/DriveCheckoutController.php b/application/modules/admin/controllers/DriveCheckoutController.php index 161a15f2df4033b1ee105d30ebcdbff401cc1156..ea2a7003fbed955f69ed341e8eeafe96b1f0743a 100644 --- a/application/modules/admin/controllers/DriveCheckoutController.php +++ b/application/modules/admin/controllers/DriveCheckoutController.php @@ -35,13 +35,13 @@ class Admin_DriveCheckoutController extends ZendAfi_Controller_Action { public function listAction() { $this->view->library = Class_Bib::find($this->_getParam('id_bib')); $this->view->date = $this->_getParam('date', $this->getCurrentDate()); + $this->view->days = (int)$this->_getParam('days', 1); $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->checkouts = Class_DriveCheckout::findAllByLibraryAndDates($this->view->library, + $this->view->date, + $this->view->days); $this->view->table_description = new Class_TableDescription_DriveCheckout_ListWithActions('checkouts'); } diff --git a/application/modules/admin/controllers/SessionActivityController.php b/application/modules/admin/controllers/SessionActivityController.php index b93a06c232806d94c6d2e550abb5081574b5a39a..f02b6bc5886d0152facf6b0eae36f3b67dfa48f5 100644 --- a/application/modules/admin/controllers/SessionActivityController.php +++ b/application/modules/admin/controllers/SessionActivityController.php @@ -308,6 +308,8 @@ class SessionOneLetterPerDayFusionStrategy extends AbstractSessionFusionStrategy class FusionDateContext { + use Trait_GetterByAttributeName; + protected $_current_date; public function __construct($datestr=null) { @@ -339,11 +341,6 @@ class FusionDateContext { $other_date->sub($this->_current_date); return ($other_date->toValue(Zend_Date::DAY)-1); } - - - public function callGetterByAttributeName($attribute) { - return call_user_func(array($this, 'get'.Storm_Inflector::camelize($attribute))); - } } diff --git a/application/modules/admin/views/scripts/drive-checkout/list.phtml b/application/modules/admin/views/scripts/drive-checkout/list.phtml index 89ac4e7de436abe227d087420b9cbb61c0ab965a..1a6acb73cf876f83e370166ddd64f286ad09935a 100644 --- a/application/modules/admin/views/scripts/drive-checkout/list.phtml +++ b/application/modules/admin/views/scripts/drive-checkout/list.phtml @@ -16,12 +16,25 @@ echo $this->Button((new Class_Entity()) ->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']); +$days_url = $this->url(['days' => null]) . '/days/'; + +echo $this + ->tag('form', + $this->tag('label', $this->_('Date'), + ['for' => 'date', 'style' => 'margin-right: 5px']) + . $this->formDate('date', + $this->date, + ['onchange' => 'window.location=\'' . $date_url . '\' + this.value']) + . BR + . $this->tag('label', $this->_('Nombre de jours à afficher'), + ['for' => 'days', 'style' => 'margin-right: 5px;']) + . $this->formNumber('days', + $this->days, + ['onchange' => 'window.location=\'' . $days_url . '\' + this.value', + 'size' => 2]) + ); -echo $this->renderTable($this->table_description, $this->checkouts); +echo $this->renderTable($this->table_description, + (new Class_TableDescription_Models($this->checkouts))->groupBy('day_label')); diff --git a/application/modules/opac/controllers/DriveCheckoutController.php b/application/modules/opac/controllers/DriveCheckoutController.php index 334fe48d4f3b4e67a43d5b0eb4aacd4bfea8b41a..3088d7be848511c26c5615387b2289fdf2d5c722 100644 --- a/application/modules/opac/controllers/DriveCheckoutController.php +++ b/application/modules/opac/controllers/DriveCheckoutController.php @@ -42,6 +42,12 @@ class DriveCheckoutController extends ZendAfi_Controller_Action { return; } + if ($library = $plan->onlyPossibleLibrary()) { + $this->getHelper('ViewRenderer')->setNoRender(); + $this->_redirect($this->view->url($plan->libraryUrlFor($library))); + return; + } + if ($this->_request->isPost() && ($checkout = $plan->persist())) { $this->getHelper('ViewRenderer')->setNoRender(); @@ -64,6 +70,7 @@ class DriveCheckoutController extends ZendAfi_Controller_Action { $message = $this->_('Impossible de supprimer un retrait inconnu.'); if ($checkout = Class_DriveCheckout::findFor($this->_getParam('id'), $this->_user)) { $checkout->delete(); + $checkout->notifydelete(); $message = $this->_('Le retrait de vos documents de %s planifié pour %s a été supprimé.', $checkout->getLibraryLabel(), $checkout->getDateTimeLabel()); diff --git a/cosmogramme/sql/patch/patch_390.php b/cosmogramme/sql/patch/patch_390.php new file mode 100644 index 0000000000000000000000000000000000000000..f1fa358912d1d1b35feccca3d7d6ab35d1b14df0 --- /dev/null +++ b/cosmogramme/sql/patch/patch_390.php @@ -0,0 +1,6 @@ +<?php +$adapter = Zend_Db_Table::getDefaultAdapter(); + +try { + $adapter->query('alter table drive_checkout add created_at datetime not null'); +} catch (Exception $e) {} diff --git a/library/Class/AdminVar.php b/library/Class/AdminVar.php index c8d2f8c801157bf0abfa9cd19bcbd3f33a7b130a..d635d59fd253a601e14b0d39c836ed588f83d904 100644 --- a/library/Class/AdminVar.php +++ b/library/Class/AdminVar.php @@ -554,10 +554,15 @@ 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_DISABLE_HOLD_CHECK' => Class_AdminVar_Meta::newOnOff($this->_('Permettre la prise de rendez-vous sans vérification de l\'existence ou de l\'état des réservations de l\'abonné')), '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>']), + 'DRIVE_TEMPLATE_DELETE_RDV_SUBJECT' => Class_AdminVar_Meta::newDefault($this->_('Sujet des courriels de suppression de rendez-vous drive.'), + ['value'=> 'Suppression de votre rendez-vous à {library.libelle}']), + 'DRIVE_TEMPLATE_DELETE_RDV_CONTENT' => Class_AdminVar_Meta::newEditor($this->_('Modèle utilisé pour les courriels de suppression de rendez-vous drive.'), + ['value'=> '<p>Bonjour {user.nom_complet},</p> <p>nous vous confirmons la suppression de votre rendez-vous <strong> à {library.libelle} {rendez_vous.date_time_label}</strong>.</p>']), ]; } @@ -1148,6 +1153,11 @@ class Class_AdminVarLoader extends Storm_Model_Loader { public function isDriveCheckoutEnabled() { return Class_AdminVar::isModuleEnabled('ENABLE_DRIVE_CHECKOUT'); } + + + public function isDriveCheckoutShouldFilterLibraries() { + return !Class_AdminVar::isModuleEnabled('DRIVE_DISABLE_HOLD_CHECK'); + } } diff --git a/library/Class/DriveCheckout.php b/library/Class/DriveCheckout.php index 0526f2d36d6bbcb8492a682def826156a6faefcb..754b326a6861f4d159241a379be2468c1f70ccf8 100644 --- a/library/Class/DriveCheckout.php +++ b/library/Class/DriveCheckout.php @@ -31,6 +31,20 @@ class Class_DriveCheckoutLoader extends Storm_Model_Loader { } + public function findAllByLibraryAndDates($library, $start_date, $days_count) { + $checkouts = new Storm_Model_Collection(); + for($i=0; $i<$days_count; $i++) { + $start_at = strftime('%Y-%m-%d', strtotime($start_date . ' +' . $i . ' day')); + $checkouts + ->addAll(Class_DriveCheckout::findAllBy(['role' => 'library', + 'model' => $library, + 'left(start_at,10)' => $start_at, + 'order' => 'start_at'])); + } + return $checkouts; + } + + public function findFor($id, $user) { return $user ? Class_DriveCheckout::findFirstBy(['id' => (int)$id, @@ -66,7 +80,9 @@ class Class_DriveCheckoutLoader extends Storm_Model_Loader { class Class_DriveCheckout extends Storm_Model_Abstract { - use Trait_Translator; + use Trait_Translator, Trait_TimeSource; + + const DATETIME_FORMAT = 'Y-m-d H:i:s'; protected $_loader_class = 'Class_DriveCheckoutLoader', @@ -80,6 +96,12 @@ class Class_DriveCheckout extends Storm_Model_Abstract { $_date_time_label; + public function beforeSave() { + if ($this->isNew()) + $this->setCreatedAt($this->getCurrentDateTime()); + } + + public function getLibraryLabel() { return ($library = $this->getLibrary()) ? $library->getLibelle() @@ -87,6 +109,11 @@ class Class_DriveCheckout extends Storm_Model_Abstract { } + public function getDayLabel() { + return strftime('%A %d %B %Y', strtotime($this->getStartAt())); + } + + public function getDateTimeLabel() { if (!$this->hasStartAt()) return $this->_('inconnu'); @@ -104,8 +131,29 @@ class Class_DriveCheckout extends Storm_Model_Abstract { } + public function getCreatedAtLabel() { + return ($created = $this->getDateTimeCreatedAt()) + ? strftime($this->_('%d %B à %Hh%M'), $created->getTimestamp()) + : $this->_('n/a'); + } + + + public function getCreatedAtForCsv() { + return ($created = $this->getDateTimeCreatedAt()) + ? $created->format(static::DATETIME_FORMAT) + : ''; + } + + + public function getDateTimeCreatedAt() { + return ($date_time = $this->getCreatedAt()) && $date_time != '0000-00-00 00:00:00' + ? DateTime::createFromFormat(static::DATETIME_FORMAT, $date_time) + : null; + } + + public function getDateTimeStartAt() { - return DateTime::createFromFormat('Y-m-d H:i:s', $this->getStartAt()); + return DateTime::createFromFormat(static::DATETIME_FORMAT, $this->getStartAt()); } @@ -165,70 +213,11 @@ class Class_DriveCheckout extends Storm_Model_Abstract { 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(); + return (new Class_DriveCheckout_CreationNotification($this))->send(); } - public function getNomComplet() { - return $this->_checkout->getNomComplet(); + public function notifyDelete() { + return (new Class_DriveCheckout_DeletionNotification($this))->send(); } - - - 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/CreationNotification.php b/library/Class/DriveCheckout/CreationNotification.php new file mode 100644 index 0000000000000000000000000000000000000000..32adb45d77f71313e48778ccbaa06a52f41cf813 --- /dev/null +++ b/library/Class/DriveCheckout/CreationNotification.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 Class_DriveCheckout_CreationNotification extends Class_DriveCheckout_Notification { + protected function _contentTemplate() { + return Class_AdminVar::getValueOrDefault('DRIVE_TEMPLATE_NEW_RDV_CONTENT'); + } + + + protected function _subjectTemplate() { + return Class_AdminVar::getValueOrDefault('DRIVE_TEMPLATE_NEW_RDV_SUBJECT'); + } + + + protected function _ical() { + return (new Class_ICal_DriveCheckout($this->_checkout)) + ->renderCalendar(Class_Profil::getCurrentProfil()); + } +} diff --git a/library/Class/DriveCheckout/DeletionNotification.php b/library/Class/DriveCheckout/DeletionNotification.php new file mode 100644 index 0000000000000000000000000000000000000000..585cb4c065c9e3080803b0ee6a7bd0c2bf85ee9d --- /dev/null +++ b/library/Class/DriveCheckout/DeletionNotification.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 Class_DriveCheckout_DeletionNotification extends Class_DriveCheckout_Notification { + protected function _contentTemplate() { + return Class_AdminVar::getValueOrDefault('DRIVE_TEMPLATE_DELETE_RDV_CONTENT'); + } + + + protected function _subjectTemplate() { + return Class_AdminVar::getValueOrDefault('DRIVE_TEMPLATE_DELETE_RDV_SUBJECT'); + } + + + protected function _ical() { + return (new Class_ICal_DriveCheckoutCancel($this->_checkout)) + ->renderCalendar(Class_Profil::getCurrentProfil()); + } +} diff --git a/library/Class/DriveCheckout/Hold.php b/library/Class/DriveCheckout/Hold.php new file mode 100644 index 0000000000000000000000000000000000000000..631ca5b63d03046d045ae664ad511dfe94620fed --- /dev/null +++ b/library/Class/DriveCheckout/Hold.php @@ -0,0 +1,109 @@ +<?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_Hold { + use Trait_GetterByAttributeName; + + 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 getCreatedAtForCsv() { + return $this->_checkout->getCreatedAtForCsv(); + } + + + 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 getSectionLabel() { + if (!$this->_item || !$section_id = $this->_item->getSection()) + return ''; + + return ($section = Class_CodifSection::find($section_id)) + ? $section->getLibelle() + : ''; + } + + + public function getEmplacementLabel() { + if (!$this->_item || !$emplacement_id = $this->_item->getEmplacement()) + return ''; + + return ($emplacement = Class_CodifEmplacement::find($emplacement_id)) + ? $emplacement->getLibelle() + : ''; + } + + + public function getRecord() { + return $this->_item + ? $this->_item->getNotice() + : null; + } +} \ No newline at end of file diff --git a/library/Class/DriveCheckout/Notification.php b/library/Class/DriveCheckout/Notification.php index dfa2a0eadda60c17f3a39dca31305246b4876a33..393a861996e28b72f7ded7129d4dd783cc83cae3 100644 --- a/library/Class/DriveCheckout/Notification.php +++ b/library/Class/DriveCheckout/Notification.php @@ -19,7 +19,7 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -class Class_DriveCheckout_Notification { +abstract class Class_DriveCheckout_Notification { protected $_checkout; public function __construct($checkout) { @@ -62,22 +62,39 @@ class Class_DriveCheckout_Notification { 'user' => $user, 'library' => $library]; - $body = (new Class_ModeleFusion()) - ->setContenu(Class_AdminVar::getValueOrDefault('DRIVE_TEMPLATE_NEW_RDV_CONTENT')) - ->setDataSource($data_source) - ->getContenuFusionne(); + $body = $this->_transform($this->_contentTemplate(), $data_source); + $subject = $this->_transform($this->_subjectTemplate(), $data_source); - $subject = (new Class_ModeleFusion()) - ->setContenu(Class_AdminVar::getValueOrDefault('DRIVE_TEMPLATE_NEW_RDV_SUBJECT')) - ->setDataSource($data_source) - ->getContenuFusionne(); + if ($library->hasMail()) + $mailer->setFrom($library->getMail()); $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'; + + if ($ical = $this->_ical()) { + $attachment = $mail->createAttachment($ical, Class_ICal_DriveCheckout::MIME_TYPE); + $attachment->filename = 'calendar.ics'; + } $mailer->send($mail); } + + + protected function _transform($template, $datas) { + return (new Class_ModeleFusion()) + ->setContenu($template) + ->setDataSource($datas) + ->getContenuFusionne(); + } + + + /** @return string Class_ModeleFusion compatible text */ + abstract protected function _subjectTemplate(); + + + /** @return string Class_ModeleFusion compatible text */ + abstract protected function _contentTemplate(); + + + /** @return string checkout as an ICAL event */ + abstract protected function _ical(); } diff --git a/library/Class/DriveCheckout/Plan.php b/library/Class/DriveCheckout/Plan.php index d95755fe2c719bc1f515921925675d05b2eca45d..11f8d4eb95c6bf2e72e7edc19698f4d68130516d 100644 --- a/library/Class/DriveCheckout/Plan.php +++ b/library/Class/DriveCheckout/Plan.php @@ -44,6 +44,7 @@ class Class_DriveCheckout_Plan { public function __construct($params, $user) { $this->_params = $params; $this->_user = $user; + $this->_should_filter_libraries = Class_AdminVar::isDriveCheckoutShouldFilterLibraries(); } @@ -79,6 +80,16 @@ class Class_DriveCheckout_Plan { } + public function onlyPossibleLibrary() { + return (!$this->selectedLibrary() + && 1 == $this->_getLibraries()->count() + && ($library = $this->_getLibraries()->first()) + && !$this->findFutureFor($library)) + ? $library + : null; + } + + public function injectIntoOpenings($value, $closure) { return $this->_getOpenings()->injectInto($value, $closure); } diff --git a/library/Class/DriveCheckout/PlanMessages.php b/library/Class/DriveCheckout/PlanMessages.php new file mode 100644 index 0000000000000000000000000000000000000000..d697015090e669bb77116b4bc5faccf961909c0f --- /dev/null +++ b/library/Class/DriveCheckout/PlanMessages.php @@ -0,0 +1,143 @@ +<?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_PlanMessages { + use Trait_Translator, Trait_Singleton; + + public function mainTitle() { + return $this->_('Planifier le retrait de mes documents'); + } + + + public function library() { + return $this->_('Site de retrait'); + } + + + public function chooseLibrary() { + return $this->_('Choisir un site de retrait'); + } + + + public function nothingToCheckout() { + return $this->_('Aucun document à retirer pour l\'instant'); + } + + + public function book() { + return $this->_('Prendre rendez-vous'); + } + + + public function bookAt($library) { + return $this->_('Prendre rendez-vous pour retirer les documents de %s', + $library->getLibelle()); + } + + + public function chooseAnotherLibrary() { + return $this->_('Choisir un autre site'); + } + + + public function backToLibraryChoice() { + return $this->_('Retourner à la liste des sites de retrait'); + } + + + public function checkoutBookedAt($existing) { + return $this->_('Retrait planifié %s', $existing->getDateTimeLabel()); + } + + + public function addToAgenda() { + return $this->_('Ajouter à mon agenda'); + } + + + public function importFromIcal($library, $existing) { + return $this->_('Importer le retrait de %s pour %s au format ICal', + $library->getLibelle(), + $existing->getDateTimeLabel()); + } + + + public function delete() { + return $this->_('Annuler ou replanifier'); + } + + + public function deleteExistingAt($library, $existing) { + return $this->_('Annuler ou replanifier le retrait de %s pour %s', + $library->getLibelle(), + $existing->getDateTimeLabel()); + } + + + public function deleteConfirm() { + return $this->_('Veuillez confirmer l\'annulation du rendez-vous courant.\nVous pourrez replanifier un rendez-vous au plus tôt à partir de demain.'); + } + + + public function holdCount($count) { + return $this->_plural($count, + 'Aucune réservation', + '%s réservation', + '%s réservations', + $count); + } + + + public function readyCount($count) { + return $this->_('%s à retirer', $count); + } + + + public function date() { + return $this->_('Date de retrait'); + } + + + public function chooseDate($label) { + return $this->_('Choisir de retirer les documents %s', $label); + } + + + public function chooseAnotherDate() { + return $this->_('Choisir une autre date'); + } + + + public function backToDateChoiceOf($library) { + return $this->_('Retourner à la liste des dates de retrait pour %s', $library->getLibelle()); + } + + + public function time() { + return $this->_('Heure de retrait'); + } + + + public function chooseTime() { + return $this->_('Retirer les documents à :'); + } +} diff --git a/library/Class/Entity.php b/library/Class/Entity.php index 10bab2a777e5dcc2c37cedf96e01097e18898833..998bf5f104304f616db80906c53fc08c2f83fe36 100644 --- a/library/Class/Entity.php +++ b/library/Class/Entity.php @@ -21,6 +21,8 @@ class Class_Entity { + use Trait_GetterByAttributeName; + protected $_attribs = [], $_methods = []; @@ -80,11 +82,6 @@ class Class_Entity { } - public function callGetterByAttributeName($attribute) { - return call_user_func([$this, 'get' . Storm_Inflector::camelize($attribute)]); - } - - public function __toString() { return get_class($this); } diff --git a/library/Class/ICal/DriveCheckout.php b/library/Class/ICal/DriveCheckout.php index c859a861b1ea568c2de9d612cd4b1a324efc44be..1b58c91dc5638a2e2e367afffbf0b29d1c057270 100644 --- a/library/Class/ICal/DriveCheckout.php +++ b/library/Class/ICal/DriveCheckout.php @@ -29,31 +29,37 @@ class Class_ICal_DriveCheckout extends Class_ICal_Abstract { 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)); + $calendar = $this->_calendar(); + $calendar->addComponent($this->_event($profil)); - $event->setUrl(Class_Url::absolute(['controller' => 'drive-checkout', - 'action' => 'plan', - 'id_profil' => $profil->getId()], - null, true)); + return $calendar->render(); + } - $calendar = new \Eluceo\iCal\Component\Calendar(static::PRODUCT_ID); - $calendar->addComponent($event); - return $calendar->render(); + protected function _calendar() { + return new \Eluceo\iCal\Component\Calendar(static::PRODUCT_ID); } - protected function _newEvent() { - return new \Eluceo\iCal\Component\Event(Class_Url::absolute([], null, true) - . static::PREFIX_ID - . $this->_model->getId()); + protected function _event($profil) { + $event = (new \Eluceo\iCal\Component\Event(Class_Url::absolute([], null, true) + . static::PREFIX_ID + . $this->_model->getId())) + ->setUseTimezone(true) + ->setDtStart($this->_model->getDateTimeStartAt()) + ->setSummary($this->_('Retrait des réservations à %s', + $this->_model->getLibraryLabel())) + ->setLocation($this->_model->getLibraryLabel()) + ->setUrl(Class_Url::absolute(['controller' => 'drive-checkout', + 'action' => 'plan', + 'id_profil' => $profil->getId()], + null, true)); + + if (($library = $this->_model->getLibrary()) + && ($latitude = $library->getLatitude()) + && ($longitude = $library->getLongitude())) + $event->setGeoLocation(new \Eluceo\iCal\Property\Event\Geo($latitude, $longitude)); + + return $event; } } diff --git a/library/Class/ICal/DriveCheckoutCancel.php b/library/Class/ICal/DriveCheckoutCancel.php new file mode 100644 index 0000000000000000000000000000000000000000..13774d159ca8a7e22f2d1bfbc05662afa731aa7d --- /dev/null +++ b/library/Class/ICal/DriveCheckoutCancel.php @@ -0,0 +1,32 @@ +<?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_DriveCheckoutCancel extends Class_ICal_DriveCheckout { + protected function _event($profil) { + return parent::_event($profil)->setCancelled(true); + } + + + protected function _calendar() { + return parent::_calendar()->setMethod(\Eluceo\iCal\Component\Calendar::METHOD_CANCEL); + } +} diff --git a/library/Class/Mail.php b/library/Class/Mail.php index 9c0315f465b4ab813b272326707f28c92c636258..9e0f96e632cd06c1f188554146c6415cc1be669c 100644 --- a/library/Class/Mail.php +++ b/library/Class/Mail.php @@ -22,20 +22,30 @@ class Class_Mail { use Trait_Translator; - private $params_ok=false; // Flag de controle si un mail peut etre envoyé + private $params_ok = false; // Flag de controle si un mail peut etre envoyé private $mail_from; // Header from public function __construct() { $this->params_ok = false; - $this->mail_from = Class_Profil::getCurrentProfil()->getMailSiteOrPortail(); - if (!$this->mail_from) - $this->mail_from = Class_CosmoVar::get('mail_admin'); + if (!$mail_from = Class_Profil::getCurrentProfil()->getMailSiteOrPortail()) + $mail_from = Class_CosmoVar::get('mail_admin'); + + $this->setFrom($mail_from); + } + + + public function setFrom($mail_from) { + $this->mail_from = $mail_from; if ($this->isMailValid($this->mail_from)) { ini_set('sendmail_from', $this->mail_from); - $this->params_ok=true; + $this->params_ok = true; + return $this; } + + $this->params_ok = false; + return $this; } diff --git a/library/Class/TableDescription/DriveCheckout/Holds.php b/library/Class/TableDescription/DriveCheckout/Holds.php index 7fcfd881fd621ab9b108aa44f8a3dc3edabaec59..acb1d31f13cc46768347ab4e366b85dec49648ef 100644 --- a/library/Class/TableDescription/DriveCheckout/Holds.php +++ b/library/Class/TableDescription/DriveCheckout/Holds.php @@ -23,6 +23,12 @@ class Class_TableDescription_DriveCheckout_Holds extends Class_TableDescription { public function init() { $this + ->addColumn($this->_('Section'), + function($hold) { return $hold->getSectionLabel(); }) + + ->addColumn($this->_('Emplacement'), + function($hold) { return $hold->getEmplacementLabel(); }) + ->addColumn($this->_('Cote'), function($hold) { return $hold->getCote(); }) diff --git a/library/Class/TableDescription/DriveCheckout/HoldsWithCheckouts.php b/library/Class/TableDescription/DriveCheckout/HoldsWithCheckouts.php index 62257a5fb78f223fb5f558db8552a5362260eb3e..7a3659267947dcae8de549b9b78f045e366cd142 100644 --- a/library/Class/TableDescription/DriveCheckout/HoldsWithCheckouts.php +++ b/library/Class/TableDescription/DriveCheckout/HoldsWithCheckouts.php @@ -27,15 +27,14 @@ class Class_TableDescription_DriveCheckout_HoldsWithCheckouts extends Class_Tabl 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->_('Carte'), 'idabon') + ->addColumn($this->_('Abonné'), 'nom_complet') + ->addColumn($this->_('Créé le'), 'created_at_for_csv') + ->addColumn($this->_('Section'), 'section_label') + ->addColumn($this->_('Emplacement'), 'emplacement_label') + ->addColumn($this->_('Cote'), 'cote') + ->addColumn($this->_('Code-barres'), 'code_barres') ->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 index 5caf56025d8585c724e02241bd0adcbbdfb48802..b648ca6153bb8ce28d6a662f334391fad431a8d9 100644 --- a/library/Class/TableDescription/DriveCheckout/List.php +++ b/library/Class/TableDescription/DriveCheckout/List.php @@ -23,6 +23,12 @@ class Class_TableDescription_DriveCheckout_List extends Class_TableDescription { public function init() { $this + ->addColumn($this->_('Jour'), + function($checkout) + { + return strftime('%d %B', + strtotime($checkout->getStartAt())); + }) ->addColumn($this->_('Heure'), function($checkout) { @@ -30,6 +36,8 @@ class Class_TableDescription_DriveCheckout_List extends Class_TableDescription { strtotime($checkout->getStartAt())); }) ->addColumn($this->_('Carte'), 'id_abon') - ->addColumn($this->_('Abonné'), 'nom_complet'); + ->addColumn($this->_('Abonné'), 'nom_complet') + ->addColumn($this->_('Créé le'), 'created_at_for_csv') + ; } } \ No newline at end of file diff --git a/library/Class/TableDescription/DriveCheckout/ListWithActions.php b/library/Class/TableDescription/DriveCheckout/ListWithActions.php index a1c2971cad301b23750a623f98d85a2a4a1e2226..a56c6efa3d8c1740e3ba0eed6f2524f9f534a62d 100644 --- a/library/Class/TableDescription/DriveCheckout/ListWithActions.php +++ b/library/Class/TableDescription/DriveCheckout/ListWithActions.php @@ -20,13 +20,23 @@ */ -class Class_TableDescription_DriveCheckout_ListWithActions extends Class_TableDescription_DriveCheckout_List { +class Class_TableDescription_DriveCheckout_ListWithActions extends Class_TableDescription { public function init() { parent::init(); $this + ->addColumn($this->_('Heure'), + function($checkout) + { + return strftime('%H:%M', + strtotime($checkout->getStartAt())); + }) + ->addColumn($this->_('Carte'), 'id_abon') + ->addColumn($this->_('Abonné'), 'nom_complet') + ->addColumn($this->_('Créé le'), 'created_at_label') ->addRowAction(['canvas_callback' => [$this, 'listHolds']]) ->addRowAction(['canvas_callback' => [$this, 'viewUser']]) - ->addRowAction(['canvas_callback' => [$this, 'deleteCheckout']]); + ->addRowAction(['canvas_callback' => [$this, 'deleteCheckout']]) + ->setSorterNone(); } diff --git a/library/Class/TableDescription/PNBItemsRenderer.php b/library/Class/TableDescription/PNBItemsRenderer.php index f40234de32f08f3a93b4602894100cdbacbbaf3b..72c48ce68b45d58c11bd03a7a3a96a78a6d7bef8 100644 --- a/library/Class/TableDescription/PNBItemsRenderer.php +++ b/library/Class/TableDescription/PNBItemsRenderer.php @@ -21,6 +21,8 @@ class Class_TableDescription_PNBItemsRenderer { + use Trait_GetterByAttributeName; + protected $_item; public function __construct($item) { @@ -28,11 +30,6 @@ class Class_TableDescription_PNBItemsRenderer { } - public function callGetterByAttributeName($attribute) { - return call_user_func([$this, 'get' . Storm_Inflector::camelize($attribute)]); - } - - public function __call($name, $params) { return call_user_func_array([$this->_item, $name], $params); } diff --git a/library/Class/User/Cards.php b/library/Class/User/Cards.php index bc7207a3799146d9a5e4c3a8ade0da6d60e23779..fd9c4cc1fd82f2473884ec7672dccc520a70d2db 100644 --- a/library/Class/User/Cards.php +++ b/library/Class/User/Cards.php @@ -208,13 +208,7 @@ class Class_User_Cards extends Storm_Model_Collection { 'Vous avez %d documents en retard.', $late_loans_count)); - $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')]] - : []; + $actions = $this->_driveNotificationActions(); if ($pullable_items_count = $this->countWaitingToBePulled()) { @@ -232,4 +226,24 @@ class Class_User_Cards extends Storm_Model_Collection { return $this; } + + + protected function _driveNotificationActions() { + if (!Class_AdminVar::isModuleEnabled('ENABLE_DRIVE_CHECKOUT')) + return []; + + $has_library = $this->detect(function($card) + { + return (new Class_DriveCheckout_Plan([], $card))->hasLibraries(); + }); + + if (!$has_library) + return []; + + return ['actions' => + [ + Class_Url::assemble(['module' => 'opac', + 'controller' => 'drive-checkout', + 'action' => 'plan']) => $this->_('Planifier le retrait de mes documents')]]; + } } \ No newline at end of file diff --git a/library/Class/Users.php b/library/Class/Users.php index 83a9052155fdd60bb45db67a26f9019083c88646..61840f66778b43d753171c73c71337e37b613612 100644 --- a/library/Class/Users.php +++ b/library/Class/Users.php @@ -1333,6 +1333,11 @@ class Class_Users extends Storm_Model_Abstract { } + public function getHoldsWaitingToBePulled() { + return $this->getEmprunteur()->getHoldsWaitingToBePulled(); + } + + public function getFicheSigb($user = null) { if ($user === null) $user = $this; // compatibilité diff --git a/library/Trait/GetterByAttributeName.php b/library/Trait/GetterByAttributeName.php new file mode 100644 index 0000000000000000000000000000000000000000..4b3208ef0a2cafb3635980e5d6f1e9363dd3cb6a --- /dev/null +++ b/library/Trait/GetterByAttributeName.php @@ -0,0 +1,27 @@ +<?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 + */ + + +trait Trait_GetterByAttributeName { + public function callGetterByAttributeName($name) { + return call_user_func([$this, 'get' . Storm_Inflector::camelize($name)]); + } +} diff --git a/library/ZendAfi/View/Helper/DriveCheckoutPlan.php b/library/ZendAfi/View/Helper/DriveCheckoutPlan.php index e531fe1e209fb5dd43ca55d15417bd89ef5ffefe..1df24949c5a2bec3107d5107ecaca9d984f2fee0 100644 --- a/library/ZendAfi/View/Helper/DriveCheckoutPlan.php +++ b/library/ZendAfi/View/Helper/DriveCheckoutPlan.php @@ -23,7 +23,8 @@ class ZendAfi_View_Helper_DriveCheckoutPlan extends ZendAfi_View_Helper_BaseHelper { protected $_plan, - $_user; + $_user, + $_messages; /** * @param $plan Class_DriveCheckout_Plan @@ -34,10 +35,11 @@ class ZendAfi_View_Helper_DriveCheckoutPlan extends ZendAfi_View_Helper_BaseHelp return ''; $this->_plan = $plan; + $this->_messages = Class_DriveCheckout_PlanMessages::getInstance(); return - $this->view->openBoiteContent($this->_('Planifier le retrait de mes documents')) - . $this->_tag('h2', $this->_('Site de retrait')) + $this->view->openBoiteContent($this->_messages->mainTitle()) + . $this->_tag('h2', $this->_messages->library()) . $this->_libraries() . $this->_dates() . $this->_times() @@ -50,7 +52,7 @@ class ZendAfi_View_Helper_DriveCheckoutPlan extends ZendAfi_View_Helper_BaseHelp return $this->_selectedLibrary($library); if (!$this->_plan->hasLibraries()) - return $this->_('Aucun document à retirer pour l\'instant'); + return $this->_messages->nothingToCheckout(); $list = $this->_plan ->injectIntoLibraries([], @@ -69,8 +71,8 @@ class ZendAfi_View_Helper_DriveCheckoutPlan extends ZendAfi_View_Helper_BaseHelp ->_tag('h3', $library->getLibelle() . ' ' . $this->_tagAnchor($this->_plan->resetUrl(), - $this->_('modifier'), - ['title' => $this->_('retour à la liste des sites de retrait')]) + $this->_messages->chooseAnotherLibrary(), + ['title' => $this->_messages->backToLibraryChoice()]) . BR . $this->_holdsInfoFor($library)); } @@ -80,32 +82,29 @@ class ZendAfi_View_Helper_DriveCheckoutPlan extends ZendAfi_View_Helper_BaseHelp return $this ->_tag('li', $library->getLibelle() - . BR . $this->_('Retrait planifié %s', $existing->getDateTimeLabel()) + . BR . $this->_messages->checkoutBookedAt($existing) . ' ' . $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->_messages->addToAgenda(), + ['title' => $this->_messages->importFromIcal($library, $existing)]) . ' / ' . $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 ?')) . '\')']) + $this->_messages->delete(), + ['title' => $this->_messages->deleteExistingAt($library, + $existing), + 'onclick' => 'return confirm(\''. $this->_escapeJsAttrib($this->_messages->deleteConfirm()) . '\')']) . 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())]) + $library->getLibelle() + . ' ' + . $this->_tagAnchor($this->_plan->libraryUrlFor($library), + $this->_messages->book(), + ['title' => $this->_messages->bookAt($library)]) . BR . $this->_holdsInfoFor($library)); } @@ -114,16 +113,10 @@ class ZendAfi_View_Helper_DriveCheckoutPlan extends ZendAfi_View_Helper_BaseHelp $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)); + + return sprintf('%s, %s', + $this->_messages->holdCount($total), + $this->_messages->readyCount($ready)); } @@ -142,7 +135,7 @@ class ZendAfi_View_Helper_DriveCheckoutPlan extends ZendAfi_View_Helper_BaseHelp return $value; }); - return $this->_tag('h2', $this->_('Date de retrait')) + return $this->_tag('h2', $this->_messages->date()) . $this->_tag('ul', implode($list)); } @@ -154,8 +147,7 @@ class ZendAfi_View_Helper_DriveCheckoutPlan extends ZendAfi_View_Helper_BaseHelp ->_tag('li', $this->_tagAnchor($this->_plan->dateUrlFor($opening), $label, - ['title' => $this->_('Choisir de retirer les documets %s', - $label)])); + ['title' => $this->_messages->chooseDate($label)])); } @@ -163,12 +155,12 @@ class ZendAfi_View_Helper_DriveCheckoutPlan extends ZendAfi_View_Helper_BaseHelp $library = $this->_plan->selectedLibrary(); return - $this->_tag('h2', $this->_('Date de retrait')) + $this->_tag('h2', $this->_messages->date()) . $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())])); + $this->_messages->chooseAnotherDate(), + ['title' => $this->_messages->backToDateChoiceOf($library)])); } @@ -184,12 +176,12 @@ class ZendAfi_View_Helper_DriveCheckoutPlan extends ZendAfi_View_Helper_BaseHelp $form = (new ZendAfi_Form(['data-backurl' => $this->view->url($this->_plan->dateUrlFor($selected_date))])) ->addElement('select', 'checkout_time', - ['label' => $this->_('Retirer les documents à :'), + ['label' => $this->_messages->chooseTime(), 'multiOptions' => $this->_plan->timesAsMultioptions()]) ->addUniqDisplayGroup('default') ; - return $this->_tag('h2', $this->_('Heure de retrait')) + return $this->_tag('h2', $this->_messages->time()) . $this->view->renderForm($form); } } diff --git a/library/ZendAfi/View/Helper/RenderCsv.php b/library/ZendAfi/View/Helper/RenderCsv.php index 787e20fd588326c2aba506a34eb030ab80c82052..233ea3fdfbb5a2e0247937469e44c6b8bb4e7481 100644 --- a/library/ZendAfi/View/Helper/RenderCsv.php +++ b/library/ZendAfi/View/Helper/RenderCsv.php @@ -34,7 +34,7 @@ class ZendAfi_View_Helper_RenderCsv extends ZendAfi_View_Helper_BaseHelper { $this->_fp_csv = fopen('php://memory', 'r+'); $this->_renderHeaders(); - $this->_renderDatas($models); + $this->_renderDatas((array)$models); rewind($this->_fp_csv); return stream_get_contents($this->_fp_csv); diff --git a/library/ZendAfi/View/Helper/RenderTable.php b/library/ZendAfi/View/Helper/RenderTable.php index 932d377154330b214c4be02c3fffc81d6e5eb5f9..bc72812ff7b4209a7325c619d3117829b8bde4b5 100644 --- a/library/ZendAfi/View/Helper/RenderTable.php +++ b/library/ZendAfi/View/Helper/RenderTable.php @@ -177,8 +177,8 @@ class ZendAfi_View_Helper_RenderTable_Body extends ZendAfi_View_Helper_BaseHelpe $this->_html .= $this->_tag('tr', $this->_tag('td', $this->view->escape($title), - ['style' => 'background-color:#888;color:white;font-size:120%;padding:2px 10px;font-weight:bold;', - 'colspan' => $this->_description->numberOfColumns()])); + ['colspan' => $this->_description->numberOfColumns()]), + ['class' => 'group_title']); } diff --git a/library/templates/Intonation/Library/View/Wrapper/DriveCheckoutPlan.php b/library/templates/Intonation/Library/View/Wrapper/DriveCheckoutPlan.php index 1340b09a17ae29d5003235d0736bcb1ef415c81f..86a2453bc777de78063da5885473677f729fd2cd 100644 --- a/library/templates/Intonation/Library/View/Wrapper/DriveCheckoutPlan.php +++ b/library/templates/Intonation/Library/View/Wrapper/DriveCheckoutPlan.php @@ -24,7 +24,7 @@ class Intonation_Library_View_Wrapper_DriveCheckoutPlan extends Intonation_Library_View_Wrapper_Abstract { public function getMainTitle() { - return $this->_('Planifier le retrait de mes documents'); + return Class_DriveCheckout_PlanMessages::getInstance()->mainTitle(); } diff --git a/library/templates/Intonation/Library/View/Wrapper/DriveCheckoutPlan/Library.php b/library/templates/Intonation/Library/View/Wrapper/DriveCheckoutPlan/Library.php index 570e53bbd95135ca16a18b0bdc6537b6ef1b63c5..5e024bb9c2f7513083b30c98194572a79e464fcf 100644 --- a/library/templates/Intonation/Library/View/Wrapper/DriveCheckoutPlan/Library.php +++ b/library/templates/Intonation/Library/View/Wrapper/DriveCheckoutPlan/Library.php @@ -26,10 +26,12 @@ class Intonation_Library_View_Wrapper_DriveCheckoutPlan_Library protected $_plan, $_html_id, - $_existing; + $_existing, + $_messages; public function setPlan($plan) { $this->_plan = $plan; + $this->_messages = Class_DriveCheckout_PlanMessages::getInstance(); return $this; } @@ -62,9 +64,8 @@ class Intonation_Library_View_Wrapper_DriveCheckoutPlan_Library 'available', 'library'), - 'Text' => $this->_('Prendre rendez-vous'), - 'Title' => $this->_('Prendre rendez-vous pour retirer les documents de %s', - $this->_model->getLibelle()), + 'Text' => $this->_messages->book(), + 'Title' => $this->_messages->bookAt($this->_model), 'Class' => 'text-primary'])]; } @@ -77,10 +78,9 @@ class Intonation_Library_View_Wrapper_DriveCheckoutPlan_Library '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()), + 'Text' => $this->_messages->addToAgenda(), + 'Title' => $this->_messages->importFromIcal($this->_model, + $existing), ]), new Intonation_Library_Link(['Url' => $this->_view->url(['action' => 'delete', 'id' => $existing->getId()]), @@ -88,12 +88,11 @@ class Intonation_Library_View_Wrapper_DriveCheckoutPlan_Library 'clean', 'utils'), - 'Text' => $this->_('Supprimer'), - 'Title' => $this->_('Supprimer le retrait de %s pour %s', - $this->_model->getLibelle(), - $existing->getDateTimeLabel()), + 'Text' => $this->_messages->delete(), + 'Title' => $this->_messages->deleteExistingAt($this->_model, + $existing), 'Class' => 'text-danger', - 'Attribs' => ['onclick' => $this->_view->confirm($this->_('Etes vous sûr de vouloir supprimer ce retrait ?'))]])]; + 'Attribs' => ['onclick' => $this->_view->confirm($this->_messages->deleteConfirm())]])]; } @@ -102,7 +101,7 @@ class Intonation_Library_View_Wrapper_DriveCheckoutPlan_Library ? $this->_view->tag('p', Class_Template::current()->getIco($this->_view, 'available', 'library') . ' ' - . $this->_('Retrait planifié %s', $existing->getDateTimeLabel()), + . $this->_messages->checkoutBookedAt($existing), ['class' => 'text-success']) : ''; @@ -127,33 +126,17 @@ class Intonation_Library_View_Wrapper_DriveCheckoutPlan_Library ((new Intonation_Library_Badge) ->setTag('span') ->setClass('primary') - ->setText($this->_plural($total, - 'Aucune réservation', - '%s réservation', - '%s réservations', - $total)) + ->setText($this->_messages->holdCount($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))), - + 'library'))), ((new Intonation_Library_Badge) ->setTag('span') ->setClass('success') - ->setText($this->_('%s à retirer', $ready)) + ->setText($this->_messages->readyCount($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))), - ]; + 'library')))]; } diff --git a/library/templates/Intonation/Library/View/Wrapper/DriveCheckoutPlan/LibrarySelected.php b/library/templates/Intonation/Library/View/Wrapper/DriveCheckoutPlan/LibrarySelected.php index 24477757c9befdc9e7194ed4eda287a66d1e9790..13aa549ed37475753d2e08a8566bea5db0abfd8c 100644 --- a/library/templates/Intonation/Library/View/Wrapper/DriveCheckoutPlan/LibrarySelected.php +++ b/library/templates/Intonation/Library/View/Wrapper/DriveCheckoutPlan/LibrarySelected.php @@ -32,8 +32,8 @@ class Intonation_Library_View_Wrapper_DriveCheckoutPlan_LibrarySelected 'refresh', 'utils'), - 'Text' => $this->_('Choisir un autre site'), - 'Title' => $this->_('Retourner à la liste des sites de retrait'), + 'Text' => $this->_messages->chooseAnotherLibrary(), + 'Title' => $this->_messages->backToLibraryChoice(), 'Class' => 'text-danger'])]; } } diff --git a/library/templates/Intonation/Library/View/Wrapper/DriveCheckoutPlan/RichContent/Date.php b/library/templates/Intonation/Library/View/Wrapper/DriveCheckoutPlan/RichContent/Date.php index 33a686af5c0690d8b3595807c2fe99d6dfb03734..d9a01a8cba6f8d9bb361f4f01e28004053956691 100644 --- a/library/templates/Intonation/Library/View/Wrapper/DriveCheckoutPlan/RichContent/Date.php +++ b/library/templates/Intonation/Library/View/Wrapper/DriveCheckoutPlan/RichContent/Date.php @@ -25,13 +25,21 @@ class Intonation_Library_View_Wrapper_DriveCheckoutPlan_RichContent_Date const HTML_CLASS = 'drivecheckout_plan_date'; + protected $_messages; + + public function setModel($model) { + $this->_messages = Class_DriveCheckout_PlanMessages::getInstance(); + return parent::setModel($model); + } + + public static function htmlIdFor($model) { return static::HTML_CLASS . '_' . $model->getId(); } public function getTitle() { - return $this->_('Date de retrait'); + return $this->_messages->date(); } @@ -80,8 +88,7 @@ class Intonation_Library_View_Wrapper_DriveCheckoutPlan_RichContent_Date 'available', 'library'), 'Text' => $label, - 'Title' => $this->_('Choisir de retirer les documents %s', - $label)]); + 'Title' => $this->_messages->chooseDate($label)]); return $this->_view->tagAction($action); } @@ -93,9 +100,8 @@ class Intonation_Library_View_Wrapper_DriveCheckoutPlan_RichContent_Date $url = $this->_view->url($url_parts) . ($url_parts ? '#' . $this->_getHtmlId() : ''); $attribs = ['Url' => $url, '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()), + 'Text' => $this->_messages->chooseAnotherDate(), + 'Title' => $this->_messages->backToDateChoiceOf($this->_model->selectedLibrary()), 'Class' => 'text-danger']; return $label . BR . $this->_view->tagAction(new Intonation_Library_Link($attribs)); diff --git a/library/templates/Intonation/Library/View/Wrapper/DriveCheckoutPlan/RichContent/Library.php b/library/templates/Intonation/Library/View/Wrapper/DriveCheckoutPlan/RichContent/Library.php index 4adcf29ccea332ea4490b6b624fb1618c8c4e6d4..cf4e988d0a457c3d8c7f273b80d272473d952baf 100644 --- a/library/templates/Intonation/Library/View/Wrapper/DriveCheckoutPlan/RichContent/Library.php +++ b/library/templates/Intonation/Library/View/Wrapper/DriveCheckoutPlan/RichContent/Library.php @@ -22,9 +22,16 @@ class Intonation_Library_View_Wrapper_DriveCheckoutPlan_RichContent_Library extends Intonation_Library_View_Wrapper_RichContent_Section{ + protected $_messages; + + public function setModel($model) { + $this->_messages = Class_DriveCheckout_PlanMessages::getInstance(); + return parent::setModel($model); + } + public function getTitle() { - return $this->_('Site de retrait'); + return $this->_messages->library(); } @@ -36,7 +43,7 @@ class Intonation_Library_View_Wrapper_DriveCheckoutPlan_RichContent_Library if ($librairies->isEmpty()) return $this->_content = $this->_view - ->tag('p', $this->_('Aucun document à retirer pour l\'instant')); + ->tag('p', $this->_messages->nothingToCheckout()); $wrapper_class = $this->_wrapperClass(); @@ -94,6 +101,6 @@ class Intonation_Library_View_Wrapper_DriveCheckoutPlan_RichContent_Library public function getNavTitle() { - return $this->_('Choisir un site de retrait'); + return $this->_messages->chooseLibrary(); } } diff --git a/library/templates/Intonation/Library/View/Wrapper/DriveCheckoutPlan/RichContent/Time.php b/library/templates/Intonation/Library/View/Wrapper/DriveCheckoutPlan/RichContent/Time.php index 0deff0716efcee48aa9f03508e820941ef8280c7..5ca09b2583261760e7b6ecdaa0f19eb1ce318d14 100644 --- a/library/templates/Intonation/Library/View/Wrapper/DriveCheckoutPlan/RichContent/Time.php +++ b/library/templates/Intonation/Library/View/Wrapper/DriveCheckoutPlan/RichContent/Time.php @@ -25,6 +25,13 @@ class Intonation_Library_View_Wrapper_DriveCheckoutPlan_RichContent_Time const HTML_CLASS = 'drivecheckout_plan_time'; + protected $_messages; + + public function setModel($model) { + $this->_messages = Class_DriveCheckout_PlanMessages::getInstance(); + return parent::setModel($model); + } + public static function htmlIdFor($model) { return static::HTML_CLASS . '_' . $model->getId(); @@ -32,7 +39,7 @@ class Intonation_Library_View_Wrapper_DriveCheckoutPlan_RichContent_Time public function getTitle() { - return $this->_('Heure de retrait'); + return $this->_messages->time(); } @@ -45,7 +52,7 @@ class Intonation_Library_View_Wrapper_DriveCheckoutPlan_RichContent_Time $form = (new ZendAfi_Form(['data-backurl' => $this->_view->url($this->getNavUrl())])) ->addElement('select', 'checkout_time', - ['label' => $this->_('Retirer les documents à :'), + ['label' => $this->_messages->chooseTime(), 'multiOptions' => $this->_model->timesAsMultioptions()]) ->addUniqDisplayGroup('default') ; diff --git a/public/admin/css/global.css b/public/admin/css/global.css index aa0c5b1bab3232d095fb7b3fde83c7f81de062dc..7d929b9941087513468b0fda76b05dad8fd8bb3b 100644 --- a/public/admin/css/global.css +++ b/public/admin/css/global.css @@ -936,6 +936,16 @@ table.models > tbody > tr:hover { } +.modules table.models .group_title { + background-color: #888; + color:white; + font-size:120%; + padding:2px 10px; + font-weight:bold; +} + + + .profils ul li ul.profil_pages li:hover { cursor: move; } @@ -1670,4 +1680,4 @@ table#logs img { padding: 1ex 1em; overflow: auto; max-height: 300px; -} \ No newline at end of file +} diff --git a/public/admin/skins/bokeh74/global.css b/public/admin/skins/bokeh74/global.css index c9fff349496f943f289e27f9044be2bcaacc0f0c..722335284fa278e4081b0a833cc8d6af198db6f9 100755 --- a/public/admin/skins/bokeh74/global.css +++ b/public/admin/skins/bokeh74/global.css @@ -371,6 +371,14 @@ table#suggestions td:last-child a { min-width: 100px; } +.modules table.models .group_title { + background-color:var(--nav-background); + color:white; + font-size:120%; + padding:2px 10px; + font-weight:bold; +} + .treeView .ui-accordion .ui-accordion-content { padding-top: 0.5em; } @@ -1055,4 +1063,4 @@ table#logs img { #learn_more button:focus, #learn_more button:hover { background-color:var(--bokeh-event-highlight); -} \ No newline at end of file +} diff --git a/public/admin/skins/noel/global.css b/public/admin/skins/noel/global.css index 51dd45fe6c9166aeec285c7fd378f8fc14275d4c..1f34ae375ca2121fae025f5de2e9cdd3d0469277 100755 --- a/public/admin/skins/noel/global.css +++ b/public/admin/skins/noel/global.css @@ -391,6 +391,14 @@ table#suggestions td:last-child a { min-width: 100px; } + +.modules table.models .group_title { + color:white; + font-size:120%; + padding:2px 10px; + font-weight:bold; +} + .treeView .ui-accordion .ui-accordion-content { padding-top: 0.5em; } @@ -1092,4 +1100,4 @@ body *:not(img) { width:100%; height: 100%; z-index: -1; -} \ No newline at end of file +} diff --git a/public/admin/skins/retro/global.css b/public/admin/skins/retro/global.css index 255ed3fc6943971849182b10f9ecee866a58f36d..2199e2a1d5c5f2376db28203d32aa00a24a55dad 100755 --- a/public/admin/skins/retro/global.css +++ b/public/admin/skins/retro/global.css @@ -279,6 +279,16 @@ table#suggestions td:last-child a { min-width: 100px; } + +.modules table.models .group_title { + background-color:var(--nav-background); + color:white; + font-size:120%; + padding:2px 10px; + font-weight:bold; +} + + .treeView .ui-accordion .ui-accordion-content { padding-top: 0.5em; } @@ -903,4 +913,4 @@ body .warning { padding: 1ex 1em; overflow: auto; max-height: 300px; -} \ No newline at end of file +} diff --git a/tests/db/UpgradeDBTest.php b/tests/db/UpgradeDBTest.php index c5338f69a40f5a750c11c3d8efbc5154d162a0cf..56f0ae8961788233999ca0dd583a863244f6b30d 100644 --- a/tests/db/UpgradeDBTest.php +++ b/tests/db/UpgradeDBTest.php @@ -239,6 +239,12 @@ abstract class UpgradeDBTestCase extends PHPUnit_Framework_TestCase { } + protected function dropFieldFrom($table, $field) { + return $this->silentQuery(sprintf('alter table %s drop column %s', + $table, $field)); + } + + protected function dropIndexedFieldFrom($table, $field) { return $this->silentQuery('ALTER TABLE ' . $table . ' ' . 'DROP KEY ' . $field . ', ' @@ -3322,4 +3328,19 @@ class UpgradeDB_389_Test extends UpgradeDBTestCase { public function tableOuverturesShouldContainsColumnMaxPerPeriodApresMidiAsInt(){ $this->assertFieldType('ouvertures', 'max_per_period_apres_midi', 'int(11)'); } +} + + + + +class UpgradeDB_390_Test extends UpgradeDBTestCase { + public function prepare() { + $this->dropFieldFrom('drive_checkout', 'created_at'); + } + + + /** @test */ + public function tableDriveCheckoutShouldContainsCreatedAtColumn() { + $this->assertFieldType('drive_checkout', 'created_at', 'datetime'); + } } \ No newline at end of file diff --git a/tests/scenarios/DriveCheckOut/DriveCheckOutBookingTest.php b/tests/scenarios/DriveCheckOut/DriveCheckOutBookingTest.php index 8528acf419118f746ba97a1d1352661956dd7bdd..60be3e417c4c12447e71313b5139d0ce07ffa2e5 100644 --- a/tests/scenarios/DriveCheckOut/DriveCheckOutBookingTest.php +++ b/tests/scenarios/DriveCheckOut/DriveCheckOutBookingTest.php @@ -60,6 +60,7 @@ abstract class DriveCheckOutBookingTestCase extends AbstractControllerTestCase { $timesource = new TimeSourceForTest('2020-05-05 11:30'); Class_DriveCheckout_Plan::setTimeSource($timesource); + Class_DriveCheckout::setTimeSource($timesource); $this->_mail_transport = new MockMailTransport(); Zend_Mail::setDefaultTransport($this->_mail_transport); @@ -72,6 +73,7 @@ abstract class DriveCheckOutBookingTestCase extends AbstractControllerTestCase { public function tearDown() { Class_DriveCheckout_Plan::setTimeSource(null); + Class_DriveCheckout::setTimeSource(null); Class_Systeme_ModulesAccueil::reset(); parent::tearDown(); } @@ -101,6 +103,7 @@ abstract class DriveCheckOutBookingTestCase extends AbstractControllerTestCase { 'closed_on_holidays' => false, 'enable_drive' => 1, 'horaire' => 'Ouvert tous les lundis, mardi et mercedi', + 'mail' => 'emilie@hotel-dieu.fr', 'ouvertures' => [ Class_Ouverture::chaqueLundi('00:00', '00:00', '12:00', '18:00') ->beDrive(), @@ -118,7 +121,12 @@ abstract class DriveCheckOutBookingTestCase extends AbstractControllerTestCase { $this->fixture('Class_Bib', ['id' => 3, 'enable_drive' => 1, - 'libelle' => 'Mauricette-Rafin']); + 'libelle' => 'Mauricette-Rafin', + 'ouvertures' => [ + Class_Ouverture::chaqueMardi('00:00', '00:00', '12:00', '18:00') + ->beDrive() + ] + ]); $this->fixture('Class_Bib', ['id' => 4, @@ -233,6 +241,15 @@ class DriveCheckOutUserNotificationsTest extends DriveCheckOutBookingTestCase { } + /** @test */ + public function withoutHoldsInDriveShouldMarcusNotificationsShouldNotContainsLinkToDriveCheckoutPlan() { + Class_Bib::find(1)->setEnableDrive(false)->save(); + Class_Bib::find(2)->setEnableDrive(false)->save(); + $this->_marcus->registerNotificationsOn($this); + $this->assertEmpty($this->_actions); + } + + /** @test */ public function withDisabledDriveMarcusNotificationActionsShouldNotContainsLinkToDriveCheckoutPlan() { Class_AdminVar::set('ENABLE_DRIVE_CHECKOUT', 0); @@ -310,6 +327,29 @@ class DriveCheckOutBookingPlanTest extends DriveCheckOutBookingTestCase { +class DriveCheckOutBookingPlanWithoutHoldCheckTest extends DriveCheckOutBookingTestCase { + public function setUp() { + parent::setUp(); + Class_AdminVar::set('DRIVE_DISABLE_HOLD_CHECK', 1); + $this->dispatch('/opac/drive-checkout/plan'); + } + + + /** @test */ + public function pageShouldContainsLinkToChooseMauricette() { + $this->assertXPath('//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(); @@ -341,7 +381,7 @@ class DriveCheckOutBookingPlanWithFuturExistingTest extends DriveCheckOutBooking /** @test */ public function pageShouldContainsLinkToDeletePlannedCheckout() { $this->assertXPathContentContains('//a[contains(@href, "/drive-checkout/delete/id/2")]', - 'Supprimer'); + 'Annuler ou replanifier'); } @@ -356,6 +396,8 @@ class DriveCheckOutBookingPlanWithFuturExistingTest extends DriveCheckOutBooking class DriveCheckOutBookingPlanDeleteExistingTest extends DriveCheckOutBookingTestCase { + protected $_mail; + public function setUp() { parent::setUp(); @@ -366,6 +408,7 @@ class DriveCheckOutBookingPlanDeleteExistingTest extends DriveCheckOutBookingTes 'start_at' => '2020-05-12 09:00:00']); $this->dispatch('/opac/drive-checkout/delete/id/2'); + $this->_mail = $this->_mail_transport->sent_mail; } @@ -379,6 +422,82 @@ class DriveCheckOutBookingPlanDeleteExistingTest extends DriveCheckOutBookingTes public function notificationShouldContainsDeletionDetails() { $this->assertFlashMessengerContentContains('Le retrait de vos documents de Albert Camus planifié pour le mardi 12 mai entre 09h00 et 09h30 a été supprimé.'); } + + + /** @test */ + public function mailShouldBeSent() { + $this->assertNotNull($this->_mail); + } + + + /** @test */ + public function mailSubjectShouldBeConfirmationDeRdv() { + $this->assertContains('Suppression de votre rendez-vous à Albert Camus', + quoted_printable_decode($this->_mail->getSubject())); + } + + + /** @test */ + public function mailBodyShouldContainsCheckoutDetails() { + $this->assertContains('le mardi 12 mai entre 09h00 et 09h30', + quoted_printable_decode($this->_mail->getBodyHtml(true))); + } + + + /** @test */ + public function mailFirstRecipientShouldBeMarus() { + $this->assertEquals('mm@any-serveur.eu', $this->_mail->getRecipients()[0]); + } + + + /** @test */ + public function mailProfilShouldBeInBcc() { + $this->assertEquals('<zemail@mabib.st>', $this->_mail->getHeaders()['Bcc'][0]); + } + + + /** @test */ + public function mailFromShouldBeMailProfil() { + $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); + return base64_decode($part->getContent()); + } + + + /** + * @test + * @depends mailShouldHaveCalendarIcsPart + */ + public function calendarMethodShouldBeCancel($ical) { + $this->assertContains('METHOD:CANCEL', $ical); + } + + + /** + * @test + * @depends mailShouldHaveCalendarIcsPart + */ + public function eventStatusShouldBeCancelled($ical) { + $this->assertContains('STATUS:CANCELLED', $ical); + } } @@ -690,7 +809,8 @@ class DriveCheckOutBookingPlanBibHotelDieuAt2020_05_12_09_00Test public function checkoutShouldBePlanned() { $this->assertNotNull(Class_DriveCheckout::findFirstBy(['user_id' => $this->_marcus->getId(), 'library_id' => 1, - 'start_at' => '2020-05-12 09:00:00'])); + 'start_at' => '2020-05-12 09:00:00', + 'created_at' => '2020-05-05 11:30:00'])); } @@ -733,14 +853,14 @@ class DriveCheckOutBookingPlanBibHotelDieuAt2020_05_12_09_00Test /** @test */ - public function mailSiteShouldBeInBcc() { - $this->assertContains('zemail@mabib.st', $this->_mail->getHeaders()['Bcc']); + public function mailSiteHotelDieuShouldBeInBcc() { + $this->assertEquals('<emilie@hotel-dieu.fr>', $this->_mail->getHeaders()['Bcc'][0]); } /** @test */ - public function mailFromShouldBeMailSite() { - $this->assertEquals('zemail@mabib.st', $this->_mail->getFrom()); + public function mailFromShouldBeMailSiteHotelDieu() { + $this->assertEquals('emilie@hotel-dieu.fr', $this->_mail->getFrom()); } @@ -766,6 +886,34 @@ class DriveCheckOutBookingPlanBibHotelDieuAt2020_05_12_09_00Test +class DriveCheckOutBookingPlanBibHotelDieuWithoutMailAt2020_05_12_09_00Test + extends DriveCheckOutBookingTestCase { + protected $_mail; + + public function setUp() { + parent::setUp(); + Class_Bib::find(1)->setMail('')->save(); + $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 mailSiteHotelDieuShouldBeInBcc() { + $this->assertEquals('<zemail@mabib.st>', $this->_mail->getHeaders()['Bcc'][0]); + } + + + /** @test */ + public function mailFromShouldBeMailSiteHotelDieu() { + $this->assertEquals('zemail@mabib.st', $this->_mail->getFrom()); + } +} + + + + class DriveCheckOutBookingPlanBibHotelDieuAt2020_05_12_05_00Test extends DriveCheckOutBookingTestCase { @@ -833,4 +981,42 @@ class DriveCheckOutBookingPlanWithoutHoldsTest extends DriveCheckOutBookingTestC public function pageShouldContainsNoHoldMessage() { $this->assertXPathContentContains('//div//p', 'Aucun document à retirer pour l\'instant'); } +} + + + + +class DriveCheckOutBookingPlanWithOnlyOnePossibleLibrary + extends DriveCheckOutBookingTestCase { + + public function setUp() { + parent::setUp(); + + $fiche = (new Class_WebService_SIGB_Emprunteur('10', 'Marcus')) + ->reservationsAddAll([$this->_holdIn(1)]); + + $this->_marcus + ->setFicheSigb(['type_comm' => Class_IntBib::COM_NANOOK, + 'fiche' => $fiche]); + } + + + /** @test */ + public function withoutFuturCheckoutshouldRedirectToDateChoiceOfFirstLibrary() { + $this->dispatch('/opac/drive-checkout/plan'); + $this->assertRedirectTo('/drive-checkout/plan/id_bib/1'); + } + + + /** @test */ + public function withFutureCheckoutShouldNotRedirect() { + $this->fixture('Class_DriveCheckout', + ['id' => 2, + 'library_id' => 1, + 'user_id' => $this->_marcus->getId(), + 'start_at' => '2020-05-12 09:00:00']); + + $this->dispatch('/opac/drive-checkout/plan'); + $this->assertNotRedirect(); + } } \ No newline at end of file diff --git a/tests/scenarios/DriveCheckOut/DriveCheckoutAdminControllerTest.php b/tests/scenarios/DriveCheckOut/DriveCheckoutAdminControllerTest.php index a19005ead070fff82c2afee64f2c795334e21cc1..5694ab45f363314f946b1079b0cc0a8269923438 100644 --- a/tests/scenarios/DriveCheckOut/DriveCheckoutAdminControllerTest.php +++ b/tests/scenarios/DriveCheckOut/DriveCheckoutAdminControllerTest.php @@ -76,6 +76,51 @@ abstract class DriveCheckOutAdminControllerTestCase extends Admin_AbstractContro 'idabon' => 'A123', 'bib' => $lib_hotel_dieu]); + + $this->fixture('Class_CodifSection', + ['id' => 3, + 'libelle' => 'Jeunesse']); + + $this->fixture('Class_CodifEmplacement', + ['id' => 2, + 'libelle' => 'BD', + 'regles' => '995$e=bd']); + + $holds = [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']), + 'emplacement' => 2, + 'section' => 3, + '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') + ]; + $maurice = $this->fixture('Class_Users', ['id' => 4, 'login' => 'maurice', @@ -85,76 +130,48 @@ abstract class DriveCheckOutAdminControllerTestCase extends Admin_AbstractContro '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') - ])]); - + ->reservationsAddAll($holds)]); $this->fixture('Class_DriveCheckout', ['id' => 1, 'user' => $emilie, 'library' => $lib_hotel_dieu, - 'start_at' => '2020-05-12 09:00:00']); + 'start_at' => '2020-05-12 09:00:00', + 'created_at' => '0000-00-00 00:00:00']); $this->fixture('Class_DriveCheckout', ['id' => 2, 'user' => $bernard, 'library' => $lib_hotel_dieu, - 'start_at' => '2020-05-12 14:00:00']); + 'start_at' => '2020-05-12 14:00:00', + 'created_at' => '2020-05-02 23:46:33']); $this->fixture('Class_DriveCheckout', ['id' => 3, 'user' => $maurice, 'library' => $lib_camus, - 'start_at' => '2020-05-14 09:00:00']); + 'start_at' => '2020-05-14 09:00:00', + 'created_at' => '0000-00-00 00:00:00']); $this->fixture('Class_DriveCheckout', ['id' => 4, 'user' => $emilie, 'library' => $lib_camus, - 'start_at' => '2020-05-14 09:15:00']); + 'start_at' => '2020-05-14 09:15:00', + 'created_at' => '2020-05-03 19:32:55']); $this->fixture('Class_DriveCheckout', ['id' => 5, 'user' => $bernard, 'library' => $lib_camus, - 'start_at' => '2020-05-15 10:00:00']); - + 'start_at' => '2020-05-15 10:00:00', + 'created_at' => '0000-00-00 00:00:00']); } } + class DriveCheckoutAdminControllerAdminVarTest extends DriveCheckOutAdminControllerTestCase { /** @test */ public function withAdminVarEnableDriveFalseShouldNotDisplayMenuEntry() { @@ -246,21 +263,27 @@ class DriveCheckoutAdminControllerListHotelDieuTest extends DriveCheckOutAdminCo } + /** @test */ + public function pageShouldContainsInputForDaysValueOne() { + $this->assertXPath('//input[@name="days"][@type="number"][@value="1"][@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"]'); + $this->assertXPath('//table//td[text()="09:00"]/following-sibling::td[text()="A121"]/following-sibling::td[text()="emilie"]/following-sibling::td[text()="n/a"]'); } /** @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"]'); + $this->assertXPath('//table//td[text()="14:00"]/following-sibling::td[text()="A123"]/following-sibling::td[text()="bernard"]/following-sibling::td[text()="02 mai à 23h46"]'); } /** @test */ public function pageShouldContainsTwoCheckouts() { - $this->assertXPathCount('//table[@id="checkouts"]/tbody/tr', 2); + $this->assertXPathCount('//table[@id="checkouts"]/tbody/tr[not(@class="group_title")]', 2); } } @@ -306,7 +329,7 @@ class DriveCheckoutAdminControllerListCamusOnMayFourteenthTest extends DriveChec /** @test */ public function pageShouldContainsTwoCheckouts() { - $this->assertXPathCount('//table[@id="checkouts"]/tbody/tr', 2); + $this->assertXPathCount('//table[@id="checkouts"]/tbody/tr[not(@class="group_title")]', 2); } @@ -325,7 +348,39 @@ class DriveCheckoutAdminControllerListCamusOnMayFourteenthTest extends DriveChec -class DriveCheckoutAdminControllerExportCSVCamusOnMayFourteenthTest extends DriveCheckOutAdminControllerTestCase { +class DriveCheckoutAdminControllerListCamusOnMayFourteenthDaysSevenTest extends DriveCheckOutAdminControllerTestCase { + public function setUp() { + parent::setUp(); + $this->dispatch('/admin/drive-checkout/list/id_bib/2/date/2020-05-14/days/7'); + } + + + /** @test */ + public function pageShouldContainsThreeCheckouts() { + $this->assertXPathCount('//table[@id="checkouts"]/tbody/tr[not(@class="group_title")]', + 3, + $this->_response->getBody()); + } + + + /** @test */ + public function pageShouldContainsGroupTitleForMayFourteenth() { + $this->assertXPathContentContains('//table//tr[@class="group_title"]', 'jeudi 14 mai 2020'); + } + + + /** @test */ + public function pageShouldContainsInputForDaysValueSeven() { + $this->assertXPath('//input[@name="days"][@type="number"][@value="7"][@onchange]'); + } +} + + + + +class DriveCheckoutAdminControllerExportCSVCamusOnMayFourteenthTest + extends DriveCheckOutAdminControllerTestCase { + public function setUp() { parent::setUp(); $this->dispatch('/admin/drive-checkout/list-csv/id_bib/2/date/2020-05-14'); @@ -342,9 +397,9 @@ class DriveCheckoutAdminControllerExportCSVCamusOnMayFourteenthTest extends Driv /** @test */ public function csvShouldContainsCheckouts() { - $this->assertEquals("Heure;Carte;Abonné\n" - ."09:00;A124;maurice\n" - ."09:15;A121;emilie\n", + $this->assertEquals("Jour;Heure;Carte;Abonné;\"Créé le\"\n" + ."\"14 mai\";09:00;A124;maurice;\n" + ."\"14 mai\";09:15;A121;emilie;\"2020-05-03 19:32:55\"\n", $this->_response->getBody()); } } @@ -352,7 +407,9 @@ class DriveCheckoutAdminControllerExportCSVCamusOnMayFourteenthTest extends Driv -class DriveCheckoutAdminControllerListHoldsForCheckoutThreeTest extends DriveCheckOutAdminControllerTestCase { +class DriveCheckoutAdminControllerListHoldsForCheckoutThreeTest + extends DriveCheckOutAdminControllerTestCase { + public function setUp() { parent::setUp(); $this->dispatch('/admin/drive-checkout/list-holds/id/3'); @@ -371,6 +428,12 @@ class DriveCheckoutAdminControllerListHoldsForCheckoutThreeTest extends DriveChe } + /** @test */ + public function tableShouldContainsHoldOnTintinWithSectionAndEmplacement() { + $this->assertXPath('//table//td[text()="Jeunesse"]/following-sibling::td[text()="BD"]/following-sibling::td[text()="BD2"]'); + } + + /** @test */ public function tableShouldNotContainsHoldOnTournesol() { $this->assertNotXPath('//table//td[contains(text(),"Tournesol")]'); @@ -432,10 +495,10 @@ class DriveCheckoutAdminControllerExportItemsCSVCamusOnMayFourteenthTest extends /** @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->assertEquals("Jour;Heure;Carte;Abonné;\"Créé le\";Section;Emplacement;Cote;Code-barres;Titre\n" + . "\"14 mai\";09:00;A124;maurice;;Jeunesse;BD;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()); } }