From 9f50f7e83310067876243fcbc3d5578f1061dd15 Mon Sep 17 00:00:00 2001 From: Maryse Simard Date: Wed, 3 Feb 2021 03:05:41 -0500 Subject: [PATCH] =?UTF-8?q?cam12263=20-=20plugin=20Pando=20-=20Ajout=20d'u?= =?UTF-8?q?n=20plugin=20pour=20int=C3=A9grer=20les=20utilisateurs=20Koha?= =?UTF-8?q?=20avec=20Pando?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit https://bugs.koha-community.org/show_bug.cgi?id=26051 --- inlibro-plugins/Pando.pm | 568 ++++++++++++++++++++ inlibro-plugins/Pando/configure.tt | 281 ++++++++++ inlibro-plugins/Pando/configure_fr-CA.tt | 281 ++++++++++ inlibro-plugins/Pando/images/inLibro_en.svg | 202 +++++++ inlibro-plugins/Pando/images/inLibro_fr.svg | 509 ++++++++++++++++++ 5 files changed, 1841 insertions(+) create mode 100644 inlibro-plugins/Pando.pm create mode 100644 inlibro-plugins/Pando/configure.tt create mode 100644 inlibro-plugins/Pando/configure_fr-CA.tt create mode 100644 inlibro-plugins/Pando/images/inLibro_en.svg create mode 100644 inlibro-plugins/Pando/images/inLibro_fr.svg diff --git a/inlibro-plugins/Pando.pm b/inlibro-plugins/Pando.pm new file mode 100644 index 0000000000..269e8b48a7 --- /dev/null +++ b/inlibro-plugins/Pando.pm @@ -0,0 +1,568 @@ +package Koha::Plugin::Pando; + +# Maryse Simard, 2021 - InLibro +# +# This plugin allows you to generate a Carrousel of books from available lists +# and insert the template into the table system preferences;OpacMainUserBlock +# +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software +# Foundation; either version 3 of the License, or (at your option) any later +# version. +# +# Koha 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with Koha; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +use base qw(Koha::Plugins::Base); +use Modern::Perl; +use CGI; + +use String::Util qw(trim); +use REST::Client; +use Try::Tiny; +use JSON; + +our $VERSION = 1.0; + +# mapping entre et les champs utilisés par l'api et ceux de +# la table borrowers +our $PANDO_BORROWER_MAPPING = { + card_number => 'cardnumber', # can't be modified + lastname => 'surname', # can't be modified + firstname => 'firstname', # can't be modified + house => 'streetnumber', + streetname => 'address', + city => 'city', + state => 'state', + zip => 'zipcode', + email => 'email', + phone => 'phone', + cell => 'mobile', + birthday => 'dateofbirth', # can't be modified + expiration_date => 'dateexpiry', + password => 'password', + password_confirmation => 'password', +}; + +=head1 NAME + +Koha::Plugin::Pando - Plugin pour l'intégration avec Pando + +=head2 METHODS + +=head3 new + +=cut + +sub new { + my ( $class, $params ) = @_; + + $params->{"metadata"} = { + name => "Intégration Pando", + author => "Maryse Simard", + description => "Permet l'intégration de la gestion des utilisateurs avec Pando.", + date_authored => "2021-01-25", + date_updated => "2021-01-25", + minimum_version => "20.05", + maximum_version => undef, + version => $VERSION, + }; + + my $self = $class->SUPER::new($params); + return $self; +} + +=head3 configure + +Gère la page de configuration du plugin : affiche la page ou +enregistre les valeurs. + +=cut + +sub configure { + my ( $self, $params ) = @_; + my $cgi = $self->{'cgi'}; + + if ( $cgi->param("action") && $cgi->param("action") eq "configure" ) { + my $base_url = $cgi->param('base_url'); + my $client_id = $cgi->param('client_id'); + my $client_secret = $cgi->param('client_secret'); + my $test_config = $cgi->param('test_config') || ""; + my @categories = $cgi->multi_param('categories'); + my $categories = encode_json(\@categories); + my $attribute = $cgi->param('attribute'); + my $auto_uid = $cgi->param('auto_uid') ? 1 : 0; + + $self->store_data( + { + base_url => $base_url, + client_id => $client_id, + client_secret => $client_secret, + test_config => $test_config, + categories => $categories, + attribute => $attribute, + auto_uid => $auto_uid, + } + ); + + $self->go_home(); + } else { + my $template = $self->retrieve_template({ name => "configure" }); + + my $categories = Koha::Patron::Categories->search({}, { order_by => 'description' }); + my $selected_categories = decode_json($self->retrieve_data('categories')) if ( defined $self->retrieve_data('categories') ); + + my $attributes = Koha::Patron::Attribute::Types->search({ unique_id => 1 }, { order_by => 'description' }); + my $attribute = $self->retrieve_data('attribute'); + + my $auto_uid = $self->retrieve_data('auto_uid') // 1; # valeur par défaut + + $template->param( + base_url => $self->retrieve_data('base_url'), + client_id => $self->retrieve_data('client_id'), + client_secret => $self->retrieve_data('client_secret'), + test_config => $self->retrieve_data('test_config'), + categories => $categories, + selected_categories => $selected_categories, + attributes => $attributes, + selected_attribute => $attribute, + auto_uid => $auto_uid, + ); + + print $cgi->header(-type => 'text/html', -charset => 'utf-8'); + print $template->output(); + } +} + +=head3 after_patron_action + +Gère le hook after_patron_action. Appele la bonne routine selon la +valeur du paramètre action. + +Seul les utilisateurs faisant partie des catégories configurées +dans le plugin seront affectés. + +=cut + +sub after_patron_action { + my ( $self, $params ) = @_; + + my $action = $params->{action} // ''; + my $patron = $params->{patron}; + my $borrowernumber = $params->{patron_id}; + + unless ( $patron ) { + $patron = Koha::Patron->find($borrowernumber); + } + + my $categories = decode_json($self->retrieve_data('categories')) if ( defined $self->retrieve_data('categories') ); + my $category = $patron->categorycode if ( $patron ); + return unless ( grep(/^$category$/, @{$categories}) ); + + if ( $action eq 'create' ) { + $self->after_patron_create($patron); + } elsif ( $action eq 'modify' ) { + $self->after_patron_modify($patron); + } elsif ( $action eq 'delete' ) { + # TODO + } +} + +=head3 after_patron_create + +Appele l'endpoint correspondant de l'api pour la création d'un +utilisateur. + +=cut + +sub after_patron_create { + my ( $self, $patron ) = @_; + + my ($client, undef) = $self->retrieve_token(); + + my $params = $self->prepare_parameters($patron); + my $json = encode_json($params); + + $client->POST("api/v1/users", $json, { 'Content-Type' => 'application/json', 'Accept' => 'application/json' }); + my $response_content = $client->responseContent(); + + my $data; + try { + my $response = decode_json($response_content); + if ( $response->{status} eq "success" ) { + $data = $response->{data}; + } else { + warn "[Koha::Plugin::Pando] Creating user error: \"" . $response->{message} . "\"."; + } + } catch { + warn "[Koha::Plugin::Pando] Creating user failed for borrowernumber " . $patron->borrowernumber; + }; + + if ( $data ) { + $self->verify_patron_data($patron, $data); + } +} + +=head3 after_patron_modify + +Appele l'endpoint correspondant de l'api pour la mise à jour d'un +utilisateur. + +TODO réessayer avec get_uid_from_email si le uid existe, mais est invalide. + +=cut + +sub after_patron_modify { + my ( $self, $patron ) = @_; + + my ($client, undef) = $self->retrieve_token(); + + my $attribute = $self->retrieve_data('attribute'); + my $patron_attribute = $patron->extended_attributes->search({ code => $attribute })->single; + my $uid = trim($patron_attribute->attribute) if ( $patron_attribute ); + + unless ( $uid ) { + my $auto_uid = $self->retrieve_data('auto_uid') // 1; + if ( $auto_uid ) { + $uid = $self->get_uid_from_email({ client => $client, email => $patron->email }); + + if ( $uid ) { + $patron_attribute->delete if ( $patron_attribute ); + $patron->add_extended_attribute( + { + code => $attribute, + attribute => $uid, + } + ); + } + } + } + + return unless ( $uid ); + + my $params = $self->prepare_parameters($patron); + my $json = encode_json($params); + + $client->PATCH("api/v1/users/$uid", $json, { 'Content-Type' => 'application/json', 'Accept' => 'application/json' }); + my $response_content = $client->responseContent(); + + my $data; + try { + my $response = decode_json($response_content); + if ( $response->{status} eq "success" ) { + $data = $response->{data}; + } else { + warn "[Koha::Plugin::Pando] Updating user error: \"" . $response->{message} . "\"."; + } + } catch { + warn "[Koha::Plugin::Pando] Updating user failed for borrowernumber " . $patron->borrowernumber; + }; + + if ( $data ) { + $self->verify_patron_data($patron, $data); + } +} + +=head3 prepare_parameters + +Prépare un objet contenant les valeurs d'un Koha::Patron en utilisant +les clés correspondantes pour l'api. +Utilise $PANDO_BORROWER_MAPPING. + +=cut + +sub prepare_parameters { + my ( $self, $patron ) = @_; + + my $params = {}; + + foreach my $key ( keys %{$PANDO_BORROWER_MAPPING} ) { + my $field = $PANDO_BORROWER_MAPPING->{$key}; + my $value = $patron->${field} if ( $patron ) ; + $params->{$key} = $value if ( defined $value ); + } + + return $params; +} + +=head3 verify_patron_data + +Vérifie que les valeurs retournées par l'api après la sauvegarde +correspondent à celles de Koha. Sinon, met à jour le borrower dans Koha +pour que les deux systèmes soient correctement synchronisés. + +Note: Cette routine utilise des requêtes sql pour modifier les données +directement dans la base de données : On veut éviter d'utiliser +Koha::Patron->store qui relancerait le hook after_patron_action. + +=cut + +sub verify_patron_data { + my ($self, $patron, $data) = @_; + + # uid + my $auto_uid = $self->retrieve_data('auto_uid') // 1; + if ( $auto_uid ) { + my $attribute = $self->retrieve_data('attribute'); + + if ( $attribute && $data->{uid} ) { + $patron->extended_attributes->search({ code => $attribute })->single->delete; + $patron->add_extended_attribute( + { + code => $attribute, + attribute => $data->{uid}, + } + ); + } + } + + # Compare les données reçues en réponse et celles de Koha + my @keys; + my @values; + foreach my $key ( keys %{$PANDO_BORROWER_MAPPING} ) { + my $field = $PANDO_BORROWER_MAPPING->{$key}; + my $value = $data->{$key}; + + next unless ( $field && $value ); + + unless ( $patron->${field} eq $value ) { + push @keys, $field; + push @values, $value; + } + } + + if ( scalar @keys ) { + my $dbh = C4::Context->dbh; + my $sth = $dbh->prepare("UPDATE borrowers SET " . (join ", ", map { "$_ = ?" } @keys) . " WHERE borrowernumber = ?"); + $sth->execute(@values, $patron->borrowernumber); + } +} + +=head3 test_config + +Teste la connexion à l'api pour les valeurs de base_url, client_id +et client_secret fournies. + +Imprime un document JSON pour réponse; à appeler par Ajax. + +=cut + +sub test_config { + my ( $self ) = @_; + + my $cgi = $self->{'cgi'}; + my $base_url = $cgi->param('base_url'); + my $client_id = $cgi->param('client_id'); + my $client_secret = $cgi->param('client_secret'); + + my $success = 0; + my $result = {}; + + my $client = REST::Client->new(host => $base_url); + $client->GET("api/v1/isalive"); + my $response_content = $client->responseContent(); + + try { + my $response = decode_json($response_content); + if ( $response->{status} eq "success" ) { + $success = $response->{data}->{isalive} + } else { + $result->{error_base_url} = 1; + } + } catch { + $result->{error_base_url} = 1; + }; + + if ( $success ) { + my (undef, $token) = $self->retrieve_token({ client => $client, client_id => $client_id, client_secret => $client_secret}); + unless ( $token ) { + $success = 0; + $result->{error_credentials} = 1; + } + } + + $result->{success} = $success; + + print $cgi->header( -type => 'application/json', -charset => 'utf-8' ); + print encode_json($result); + exit 0; +} + +=head3 retrieve_token + +my ($client, $token) = retrieve_token(); + +Récupère et retourne le token de connexion. +Retourne aussi un REST::Client ayant le host et le token configurés. + +=cut + +sub retrieve_token () { + my ( $self, $params ) = @_; + + my $client = $params->{client}; + my $client_id = $params->{client_id} // $self->retrieve_data('client_id'); + my $client_secret = $params->{client_secret} // $self->retrieve_data('client_secret'); + + my $token = undef; + + unless ( $client ) { + my $base_url = $params->{base_url} // $self->retrieve_data('base_url'); + $client = REST::Client->new(host => $base_url); + } + + $client->GET("api/v1/auth/access_token", {"Access-Token" => "$client_id|$client_secret"}); + my $response_content = $client->responseContent(); + + try { + my $response = decode_json($response_content); + $token = $response->{data}->{token} if ( $response->{status} eq "success" ); + }; + + $client->addHeader( "Authorization", "Bearer $token" ) if ( $token ); + + return ( $client, $token ); +} + +=head3 get_uid_from_email + +Récupère le uid à partir de la liste de tous les utilisateurs. + +=cut + +sub get_uid_from_email { + my ( $self, $params ) = @_; + + my $email = $params->{email}; + my $client = $params->{client}; + + my $uid = undef; + + unless ( $client ) { + ($client, undef) = $self->retrieve_token(); + } + + $client->GET("api/v1/users"); + my $response_content = $client->responseContent(); + + try { + my $response = decode_json($response_content); + if ( $response->{status} eq "success" ) { + foreach my $user ( @{$response->{data}} ) { + if ( $user->{email} eq $email ) { + $uid = $user->{uid}; + last; + } + } + } + }; + + return $uid; +} + +=head3 patron_attributes_delete_filter + +Filtre les attributs utilisateurs lors de leur suppression. + +On veut ici empêcher de supprimer l'attribut choisi pour contenir le +uid. Raison : memberentry.pl met à jour les attributs en les supprimant +puis les recréant, APRÈS la mise à jour de l'utilisateur. Ce qui +implique que si l'on touche à la valeur du uid dans ce plugin, ça sera +écrasé par la valeur contenu dans le formulaire. + +Appelé par Koha::Patron::Attributes->delete. + +Note : l'attribut utilisateur ne pourra donc jamais être supprimé via +memberentry.pl (plus précisement, tout appel à Koha::Patron::Attributes->delete) +tant que ce plugin est actif/cet attribut est configuré. + +=cut + +sub patron_attributes_delete_filter { + my ( $self, $params ) = @_; + + my $auto_uid = $self->retrieve_data('auto_uid') // 1; + return unless ( $auto_uid ); + + my $attribute = $self->retrieve_data('attribute'); + return unless ( $attribute ); + + my $attributes = $params->{attributes}; + return $attributes->search({ -not => { "me.code" => $attribute } }); +} + +=head3 intranet_js + +Ajoute du javascript à l'intranet. Retire le champ pour le uid si +auto_uid est à vrai. + +=cut + +sub intranet_js { + my ( $self, $args ) = @_; + + my $auto_uid = $self->retrieve_data('auto_uid') // 1; + return unless ( $auto_uid ); + + my $attribute = $self->retrieve_data('attribute'); + return unless ( $attribute ); + + return ""; +} + +=head3 retrieve_template + +Retourne le template pour le nom fourni selon la langue. + +=cut + +sub retrieve_template { + my ( $self, $params ) = @_; + + my $name = $params->{name} // "configure"; + + # Le template dépend du cookie, celui par défault est anglais + my $preferedLanguage = $self->{"cgi"}->cookie("KohaOpacLanguage") || ""; + my $template = undef; + + eval {$template = $self->get_template( { file => $name . "_$preferedLanguage.tt" } )}; + unless ( $template ) { + $preferedLanguage = substr $preferedLanguage, 0, 2; + eval {$template = $self->get_template( { file => $name . "_$preferedLanguage.tt" } )}; + } + $template = $self->get_template( { file => "$name.tt" } ) unless ( $template ); + + return $template; +} + +=head3 install + +Installe le plugin. + +=cut + +sub install { + my ( $self, $params ) = @_; + return 1; +} + +=head3 uninstall + +Supprime le plugin. + +=cut + +sub uninstall { + my ( $self, $params ) = @_; + return 1; +} + +1; diff --git a/inlibro-plugins/Pando/configure.tt b/inlibro-plugins/Pando/configure.tt new file mode 100644 index 0000000000..22b4cfab66 --- /dev/null +++ b/inlibro-plugins/Pando/configure.tt @@ -0,0 +1,281 @@ +[% USE raw %] +[% USE Asset %] +[% SET footerjs = 1 %] +[% INCLUDE 'doc-head-open.inc' %] + Koha › Pando Plugin › Configuration + [% INCLUDE 'doc-head-close.inc' %] + [% Asset.css("lib/jquery/plugins/multiple-select/multiple-select.css") | $raw %] + + + + + +[% INCLUDE 'header.inc' %] +[% INCLUDE 'cat-search.inc' %] + + + +
+
+
+ +
+

