--- /dev/null
+[% PROCESS 'authorities-search-results.inc' %]
+[% INCLUDE 'doc-head-open.inc' %]
+<title>Koha › Tools › Batch record modification</title>
+[% INCLUDE 'doc-head-close.inc' %]
+[% INCLUDE 'greybox.inc' %]
+<link rel="stylesheet" type="text/css" href="[% themelang %]/css/datatables.css" />
+[% INCLUDE 'datatables.inc' %]
+<script type="text/javascript" src="[% themelang %]/lib/jquery/plugins/jquery.checkboxes.min.js"></script>
+<script type="text/javascript" src="[% themelang %]/js/background-job-progressbar.js"></script>
+<script type="text/javascript">
+//<![CDATA[
+$(document).ready(function() {
+ $("#selectall").click(function(e){
+ e.preventDefault();
+ $(".records").checkCheckboxes();
+ });
+ $("#clearall").click(function(e){
+ e.preventDefault();
+ $(".records").unCheckCheckboxes();
+ });
+ $("#selectall").click();
+
+ $("table#biblios").dataTable($.extend(true, {}, dataTablesDefaults, {
+ "aoColumnDefs": [
+ { "aTargets": [ 0, 3 ], "bSortable": false, "bSearchable": false },
+ { "aTargets": [ 1 ], "sType": "num-html" }
+ ],
+ "sDom": 't',
+ "aaSorting": [],
+ "bPaginate": false
+ }));
+
+ $("table#authorities").dataTable($.extend(true, {}, dataTablesDefaults, {
+ "aoColumnDefs": [
+ { "aTargets": [ 0, 3 ], "bSortable": false, "bSearchable": false },
+ { "aTargets": [ 1 ], "sType": "num-html" }
+ ],
+ "sDom": 't',
+ "aaSorting": [],
+ "bPaginate": false
+ }));
+
+ $("#mainformsubmit").click(function(){
+ return submitBackgroundJob(document.getElementById("process"));
+ });
+
+});
+//]]>
+</script>
+</head>
+<body id="tools_batch_record_modification" class="tools">
+[% 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/batch_record_modification.pl">Batch record modification</a>
+</div>
+
+<div id="doc3" class="yui-t2">
+<div id="bd">
+<div id="yui-main">
+<div class="yui-b">
+ <h1>Batch record modification</h1>
+ [% FOREACH message IN messages %]
+ [% IF message.type == 'success' %]
+ <div class="dialog message">
+ [% ELSIF message.type == 'warning' %]
+ <div class="dialog alert">
+ [% ELSIF message.type == 'error' %]
+ <div class="dialog error" style="margin:auto;">
+ [% END %]
+ <td><a href="/cgi-bin/koha/catalogue/detail.pl?biblionumber=[% biblio.biblionumber %]">[% biblio.title %]</a></td>
+ [% IF message.code == 'no_action_defined_for_the_template' %]
+ The selected template (id=[% message.mmtid%]) does not exist or no action is defined.
+ [% ELSIF message.code == 'biblio_not_exists' %]
+ The biblionumber [% message.biblionumber %] does not exist in the database.
+ [% ELSIF message.code == 'authority_not_exists' %]
+ The authority id [% message.authid %] does not exist in the database.
+ [% ELSIF message.code == 'biblio_not_modified' %]
+ The biblio <a href="/cgi-bin/koha/catalogue/detail.pl?biblionumber=[% message.biblionumber %]">[% message.biblionumber %]</a> has not been modified. An error occurred on modifying it.
+ [% ELSIF message.code == 'authority_not_modified' %]
+ The authority <a href="/cgi-bin/koha/authorities/detail.pl?authid=[% message.authid %]">[% message.authid %]</a> has not been modified. An error occurred on modifying it.
+ [% ELSIF message.code == 'biblio_modified' %]
+ The biblio <a href="/cgi-bin/koha/catalogue/detail.pl?biblionumber=[% message.biblionumber %]">[% message.biblionumber %]</a> has successfully been modified.
+ [% ELSIF message.code == 'authority_modified' %]
+ The authority <a href="/cgi-bin/koha/authorities/detail.pl?authid=[% message.authid %]">[% message.authid %]</a> has successfully been modified.
+ [% END %]
+ [% IF message.error %]
+ (The error was: [% message.error%], see the Koha logfile for more information).
+ [% END %]
+ </div>
+ [% END %]
+ [% IF view == 'form' %]
+ <form method="post" enctype="multipart/form-data" action="/cgi-bin/koha/tools/batch_record_modification.pl">
+ <fieldset class="rows">
+ <legend>Record type</legend>
+ <ol>
+ <li><label for="biblio_type">Biblios: </label><input type="radio" name="recordtype" value="biblio" id="biblio_type" checked="checked" /></li>
+ <li><label for="authority_type">Authorities: </label><input type="radio" name="recordtype" value="authority" id="authority_type" /></li>
+ </ol>
+ </fieldset>
+ <fieldset class="rows">
+ <legend>Use a file</legend>
+ <ol>
+ <li><label for="uploadfile">File: </label> <input type="file" id="uploadfile" name="uploadfile" /></li>
+ </ol>
+ </fieldset>
+ <fieldset class="rows">
+ <legend>Or enter a list of record numbers</legend>
+ <ol>
+ <li>
+ <label for="recordnumber_list">Record number list (one per line): </label>
+ <textarea rows="10" cols="30" id="recordnumber_list" name="recordnumber_list"></textarea>
+ </li>
+ </ol>
+ </fieldset>
+ <fieldset class="rows">
+ <legend>Use MARC Modification Template:</legend>
+ <ol>
+ <li>
+ <label for="marc_modification_template_id" class="required">Modify record using the following template: </label>
+ <select name="marc_modification_template_id" id="marc_modification_template_id" required="required">
+ <option value="">Select a template</option>
+ [% FOREACH mmt IN MarcModificationTemplatesLoop %]
+ <option value="[% mmt.template_id %]">[% mmt.name %]</option>
+ [% END %]
+ </select>
+ </li>
+ </ol>
+ </fieldset>
+ <fieldset class="action">
+ <input type="hidden" name="op" value="list" />
+ <input type="submit" value="Continue" class="button" />
+ <a class="cancel" href="/cgi-bin/koha/tools/tools-home.pl">Cancel</a>
+ </fieldset>
+ </form>
+ [% ELSIF view == 'list' %]
+ [% IF records %]
+ [% IF recordtype == 'biblio' %]
+ <div id="toolbar">
+ <a id="selectall" href="#">Select All</a>
+ | <a id="clearall" href="#">Clear All</a>
+ </div>
+ <form action="/cgi-bin/koha/tools/batch_record_modification.pl" method="post" id="process">
+ <table id="biblios" class="records">
+ <thead>
+ <tr>
+ <th></th>
+ <th>Biblionumber</th>
+ <th>Title</th>
+ <th>Preview</th>
+ </tr>
+ </thead>
+ <tbody>
+ [% FOR biblio IN records %]
+ <tr>
+ <td><input type="checkbox" name="record_id" value="[% biblio.biblionumber %]" data-items="[% biblio.itemnumbers.size %]" data-issues="[% biblio.issues_count %]" data-reserves="[% biblio.reserves.size %]" /></td>
+ <td>[% biblio.biblionumber %]</td>
+ <td><a href="/cgi-bin/koha/catalogue/detail.pl?biblionumber=[% biblio.biblionumber %]">[% biblio.title %]</a></td>
+ <td><a href="/cgi-bin/koha/svc/records/preview?record_type=biblio&record_id=[% biblio.biblionumber %]&mmtid=[% mmtid %]" title="MARC preview" rel="gb_page_center[600,500]">Preview MARC</a>
+
+ </tr>
+ [% END %]
+ </tbody>
+ </table>
+ <div class="note">Reminder: this action will modify all selected biblios!</div>
+ [% ELSE %]
+ <div id="toolbar">
+ <a id="selectall" href="#">Select All</a>
+ | <a id="clearall" href="#">Clear All</a>
+ </div>
+ <form action="/cgi-bin/koha/tools/batch_record_modification.pl" method="post" id="process">
+ <table id="authorities" class="records">
+ <thead>
+ <tr>
+ <th></th>
+ <th>Authid</th>
+ <th>Summary</th>
+ <th>Preview</th>
+ </tr>
+ </thead>
+ <tbody>
+ [% FOR authority IN records %]
+ <tr>
+ <td><input type="checkbox" name="record_id" value="[% authority.authid %]" data-usage="[% authority.count_usage %]" /></td>
+ <td><a href="/cgi-bin/koha/authorities/detail.pl?authid=[% authority.authid %]">[% authority.authid %]</a></td>
+ <td>[% PROCESS authresult summary=authority.summary %]</td>
+ <td><a href="/cgi-bin/koha/svc/records/preview?record_type=authority&record_id=[% authority.authid %]&mmtid=[% mmtid %]" title="MARC preview" rel="gb_page_center[600,500]">Preview MARC</a>
+ </tr>
+ [% END %]
+ </tbody>
+ </table>
+ <div class="note">Reminder: this action will modify all selected authorities!</div>
+ [% END %]
+ <fieldset class="action">
+ <input type="hidden" name="op" value="modify" />
+ <input type="hidden" name="recordtype" value="[% recordtype %]" />
+ <input type="button" id="mainformsubmit" value="Modify selected records" class="button" />
+ <input type="hidden" name="runinbackground" id="runinbackground" value="" />
+ <input type="hidden" name="completedJobID" id="completedJobID" value="" />
+ <input type="hidden" name="marc_modification_template_id" value="[% mmtid %]" />
+ <a class="cancel" href="/cgi-bin/koha/tools/batch_record_modification.pl">Cancel</a>
+ </fieldset>
+ <div id="jobpanel">
+ <div id="jobstatus">Job progress: <div id="jobprogress"></div> <span id="jobprogresspercent">0</span>%</div>
+ <div id="jobfailed"></div>
+ </div>
+ </form>
+ [% ELSE %]
+ There is no record ids defined.
+ [% END %]
+ [% ELSIF view == 'report' %]
+ [% IF report.total_records == report.total_success %]
+ All records have successfully been modified!
+ [% ELSE %]
+ [% report.total_success %] / [% report.total_records %] records have successfully been modified.
+ Some errors occurred.
+ [% END %]
+ <p><a href="/cgi-bin/koha/tools/batch_record_modification.pl" title="New batch record modification">New batch record modification</a></p>
+ [% ELSIF view == 'errors' %]
+ [% FOR error IN errors %]
+ [% IF error == 'no_template_defined' %]
+ No MARC modification template is defined. You have <a href="/cgi-bin/koha/tools/marc_modification_templates.pl">to create</a> at least one template for using this tool.
+ [% END %]
+ [% END %]
+ [% ELSE %]
+ No action defined for the template.
+ [% END %]
+</div>
+</div>
+<div class="yui-b">
+ [% INCLUDE 'tools-menu.inc' %]
+</div>
+</div>
+[% INCLUDE 'intranet-bottom.inc' %]
--- /dev/null
+#!/usr/bin/perl
+
+# This file is part of Koha.
+#
+# Copyright 2013 BibLibre
+#
+# 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
+# <http://www.gnu.org/licenses>
+
+use Modern::Perl;
+
+use CGI;
+use List::MoreUtils qw( uniq );
+
+use C4::Auth qw( get_template_and_user );
+use C4::Output qw( output_html_with_http_headers );
+use C4::AuthoritiesMarc qw( BuildSummary GetAuthTypeCode ModAuthority );
+use C4::BackgroundJob;
+use C4::Biblio qw( GetMarcBiblio ModBiblio );
+use C4::MarcModificationTemplates qw( GetModificationTemplateActions GetModificationTemplates ModifyRecordWithTemplate );
+use Koha::Authority;
+
+my $input = new CGI;
+our $dbh = C4::Context->dbh;
+my $op = $input->param('op') // q|form|;
+my $recordtype = $input->param('recordtype') // 'biblio';
+my $mmtid = $input->param('marc_modification_template_id');
+
+my ( @messages );
+
+my ( $template, $loggedinuser, $cookie ) = get_template_and_user({
+ template_name => 'tools/batch_record_modification.tt',
+ query => $input,
+ type => "intranet",
+ authnotrequired => 0,
+ flagsrequired => { tools => 'biblio_batchmod' },
+});
+
+
+my $sessionID = $input->cookie("CGISESSID");
+
+my $runinbackground = $input->param('runinbackground');
+my $completedJobID = $input->param('completedJobID');
+if ( $completedJobID ) {
+ my $job = C4::BackgroundJob->fetch($sessionID, $completedJobID);
+ my $report = $job->get('report');
+ my $messages = $job->get('messages');
+ $template->param(
+ report => $report,
+ messages => $messages,
+ view => 'report',
+ );
+ output_html_with_http_headers $input, $cookie, $template->output;
+ exit;
+}
+
+my @templates = GetModificationTemplates();
+unless ( @templates ) {
+ $op = 'error';
+ $template->param(
+ view => 'errors',
+ errors => ['no_template_defined'],
+ );
+ output_html_with_http_headers $input, $cookie, $template->output;
+}
+
+if ( $mmtid ) {
+ my @actions = GetModificationTemplateActions( $mmtid );
+ unless ( @actions ) {
+ $op = 'form';
+ push @messages, {
+ type => 'error',
+ code => 'no_action_defined_for_the_template',
+ mmtid => $mmtid,
+ };
+ }
+}
+
+if ( $op eq 'form' ) {
+ # Display the form
+ $template->param(
+ view => 'form',
+ MarcModificationTemplatesLoop => \@templates,
+ );
+} elsif ( $op eq 'list' ) {
+ # List all records to process
+ my ( @records, @record_ids );
+ if ( my $bib_list = $input->param('bib_list') ) {
+ # Come from the basket
+ @record_ids = split /\//, $bib_list;
+ $recordtype = 'biblio';
+ } elsif ( my $uploadfile = $input->param('uploadfile') ) {
+ # A file of id is given
+ while ( my $content = <$uploadfile> ) {
+ next unless $content;
+ $content =~ s/[\r\n]*$//;
+ push @record_ids, $content if $content;
+ }
+ } else {
+ # The user enters manually the list of id
+ push @record_ids, split( /\s\n/, $input->param('recordnumber_list') );
+ }
+
+ for my $record_id ( uniq @record_ids ) {
+ if ( $recordtype eq 'biblio' ) {
+ # Retrieve biblio information
+ my $biblio = C4::Biblio::GetBiblio( $record_id );
+ unless ( $biblio ) {
+ push @messages, {
+ type => 'warning',
+ code => 'biblio_not_exists',
+ biblionumber => $record_id,
+ };
+ next;
+ }
+ push @records, $biblio;
+ } else {
+ # Retrieve authority information
+ my $authority = Koha::Authority->get_from_authid( $record_id );
+ unless ( $authority ) {
+ push @messages, {
+ type => 'warning',
+ code => 'authority_not_exists',
+ authid => $record_id,
+ };
+ next;
+ }
+
+ push @records, {
+ authid => $record_id,
+ summary => C4::AuthoritiesMarc::BuildSummary( $authority->record, $record_id ),
+ };
+ }
+ }
+ $template->param(
+ records => \@records,
+ mmtid => $mmtid,
+ view => 'list',
+ );
+} elsif ( $op eq 'modify' ) {
+ # We want to modify selected records!
+ my @record_ids = $input->param('record_id');
+
+ my ( $job );
+ if ( $runinbackground ) {
+ my $job_size = scalar( @record_ids );
+ $job = C4::BackgroundJob->new( $sessionID, "FIXME", $ENV{SCRIPT_NAME}, $job_size );
+ my $job_id = $job->id;
+ if (my $pid = fork) {
+ $dbh->{InactiveDestroy} = 1;
+
+ my $reply = CGI->new("");
+ print $reply->header(-type => 'text/html');
+ print '{"jobID":"' . $job_id . '"}';
+ exit 0;
+ } elsif (defined $pid) {
+ close STDOUT;
+ close STDERR;
+ } else {
+ warn "fork failed while attempting to run $ENV{'SCRIPT_NAME'} as a background job";
+ exit 0;
+ }
+ }
+
+ my $report = {
+ total_records => 0,
+ total_success => 0,
+ };
+ my $progress = 0;
+ $dbh->{RaiseError} = 1;
+ RECORD_IDS: for my $record_id ( sort { $a <=> $b } @record_ids ) {
+ $report->{total_records}++;
+ next unless $record_id;
+
+ if ( $recordtype eq 'biblio' ) {
+ # Biblios
+ my $biblionumber = $record_id;
+
+ # Finally, modify the biblio
+ my $error = eval {
+ my $record = GetMarcBiblio( $biblionumber );
+ ModifyRecordWithTemplate( $mmtid, $record );
+ ModBiblio( $record, $biblionumber );
+ };
+ if ( $error and $error != 1 or $@ ) { # ModBiblio returns 1 if everything as gone well
+ push @messages, {
+ type => 'error',
+ code => 'biblio_not_modified',
+ biblionumber => $biblionumber,
+ error => ($@ ? $@ : $error),
+ };
+ } else {
+ push @messages, {
+ type => 'success',
+ code => 'biblio_modified',
+ biblionumber => $biblionumber,
+ };
+ $report->{total_success}++;
+ }
+ } else {
+ # Authorities
+ my $authid = $record_id;
+ my $error = eval {
+ my $authority = Koha::Authority->get_from_authid( $authid );
+ my $record = $authority->record;
+ ModifyRecordWithTemplate( $mmtid, $record );
+ ModAuthority( $authid, $record, GetAuthTypeCode( $authid ) );
+ };
+ if ( $error and $error != $authid or $@ ) {
+ push @messages, {
+ type => 'error',
+ code => 'authority_not_modified',
+ authid => $authid,
+ error => ($@ ? $@ : 0),
+ };
+ } else {
+ push @messages, {
+ type => 'success',
+ code => 'authority_modified',
+ authid => $authid,
+ };
+ $report->{total_success}++;
+ }
+ }
+
+ $job->set({
+ view => 'report',
+ report => $report,
+ messages => \@messages,
+ });
+ $job->progress( ++$progress ) if $runinbackground;
+ }
+
+ if ($runinbackground) {
+ $job->finish if defined $job;
+ } else {
+ $template->param(
+ view => 'report',
+ report => $report,
+ messages => \@messages,
+ );
+ }
+}
+
+$template->param(
+ messages => \@messages,
+ recordtype => $recordtype,
+);
+
+output_html_with_http_headers $input, $cookie, $template->output;