Bug 10565: Add a "Patron List" feature for storing and manipulating collections of...
authorKyle M Hall <kyle@bywatersolutions.com>
Wed, 10 Jul 2013 13:38:40 +0000 (09:38 -0400)
committerGalen Charlton <gmc@esilibrary.com>
Mon, 14 Oct 2013 21:29:44 +0000 (21:29 +0000)
The patron lists feature is somewhat similar to the record lists feature
in that it allows a librarian to create a list of patrons for later
retrieval and manipluation. These lists can then be used with the batch
patron modification tool.

Test Plan:
 0) Apply the patch for Bug 8798
 1) Apply this patch
 2) Run updatedatabase.pl
 3) Access the patron lists feature from Koha's Tools menu.
 4) Create a new list via the "New patron list" button.
 5) For this list, click the "Edit" button, and change the list name.
 6) For this list, click the "Add patrons" button, and search for and
    add some patrons to your list.
 7) For this list select some patrons to remove them.
 8) Try both adding some new patrons, and removing some old patrons
    as a single action.
 9) Click the "Patrons" link on the Koha toolbar
10) Search the patrons, or browse by letter to get patron results
11) Check the checkboxes next to one or more patrons, and add the
    selected patrons to your existing list.
12) Change the "Selected patrons" pulldown to "All resultant patrons"
    and add them to your list.
13) Check the checkboxes next to one or more patrons, and add the
    selected patrons to a new list.
14) Try manipulating a list of patrons using the batch patron
    modification tool.
15) Go back to the Patron Lists feature and delete your lists.
16) Run 'prove t/db_dependent/PatronLists.t'

Signed-off-by: Nora Blake <nblake@masslibsystem.org>
Signed-off-by: Galen Charlton <gmc@esilibrary.com>
19 files changed:
Koha/List/Patron.pm [new file with mode: 0644]
Koha/Schema/Result/PatronList.pm [new file with mode: 0644]
Koha/Schema/Result/PatronListPatron.pm [new file with mode: 0644]
installer/data/mysql/kohastructure.sql
installer/data/mysql/updatedatabase.pl
koha-tmpl/intranet-tmpl/prog/en/modules/members/member.tt
koha-tmpl/intranet-tmpl/prog/en/modules/patron_lists/add-modify.tt [new file with mode: 0644]
koha-tmpl/intranet-tmpl/prog/en/modules/patron_lists/list.tt [new file with mode: 0644]
koha-tmpl/intranet-tmpl/prog/en/modules/patron_lists/lists.tt [new file with mode: 0644]
koha-tmpl/intranet-tmpl/prog/en/modules/tools/modborrowers.tt
koha-tmpl/intranet-tmpl/prog/en/modules/tools/tools-home.tt
members/member.pl
patron_lists/add-modify.pl [new file with mode: 0755]
patron_lists/delete.pl [new file with mode: 0755]
patron_lists/list.pl [new file with mode: 0755]
patron_lists/lists.pl [new file with mode: 0755]
patron_lists/patrons.pl [new file with mode: 0755]
t/db_dependent/PatronLists.t [new file with mode: 0755]
tools/modborrowers.pl