This plugin created by

+ inLibro +

Visit our website for more Koha plugins

+
+ +

Configuration of Pando integration

+ +
+ [%# mandatory parameters %] + + + [%# end mandatory %] + + +
+ API settings +
    +
  1. + + + +
  2. +
  3. + + + +
  4. +
  5. + + + +
  6. +
+ +
+ + + +
+
+ +
+ Patrons +
    +
  1. + + + Only these categories' patrons will be synchronised. +
  2. +
  3. + + + Patron attribute where is stored the unique identifier (uid) used by Pando. +
  4. +
  5. + + [% IF auto_uid == 0 %] + + [% ELSE %] + + [% END %] + When checked, the plugin automatically takes care of getting and updating Pando's uid in Koha. Note: The attribute's value can't be manually changed in the interface. Default: true +
  6. +
+
+ +
+ +
+
+
+
+[%# last div closed by intranet-bottom.inc %] + +[% MACRO jsinclude BLOCK %] + [% Asset.js("lib/jquery/plugins/multiple-select/jquery.multiple.select.js") | $raw %] + + +[% END %] + +[% INCLUDE 'intranet-bottom.inc' %] diff --git a/inlibro-plugins/Pando/configure_fr-CA.tt b/inlibro-plugins/Pando/configure_fr-CA.tt new file mode 100644 index 0000000000..9d0459df30 --- /dev/null +++ b/inlibro-plugins/Pando/configure_fr-CA.tt @@ -0,0 +1,281 @@ +[% USE raw %] +[% USE Asset %] +[% SET footerjs = 1 %] +[% INCLUDE 'doc-head-open.inc' %] + Koha › Plugin Pando › Configuration + [% INCLUDE 'doc-head-close.inc' %] + [% Asset.css("lib/jquery/plugins/multiple-select/multiple-select.css") | $raw %] + + + + + +[% INCLUDE 'header.inc' %] +[% INCLUDE 'cat-search.inc' %] + + + +
+
+
+ +
+

Cette extension a été créée par

+ inLibro +

Visitez notre site web pour d'autres extension Koha

+
+ +

Configuration de l'intégration Pando

+ +
+ [%# mandatory parameters %] + + + [%# end mandatory %] + + +
+ Paramètres de l'API +
    +
  1. + + + +
  2. +
  3. + + + +
  4. +
  5. + + + +
  6. +
+ +
+ + + +
+
+ +
+ Utilisateurs +
    +
  1. + + + Seulement les utilisateurs de ces catégories seront synchronisés. +
  2. +
  3. + + + L'attribut utilisateur contenant l'identifiant unique (uid) utilisé par Pando. +
  4. +
  5. + + [% IF auto_uid == 0 %] + + [% ELSE %] + + [% END %] + Lorsque coché, l'extension se chargera automatiquement de récupérer et mettre à jour l'uid de Pando dans Koha. Note : La valeur de l'attribut utilisateur ne pourra pas être changé manuellement dans l'interface. Valeur par défaut : vrai +
  6. +
+
+ +
+ +
+
+
+
+[%# last div closed by intranet-bottom.inc %] + +[% MACRO jsinclude BLOCK %] + [% Asset.js("lib/jquery/plugins/multiple-select/jquery.multiple.select.js") | $raw %] + + +[% END %] + +[% INCLUDE 'intranet-bottom.inc' %] diff --git a/inlibro-plugins/Pando/images/inLibro_en.svg b/inlibro-plugins/Pando/images/inLibro_en.svg new file mode 100644 index 0000000000..3072fbfa88 --- /dev/null +++ b/inlibro-plugins/Pando/images/inLibro_en.svg @@ -0,0 +1,202 @@ + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Document technologies specialists + + + + diff --git a/inlibro-plugins/Pando/images/inLibro_fr.svg b/inlibro-plugins/Pando/images/inLibro_fr.svg new file mode 100644 index 0000000000..0186f90402 --- /dev/null +++ b/inlibro-plugins/Pando/images/inLibro_fr.svg @@ -0,0 +1,509 @@ + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -- 2.17.1