@@ -, +, @@ reservior). --- C4/Biblio.pm | 3 + C4/ImportBatch.pm | 19 +++ cataloguing/addbiblio.pl | 12 +- cataloguing/addbooks.pl | 48 +++++-- cataloguing/batch_records_ajax.pl | 123 ++++++++++++++++++ cataloguing/draftrecords.pl | 125 ++++++++++++++++++ .../atomicupdate/bug_20678_save_draft_records.sql | 3 + .../prog/en/includes/cataloging-search.inc | 5 + .../intranet-tmpl/prog/en/includes/permissions.inc | 1 + .../prog/en/modules/cataloguing/addbiblio.tt | 17 ++- .../prog/en/modules/cataloguing/addbooks.tt | 39 +++++- .../prog/en/modules/cataloguing/draftrecords.tt | 142 +++++++++++++++++++++ t/db_dependent/ImportBatch.t | 7 +- 13 files changed, 522 insertions(+), 22 deletions(-) create mode 100755 cataloguing/batch_records_ajax.pl create mode 100755 cataloguing/draftrecords.pl create mode 100644 installer/data/mysql/atomicupdate/bug_20678_save_draft_records.sql create mode 100644 koha-tmpl/intranet-tmpl/prog/en/modules/cataloguing/draftrecords.tt --- a/C4/Biblio.pm +++ a/C4/Biblio.pm @@ -1112,7 +1112,10 @@ for the given frameworkcode or default framework if $frameworkcode is missing sub GetMarcFromKohaField { my ( $kohafield, $frameworkcode ) = @_; return (0, undef) unless $kohafield; + $frameworkcode = 'ACQ' if not defined $frameworkcode; my $mss = GetMarcSubfieldStructure( $frameworkcode ); +# warn $mss->{$kohafield}{tagfield}; + warn $mss->{$kohafield}{tagsubfield}; return ( $mss->{$kohafield}{tagfield}, $mss->{$kohafield}{tagsubfield} ); } --- a/C4/ImportBatch.pm +++ a/C4/ImportBatch.pm @@ -29,6 +29,7 @@ use C4::AuthoritiesMarc; use C4::MarcModificationTemplates; use Koha::Plugins::Handler; use Koha::Logger; +use Data::Dumper; use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS); @@ -44,6 +45,7 @@ BEGIN { GetImportBatch AddAuthToBatch AddBiblioToBatch + DeleteBiblioInBatch AddItemsToImportBiblio ModAuthorityInBatch ModBiblioInBatch @@ -296,6 +298,15 @@ sub AddBiblioToBatch { return $import_record_id; } +=head2 DeleteBiblioInBatch + DeleteBiblioInBatch( $import_record_id ); +=cut + +sub DeleteBiblioInBatch { + my ($subroutine, $import_record_id) = @_; + _delete_import_record_marc($import_record_id); +} + =head2 ModBiblioInBatch ModBiblioInBatch($import_record_id, $marc_record); @@ -1615,6 +1626,14 @@ sub _update_import_record_marc { $sth->finish(); } +sub _delete_import_record_marc { + my ($import_record_id, $subroutine) = @_; + my $dbh = C4::Context->dbh; + my $sth = $dbh->prepare("DELETE FROM import_records WHERE import_record_id = ?"); + $sth->execute($import_record_id); + $sth->finish(); +} + sub _add_auth_fields { my ($import_record_id, $marc_record) = @_; --- a/cataloguing/addbiblio.pl +++ a/cataloguing/addbiblio.pl @@ -708,7 +708,7 @@ my $fa_barcode = $input->param('barcode'); my $fa_branch = $input->param('branch'); my $fa_stickyduedate = $input->param('stickyduedate'); my $fa_duedatespec = $input->param('duedatespec'); - +my $previewrecord = $input->param('preview'); my $userflags = 'edit_catalogue'; my $changed_framework = $input->param('changed_framework'); @@ -849,7 +849,15 @@ if ( $op eq "addbiblio" ) { ModBiblio( $record, $biblionumber, $frameworkcode ); } else { - ( $biblionumber, $oldbibitemnum ) = AddBiblio( $record, $frameworkcode ); + if ($previewrecord) { + AddBiblioToBatch(1, 0, $record, 'biblio', int(rand(99999)), 1); + print $input->redirect('/cgi-bin/koha/cataloguing/addbooks.pl'); + } else { + if ($breedingid) { + C4::ImportBatch->DeleteBiblioInBatch($breedingid); + } + ( $biblionumber, $oldbibitemnum ) = AddBiblio( $record, $frameworkcode ); + } } if ($redirect eq "items" || ($mode ne "popup" && !$is_a_modif && $redirect ne "view" && $redirect ne "just_save")){ if ($frameworkcode eq 'FA'){ --- a/cataloguing/addbooks.pl +++ a/cataloguing/addbooks.pl @@ -44,6 +44,7 @@ my $success = $input->param('biblioitem'); my $query = $input->param('q'); my @value = $input->multi_param('value'); my $page = $input->param('page') || 1; +my $drafts_only = $input->param('drafts_only'); my $results_per_page = 20; @@ -58,8 +59,8 @@ my ( $template, $loggedinuser, $cookie ) = get_template_and_user( } ); -# Searching the catalog. -if ($query) { +# Searching the catalog unless 'drafts_only' has been selected +if (($query) && !($drafts_only)) { # build query my @operands = $query; @@ -121,16 +122,41 @@ if ($query) { ( $countbr, @resultsbr ) = BreedingSearch( $title, $isbn ); } my $breeding_loop = []; +my $count; + for my $resultsbr (@resultsbr) { - push @{$breeding_loop}, { - id => $resultsbr->{import_record_id}, - isbn => $resultsbr->{isbn}, - copyrightdate => $resultsbr->{copyrightdate}, - editionstatement => $resultsbr->{editionstatement}, - file => $resultsbr->{file_name}, - title => $resultsbr->{title}, - author => $resultsbr->{author}, - }; + + #Only return records associated with 'Draft record' file_name if drafts_only has been set + if ($drafts_only) { + if ($resultsbr->{file_name} eq "Draft record") { + push @{$breeding_loop}, { + id => $resultsbr->{import_record_id}, + isbn => $resultsbr->{isbn}, + copyrightdate => $resultsbr->{copyrightdate}, + editionstatement => $resultsbr->{editionstatement}, + file => $resultsbr->{file_name}, + title => $resultsbr->{title}, + author => $resultsbr->{author}, + }; + $count++; + } + } else { + push @{$breeding_loop}, { + id => $resultsbr->{import_record_id}, + isbn => $resultsbr->{isbn}, + copyrightdate => $resultsbr->{copyrightdate}, + editionstatement => $resultsbr->{editionstatement}, + file => $resultsbr->{file_name}, + title => $resultsbr->{title}, + author => $resultsbr->{author}, + }; + } +} + +#If drafts_only has been set then only draft records will be returned to the template. $countbr must be based on the number of drafts in the reservoir +if ($drafts_only) { + $countbr = $count; + $template->param( query => $query, drafts_only => $drafts_only ); } my $schema = Koha::Database->new()->schema(); --- a/cataloguing/batch_records_ajax.pl +++ a/cataloguing/batch_records_ajax.pl @@ -0,0 +1,123 @@ +#!/usr/bin/perl + +# Copyright 2013 ByWater Solutions +# Based on circ/ysearch.pl: Copyright 2007 Tamil s.a.r.l. +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software +# Foundation; either version 3 of the License, or (at your option) any later +# version. +# +# Koha is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with Koha; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +=head1 NAME + +batch_records_ajax.pl - A script for searching batch imported records via ajax + +=head1 SYNOPSIS + +This script is to be used as a data source for DataTables that load and display +the records from an import batch. + +=cut + +use Modern::Perl; + +use CGI qw ( -utf8 ); +use JSON qw/ to_json /; + +use C4::Context; +use C4::Charset; +use C4::Auth qw/check_cookie_auth/; +use C4::ImportBatch; +use Data::Dumper; + +my $input = new CGI; + +my @sort_columns = + qw/import_record_id title status overlay_status overlay_status/; + +my $import_batch_id = $input->param('import_batch_id'); +my $offset = $input->param('iDisplayStart'); +my $results_per_page = $input->param('iDisplayLength'); +my $sorting_column = $sort_columns[ $input->param('iSortCol_0') // 0 ]; +my $sorting_direction = $input->param('sSortDir_0'); + +$results_per_page = undef if $results_per_page && $results_per_page == -1; + +binmode STDOUT, ":encoding(UTF-8)"; +print $input->header( -type => 'text/plain', -charset => 'UTF-8' ); + +my ( $auth_status, $sessionID ) = + check_cookie_auth( $input->cookie('CGISESSID'), { tools => 'manage_staged_marc' } ); +if ( $auth_status ne "ok" ) { + exit 0; +} + +my $batch = GetImportBatch($import_batch_id); +my $records = + GetImportRecordsRange( $import_batch_id, $offset, $results_per_page, undef, + { order_by => $sorting_column, order_by_direction => $sorting_direction } ); +my @list = (); +foreach my $record (@$records) { + my $citation = $record->{'title'} || $record->{'authorized_heading'}; + $citation .= " $record->{'author'}" if $record->{'author'}; + $citation .= " (" if $record->{'issn'} or $record->{'isbn'}; + $citation .= $record->{'isbn'} if $record->{'isbn'}; + $citation .= ", " if $record->{'issn'} and $record->{'isbn'}; + $citation .= $record->{'issn'} if $record->{'issn'}; + $citation .= ")" if $record->{'issn'} or $record->{'isbn'}; + + my $match = GetImportRecordMatches( $record->{'import_record_id'}, 1 ); + my $match_citation = ''; + my $match_id; + if ( $#$match > -1 ) { + if ( $match->[0]->{'record_type'} eq 'biblio' ) { + $match_citation .= $match->[0]->{'title'} + if defined( $match->[0]->{'title'} ); + $match_citation .= ' ' . $match->[0]->{'author'} + if defined( $match->[0]->{'author'} ); + $match_id = $match->[0]->{'biblionumber'}; + } + elsif ( $match->[0]->{'record_type'} eq 'auth' ) { + if ( defined( $match->[0]->{'authorized_heading'} ) ) { + $match_citation .= $match->[0]->{'authorized_heading'}; + $match_id = $match->[0]->{'candidate_match_id'}; + } + } + } + + push @list, + { + DT_RowId => $record->{'import_record_id'}, + import_record_id => $record->{'import_record_id'}, + citation => $citation, + status => $record->{'status'}, + overlay_status => $record->{'overlay_status'}, + match_citation => $match_citation, + matched => $record->{'matched_biblionumber'} + || $record->{'matched_authid'} + || q{}, + score => $#$match > -1 ? $match->[0]->{'score'} : 0, + match_id => $match_id, + diff_url => $match_id ? "/cgi-bin/koha/tools/showdiffmarc.pl?batchid=$import_batch_id&importid=$record->{import_record_id}&id=$match_id" : undef, + catalog_link => "/cgi-bin/koha/cataloguing/addbiblio.pl?breedingid=$record->{import_record_id}", + delete_link =>"/cgi-bin/koha/cataloguing/draftrecords.pl?biblionumber=$record->{import_record_id}&op=delete_record", + }; +} +warn Dumper(@list); +my $data; +$data->{'iTotalRecords'} = $batch->{'num_records'}; +$data->{'iTotalDisplayRecords'} = $batch->{'num_records'}; +$data->{'sEcho'} = $input->param('sEcho') || undef; +$data->{'aaData'} = \@list; + +print to_json($data); --- a/cataloguing/draftrecords.pl +++ a/cataloguing/draftrecords.pl @@ -0,0 +1,125 @@ +#!/usr/bin/perl + +# Copyright 2018 Catalyst IT +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# Koha is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Koha; if not, see . + +=head1 cataloguing:draftrecords.pl + + TODO + +=cut + +use strict; +use warnings; +use CGI qw ( -utf8 ); +use C4::Auth; +use C4::Biblio; +use C4::Breeding; +use C4::Output; +use C4::Koha; +use C4::Search; +use C4::ImportBatch; + +use Koha::BiblioFrameworks; +use Koha::SearchEngine::Search; +use Koha::SearchEngine::QueryBuilder; + +my $input = new CGI; + +my $success = $input->param('biblioitem'); +my $query = $input->param('q'); +my @value = $input->multi_param('value'); +my $page = $input->param('page') || 1; +my $biblionumber= $input->param('biblionumber'); +my $op = $input->param('op'); +my $results_per_page = 20; + +my ( $template, $loggedinuser, $cookie ) = get_template_and_user( + { + template_name => "cataloguing/draftrecords.tt", + query => $input, + type => "intranet", + authnotrequired => 0, + flagsrequired => { editcatalogue => '*' }, + debug => 1, + } +); + +# fill with books in breeding farm + +my $countbr = 0; +my @resultsbr; +if ($query) { +# fill isbn or title, depending on what has been entered +#u must do check on isbn because u can find number in beginning of title +#check is on isbn legnth 13 for new isbn and 10 for old isbn + my ( $title, $isbn ); + if ($query=~/\d/) { + my $clean_query = $query; + $clean_query =~ s/-//g; # remove hyphens + my $querylength = length $clean_query; + if ( $querylength == 13 || $querylength == 10 ) { + $isbn = $query; + } + } + if (!$isbn) { + $title = $query; + } + ( $countbr, @resultsbr ) = BreedingSearch( $title, $isbn ); +} + +if ($op && $op eq 'delete_record') { + C4::ImportBatch->DeleteBiblioInBatch($biblionumber); +} elsif ($op && $op eq 'delete_all') { + CleanBatch(1); +} + + +my $breeding_loop = []; +for my $resultsbr (@resultsbr) { + push @{$breeding_loop}, { + id => $resultsbr->{import_record_id}, + isbn => $resultsbr->{isbn}, + copyrightdate => $resultsbr->{copyrightdate}, + editionstatement => $resultsbr->{editionstatement}, + file => $resultsbr->{file_name}, + title => $resultsbr->{title}, + author => $resultsbr->{author}, + }; +} + +my $schema = Koha::Database->new()->schema(); +my $servers = $schema->resultset('Z3950server')->search( + { + recordtype => 'biblio', + servertype => ['zed', 'sru'], + }, + { result_class => 'DBIx::Class::ResultClass::HashRefInflator', + order_by => ['rank', 'servername'], + }, +); + +my $frameworks = Koha::BiblioFrameworks->search({}, { order_by => ['frameworktext'] }); +$template->param( + servers => $servers, + frameworks => $frameworks, + breeding_count => $countbr, + breeding_loop => $breeding_loop, + z3950_search_params => C4::Search::z3950_search_args($query), +); + +output_html_with_http_headers $input, $cookie, $template->output; --- a/installer/data/mysql/atomicupdate/bug_20678_save_draft_records.sql +++ a/installer/data/mysql/atomicupdate/bug_20678_save_draft_records.sql @@ -0,0 +1,3 @@ +INSERT INTO import_batches (import_batch_id,record_type,file_name,comments) VALUES (1, 'biblio', 'Draft record', 'Draft MARC records created in the addbiblio.pl file but not saved to the Koha catalog'); +INSERT INTO permissions (module_bit, code, description) VALUES (9, 'commit_MARC_records_to_catalog', 'Commit MARC records to the catalog. If not enabled then a patron will only be able to create draft records if other editcatalogue subpermissions are enabled'); +INSERT INTO systempreferences (variable, value, options, explanation, type) VALUES ('EnableDraftRecords', 1, NULL, 'If enabled staff and catalogers will be able to save records to the reservoir as a draft rather than to the catalog. Also the link to the draft records interface in the Cataloguing module will be visible', 'YesNo'); --- a/koha-tmpl/intranet-tmpl/prog/en/includes/cataloging-search.inc +++ a/koha-tmpl/intranet-tmpl/prog/en/includes/cataloging-search.inc @@ -6,7 +6,12 @@

Search the catalog and the reservoir:

+ [-] + [+] +
+

Drafts only

+
--- a/koha-tmpl/intranet-tmpl/prog/en/includes/permissions.inc +++ a/koha-tmpl/intranet-tmpl/prog/en/includes/permissions.inc @@ -41,6 +41,7 @@ [%- CASE 'edit_items' -%]Edit items [%- CASE 'edit_items_restricted' -%]Limit item modification to subfields defined in the SubfieldsToAllowForRestrictedEditing preference (please note that edit_item is still required) [%- CASE 'fast_cataloging' -%]Fast cataloging + [%- CASE 'commit_MARC_records_to_catalog' -%]Commit MARC records to the catalog. If not enabled then a patron will only be able to create draft records if other editcatalogue subpermissions are enabled [%- CASE 'remaining_permissions' -%]Remaining permissions for managing fines and fees [%- CASE 'writeoff' -%]Write off fines and fees [%- CASE 'budget_add_del' -%]Add and delete budgets (but can't modify budgets) --- a/koha-tmpl/intranet-tmpl/prog/en/modules/cataloguing/addbiblio.tt +++ a/koha-tmpl/intranet-tmpl/prog/en/modules/cataloguing/addbiblio.tt @@ -88,6 +88,13 @@ return false; }); + $("#previewrecord").click(function(){ + $(".btn-group").removeClass("open"); + document.getElementById('preview').value = 1; + onOption(); + return false; + }); + $("#saveandview").click(function(){ $(".btn-group").removeClass("open"); redirect("view"); @@ -467,9 +474,12 @@ function Changefwk() { [% END %] @@ -557,6 +567,7 @@ function Changefwk() { [% END %] + --- a/koha-tmpl/intranet-tmpl/prog/en/modules/cataloguing/addbooks.tt +++ a/koha-tmpl/intranet-tmpl/prog/en/modules/cataloguing/addbooks.tt @@ -9,6 +9,15 @@ //Set focus to cataloging search $("input[name=q]:eq(0)").focus(); + $("#filteraction_off").hide(); + $("#filters").hide(); + + $("#filteraction_off, #filteraction_on").on('click', function(e) { + e.preventDefault(); + $('#filters').toggle(); + $('.filteraction').toggle(); + }); + $("#z3950search").click(function(){ PopupZ3950(""); return false; @@ -119,6 +128,7 @@ [% END %] + View draft records [% IF ( total ) %]
[% END %] @@ -217,7 +227,7 @@ [% ELSE %] - [% IF ( query ) %] + [% IF (( query ) && !(drafts_only)) %] [% IF ( error ) %]
[% END %]No results found [% IF ( error ) %] Error: [% error %]
@@ -226,8 +236,17 @@ [% END %] [% IF ( query ) %] -
-

Biblios in reservoir

+ [% IF (drafts_only) %] +
+ [% breeding_count %] result(s) found in reservoir +
+ [% END %] +
+ [% IF (drafts_only) %] +

Draft records in reservoir

+ [% ELSE %] +

Biblios in reservoir

+ [% END %] [% IF ( breeding_loop ) %] @@ -245,7 +264,13 @@ - +
[% breeding_loo.isbn %] [% breeding_loo.copyrightdate %] [% breeding_loo.edition %][% breeding_loo.file %] + [% IF breeding_loo.file == "Draft record" %] + Draft record + [% ELSE %] + [% breeding_loo.file %] + [% END %] +
@@ -255,7 +280,11 @@
  • MARC preview
  • Card preview
  • [% IF ( CAN_user_editcatalogue_edit_catalogue ) %] -
  • Add biblio
  • + [% IF breeding_loo.file == "Draft record" %] +
  • Continue editing draft record
  • + [% ELSE %] +
  • Add biblio
  • + [% END %] [% END %]
    --- a/koha-tmpl/intranet-tmpl/prog/en/modules/cataloguing/draftrecords.tt +++ a/koha-tmpl/intranet-tmpl/prog/en/modules/cataloguing/draftrecords.tt @@ -0,0 +1,142 @@ +[% SET footerjs = 1 %] +[% INCLUDE 'doc-head-open.inc' %] +Koha › Cataloging › Draft records +[% INCLUDE 'doc-head-close.inc' %] +[% INCLUDE 'browser-strings.inc' %] + + + + + [% INCLUDE 'header.inc' %] + +
    +
    +
    + [% IF ( CAN_user_editcatalogue_edit_catalogue ) %] +
    + [% IF Koha.Preference( 'EnableAdvancedCatalogingEditor' ) == 1 %] + Advanced editor + [% END %] +
    + + +
    + [% IF servers.count > 0 %] +
    + + + +
    + [% END %] + View draft records + [% IF ( total ) %] +
    + [% END %] +
    + [% END %] +

    View draft records

    +
    +

    To edit or save these draft records click the 'Edit' link beside the appropriate record. +

    + + +
    +

    + + + + + + + + + + +
    #CitationStatusEdit recordDelete record
    +
    +
    +
    +
    + +[% MACRO jsinclude BLOCK %] + + +[% INCLUDE 'datatables.inc' %] + +[% END %] +[% INCLUDE 'intranet-bottom.inc' %] + + + --- a/t/db_dependent/ImportBatch.t +++ a/t/db_dependent/ImportBatch.t @@ -1,7 +1,7 @@ #!/usr/bin/perl use Modern::Perl; -use Test::More tests => 14; +use Test::More tests => 15; use Cwd qw(abs_path); use File::Temp qw/tempfile/; @@ -144,6 +144,11 @@ $dbh->do( "DELETE FROM items WHERE itemnumber=?", undef, $itemno ); is( @a, 0, 'No item numbers expected since we deleted the item' ); $dbh->do( $sql, undef, undef, $import_record_id ); # remove link again +#Test DeleteBiblioInBatch +my @a = DeleteBiblioInBatch( $import_record_id); #Remove the MARC record from import_biblios and import_records tables +my $biblio_in_import_records = $dbh->do('SELECT * FROM import_records WHERE import_batch_id = "$import_record_id"'); +is( $biblio_in_import_records, "0E0", 'No biblio expected since we deleted the biblio from the batch' ); + # fresh data my $sample_import_batch3 = { matcher_id => 3, --