diff --git a/Koha/List/Patron.pm b/Koha/List/Patron.pm
new file mode 100644 (file)
index 0000000..2656b86
--- /dev/null
@@ -0,0 +1,222 @@
+package Koha::List::Patron;
+
+# Copyright 2013 ByWater Solutions
+#
+# 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.
+
+=head1 NAME
+
+Koha::List::Patron - Managment of lists of patrons
+
+=head1 FUNCTIONS
+
+=cut
+
+use Modern::Perl;
+
+use Carp;
+
+use Koha::Database;
+
+use base 'Exporter';
+our @EXPORT = (
+    qw(
+      GetPatronLists
+
+      DelPatronList
+      AddPatronList
+      ModPatronList
+
+      AddPatronsToList
+      DelPatronsFromList
+      )
+);
+
+=head2 GetPatronLists
+
+    my @lists = GetPatronLists( $params );
+
+    Returns an array of lists created by the the given user
+    or the logged in user if none is passed in.
+=cut
+
+sub GetPatronLists {
+    my ($params) = @_;
+
+    $params->{owner} ||= C4::Context->userenv->{'number'};
+
+    unless ( $params->{owner} ) {
+        carp("No owner passed in or defined!");
+        return;
+    }
+
+    delete( $params->{owner} ) if ( C4::Context->IsSuperLibrarian() );
+
+    my $schema = Koha::Database->new()->schema();
+
+    my @patron_lists = $schema->resultset('PatronList')->search($params);
+
+    return wantarray() ? @patron_lists : \@patron_lists;
+}
+
+=head2 DelPatronList
+
+    DelPatronList( { patron_list_id => $list_id [, owner => $owner ] } );
+
+=cut
+
+sub DelPatronList {
+    my ($params) = @_;
+
+    $params->{owner} ||= C4::Context->userenv->{'number'};
+
+    unless ( $params->{patron_list_id} ) {
+        croak("No patron list id passed in!");
+    }
+    unless ( $params->{owner} ) {
+        carp("No owner passed in or defined!");
+        return;
+    }
+
+    delete( $params->{owner} ) if ( C4::Context->IsSuperLibrarian() );
+
+    return Koha::Database->new()->schema()->resultset('PatronList')
+      ->search($params)->single()->delete();
+}
+
+=head2 AddPatronList
+
+    AddPatronList( { name => $name [, owner => $owner ] } );
+
+=cut
+
+sub AddPatronList {
+    my ($params) = @_;
+
+    $params->{owner} ||= C4::Context->userenv->{'number'};
+
+    unless ( $params->{owner} ) {
+        carp("No owner passed in or defined!");
+        return;
+    }
+
+    unless ( $params->{name} ) {
+        carp("No list name passed in!");
+        return;
+    }
+
+    return Koha::Database->new()->schema()->resultset('PatronList')
+      ->create($params);
+}
+
+=head2 ModPatronList
+
+    ModPatronList( { patron_list_id => $id, name => $name [, owner => $owner ] } );
+
+=cut
+
+sub ModPatronList {
+    my ($params) = @_;
+
+    unless ( $params->{patron_list_id} ) {
+        carp("No patron list id passed in!");
+        return;
+    }
+
+    my ($list) = GetPatronLists(
+        {
+            patron_list_id => $params->{patron_list_id},
+            owner          => $params->{owner}
+        }
+    );
+
+    return $list->update($params);
+}
+
+=head2 AddPatronsToList
+
+    AddPatronsToList({ list => $list, cardnumbers => \@cardnumbers });
+
+=cut
+
+sub AddPatronsToList {
+    my ($params) = @_;
+
+    my $list            = $params->{list};
+    my $cardnumbers     = $params->{'cardnumbers'};
+    my $borrowernumbers = $params->{'borrowernumbers'};
+
+    return unless ( $list && ( $cardnumbers || $borrowernumbers ) );
+
+    my @borrowernumbers;
+
+    if ($cardnumbers) {
+        @borrowernumbers =
+          Koha::Database->new()->schema()->resultset('Borrower')->search(
+            { cardnumber => { 'IN' => $cardnumbers } },
+            { columns    => [qw/ borrowernumber /] }
+          )->get_column('borrowernumber')->all();
+    }
+    else {
+        @borrowernumbers = @$borrowernumbers;
+    }
+
+    my $patron_list_id = $list->patron_list_id();
+
+    my $plp_rs = Koha::Database->new()->schema()->resultset('PatronListPatron');
+
+    my @results;
+    foreach my $borrowernumber (@borrowernumbers) {
+        my $result = $plp_rs->update_or_create(
+            {
+                patron_list_id => $patron_list_id,
+                borrowernumber => $borrowernumber
+            }
+        );
+        push( @results, $result );
+    }
+
+    return wantarray() ? @results : \@results;
+}
+
+=head2 DelPatronsFromList
+
+    DelPatronsFromList({ list => $list, patron_list_patrons => \@patron_list_patron_ids });
+
+=cut
+
+sub DelPatronsFromList {
+    my ($params) = @_;
+
+    my $list                = $params->{list};
+    my $patron_list_patrons = $params->{patron_list_patrons};
+
+    return unless ( $list && $patron_list_patrons );
+
+    return Koha::Database->new()->schema()->resultset('PatronListPatron')
+      ->search( { patron_list_patron_id => { 'IN' => $patron_list_patrons } } )
+      ->delete();
+}
+
+=head1 AUTHOR
+
+Kyle M Hall, E<lt>kyle@bywatersolutions.comE<gt>
+
+=cut
+
+1;
+
+__END__
diff --git a/Koha/Schema/Result/PatronList.pm b/Koha/Schema/Result/PatronList.pm
new file mode 100644 (file)
index 0000000..8d1d9dc
--- /dev/null
@@ -0,0 +1,90 @@
+package Koha::Schema::Result::PatronList;
+
+# Created by DBIx::Class::Schema::Loader
+# DO NOT MODIFY THE FIRST PART OF THIS FILE
+
+use strict;
+use warnings;
+
+use base 'DBIx::Class::Core';
+
+
+=head1 NAME
+
+Koha::Schema::Result::PatronList
+
+=cut
+
+__PACKAGE__->table("patron_lists");
+
+=head1 ACCESSORS
+
+=head2 patron_list_id
+
+  data_type: 'integer'
+  is_auto_increment: 1
+  is_nullable: 0
+
+=head2 name
+
+  data_type: 'varchar'
+  is_nullable: 0
+  size: 255
+
+=head2 owner
+
+  data_type: 'integer'
+  is_foreign_key: 1
+  is_nullable: 0
+
+=cut
+
+__PACKAGE__->add_columns(
+  "patron_list_id",
+  { data_type => "integer", is_auto_increment => 1, is_nullable => 0 },
+  "name",
+  { data_type => "varchar", is_nullable => 0, size => 255 },
+  "owner",
+  { data_type => "integer", is_foreign_key => 1, is_nullable => 0 },
+);
+__PACKAGE__->set_primary_key("patron_list_id");
+
+=head1 RELATIONS
+
+=head2 patron_list_patrons
+
+Type: has_many
+
+Related object: L<Koha::Schema::Result::PatronListPatron>
+
+=cut
+
+__PACKAGE__->has_many(
+  "patron_list_patrons",
+  "Koha::Schema::Result::PatronListPatron",
+  { "foreign.patron_list_id" => "self.patron_list_id" },
+  { cascade_copy => 0, cascade_delete => 0 },
+);
+
+=head2 owner
+
+Type: belongs_to
+
+Related object: L<Koha::Schema::Result::Borrower>
+
+=cut
+
+__PACKAGE__->belongs_to(
+  "owner",
+  "Koha::Schema::Result::Borrower",
+  { borrowernumber => "owner" },
+  { on_delete => "CASCADE", on_update => "CASCADE" },
+);
+
+
+# Created by DBIx::Class::Schema::Loader v0.07000 @ 2013-07-10 10:39:50
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:XegvNUkfR/cYwxlLLX3h3A
+
+
+# You can replace this text with custom content, and it will be preserved on regeneration
+1;
diff --git a/Koha/Schema/Result/PatronListPatron.pm b/Koha/Schema/Result/PatronListPatron.pm
new file mode 100644 (file)
index 0000000..cb7ee6b
--- /dev/null
@@ -0,0 +1,90 @@
+package Koha::Schema::Result::PatronListPatron;
+
+# Created by DBIx::Class::Schema::Loader
+# DO NOT MODIFY THE FIRST PART OF THIS FILE
+
+use strict;
+use warnings;
+
+use base 'DBIx::Class::Core';
+
+
+=head1 NAME
+
+Koha::Schema::Result::PatronListPatron
+
+=cut
+
+__PACKAGE__->table("patron_list_patrons");
+
+=head1 ACCESSORS
+
+=head2 patron_list_patron_id
+
+  data_type: 'integer'
+  is_auto_increment: 1
+  is_nullable: 0
+
+=head2 patron_list_id
+
+  data_type: 'integer'
+  is_foreign_key: 1
+  is_nullable: 0
+
+=head2 borrowernumber
+
+  data_type: 'integer'
+  is_foreign_key: 1
+  is_nullable: 0
+
+=cut
+
+__PACKAGE__->add_columns(
+  "patron_list_patron_id",
+  { data_type => "integer", is_auto_increment => 1, is_nullable => 0 },
+  "patron_list_id",
+  { data_type => "integer", is_foreign_key => 1, is_nullable => 0 },
+  "borrowernumber",
+  { data_type => "integer", is_foreign_key => 1, is_nullable => 0 },
+);
+__PACKAGE__->set_primary_key("patron_list_patron_id");
+
+=head1 RELATIONS
+
+=head2 borrowernumber
+
+Type: belongs_to
+
+Related object: L<Koha::Schema::Result::Borrower>
+
+=cut
+
+__PACKAGE__->belongs_to(
+  "borrowernumber",
+  "Koha::Schema::Result::Borrower",
+  { borrowernumber => "borrowernumber" },
+  { on_delete => "CASCADE", on_update => "CASCADE" },
+);
+
+=head2 patron_list
+
+Type: belongs_to
+
+Related object: L<Koha::Schema::Result::PatronList>
+
+=cut
+
+__PACKAGE__->belongs_to(
+  "patron_list",
+  "Koha::Schema::Result::PatronList",
+  { patron_list_id => "patron_list_id" },
+  { on_delete => "CASCADE", on_update => "CASCADE" },
+);
+
+
+# Created by DBIx::Class::Schema::Loader v0.07000 @ 2013-07-10 10:39:50
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:c+znpWBlv6I+yi1EuGUKrQ
+
+
+# You can replace this text with custom content, and it will be preserved on regeneration
+1;
index 024039f..e1e3e96 100644 (file)
@@ -3235,6 +3235,47 @@ CREATE TABLE IF NOT EXISTS plugin_data (
   PRIMARY KEY (plugin_class,plugin_key)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 
+--
+-- Table structure for table `patron_lists`
+--
+
+DROP TABLE IF EXISTS patron_lists;
+CREATE TABLE patron_lists (
+  patron_list_id int(11) NOT NULL AUTO_INCREMENT, -- unique identifier
+  name varchar(255) CHARACTER SET utf8 NOT NULL,  -- the list's name
+  owner int(11) NOT NULL,                         -- borrowernumber of the list creator
+  PRIMARY KEY (patron_list_id),
+  KEY owner (owner)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+--
+-- Constraints for table `patron_lists`
+--
+ALTER TABLE `patron_lists`
+  ADD CONSTRAINT patron_lists_ibfk_1 FOREIGN KEY (`owner`) REFERENCES borrowers (borrowernumber) ON DELETE CASCADE ON UPDATE CASCADE;
+
+--
+-- Table structure for table 'patron_list_patrons'
+--
+
+DROP TABLE IF EXISTS patron_list_patrons;
+CREATE TABLE patron_list_patrons (
+  patron_list_patron_id int(11) NOT NULL AUTO_INCREMENT, -- unique identifier
+  patron_list_id int(11) NOT NULL,                       -- the list this entry is part of
+  borrowernumber int(11) NOT NULL,                       -- the borrower that is part of this list
+  PRIMARY KEY (patron_list_patron_id),
+  KEY patron_list_id (patron_list_id),
+  KEY borrowernumber (borrowernumber)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+--
+-- Constraints for table `patron_list_patrons`
+--
+ALTER TABLE `patron_list_patrons`
+  ADD CONSTRAINT patron_list_patrons_ibfk_1 FOREIGN KEY (patron_list_id) REFERENCES patron_lists (patron_list_id) ON DELETE CASCADE ON UPDATE CASCADE,
+  ADD CONSTRAINT patron_list_patrons_ibfk_2 FOREIGN KEY (borrowernumber) REFERENCES borrowers (borrowernumber) ON DELETE CASCADE ON UPDATE CASCADE;
+
+
 /*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
 /*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
 /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
index 5c020cb..bf7d654 100755 (executable)
@@ -7262,6 +7262,44 @@ if ( CheckVersion($DBversion) ) {
     $dbh->{RaiseError} = 0;
 }
 
+$DBversion = "3.13.00.XXX";
+if ( CheckVersion($DBversion) ) {
+
+    $dbh->do(q{
+        CREATE TABLE IF NOT EXISTS `patron_lists` (
+          patron_list_id int(11) NOT NULL AUTO_INCREMENT,
+          name varchar(255) CHARACTER SET utf8 NOT NULL,
+          owner int(11) NOT NULL,
+          PRIMARY KEY (patron_list_id),
+          KEY owner (owner)
+        ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+    });
+
+    $dbh->do(q{
+        ALTER TABLE `patron_lists`
+          ADD CONSTRAINT patron_lists_ibfk_1 FOREIGN KEY (`owner`) REFERENCES borrowers (borrowernumber) ON DELETE CASCADE ON UPDATE CASCADE;
+    });
+
+    $dbh->do(q{
+        CREATE TABLE patron_list_patrons (
+          patron_list_patron_id int(11) NOT NULL AUTO_INCREMENT,
+          patron_list_id int(11) NOT NULL,
+          borrowernumber int(11) NOT NULL,
+          PRIMARY KEY (patron_list_patron_id),
+          KEY patron_list_id (patron_list_id),
+          KEY borrowernumber (borrowernumber)
+        ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+    });
+
+    $dbh->do(q{
+        ALTER TABLE `patron_list_patrons`
+          ADD CONSTRAINT patron_list_patrons_ibfk_1 FOREIGN KEY (patron_list_id) REFERENCES patron_lists (patron_list_id) ON DELETE CASCADE ON UPDATE CASCADE,
+          ADD CONSTRAINT patron_list_patrons_ibfk_2 FOREIGN KEY (borrowernumber) REFERENCES borrowers (borrowernumber) ON DELETE CASCADE ON UPDATE CASCADE;
+    });
+
+    print "Upgrade to $DBversion done (Bug 10565 - Add a 'Patron List' feature for storing and manipulating collections of patrons)\n";
+    SetVersion($DBversion);
+}
 
 =head1 FUNCTIONS
 
index f44754a..cefbfd5 100644 (file)
@@ -1,6 +1,73 @@
 [% INCLUDE 'doc-head-open.inc' %]
 <title>Koha &rsaquo; Patrons [% IF ( searching ) %]&rsaquo; Search results[% END %]</title>
 [% INCLUDE 'doc-head-close.inc' %]
+
+<script type="text/javascript">
+//<![CDATA[
+$(document).ready(function() {
+    $('#add_to_patron_list_submit').attr('disabled', 'disabled');
+    $('#new_patron_list').hide();
+
+    $('#add_to_patron_list').change(function() {
+        var value = $('#add_to_patron_list').val();
+        if ( value == 'new' ) {
+            $('#new_patron_list').val('')
+            $('#new_patron_list').show();
+            $('#new_patron_list').focus();
+        } else if ( value ) {
+            $('#new_patron_list').hide();
+            $('#add_to_patron_list_submit').removeAttr('disabled');
+        } else {
+            $('#new_patron_list').hide();
+            $('#add_to_patron_list_submit').attr('disabled', 'disabled');
+        }
+
+    });
+
+    $('#new_patron_list').on('input', function() {
+        if ( $('#new_patron_list').val() ) {
+            $('#add_to_patron_list_submit').removeAttr('disabled');
+        } else {
+            $('#add_to_patron_list_submit').attr('disabled', 'disabled');
+        }
+    });
+});
+
+function CheckForm() {
+    if ( $('#add_to_patron_list').val() == 'new' ) {
+        if ( $('#new_patron_list').val() ) {
+            var exists = false;
+            $("#add_to_patron_list option").each(function() {
+                if ( $(this).text() == $('#new_patron_list').val() ) {
+                    exists = true;
+                    return false;
+                }
+            });
+
+            if ( exists ) {
+                alert( _("You already have a list with that name!") );
+                return false;
+            }
+        } else {
+            alert( _("You must give your new patron list a name!") );
+            return false;
+        }
+    }
+
+    if ( $('#add_to_patron_list_which').val() == 'all' ) {
+        return confirm( _("Are you sure you want to add the entire set of patron results to this list ( including results on other pages )?") );
+    } else {
+         if ( $("#add-patrons-to-list-form input:checkbox:checked").length == 0 ) {
+             alert( _("You have not selected any patrons to add to a list!") );
+             return false;
+         }
+    }
+
+    return true;
+}
+//]]>
+</script>
+
 </head>
 <body id="pat_member" class="pat">
 [% INCLUDE 'header.inc' %]
                    <div class="yui-b">
                                <div class="yui-g">
 
+                [% IF patron_list %]
+                    <div class="dialog alert">
+                        Added [% patrons_added_to_list.size %] patrons to <a href="/cgi-bin/koha/patron_lists/list.pl?patron_list_id=[% patron_list.patron_list_id %]">[% patron_list.name %]</a>.
+                    </div>
+                [% END %]
+
                                [% INCLUDE 'patron-toolbar.inc' %]
 
        [% IF ( no_add ) %]<div class="dialog alert"><h3>Cannot add patron</h3>
                         </div>
                     [% END %]
 
-                                               [% IF ( resultsloop ) %]
-                                               <div id="searchheader"> <h3>Results [% from %] to [% to %] of [% numresults %] found for [% IF ( member ) %]'<span class="ex">[% member %]</span>'[% END %][% IF ( surname ) %]'<span class="ex">[% surname %]</span>'[% END %]</h3></div>
+                    [% IF ( resultsloop ) %]
+                    <form id="add-patrons-to-list-form" method="post" action="member.pl" onsubmit="return CheckForm()">
+                        <div id="searchheader">
+                            <h3>Results [% from %] to [% to %] of [% numresults %] found for [% IF ( member ) %]'<span class="ex">[% member %]</span>'[% END %][% IF ( surname ) %]'<span class="ex">[% surname %]</span>'[% END %]</h3>
+
+                            <div>
+                                <a href="javascript:void(0)" onclick="$('.selection').prop('checked', true)">Select all</a>
+                                |
+                                <a href="javascript:void(0)" onclick="$('.selection').prop('checked', false)">Clear all</a>
+                                |
+                                <span>
+                                    <label for="add_to_patron_list_which">Add:</label>
+                                    <select id="add_to_patron_list_which" name="add_to_patron_list_which">
+                                        <option value="selected">Selected patrons</option>
+                                        <option value="all">All resultant patrons</option>
+                                    </select>
+
+                                    <label for="add_to_patron_list">to:</label>
+                                    <select id="add_to_patron_list" name="add_to_patron_list">
+                                        <option value=""></option>
+                                        [% IF patron_lists %]
+                                            <optgroup label="Patron lists:">
+                                                [% FOREACH pl IN patron_lists %]
+                                                    <option value="[% pl.patron_list_id %]">[% pl.name %]</option>
+                                                [% END %]
+                                            </optgroup>
+                                        [% END %]
+
+                                        <option value="new">[ New list ]</option>
+                                    </select>
+
+                                    <input type="text" id="new_patron_list" name="new_patron_list" id="new_patron_list" />
+
+                                    [% FOREACH key IN search_parameters.keys %]
+                                        <input type="hidden" name="[% key %]" value="[% search_parameters.$key %]" />
+                                    [% END %]
+
+                                    <input id="add_to_patron_list_submit" type="submit" class="submit" value="Save">
+                                </span>
+                            </div>
+                        </div>
                                                <div class="searchresults">
 
                                                        <table id="memberresultst">
                                                        <thead>
                                                        <tr>
+                            <th>&nbsp</th>
                                                        <th>Card</th>
                                                        <th>Name</th>
                                                        <th>Cat</th>
                                                        <tr>
                                                        [% END %]
                                                        [% END %]
+                            <td><input type="checkbox" class="selection" name="borrowernumber" value="[% resultsloo.borrowernumber %]" /></td>
                                                        <td>[% resultsloo.cardnumber %]</td>
                             <td style="white-space: nowrap;">
                             <a href="/cgi-bin/koha/members/moremember.pl?borrowernumber=[% resultsloo.borrowernumber %]">
                                                        </table>
                                                        <div class="pages">[% IF ( multipage ) %][% paginationbar %][% END %]</div>
                                                </div>
-                                               [% ELSE %]
-                                               [% IF ( searching ) %]
-                                               <div class="dialog alert">No results found</div>
-                                               [% END %]
-                                               [% END %]
+                    </form>
+                    [% ELSE %]
+                        [% IF ( searching ) %]
+                            <div class="dialog alert">No results found</div>
+                        [% END %]
+                    [% END %]
 
                                        </div>
                                </div>
diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/patron_lists/add-modify.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/patron_lists/add-modify.tt
new file mode 100644 (file)
index 0000000..5ec0cd1
--- /dev/null
@@ -0,0 +1,56 @@
+[% INCLUDE 'doc-head-open.inc' %]
+<title>Koha &rsaquo; Patron lists &rsaquo; New list</title>
+[% INCLUDE 'doc-head-close.inc' %]
+
+<script type="text/javascript">
+//<![CDATA[
+
+function CheckForm() {
+  if ( !$("#list-name").val() ) {
+    alert( _("Name is a required field!")  );
+    return false;
+  }
+
+  return true;
+}
+
+//]]>
+</script>
+
+</head>
+
+<body>
+[% INCLUDE 'header.inc' %]
+[% INCLUDE 'cat-search.inc' %]
+<div id="breadcrumbs"><a href="/cgi-bin/koha/mainpage.pl">Home</a> &rsaquo; <a href="lists.pl">Patron lists</a> &rsaquo; Add / modify list</div>
+
+
+<div class="yui-t7">
+    <div class="yui-main">
+        <h1>New patron list</h1>
+
+        <form method="post" onsubmit="return CheckForm()">
+            <fieldset class="rows">
+
+                <legend>Create a new patron list</legend>
+
+                <ol>
+                    <li>
+                        <label class="required" for="name">Name:</label>
+                        <input id="list-name" name="name" type="text" value="[% list.name %]" />
+                    </li>
+
+                    <li>
+                        <span class="label">Owner: </span>[% loggedinusername %]
+                    </li>
+                </ol>
+
+            </fieldset>
+
+            <input type="hidden" name="patron_list_id" value="[% list.patron_list_id %]" />
+            <input type="submit" class="btn" value="Save" />
+            <a href="lists.pl" class="cancel">Cancel</a>
+        </form>
+    </div>
+</div>
+[% INCLUDE 'intranet-bottom.inc' %]
diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/patron_lists/list.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/patron_lists/list.tt
new file mode 100644 (file)
index 0000000..ebcd71f
--- /dev/null
@@ -0,0 +1,132 @@
+[% USE KohaDates %]
+[% INCLUDE 'doc-head-open.inc' %]
+<title>Koha &rsaquo; Patron lists &rsaquo; New list</title>
+[% INCLUDE 'doc-head-close.inc' %]
+
+<link rel="stylesheet" type="text/css" href="[% themelang %]/css/datatables.css" />
+<script type="text/javascript" src="[% themelang %]/lib/jquery/plugins/jquery.dataTables.min.js"></script>
+[% INCLUDE 'datatables-strings.inc' %]
+<script type="text/javascript" src="[% themelang %]/js/datatables.js"></script>
+
+<script type="text/javascript">
+//<![CDATA[
+$(document).ready(function() {
+    $('#patrons_to_add_fieldset').hide();
+
+    $('#patron-list-table').dataTable($.extend(true, {}, dataTablesDefaults));
+
+    $( "#find_patron" ).autocomplete({
+        source: "/cgi-bin/koha/circ/ysearch.pl",
+        minLength: 3,
+        select: function( event, ui ) {
+            AddPatron( ui.item.firstname + " " + ui.item.surname, ui.item.cardnumber );
+            return false;
+        }
+    })
+    .data( "autocomplete" )._renderItem = function( ul, item ) {
+        return $( "<li></li>" )
+        .data( "item.autocomplete", item )
+        .append( "<a>" + item.surname + ", " + item.firstname + " (" + item.cardnumber + ") <small>" + item.address + " " + item.city + " " + item.zipcode + " " + item.country + "</small></a>" )
+        .appendTo( ul );
+    };
+});
+
+function AddPatron( name, cardnumber ) {
+    div = "<div id='borrower_" + cardnumber + "'>" + name + " ( <a href='javascript:void()' onclick='RemovePatron(" + cardnumber + ");'> Remove </a> ) <input type='hidden' name='patrons_to_add' value='" + cardnumber + "' /></div>";
+    $('#patrons_to_add').append( div );
+
+    $('#find_patron').val('').focus();
+
+    $('#patrons_to_add_fieldset').show( 800 );
+}
+
+function RemovePatron( cardnumber ) {
+    $( '#borrower_' + cardnumber ).remove();
+
+    if ( ! $('#patrons_to_add').html() ) {
+        $('#patrons_to_add_fieldset').hide( 800 );
+    }
+}
+//]]>
+</script>
+
+</head>
+
+<body>
+[% INCLUDE 'header.inc' %]
+[% INCLUDE 'cat-search.inc' %]
+<div id="breadcrumbs"><a href="/cgi-bin/koha/mainpage.pl">Home</a> &rsaquo; <a href="lists.pl">Patron lists</a> &rsaquo; Add patrons</div>
+
+<div class="yui-t7">
+    <div class="yui-main">
+        <h1>[% list.name %]</h1>
+
+        <form action="list.pl" method="post">
+            <fieldset>
+                <legend>Add patrons</legend>
+
+                    <label for="find_patron">Patron search: </label>
+                    <input autocomplete="off" id="find_patron" type="text" style="width:150px" class="noEnterSubmit" />
+                    <div id="find_patron_container"></div>
+
+                <fieldset id="patrons_to_add_fieldset">
+                    <legend>Patrons to be added</legend>
+                    <div id="patrons_to_add"></div>
+                </fieldset>
+
+            </fieldset>
+
+            <p>
+                <input type="submit" class="btn" value="Update" />
+                <a href="lists.pl" class="cancel">Cancel</a>
+            </p>
+
+            <table id="patron-list-table">
+                <thead>
+                    <tr>
+                        <th><i title="Remove patron from list" class="icon-trash"></i></th>
+                        <th>Card</th>
+                        <th>Firstname</th>
+                        <th>Surname</th>
+                        <th>Address</th>
+                        <th>Category</th>
+                        <th>Library</th>
+                        <th>Expires on</th>
+                        <th>Circ notes</th>
+                    </tr>
+                </thead>
+
+                <tbody>
+                    [% FOREACH p IN list.patron_list_patrons %]
+                        <tr>
+                            <td><input type="checkbox" name="patrons_to_remove" value="[% p.patron_list_patron_id %]" /></td>
+                            <td>
+                                <a href="/cgi-bin/koha/members/moremember.pl?borrowernumber=[% p.borrowernumber.borrowernumber %]">
+                                    [% p.borrowernumber.cardnumber %]
+                                </a>
+                            </td>
+                            <td>[% p.borrowernumber.firstname %]</td>
+                            <td>[% p.borrowernumber.surname %]</td>
+                            <td>
+                                [% p.borrowernumber.address %]
+                                [% p.borrowernumber.address2 %]
+                                [% p.borrowernumber.city %]
+                                [% p.borrowernumber.state %]
+                                [% p.borrowernumber.country %]
+                            </td>
+                            <td>[% p.borrowernumber.categorycode.description %] ([% p.borrowernumber.categorycode.categorycode %])</td>
+                            <td>[% p.borrowernumber.branchcode.branchname %]</td>
+                            <td>[% p.borrowernumber.dateexpiry | $KohaDates %]</td>
+                            <td>[% p.borrowernumber.borrowernotes %]</td>
+                        </tr>
+                    [% END %]
+                </tbody>
+            </table>
+
+            <input type="hidden" name="patron_list_id" value="[% list.patron_list_id %]" />
+            <input type="submit" class="btn" value="Update" />
+            <a href="lists.pl" class="cancel">Cancel</a>
+        </form>
+    </div>
+</div>
+[% INCLUDE 'intranet-bottom.inc' %]
diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/patron_lists/lists.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/patron_lists/lists.tt
new file mode 100644 (file)
index 0000000..972f36d
--- /dev/null
@@ -0,0 +1,74 @@
+[% INCLUDE 'doc-head-open.inc' %]
+<title>Koha &rsaquo; Patron lists</title>
+[% INCLUDE 'doc-head-close.inc' %]
+
+<link rel="stylesheet" type="text/css" href="[% themelang %]/css/datatables.css" />
+<script type="text/javascript" src="[% themelang %]/lib/jquery/plugins/jquery.dataTables.min.js"></script>
+[% INCLUDE 'datatables-strings.inc' %]
+<script type="text/javascript" src="[% themelang %]/js/datatables.js"></script>
+
+<script type="text/javascript">
+//<![CDATA[
+    $(document).ready(function() {
+        $('#patron-lists-table').dataTable($.extend(true, {}, dataTablesDefaults));
+    });
+
+    function ConfirmDelete( list ) {
+        return confirm( _("Are you sure you want to delete the list ") + list + "?" );
+    }
+//]]>
+</script>
+
+</head>
+
+<body>
+[% INCLUDE 'header.inc' %]
+[% INCLUDE 'cat-search.inc' %]
+<div id="breadcrumbs"><a href="/cgi-bin/koha/mainpage.pl">Home</a> &rsaquo; Patron lists</div>
+
+<div class="yui-t7">
+    <div class="yui-main">
+        <h1>Your patron lists</h1>
+
+        <div class="btn-group">
+            <a class="btn btn-small" href="add-modify.pl"><i class="icon-plus"></i> New patron list</a>
+        </div>
+
+        <table id="patron-lists-table">
+            <thead>
+                <tr>
+                    <th>Name</th>
+                    <th>Patrons in list</th>
+                    <th>&nbsp;</th>
+                    <th>&nbsp;</th>
+                    <th>&nbsp;</th>
+                </tr>
+            </thead>
+
+            <tbody>
+                [% FOREACH l IN lists %]
+                    <tr>
+                        <td>[% l.name %]</td>
+                        <td>[% l.patron_list_patrons.size || 0 %]</td>
+                        <td>
+                            <a class="btn" href="list.pl?patron_list_id=[% l.patron_list_id %]">
+                                <i class="icon-plus-sign"></i> Add patrons <i class="icon-user"></i>
+                            </a>
+                        </td>
+                        <td>
+                            <a class="btn" href="add-modify.pl?patron_list_id=[% l.patron_list_id %]">
+                                <i class="icon-edit"></i> Edit
+                            </a>
+                        </td>
+                        <td>
+                            <a class="btn" href="delete.pl?patron_list_id=[% l.patron_list_id %]" onclick='return ConfirmDelete("[% l.name | html %]")'>
+                                <i class="icon-trash"></i> Delete
+                            </a>
+                        </td>
+                    </tr>
+                [% END %]
+            </tbody>
+        </table>
+    </div>
+</div>
+[% INCLUDE 'intranet-bottom.inc' %]
index 957d559..b4b899c 100644 (file)
                 <h1>Batch patron modification</h1>
                 <form method="post" enctype="multipart/form-data" action="/cgi-bin/koha/tools/modborrowers.pl">
                     <fieldset class="rows">
-                    <legend>Use a file</legend>
-                    <ol>
-                    <li><label for="uploadfile">File: </label> <input type="file" id="uploadfile" name="uploadfile" /></li>
-                    </ol>
+                        <legend>Use a file</legend>
+                        <ol>
+                            <li><label for="uploadfile">File: </label> <input type="file" id="uploadfile" name="uploadfile" /></li>
+                        </ol>
                     </fieldset>
+
+                    [% IF patron_lists %]
+                    <fieldset class="rows">
+                        <legend>Or use a patron list</legend>
+                        <ol>
+                            <li>
+                                <label for="patron_list_id">Patron list: </label>
+                                <select id="patron_list_id" name="patron_list_id">
+                                    <option value=""></option>
+                                    [% FOREACH pl IN patron_lists %]
+                                        <option value="[% pl.patron_list_id %]">[% pl.name %]</option>
+                                    [% END %]
+                                </select>
+                            </li>
+                        </ol>
+                    </fieldset>
+                    [% END %]
+
                     <fieldset class="rows">
                         <legend>Or list cardnumbers one by one</legend>
                         <ol>
index 4fff721..b72be1e 100644 (file)
 <div class="yui-u first">
 <h3>Patrons and circulation</h3>
 <dl>
-    [% IF ( CAN_user_tools_moderate_comments ) %]
+    <dt><a href="/cgi-bin/koha/patron_lists/lists.pl">Patron lists</a>
+    <dd>Manage lists of patrons.</dd>
+
+[% IF ( CAN_user_tools_moderate_comments ) %]
     <dt><a href="/cgi-bin/koha/reviews/reviewswaiting.pl">Comments</a> [% IF ( pendingcomments ) %]<span class="holdcount"><a href="/cgi-bin/koha/reviews/reviewswaiting.pl">[% pendingcomments %]</a></span>[% END %]</dt>
        <dd>Moderate patron comments. </dd>
     [% END %]
index 78b762f..afab693 100755 (executable)
@@ -32,6 +32,7 @@ use C4::Branch;
 use C4::Category;
 use Koha::DateUtils;
 use File::Basename;
+use Koha::List::Patron;
 
 my $input = new CGI;
 my $quicksearch = $input->param('quicksearch') || '';
@@ -48,6 +49,15 @@ my ($template, $loggedinuser, $cookie)
 
 my $theme = $input->param('theme') || "default";
 
+my $add_to_patron_list       = $input->param('add_to_patron_list');
+my $add_to_patron_list_which = $input->param('add_to_patron_list_which');
+my $new_patron_list          = $input->param('new_patron_list');
+my @borrowernumbers          = $input->param('borrowernumber');
+$input->delete(
+    'add_to_patron_list', 'add_to_patron_list_which',
+    'new_patron_list',    'borrowernumber',
+);
+
 my $patron = $input->Vars;
 foreach (keys %$patron){
        delete $$patron{$_} unless($$patron{$_});
@@ -119,6 +129,29 @@ if ($member || keys %$patron) {
     ($results) = Search( $member || $patron, \@orderby, undef, undef, \@searchfields, $search_scope );
 }
 
+if ($add_to_patron_list) {
+    my $patron_list;
+
+    if ( $add_to_patron_list eq 'new' ) {
+        $patron_list = AddPatronList( { name => $new_patron_list } );
+    }
+    else {
+        $patron_list =
+          [ GetPatronLists( { patron_list_id => $add_to_patron_list } ) ]->[0];
+    }
+
+    if ( $add_to_patron_list_which eq 'all' ) {
+        @borrowernumbers = map { $_->{borrowernumber} } @$results;
+    }
+
+    my @patrons_added_to_list = AddPatronsToList( { list => $patron_list, borrowernumbers => \@borrowernumbers } );
+
+    $template->param(
+        patron_list           => $patron_list,
+        patrons_added_to_list => \@patrons_added_to_list,
+      )
+}
+
 if ($results) {
        for my $field ('categorycode','branchcode'){
                next unless ($patron->{$field});
@@ -176,24 +209,29 @@ my $base_url =
 my @letters = map { {letter => $_} } ( 'A' .. 'Z');
 
 $template->param(
-    letters => \@letters,
+    %$patron,
+    letters       => \@letters,
     paginationbar => pagination_bar(
         $base_url,
-        int( $count / $resultsperpage ) + ($count % $resultsperpage ? 1 : 0),
-        $startfrom, 'startfrom'
+        int( $count / $resultsperpage ) + ( $count % $resultsperpage ? 1 : 0 ),
+        $startfrom,
+        'startfrom'
     ),
-    startfrom => $startfrom,
-    from      => ($startfrom-1)*$resultsperpage+1,  
-    to        => $to,
-    multipage => ($count != $to || $startfrom!=1),
-    advsearch => ($$patron{categorycode} || $$patron{branchcode}),
-    branchloop=>\@branchloop,
-    categories=>\@categories,
-    searching       => "1",
-               actionname              =>basename($0),
-               %$patron,
-        numresults      => $count,
-        resultsloop     => \@resultsdata,
-            );
+    startfrom    => $startfrom,
+    from         => ( $startfrom - 1 ) * $resultsperpage + 1,
+    to           => $to,
+    multipage    => ( $count != $to || $startfrom != 1 ),
+    advsearch    => ( $$patron{categorycode} || $$patron{branchcode} ),
+    branchloop   => \@branchloop,
+    categories   => \@categories,
+    searching    => "1",
+    actionname   => basename($0),
+    numresults   => $count,
+    resultsloop  => \@resultsdata,
+    results_per_page => $resultsperpage,
+    member => $member,
+    search_parameters => \%parameters,
+    patron_lists => [ GetPatronLists() ],
+);
 
 output_html_with_http_headers $input, $cookie, $template->output;
diff --git a/patron_lists/add-modify.pl b/patron_lists/add-modify.pl
new file mode 100755 (executable)
index 0000000..ed67932
--- /dev/null
@@ -0,0 +1,61 @@
+#!/usr/bin/perl
+
+# Copyright 2013 ByWater Solutions
+#
+# 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::Output;
+use Koha::List::Patron;
+
+my $cgi = new CGI;
+
+my ( $template, $loggedinuser, $cookie ) = get_template_and_user(
+    {
+        template_name   => "patron_lists/add-modify.tt",
+        query           => $cgi,
+        type            => "intranet",
+        authnotrequired => 1,
+    }
+);
+
+my $id   = $cgi->param('patron_list_id');
+my $name = $cgi->param('name');
+
+if ($id) {
+    my ($list) = GetPatronLists( { patron_list_id => $id } );
+    $template->param( list => $list );
+}
+
+if ($name) {
+    if ($id) {
+        ModPatronList( { patron_list_id => $id, name => $name } );
+        print $cgi->redirect('lists.pl');
+    }
+    else {
+        my $list = AddPatronList( { name => $name } );
+        print $cgi->redirect(
+            "list.pl?patron_list_id=" . $list->patron_list_id() );
+    }
+
+    exit;
+}
+
+output_html_with_http_headers( $cgi, $cookie, $template->output );
diff --git a/patron_lists/delete.pl b/patron_lists/delete.pl
new file mode 100755 (executable)
index 0000000..dfa698b
--- /dev/null
@@ -0,0 +1,43 @@
+#!/usr/bin/perl
+
+# Copyright 2013 ByWater Solutions
+#
+# 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::Output;
+use Koha::List::Patron;
+
+my $cgi = new CGI;
+
+my ( $template, $loggedinuser, $cookie ) = get_template_and_user(
+    {
+        template_name   => "patron_lists/lists.tt",
+        query           => $cgi,
+        type            => "intranet",
+        authnotrequired => 1,
+    }
+);
+
+my $id = $cgi->param('patron_list_id');
+
+DelPatronList( { patron_list_id => $id } );
+
+print $cgi->redirect('lists.pl');
diff --git a/patron_lists/list.pl b/patron_lists/list.pl
new file mode 100755 (executable)
index 0000000..2723e71
--- /dev/null
@@ -0,0 +1,54 @@
+#!/usr/bin/perl
+
+# Copyright 2013 ByWater Solutions
+#
+# 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::Output;
+use Koha::List::Patron;
+
+my $cgi = new CGI;
+
+my ( $template, $logged_in_user, $cookie ) = get_template_and_user(
+    {
+        template_name   => "patron_lists/list.tt",
+        query           => $cgi,
+        type            => "intranet",
+        authnotrequired => 1,
+    }
+);
+
+my ($list) =
+  GetPatronLists( { patron_list_id => $cgi->param('patron_list_id') } );
+
+my @patrons_to_add = $cgi->param('patrons_to_add');
+if (@patrons_to_add) {
+    AddPatronsToList( { list => $list, cardnumbers => \@patrons_to_add } );
+}
+
+my @patrons_to_remove = $cgi->param('patrons_to_remove');
+if (@patrons_to_remove) {
+    DelPatronsFromList( { list => $list, patron_list_patrons => \@patrons_to_remove } );
+}
+
+$template->param( list => $list );
+
+output_html_with_http_headers( $cgi, $cookie, $template->output );
diff --git a/patron_lists/lists.pl b/patron_lists/lists.pl
new file mode 100755 (executable)
index 0000000..0244511
--- /dev/null
@@ -0,0 +1,43 @@
+#!/usr/bin/perl
+
+# Copyright 2013 ByWater Solutions
+#
+# 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::Output;
+use Koha::List::Patron;
+
+my $cgi = new CGI;
+
+my ( $template, $loggedinuser, $cookie ) = get_template_and_user(
+    {
+        template_name   => "patron_lists/lists.tt",
+        query           => $cgi,
+        type            => "intranet",
+        authnotrequired => 1,
+    }
+);
+
+my @lists = GetPatronLists();
+
+$template->param( lists => \@lists );
+
+output_html_with_http_headers( $cgi, $cookie, $template->output );
diff --git a/patron_lists/patrons.pl b/patron_lists/patrons.pl
new file mode 100755 (executable)
index 0000000..510f7e0
--- /dev/null
@@ -0,0 +1,59 @@
+#!/usr/bin/perl
+
+# Copyright 2013 ByWater Solutions
+#
+# 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::Output;
+use Koha::List::Patron;
+
+my $cgi = new CGI;
+
+my ( $template, $loggedinuser, $cookie ) = get_template_and_user(
+    {
+        template_name   => "patron_lists/add-modify.tt",
+        query           => $cgi,
+        type            => "intranet",
+        authnotrequired => 1,
+    }
+);
+
+my $id   = $cgi->param('patron_list_id');
+my $name = $cgi->param('name');
+
+if ($id) {
+    my ($list) = GetPatronLists( { patron_list_id => $id } );
+    $template->param( list => $list );
+}
+
+if ($name) {
+    if ($id) {
+        ModPatronList( { patron_list_id => $id, name => $name } );
+    }
+    else {
+        AddPatronList( { name => $name } );
+    }
+
+    print $cgi->redirect('lists.pl');
+    exit;
+}
+
+output_html_with_http_headers( $cgi, $cookie, $template->output );
diff --git a/t/db_dependent/PatronLists.t b/t/db_dependent/PatronLists.t
new file mode 100755 (executable)
index 0000000..146b4f0
--- /dev/null
@@ -0,0 +1,75 @@
+#!/usr/bin/perl
+#
+use Modern::Perl;
+
+use Test::More tests => 9;
+
+BEGIN {
+    use_ok('C4::Context');
+    use_ok('Koha::List::Patron');
+}
+
+my $dbh = C4::Context->dbh;
+my $sth = $dbh->prepare("SELECT * FROM borrowers ORDER BY RAND() LIMIT 10");
+$sth->execute();
+my @borrowers = @{ $sth->fetchall_arrayref( {} ) };
+
+my @lists = GetPatronLists( { owner => 1 } );
+my $list_count_original = @lists;
+
+my $list1 = AddPatronList( { name => 'Test List 1', owner => 1 } );
+ok( $list1->name() eq 'Test List 1', 'AddPatronList works' );
+
+my $list2 = AddPatronList( { name => 'Test List 2', owner => 1 } );
+
+ModPatronList(
+    {
+        patron_list_id => $list2->patron_list_id(),
+        name           => 'Test List 3',
+        owner          => 1
+    }
+);
+$list2->discard_changes();
+ok( $list2->name() eq 'Test List 3', 'ModPatronList works' );
+
+AddPatronsToList(
+    { list => $list1, cardnumbers => [ map { $_->{cardnumber} } @borrowers ] }
+);
+ok(
+    scalar @borrowers ==
+      $list1->patron_list_patrons()->search_related('borrowernumber')->all(),
+    'AddPatronsToList works for cardnumbers'
+);
+
+AddPatronsToList(
+    {
+        list            => $list2,
+        borrowernumbers => [ map { $_->{borrowernumber} } @borrowers ]
+    }
+);
+ok(
+    scalar @borrowers ==
+      $list2->patron_list_patrons()->search_related('borrowernumber')->all(),
+    'AddPatronsToList works for borrowernumbers'
+);
+
+my @ids =
+  $list1->patron_list_patrons()->get_column('patron_list_patron_id')->all();
+DelPatronsFromList(
+    {
+        list                => $list1,
+        patron_list_patrons => \@ids,
+    }
+);
+$list1->discard_changes();
+ok( !$list1->patron_list_patrons()->count(), 'DelPatronsFromList works.' );
+
+@lists = GetPatronLists( { owner => 1 } );
+ok( @lists == $list_count_original + 2, 'GetPatronLists works' );
+
+DelPatronList( { patron_list_id => $list1->patron_list_id(), owner => 1 } );
+DelPatronList( { patron_list_id => $list2->patron_list_id(), owner => 1 } );
+
+@lists =
+  GetPatronLists( { patron_list_id => $list1->patron_list_id(), owner => 1 } );
+ok( !@lists, 'DelPatronList works' );
index 1bba4a0..7009014 100755 (executable)
@@ -34,6 +34,7 @@ use C4::Members::Attributes;
 use C4::Members::AttributeTypes qw/GetAttributeTypes_hashref/;
 use C4::Output;
 use List::MoreUtils qw /any uniq/;
+use Koha::List::Patron;
 
 my $input = new CGI;
 my $op = $input->param('op') || 'show_form';
@@ -50,12 +51,11 @@ my %cookies   = parse CGI::Cookie($cookie);
 my $sessionID = $cookies{'CGISESSID'}->value;
 my $dbh       = C4::Context->dbh;
 
-
-
 # Show borrower informations
 if ( $op eq 'show' ) {
-    my $filefh      = $input->upload('uploadfile');
-    my $filecontent = $input->param('filecontent');
+    my $filefh         = $input->upload('uploadfile');
+    my $filecontent    = $input->param('filecontent');
+    my $patron_list_id = $input->param('patron_list_id');
     my @borrowers;
     my @cardnumbers;
     my @notfoundcardnumbers;
@@ -67,6 +67,13 @@ if ( $op eq 'show' ) {
             $content =~ s/[\r\n]*$//g;
             push @cardnumbers, $content if $content;
         }
+    } elsif ( $patron_list_id ) {
+        my ($list) = GetPatronLists( { patron_list_id => $patron_list_id } );
+
+        @cardnumbers =
+          $list->patron_list_patrons()->search_related('borrowernumber')
+          ->get_column('cardnumber')->all();
+
     } else {
         if ( my $list = $input->param('cardnumberlist') ) {
             push @cardnumbers, split( /\s\n/, $list );
@@ -314,6 +321,9 @@ if ( $op eq 'do' ) {
 
     $template->param( borrowers => \@borrowers );
     $template->param( errors => \@errors );
+} else {
+
+    $template->param( patron_lists => [ GetPatronLists() ] );
 }
 
 $template->param(