From 8326ed6dfb839f3aa64f2f46db5dbe7031c552e9 Mon Sep 17 00:00:00 2001 From: vinot <g.vinot@ville-montrouge.fr> Date: Fri, 26 Jun 2015 15:01:53 +0200 Subject: [PATCH] =?UTF-8?q?Premier=20Chapitre=20V1.1=20configuration=20:?= =?UTF-8?q?=20/admin/premier-chapitre/=20*=20proc=C3=A9dure=20complete=20d?= =?UTF-8?q?e=20r=C3=A9cup=C3=A9ration=20des=20donn=C3=A9es=20premier-chapi?= =?UTF-8?q?tre=20usage=20/premier-chapitre/=20*=20pr=C3=A9sentation=20du?= =?UTF-8?q?=20service=20*selection=20al=C3=A9atoire=20/premier-chapitre/al?= =?UTF-8?q?eatoire/=20=3D=3D=20/premier-chapitre/aleatoire/nb/8=20*=20list?= =?UTF-8?q?e=20complete=20/premier-chapitre/liste-complete/?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controllers/PremierChapitreController.php | 281 ++++++++++++++++++ .../scripts/premier-chapitre/index.phtml | 13 + .../controllers/PremierChapitreController.php | 127 ++++++++ .../scripts/premier-chapitre/common.phtml | 13 + .../scripts/premier-chapitre/inactive.phtml | 1 + .../scripts/premier-chapitre/index.phtml | 18 ++ .../views/scripts/recherche/viewnotice.phtml | 6 +- cosmogramme/sql/patch/patch_260.php | 13 + library/Class/Batch/PremierChapitre.php | 28 ++ library/Class/PremierChapitre.php | 123 ++++++++ library/Class/WebService/PremierChapitre.php | 257 ++++++++++++++++ .../View/Helper/ListeNotices/Vignettes.php | 7 +- .../View/Helper/PremierChapitre/Abstract.php | 31 ++ .../View/Helper/PremierChapitre/Frame.php | 43 +++ .../View/Helper/PremierChapitre/Lien.php | 53 ++++ .../View/Helper/PremierChapitre/Vignettes.php | 72 +++++ .../opac/images/lireavecpremierchapitre.png | Bin 0 -> 9853 bytes 17 files changed, 1082 insertions(+), 4 deletions(-) create mode 100644 application/modules/admin/controllers/PremierChapitreController.php create mode 100644 application/modules/admin/views/scripts/premier-chapitre/index.phtml create mode 100644 application/modules/opac/controllers/PremierChapitreController.php create mode 100644 application/modules/opac/views/scripts/premier-chapitre/common.phtml create mode 100644 application/modules/opac/views/scripts/premier-chapitre/inactive.phtml create mode 100644 application/modules/opac/views/scripts/premier-chapitre/index.phtml create mode 100644 cosmogramme/sql/patch/patch_260.php create mode 100644 library/Class/Batch/PremierChapitre.php create mode 100644 library/Class/PremierChapitre.php create mode 100644 library/Class/WebService/PremierChapitre.php create mode 100644 library/ZendAfi/View/Helper/PremierChapitre/Abstract.php create mode 100644 library/ZendAfi/View/Helper/PremierChapitre/Frame.php create mode 100644 library/ZendAfi/View/Helper/PremierChapitre/Lien.php create mode 100644 library/ZendAfi/View/Helper/PremierChapitre/Vignettes.php create mode 100644 public/opac/images/lireavecpremierchapitre.png diff --git a/application/modules/admin/controllers/PremierChapitreController.php b/application/modules/admin/controllers/PremierChapitreController.php new file mode 100644 index 00000000000..b8e835a8c65 --- /dev/null +++ b/application/modules/admin/controllers/PremierChapitreController.php @@ -0,0 +1,281 @@ +<?php +/** + * Copyright (c) 2015, Gael VINOT (Ville de Montrouge). All rights reserved. + * + * BOKEH is free software; you can redistribute it and/or modify + * it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE as published by + * the Free Software Foundation. + * + * There are special exceptions to the terms and conditions of the AGPL as it + * is applied to this software (see README file). + * + * BOKEH is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE + * along with BOKEH; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +class Admin_PremierChapitreController extends ZendAfi_Controller_Action { + use Trait_Translator; + protected $_baseUrlOptions = ['module' => 'admin', 'controller' => 'premier-chapitre']; + + private $active; + private $pc_bib_id; + private $pc_bmid; + private $pc_bmkey; + + private function objectsIntoArray($arrObjData, $arrSkipIndices = array()) { + $arrData = array(); + // if input is object, convert into array + if (is_object($arrObjData)) { + $arrObjData = get_object_vars($arrObjData); + } + if (is_array($arrObjData)) { + foreach ($arrObjData as $index => $value) { + if (is_object($value) || is_array($value)) { + $value = self::objectsIntoArray($value, $arrSkipIndices); // recursive call + } + if (in_array($index, $arrSkipIndices)) { + continue; + } + $arrData[$index] = $value; + } + } + return $arrData; + } + + public function init() { + parent::init(); + $this->view->titre = 'Premier Chapitre'; + $this->pc_bib_id = Class_AdminVar::get('PREMIERCHAPITRE_BIB_ID'); + $this->pc_bmid = Class_AdminVar::get('PREMIERCHAPITRE_BMID'); + $this->pc_bmkey = Class_AdminVar::get('PREMIERCHAPITRE_BMKEY'); + $this->active = (!empty($this->pc_bib_id) && !empty($this->pc_bmid) && !empty($this->pc_bmkey)); + } + + public function preDispatch() { + $no_front_actions = array(); + $request = $this->_request; + $action = $request->getActionName(); + if (!$this->active && $action != 'configure' && $action != 'adminvaredit') { + $request->setActionName('configure'); + $request->setDispatched(false); + } + else + // Desactiver le view renderer normal + if (in_array($this->_request->getParam("action"),$no_front_actions)) { + $viewRenderer = $this->getHelper('ViewRenderer'); + $viewRenderer->setNoRender(); + } + } + + public function indexAction() { + $contenu .= '<ul>'; + $contenu .= '<li><a href="/admin/premier-chapitre/configure/">Configuration</a></li>'; + //tant que le service n'est pas configuré on n'autorise pas les actions de mise en Å“uvre + if ($this->active) { + $contenu .= '<li><a href="/admin/premier-chapitre/maj-ref/">Mise à jour du référentiel</a></li>'; + $contenu .= '<li><a href="/admin/premier-chapitre/download-ref/">Téléchargement du référentiel</a></li>'; + $contenu .= '<li><a href="/admin/premier-chapitre/maj-db/">Mise à jour de la base</a></li>'; + $contenu .= '<li><a href="/admin/premier-chapitre/link-notices/">Lier les notices</a></li>'; + } + $contenu .= '</ul>'; + $this->view->titre .= ' :: Index des fonctions'; + $this->view->contenu = $contenu; + } + + function majRefAction() { + $data = false; + $pc_ws = new Class_WebService_PremierChapitre(); + $data = $pc_ws->generate(); + if ($data) { + $contenu = date('H:i:s')." Le référentiel a bien été mise à jour\n"; + $contenu .= '<a href="/admin/premier-chapitre/download-ref/">Téléchargement du référentiel</a>'; + } + else $contenu = "Une erreur est survenue\nle référentiel n'a pas pu être mis a jour\nVeuillez réessayer ultérieurement"; + $this->view->contenu = '<pre>'.$contenu.'</pre>'; + $this->view->contenu .= "<br/><a href='/admin/premier-chapitre/'>Retour à l'index du service Premier-chapitre</a>"; + $this->view->titre .= ' :: Mise à jour du référentiel'; + $this->renderScript('premier-chapitre/index.phtml'); + } + function downloadRefAction() { + $data = false; + $pc_ws = new Class_WebService_PremierChapitre(); + $data = $pc_ws->getDatafile(); + if ($data){ + $contenu = date('H:i:s')." Le référentiel a bien été téléchargé sur le serveur\nIl est possible de procéder à la mise à jour de la base\n"; + $contenu .= '<a href="/admin/premier-chapitre/maj-db/">Mise à jour de la base</a>'; + } + else $contenu = "Une erreur est survenue\nle référentiel n'a pas pu être téléchargé\nVeuillez réessayer ultérieurement"; + $this->view->contenu = '<pre>'.$contenu.'</pre>'; + $this->view->contenu .= "<br/><a href='/admin/premier-chapitre/'>Retour à l'index du service Premier-chapitre</a>"; + $this->view->titre .= ' :: Téléchargement du référentiel'; + $this->renderScript('premier-chapitre/index.phtml'); + } + + function majDbAction() { + $data = false; + $pc_ws = new Class_WebService_PremierChapitre(); + $data = $pc_ws->updateDatabase(); + if ($data) { + $contenu = date('H:i:s')." La base a bien été mise à jour\n"; + $contenu .= '* '.$data['total']." oeuvres ont été lues\n"; + if ($data['new']) $contenu .= '* '.$data['new'].(($data['new']>1)?" ont été ajoutées":" a été ajoutée")."\n"; + if ($data['maj']) $contenu .= '* '.$data['maj'].(($data['maj']>1)?" ont été mises":" a été mise")." à jour\n"; + if ($data['err']) $contenu .= '* '.$data['err'].(($data['err']>1)?" erreurs n'ont pas été traitées":" erreur n'a pas été traitée")."\n"; + $contenu .= '<a href="/admin/premier-chapitre/link-notices/">Lier les notices</a>'; + } + else $contenu = "Une erreur est survenue\nLa base n'a pu être mise à jour\nVeuillez réessayer ultérieurement"; + $this->view->contenu = '<pre>'.$contenu.'</pre>'; + $this->view->contenu .= "<br/><a href='/admin/premier-chapitre/'>Retour à l'index du service Premier-chapitre</a>"; + $this->view->titre .= ' :: Mise à jour de la base'; + $this->renderScript('premier-chapitre/index.phtml'); + } + + function linkNoticesAction() { + $data = false; + $pc_ws = new Class_WebService_PremierChapitre(); + $data = $pc_ws->linkToNotices(); + if ($data) { + $contenu = date('H:i:s')." Les premiers-chapitres ont bien été liés aux notices\n"; + $contenu .= '* '.$data['total']." oeuvres ont été traités\n"; + $contenu .= '* '.$data['maj'].(($data['maj']>1)?" ont été mises":" a été mise")." à jour\n"; + } + else $contenu = "Une erreur est survenue\nLes liaisons n'ont pu être faites\nVeuillez réessayer ultérieurement"; + $this->view->contenu = '<pre>'.$contenu.'</pre>'; + $this->view->contenu .= "<br/><a href='/admin/premier-chapitre/'>Retour à l'index du service Premier-chapitre</a>"; + $this->view->titre .= ' :: Liaison aux notices'; + $this->renderScript('premier-chapitre/index.phtml'); + } + + public function configureAction() { + $this->view->titre .= ' :: Configuration du service'; + Class_ScriptLoader::getInstance() + ->loadDataTables() + ->addJQueryReady("$('#adminvars').dataTable({ 'iDisplayLength': -1, 'bPaginate': false, 'columnDefs' : [{'orderable': false, 'targets': 2}] })") + ->loadJQueryUI() + ->addJQueryReady('$(document).tooltip({ + items: "[data-tooltip]", + content: function() { + var element = $(this); + var html = element.find("span").html(); + return (html.trim() != "") ? html : "Description non disponible"; + }, + position: { my: "left top+30", at: "left top"}, + hide: {duration: 1000} + })'); + + $var_connues = array('PREMIERCHAPITRE_BIB_ID', 'PREMIERCHAPITRE_BMID', 'PREMIERCHAPITRE_BMKEY'); + $existing_variables = Class_AdminVar::findAllBy(['where'=> "CLEF like 'PREMIERCHAPITRE%'",'order' => 'CLEF']); + $existing_clefs = []; + foreach ($existing_variables as $var) + $existing_clefs[] = $var->getId(); + + // creer les variables manquantes + foreach ($var_connues as $name) + if (!in_array($name, $existing_clefs)) + $existing_variables[] = Class_AdminVar::set($name, ''); + + $table = '<table id="adminvars"> + <thead> + <tr class="soustitre"> + <th>'.$this->traduire('Clef').'</th> + <th>'.$this->traduire('Valeur').'</th> + <th>'.$this->traduire('action').'</th> + </tr> + </thead> + <tbody>'; + $ligne = 0; + foreach($existing_variables as $var) { + if(preg_match('^%0D%0A^',$var->getValeur())) + $value = urldecode(str_replace('%0D%0A','<br />',$var->getValeur())); + else + $value = urldecode($var->getValeur()); + $ligne ++ ; + $table .= '<tr class="'.(($ligne & 1) ? "first" : "second").'" data-tooltip=""> + <td style="vertical-align:top;" >'.$var->getClef().' + <span style="display:none;"> + '.Class_AdminVar::helpFor($var->getClef()).' + </span> + </td> + <td>'; + $table .= wordwrap($value, 35, "<br />", 1); + $table .=' </td> + <td style="width:2%;text-align:center"> + <a data-popup="true" href="'.$this->view->url(['action' => 'adminvaredit', 'cle' => $var->getClef()]).'">'.$this->view->boutonIco("type=edit").'</a> + </td> + </tr>'; + } + $table .= '</tbody></table>'; + $this->view->contenu = $table; + $this->view->contenu .= "<br/><a href='/admin/premier-chapitre/'>Retour à l'index du service Premier-chapitre</a>"; + $this->renderScript('premier-chapitre/index.phtml'); + } + + public function adminvareditAction() { + $id = $this->_getParam('cle'); + $cle = Class_AdminVar::find($id); + + if ($this->_request->isPost()) { + $filter = new Zend_Filter_StripTags(); + $new_valeur = $this->_request->getPost('valeur'); + $cle->setValeur(trim($filter->filter($new_valeur))); + $cle->save(); + $this->_helper->notify('Variable '.$id.' sauvegardée'); + $this->_redirect('admin/premier-chapitre/adminvaredit/cle/'.$id); + return; + } + + $var_valeur = $cle->getValeur(); + + $var_cle = $cle->getId(); + $tuto = Class_AdminVar::helpFor($cle->getId()); + $contenu = '<center> +<div class="form" align="center"> +<form name="form" action="'.BASE_URL.'/admin/premier-chapitre/adminvaredit" method="post"> +<fieldset> +<legend>'.$this->view->traduire('Variable').'</legend> +<br /> +<div class="formTable"> +<table cellspacing="2"> + <tr> + <td class="droite">'.$var_cle.'</td> + <td class="gauche">'.$tuto.'</td> + </tr> + <tr> + <td class="droite">Nouvelle valeur</td> + <td class="gauche"><textarea rows="10" cols="60" name="valeur">'.$var_valeur.'</textarea></td> + </tr> +</table><br /> +</div> +<input type="hidden" name="cle" value="'.$var_cle.'"/> +</fieldset><br> + +<table> +<tr> + <td align="right" style="padding-right:5px;">'.$this->view->bouton('type=V').' </td> + <td align="left" style="padding-left:5px;"> '.$this->view->bouton('id=29','picto=back.gif','texte=Retour','url='.BASE_URL.'/admin/premier-chapitre/configure','largeur=120px').'</td> +</tr> +</table> +</form> +</div> +</center>'; + $this->view->titre .= ' :: Modifier la variable: ' . $cle->getId(); + $this->view->contenu = $contenu; + $this->renderScript('premier-chapitre/index.phtml'); + } + + public function __call($method, $args) { + if ('Action' == substr($method, -6)) { + // Si une méthode d'action n'est pas trouvée, + // rediriger vers l'action + return $this->_forward('index'); + } + // pour tout autre méthode, levée d'une exception + throw new Exception('Méthode invalide "' . $method . '" appelée',500); + } +} +?> diff --git a/application/modules/admin/views/scripts/premier-chapitre/index.phtml b/application/modules/admin/views/scripts/premier-chapitre/index.phtml new file mode 100644 index 00000000000..984b2f459d8 --- /dev/null +++ b/application/modules/admin/views/scripts/premier-chapitre/index.phtml @@ -0,0 +1,13 @@ +<div class="titre"> + <div class="titreInner"> + <h1><?php echo $this->escape($this->title);?></h1> + <div class="rss"></div> + </div> +</div> +<div class="contenu"> + <div class="contenuInner"> + <div class="news-3 news"> +<?php echo $this->contenu;?> + </div> + </div> +</div> \ No newline at end of file diff --git a/application/modules/opac/controllers/PremierChapitreController.php b/application/modules/opac/controllers/PremierChapitreController.php new file mode 100644 index 00000000000..29b9eedfa82 --- /dev/null +++ b/application/modules/opac/controllers/PremierChapitreController.php @@ -0,0 +1,127 @@ +<?php +/** + * Copyright (c) 2015, Gael VINOT (Ville de Montrouge). 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 PremierChapitreController extends Zend_Controller_Action { + + private $active; + public function init() { + $pc_bib_id = Class_AdminVar::get('PREMIERCHAPITRE_BIB_ID'); + $pc_bmid = Class_AdminVar::get('PREMIERCHAPITRE_BMID'); + $pc_bmkey = Class_AdminVar::get('PREMIERCHAPITRE_BMKEY'); + $this->active = (!empty($pc_bib_id) && !empty($pc_bmid) && !empty($pc_bmkey)); + } + + public function preDispatch() { + $no_front_actions = array("aleatoire", "ajout-frame"); + $request = $this->_request; + $action = $request->getActionName(); + if (!$this->active && $action != 'inactive') { + $request->setActionName('inactive'); + $request->setDispatched(false); //force le rechargement du controller avec la nouvelle action + } + else { + // Désactiver le view renderer normal pour les éléments type blocs + if (in_array($this->_request->getParam("action"),$no_front_actions)) { + $viewRenderer = $this->getHelper('ViewRenderer'); + $viewRenderer->setNoRender(); + } + } + } + + function inactiveAction() { + $this->render('inactive'); + } + + function indexAction() { + $html = ""; + $this->title = ""; + $this->view->pc_ids = Class_PremierChapitre::getAleatoire(14); + } + + function listeCompleteAction() { + $pcs = Class_PremierChapitre::getAll(); + $html = $this->view->premierChapitre_Vignettes($pcs); + $this->view->title = "liste des Premiers Chapitres"; + $this->view->contenu = $html; + $this->renderScript('premier-chapitre/common.phtml'); + } + + function noticeAction() { + $ean = $this->_request->getParam('ean'); + $ean = Class_PremierChapitre::correct_ean($ean); + if ($ean) { + $pc = Class_PremierChapitre::findFirstBy(['ean' => $ean]); + if ($pc) { + $ntc = $pc->getNotice(); + $url_notice = ZendAfi_View_Helper_UrlNotice::urlNotice($ntc); + $this->_redirect($url_notice); + } + else + $this->_redirect('/'); + } + else + $this->_redirect('/'); + } + + function ajoutFrameAction() { + $params = $this->_request->getParams(); + if (!empty($params['id_notice'])) { + $notice = Class_Notice::find($params['id_notice']); + $ean = $notice->getIsbnOrEan(); + } + else { + $ean = (!empty($params['ean']))?$params['ean']:((!empty($params['id']))?$params['id']:""); + } + $ean = Class_PremierChapitre::getLoader()->correct_ean($ean); + $html = ""; + if ($ean) { + $pc = Class_PremierChapitre::findFirstBy(['ean' => $ean]); + if (!empty($pc)) { + $html = $this->view->premierChapitre_Frame($pc); + } + } + $this->getResponse()->setHeader('Content-Type', 'text/html;charset=utf-8'); + $this->getResponse()->setBody($html . Class_ScriptLoader::getInstance()->html()); + } + + function aleatoireAction() { + $nb = $this->_request->getParam('nb'); + if (!preg_match('@^\d+$@',$nb)) + $pcs = Class_PremierChapitre::getAleatoire(); + else + $pcs = Class_PremierChapitre::getAleatoire($nb); + $html = $this->view->premierChapitre_Vignettes($pcs); + $this->getResponse()->setHeader('Content-Type', 'text/html;charset=utf-8'); + $this->getResponse()->setHeader('Pragma', 'no-cache', true); + $this->getResponse()->setBody($html . Class_ScriptLoader::getInstance()->html()); + } + + public function __call($method, $args) { + if ('Action' == substr($method, -6)) { + // Si une méthode d'action n'est pas trouvée, + // rediriger vers l'action + return $this->_forward('index'); + } + // pour tout autre méthode, levée d'une exception + throw new Exception('Méthode invalide "' . $method . '" appelée',500); + } +} +?> diff --git a/application/modules/opac/views/scripts/premier-chapitre/common.phtml b/application/modules/opac/views/scripts/premier-chapitre/common.phtml new file mode 100644 index 00000000000..2bfe9708e94 --- /dev/null +++ b/application/modules/opac/views/scripts/premier-chapitre/common.phtml @@ -0,0 +1,13 @@ +<div class="titre"> + <div class="titreInner"> + <h1><?php echo $this->escape($this->title);?></h1> + <div class="rss"></div> + </div> +</div> +<div class="contenu"> + <div class="contenuInner"> + <div class="news-3 news"> +<?php echo $this->contenu;?> + </div> + </div> +</div> diff --git a/application/modules/opac/views/scripts/premier-chapitre/inactive.phtml b/application/modules/opac/views/scripts/premier-chapitre/inactive.phtml new file mode 100644 index 00000000000..c15b626f412 --- /dev/null +++ b/application/modules/opac/views/scripts/premier-chapitre/inactive.phtml @@ -0,0 +1 @@ +Service inactif diff --git a/application/modules/opac/views/scripts/premier-chapitre/index.phtml b/application/modules/opac/views/scripts/premier-chapitre/index.phtml new file mode 100644 index 00000000000..f1e43c1af94 --- /dev/null +++ b/application/modules/opac/views/scripts/premier-chapitre/index.phtml @@ -0,0 +1,18 @@ +<div class="titre"> + <div class="titreInner"> + <h1><?php echo $this->escape($this->title);?></h1> + <div class="rss"></div> + </div> +</div> +<div class="contenu"> + <div class="contenuInner"> + <div class="news-3 news"> + <div> + <p><img src="/public/opac/images/lireavecpremierchapitre.png" alt="premierchapitre" style="position:relative;top:-5px;"></p> + <p><span style="font-size:12px">Cliquez sur une couverture pour lire un extrait. Vous pouvez ensuite récupérer le livre à la bibliothèque.</span></p> + <p><span style="font-size:12px"><a href="/premier-chapitre/liste-complete/">Cliquez ici Pour voir la liste complète <img src="/public/opac/images/buttons/2rightarrow.png"></a></span></p> + </div> +<?php echo $this->premierChapitre_Vignettes($this->pc_ids,['target' => 'frame', 'linkNotice' => 1]); ?> + </div> + </div> +</div> diff --git a/application/modules/opac/views/scripts/recherche/viewnotice.phtml b/application/modules/opac/views/scripts/recherche/viewnotice.phtml index 83c3650763e..a7fda1b98e3 100644 --- a/application/modules/opac/views/scripts/recherche/viewnotice.phtml +++ b/application/modules/opac/views/scripts/recherche/viewnotice.phtml @@ -95,8 +95,9 @@ $script_loader = Class_ScriptLoader::getInstance() </div> </div> </div> - <?php +//picto premier-chapitre +echo $this->premierChapitre_Lien($this->notice,['targetLink'=>'ancre']); // Entete Notice echo sprintf('<div class="entete_notice">%s</div>'. '<div class="flags_notice">%s</div>'. @@ -106,6 +107,7 @@ echo sprintf('<div class="entete_notice">%s</div>'. $this->notice_Flags($this->notice, $this->preferences), $this->notice_Blocs($this->notice, $this->preferences)."<div class='clear'></div>", $this->notice_Onglets($this->notice, $this->preferences)); - +//frame premier-chapitre +echo '<a name="pc_ancre"></a>'.$this->premierChapitre_Frame($this->notice); $this->closeBoite(); ?> diff --git a/cosmogramme/sql/patch/patch_260.php b/cosmogramme/sql/patch/patch_260.php new file mode 100644 index 00000000000..d4938cdb2ed --- /dev/null +++ b/cosmogramme/sql/patch/patch_260.php @@ -0,0 +1,13 @@ +<?php +$adapter = Zend_Registry::get('sql'); +$adapter->query('CREATE TABLE IF NOT EXISTS `pc_database` ( ' + .'`ean` varchar(17) NOT NULL, ' + .'`url` varchar(250) NOT NULL, ' + .'`titre` text NOT NULL, ' + .'`auteur` text NOT NULL, ' + .'`couverture` varchar(250) NOT NULL, ' + .'`couverture_mini` varchar(250) NOT NULL, ' + .'`clef_oeuvre` varchar(200) NOT NULL ' + .') engine=MyISAM default charset=utf8;'); +$adapter->query('ALTER TABLE `pc_database` ADD PRIMARY KEY (`ean`), ADD UNIQUE KEY `ean` (`ean`);'); +?> \ No newline at end of file diff --git a/library/Class/Batch/PremierChapitre.php b/library/Class/Batch/PremierChapitre.php new file mode 100644 index 00000000000..cbfc2bc81b3 --- /dev/null +++ b/library/Class/Batch/PremierChapitre.php @@ -0,0 +1,28 @@ +<?php +/** + * Copyright (c) 2015, Gaël VINOT (Ville de Montrouge). 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_Batch_PremierChapitre extends Class_Batch_RessourceNumerique{ + protected function _getService() { + return new Class_WebService_PremierChapitre(); + } +} +?> \ No newline at end of file diff --git a/library/Class/PremierChapitre.php b/library/Class/PremierChapitre.php new file mode 100644 index 00000000000..b4b73109d51 --- /dev/null +++ b/library/Class/PremierChapitre.php @@ -0,0 +1,123 @@ +<?php +/** + * Copyright (c) 2015, Gaël VINOT (Ville de Montrouge). 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 PremierChapitreLoader extends Storm_Model_Loader { + //nettoyage de l'entrée isbn/ean + public function correct_ean($isbn) { + $elem = new Class_Isbn($isbn); + $ean = $elem->getAll(); + if ($ean['statut'] != 2) return false; + if (!empty($ean['ean'])) return $ean['ean']; + if (!empty($ean['isbn13'])) return str_replace('-','',$ean['isbn13']); + return false; + } + + public function getPremierChapitreIdsByRequete($req) { + if (!$req) + return []; + + $closure = function() use ($req) { + return Zend_Registry::get('sql')->fetchAllByColumn($req); + }; + return (new Storm_Cache()) + ->useImplodeExplodeSerialization() + ->memoize([$req, __CLASS__, __FUNCTION__], + $closure); + } + + public function findAllByRequete($req, $nb_par_page, $page_no=1 ) { + if (!$page_no) $page_no=1; + $ids = $this->getPremierChapitreIdsByRequete($req); + if ($nb_par_page) + $ids = array_slice($ids, + ($page_no-1) * $nb_par_page, + $nb_par_page); + if (empty($ids)) + return []; + return $this->findAllBy(['ean' => $ids, 'order' => 'titre ASC']); + } + + public function getAll($nb_par_page, $page_no=1) { + $req = "select t.`ean` from pc_database t left join notices n on replace(n.`isbn`,'-','') = t.`ean` or n.`clef_oeuvre` = t.`clef_oeuvre` where n.`id_notice` is not null"; + if ($nb_par_page) return $this->findAllByRequete($req,$nb_par_page,$page_no); + else return $this->findAllByRequete($req); + } + + public function getAleatoire($nb_aff) { + if (!$nb_aff) $nb_aff = 8; + $req = "select t.`ean` from pc_database t\n"; + $req .="left join notices n on replace(n.`isbn`,'-','') = t.`ean`\n"; + $req .= "where n.`id_notice` is not null ORDER BY RAND() LIMIT 0,".$nb_aff; + return $this->findAllByRequete($req); + } + + public function getByClef($clef) { + return $this->findFirstBy(['clef_oeuvre' => $clef]); + } + + public function getByNotice($ntc) { + $pc = $this->getByClef($ntc->getClefOeuvre()); + if (!$pc) { + $ean = $this->correct_ean($ntc->getIsbn()); + if ($ean) { + $pc = $this->findFirstBy(['ean' =>$ean]); + if($pc) $pc->setClefOeuvre($ntc->getClefOeuvre())->save(); + } + } + return $pc; + } +} + +class Class_PremierChapitre extends Storm_Model_Abstract { + protected $_loader_class = 'PremierChapitreLoader'; + protected $_table_name = 'pc_database'; + protected $_table_primary = 'ean'; + + protected $_default_attribute_values = [ + 'ean' => '', + 'url' => '', + 'titre' => '', + 'auteur' => '', + 'couverture' => '', + 'couverture_mini' => '', + 'clef_oeuvre' => '']; + protected $_belongs_to = ['notice' => ['model' => 'Class_Notice', 'referenced_in' => 'clef_oeuvre']]; + + public function getMiniature() { + return $this->getCouvertureMini(); + } + + public function getNotice() { + $cle = $this->getClefOeuvre(); + if (!empty($cle)) { + $ntc = Class_Notice::findFirstBy(['clef_oeuvre' => $cle]); + } + else { + //on fait une recherche simple de la notice + $ntc = Class_Notice::findFirstBy(['where' => "replace(`isbn`,'-','') = '".$this->getId()."'"]); + if ($ntc) { + //on met à jour l'enregistrement premier-chapitre + $this->setClefOeuvre($ntc->getClefOeuvre())->save(); + } + } + return $ntc; + } +} +?> diff --git a/library/Class/WebService/PremierChapitre.php b/library/Class/WebService/PremierChapitre.php new file mode 100644 index 00000000000..963b8d0f8f2 --- /dev/null +++ b/library/Class/WebService/PremierChapitre.php @@ -0,0 +1,257 @@ +<?php +/** + * Copyright (c) 2015, Gaël VINOT (Ville de Montrouge). 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_WebService_PremierChapitre {// extends Class_WebService_Abstract { - on redefini l'ensemble du modele a l'identique + protected $_message; + protected static $_http_client; + protected static $_auth = false; + protected static $_baseurl = 'http://www.premierchapitre.fr/client/'; + protected static $_datafile = "liste_livre.xml"; + + private $pc_bib_id; + private $pc_bmid; + private $pc_bmkey; + + public function __construct() { + $this->pc_bib_id = Class_AdminVar::get('PREMIERCHAPITRE_BIB_ID'); + $this->pc_bmid = Class_AdminVar::get('PREMIERCHAPITRE_BMID'); + $this->pc_bmkey = Class_AdminVar::get('PREMIERCHAPITRE_BMKEY'); + } + + public static function setDefaultHttpClient($http_client) { + static::$_http_client = $http_client; + } + + public static function getHttpClient() { + if (static::$_http_client==null || !isset(static::$_http_client)) { + static::$_http_client = new Zend_Http_Client(static::$_baseurl, array('keepalive' => true)); + static::$_http_client->setCookieJar(); + } + return static::$_http_client; + } + public static function resetHttpClient() { + static::$_http_client = null; + static::$_auth = false; + } + + public function authentify() { + $access_url = static::$_baseurl.'connexion/connexion.php'; + $access_params = ["rep" => $this->pc_bib_id, "email" => $this->pc_bmid, "mdp" => $this->pc_bmkey]; + $admin_url = static::$_baseurl.$this->pc_bib_id.'/'; + + if(static::$_auth == true) { + $data = $this->httpGet($admin_url); + if (preg_match('@body data-client="([^"]*)"@Usi',$data->getBody(),$m)) { + return static::$_auth; + } + static::$_auth = false; + } + $this->httpPost($access_url,$access_params); + $data = $this->httpGet($admin_url); + if (preg_match('@body data-client="([^"]*)"@Usi',$data->getBody(),$m)) { + static::$_auth = true; + } + return static::$_auth; + } + + public function generate() { + if (!$this->authentify()) { + throw new Exception("PremierChapitre : Erreur d'identification au service",500); + return false; + } + $admin_url = static::$_baseurl.$this->pc_bib_id.'/'; + $data = $this->httpGet($admin_url); + if (!preg_match('@body data-client="([^"]*)"@Usi',$data->getBody(),$m)) { + static::$_auth = false; + throw new Exception("PremierChapitre : Erreur d'identification au service",500); + return false; + } + $clientId = $m[1]; + $generate_url = static::$_baseurl.'class/generation_xml.php'; + $generate_params = ['clientId'=>$clientId,'clientRep'=>$this->pc_bib_id]; + $data = $this->httpPost($generate_url,$generate_params); + //$this->setMessage("Mise à jour : ".date("d/m/Y à H:i:s")); + if ($data->getMessage() == "OK") return true; + return false; + //return $data; + } + public function getDatafile() { + if (!$this->authentify()) { + throw new Exception("PremierChapitre : Erreur d'identification au service",500); + return false; + } + $download_url = static::$_baseurl.'plugin/php_download/download.php?f=liste_livre.xml&r='.$this->pc_bib_id; + $data = $this->httpGet($download_url); + $ret = file_put_contents(static::$_datafile,$data->getBody()); + if ($ret === false) { + throw new Exception("PremierChapitre : Erreur de récupération des données",500); + return false; + } + return true; + } + + public function updateDatabase() { + if (!file_exists(static::$_datafile)) { + $this->getDatafile(); + } + $champs = array( + 'ean' => '', + 'url_reader' => 'url', + 'titre' => '', + 'auteur_nom' => 'auteur', + 'url_couverture' => 'couverture', + 'url_couverture_small' => 'couverture_mini', + ); + $datas = file_get_contents(static::$_datafile); + $xmlObj = simplexml_load_string($datas); + $datas = $this->objectsIntoArray($xmlObj); + //on charge la base actuelle + $pc_all = Class_PremierChapitre::findAll(); + $pc_db = array(); + foreach ($pc_all as $ch) { + $pc_db[$ch->getId()] = $ch; + } + //on procède à la moisson : mise a jour des données existantes et ajout des nouveaux enregistrements + $cpt = ['maj' => 0, 'new' => 0, 'err' =>0, 'total' => count($datas['livre'])]; + foreach ($datas['livre'] as $key => $item) { + //on nettoie l'ean/isbn + $id = Class_PremierChapitre::correct_ean($item['ean']); + if ($id) { + $item['ean'] = $id; + //le document existe deja dans la base + if (!empty($pc_db[$id])) { + $m = false; + foreach ($champs as $k => $v) { + $f = $k; + $tmp = (!empty($item[$f]))?trim($item[$f]):''; + if (!empty($v)) $f = $v; + if ($pc_db[$id]->callGetterByAttributeName($f) != $tmp) { + $pc_db[$id]->callSetterByAttributeName($f,$tmp); + $m = true; + } + } + if($m) { + $pc_db[$id]->save(); + $cpt['maj']++; + } + } + else { + $chap = array(); + foreach ($champs as $k => $v) { + $f = $k; + $tmp = (!empty($item[$f]))?trim($item[$f]):''; + if (!empty($v)) $f = $v; + $chap[$f] = $tmp; + } + $cur_pc = Class_PremierChapitre::newInstance($chap); + $cur_pc->save(); + $pc_db[$id] = $cur_pc; + $cpt['new']++; + } + } + else { + //ean incorrect on ignore + $cpt['err']++; + } + } + return $cpt; + } + + public function linkToNotices() { + $data = fetchAll("select t.`ean`, n.`clef_oeuvre` from pc_database t left join notices n on replace(n.`isbn`,'-','') = t.`ean` where n.`clef_oeuvre` is not null"); + if (!$data) return false; + $pc_all = Class_PremierChapitre::findAll(); + $pc_db = array(); + foreach ($pc_all as $ch) { + $pc_db[$ch->getId()] = $ch; + } + $cpt = ['maj' => 0, 'total' => count($data)]; + foreach ($data as $enreg) { + $f = "clef_oeuvre"; + if (!$enreg[$f]) continue; + $id = $enreg["ean"]; $tmp = $enreg[$f]; + if (!empty($pc_db[$id])) { + if ($pc_db[$id]->callGetterByAttributeName($f) != $tmp) { + $pc_db[$id]->callSetterByAttributeName($f,$tmp); + $pc_db[$id]->save(); + $cpt['maj']++; + } + } + } + return $cpt; + } + + function setMessage($message) { + $this->_message=$message; + } + function getMessage() { + return $this->_message; + } + + public function httpGet($url) { + $client = $this->getHttpClient(); + $client->resetParameters(); + $client->setUri($url); + return $client->request(Zend_Http_Client::GET); + } + public function httpPost($url,$params) { + $client = $this->getHttpClient(); + $client->resetParameters(); + $client->setUri($url); + $client->setParameterPost($params); + return $client->request(Zend_Http_Client::POST); + } + + public function isEnabled() { + return ('' != trim(Class_AdminVar::get('PREMIERCHAPITRE_BIB_ID'))); + } + public function getName() { + return 'Premier-Chapitre'; + } + protected function _deleteNonHarvested() { + return $this; + } + private function objectsIntoArray($arrObjData, $arrSkipIndices = array()) { + $arrData = array(); + // if input is object, convert into array + if (is_object($arrObjData)) { + $arrObjData = get_object_vars($arrObjData); + } + if (is_array($arrObjData)) { + foreach ($arrObjData as $index => $value) { + if (is_object($value) || is_array($value)) { + $value = $this->objectsIntoArray($value, $arrSkipIndices); // recursive call + } + if (in_array($index, $arrSkipIndices)) { + continue; + } + $arrData[$index] = $value; + } + } + return $arrData; + } + + public function update() { + } + public function harvest() { + } +} +?> diff --git a/library/ZendAfi/View/Helper/ListeNotices/Vignettes.php b/library/ZendAfi/View/Helper/ListeNotices/Vignettes.php index 957571966f2..287af74c6e4 100644 --- a/library/ZendAfi/View/Helper/ListeNotices/Vignettes.php +++ b/library/ZendAfi/View/Helper/ListeNotices/Vignettes.php @@ -46,7 +46,8 @@ class ZendAfi_View_Helper_ListeNotices_Vignettes extends ZendAfi_View_Helper_Lis . $this->_author($notice, $url_notice) . $this->_docType($notice) . $this->_image($notice, $preferences) - . $this->_info($notice, $champs); + . $this->_info($notice, $champs) + . $this->_pcTag($notice); return $this->_tag('div', $html, ['class' => 'vignette', 'data-id' => $notice->getId()]); @@ -106,7 +107,9 @@ class ZendAfi_View_Helper_ListeNotices_Vignettes extends ZendAfi_View_Helper_Lis ['entete' => str_replace('T', '', $fields)]), ['class' => 'vignette_info']); } - + protected function _pcTag($record) { + return $this->_tag('div',$this->view->premierChapitre_Lien($record),['style' => 'margin-left:70px;']); + } protected function _readSpeakerTagFor($record) { if (!$this->_read_speaker) diff --git a/library/ZendAfi/View/Helper/PremierChapitre/Abstract.php b/library/ZendAfi/View/Helper/PremierChapitre/Abstract.php new file mode 100644 index 00000000000..cf264031769 --- /dev/null +++ b/library/ZendAfi/View/Helper/PremierChapitre/Abstract.php @@ -0,0 +1,31 @@ +<?php +/** + * Copyright (c) 2012, Agence Française Informatique (AFI). All rights reserved. + * + * BOKEH is free software; you can redistribute it and/or modify + * it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE as published by + * the Free Software Foundation. + * + * There are special exceptions to the terms and conditions of the AGPL as it + * is applied to this software (see README file). + * + * BOKEH is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE + * along with BOKEH; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +abstract class ZendAfi_View_Helper_PremierChapitre_Abstract extends ZendAfi_View_Helper_BaseHelper { + public function loadScript(){} + protected function _tag($name, $content=null, $attribs=[]) { + $html = '<' . $name . $this->_htmlAttribs($attribs); + if (null === $content) + return $html . $this->getClosingBracket(); + return $html . '>' . $content . '</' . $name . '>'; + } + +} +?> \ No newline at end of file diff --git a/library/ZendAfi/View/Helper/PremierChapitre/Frame.php b/library/ZendAfi/View/Helper/PremierChapitre/Frame.php new file mode 100644 index 00000000000..69326253fa1 --- /dev/null +++ b/library/ZendAfi/View/Helper/PremierChapitre/Frame.php @@ -0,0 +1,43 @@ +<?php +/** + * Copyright (c) 2015, Gaël VINOT (Ville de Montrouge). All rights reserved. + * + * BOKEH is free software; you can redistribute it and/or modify + * it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE as published by + * the Free Software Foundation. + * + * There are special exceptions to the terms and conditions of the AGPL as it + * is applied to this software (see README file). + * + * BOKEH is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE + * along with BOKEH; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +class ZendAfi_View_Helper_PremierChapitre_Frame extends ZendAfi_View_Helper_PremierChapitre_Abstract { + public function premierChapitre_Frame($data, $preferences=[]) { + $this->loadScript(); + $html = ''; + $pc_active = new Class_WebService_PremierChapitre(); + if (!$pc_active->isEnabled()) return $html; + if ($data instanceOf Class_PremierChapitre) $pc = $data; + else if ($data instanceOf Class_Notice) { + $ntc = $data; + $pc = Class_PremierChapitre::getByNotice($ntc); + if (!$pc) return $html; + } + else return $html; + $frm_attribs = ['id' => 'pc_frame', + 'class' => 'iframe_premierchapitre', + 'style' => 'width:700px; height:700px; border:none;', + 'src' => $pc->getUrl()]; + return $this->_tag('iframe',null,$frm_attribs); + } +} +?> \ No newline at end of file diff --git a/library/ZendAfi/View/Helper/PremierChapitre/Lien.php b/library/ZendAfi/View/Helper/PremierChapitre/Lien.php new file mode 100644 index 00000000000..804fd80f4d0 --- /dev/null +++ b/library/ZendAfi/View/Helper/PremierChapitre/Lien.php @@ -0,0 +1,53 @@ +<?php +/** + * Copyright (c) 2015, Gaël VINOT (Ville de Montrouge). All rights reserved. + * + * BOKEH is free software; you can redistribute it and/or modify + * it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE as published by + * the Free Software Foundation. + * + * There are special exceptions to the terms and conditions of the AGPL as it + * is applied to this software (see README file). + * + * BOKEH is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE + * along with BOKEH; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +class ZendAfi_View_Helper_PremierChapitre_Lien extends ZendAfi_View_Helper_PremierChapitre_Abstract { + private $_img = '/public/opac/images/lireavecpremierchapitre.png'; + + public function premierChapitre_Lien($data, $preferences=[]) { + $this->loadScript(); + $html = ''; + $pc_active = new Class_WebService_PremierChapitre(); + if (!$pc_active->isEnabled()) return $html; + if ($data instanceOf Class_PremierChapitre) $pc = $data; + else if ($data instanceOf Class_Notice) { + $ntc = $data; + $pc = Class_PremierChapitre::getByNotice($ntc); + if (!$pc) return $html; + } + else return $html; + $lnk_attribs = [ 'id' => 'pc_lienNotice', + 'href' => $pc->getUrl()]; + $contenu = $this->_tag('img',null,['src' => $this->_img, + 'style' => 'height: 30px; margin-left: 30px;']); + if ($preferences['linkNotice']) { + $lnk_attribs['href'] = '/premier-chapitre/notice/ean/'.$pc->getId(); + $contenu = 'Voir le document'; + } + else if ($preferences['targetLink'] == 'ancre' ) { + $lnk_attribs['href'] = '#pc_ancre'; + } + + return $this->_tag('a',$contenu,$lnk_attribs); + } +} +?> \ No newline at end of file diff --git a/library/ZendAfi/View/Helper/PremierChapitre/Vignettes.php b/library/ZendAfi/View/Helper/PremierChapitre/Vignettes.php new file mode 100644 index 00000000000..8afecf9cae8 --- /dev/null +++ b/library/ZendAfi/View/Helper/PremierChapitre/Vignettes.php @@ -0,0 +1,72 @@ +<?php +/** + * Copyright (c) 2015, Gaël VINOT (Ville de Montrouge). All rights reserved. + * + * BOKEH is free software; you can redistribute it and/or modify + * it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE as published by + * the Free Software Foundation. + * + * There are special exceptions to the terms and conditions of the AGPL as it + * is applied to this software (see README file). + * + * BOKEH is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE + * along with BOKEH; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +class ZendAfi_View_Helper_PremierChapitre_Vignettes extends ZendAfi_View_Helper_PremierChapitre_Abstract { + private $_imgCss = 'margin: 5px;box-sizing: border-box;box-shadow: 0 1px 2px rgba(0,0,0,.075);padding: 4px;max-height: 122px; max-width: 200px;'; + + public function premierChapitre_Vignettes($data, $preferences=[]) { + $this->loadScript(); + $html = ''; + $pc_active = new Class_WebService_PremierChapitre(); + if (!$pc_active->isEnabled()) return $html; + $bloc_vignettes = ''; + foreach($data as $pc) { + if ($preferences['target'] == 'frame') + $bloc_vignettes .= $this->_vignetteFrame($pc,$preferences); + else + $bloc_vignettes .= $this->_vignetteNotice($pc,$preferences); + } + $html .= $this->_tag('div',$bloc_vignettes); + if ($preferences['linkNotice']) { + $html .= $this->_tag('div','<p></p><p>'.$this->view->premierChapitre_Lien($data[0],$preferences).'</p>'); + } + if ($preferences['target'] == 'frame') { + $html .= $this->view->premierChapitre_Frame($data[0],$preferences); + } + return $this->_tag('div', $html); + } + + protected function _vignetteFrame($pc, $preferences){ + $onclick = "\$('#pc_frame')[0].src='".$pc->getUrl()."';"; + if($preferences['linkNotice']) + $onclick .= "\$('#pc_lienNotice')[0].href='/premier-chapitre/notice/ean/".$pc->getId()."';"; + + $lnk_attribs = ['onclick' => $onclick,'style'=>'cursor:pointer;']; + return $this->_tag('a',$this->_vignetteImg($pc),$lnk_attribs); + } + + protected function _vignetteNotice($pc, $preferences){ + $lnk_attribs = ['href' => '/premier-chapitre/notice/ean/"'.$pc->getId()]; + + return $this->_tag('a',$this->_vignetteImg($pc),$lnk_attribs); + } + + protected function _vignetteImg($pc){ + $img_attribs = ['src' => $pc->getMiniature(), + 'alt' => $pc->getTitre().' - '.$pc->getAuteur(), + 'title' => $pc->getTitre().' - '.$pc->getAuteur(), + 'style' => $this->_imgCss, + 'data-toggwle'=>"tooltip"]; + return $this->_tag('img',null,$img_attribs); + } +} +?> \ No newline at end of file diff --git a/public/opac/images/lireavecpremierchapitre.png b/public/opac/images/lireavecpremierchapitre.png new file mode 100644 index 0000000000000000000000000000000000000000..6f476daa254509ae2d77129995f1f0fa59d038f6 GIT binary patch literal 9853 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 literal 0 HcmV?d00001 -- GitLab