diff --git a/.gitmodules b/.gitmodules index 9e8a01cc2c311346b15f932fb1eec25124357b8f..6316c6af50d3a04fc9b8bc28731d031ee3dafaf4 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,6 @@ [submodule "library/storm"] path = library/storm url = http://git.afi-sa.fr/afi/storm.git +[submodule "library/Redmine"] + path = library/Redmine + url = http://git.afi-sa.fr/afi/php-redmine-api.git diff --git a/VERSIONS_WIP/33376 b/VERSIONS_WIP/33376 new file mode 100644 index 0000000000000000000000000000000000000000..98403ca586c572fa62a13dcd4f8bd68f36c37d22 --- /dev/null +++ b/VERSIONS_WIP/33376 @@ -0,0 +1 @@ + - ticket #33376 : Créer un SSO entre Bokeh et afi-forge pour créer des tickets support suivre ses tickets (1/3) \ No newline at end of file diff --git a/application/modules/admin/controllers/BibController.php b/application/modules/admin/controllers/BibController.php index daee143b5bcc150e419bfcf7ece944463c2e1532..778114f0dd6ec55ede01599eda6641101ea28e08 100644 --- a/application/modules/admin/controllers/BibController.php +++ b/application/modules/admin/controllers/BibController.php @@ -195,6 +195,9 @@ class Admin_BibController extends Zend_Controller_Action { $horaire = trim(urlencode($this->_request->getPost('horaire'))); $photo = trim($filter->filter($this->_request->getPost('photo'))); $gln = trim($filter->filter($this->_request->getPost('gln'))); + $redmine_login = trim($filter->filter($this->_request->getPost('redmine_login'))); + $redmine_password = trim($filter->filter($this->_request->getPost('redmine_password'))); + $redmine_api_key = trim($filter->filter($this->_request->getPost('redmine_api_key'))); if ($id !== false ) { $data = ['ID_SITE' => $id, @@ -218,7 +221,10 @@ class Admin_BibController extends Zend_Controller_Action { 'PHOTO' => $photo, 'HORAIRE' => $horaire, 'INTERDIRE_RESA' => $this->_request->getPost('interdire_resa'), - 'GLN' => $gln]; + 'GLN' => $gln, + 'redmine_login' => $redmine_login, + 'redmine_password' => $redmine_password, + 'redmine_api_key' => $redmine_api_key]; $bib->updateAttributes($data)->save(); diff --git a/application/modules/admin/controllers/RedmineController.php b/application/modules/admin/controllers/RedmineController.php new file mode 100644 index 0000000000000000000000000000000000000000..9878356a63c0b8c7840869f2a0919d38271548b8 --- /dev/null +++ b/application/modules/admin/controllers/RedmineController.php @@ -0,0 +1,60 @@ +<?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 Admin_RedmineController extends ZendAfi_Controller_Action { + + public function testAction() { + $renderSuccess = function($data) { + return $this->view->tag('p', $this->extractConnectedUser($data), ['data-success' => 'true']); + }; + + return $this->withResponseDoInPopup(Class_WebService_Redmine::getCurrentUser(Class_Bib::find($this->_getParam('id_bib', 0))), + $renderSuccess, + $this->_('Echec de connexion'), + $this->_('Test de l\'API Redmine')); + } + + + protected function extractConnectedUser($data) { + return $this->_('Vous êtes connecté(e) en temps que %s %s', + $data['user']['firstname'], + $data['user']['lastname']); + } + + + protected function withResponseDoInPopup($data, $call_back, $message, $title) { + return $this->_helper->json(['title' => $title, + 'content' => $this->withResponseDo($data, $call_back, $message)]); + } + + + protected function withResponseDo($data, $call_back, $message) { + if(false === $data) + return $message; + + if(isset($data['error_message']) && '' != $data['error_message']) + return $data['error_message']; + + return call_user_func($call_back, $data); + } +} +?> \ No newline at end of file diff --git a/application/modules/admin/views/scripts/bib/_form.phtml b/application/modules/admin/views/scripts/bib/_form.phtml index fd89a4ca62b210ac6cfe43b7cdf1285370d8c440..15682b20fdbd69d5297494764148140590c88d95 100644 --- a/application/modules/admin/views/scripts/bib/_form.phtml +++ b/application/modules/admin/views/scripts/bib/_form.phtml @@ -6,145 +6,146 @@ $combo_statut_bib = $bibClass->getComboStatutBib($this->bib->VISIBILITE); ?> <center><div class="form" style="text-align:center;"> - <div class="formTable"> - <form name="form" action="<?php echo BASE_URL ?>/admin/bib/<?php echo $this->action; ?>/id/<?php echo $this->id; ?>" method="post"> + <div class="formTable"> + <form name="form" action="<?php echo BASE_URL ?>/admin/bib/<?php echo $this->action; ?>/id/<?php echo $this->id; ?>" method="post"> - <fieldset> - <span id="abonne_erreur" class="abonne"><?php echo $this->message; ?></span> - <legend><?php echo $this->traduire('Bibliothèque'); ?></legend> - <br /> - <table> - <tr> - <td class="droite" style="width:120px;"><?php echo $this->traduire('Nom'); ?> *</td> - <td class="gauche"><input type="text" id="libelle" name="libelle" value="<?php echo $this->escape(trim($this->bib->LIBELLE)); ?>" style="width:100%;" maxlength="70"/></td> - </tr> - <tr> - <td class="droite"><?php echo $this->traduire('Nom du responsable'); ?></td> - <td class="gauche"><input type="text" id="responsable" name="responsable" value="<?php echo $this->escape(trim($this->bib->RESPONSABLE)); ?>" style="width:100%;" maxlength="50"/></td> - </tr> - </table> - </fieldset><br /> + <fieldset> + <span id="abonne_erreur" class="abonne"><?php echo $this->message; ?></span> + <legend><?php echo $this->traduire('Bibliothèque'); ?></legend> + <br /> + <table> + <tr> + <td class="droite" style="width:120px;"><?php echo $this->traduire('Nom'); ?> *</td> + <td class="gauche"><input type="text" id="libelle" name="libelle" value="<?php echo $this->escape(trim($this->bib->LIBELLE)); ?>" style="width:100%;" maxlength="70"/></td> + </tr> + <tr> + <td class="droite"><?php echo $this->traduire('Nom du responsable'); ?></td> + <td class="gauche"><input type="text" id="responsable" name="responsable" value="<?php echo $this->escape(trim($this->bib->RESPONSABLE)); ?>" style="width:100%;" maxlength="50"/></td> + </tr> + </table> + </fieldset><br /> - <fieldset> - <legend><?php echo $this->traduire('Photo'); ?></legend> - <table> - <tr> - <td><iframe style="height:180px;width:650px;overflow:hidden" frameborder="0" src="<?php print(BASE_URL) ?>/admin/bib/photo?id=<?php echo $this->id; ?>"></iframe></td> - </tr> + <fieldset> + <legend><?php echo $this->traduire('Photo'); ?></legend> + <table> + <tr> + <td><iframe style="height:180px;width:650px;overflow:hidden" frameborder="0" src="<?php print(BASE_URL) ?>/admin/bib/photo?id=<?php echo $this->id; ?>"></iframe></td> + </tr> - </table> - </fieldset><br /> + </table> + </fieldset><br /> - <fieldset> - <legend><?php echo $this->traduire('Adresse'); ?></legend> - <br /> - <table> - <tr> - <td class="droite" style="width:120px;"><?php echo $this->traduire('Adresse'); ?></td> - <td class="gauche"><textarea id="adresse" name="adresse" cols="35"><?php echo $this->escape(trim($this->bib->ADRESSE)); ?></textarea></td> - </tr> - <tr> - <td class="droite"><?php echo $this->traduire('Code postal'); ?></td> - <td class="gauche"><input type="text" id="cp" name="cp" value="<?php echo $this->escape(trim($this->bib->CP)); ?>" style="width:100%;"/></td> - </tr> - <tr> - <td class="droite"><?php echo $this->traduire('Ville'); ?> *</td> - <td class="gauche"><input type="text" id="ville" name="ville" value="<?php echo $this->escape(trim($this->bib->VILLE)); ?>" style="width:100%;"/></td> - </tr> - <tr> - <td class="droite"><?php echo $this->traduire('Téléphone'); ?></td> - <td class="gauche"><input type="text" id="tel" name="tel" value="<?php echo $this->escape(trim($this->bib->TELEPHONE)); ?>" style="width:100%;"/></td> - </tr> - <tr> - <td class="droite"><?php echo $this->traduire('Mail'); ?></td> - <td class="gauche"><input type="text" id="mail" name="mail" value="<?php echo $this->escape(trim($this->bib->MAIL)); ?>" style="width:100%;"/></td> - </tr> - <tr> - <td class="droite"><?php echo $this->traduire('Mail suggestions'); ?></td> - <td class="gauche"><input type="text" id="mail_suggestions" name="mail_suggestions" value="<?php echo $this->escape(trim($this->bib->MAIL_SUGGESTIONS)); ?>" style="width:100%;"/></td> - </tr> - </table> - </fieldset><br /> + <fieldset> + <legend><?php echo $this->traduire('Adresse'); ?></legend> + <br /> + <table> + <tr> + <td class="droite" style="width:120px;"><?php echo $this->traduire('Adresse'); ?></td> + <td class="gauche"><textarea id="adresse" name="adresse" cols="35"><?php echo $this->escape(trim($this->bib->ADRESSE)); ?></textarea></td> + </tr> + <tr> + <td class="droite"><?php echo $this->traduire('Code postal'); ?></td> + <td class="gauche"><input type="text" id="cp" name="cp" value="<?php echo $this->escape(trim($this->bib->CP)); ?>" style="width:100%;"/></td> + </tr> + <tr> + <td class="droite"><?php echo $this->traduire('Ville'); ?> *</td> + <td class="gauche"><input type="text" id="ville" name="ville" value="<?php echo $this->escape(trim($this->bib->VILLE)); ?>" style="width:100%;"/></td> + </tr> + <tr> + <td class="droite"><?php echo $this->traduire('Téléphone'); ?></td> + <td class="gauche"><input type="text" id="tel" name="tel" value="<?php echo $this->escape(trim($this->bib->TELEPHONE)); ?>" style="width:100%;"/></td> + </tr> + <tr> + <td class="droite"><?php echo $this->traduire('Mail'); ?></td> + <td class="gauche"><input type="text" id="mail" name="mail" value="<?php echo $this->escape(trim($this->bib->MAIL)); ?>" style="width:100%;"/></td> + </tr> + <tr> + <td class="droite"><?php echo $this->traduire('Mail suggestions'); ?></td> + <td class="gauche"><input type="text" id="mail_suggestions" name="mail_suggestions" value="<?php echo $this->escape(trim($this->bib->MAIL_SUGGESTIONS)); ?>" style="width:100%;"/></td> + </tr> + </table> + </fieldset><br /> - <fieldset> - <legend><?php echo $this->traduire('Territoire'); ?></legend> - <br /> - <table> - <tr> - <td class="droite" style="width:120px;"><?php echo $this->traduire('Territoire'); ?></td> - <td class="gauche"><?php echo $this->combo_zone; ?></td> - </tr> - <tr> - <td class="droite"><?php echo $this->traduire('Lien cartographique'); ?></td> - <td class="gauche"><input type="text" id="carto" name="carto" value="<?php echo $this->escape(trim($this->bib->LIEN_CARTO)); ?>" style="width:530px;"/></td> - </tr> - </table> - </fieldset><br /> + <fieldset> + <legend><?php echo $this->traduire('Territoire'); ?></legend> + <br /> + <table> + <tr> + <td class="droite" style="width:120px;"><?php echo $this->traduire('Territoire'); ?></td> + <td class="gauche"><?php echo $this->combo_zone; ?></td> + </tr> + <tr> + <td class="droite"><?php echo $this->traduire('Lien cartographique'); ?></td> + <td class="gauche"><input type="text" id="carto" name="carto" value="<?php echo $this->escape(trim($this->bib->LIEN_CARTO)); ?>" style="width:530px;"/></td> + </tr> + </table> + </fieldset><br /> - <fieldset> - <legend><?php echo $this->traduire('Information'); ?></legend> - <br /> - <table> - <tr> - <td class="droite" style="width:120px;"><?php echo $this->traduire('Inscription'); ?></td> - <td class="gauche"><textarea id="inscription" name="inscription" cols="35" ><?php echo $this->escape(trim(urldecode($this->bib->INSCRIPTION))); ?> </textarea></td> - </tr> - <tr> - <td class="droite"><?php echo $this->traduire('Prêt'); ?></td> - <td class="gauche"><textarea id="pret" name="pret" cols="35"><?php echo $this->escape(trim(urldecode($this->bib->PRET))); ?> </textarea></td> - </tr> - <tr> - <td class="droite"><?php echo $this->traduire('Fonds'); ?></td> - <td class="gauche"><textarea type="text" id="fond" name="fond" cols="35"><?php echo $this->escape(trim(urldecode($this->bib->FOND))); ?> </textarea></td> - </tr> - <tr> - <td class="droite"><?php echo $this->traduire('Comment se procurer ce document '); ?></td> - <td class="gauche"><textarea type="text" id="procur" name="procur" cols="35" ><?php echo $this->escape(trim(urldecode($this->bib->PROCURE))); ?> </textarea></td> - </tr> - <tr> - <td class="droite"><?php echo $this->traduire('Annexe'); ?></td> - <td class="gauche"><textarea type="text" id="annexe" name="annexe" cols="35"><?php echo $this->escape(trim(urldecode($this->bib->ANNEXE))); ?> </textarea></td> - </tr> - <tr> - <td class="droite" style="width:120px;"><?php echo $this->traduire('Horaire'); ?></td> - <td class="gauche"><textarea id="horaire" name="horaire" cols="35" ><?php echo $this->escape(trim(urldecode($this->bib->HORAIRE))); ?> </textarea></td> - </tr> - </table> - <input type="hidden" id="photo" name="photo" value="<?php echo $this->escape(trim($this->bib->PHOTO)); ?>"/> - </fieldset><br /> + <fieldset> + <legend><?php echo $this->traduire('Information'); ?></legend> + <br /> + <table> + <tr> + <td class="droite" style="width:120px;"><?php echo $this->traduire('Inscription'); ?></td> + <td class="gauche"><textarea id="inscription" name="inscription" cols="35" ><?php echo $this->escape(trim(urldecode($this->bib->INSCRIPTION))); ?> </textarea></td> + </tr> + <tr> + <td class="droite"><?php echo $this->traduire('Prêt'); ?></td> + <td class="gauche"><textarea id="pret" name="pret" cols="35"><?php echo $this->escape(trim(urldecode($this->bib->PRET))); ?> </textarea></td> + </tr> + <tr> + <td class="droite"><?php echo $this->traduire('Fonds'); ?></td> + <td class="gauche"><textarea type="text" id="fond" name="fond" cols="35"><?php echo $this->escape(trim(urldecode($this->bib->FOND))); ?> </textarea></td> + </tr> + <tr> + <td class="droite"><?php echo $this->traduire('Comment se procurer ce document '); ?></td> + <td class="gauche"><textarea type="text" id="procur" name="procur" cols="35" ><?php echo $this->escape(trim(urldecode($this->bib->PROCURE))); ?> </textarea></td> + </tr> + <tr> + <td class="droite"><?php echo $this->traduire('Annexe'); ?></td> + <td class="gauche"><textarea type="text" id="annexe" name="annexe" cols="35"><?php echo $this->escape(trim(urldecode($this->bib->ANNEXE))); ?> </textarea></td> + </tr> + <tr> + <td class="droite" style="width:120px;"><?php echo $this->traduire('Horaire'); ?></td> + <td class="gauche"><textarea id="horaire" name="horaire" cols="35" ><?php echo $this->escape(trim(urldecode($this->bib->HORAIRE))); ?> </textarea></td> + </tr> + </table> + <input type="hidden" id="photo" name="photo" value="<?php echo $this->escape(trim($this->bib->PHOTO)); ?>"/> + </fieldset><br /> - <fieldset> - <legend><?php echo $this->traduire('Configuration'); ?></legend> - <br /> - <table> - <tr> - <td class="droite" style="width:160px;"><?php echo $this->traduire('Statut de la bib'); ?></td> - <td class="gauche"><?php echo $combo_statut_bib; ?></td> - </tr> - <tr> - <td class="droite"><?php echo $this->traduire('Adresse URL'); ?></td> - <td class="gauche"><input type="text" id="url" name="url" value="<?php echo $this->escape(trim($this->bib->URL_WEB)); ?>" style="width:100%;"/></td> - </tr> - <tr> - <td class="droite"><?php echo $this->traduire('Interdire les réservations'); ?></td> - <td class="gauche"> - <?php echo $this->formSelect("interdire_resa", $this->bib->INTERDIRE_RESA, "", array("0" => "non", "1" => "oui")) ?> - </td> - </tr> - <?php if (Class_AdminVar::isDilicomPNBEnabled()) { ?> + <fieldset> + <legend><?php echo $this->traduire('Configuration'); ?></legend> + <br /> + <table> + <tr> + <td class="droite" style="width:160px;"><?php echo $this->traduire('Statut de la bib'); ?></td> + <td class="gauche"><?php echo $combo_statut_bib; ?></td> + </tr> + <tr> + <td class="droite"><?php echo $this->traduire('Adresse URL'); ?></td> + <td class="gauche"><input type="text" id="url" name="url" value="<?php echo $this->escape(trim($this->bib->URL_WEB)); ?>" style="width:100%;"/></td> + </tr> + <tr> + <td class="droite"><?php echo $this->traduire('Interdire les réservations'); ?></td> + <td class="gauche"> + <?php echo $this->formSelect("interdire_resa", $this->bib->INTERDIRE_RESA, "", array("0" => "non", "1" => "oui")) ?> + </td> + </tr> + <?php if (Class_AdminVar::isDilicomPNBEnabled()) { ?> <tr> <td class="droite"><?php echo $this->_('GLN (PNB Dilicom)'); ?></td> <td class="gauche"><input type="text" id="gln" name="gln" value="<?php echo $this->escape(trim($this->bib->GLN)); ?>" style="width:100%;"/></td> </tr> - <?php } ?> - </table> - </fieldset><br /> - <table> - <tr> - <td align="right" style="padding-right:5px;"> <?php echo $this->bouton('type=V'); ?></td> - <td align="left" style="padding-left:5px;"> <?php echo $this->bouton('id=c_29', 'picto=del.gif', 'texte=Annuler', 'url=' . BASE_URL . '/admin/bib', 'largeur=120px'); ?></td> - </tr> + <?php } ?> </table> - <input type="hidden" name="id_bib" id="id_bib" value="<?php echo $this->bib->ID_SITE; ?>" /> - </form></div></div><br> + </fieldset><br /> + <?php echo $this->Redmine_Account($this->bib); ?> + <table> + <tr> + <td align="right" style="padding-right:5px;"> <?php echo $this->bouton('type=V'); ?></td> + <td align="left" style="padding-left:5px;"> <?php echo $this->bouton('id=c_29', 'picto=del.gif', 'texte=Annuler', 'url=' . BASE_URL . '/admin/bib', 'largeur=120px'); ?></td> + </tr> + </table> + <input type="hidden" name="id_bib" id="id_bib" value="<?php echo $this->bib->ID_SITE; ?>" /> + </form></div></div><br> </center> diff --git a/cosmogramme/sql/patch/patch_283.php b/cosmogramme/sql/patch/patch_283.php new file mode 100644 index 0000000000000000000000000000000000000000..85f33d09f12460d04935a05e76bf1bc4999dbbcd --- /dev/null +++ b/cosmogramme/sql/patch/patch_283.php @@ -0,0 +1,7 @@ +<?php +$adapter = Zend_Db_Table::getDefaultAdapter(); + +try { + $adapter->query('ALTER TABLE bib_c_site ADD COLUMN redmine_login varchar(250) null, ADD COLUMN redmine_password varchar(250) null, ADD COLUMN redmine_api_key varchar(250) null'); +} catch(Exception $e) {} +?> \ No newline at end of file diff --git a/library/Class/AdminVar.php b/library/Class/AdminVar.php index df929ab1c55bae5602eb9a7ac0014fa145739de9..d92e3660073d91c6dd11c076e6460dd05687f03c 100644 --- a/library/Class/AdminVar.php +++ b/library/Class/AdminVar.php @@ -286,10 +286,10 @@ class Class_AdminVarLoader extends Storm_Model_Loader { 'PREMIERCHAPITRE_BIB_ID' => Class_Adminvar_Meta::newDefault($this->_('Clé d\'identification Premier-Chapitre de la bibliothèque. Cette clé doit être fournie par Premier-Chapitre.'))->bePrivate(), 'PREMIERCHAPITRE_BMID' => Class_Adminvar_Meta::newDefault($this->_('Login du portail fourni par Premier-Chapitre.'))->bePrivate(), 'PREMIERCHAPITRE_BMKEY' => Class_Adminvar_Meta::newDefault($this->_('Password du portail fourni par Premier-Chapitre'))->bePrivate(), - ], + 'REDMINE_SERVER_URL' => Class_Adminvar_Meta::newDefault($this->_('Url du serveur redmine'))->bePrivate() ], 'users' => ['NDAYS_EXPIRY_NOTICE' => Class_AdminVar_Meta::newDefault($this->_('Prévenir l\'utilisateur xx jour(s) avant l\'expiration de son abonnement (par défaut 30 jours).'), ['value' => 30]), 'DISABLE_SUGGESTIONS' => Class_AdminVar_Meta::newOnOff($this->_('Désactivation des suggestions d\'achats'))->bePrivate(), - ] + ] ]; } @@ -438,6 +438,14 @@ class Class_AdminVarLoader extends Storm_Model_Loader { } + /** + * @return bool + */ + public function isRedmineEnabled() { + return ('' != Class_AdminVar::get('REDMINE_SERVER_URL')); + } + + /** * @return bool */ diff --git a/library/Class/WebService/Redmine.php b/library/Class/WebService/Redmine.php new file mode 100644 index 0000000000000000000000000000000000000000..43698210e0c71bad65296be66a11cd631c6b172d --- /dev/null +++ b/library/Class/WebService/Redmine.php @@ -0,0 +1,75 @@ +<?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_WebService_Redmine extends Class_WebService_Abstract { + use Trait_Translator; + + protected static $_redmine_client; + + protected function missingApiRequirement($library) { + if($error_message = $this->missingApiRequirementMessage($library)) + return ['error_message' => $error_message]; + return null; + } + + + protected function missingApiRequirementMessage($library) { + if(!$library) + return $this->_('Vous devez fournir un identifiant de bibliothèque en paramètre'); + + if(!Class_AdminVar::isRedmineEnabled()) + return $this->_('Aucun serveur Redmine est renseigné'); + + if((!$library->getRedmineLogin() || !$library->getRedminePassword()) && !$library->getRedmineApiKey()) + return $this->_('Vous devez renseigner les champs login et password ou le chammp clé d\'API'); + + return null; + } + + + protected function getUserApi($library) { + return $this->getClient($library)->api('user'); + } + + + public function getClient($library) { + if(static::$_redmine_client == null || !isset(static::$_redmine_client)) + static::$_redmine_client = new Redmine\Client(Class_AdminVar::get('REDMINE_SERVER_URL'), $library->getRedmineLogin(), $library->getRedminePassword()); + + return static::$_redmine_client; + } + + + public static function setClient($redmine_client) { + static::$_redmine_client = $redmine_client; + } + + + public static function getCurrentUser($library) { + $instance = new self(); + + if($requirement = $instance->missingApiRequirement($library)) + return $requirement; + + return $instance->getUserApi($library)->getCurrentUser(); + } +} +?> \ No newline at end of file diff --git a/library/Redmine b/library/Redmine new file mode 160000 index 0000000000000000000000000000000000000000..c6b073270e243f72aac0d164654cd39f25b5a7bf --- /dev/null +++ b/library/Redmine @@ -0,0 +1 @@ +Subproject commit c6b073270e243f72aac0d164654cd39f25b5a7bf diff --git a/library/ZendAfi/View/Helper/Redmine/Account.php b/library/ZendAfi/View/Helper/Redmine/Account.php new file mode 100644 index 0000000000000000000000000000000000000000..7b2f522c63943e7911ce32ce748e4b94031c2beb --- /dev/null +++ b/library/ZendAfi/View/Helper/Redmine/Account.php @@ -0,0 +1,83 @@ +<?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 ZendAfi_View_Helper_Redmine_Account extends ZendAfi_View_Helper_BaseHelper { + + public function Redmine_Account($library) { + if (!Class_AdminVar::isRedmineEnabled()) + return ''; + + $html = + '<fieldset>' . + '<legend> ' . $this->_('Configuration du compte Redmine') . '</legend><br />' . + '<table>' . + '<tr>'. + '<td class="droite">' . $this->_('Login') . '</td>' . + '<td class="gauche"><input type="text" id="redmine_login" name="redmine_login" value="' . $library->getRedmineLogin() . '" style="width:100%;"/></td>' . + '</tr>' . + '<tr>' . + '<td class="droite">' . $this->_('Password') . '</td>' . + '<td class="gauche"><input type="password" id="redmine_password" name="redmine_password" value="' . $library->getRedminePassword() . '" style="width:100%;"/></td>' . + '</tr>' . + '<tr>'. + '<td class="droite"></td>' . + '<td class="gauche" style="width: 100%">' . $this->_('ou') . '</td>' . + '</tr>'. + '<tr>' . + '<td class="droite">' . $this->_('Clé d\'API') . '</td>' . + '<td class="gauche"><input type="text" id="redmine_api_key" name="redmine_api_key" value="' . $library->getRedmineApiKey() . '" style="width:100%;"/></td>' . + '</tr>' . + '<tr>' . + '<td class="droite">' . $this->_('Tester les paramètres') . '</td>' . + '<td class="gauche">' . $this->Redmine_AccountStatus($library) . '</td>' . + '</tr>' . + '</table>' . + '</fieldset><br />'; + + return $html; + } + + + public function Redmine_AccountStatus($library) { + if (!Class_AdminVar::isRedmineEnabled()) + return ''; + + Class_ScriptLoader::getInstance() + ->addOpacScript('redmine') + ->addJQueryReady('$("a.redmine_status").autoRefresh();'); + + $url = $this->view->url(['module' => 'admin', + 'controller' => 'redmine', + 'action' => 'test', + 'id_bib' => $library->getId()], null, true); + + $anchor = $this->view->tagAnchor($url, + '', + ['data-popup' => 'true', + 'data-status' => $url, + 'title' => $this->_('Tester la connexion vers l\'API de redmine'), + 'class' => 'redmine_status']); + + return $anchor; + } +} +?> \ No newline at end of file diff --git a/library/startup.php b/library/startup.php index 1763d0903e2dea8d1e46e2090957f1c972eab22a..1a678be59dbecba7f425ae1abf94d7ffdbba57c0 100644 --- a/library/startup.php +++ b/library/startup.php @@ -51,6 +51,7 @@ function setupOpac() { $front_controller = setupFrontController($cfg); setupPagination(); setupRestful(); + setupRedmine(); return $front_controller; } @@ -359,3 +360,11 @@ function setupRestful() { 'rest', Zend_Controller_Action_HelperBroker::getExistingHelper('ViewRenderer')); } + + +function setupRedmine() { + if(!Class_AdminVar::isRedmineEnabled()) + return; + + require_once __DIR__ . '/Redmine/lib/autoload.php'; +} \ No newline at end of file diff --git a/public/admin/css/global.css b/public/admin/css/global.css index 6c8c328d7f28e989466cb1d40ba053734cb816ea..fd8e7927ea0f1383c24bbc2a3fcd00e79a13054b 100644 --- a/public/admin/css/global.css +++ b/public/admin/css/global.css @@ -1318,3 +1318,16 @@ div#reader { font-weight: bold; color:#808080; } + + +.redmine_status { + display: inline-block; + border-radius: 8px; + width: 16px; + height: 16px; + background-color: red; +} + +.redmine_status.redmine_green { + background-color: green; +} \ No newline at end of file diff --git a/public/opac/js/redmine.js b/public/opac/js/redmine.js new file mode 100644 index 0000000000000000000000000000000000000000..3e12ed93849cc24053d8761926cbb860a8b5e259 --- /dev/null +++ b/public/opac/js/redmine.js @@ -0,0 +1,50 @@ +/** + * 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 + */ + +(function ( $ ) { + $.fn.autoRefresh = function () { + var anchor = $(this); + + var update = function(anchor) { + $.getJSON(anchor.attr('data-status')).done( function(data) { + anchor.addClass('redmine_green'); + if( -1 == data.content.indexOf('data-success')) + anchor.removeClass('redmine_green'); + }); + }; + + update(anchor); + + // var form = $('[action*="admin/bib/edit"]'); + // form.find('#redmine_login, #redmine_password').change(function() { + // form.submit(function (e) { + // var url = form.attr('action'); + // $.ajax({ + // type: 'POST', + // url: url, + // data: form.serialize() + // }).done(function(msg) { + // update(anchor); + // }); + // }); + // }); + + }; +} (jQuery)); diff --git a/tests/application/modules/admin/controllers/AdminIndexControllerTest.php b/tests/application/modules/admin/controllers/AdminIndexControllerTest.php index 916e14fe861640530f16cbc7af2e9cf6ccb57035..69489966362a8af71264a16bda562a89788a800a 100644 --- a/tests/application/modules/admin/controllers/AdminIndexControllerTest.php +++ b/tests/application/modules/admin/controllers/AdminIndexControllerTest.php @@ -187,6 +187,12 @@ class AdminIndexControllerAdminVarActionTest extends Admin_AbstractControllerTes } + /** @test */ + public function redmineServerUrlShouldBePresent() { + $this->assertXpathContentContains('//td', 'REDMINE_SERVER_URL'); + } + + /** @test */ public function pageShouldBeHTML5Valid() { $this->assertHTML5(); diff --git a/tests/application/modules/admin/controllers/BibControllerTest.php b/tests/application/modules/admin/controllers/BibControllerTest.php index bfe4484309f182f1b3af6ade2c265f8ac82ec8c4..5136632b1cf94164d4382608685a1fb1124254df 100644 --- a/tests/application/modules/admin/controllers/BibControllerTest.php +++ b/tests/application/modules/admin/controllers/BibControllerTest.php @@ -38,6 +38,9 @@ abstract class BibControllerTestCase extends AbstractControllerTestCase { 'mail' => 'jp@annecy.com', 'telephone' => '04 50 51 32 12', 'gln' => '45321', + 'redmine_login' => '', + 'redmine_password' => '', + 'redmine_api_key' => '', 'article_categories' => []]); @@ -237,6 +240,12 @@ class BibControllerWithAdminBibEditAnnecyTest extends BibControllerWithAdminBibT public function inputGlnShouldNotBeVisible() { $this->assertNotXPath('//input[@name="gln"]'); } + + + /** @test */ + public function withNoRedmineAPISetRedmineAccountNameShouldNotBePresent() { + $this->assertNotXPathContentContains('//table//tr//td', 'nom d\'utilsateur'); + } } @@ -1159,4 +1168,100 @@ class BibControllerPermissionsPortalPostActionTest $this->group, Class_Bib::getPortail())); } +} + + + +abstract class BibControllerWithRedmineAPITestCase extends BibControllerWithAdminBibTestCase { + public function setUp() { + parent::setUp(); + $this->fixture('Class_AdminVar', + ['id' => 'REDMINE_API_KEY', 'valeur' => '123456789']); + + $this->fixture('Class_AdminVar', + ['id' => 'REDMINE_SERVER_URL', 'valeur' => 'http://redmine-forge.gnu']); + } +} + + + +class BibControllerWithRemineAPITest extends BibControllerWithRedmineAPITestCase { + public function setUp() { + parent::setUp(); + $this->dispatch('admin/bib/edit/id/2', true); + } + + + /** @test */ + public function redmineAccountNameShouldBeEmpty() { + $this->assertXPath('//table//tr//td//input[@name="redmine_login"][@value=""]', $this->_response->getBody()); + } + + + /** @test */ + public function redminePasswordShouldBeEmpty() { + $this->assertXPath('//table//tr//td//input[@name="redmine_password"][@value=""][@type="password"]', $this->_response->getBody()); + } + + + /** @test */ + public function redmineApiKeyShouldBeEmpty() { + $this->assertXPath('//table//tr//td//input[@name="redmine_api_key"][@value=""][@type="text"]', $this->_response->getBody()); + } + + + /** @test */ + public function anchorTestApiSettingsShouldBePresent() { + $this->assertXPath('//a[contains(@href, "/admin/redmine/test/id_bib/2")][@data-popup="true"][@data-status="/admin/redmine/test/id_bib/2"]', $this->_response->getBody()); + } + + + /** @test */ + public function redmineJSShouldBeLoaded() { + $this->assertXPath('//script[contains(@src, "/opac/js/redmine.js")]'); + } + + + /** @test */ + public function redmineAutoRefreshShouldLoaded() { + $this->assertXPathContentContains('//script', '$("a.redmine_status").autoRefresh();'); + } +} + + + +class BibControllerWithRemineAPIPostDispatchTest extends BibControllerWithRedmineAPITestCase { + public function setUp() { + parent::setUp(); + + $this->fixture('Class_Users', + ['id' => 1, + 'login' => 'admin', + 'password' => 'admin', + 'bib' => Class_Bib::find(2), + 'role_level' => ZendAfi_Acl_AdminControllerRoles::ADMIN_BIB]); + + $this->postDispatch('admin/bib/edit/id/2', ['id_bib' => 2, + 'redmine_login' => 'lib@annecy', + 'redmine_password' => 'pwd', + 'redmine_api_key' => '123456789']); + } + + + /** @test */ + public function libAnnecyRedmineLoginShouldBeLibAtAnnecy() { + $this->assertEquals('lib@annecy', Class_Bib::find(2)->getRedmineLogin()); + } + + + /** @test */ + public function libAnnecyRedminePasswordShouldBePwd() { + $this->assertEquals('pwd', Class_Bib::find(2)->getRedminePassword()); + } + + + /** @test */ + public function libAnnecyRedmineAPIKeyShouldBe123456789() { + $this->assertEquals('123456789', Class_Bib::find(2)->getRedmineApiKey()); + } } \ No newline at end of file diff --git a/tests/application/modules/admin/controllers/RedmineControllerTest.php b/tests/application/modules/admin/controllers/RedmineControllerTest.php new file mode 100644 index 0000000000000000000000000000000000000000..a5be148472b18fe96cf8309cf56fae9f7ed7d2f3 --- /dev/null +++ b/tests/application/modules/admin/controllers/RedmineControllerTest.php @@ -0,0 +1,173 @@ +<?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 + */ + +require_once 'tests/fixtures/RedmineFixtures.php'; + +class Admin_RedmineControllerTestCase extends Admin_AbstractControllerTestCase { + protected $_storm_default_to_volatile = true; +} + + + +class Admin_RedmineControllerTestActionWithNoBibTest extends Admin_RedmineControllerTestCase { + + public function setUp() { + parent::setUp(); + $this->dispatch('/admin/redmine/test', true); + } + + + /** @test */ + public function responseShouldContainsNoBibGiven() { + $this->assertEquals(RedmineFixtures::noLibraryGiven(), $this->_response->getBody()); + } +} + + + +class Admin_RedmineControllerTestActionWithBibTest extends Admin_RedmineControllerTestCase { + + + public function setUp() { + parent::setUp(); + + $this->fixture('Class_Bib', + ['id' => 1, + 'libelle' => 'Mediatheque d\'Annecy', + 'redmine_api_key' => '123456789', + 'redmine_login' => 'annecy@customer', + 'redmine_password' => 'soon']); + + $this->dispatch('/admin/redmine/test/id_bib/1', true); + } + + + /** @test */ + public function responseShouldContainsNoBibGiven() { + $this->assertEquals(RedmineFixtures::noServerUrlGiven(), $this->_response->getBody()); + } +} + + + +class Admin_RedmineControllerTestActionWithEmptyBibTest extends Admin_RedmineControllerTestCase { + + public function setUp() { + parent::setUp(); + + $this->fixture('Class_AdminVar', + ['id' => 'REDMINE_SERVER_URL', + 'valeur' => 'http://forge.afi-sa.fr']); + + $this->fixture('Class_Bib', + ['id' => 1, + 'libelle' => 'Mediatheque d\'Annecy', + 'redmine_api_key' => '', + 'redmine_login' => 'haveBeenSet', + 'redmine_password' => '']); + + $this->dispatch('/admin/redmine/test/id_bib/1', true); + } + + + /** @test */ + public function responseShouldContainsNoBibGiven() { + $this->assertEquals(RedmineFixtures::missingParams(), $this->_response->getBody()); + } +} + + + +abstract class Admin_RedmineControllerWithApiRequirementSetTestCase extends Admin_RedmineControllerTestCase { + + public function setUp() { + parent::setUp(); + + $redmine_api = Storm_Test_ObjectWrapper::mock(); + $redmine_api + ->whenCalled('getCurrentUser') + ->answers(RedmineFixtures::currentUser()); + + $redmine_client = Storm_Test_ObjectWrapper::mock(); + $redmine_client + ->whenCalled('api') + ->answers($redmine_api); + + Class_WebService_Redmine::setClient($redmine_client); + + $this->fixture('Class_AdminVar', + ['id' => 'REDMINE_SERVER_URL', + 'valeur' => 'http://forge.afi-sa.fr']); + + $this->fixture('Class_Bib', + ['id' => 1, + 'libelle' => 'Mediatheque d\'Annecy', + 'redmine_api_key' => '', + 'redmine_login' => 'haveBeenSet', + 'redmine_password' => '123456']); + } + + + public function tearDown() { + Class_WebService_Redmine::setClient(null); + parent::tearDown(); + } +} + + + +class Admin_RedmineControllerTestActionWithBibAndLoginPasswordSetTest extends Admin_RedmineControllerWithApiRequirementSetTestCase { + + public function setUp() { + parent::setUp(); + $this->dispatch('/admin/redmine/test/id_bib/1', true); + } + + + /** @test */ + public function responseShouldContainsSandreBocoeur() { + $this->assertEquals(RedmineFixtures::sandreBocoeur(), $this->_response->getBody()); + } +} + + + +class Admin_RedmineControllerTestActionWithBibAndAPIKKeyTest extends Admin_RedmineControllerWithApiRequirementSetTestCase { + + public function setUp() { + parent::setUp(); + + $this->fixture('Class_Bib', + ['id' => 1, + 'libelle' => 'Mediatheque d\'Annecy', + 'redmine_api_key' => '123456789abc', + 'redmine_login' => '', + 'redmine_password' => '']); + + $this->dispatch('/admin/redmine/test/id_bib/1', true); + } + + + /** @test */ + public function responseShouldContainsSandreBocoeur() { + $this->assertEquals(RedmineFixtures::sandreBocoeur(), $this->_response->getBody()); + } +} diff --git a/tests/db/UpgradeDBTest.php b/tests/db/UpgradeDBTest.php index b1fe317f8a1a911ba22f420e86688ec45b661b1c..c244161f4739171b820ada506576ec36f7bf3dae 100644 --- a/tests/db/UpgradeDBTest.php +++ b/tests/db/UpgradeDBTest.php @@ -307,3 +307,32 @@ class UpgradeDB_282_Test extends UpgradeDBTestCase { $this->assertColumnExists('bib_admin_users.SETTINGS'); } } + + + +class UpgradeDB_283_Test extends UpgradeDBTestCase { + + public function prepare() { + try { + $this->query('ALTER TABLE bib_c_site DROP redmine_login, DROP redmine_password, DROP redmine_api_key'); + } catch(Exception $e) {} + } + + + /** @test */ + public function bibRedmineLoginColumnShouldExist() { + $this->assertColumn('bib_c_site', 'redmine_login'); + } + + + /** @test */ + public function bibRedminePasswordColumnShouldExist() { + $this->assertColumn('bib_c_site', 'redmine_password'); + } + + + /** @test */ + public function bibRedmineApiKeyColumnShouldExist() { + $this->assertColumn('bib_c_site', 'redmine_api_key'); + } +} diff --git a/tests/fixtures/RedmineFixtures.php b/tests/fixtures/RedmineFixtures.php new file mode 100644 index 0000000000000000000000000000000000000000..67c93f9ef0c2e665ebdc84a6554268a89f4d029a --- /dev/null +++ b/tests/fixtures/RedmineFixtures.php @@ -0,0 +1,57 @@ +<?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 RedmineFixtures { + public static function noLibraryGiven() { + return '{"title":"Test de l\'API Redmine","content":"Vous devez fournir un identifiant de biblioth\u00e8que en param\u00e8tre"}'; + } + + + public static function noServerUrlGiven() { + return '{"title":"Test de l\'API Redmine","content":"Aucun serveur Redmine est renseign\u00e9"}'; + } + + + public static function missingParams() { + return '{"title":"Test de l\'API Redmine","content":"Vous devez renseigner les champs login et password ou le chammp cl\u00e9 d\'API"}'; + } + + + public static function currentUser() { + return ['user' => ['id' => '123456', + 'login' => 'haveBeenSet', + 'firstname' => 'Sandre', + 'lastname' => 'Bocoeur', + 'mail' => 'bocoeur@bokeh-users.fr', + 'created_on' => '2013-04-02T11:41:36Z', + 'last_login_on' => '2015-12-09T11:02:27Z', + 'api_key' => '123456789abcd', + 'custom_fields' => [], + 'memberships' => [] ] ]; + } + + + public static function sandreBocoeur() { + return '{"title":"Test de l\'API Redmine","content":"<p data-success=\"true\">Vous \u00eates connect\u00e9(e) en temps que Sandre Bocoeur<\/p>"}'; + } +} +?> \ No newline at end of file