[Koha-patches] [PATCH 1/1] But 5742: Batch edit patrons
Jonathan Druart
jonathan.druart at biblibre.com
Thu Mar 29 15:57:09 CEST 2012
new permission : edit_patrons
Plan test:
- Go on the page: tools > Patrons modification (modborrowers.pl)
- Enter a list of cardnumbers (or use a file)
- Modify one or more patron's fields (you can modify surname, firstname,
branchname, category, sort1, sort2, date of enrollment, date of expiry,
debarred date, debarred comment and borrower note)
- Save
- Check on the result page (or in database for non-displayed fields) if
modifications have been correctly made.
- re test with different attributes. For each attributes filled with an
authorized value category, you can select a value in a drop-down list.
Else it's a free input text. If your attribute is filled with a patron
category, the modification performs only on patrons belonging to this
category
- Save
- Verify on the result page
---
C4/Members.pm | 151 +++++----
C4/Members/Attributes.pm | 56 +++-
.../data/mysql/de-DE/mandatory/userpermissions.sql | 1 +
.../data/mysql/en/mandatory/userpermissions.sql | 1 +
.../data/mysql/es-ES/mandatory/userpermissions.sql | 1 +
.../mysql/fr-FR/1-Obligatoire/userpermissions.sql | 1 +
.../data/mysql/it-IT/necessari/userpermissions.sql | 1 +
.../mysql/nb-NO/1-Obligatorisk/userpermissions.sql | 1 +
.../data/mysql/pl-PL/mandatory/userpermissions.sql | 1 +
installer/data/mysql/updatedatabase.pl | 10 +
.../prog/en/modules/tools/modborrowers.tt | 310 +++++++++++++++++++
.../prog/en/modules/tools/tools-home.tt | 5 +
tools/modborrowers.pl | 322 ++++++++++++++++++++
13 files changed, 789 insertions(+), 72 deletions(-)
create mode 100644 koha-tmpl/intranet-tmpl/prog/en/modules/tools/modborrowers.tt
create mode 100755 tools/modborrowers.pl
diff --git a/C4/Members.pm b/C4/Members.pm
index 6161ac9..5290b0e 100644
--- a/C4/Members.pm
+++ b/C4/Members.pm
@@ -42,86 +42,87 @@ use Koha::DateUtils;
our ($VERSION, at ISA, at EXPORT, at EXPORT_OK,$debug);
BEGIN {
- $VERSION = 3.02;
- $debug = $ENV{DEBUG} || 0;
- require Exporter;
- @ISA = qw(Exporter);
- #Get data
- push @EXPORT, qw(
- &Search
- &GetMemberDetails
+ $VERSION = 3.02;
+ $debug = $ENV{DEBUG} || 0;
+ require Exporter;
+ @ISA = qw(Exporter);
+ #Get data
+ push @EXPORT, qw(
+ &Search
+ &GetMemberDetails
&GetMemberRelatives
- &GetMember
+ &GetMember
- &GetGuarantees
+ &GetGuarantees
- &GetMemberIssuesAndFines
- &GetPendingIssues
- &GetAllIssues
+ &GetMemberIssuesAndFines
+ &GetPendingIssues
+ &GetAllIssues
- &get_institutions
- &getzipnamecity
- &getidcity
+ &get_institutions
+ &getzipnamecity
+ &getidcity
- &GetFirstValidEmailAddress
+ &GetFirstValidEmailAddress
- &GetAge
- &GetCities
- &GetRoadTypes
- &GetRoadTypeDetails
- &GetSortDetails
- &GetTitles
+ &GetAge
+ &GetCities
+ &GetRoadTypes
+ &GetRoadTypeDetails
+ &GetSortDetails
+ &GetTitles
- &GetPatronImage
- &PutPatronImage
- &RmPatronImage
+ &GetPatronImage
+ &PutPatronImage
+ &RmPatronImage
- &GetHideLostItemsPreference
+ &GetHideLostItemsPreference
- &IsMemberBlocked
- &GetMemberAccountRecords
- &GetBorNotifyAcctRecord
+ &IsMemberBlocked
+ &GetMemberAccountRecords
+ &GetBorNotifyAcctRecord
- &GetborCatFromCatType
- &GetBorrowercategory
- &GetBorrowercategoryList
+ &GetborCatFromCatType
+ &GetBorrowercategory
+ GetBorrowerCategorycode
+ &GetBorrowercategoryList
- &GetBorrowersWhoHaveNotBorrowedSince
- &GetBorrowersWhoHaveNeverBorrowed
- &GetBorrowersWithIssuesHistoryOlderThan
+ &GetBorrowersWhoHaveNotBorrowedSince
+ &GetBorrowersWhoHaveNeverBorrowed
+ &GetBorrowersWithIssuesHistoryOlderThan
- &GetExpiryDate
+ &GetExpiryDate
- &AddMessage
- &DeleteMessage
- &GetMessages
- &GetMessagesCount
+ &AddMessage
+ &DeleteMessage
+ &GetMessages
+ &GetMessagesCount
&IssueSlip
- GetBorrowersWithEmail
- );
+ GetBorrowersWithEmail
+ );
- #Modify data
- push @EXPORT, qw(
- &ModMember
- &changepassword
+ #Modify data
+ push @EXPORT, qw(
+ &ModMember
+ &changepassword
&ModPrivacy
- );
-
- #Delete data
- push @EXPORT, qw(
- &DelMember
- );
-
- #Insert data
- push @EXPORT, qw(
- &AddMember
- &add_member_orgs
- &MoveMemberToDeleted
- &ExtendMemberSubscriptionTo
- );
-
- #Check data
+ );
+
+ #Delete data
+ push @EXPORT, qw(
+ &DelMember
+ );
+
+ #Insert data
+ push @EXPORT, qw(
+ &AddMember
+ &add_member_orgs
+ &MoveMemberToDeleted
+ &ExtendMemberSubscriptionTo
+ );
+
+ #Check data
push @EXPORT, qw(
&checkuniquemember
&checkuserpassword
@@ -1409,10 +1410,6 @@ sub GetborCatFromCatType {
Given the borrower's category code, the function returns the corresponding
data hashref for a comprehensive information display.
- $arrayref_hashref = &GetBorrowercategory;
-
-If no category code provided, the function returns all the categories.
-
=cut
sub GetBorrowercategory {
@@ -1433,6 +1430,26 @@ sub GetBorrowercategory {
return;
} # sub getborrowercategory
+
+=head2 GetBorrowerCategorycode
+
+ $categorycode = &GetBorrowerCategoryCode( $borrowernumber );
+
+Given the borrowernumber, the function returns the corresponding categorycode
+=cut
+
+sub GetBorrowerCategorycode {
+ my ( $borrowernumber ) = @_;
+ my $dbh = C4::Context->dbh;
+ my $sth = $dbh->prepare( qq{
+ SELECT categorycode
+ FROM borrowers
+ WHERE borrowernumber = ?
+ } );
+ $sth->execute( $borrowernumber );
+ return $sth->fetchrow;
+}
+
=head2 GetBorrowercategoryList
$arrayref_hashref = &GetBorrowercategoryList;
diff --git a/C4/Members/Attributes.pm b/C4/Members/Attributes.pm
index 33d2407..b373c94 100644
--- a/C4/Members/Attributes.pm
+++ b/C4/Members/Attributes.pm
@@ -32,8 +32,9 @@ BEGIN {
$VERSION = 3.01;
@ISA = qw(Exporter);
@EXPORT_OK = qw(GetBorrowerAttributes GetBorrowerAttributeValue CheckUniqueness SetBorrowerAttributes
+ DeleteBorrowerAttribute UpdateBorrowerAttribute
extended_attributes_code_value_arrayref extended_attributes_merge
- SearchIdMatchingAttribute);
+ SearchIdMatchingAttribute);
%EXPORT_TAGS = ( all => \@EXPORT_OK );
}
@@ -76,18 +77,19 @@ sub GetBorrowerAttributes {
FROM borrower_attributes
JOIN borrower_attribute_types USING (code)
LEFT JOIN authorised_values ON (category = authorised_value_category AND attribute = authorised_value)
- WHERE borrowernumber = ?";
+ WHERE 1";
+ $query .= "\nAND borrowernumber = ?" if $borrowernumber;
$query .= "\nAND opac_display = 1" if $opac_only;
$query .= "\nORDER BY code, attribute";
my $sth = $dbh->prepare_cached($query);
- $sth->execute($borrowernumber);
+ $sth->execute($borrowernumber ? $borrowernumber : ());
my @results = ();
while (my $row = $sth->fetchrow_hashref()) {
push @results, {
code => $row->{'code'},
description => $row->{'description'},
- value => $row->{'attribute'},
- value_description => $row->{'lib'},
+ value => $row->{'attribute'},
+ value_description => $row->{'lib'},
password => $row->{'password'},
display_checkout => $row->{'display_checkout'},
category_code => $row->{'category_code'},
@@ -233,6 +235,50 @@ sub SetBorrowerAttributes {
return 1; # borower attributes successfully set
}
+=head2 DeleteBorrowerAttribute
+
+ DeleteBorrowerAttribute($borrowernumber, $attribute);
+
+Delete a borrower attribute for the patron identified by C<$borrowernumber> and the attribute code of C<$attribute>
+
+=cut
+sub DeleteBorrowerAttribute {
+ my ( $borrowernumber, $attribute ) = @_;
+
+ my $dbh = C4::Context->dbh;
+ my $sth = $dbh->prepare(qq{
+ DELETE FROM borrower_attributes
+ WHERE borrowernumber = ?
+ AND code = ?
+ } );
+ $sth->execute( $borrowernumber, $attribute->{code} );
+}
+
+=head2 UpdateBorrowerAttribute
+
+ UpdateBorrowerAttribute($borrowernumber, $attribute );
+
+Update a borrower attribute C<$attribute> for the patron identified by C<$borrowernumber>,
+
+=cut
+sub UpdateBorrowerAttribute {
+ my ( $borrowernumber, $attribute ) = @_;
+
+ DeleteBorrowerAttribute $borrowernumber, $attribute;
+
+ my $dbh = C4::Context->dbh;
+ my $query = "INSERT INTO borrower_attributes SET attribute = ?, code = ?, borrowernumber = ?";
+ my @params = ( $attribute->{attribute}, $attribute->{code}, $borrowernumber );
+ if ( defined $attribute->{password} ) {
+ $query .= ", password = ?";
+ push @params, $attribute->{password};
+ }
+ my $sth = $dbh->prepare( $query );
+
+ $sth->execute( @params );
+}
+
+
=head2 extended_attributes_code_value_arrayref
my $patron_attributes = "homeroom:1150605,grade:01,extradata:foobar";
diff --git a/installer/data/mysql/de-DE/mandatory/userpermissions.sql b/installer/data/mysql/de-DE/mandatory/userpermissions.sql
index 2302273..28326bd 100644
--- a/installer/data/mysql/de-DE/mandatory/userpermissions.sql
+++ b/installer/data/mysql/de-DE/mandatory/userpermissions.sql
@@ -28,6 +28,7 @@ INSERT INTO permissions (module_bit, code, description) VALUES
(13, 'manage_staged_marc', 'MARC-Importe verwalten, auch Übernahme in Katalog und Import rückgängig machen'),
(13, 'export_catalog', 'Titel- und Exemplardaten exportieren'),
(13, 'import_patrons', 'Benutzerdaten importieren'),
+ (13, 'edit_patrons', 'Perform batch modification of patrons'),
(13, 'delete_anonymize_patrons', 'Inaktive Benutzer löschen und Ausleihhistorie anonymisieren (Benutzerausleihhistorie löschen)'),
(13, 'batch_upload_patron_images', 'Benutzerfotos einzeln oder im Stapel hochladen'),
(13, 'schedule_tasks', 'Aufgabenplaner verwenden'),
diff --git a/installer/data/mysql/en/mandatory/userpermissions.sql b/installer/data/mysql/en/mandatory/userpermissions.sql
index 873089a..da61f69 100644
--- a/installer/data/mysql/en/mandatory/userpermissions.sql
+++ b/installer/data/mysql/en/mandatory/userpermissions.sql
@@ -28,6 +28,7 @@ INSERT INTO permissions (module_bit, code, description) VALUES
(13, 'manage_staged_marc', 'Managed staged MARC records, including completing and reversing imports'),
(13, 'export_catalog', 'Export bibliographic and holdings data'),
(13, 'import_patrons', 'Import patron data'),
+ (13, 'edit_patrons', 'Perform batch modification of patrons'),
(13, 'delete_anonymize_patrons', 'Delete old borrowers and anonymize circulation history (deletes borrower reading history)'),
(13, 'batch_upload_patron_images', 'Upload patron images in batch or one at a time'),
(13, 'schedule_tasks', 'Schedule tasks to run'),
diff --git a/installer/data/mysql/es-ES/mandatory/userpermissions.sql b/installer/data/mysql/es-ES/mandatory/userpermissions.sql
index ec61ea0..ce96ec1 100644
--- a/installer/data/mysql/es-ES/mandatory/userpermissions.sql
+++ b/installer/data/mysql/es-ES/mandatory/userpermissions.sql
@@ -28,6 +28,7 @@ INSERT INTO permissions (module_bit, code, description) VALUES
(13, 'manage_staged_marc', 'Managed staged MARC records, including completing and reversing imports'),
(13, 'export_catalog', 'Export bibliographic and holdings data'),
(13, 'import_patrons', 'Import patron data'),
+ (13, 'edit_patrons', 'Perform batch modification of patrons'),
(13, 'delete_anonymize_patrons', 'Delete old borrowers and anonymize circulation history (deletes borrower reading history)'),
(13, 'batch_upload_patron_images', 'Upload patron images in batch or one at a time'),
(13, 'schedule_tasks', 'Schedule tasks to run'),
diff --git a/installer/data/mysql/fr-FR/1-Obligatoire/userpermissions.sql b/installer/data/mysql/fr-FR/1-Obligatoire/userpermissions.sql
index faaaf39..7a30f82 100644
--- a/installer/data/mysql/fr-FR/1-Obligatoire/userpermissions.sql
+++ b/installer/data/mysql/fr-FR/1-Obligatoire/userpermissions.sql
@@ -18,6 +18,7 @@ INSERT INTO permissions (module_bit, code, description) VALUES
(13, 'manage_staged_marc', 'Gérer les notices du réservoir, les charger ou annuler leur chargement'),
(13, 'export_catalog', 'Exporter des notices bibliographiques et leurs exemplaires'),
(13, 'import_patrons', 'Importer des données d''adhérents'),
+ (13, 'edit_patrons', 'Modification par lot des lecteurs'),
(13, 'delete_anonymize_patrons', 'Supprimer les anciens adhérents et anonymiser l''historique des prêts (supprime l''historique des prêts des lecteurs'),
(13, 'batch_upload_patron_images', 'Charger sur le serveur les images des adhérents par lot ou un par un'),
(13, 'schedule_tasks', 'Planifier les tâches à exécuter'),
diff --git a/installer/data/mysql/it-IT/necessari/userpermissions.sql b/installer/data/mysql/it-IT/necessari/userpermissions.sql
index 926b47c..fd7cb12 100644
--- a/installer/data/mysql/it-IT/necessari/userpermissions.sql
+++ b/installer/data/mysql/it-IT/necessari/userpermissions.sql
@@ -30,6 +30,7 @@ INSERT INTO permissions (module_bit, code, description) VALUES
(13, 'manage_staged_marc', 'Gestisci i record MARC in lavorazione, inclusi il completare e il cancellare gli import'),
(13, 'export_catalog', 'Esporta i dati bibliografici e di copia'),
(13, 'import_patrons', 'Importa i dati utente'),
+ (13, 'edit_patrons', 'Perform batch modification of patrons'),
(13, 'delete_anonymize_patrons', 'Cancella i vecchi prestiti e rendi anonimo lo storico della circolazione (canella in lettura lo storico utenti prestito)'),
(13, 'batch_upload_patron_images', 'Aggiorna le foto utente in modalità batch o al momento'),
(13, 'schedule_tasks', 'Schedula i task da far andare'),
diff --git a/installer/data/mysql/nb-NO/1-Obligatorisk/userpermissions.sql b/installer/data/mysql/nb-NO/1-Obligatorisk/userpermissions.sql
index 24f081e..8525a20 100644
--- a/installer/data/mysql/nb-NO/1-Obligatorisk/userpermissions.sql
+++ b/installer/data/mysql/nb-NO/1-Obligatorisk/userpermissions.sql
@@ -49,6 +49,7 @@ INSERT INTO permissions (module_bit, code, description) VALUES
(13, 'manage_staged_marc', 'Behandle lagrede MARC-poster, inkludert ferdigstilling og reversering av importer'),
(13, 'export_catalog', 'Eksportere bibliografiske data og beholdningsdata'),
(13, 'import_patrons', 'Importere låneropplysninger'),
+ (13, 'edit_patrons', 'Perform batch modification of patrons'),
(13, 'delete_anonymize_patrons', 'Slette utgåtte lånere og anonymisere lånehistorikk'),
(13, 'batch_upload_patron_images', 'Laste opp bilder av lånere enkeltvis eller en masse'),
(13, 'schedule_tasks', 'Planlegge oppgaver som skal kjøres'),
diff --git a/installer/data/mysql/pl-PL/mandatory/userpermissions.sql b/installer/data/mysql/pl-PL/mandatory/userpermissions.sql
index f16a14d..a46ddfe 100644
--- a/installer/data/mysql/pl-PL/mandatory/userpermissions.sql
+++ b/installer/data/mysql/pl-PL/mandatory/userpermissions.sql
@@ -28,6 +28,7 @@ INSERT INTO permissions (module_bit, code, description) VALUES
(13, 'manage_staged_marc', 'Managed staged MARC records, including completing and reversing imports'),
(13, 'export_catalog', 'Export bibliographic and holdings data'),
(13, 'import_patrons', 'Import patron data'),
+ (13, 'edit_patrons', 'Perform batch modification of patrons'),
(13, 'delete_anonymize_patrons', 'Delete old borrowers and anonymize circulation history (deletes borrower reading history)'),
(13, 'batch_upload_patron_images', 'Upload patron images in batch or one at a time'),
(13, 'schedule_tasks', 'Schedule tasks to run'),
diff --git a/installer/data/mysql/updatedatabase.pl b/installer/data/mysql/updatedatabase.pl
index 9225c32..1aacd81 100755
--- a/installer/data/mysql/updatedatabase.pl
+++ b/installer/data/mysql/updatedatabase.pl
@@ -5086,6 +5086,16 @@ if ( C4::Context->preference("Version") < TransformToNum($DBversion) ) {
SetVersion($DBversion);
}
+
+
+
+$DBversion = "3.07.00.XXX";
+if ( C4::Context->preference("Version") < TransformToNum($DBversion) ) {
+ $dbh->do("INSERT IGNORE INTO `permissions` (`module_bit`, `code`, `description`) VALUES('13', 'edit_patrons', 'Perform batch modifivation of patrons')");
+ print "Upgrade to $DBversion done (Adds permissions flag for access to the patron modifications tool)\n";
+ SetVersion($DBversion);
+}
+
=head1 FUNCTIONS
=head2 DropAllForeignKeys($table)
diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/modborrowers.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/tools/modborrowers.tt
new file mode 100644
index 0000000..c363181
--- /dev/null
+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/tools/modborrowers.tt
@@ -0,0 +1,310 @@
+[% USE KohaDates %]
+[% INCLUDE 'doc-head-open.inc'%]
+<title>Koha › Tools › [% IF ( del ) %]Batch item deletion[% ELSE %]Batch item modification[% END %] </title>
+[% INCLUDE 'doc-head-close.inc' %]
+[% INCLUDE 'calendar.inc' %]
+<script type="text/javascript" src="[% themelang %]/lib/jquery/plugins/jquery.tablesorter.min.js"></script>
+<script type="text/JavaScript" language="JavaScript">
+//<![CDATA[
+ var patron_attributes_lib = new Array();
+ var patron_attributes_values = new Array();
+ $(document).ready(function() {
+ $("#borrowerst").tablesorter();
+
+ $("#selectallbutton").click(function() {
+ $("#borrowerst").find("input:checkbox").each(function() {
+ $(this).attr("checked", true);
+ });
+ return false;
+ });
+ $("#clearallbutton").click(function() {
+ $("#borrowerst").find("input:checkbox").each(function() {
+ $(this).attr("checked", false);
+ });
+ return false;
+ });
+
+ var values = new Array();
+ var lib = new Array();
+ [% FOREACH pav IN patron_attributes_values %]
+ values = new Array();
+ lib = new Array();
+ [% FOREACH option IN pav.options %]
+ values.push("[% option.lib %]");
+ lib.push("[% option.authorised_value %]");
+ [% END %]
+ patron_attributes_lib["[% pav.attribute_code %]"] = values;
+ patron_attributes_values["[% pav.attribute_code %]"] = lib;
+ [% END %]
+
+ $('select[name="patron_attributes"]').change(function() {
+ updateAttrValues(this);
+ } );
+
+ $('select[name="patron_attributes"]').change();
+
+ } );
+
+ function updateAttrValues (select_attr) {
+ var attr_code = $(select_attr).val();
+ var type = $(select_attr).find("option:selected").attr('data-type');
+ var category = $(select_attr).find("option:selected").attr('data-category');
+ var span = $(select_attr).parent().parent().find('span.patron_attributes_value');
+ var information_category_node = $(select_attr).parent().parent().find('span.information_category');
+ information_category_node.html("");
+ if ( category.length > 0 ) {
+ information_category_node.html('This attribute will be only applied to the borrower\'s category "' + category + '"');
+ }
+ if ( type == 'select' ) {
+ var options = '<option value = ""></option>';
+ for ( var i = 0 ; i < patron_attributes_values[attr_code].length ; i++ ) {
+ options += '<option value="'+patron_attributes_values[attr_code][i]+'">'+patron_attributes_lib[attr_code][i]+'</option>';
+ }
+ span.html('<select name="patron_attributes_value">' + options + '</select>');
+ } else {
+ span.html('<input type="text" name="patron_attributes_value"/>')
+ }
+ }
+
+ function add_attributes() {
+ var li_node = $("li.attributes:last");
+ var li_clone = $(li_node).clone();
+ if ( $(li_clone).find("a.delete").length == 0 ) {
+ $(li_clone).append('[<a href="#" title="Delete" class="delete" onclick="del_attributes(this);return false;">X</a>]');
+ }
+ $(li_clone).find('select[name="patron_attributes"]').change(function() {
+ updateAttrValues(this);
+ } );
+
+ $(li_clone).find('select[name="patron_attributes"]').change();
+
+ $("#fields_list>ol").append(li_clone);
+ update_attr_values();
+ }
+
+ function del_attributes(a_node) {
+ $(a_node).parent('li').remove();
+ update_attr_values();
+ }
+
+ function update_attr_values() {
+ $("li.attributes").each(function(i) {
+ $(this).find("input:checkbox").val("attr"+i+"_value");
+ });
+ }
+ function clearDate(nodeid) {
+ $("#"+nodeid).val("");
+ }
+
+//]]>
+</script>
+</head>
+<body>
+[% INCLUDE 'header.inc' %]
+[% INCLUDE 'cat-search.inc' %]
+
+<div id="breadcrumbs">
+ <a href="/cgi-bin/koha/mainpage.pl">Home</a> ›
+ <a href="/cgi-bin/koha/tools/tools-home.pl">Tools</a> ›
+ <a href="/cgi-bin/koha/tools/modborrowers.pl">Patrons modification</a>
+</div>
+
+<div id="doc3" class="yui-t2">
+ <div id="bd">
+ <div id="yui-main">
+ <div class="yui-b">
+ [% IF ( op == 'show_form' ) %]
+ <h1>Batch patrons modification</h1>
+ <form method="post" enctype="multipart/form-data" action="/cgi-bin/koha/tools/modborrowers.pl">
+ <fieldset class="rows">
+ <legend>Use a file</legend>
+ <label for="uploadfile">File: </label> <input type="file" id="uploadfile" name="uploadfile" />
+ </fieldset>
+ <fieldset class="rows">
+ <legend>Or list cardnumbers one by one</legend>
+ <ol>
+ <li>
+ <label for="cardnumberlist">Carnumber list (one cardnumber per line): </label>
+ <textarea rows="10" cols="30" id="cardnumberlist" name="cardnumberlist">[% cardnumberlist %]</textarea>
+ </li>
+ </ol>
+ </fieldset>
+ <input type="hidden" name="op" value="show" />
+ <fieldset class="action">
+ <input type="submit" value="Continue" class="button" />
+ <a class="cancel" href="/cgi-bin/koha/tools/tools-home.pl">Cancel</a>
+ </fieldset>
+ </form>
+ [% END %]
+
+ [% IF ( op == 'show' or op == 'show_results' ) %]
+ [% IF ( op == 'show' ) %]
+ <h1>Batch patrons modification</h1>
+ [% ELSE %]
+ <h1>Batch patrons results</h1>
+ [% END %]
+ [% IF ( notfoundcardnumbers ) %]
+ <div class="dialog alert"><p>Warning, the following cardnumbers were not found:</p></div>
+ <table style="margin:auto;">
+ <thead>
+ <tr><th>Cardnumbers not found</th></tr>
+ </thead>
+ <tbody>
+ [% FOREACH notfoundcardnumber IN notfoundcardnumbers %]
+ <tr><td>[% notfoundcardnumber.cardnumber %]</td></tr>
+ [% END %]
+ </tbody>
+ </table>
+ [% END %]
+
+ [% IF ( op == 'show_results' ) %]
+ [% IF ( errors ) %]
+ Errors occured :
+ <ul class="warnings">
+ [% FOREACH error IN errors %]
+ [% IF ( error.error == 'can_not_update' ) %]
+ <li>Can not update borrower with borrowernumber [% error.borrowernumber %]</li>
+ [% ELSE %]
+ <li>[% error.error %]</li>
+ [% END %]
+ [% END %]
+ </ul>
+ [% END %]
+ <br/>
+ [% END %]
+
+ [% IF ( op == 'show' ) %]
+ <form name="f" action="modborrowers.pl" method="post">
+ <input type="hidden" name="op" value="do" />
+ [% IF ( borrowers ) %]
+ <div id="toolbar"><a id="selectallbutton" href="#">Select All</a> | <a id="clearallbutton" href="#">Clear All</a></div>
+ [% END %]
+ [% END %]
+ <div id="cataloguing_additem_itemlist">
+ <div style="overflow:auto">
+ <table id="borrowerst">
+ <thead>
+ <tr>
+ [% IF ( op == 'show' ) %]
+ <th> </th>
+ [% END %]
+ <th>Surname</th>
+ <th>Firstname</th>
+ <th>Branchname</th>
+ <th>Categorycode</th>
+ <th>Cardnumber</th>
+ <th>dateenrolled</th>
+ <th>dateexpiry</th>
+ <th>debarred</th>
+ [% FOREACH attrh IN attributes_header %]
+ <th>[% attrh.attribute %]</th>
+ [% END %]
+ </tr>
+ </thead>
+ <tbody>
+ [% FOREACH borrower IN borrowers %]
+ <tr>
+ [% IF ( op == 'show' ) %]
+ <td><input type="checkbox" name="borrowernumber" value="[% borrower.borrowernumber %]" checked="checked" /></td>
+ [% END %]
+ <td>[% borrower.surname %]</td>
+ <td>[% borrower.firstname %]</td>
+ <td>[% borrower.branchname %]</td>
+ <td>[% borrower.categorycode %]</td>
+ <td>[% borrower.cardnumber %]</td>
+ <td>[% borrower.dateenrolled | $KohaDates %]</td>
+ <td>[% borrower.dateexpiry | $KohaDates %]</td>
+ <td>[% borrower.debarred | $KohaDates %]</td>
+ [% FOREACH pa IN borrower.patron_attributes %]
+ [% IF ( pa.code ) %]
+ <td>[% pa.code %]=[% pa.value %]</td>
+ [% ELSE %]
+ <td></td>
+ [% END %]
+ [% END %]
+ </tr>
+ [% END %]
+ </tbody>
+ </table>
+ </div>
+ </div>
+
+ [% IF ( op == 'show' ) %]
+ <div id="cataloguing_additem_newitem">
+ <h2>Edit Patrons</h2>
+ <div class="hint">Checking the box right next the label will disable the entry and delete the values of that field on all selected patrons</div>
+ <fieldset class="rows" id="fields_list">
+ <ol>
+ [% FOREACH field IN fields %]
+ <li>
+ <label style="width:20em;">[% field.lib %]</label>
+ <input type="checkbox" title="check to delete this field" name="disable_input" value="[% field.name %]"/>
+ [% IF ( field.type == 'text' ) %]
+ <input type="text" name="[% field.name %]" value="" />
+ [% END %]
+ [% IF ( field.type == 'select' ) %]
+ [% IF field.option.size %]
+ <select name="[% field.name %]" >
+ [% FOREACH opt IN field.option %]
+ <option value="[% opt.value %]">[% opt.lib %]</option>
+ [% END %]
+ </select>
+ [% ELSE %]
+ There is no value defined for [% field.name %]
+ [% END %]
+ [% END %]
+ [% IF ( field.type == 'date' ) %]
+ <img src="[% themelang %]/lib/calendar/cal.gif" style="cursor: pointer;" alt="Show Calendar" title="Show Calendar" id="[% field.name %]button"/>
+ <input type="text" name="[% field.name %]" id="[% field.name %]" value="" size="10" maxlength="10" readonly />
+ <a href="#" onclick="clearDate('[% field.name %]');return false;">X</a>
+ <script type="text/javascript">
+ //<![CDATA[
+ Calendar.setup({
+ inputField : "[% field.name %]",
+ button : "[% field.name %]button",
+ ifFormat : "[% DHTMLcalendar_dateformat %]",
+ onClose: function() { $("#[% field.name %]").change(); this.hide();}
+ });
+ //]]>
+ </script>
+ [% END %]
+ </li>
+ [% END %]
+ <li class="attributes">
+ <label style="width:20em;">Attribute
+ <select name="patron_attributes">
+ [% FOREACH pac IN patron_attributes_codes %]
+ <option value="[% pac.attribute_code %]" data-type="[% pac.type %]" data-category="[% pac.category_lib %]">[% pac.attribute_lib %]</option>
+ [% END %]
+ </select>
+ </label>
+ <input type="checkbox" title="check to delete this field" name="disable_input" value="attr0_value" />
+ <span class="patron_attributes_value"></span>
+ <a href="#" title="Add an attribute" onclick="add_attributes(); return false;">+</a>
+ <span class="information_category"></span>
+ </li>
+ </ol>
+ </fieldset>
+ <fieldset class="action">
+ <input type="submit" name="mainformsubmit" value="Save" />
+ <a href="/cgi-bin/koha/tools/modborrowers.pl" class="cancel">Cancel</a>
+ </fieldset>
+ </div>
+ </form>
+ [% END %]
+ [% END %]
+ [% IF ( op == 'show_results' ) %]
+ <br/>
+ <a href="/cgi-bin/koha/tools/modborrowers.pl" title="new Batch patrons modification">new Batch patrons modification</a>
+ [% END %]
+ </div>
+ </div>
+ <div class="yui-b">
+ [% INCLUDE 'tools-menu.inc' %]
+ </div>
+ </div>
+ </div>
+</div>
+</body>
+</html>
+
diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/tools-home.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/tools/tools-home.tt
index 0cd163d..58400d8 100644
--- a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/tools-home.tt
+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/tools/tools-home.tt
@@ -45,6 +45,11 @@
<dd>Delete old borrowers and anonymize circulation history (deletes borrower reading history)</dd>
[% END %]
+ [% IF ( CAN_user_tools_edit_patrons ) %]
+ <dt><a href="/cgi-bin/koha/tools/modborrowers.pl">Patrons (Modification)</a></dt>
+ <dd>Modify patrons</dd>
+ [% END %]
+
[% IF ( CAN_user_tools_moderate_tags ) %]
<dt><a href="/cgi-bin/koha/tags/review.pl">Tags</a> [% IF ( pendingtags ) %]<span class="holdcount"><a href="/cgi-bin/koha/tags/review.pl">[% pendingtags %]</a></span>[% END %]</dt>
<dd>Moderate patron tags</dd>
diff --git a/tools/modborrowers.pl b/tools/modborrowers.pl
new file mode 100755
index 0000000..6dac4e8
--- /dev/null
+++ b/tools/modborrowers.pl
@@ -0,0 +1,322 @@
+#!/usr/bin/perl
+
+# Copyright 2012 BibLibre
+#
+# 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 2 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 Modern::Perl;
+use CGI;
+use C4::Auth;
+use C4::Branch;
+use C4::Koha;
+use C4::Members;
+use C4::Members::Attributes qw(GetBorrowerAttributes UpdateBorrowerAttribute DeleteBorrowerAttribute);
+use C4::Members::AttributeTypes qw/GetAttributeTypes_hashref/;
+use C4::Output;
+use List::MoreUtils qw /any uniq/;
+
+my $input = new CGI;
+my $op = $input->param('op') || 'show_form';
+my ( $template, $loggedinuser, $cookie ) = get_template_and_user(
+ { template_name => "tools/modborrowers.tmpl",
+ query => $input,
+ type => "intranet",
+ authnotrequired => 0,
+ flagsrequired => { tools => "edit_patrons" },
+ }
+);
+
+my %cookies = parse CGI::Cookie($cookie);
+my $sessionID = $cookies{'CGISESSID'}->value;
+my $dbh = C4::Context->dbh;
+
+
+
+if ( $op eq 'show' ) {
+ my $filefh = $input->upload('uploadfile');
+ my $filecontent = $input->param('filecontent');
+ my @borrowers;
+ my @cardnumbers;
+ my @notfoundcardnumbers;
+
+ my @contentlist;
+ if ($filefh) {
+ while ( my $content = <$filefh> ) {
+ $content =~ s/[\r\n]*$//g;
+ push @cardnumbers, $content if $content;
+ }
+ } else {
+ if ( my $list = $input->param('cardnumberlist') ) {
+ push @cardnumbers, split( /\s\n/, $list );
+ }
+ }
+
+ my $max_nb_attr = 0;
+ for my $cardnumber ( @cardnumbers ) {
+ my $borrower = GetBorrowerInfos( cardnumber => $cardnumber );
+ if ( $borrower ) {
+ $max_nb_attr = scalar( @{ $borrower->{patron_attributes} } )
+ if scalar( @{ $borrower->{patron_attributes} } ) > $max_nb_attr;
+ push @borrowers, $borrower;
+ } else {
+ push @notfoundcardnumbers, $cardnumber;
+ }
+ }
+
+ # Just for a correct display
+ for my $borrower ( @borrowers ) {
+ my $length = scalar( @{ $borrower->{patron_attributes} } );
+ push @{ $borrower->{patron_attributes} }, {} for ( $length .. $max_nb_attr - 1);
+ }
+
+ my @patron_attributes_values;
+ my @patron_attributes_codes;
+ my $patron_attribute_types = C4::Members::AttributeTypes::GetAttributeTypes_hashref('all');
+ my $patron_categories = C4::Members::GetBorrowercategoryList;
+ for ( values $patron_attribute_types ) {
+ my $attr_type = C4::Members::AttributeTypes->fetch( $_->{code} );
+ my $options = $attr_type->authorised_value_category
+ ? GetAuthorisedValues( $attr_type->authorised_value_category )
+ : undef;
+ push @patron_attributes_values,
+ {
+ attribute_code => $_->{code},
+ options => $options,
+ };
+
+ my $category_code = $_->{category_code};
+ my ( $category_lib ) = map {
+ ( defined $category_code and $_->{categorycode} eq $category_code ) ? $_->{description} : ()
+ } @$patron_categories;
+ push @patron_attributes_codes,
+ {
+ attribute_code => $_->{code},
+ attribute_lib => $_->{description},
+ category_lib => $category_lib,
+ type => $attr_type->authorised_value_category ? 'select' : 'text',
+ };
+ }
+
+ my @attributes_header = ();
+ for ( 1 .. scalar( $max_nb_attr ) ) {
+ push @attributes_header, { attribute => "Attributes $_" };
+ }
+ $template->param( borrowers => \@borrowers );
+ $template->param( attributes_header => \@attributes_header );
+ @notfoundcardnumbers = map { { cardnumber => $_ } } @notfoundcardnumbers;
+ $template->param( notfoundcardnumbers => \@notfoundcardnumbers )
+ if @notfoundcardnumbers;
+
+ my $branches = GetBranchesLoop;
+ my @branches_option;
+ push @branches_option, { value => $_->{value}, lib => $_->{branchname} } for @$branches;
+ unshift @branches_option, { value => "", lib => "" };
+ my $categories = GetBorrowercategoryList;
+ my @categories_option;
+ push @categories_option, { value => $_->{categorycode}, lib => $_->{description} } for @$categories;
+ unshift @categories_option, { value => "", lib => "" };
+ my $bsort1 = GetAuthorisedValues("Bsort1");
+ my @sort1_option;
+ push @sort1_option, { value => $_->{authorised_value}, lib => $_->{lib} } for @$bsort1;
+ unshift @sort1_option, { value => "", lib => "" }
+ if @sort1_option;
+ my $bsort2 = GetAuthorisedValues("Bsort2");
+ my @sort2_option;
+ push @sort2_option, { value => $_->{authorised_value}, lib => $_->{lib} } for @$bsort2;
+ unshift @sort2_option, { value => "", lib => "" }
+ if @sort2_option;
+ my @fields = (
+ {
+ name => "surname",
+ lib => "Surname",
+ type => "text",
+ }
+ ,
+ {
+ name => "firstname",
+ lib => "Firstname",
+ type => "text",
+ }
+ ,
+ {
+ name => "branchcode",
+ lib => "Branchname",
+ type => "select",
+ option => \@branches_option,
+ }
+ ,
+ {
+ name => "categorycode",
+ lib => "Category",
+ type => "select",
+ option => \@categories_option,
+ }
+ ,
+ {
+ name => "sort1",
+ lib => "Sort 1",
+ type => "select",
+ option => \@sort1_option,
+ }
+ ,
+ {
+ name => "sort2",
+ lib => "Sort 2",
+ type => "select",
+ option => \@sort2_option,
+ }
+ ,
+ {
+ name => "dateenrolled",
+ lib => "Date enrolled",
+ type => "date",
+ }
+ ,
+ {
+ name => "dateexpiry",
+ lib => "Date expiry",
+ type => "date",
+ }
+ ,
+ {
+ name => "debarred",
+ lib => "Debarred",
+ type => "date",
+ }
+ ,
+ {
+ name => "debarredcomment",
+ lib => "Debarred comment",
+ type => "text",
+ }
+ ,
+ {
+ name => "borrowernotes",
+ lib => "Borrower Notes",
+ type => "text",
+ }
+ );
+
+ $template->param('patron_attributes_codes', \@patron_attributes_codes);
+ $template->param('patron_attributes_values', \@patron_attributes_values);
+
+ $template->param( fields => \@fields );
+ $template->param( DHTMLcalendar_dateformat => C4::Dates->DHTMLcalendar() );
+}
+
+if ( $op eq 'do' ) {
+
+ my @disabled = $input->param('disable_input');
+ my $infos;
+ for my $field ( qw/surname firstname branchcode categorycode sort1 sort2 dateenrolled dateexpiry debarred debarredcomment borrowernotes/ ) {
+ my $value = $input->param($field);
+ $infos->{$field} = $value if $value;
+ $infos->{$field} = "" if grep { /^$field$/ } @disabled;
+ }
+
+ my @attributes = $input->param('patron_attributes');
+ my @attr_values = $input->param('patron_attributes_value');
+
+ my @errors;
+ my @borrowernumbers = $input->param('borrowernumber');
+ for my $borrowernumber ( @borrowernumbers ) {
+ if ( defined $infos ) {
+ $infos->{borrowernumber} = $borrowernumber;
+ my $success = ModMember(%$infos);
+ push @errors, { error => "can_not_update", borrowernumber => $infos->{borrowernumber} } if not $success;
+ }
+
+ my $borrower_categorycode = GetBorrowerCategorycode $borrowernumber;
+ my $i=0;
+ for ( @attributes ) {
+ my $attribute;
+ $attribute->{code} = $_;
+ $attribute->{attribute} = $attr_values[$i];
+ my $attr_type = C4::Members::AttributeTypes->fetch( $_ );
+ ++$i and next if $attr_type->{category_code} and $attr_type->{category_code} ne $borrower_categorycode;
+ my $valuename = "attr" . $i . "_value";
+ if ( grep { /^$valuename$/ } @disabled ) {
+ eval {
+ DeleteBorrowerAttribute $borrowernumber, $attribute;
+ };
+ push @errors, { error => $@ } if $@;
+ } else {
+ ++$i and next if not $attribute->{attribute};
+ eval {
+ UpdateBorrowerAttribute $borrowernumber, $attribute;
+ };
+ push @errors, { error => $@ } if $@;
+ }
+ $i++;
+ }
+ }
+ $op = "show_results";
+
+ my @borrowers;
+ my $max_nb_attr = 0;
+ for my $borrowernumber ( @borrowernumbers ) {
+ my $borrower = GetBorrowerInfos( borrowernumber => $borrowernumber );
+ if ( $borrower ) {
+ $max_nb_attr = scalar( @{ $borrower->{patron_attributes} } )
+ if scalar( @{ $borrower->{patron_attributes} } ) > $max_nb_attr;
+ push @borrowers, $borrower;
+ }
+ }
+ my @patron_attributes_option;
+ for my $borrower ( @borrowers ) {
+ push @patron_attributes_option, { value => "$_->{code}", lib => $_->{code} } for @{ $borrower->{patron_attributes} };
+ my $length = scalar( @{ $borrower->{patron_attributes} } );
+ push @{ $borrower->{patron_attributes} }, {} for ( $length .. $max_nb_attr - 1);
+ }
+
+ my @attributes_header = ();
+ for ( 1 .. scalar( $max_nb_attr ) ) {
+ push @attributes_header, { attribute => "Attributes $_" };
+ }
+
+ $template->param( borrowers => \@borrowers );
+ $template->param( attributes_header => \@attributes_header );
+
+ $template->param( borrowers => \@borrowers );
+ $template->param( errors => \@errors );
+}
+
+$template->param(
+ op => $op,
+);
+output_html_with_http_headers $input, $cookie, $template->output;
+exit;
+
+sub GetBorrowerInfos {
+ my ( %info ) = @_;
+ my $borrower = GetMember( %info );
+ if ( $borrower ) {
+ $borrower->{branchname} = GetBranchName( $borrower->{branchcode} );
+ for ( qw(dateenrolled dateexpiry debarred) ) {
+ my $userdate = $borrower->{$_};
+ unless ($userdate && $userdate ne "0000-00-00" and $userdate ne "9999-12-31") {
+ $borrower->{$_} = '';
+ next;
+ }
+ $borrower->{$_} = $userdate || '';
+ }
+ $borrower->{category_description} = GetBorrowercategory( $borrower->{categorycode} )->{description};
+ my $attr_loop = C4::Members::Attributes::GetBorrowerAttributes( $borrower->{borrowernumber} );
+ $borrower->{patron_attributes} = $attr_loop;
+ }
+ return $borrower;
+}
+
--
1.7.7.3
More information about the Koha-patches
mailing list