diff --git a/VERSIONS_WIP/129107 b/VERSIONS_WIP/129107 new file mode 100644 index 0000000000000000000000000000000000000000..699fe7d19e3edb3aec1b1feaf742dfedf329c8ef --- /dev/null +++ b/VERSIONS_WIP/129107 @@ -0,0 +1 @@ + - ticket #129107 : Dilicom PNB : possibilité de n'afficher que les liseuses LCP . \ No newline at end of file diff --git a/application/modules/opac/controllers/BibNumeriqueController.php b/application/modules/opac/controllers/BibNumeriqueController.php index d00fc8f1657a2967581928d66fc9ba55afef166f..0178da8c1f15934e7b01df3afde4e792cc2abf45 100644 --- a/application/modules/opac/controllers/BibNumeriqueController.php +++ b/application/modules/opac/controllers/BibNumeriqueController.php @@ -294,14 +294,12 @@ class BibNumeriqueController extends ZendAfi_Controller_Action { ->setMethod('get') ->setAction($this->view->url(['action' => 'download-loan-book-ajax'])); - if (($default_device=Class_AdminVar::get('DILICOM_PNB_CHOOSE_DEVICES')) - && ($user_agents = (new Class_WebService_BibNumerique_Dilicom_Hub())->listUserAgents())) + if ($user_agents = (new Class_AdminVar_Dilicom())->getUserAgents()) $this->view->form->addElement('select', 'devices', ['label' => $this->_('Type de liseuse'), 'multiOptions' => $user_agents, - 'value'=> $default_device]) + 'value'=> (new Class_AdminVar_Dilicom())->defaultDevice()]) ->addDisplayGroup(['devices'], 'devices-group',[]); - $this->view->form ->addElement('Submit', 'yes-button', ['label' => $this->_('Oui')]) ->addElement('Button', 'no-button', diff --git a/library/Class/AdminVar.php b/library/Class/AdminVar.php index 49ba0efc1007e49c7c5c414859c03a3982f47a63..63138f3c9ae4ee9ac3958153e69e118536c5003f 100644 --- a/library/Class/AdminVar.php +++ b/library/Class/AdminVar.php @@ -160,7 +160,7 @@ class Class_AdminVarLoader extends Storm_Model_Loader { return $this->_getStaticAlbumVars() + $this->_getDynamicAlbumVars() - + $this->_getDilicomPnbVars(); + + (new Class_AdminVar_Dilicom())->getDilicomPnbVars(); } @@ -189,35 +189,6 @@ class Class_AdminVarLoader extends Storm_Model_Loader { } - protected function _getDilicomPnbVars() { - return [ - 'DILICOM_PNB' => Class_AdminVar_Meta::newOnOff($this->_('Activation du PNB Dilicom')), - 'DILICOM_PNB_GLN_COLLECTIVITE' => Class_AdminVar_Meta::newDefault($this->_('Gln de la collectivité, il est fourni par Dilicom.')), - 'DILICOM_PNB_PWD_COLLECTIVITE' => Class_AdminVar_Meta::newDefault($this->_('Mot de passe de la collectivité, il est fourni par Dilicom.')), - 'DILICOM_PNB_GLN_CONTRACTOR' => Class_AdminVar_Meta::newDefault($this->_('Contracteur du PNB Dilicom')), - 'DILICOM_PNB_SERVER_URL' => Class_AdminVar_Meta::newDefault($this->_('Url du serveur PNB Dilicom')), - 'DILICOM_PNB_IP_ADRESSES' => Class_AdminVar_Meta::newMultiInput($this->_('Liste des adresses IP publiques autorisées pour la consultation des documents'), - ['validate' => 'ZendAfi_Validate_Dilicom_IpAdresses']), - 'DILICOM_PNB_FTP_SERVER' => Class_AdminVar_Meta::newDefault($this->_('Serveur FTP de diffusion des offres PNB Dilicom')), - 'DILICOM_PNB_FTP_USER' => Class_AdminVar_Meta::newDefault($this->_('Utilisateur FTP de diffusion des offres PNB Dilicom')), - 'DILICOM_PNB_FTP_PASS' => Class_AdminVar_Meta::newDefault($this->_('Mot de passe FTP de diffusion des offres PNB Dilicom')), - 'DILICOM_PNB_MAX_LOAN_DURATION' => Class_AdminVar_Meta::newDefault($this->_('Durée maximale (en jours) d\'un prêt PNB Dilicom')), - 'DILICOM_PNB_LOAN_COUNT_LIMIT' => Class_AdminVar_Meta::newDefault($this->_('Nombre de prêts simultanés maximum pour un livre PNB Dilicom')), - 'DILICOM_PNB_MAX_LOAN_PER_USER' => Class_AdminVar_Meta::newDefault($this->_('Nombre de prêts simultanés maximum pour un abonné PNB Dilicom (par défaut 3)'), ['value' => 3]), - 'DILICOM_PNB_LOAN_WARNING_MESSAGE' => Class_AdminVar_Meta::newEditor($this->_('Message d\'avertissement affiché sur la popup d\'emprunt'), - ['value' => $this->_('Votre compte sera mis à jour dans un délai de 15 minutes après le retour anticipé du document.')]), - 'DILICOM_PNB_ENABLE_HOLDS' => Class_AdminVar_Meta::newOnOff($this->_('Activation de la gestion des réservations de documents PNB Dilicom')), - 'DILICOM_PNB_HOLD_MAX_AVAILABILITY' => Class_AdminVar_Meta::newDefault($this->_('Nombre de jours de disponibilité de la réservation (par défaut 8)'), ['value' => 8]), - - 'DILICOM_PNB_HOLD_AVAILABLE_MAIL' => Class_AdminVar_Meta::newEditor($this->_('Email de notification de disponibilité de reservation numérique'), ['value'=>'Bonjour {user_name},<p>Le document <a href="{record_url}">{record_title}</a> vous est réservé pour emprunt jusqu\'au {hold_expiration_date}.</p>']), - 'DILICOM_PNB_RECORD_MAX_HOLD_COUNT' => Class_AdminVar_Meta::newDefault($this->_('Nombre maximum de réservations par document numérique'), ['value' => 3]), - 'DILICOM_PNB_PATRON_MAX_HOLD_COUNT' => Class_AdminVar_Meta::newDefault($this->_('Nombre maximum de réservations par utilisateur'), ['value' => 2]), - 'DILICOM_PNB_BOARD_DISPLAY_SECTION' => Class_AdminVar_Meta::newOnOff($this->_('Affichage des sections et genres dans le tableau d\'administration des prêts PNB')), - 'DILICOM_PNB_CHOOSE_DEVICES' => Class_AdminVar_Meta::newCombo($this->_('Activer la selection de la liseuse'), ['options' => function() { return ['selectOptions' => ['label' => $this->_('Liseuse selectionnée par défaut'), - 'multioptions' => ['0' => 'Désactiver la selection de la liseuse'] + (new Class_WebService_BibNumerique_Dilicom_Hub())->listUserAgents()]];}]), - ]; - } - protected function _getStaticAlbumVars() { return ['ALBUMS_LIST_MODE' => Class_AdminVar_Meta::newOnOff($this->_('Le gestionnaire de contenu affiche les albums sous forme de liste paginée au lieu de d\'une arborescence. Cet affichage est adapté lorsque le nombre d\'albums devient trop important')), @@ -758,7 +729,7 @@ class Class_AdminVarLoader extends Storm_Model_Loader { * @return bool */ public function isDilicomPNBEnabled() { - return ('' != Class_AdminVar::get('DILICOM_PNB_GLN_CONTRACTOR')) && ('' != Class_AdminVar::get('DILICOM_PNB_SERVER_URL')); + return (new Class_AdminVar_Dilicom())->isEnabled(); } diff --git a/library/Class/AdminVar/Dilicom.php b/library/Class/AdminVar/Dilicom.php new file mode 100644 index 0000000000000000000000000000000000000000..b9b8247fb46b56c924f89988be23ba047ed74009 --- /dev/null +++ b/library/Class/AdminVar/Dilicom.php @@ -0,0 +1,98 @@ +<?php +/** + * Copyright (c) 2012-2014, Agence Française Informatique (AFI). All rights reserved. + * + * BOKEH is free software; you can redistribute it and/or modify + * it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE as published by + * the Free Software Foundation. + * + * There are special exceptions to the terms and conditions of the AGPL as it + * is applied to this software (see README file). + * + * BOKEH is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE + * along with BOKEH; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +class Class_AdminVar_Dilicom { + use Trait_Translator; + + public function isEnabled() { + return ('' != Class_AdminVar::get('DILICOM_PNB_GLN_CONTRACTOR')) + && ('' != Class_AdminVar::get('DILICOM_PNB_SERVER_URL')); + } + + + public function getUserAgents() { + if (($agents = Class_AdminVar::get('DILICOM_PNB_CHOOSE_DEVICES_ACTIVATE')) + && ($user_agents = (new Class_WebService_BibNumerique_Dilicom_Hub())->listUserAgents($agents))) + return $user_agents; + + } + + + public function defaultDevice() { + return Class_AdminVar::get('DILICOM_PNB_CHOOSE_DEVICES'); + } + + + public function getDilicomPnbVars() { + return [ + 'DILICOM_PNB' => Class_AdminVar_Meta::newOnOff($this->_('Activation du PNB Dilicom')), + 'DILICOM_PNB_GLN_COLLECTIVITE' => Class_AdminVar_Meta::newDefault($this->_('Gln de la collectivité, il est fourni par Dilicom.')), + 'DILICOM_PNB_PWD_COLLECTIVITE' => Class_AdminVar_Meta::newDefault($this->_('Mot de passe de la collectivité, il est fourni par Dilicom.')), + 'DILICOM_PNB_GLN_CONTRACTOR' => Class_AdminVar_Meta::newDefault($this->_('Contracteur du PNB Dilicom')), + 'DILICOM_PNB_SERVER_URL' => Class_AdminVar_Meta::newDefault($this->_('Url du serveur PNB Dilicom')), + 'DILICOM_PNB_IP_ADRESSES' => Class_AdminVar_Meta::newMultiInput($this->_('Liste des adresses IP publiques autorisées pour la consultation des documents'), + ['validate' => 'ZendAfi_Validate_Dilicom_IpAdresses']), + 'DILICOM_PNB_FTP_SERVER' => Class_AdminVar_Meta::newDefault($this->_('Serveur FTP de diffusion des offres PNB Dilicom')), + 'DILICOM_PNB_FTP_USER' => Class_AdminVar_Meta::newDefault($this->_('Utilisateur FTP de diffusion des offres PNB Dilicom')), + 'DILICOM_PNB_FTP_PASS' => Class_AdminVar_Meta::newDefault($this->_('Mot de passe FTP de diffusion des offres PNB Dilicom')), + 'DILICOM_PNB_MAX_LOAN_DURATION' => Class_AdminVar_Meta::newDefault($this->_('Durée maximale (en jours) d\'un prêt PNB Dilicom')), + 'DILICOM_PNB_LOAN_COUNT_LIMIT' => Class_AdminVar_Meta::newDefault($this->_('Nombre de prêts simultanés maximum pour un livre PNB Dilicom')), + 'DILICOM_PNB_MAX_LOAN_PER_USER' => Class_AdminVar_Meta::newDefault($this->_('Nombre de prêts simultanés maximum pour un abonné PNB Dilicom (par défaut 3)'), ['value' => 3]), + 'DILICOM_PNB_LOAN_WARNING_MESSAGE' => Class_AdminVar_Meta::newEditor($this->_('Message d\'avertissement affiché sur la popup d\'emprunt'), + ['value' => $this->_('Votre compte sera mis à jour dans un délai de 15 minutes après le retour anticipé du document.')]), + 'DILICOM_PNB_ENABLE_HOLDS' => Class_AdminVar_Meta::newOnOff($this->_('Activation de la gestion des réservations de documents PNB Dilicom')), + 'DILICOM_PNB_HOLD_MAX_AVAILABILITY' => Class_AdminVar_Meta::newDefault($this->_('Nombre de jours de disponibilité de la réservation (par défaut 8)'), ['value' => 8]), + + 'DILICOM_PNB_HOLD_AVAILABLE_MAIL' => Class_AdminVar_Meta::newEditor($this->_('Email de notification de disponibilité de reservation numérique'), ['value'=>'Bonjour {user_name},<p>Le document <a href="{record_url}">{record_title}</a> vous est réservé pour emprunt jusqu\'au {hold_expiration_date}.</p>']), + 'DILICOM_PNB_RECORD_MAX_HOLD_COUNT' => Class_AdminVar_Meta::newDefault($this->_('Nombre maximum de réservations par document numérique'), ['value' => 3]), + 'DILICOM_PNB_PATRON_MAX_HOLD_COUNT' => Class_AdminVar_Meta::newDefault($this->_('Nombre maximum de réservations par utilisateur'), ['value' => 2]), + 'DILICOM_PNB_BOARD_DISPLAY_SECTION' => Class_AdminVar_Meta::newOnOff($this->_('Affichage des sections et genres dans le tableau d\'administration des prêts PNB')), + 'DILICOM_PNB_CHOOSE_DEVICES_ACTIVATE' => $this->_getActivateDevices(), + 'DILICOM_PNB_CHOOSE_DEVICES' => $this->_getDefaultPlayer(), + ]; + + } + + + protected function _getActivateDevices() { + return Class_AdminVar_Meta::newCombo($this->_('Activer la sélection de la liseuse'), + ['options' => function() { + return ['selectOptions' => + ['label' => $this->_('Liste à afficher:'), + 'multioptions' => ['0' => $this->_('Désactiver la sélection') , + Class_WebService_BibNumerique_Dilicom_Hub::FILTER_LCP => $this->_('Afficher les liseuses LCP uniquement'), + Class_WebService_BibNumerique_Dilicom_Hub::FILTER_ALL => $this->_('Afficher toutes les liseuses')]]];}, + 'value' => 0]); + + } + + + protected function _getDefaultPlayer() { + return + Class_AdminVar_Meta::newCombo($this->_('Liseuse séléctionnée par défaut'), + ['options' => function() { + return ['selectOptions' => + ['label' => $this->_('Liseuse selectionnée par défaut'), + 'multioptions' => (new Class_WebService_BibNumerique_Dilicom_Hub())->listUserAgents(Class_AdminVar::get('DILICOM_PNB_CHOOSE_DEVICES_ACTIVATE'))]];}, + 'value' => 0]); + } +} diff --git a/library/Class/WebService/BibNumerique/Dilicom/Hub.php b/library/Class/WebService/BibNumerique/Dilicom/Hub.php index 7e0db8e6dc6ef3fef02fa65cd324fbfc50f9e7c3..46dca862962639d181c522bec9c6bf2d2d2e931b 100644 --- a/library/Class/WebService/BibNumerique/Dilicom/Hub.php +++ b/library/Class/WebService/BibNumerique/Dilicom/Hub.php @@ -24,6 +24,8 @@ class Class_WebService_BibNumerique_Dilicom_Hub extends Class_WebService_Abstrac use Trait_Translator, Trait_StaticPhpCommand; const STATUS_OK = 'OK'; + const FILTER_LCP = 1; + const FILTER_ALL = 2; protected $_now; @@ -144,7 +146,7 @@ class Class_WebService_BibNumerique_Dilicom_Hub extends Class_WebService_Abstrac return $this->_error($this->_('Retour impossible. Le service "returnLoan" n\'a pas renvoyé une réponse valide.')); if (isset($content->returnMessage) && ($error = $content->returnMessage)) { - return $this->_error($this->_('Retour impossible. Le service "returnLoan" a renvoyé une erreur : "%s"', $this->returnError(implode($error)))); + return $this->_error($this->_('Retour impossible: "%s"', $this->returnError(implode($error)))); } if ($content->date) { @@ -217,10 +219,16 @@ class Class_WebService_BibNumerique_Dilicom_Hub extends Class_WebService_Abstrac } - public function listUserAgents() { + public function listUserAgents($filter = null) { + $options = []; + if ($filter == static::FILTER_LCP) + $options = ['drm' => 'LCP']; + + if (!Class_AdminVar::isDilicomPNBEnabled()) return ['0' => $this->_('PNB désactivé')]; - $response = $this->_dilicomCall('getUserAgent',[]); + + $response = $this->_dilicomCall('getUserAgent',$options); if (!$response) return []; @@ -230,11 +238,15 @@ class Class_WebService_BibNumerique_Dilicom_Hub extends Class_WebService_Abstrac if (!isset($content->listUserAgent)) return []; - $user_agents =[]; + $user_agents = []; foreach ($content->listUserAgent as $user_agent) $user_agents[$user_agent->userAgentId]=$user_agent->appName; asort($user_agents, SORT_STRING | SORT_FLAG_CASE | SORT_NATURAL); - return $user_agents; + + if (empty($options)) + return $user_agents; + + return ['0' => $this->_('Mon application n\'est pas dans la liste: DRM Adobe')] + $user_agents; } @@ -279,6 +291,10 @@ class Class_WebService_BibNumerique_Dilicom_Hub extends Class_WebService_Abstrac public function isAlbumLoanableBy($album, $user) { $currently_loaned_by_user = $this->_isAlbumCurrentlyLoanedByUser($album, $user); + + if ($currently_loaned_by_user->status == "success") + return $currently_loaned_by_user; + if (!$currently_loaned_by_user->returnMessage) return $currently_loaned_by_user; diff --git a/library/ZendAfi/View/Helper/TagDilicomWidget.php b/library/ZendAfi/View/Helper/TagDilicomWidget.php index 0ddd75efe01465f47a0ca076eb19368b1eb9317b..2e03e75590417ba4c34ca7f6a0c5808c4c780692 100644 --- a/library/ZendAfi/View/Helper/TagDilicomWidget.php +++ b/library/ZendAfi/View/Helper/TagDilicomWidget.php @@ -132,9 +132,14 @@ class ZendAfi_View_Helper_TagDilicomWidget extends ZendAfi_View_Helper_BaseHelpe if (!$this->_user) return $loanBookAnchor; - $hub = new Class_WebService_BibNumerique_Dilicom_Hub(); + $loanable = $hub->isAlbumLoanableBy($this->_album, $this->_user); + + if ((isset($loanable->link) && isset($loanable->link->url) && $loanable->link->url)) + $loanBookAnchor = $this->_anchor($loanable->link->url, + $this->_('Télécharger le livre')); + return ($error = $loanable->returnMessage) ? $this->_errorButton($error[0]) : $loanBookAnchor; diff --git a/tests/scenarios/PnbDilicom/PnbDilicomDisplayTest.php b/tests/scenarios/PnbDilicom/PnbDilicomDisplayTest.php index 9e8f9bbb445e0e3874250af1e5f339179e32006d..26f1426021af6f814708aeba9e79f76aee98bd18 100644 --- a/tests/scenarios/PnbDilicom/PnbDilicomDisplayTest.php +++ b/tests/scenarios/PnbDilicom/PnbDilicomDisplayTest.php @@ -482,6 +482,8 @@ class PnbDilicomDisplayBibNumeriqueControllerAjaxPopupBookActionTest extends Pnb /** @test */ public function loanBookPopupShouldContainsDeviceSelectorInAlphabeticOrderIfAdminVarSet() { + Class_AdminVar::set('DILICOM_PNB_CHOOSE_DEVICES_ACTIVATE', + Class_WebService_BibNumerique_Dilicom_Hub::FILTER_ALL); Class_AdminVar::set('DILICOM_PNB_CHOOSE_DEVICES', 285); $this->_http ->whenCalled('open_url') @@ -493,10 +495,27 @@ class PnbDilicomDisplayBibNumeriqueControllerAjaxPopupBookActionTest extends Pnb $this->assertXpathContentContains('//select[@name="devices"]//option[2][@label="Adobe Digital editions"][@value=1]','Adobe Digital editions'); $this->assertXpathContentContains('//select[@name="devices"]//option[1][@label="5 EBOOK READER SERIES"][@value=180]','5 EBOOK READER SERIES'); $this->assertXpathContentContains('//select[@name="devices"]//option[7][@label="baobab"][@value=285][@selected="selected"]','baobab'); + } + + + /** @test */ + public function loanBookPopupShouldContainsDeviceLCPFilteredSelectorInAlphabeticOrderIfAdminVarSet() { + Class_AdminVar::set('DILICOM_PNB_CHOOSE_DEVICES', 285); + Class_AdminVar::set('DILICOM_PNB_CHOOSE_DEVICES_ACTIVATE', Class_WebService_BibNumerique_Dilicom_Hub::FILTER_LCP); + $this->_http + ->whenCalled('open_url') + ->with(static::BASE_TEST_URL.'getUserAgent?login=afi-bib&password=secretPassword&glnContractor=123456789&drm=LCP',['auth' => ['user' =>'afi-bib', + 'password' => 'secretPassword']]) + ->answers(file_get_contents(__DIR__.'/getuseragent.json')); + $this->dispatch('/bib-numerique/loan-book-ajax/id/3', true); + $this->assertXpathContentContains('//select[@name="devices"]//option["0"][@value="0"]','Mon application n\'est pas dans la liste'); + $this->assertXpathContentContains('//select[@name="devices"]//option[2][@label="5 EBOOK READER SERIES"][@value=180]','5 EBOOK READER SERIES'); + $this->assertXpathContentContains('//select[@name="devices"]//option[8][@label="baobab"][@value=285][@selected="selected"]','baobab'); } + /** @test */ public function loanBookPopupShouldNotContainsDeviceSelectorIfAdminVarSetButErrorReturned() { Class_AdminVar::set('DILICOM_PNB_CHOOSE_DEVICES', 1); diff --git a/tests/scenarios/PnbDilicom/PnbDilicomViewHelperTest.php b/tests/scenarios/PnbDilicom/PnbDilicomViewHelperTest.php index c4ab89022664589f8dbaae23a95059bc23d07f47..6570421db05d9fc05b0b641a76945650e7061fb4 100644 --- a/tests/scenarios/PnbDilicom/PnbDilicomViewHelperTest.php +++ b/tests/scenarios/PnbDilicom/PnbDilicomViewHelperTest.php @@ -507,7 +507,7 @@ class PnbDilicomViewHelperRenderAlbumGetLoanStatusTest /** @test */ - public function withSimultaneousLoanLimitReachedButCurrentlyLoanedByCurrentUserHtmlShouldContainsButtonToLoadAgain() { + public function withSimultaneousLoanLimitReachedButCurrentlyLoanedByCurrentUserHtmlShouldContainsDowloadButton() { Class_Album_UsageConstraint::find(1)->setMaxNumberOfUsers(0)->assertSave(); Class_AdminVar::set('DILICOM_PNB_LOAN_COUNT_LIMIT',0); $this->fixture('Class_Loan_Pnb', @@ -516,13 +516,33 @@ class PnbDilicomViewHelperRenderAlbumGetLoanStatusTest 'record_origin_id' => 'Dilicom-88817216', 'order_line_id' => 'x321', 'expected_return_date' => '2025-06-01 20:10:00', - 'loan_link' => 'https://pnb-dilicom.centprod.com/v3//link/xxx.do']); + 'loan_link' => 'https://pnb-dilicom.centprod.com/v3//link/xxx.do']); $this->_html = $this->_helper->renderAlbum($this->book); - $this->assertXPath($this->_html, - '//a[contains(@href, "/bib-numerique/loan-book-ajax/id/3")]', - $this->_html); + $this->assertXPathContentContains($this->_html, + '//a[contains(@href, "https://pnb-dilicom.centprod.com/v3//link/xxx.do")]', + utf8_encode('Télécharger le livre')); + } + + + /** @test */ + public function withAlreadyLoanLimitReachedAndPreviousLoanShouldContainsImpossibleLoan() { + Class_Album_UsageConstraint::find(1)->setMaxNumberOfUsers(0)->assertSave(); + Class_AdminVar::set('DILICOM_PNB_LOAN_COUNT_LIMIT',0); + $this->fixture('Class_Loan_Pnb', + ['id' => 1, + 'user_id' => $this->logged_user->getId(), + 'record_origin_id' => 'Dilicom-88817216', + 'order_line_id' => 'x321', + 'expected_return_date' => '2005-06-01 20:10:00', + 'loan_link' => 'https://pnb-dilicom.centprod.com/v3//link/xxx.do']); + + $this->_html = $this->_helper->renderAlbum($this->book); + + $this->assertXPathContentContains($this->_html, + '//span', + utf8_encode('Emprunt impossible. Le nombre d\'emprunts simultanés pour ce document est atteint')); }