diff --git a/Koha/BackgroundJob/PatronImport.pm b/Koha/BackgroundJob/PatronImport.pm
index 987edb9fda..5cd6e2fdf8 100644
--- a/Koha/BackgroundJob/PatronImport.pm
+++ b/Koha/BackgroundJob/PatronImport.pm
@@ -20,6 +20,7 @@ use Try::Tiny qw( catch try );
use Koha::List::Patron qw( AddPatronList AddPatronsToList );
use Koha::Patrons::Import;
+use Koha::UploadedFiles;
use base 'Koha::BackgroundJob';
@@ -54,8 +55,12 @@ sub process {
$self->start;
- my $file_content = $args->{file_content};
- open my $fh, '<', \$file_content or die "Cannot create in-memory file handle: $!";
+ my $upload = Koha::UploadedFiles->find( $args->{uploaded_file_id} );
+ unless ($upload) {
+ $self->status('failed')->store;
+ return;
+ }
+ my $fh = $upload->file_handle;
my $result = try {
Koha::Patrons::Import->new->import_patrons(
@@ -80,7 +85,6 @@ sub process {
};
my $data = $self->decoded_data;
- delete $data->{file_content}; # No longer needed; free DB space
if ($result) {
if ( $args->{createpatronlist} && @{ $result->{imported_borrowers} // [] } ) {
@@ -110,9 +114,17 @@ Enqueue the new job.
sub enqueue {
my ( $self, $args ) = @_;
- return unless $args->{file_content};
+ return unless $args->{uploaded_file_id};
- my $line_count = scalar grep { /\S/ } split /\n/, $args->{file_content};
+ my $upload = Koha::UploadedFiles->find( $args->{uploaded_file_id} );
+ return unless $upload;
+
+ my $fh = $upload->file_handle;
+ my $line_count = 0;
+ while ( my $line = <$fh> ) {
+ $line_count++ if $line =~ /\S/;
+ }
+ close $fh;
$self->SUPER::enqueue(
{
diff --git a/Koha/Uploader.pm b/Koha/Uploader.pm
index 2b96e64937..f4c7b1383a 100644
--- a/Koha/Uploader.pm
+++ b/Koha/Uploader.pm
@@ -176,6 +176,7 @@ sub allows_add_by {
{ circulate => 'circulate_remaining_permissions' },
{ tools => 'stage_marc_import' },
{ tools => 'upload_local_cover_images' },
+ { tools => 'import_patrons' },
];
require C4::Auth;
foreach (@$flags) {
diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/import_borrowers.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/tools/import_borrowers.tt
index 6734b1ef8a..b5efb3d8fe 100644
--- a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/import_borrowers.tt
+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/tools/import_borrowers.tt
@@ -26,6 +26,11 @@
code {
background-color: yellow;
}
+ #fileuploadstatus,
+ #fileuploadfailed,
+ #fileuploadcancel {
+ display: none;
+ }
@@ -61,6 +66,11 @@
[% IF error == 'cannot_enqueue_job' %]
The import job could not be enqueued. Please try again.
+ [% ELSIF error == 'job_failed_to_queue' %]
+
+ The import job was created but could not be queued for processing.
+
View the job for more details, or try again.
+
[% ELSE %]
The patron import job has been enqueued!
@@ -81,18 +91,41 @@
If a card number exists in the table, you can choose whether to ignore the new one or overwrite the old one.
-
+
+
@@ -295,7 +328,9 @@
[% Asset.js("js/tools-menu.js") | $raw %]
[% INCLUDE 'str/members-menu.inc' %]
[% Asset.js("js/members-menu.js") | $raw %]
+ [% Asset.js("js/file-upload.js") | $raw %]
[% END %]
[% INCLUDE 'intranet-bottom.inc' %]
diff --git a/t/db_dependent/Koha/BackgroundJob/PatronImport.t b/t/db_dependent/Koha/BackgroundJob/PatronImport.t
index 1704d3f557..b2713fbb0d 100755
--- a/t/db_dependent/Koha/BackgroundJob/PatronImport.t
+++ b/t/db_dependent/Koha/BackgroundJob/PatronImport.t
@@ -17,6 +17,8 @@
use Modern::Perl;
+use File::Temp qw( tempdir );
+use Test::MockModule;
use Test::NoWarnings;
use Test::More tests => 7;
@@ -24,6 +26,7 @@ use Koha::BackgroundJob::PatronImport;
use Koha::BackgroundJobs;
use Koha::Database;
use Koha::Patrons;
+use Koha::UploadedFiles;
use t::lib::Mocks;
use t::lib::TestBuilder;
@@ -31,6 +34,34 @@ use t::lib::TestBuilder;
my $schema = Koha::Database->new->schema;
my $builder = t::lib::TestBuilder->new;
+my $tempdir = tempdir( CLEANUP => 1 );
+my $ctx_mock = Test::MockModule->new('C4::Context');
+$ctx_mock->mock( 'temporary_directory' => sub { return $tempdir; } );
+
+my $upload_counter = 0;
+
+sub build_uploaded_file {
+ my ($csv) = @_;
+
+ $upload_counter++;
+ my $upload = Koha::UploadedFile->new(
+ {
+ hashvalue => sprintf( 'testhash%03d', $upload_counter ),
+ filename => "patrons_$upload_counter.csv",
+ dir => '',
+ uploadcategorycode => 'temp',
+ filesize => length($csv),
+ permanent => 0,
+ }
+ )->store;
+
+ open my $fh, '>', $upload->full_path or die "Cannot write test upload file: $!";
+ print $fh $csv;
+ close $fh;
+
+ return $upload;
+}
+
subtest 'enqueue() tests' => sub {
plan tests => 5;
@@ -39,10 +70,11 @@ subtest 'enqueue() tests' => sub {
my $csv = "surname,branchcode,categorycode\nSmith,CPL,PT\nJones,CPL,PT\n";
+ my $upload = build_uploaded_file($csv);
my $job_id = Koha::BackgroundJob::PatronImport->new->enqueue(
{
- file_content => $csv,
- matchpoint => 'cardnumber',
+ uploaded_file_id => $upload->id,
+ matchpoint => 'cardnumber',
}
);
my $job = Koha::BackgroundJobs->find($job_id)->_derived_class;
@@ -53,10 +85,11 @@ subtest 'enqueue() tests' => sub {
is( $job->type, 'patron_import', 'job_type is patron_import' );
my $csv_no_newline = "surname,branchcode,categorycode\nSmith,CPL,PT\nJones,CPL,PT";
+ my $upload2 = build_uploaded_file($csv_no_newline);
my $job_id2 = Koha::BackgroundJob::PatronImport->new->enqueue(
{
- file_content => $csv_no_newline,
- matchpoint => 'cardnumber',
+ uploaded_file_id => $upload2->id,
+ matchpoint => 'cardnumber',
}
);
my $job2 = Koha::BackgroundJobs->find($job_id2)->_derived_class;
@@ -67,7 +100,7 @@ subtest 'enqueue() tests' => sub {
subtest 'process() tests' => sub {
- plan tests => 8;
+ plan tests => 7;
$schema->storage->txn_begin;
@@ -85,9 +118,10 @@ subtest 'process() tests' => sub {
""
);
+ my $upload = build_uploaded_file($csv);
my $job_id = Koha::BackgroundJob::PatronImport->new->enqueue(
{
- file_content => $csv,
+ uploaded_file_id => $upload->id,
matchpoint => 'cardnumber',
overwrite_cardnumber => 0,
defaults => {},
@@ -107,7 +141,6 @@ subtest 'process() tests' => sub {
is( $data->{already_in_db}, 0, 'No patrons already in db' );
is( $data->{invalid}, 0, 'No invalid rows' );
is( $data->{total}, 2, 'Total count is correct' );
- ok( !exists $data->{file_content}, 'file_content cleared from stored data' );
is(
Koha::Patrons->search( { cardnumber => { '-in' => [ 'TEST001', 'TEST002' ] } } )->count,
2, 'Patrons exist in the database'
@@ -136,9 +169,10 @@ subtest 'process() with errors' => sub {
""
);
+ my $upload = build_uploaded_file($csv);
my $job_id = Koha::BackgroundJob::PatronImport->new->enqueue(
{
- file_content => $csv,
+ uploaded_file_id => $upload->id,
matchpoint => 'cardnumber',
overwrite_cardnumber => 0,
defaults => {},
@@ -180,11 +214,12 @@ subtest 'process() already_in_db and overwritten' => sub {
""
);
+ my $upload = build_uploaded_file($csv);
my %base_args = (
- file_content => $csv,
- matchpoint => 'cardnumber',
- defaults => {},
- preserve_fields => [],
+ uploaded_file_id => $upload->id,
+ matchpoint => 'cardnumber',
+ defaults => {},
+ preserve_fields => [],
);
# First import creates the patrons
@@ -238,9 +273,10 @@ subtest 'process() with patron list creation' => sub {
""
);
+ my $upload = build_uploaded_file($csv);
my $job_id = Koha::BackgroundJob::PatronImport->new->enqueue(
{
- file_content => $csv,
+ uploaded_file_id => $upload->id,
matchpoint => 'cardnumber',
overwrite_cardnumber => 0,
defaults => {},
@@ -284,9 +320,10 @@ subtest 'process() with cancelled job' => sub {
""
);
+ my $upload = build_uploaded_file($csv);
my $job_id = Koha::BackgroundJob::PatronImport->new->enqueue(
{
- file_content => $csv,
+ uploaded_file_id => $upload->id,
matchpoint => 'cardnumber',
overwrite_cardnumber => 0,
defaults => {},
diff --git a/tools/import_borrowers.pl b/tools/import_borrowers.pl
index 92c729757b..175ede475a 100755
--- a/tools/import_borrowers.pl
+++ b/tools/import_borrowers.pl
@@ -46,6 +46,7 @@ use Koha::DateUtils qw( dt_from_string );
use Koha::Patron::Attribute::Types;
use Koha::Patron::Categories;
use Koha::Patrons;
+use Koha::UploadedFiles;
use Text::CSV;
@@ -88,9 +89,9 @@ if ( $input->param('sample') ) {
my @preserve_fields = $input->multi_param('preserve_existing');
-my $uploadborrowers = $input->param('uploadborrowers');
-my $matchpoint = $input->param('matchpoint');
-my $welcome_new = $input->param('welcome_new');
+my $uploadedfileid = $input->param('uploadedfileid');
+my $matchpoint = $input->param('matchpoint');
+my $welcome_new = $input->param('welcome_new');
if ($matchpoint) {
$matchpoint =~ s/^patron_attribute_//;
}
@@ -100,11 +101,10 @@ my $createpatronlist = $input->param('createpatronlist') || 0;
my $dt = dt_from_string();
my $timestamp = $dt->ymd('-') . ' ' . $dt->hms(':');
-if ( $op eq 'cud-import' && $uploadborrowers && length($uploadborrowers) > 0 ) {
+if ( $op eq 'cud-import' && $uploadedfileid ) {
- my $handle = $input->upload('uploadborrowers');
- my $file_content = do { local $/; <$handle> };
- my $patronlistname = $uploadborrowers . ' (' . $timestamp . ')';
+ my $upload = Koha::UploadedFiles->find($uploadedfileid);
+ my $patronlistname = ( $upload ? $upload->filename : $uploadedfileid ) . ' (' . $timestamp . ')';
my %defaults;
for my $k (@columnkeys) {
@@ -115,10 +115,11 @@ if ( $op eq 'cud-import' && $uploadborrowers && length($uploadborrowers) > 0 ) {
my $overwrite_passwords = defined $input->param('overwrite_passwords') ? 1 : 0;
my $update_dateexpiry = $input->param('update_dateexpiry') // "";
+ my $job = Koha::BackgroundJob::PatronImport->new;
try {
- my $job_id = Koha::BackgroundJob::PatronImport->new->enqueue(
+ my $job_id = $job->enqueue(
{
- file_content => $file_content,
+ uploaded_file_id => $uploadedfileid,
defaults => \%defaults,
matchpoint => $matchpoint,
overwrite_cardnumber => scalar $input->param('overwrite_cardnumber'),
@@ -134,15 +135,30 @@ if ( $op eq 'cud-import' && $uploadborrowers && length($uploadborrowers) > 0 ) {
}
);
- $template->param(
- op => 'enqueued',
- job_id => $job_id,
- );
+ if ($job_id) {
+ $template->param(
+ op => 'enqueued',
+ job_id => $job_id,
+ );
+ } else {
+ $template->param(
+ op => 'enqueued',
+ error => 'cannot_enqueue_job',
+ );
+ }
} catch {
- $template->param(
- op => 'enqueued',
- error => 'cannot_enqueue_job',
- );
+ if ( $job->id ) {
+ $template->param(
+ op => 'enqueued',
+ job_id => $job->id,
+ error => 'job_failed_to_queue',
+ );
+ } else {
+ $template->param(
+ op => 'enqueued',
+ error => 'cannot_enqueue_job',
+ );
+ }
};
} else {