diff --git a/C4/Form/AddItem.pm b/C4/Form/AddItem.pm new file mode 100644 index 0000000000..693baedb19 --- /dev/null +++ b/C4/Form/AddItem.pm @@ -0,0 +1,381 @@ +package C4::Form::AddItem; + +# Copyright 2009 Jesse Weaver +# +# 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., 59 Temple Place, +# Suite 330, Boston, MA 02111-1307 USA + +use strict; +use warnings; + +use CGI; +use C4::Context; +use C4::Debug; +use C4::Dates; +use C4::ClassSource; +use C4::Biblio; +use C4::Branch qw( GetBranches ); +use C4::Koha qw( subfield_is_koha_internal_p ); # XXX subfield_is_koha_internal_p +use MARC::Record; +use MARC::File::XML; + +=head1 NAME + +C4::Form::AddItem - manage item values input form + +=head1 SYNOPSIS + +In script: + + use C4::Form::AddItem; + C4::Form::AddItem::set_form_values( $existing_values, $template ); + +In HTML template: + + + +=head1 DESCRIPTION + +This module manages the add item screen used in the import profiles +functionality of the stage-marc-import.pl tool. It is used both by the tool +itself and the service that returns existing item definitions for a profile. + +=head1 FUNCTIONS + +=head2 handle_form_action + + C4::Form::MessagingPreferences::handle_form_action( $input, { categorycode => 'CPL' }, $template ); + +Processes CGI parameters and updates the target patron or patron category's +preferences. + +C<$input> is the CGI query object. + +C<$target_params> is a hashref containing either a C key or a C key +identifying the patron or patron category whose messaging preferences are to be updated. + +C<$template> is the HTML::Template::Pro object for the response; this routine +adds a settings_updated template variable. + +=cut + +sub get_item_record { + my ( $input, $frameworkcode, $item_index, $itemnumber ) = @_; + my $dbh = C4::Context->dbh; + my @tags = $input->param( "tag_$item_index" ); + my @subfields = $input->param( "subfield_$item_index" ); + my @values = $input->param( "field_value_$item_index" ); + # build indicator hash. + my @ind_tag = $input->param( "ind_tag_$item_index" ); + my @indicator = $input->param( "indicator_$item_index" ); + # my $itemnumber = $input->param( 'itemnumber' ); + my $xml = TransformHtmlToXml( \@tags, \@subfields, \@values, \@indicator, \@ind_tag, 'ITEM' ); + my @params = $input->param(); + my $itemtosave=MARC::Record::new_from_xml( $xml, 'UTF-8' ); + if ( !$itemnumber && C4::Context->preference( 'autoBarcode' ) eq 'incremental' ) { + my ( $tagfield, $tagsubfield ) = &GetMarcFromKohaField( "items.barcode", $frameworkcode ); + unless ( $itemtosave->field( $tagfield )->subfield( $tagsubfield ) ) { + my $sth_barcode = $dbh->prepare( "select max( abs( barcode ) ) from items" ); + $sth_barcode->execute; + my ( $newbarcode ) = $sth_barcode->fetchrow; + $newbarcode++; + # OK, we have the new barcode, now create the entry in MARC record + my $fieldItem = $itemtosave->field( $tagfield ); + $itemtosave->delete_field( $fieldItem ); + $fieldItem->add_subfields( $tagsubfield => $newbarcode ); + $itemtosave->insert_fields_ordered( $fieldItem ); + } + } + # MARC::Record builded => now, record in DB + # warn "R: ".$record->as_formatted; + # check that the barcode don't exist already + my $addedolditem = TransformMarcToKoha( $dbh, $itemtosave ); + my $exist_itemnumber = get_item_from_barcode( $addedolditem->{'barcode'} ); + return ( $itemtosave, ( $exist_itemnumber && $exist_itemnumber != $itemnumber ) ); +} + +sub get_all_items { + my ( $input, $frameworkcode ) = @_; + + my @items = $input->param( 'items' ); + + return map { my ($item_record, $not_unique) = get_item_record( $input, $frameworkcode, $_ ); $item_record } @items; +} + +=head2 set_form_values + + C4::Form::MessagingPreferences::set_form_value( { borrowernumber => 51 }, $template ); + +Retrieves the messaging preferences for the specified patron or patron category +and fills the corresponding template variables. + +C<$target_params> is a hashref containing either a C key or a C key +identifying the patron or patron category. + +C<$template> is the HTML::Template::Pro object for the response. + +=cut + +=head2 get_form_values + + C4::Form::AddItem::get_form_values( $item_index, $existing_record ); + +Creates the item addition form, and returns an arrayref that can be used in a +template. + +C<$item_index> The index, from 0, of the item ( used to distinguish multiple +forms, like on the import screen ) + +C<$existing_record> If basing this on an existing item/created item, this +should be the relevant MARC blob for that item. + +=cut + +sub get_form_values { + my ( $tagslib, $item_index, $options ) = @_; + $options ||= { }; + $options = { + biblio => MARC::Record->new(), + frameworkcode => '', + omit => [], + allow_repeatable => 1, + %$options + }; + my $dbh = C4::Context->dbh; + my @loop_data =( ); + my $i=0; + my $today_iso = C4::Dates->today( 'iso' ); + my $authorised_values_sth = $dbh->prepare( "SELECT authorised_value,lib FROM authorised_values WHERE category=? ORDER BY lib" ); + + my $onlymine = C4::Context->preference( 'IndependantBranches' ) && + C4::Context->userenv && + C4::Context->userenv->{flags} % 2 == 0 && + C4::Context->userenv->{branch}; + my $branches = GetBranches( $onlymine ); # build once ahead of time, instead of multiple times later. + + foreach my $tag ( sort keys %{$tagslib} ) { + # loop through each subfield + foreach my $subfield ( sort keys %{$tagslib->{$tag}} ) { + next if subfield_is_koha_internal_p( $subfield ); + my $subfieldlib = $tagslib->{$tag}->{$subfield}; + next if ( $subfieldlib->{'tab'} ne "10" ); + next if ( $subfieldlib->{'kohafield'} && $options->{'omit'} && grep( { $_ eq $subfieldlib->{'kohafield'} } @{ $options->{'omit'} } ) ); + my %subfield_data; + + my $index_subfield = int( rand( 1000000 ) ); + if ( $subfield eq '@' ){ + $subfield_data{id} = "tag_".$tag."_subfield_00_".$index_subfield; + } else { + $subfield_data{id} = "tag_".$tag."_subfield_".$subfield."_".$index_subfield; + } + $subfield_data{item_index} = $item_index; + $subfield_data{tag} = $tag; + $subfield_data{subfield} = $subfield; + $subfield_data{random} = int( rand( 1000000 )); # why do we need 2 different randoms? + # $subfield_data{marc_lib} = $tagslib->{$tag}->{$subfield}->{lib}; + $subfield_data{marc_lib} ="{lib}."\">".$subfieldlib->{lib}.""; + $subfield_data{mandatory} = $subfieldlib->{mandatory}; + $subfield_data{repeatable} = $subfieldlib->{repeatable} && $options->{'allow_repeatable'}; + my ( $indicator, $value ) = ( '', '' ); + ( $indicator, $value ) = _find_value( $tag,$subfield, $options->{'item'} ) if ( $options->{'item'} ); + $value =~ s/"/"/g; + unless ( $value ) { + $value = $subfieldlib->{defaultvalue} || ''; + # get today date & replace YYYY, MM, DD if provided in the default value + my ( $year, $month, $day ) = split '-', $today_iso; + $value =~ s/YYYY/$year/g; + $value =~ s/MM/$month/g; + $value =~ s/DD/$day/g; + } + $subfield_data{visibility} = "display:none;" if ( ($subfieldlib->{hidden} > 4 ) || ( $subfieldlib->{hidden} < -4 )); + # testing branch value if IndependantBranches. + my $pref_itemcallnumber = C4::Context->preference( 'itemcallnumber' ); + if ( !$value && $subfieldlib->{kohafield} eq 'items.itemcallnumber' && $pref_itemcallnumber ) { + my $CNtag = substr( $pref_itemcallnumber, 0, 3 ); + my $CNsubfield = substr( $pref_itemcallnumber, 3, 1 ); + my $CNsubfield2 = substr( $pref_itemcallnumber, 4, 1 ); + my $temp2 = $options->{'biblio'}->field( $CNtag ); + if ( $temp2 ) { + $value = ( $temp2->subfield( $CNsubfield )).' '.( $temp2->subfield( $CNsubfield2 )); + #remove any trailing space incase one subfield is used + $value =~ s/^\s+|\s+$//g; + } + } + + my $attributes_no_value = qq( tabindex="1" id="$subfield_data{id}" name="field_value_$item_index" class="input_marceditor" size="67" maxlength="255" ); + my $attributes = qq( $attributes_no_value value="$value" ); + if ( $subfieldlib->{authorised_value} ) { + my @authorised_values; + my %authorised_lib; + # builds list, depending on authorised value... + + if ( $subfieldlib->{authorised_value} eq "branches" ) { + foreach my $thisbranch ( sort keys %$branches ) { + push @authorised_values, $thisbranch; + $authorised_lib{$thisbranch} = $branches->{$thisbranch}->{'branchname'}; + } + } + elsif ( $subfieldlib->{authorised_value} eq "itemtypes" ) { + push @authorised_values, "" unless ( $subfieldlib->{mandatory} ); + my $sth = $dbh->prepare( "select itemtype,description from itemtypes order by description" ); + $sth->execute; + my $itemtype; # FIXME: double declaration of $itemtype + while ( my ( $itemtype, $description ) = $sth->fetchrow_array ) { + push @authorised_values, $itemtype; + $authorised_lib{$itemtype} = $description; + } + + my ( $itemtype_tag, $itemtype_subfield ) = &GetMarcFromKohaField( "biblioitems.itemtype", $options->{'frameworkcode'} ); + my $itemtype_field = $options->{'biblio'}->field( $itemtype_tag ); + if ( !$value && $itemtype_field && $itemtype_field->subfield( $itemtype_subfield ) ) { + $value = $itemtype_field->subfield( $itemtype_subfield ); + } + + #---- class_sources + } + elsif ( $subfieldlib->{authorised_value} eq "cn_source" ) { + push @authorised_values, "" unless ( $subfieldlib->{mandatory} ); + + my $class_sources = GetClassSources( ); + my $default_source = C4::Context->preference( "DefaultClassificationSource" ); + + foreach my $class_source ( sort keys %$class_sources ) { + next unless $class_sources->{$class_source}->{'used'} or + ( $value and $class_source eq $value ) or + ( $class_source eq $default_source ); + push @authorised_values, $class_source; + $authorised_lib{$class_source} = $class_sources->{$class_source}->{'description'}; + } + $value = $default_source unless ( $value ); + + #---- "true" authorised value + } + else { + push @authorised_values, "" unless ( $subfieldlib->{mandatory} ); + $authorised_values_sth->execute( $subfieldlib->{authorised_value} ); + while ( my ( $value, $lib ) = $authorised_values_sth->fetchrow_array ) { + push @authorised_values, $value; + $authorised_lib{$value} = $lib; + } + } + $subfield_data{marc_value} =CGI::scrolling_list( # FIXME: factor out scrolling_list + -name => "field_value_$item_index", + -values => \@authorised_values, + -default => $value, + -labels => \%authorised_lib, + -override => 1, + -size => 1, + -multiple => 0, + -tabindex => 1, + -id => "tag_".$tag."_subfield_".$subfield."_".$index_subfield, + -class => "input_marceditor", + ); + # it's a thesaurus / authority field + } + elsif ( $subfieldlib->{authtypecode} ) { + $subfield_data{marc_value} = " + ... + "; + # it's a plugin field + } + elsif ( $subfieldlib->{value_builder} ) { + # opening plugin + my $plugin = C4::Context->intranetdir . "/cataloguing/value_builder/" . $subfieldlib->{'value_builder'}; + if ( do $plugin ) { + my $extended_param = plugin_parameters( $dbh, $options->{'biblio'}, $tagslib, $subfield_data{id}, \@loop_data ); + my ( $function_name, $javascript ) = plugin_javascript( $dbh, $options->{'biblio'}, $tagslib, $subfield_data{id}, \@loop_data ); + $subfield_data{marc_value} = qq[ + ... + $javascript]; + } else { + warn "Plugin Failed: $plugin"; + $subfield_data{marc_value} = ""; # supply default input form + } + } + elsif ( $tag eq '' ) { # it's an hidden field + $subfield_data{marc_value} = qq( ); + } + elsif ( $subfieldlib->{'hidden'} ) { # FIXME: shouldn't input type be "hidden" ? + $subfield_data{marc_value} = qq( ); + } + elsif ( length( $value ) > 100 + or ( C4::Context->preference( "marcflavour" ) eq "UNIMARC" and + 300 <= $tag && $tag < 400 && $subfield eq 'a' ) + or ( C4::Context->preference( "marcflavour" ) eq "MARC21" and + 500 <= $tag && $tag < 600 ) + ) { + # oversize field ( textarea ) + $subfield_data{marc_value} = "\n"; + } else { + # it's a standard field + $subfield_data{marc_value} = ""; + } + # $subfield_data{marc_value}=""; + push ( @loop_data, \%subfield_data ); + $i++ + } + } + + return \@loop_data; +} + +sub _find_value { + my ( $tagfield,$insubfield,$record ) = @_; + my $indicator = ''; + my $result = ''; + foreach my $field ( $record->field( $tagfield )) { + my @subfields = $field->subfields( ); + foreach my $subfield ( @subfields ) { + if ( @$subfield[0] eq $insubfield ) { + $result .= @$subfield[1]; + $indicator = $field->indicator( 1 ).$field->indicator( 2 ); + } + } + } + return( $indicator,$result ); +} + +sub get_item_from_barcode { + my ( $barcode )=@_; + my $dbh=C4::Context->dbh; + my $result; + my $rq=$dbh->prepare( "SELECT itemnumber from items where items.barcode=?" ); + $rq->execute( $barcode ); + ( $result )=$rq->fetchrow; + return( $result ); +} + +=head1 TODO + +=over 4 + +=item Generalize into a system of form handler clases + +=back + +=head1 SEE ALSO + +F + +=head1 AUTHOR + +Jesse Weaver + +=cut + +1; diff --git a/C4/ImportBatch.pm b/C4/ImportBatch.pm index d0bfc72a38..ee666e3e96 100644 --- a/C4/ImportBatch.pm +++ b/C4/ImportBatch.pm @@ -22,6 +22,7 @@ use warnings; use C4::Context; use C4::Koha; +use C4::Dates; use C4::Biblio; use C4::Items; use C4::Charset; @@ -69,6 +70,11 @@ BEGIN { SetImportRecordStatus GetImportRecordMatches SetImportRecordMatches + AddImportProfile + GetImportProfile + GetImportProfileLoop + GetImportProfileItems + GetImportProfileSubfieldActions ); } @@ -208,6 +214,44 @@ sub AddBiblioToBatch { my $encoding = shift; my $z3950random = shift; my $update_counts = @_ ? shift : 1; + my $subfield_actions = @_ ? shift : []; + + foreach my $action (@$subfield_actions) { + my @fields = $marc_record->field($action->{'tag'}); + if ( $action->{'action'} eq 'delete' ) { + foreach my $field (@fields) { + $marc_record->delete_field($field); + } + } elsif ( $action->{'action'} eq 'delete_sub' ) { + foreach my $field (@fields) { + $field->delete_subfield(code => $action->{'subfield'}); + } + } elsif ( $action->{'action'} eq 'delete_match' ) { + foreach my $field (@fields) { + if ($field->subfield($action->{'subfield'})){ + my $cmp1 = _normalize_string($field->subfield($action->{'subfield'})); + my $cmp2 = _normalize_string($action->{'contents'}); + if ($cmp1 eq $cmp2){ + $field->delete_subfield(code => $action->{'subfield'}); + } + } + } + } elsif ( $action->{'action'} eq 'add_always' ) { + $marc_record->insert_grouped_field(MARC::Field->new($action->{'tag'},'','',$action->{'subfield'} => $action->{'contents'})); + } elsif ( $action->{'action'} eq 'add' ) { + my $found =0; + foreach my $field (@fields) { + if ($field->subfield($action->{'subfield'})){ + my $cmp1 = _normalize_string($field->subfield($action->{'subfield'})); + my $cmp2 = _normalize_string($action->{'contents'}); + $found = 1 if ($cmp1 eq $cmp2); + } + } + if (!$found){ + $marc_record->insert_grouped_field(MARC::Field->new($action->{'tag'},'','',$action->{'subfield'} => $action->{'contents'})); + } + } + } my $import_record_id = _create_import_record($batch_id, $record_sequence, $marc_record, 'biblio', $encoding, $z3950random); _add_biblio_fields($import_record_id, $marc_record); @@ -255,6 +299,8 @@ sub BatchStageMarcRecords { my $branch_code = shift; my $parse_items = shift; my $leave_as_staging = shift; + my $added_items = shift; + my $subfield_actions = shift; # optional callback to monitor status # of job @@ -268,7 +314,8 @@ sub BatchStageMarcRecords { } my $batch_id = AddImportBatch('create_new', 'staging', 'batch', $file_name, $comments); - if ($parse_items) { +use Data::Dumper; warn Dumper(@$added_items); + if ($parse_items || @$added_items) { SetImportBatchItemAction($batch_id, 'always_add'); } else { SetImportBatchItemAction($batch_id, 'ignore'); @@ -279,6 +326,8 @@ sub BatchStageMarcRecords { my $num_items = 0; # FIXME - for now, we're dealing only with bibs my $rec_num = 0; + my ($barcode_tag, $barcode_subfield) = &GetMarcFromKohaField( "items.barcode", '' ); + my (undef, $dateaccessioned_subfield) = &GetMarcFromKohaField( "items.dateaccessioned", '' ); foreach my $marc_blob (split(/\x1D/, $marc_records)) { $marc_blob =~ s/^\s+//g; $marc_blob =~ s/\s+$//g; @@ -294,8 +343,17 @@ sub BatchStageMarcRecords { push @invalid_records, $marc_blob; } else { $num_valid++; - $import_record_id = AddBiblioToBatch($batch_id, $rec_num, $marc_record, $marc_flavor, int(rand(99999)), 0); - if ($parse_items) { + $import_record_id = AddBiblioToBatch($batch_id, $rec_num, $marc_record, $marc_flavor, int(rand(99999)), 0, $subfield_actions); + if (@$added_items) { + foreach my $item ( @$added_items ) { + my $field = $item->field($barcode_tag); + $field->add_subfields( + $dateaccessioned_subfield => C4::Dates->today() + ) if ( !$field->subfield( $dateaccessioned_subfield ) ); + $marc_record->append_fields($field); + } + } + if ($parse_items || @$added_items) { my @import_items_ids = AddItemsToImportBiblio($batch_id, $import_record_id, $marc_record, 0); $num_items += scalar(@import_items_ids); } @@ -1245,8 +1303,151 @@ sub SetImportRecordMatches { } } +sub AddImportProfile { + my ($description, $matcher_id, $template_id, $overlay_action, $nomatch_action, $parse_items, $item_action, $added_items, $subfield_actions) = @_; + + my $dbh = C4::Context->dbh; + + my $profile_id = $dbh->selectrow_array("SELECT profile_id FROM import_profiles WHERE description = ?", {}, $description); + + my $sth; + + if ($profile_id) { + $sth = $dbh->prepare(" + UPDATE + import_profiles + SET matcher_id = ?, template_id = ?, overlay_action = ?, nomatch_action = ?, parse_items = ?, item_action = ? + WHERE profile_id = ? + "); + + $sth->execute($matcher_id, $template_id, $overlay_action, $nomatch_action, $parse_items, $item_action, $profile_id); + $dbh->do(" + DELETE + FROM import_profile_added_items + WHERE profile_id = ? + ", {}, $profile_id); + $dbh->do(" + DELETE + FROM import_profile_subfield_actions + WHERE profile_id = ? + ", {}, $profile_id); + } else { + $sth = $dbh->prepare(" + INSERT + INTO import_profiles(description, matcher_id, template_id, overlay_action, nomatch_action, parse_items, item_action) + VALUES(?, ?, ?, ?, ?, ?, ?) + "); + + $sth->execute($description, $matcher_id, $template_id, $overlay_action, $nomatch_action, $parse_items, $item_action); + $profile_id = $dbh->{mysql_insertid}; + } + + $sth = $dbh->prepare(" + INSERT + INTO import_profile_added_items(profile_id, marcxml) + VALUES(?, ?) + "); + + foreach my $item (@$added_items) { + $sth->execute($profile_id, $item->as_xml()); + } + + $sth = $dbh->prepare(" + INSERT + INTO import_profile_subfield_actions(profile_id, tag, subfield, action, contents) + VALUES(?, ?, ?, ?, ?) + "); + + foreach my $action (@$subfield_actions) { + $sth->execute($profile_id, $action->{'tag'}, $action->{'subfield'}, $action->{'action'}, $action->{'contents'}); + } +} + +sub GetImportProfile { + my $profile_id = shift; + + my $dbh = C4::Context->dbh; + + my $profile = $dbh->selectrow_hashref( " + SELECT + description, matcher_id, template_id, overlay_action, nomatch_action, parse_items, item_action + FROM import_profiles + WHERE profile_id = ? + ", {}, $profile_id ); +} + +sub GetImportProfileLoop { + my $dbh = C4::Context->dbh; + + my $results = $dbh->selectall_arrayref( " + SELECT + import_profiles.profile_id, description, matcher_id, template_id, overlay_action, nomatch_action, parse_items, item_action, COUNT( marcxml ) AS added_items, ip_actions.tag, ip_actions.subfield + FROM import_profiles + LEFT JOIN import_profile_added_items AS ip_items ON ( import_profiles.profile_id = ip_items.profile_id ) + LEFT JOIN import_profile_subfield_actions AS ip_actions ON ( import_profiles.profile_id = ip_actions.profile_id ) + GROUP BY import_profiles.profile_id, ip_actions.tag, ip_actions.subfield + ", { Slice => {} } ); + return [] unless ( @$results ); + + my @profiles; + my $current_profile; + + foreach my $result ( @$results ) { + if ( $current_profile && $current_profile->{'profile_id'} == $result->{'profile_id'} ) { + push @{ $current_profile->{'modified_subfields'} }, { tag => $result->{'tag'}, subfield => $result->{'subfield'} } + } else { + push @profiles, $result; + $current_profile = $result; + $current_profile->{'modified_subfields'} = $result->{'tag'} ? [ { tag => $result->{'tag'}, subfield => $result->{'subfield'} } ] : []; + } + delete $result->{'tag'}; + delete $result->{'subfield'}; + } + + return \@profiles; +} + +sub GetImportProfileItems { + my ( $profile_id ) = @_; + my $dbh = C4::Context->dbh; + + return map { MARC::Record::new_from_xml( $_, 'UTF-8' ) } @{ $dbh->selectcol_arrayref( " + SELECT + marcxml + FROM import_profile_added_items + WHERE profile_id = ? + ", {}, $profile_id ) }; +} + +sub GetImportProfileSubfieldActions { + my ( $profile_id ) = @_; + my $dbh = C4::Context->dbh; + + return map { + +{ + "action_type_" . $_->{'action_type'} => 1, + %$_ + }; + } @{ $dbh->selectall_arrayref( " + SELECT + tag as action_tag, subfield as action_subfield, action as action_type, contents as action_contents + FROM import_profile_subfield_actions + WHERE profile_id = ? + ", { Slice => {} }, $profile_id ) }; +} # internal functions +sub _normalize_string { + my ($str) = @_; + $str = uc $str; + $str =~ s/^\s+//; + $str =~ s/\s+$//; + $str =~ s/\W+$//; + $str =~ s/\s+$//; + $str =~ s/\s+/_/g; + $str =~ s/__/_/g; + return $str; +} sub _create_import_record { my ($batch_id, $record_sequence, $marc_record, $record_type, $encoding, $z3950random) = @_; diff --git a/cataloguing/additem.pl b/cataloguing/additem.pl index 8ff4940f2b..6c65dcec30 100755 --- a/cataloguing/additem.pl +++ b/cataloguing/additem.pl @@ -20,15 +20,14 @@ use CGI; use strict; +use warnings; use C4::Auth; use C4::Output; use C4::Biblio; use C4::Items; use C4::Context; -use C4::Koha; # XXX subfield_is_koha_internal_p -use C4::Branch; # XXX subfield_is_koha_internal_p -use C4::ClassSource; use C4::Dates; +use C4::Form::AddItem; use MARC::File::XML; @@ -72,8 +71,8 @@ sub set_item_default_location { my $dbh = C4::Context->dbh; my $error = $input->param('error'); my $biblionumber = $input->param('biblionumber'); -my $itemnumber = $input->param('itemnumber'); -my $op = $input->param('op'); +my $itemnumber = $input->param('itemnumber') || ''; +my $op = $input->param('op') || ''; my ($template, $loggedinuser, $cookie) = get_template_and_user({template_name => "cataloguing/additem.tmpl", @@ -98,15 +97,7 @@ sub set_item_default_location { #------------------------------------------------------------------------------- if ($op eq "additem") { #------------------------------------------------------------------------------- - # rebuild - my @tags = $input->param('tag'); - my @subfields = $input->param('subfield'); - my @values = $input->param('field_value'); - # build indicator hash. - my @ind_tag = $input->param('ind_tag'); - my @indicator = $input->param('indicator'); - my $xml = TransformHtmlToXml(\@tags,\@subfields,\@values,\@indicator,\@ind_tag, 'ITEM'); - my $record = MARC::Record::new_from_xml($xml, 'UTF-8'); + my ( $record, $barcode_not_unique ) = C4::Form::AddItem::get_item_record( $input, $frameworkcode, 0 ); # type of add my $add_submit = $input->param('add_submit'); @@ -114,24 +105,6 @@ sub set_item_default_location { my $add_multiple_copies_submit = $input->param('add_multiple_copies_submit'); my $number_of_copies = $input->param('number_of_copies'); - # if autoBarcode is set to 'incremental', calculate barcode... - # NOTE: This code is subject to change in 3.2 with the implemenation of ajax based autobarcode code - # NOTE: 'incremental' is the ONLY autoBarcode option available to those not using javascript - if (C4::Context->preference('autoBarcode') eq 'incremental') { - my ($tagfield,$tagsubfield) = &GetMarcFromKohaField("items.barcode",$frameworkcode); - unless ($record->field($tagfield)->subfield($tagsubfield)) { - my $sth_barcode = $dbh->prepare("select max(abs(barcode)) from items"); - $sth_barcode->execute; - my ($newbarcode) = $sth_barcode->fetchrow; - $newbarcode++; - # OK, we have the new barcode, now create the entry in MARC record - my $fieldItem = $record->field($tagfield); - $record->delete_field($fieldItem); - $fieldItem->add_subfields($tagsubfield => $newbarcode); - $record->insert_fields_ordered($fieldItem); - } - } - my $addedolditem = TransformMarcToKoha($dbh,$record); # If we have to add or add & duplicate, we add the item @@ -207,7 +180,7 @@ sub set_item_default_location { } # Checking if the barcode already exists - $exist_itemnumber = get_item_from_barcode($barcodevalue); + $exist_itemnumber = C4::Form::AddItem::get_item_from_barcode($barcodevalue); } # Adding the item @@ -268,22 +241,9 @@ sub set_item_default_location { } elsif ($op eq "saveitem") { #------------------------------------------------------------------------------- # rebuild - my @tags = $input->param('tag'); - my @subfields = $input->param('subfield'); - my @values = $input->param('field_value'); - # build indicator hash. - my @ind_tag = $input->param('ind_tag'); - my @indicator = $input->param('indicator'); - # my $itemnumber = $input->param('itemnumber'); - my $xml = TransformHtmlToXml(\@tags,\@subfields,\@values,\@indicator,\@ind_tag,'ITEM'); - my $itemtosave=MARC::Record::new_from_xml($xml, 'UTF-8'); - # MARC::Record builded => now, record in DB - # warn "R: ".$record->as_formatted; - # check that the barcode don't exist already - my $addedolditem = TransformMarcToKoha($dbh,$itemtosave); - my $exist_itemnumber = get_item_from_barcode($addedolditem->{'barcode'}); - if ($exist_itemnumber && $exist_itemnumber != $itemnumber) { - push @errors,"barcode_not_unique"; + my ( $itemtosave, $barcode_not_unique ) = C4::Form::AddItem::get_item_record( $input, $frameworkcode, 0, $itemnumber ); + if ( $barcode_not_unique ) { + push @errors, 'barcode_not_unique'; } else { my ($oldbiblionumber,$oldbibnum,$oldbibitemnum) = ModItemFromMarc($itemtosave,$biblionumber,$itemnumber); $itemnumber=""; @@ -323,7 +283,7 @@ sub set_item_default_location { || $subf[$i][1]; } - if (($field->tag eq $branchtagfield) && ($subf[$i][$0] eq $branchtagsubfield) && C4::Context->preference("IndependantBranches")) { + if (($field->tag eq $branchtagfield) && ($subf[$i][0] eq $branchtagsubfield) && C4::Context->preference("IndependantBranches")) { #verifying rights my $userenv = C4::Context->userenv(); unless (($userenv->{'flags'} == 1) or (($userenv->{'branch'} eq $subf[$i][1]))){ @@ -559,7 +519,11 @@ sub set_item_default_location { author => $oldrecord->{author}, item_loop => \@item_value_loop, item_header_loop => \@header_value_loop, - item => \@loop_data, + item => C4::Form::AddItem::get_form_values( $tagslib, 0, { + item => $itemrecord, + biblio => $temp, + frameworkcode => $frameworkcode, + }), itemnumber => $itemnumber, itemtagfield => $itemtagfield, itemtagsubfield => $itemtagsubfield, diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/background-job.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/background-job.inc index 3cfe0294c7..f1b39c6033 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/includes/background-job.inc +++ b/koha-tmpl/intranet-tmpl/prog/en/includes/background-job.inc @@ -72,7 +72,7 @@ backgroundJobProgressTimer = setInterval("updateJobProgress()", 500); }, error: function(xml, textStatus) { - alert('Failed to submit form: ' + testStatus); + alert('Failed to submit form: ' + textStatus); } }); diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/import-subfield-action-form.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/import-subfield-action-form.inc new file mode 100644 index 0000000000..db94258813 --- /dev/null +++ b/koha-tmpl/intranet-tmpl/prog/en/includes/import-subfield-action-form.inc @@ -0,0 +1,14 @@ +
  • + + tag " size="3" maxlength="3" /> + + + + +
  • diff --git a/koha-tmpl/intranet-tmpl/prog/en/includes/item-fields.inc b/koha-tmpl/intranet-tmpl/prog/en/includes/item-fields.inc new file mode 100644 index 0000000000..a51da34ebf --- /dev/null +++ b/koha-tmpl/intranet-tmpl/prog/en/includes/item-fields.inc @@ -0,0 +1,15 @@ + +
  • "> + + + " value=" " /> + + " value="" /> + " value="" /> + " value="" /> + + ')">+ + + +
  • + diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/cataloguing/additem.tmpl b/koha-tmpl/intranet-tmpl/prog/en/modules/cataloguing/additem.tmpl index 6f3ab61be3..67e1e4aaa8 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/cataloguing/additem.tmpl +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/cataloguing/additem.tmpl @@ -270,8 +270,7 @@ function set_to_today(id, force) { - - + " />
    @@ -292,10 +291,17 @@ function set_to_today(id, force) { +<<<<<<< HEAD:koha-tmpl/intranet-tmpl/prog/en/modules/cataloguing/additem.tmpl " /> " /> " /> +======= + " /> + " /> + " /> + +>>>>>>> Bug4247:koha-tmpl/intranet-tmpl/prog/en/modules/cataloguing/additem.tmpl
    diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/pieces/import-profile-actions.tmpl b/koha-tmpl/intranet-tmpl/prog/en/modules/tools/pieces/import-profile-actions.tmpl new file mode 100644 index 0000000000..8c26dd1dd8 --- /dev/null +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/tools/pieces/import-profile-actions.tmpl @@ -0,0 +1,4 @@ + + + + diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/pieces/import-profile-items.tmpl b/koha-tmpl/intranet-tmpl/prog/en/modules/tools/pieces/import-profile-items.tmpl new file mode 100644 index 0000000000..625cdc717b --- /dev/null +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/tools/pieces/import-profile-items.tmpl @@ -0,0 +1,6 @@ + +
      " class="item"> + +
    +" /> + diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/stage-marc-import.tmpl b/koha-tmpl/intranet-tmpl/prog/en/modules/tools/stage-marc-import.tmpl index 8660db0e2c..b9c60a1f6a 100644 --- a/koha-tmpl/intranet-tmpl/prog/en/modules/tools/stage-marc-import.tmpl +++ b/koha-tmpl/intranet-tmpl/prog/en/modules/tools/stage-marc-import.tmpl @@ -19,13 +19,224 @@ function CheckForm(f) { if ($("#fileToUpload").value == '') { alert('Please upload a file first.'); } else { + // This hack is required due to an odd jQuery/AJAX/Firefox Heisenbug where empty fields don't get submitted + $( ':input[name=items]' ).each( function () { + var item = this.value; + var field_values = f['field_value_' + item]; + for ( var i = field_values.length - 1; i; i-- ) { + if ( field_values[i].value == '' ) { + $( field_values[i] ).parents( '.subfield_line' ).find( ':input[name$=_' + item + ']' ).attr('disabled', 'disabled'); + } + } + } ); return submitBackgroundJob(f); } return false; } + + +var available_profiles = { + + : { + matcher_id: "", + overlay_action: "", + nomatch_action: "", + parse_items: "", + item_action: "", + added_items: , + modified_subfields: [ [ "", "" ], ] + }, + +}; + +$( document ).ready( function( ){ + $( '#profile' ).change( function( ){ + var profile = $( this ).val( ); + + if ( !profile ) return; + + profile = available_profiles[profile]; + + $( '#matcher' ).val( profile.matcher_id ); + $( '#overlay_action' ).val( profile.overlay_action ); + $( '#nomatch_action' ).val( profile.nomatch_action ); + $( '#parse_items' + ( profile.parse_items ? 'yes' : 'no' ) ).val( ['parse_items'] ); + $( '#item_action' ).val( profile.item_action ); + if ( profile.added_items ) { + $( '#profile-items-num-added' ).text( profile.added_items ); + $( '#profile-items-info' ).show(); + $( '#add-items input[name=include_items_from_profile]' ).val( 1 ); + } else { + $( '#profile-items-info:visible' ).hide(); + } + if ( profile.modified_subfields.length ) { + var modified_subfields = []; + $.each( profile.modified_subfields, function ( i, subfield ) { + modified_subfields.push( subfield[0] + '$' + subfield[1] ); + } ); + console.log( modified_subfields ); + $( '#profile-actions-subfields' ).text( modified_subfields.join( ', ' ) ); + $( '#profile-actions-info' ).show(); + $( '#modify-subfields input[name=include_actions_from_profile]' ).val( 1 ); + } else { + $( '#profile-actions-info:visible' ).hide(); + } + } ); +} ); + + + +function change_type() { + if ( this.value == 'delete' ) { + $( this ).nextAll( '.action-contents-text' ).eq( 0 ).hide(); + $( this ).nextAll( '.action-contents-subfield' ).eq( 0 ).hide(); + } else if ( this.value == 'delete_sub' ){ + $( this ).nextAll( '.action-contents-text' ).eq( 0 ).hide(); + $( this ).nextAll( '.action-contents-subfield' ).eq( 0 ).show(); + } else { + $( this ).nextAll( '.action-contents-text' ).eq( 0 ).show(); + $( this ).nextAll( '.action-contents-subfield' ).eq( 0 ).show(); + } +} + +function verify_tag() { + if ( /^[0-9]{3}$/.test( this.value ) ) { + $( this ).removeClass( 'alert' ); + } else { + $( this ).addClass( 'alert' ); + } +} + +function verify_subfield() { + if ( /^[0-9a-z]$/.test( this.value ) ) { + $( this ).removeClass( 'alert' ); + } else { + $( this ).addClass( 'alert' ); + } +} + +function remove_action() { + $( this ).parent().remove(); +} + +function new_action() { + var new_row = $( this ).parent().clone(); + + bind_subfield_actions( new_row ); + $( new_row ) + .find( 'input[type=text]' ).val( '' ).end() + .find( 'select' ).val( 'delete' ).change().end() + .find( '.remove-action' ).show().end() + .insertAfter( $( this ).parent() ); +} + +function bind_subfield_actions( selector ) { + $( selector ) + .find( 'select' ).change( change_type ).end() + .find( 'input[name=action_tag]' ).keyup( verify_tag ).end() + .find( 'input[name=action_subfield]' ).keyup( verify_subfield ).end() + .find( '.remove-action' ).click( remove_action ).end() + .find( '.new-action' ).click( new_action ).end(); +} + +function remove_item() { + var parent = $( this ).parents( '.item' ); + var id = parent.attr( 'id' ).split( '-' )[1]; + + $( '#add-items input:hidden[name=items][value=' + id + ']' ).remove(); + if ( id == 0 ) { + parent.hide(); + } else { + parent.remove(); + } +} + +$( document ).ready( function () { + $( '#subfield-actions .remove-action' ).hide(); + bind_subfield_actions( '#subfield-actions' ); + $( '#new-item' ).click( function () { + if ( $( '#item-0' ).is( ':hidden' ) ) { + $( '#item-0' ).show(); + $( '#add-items' ).append( '' ); + } else { + var new_item = $( '#item-0' ).clone(); + + var item_id = Math.floor( Math.random() * 100000000 ); + + $( new_item ).attr( 'id', 'item-' + item_id ).find( ':text, input:hidden, select' ).attr( 'name', function () { + var cur_name = $( this ).attr( 'name').split( '_' ); + cur_name[cur_name.length - 1] = item_id; + + return cur_name.join( '_' ); + } ); + + $( new_item ) + .find( ':text' ).val( '' ).end() + .find( 'select' ).each( function () { this.selectedIndex = 0 } ).end() // Really need to find a better way of resetting ' ); + } + } ); + $( '.remove-item' ).click( remove_item ); + $( '#profile-items-modify' ).click( function () { + $.ajax( { + type: 'GET', + url: '/cgi-bin/koha/tools/stage-marc-import.pl', + dataType: 'html', + data: { profile: $( '#profile' ).val(), retrieve: 'profile-items' }, + success: function ( data ) { + $( data ) + .find( '.remove-item' ).click( remove_item ).end() + .insertBefore( '#item-0' ); + $( '#profile-items-info' ).hide(); + $( '#modify-subfields input[name=include_items_from_profile]' ).val( 0 ); + } + } ); + return false; + } ); + $( '#profile-actions-modify' ).click( function () { + $.ajax( { + type: 'GET', + url: '/cgi-bin/koha/tools/stage-marc-import.pl', + dataType: 'html', + data: { profile: $( '#profile' ).val(), retrieve: 'profile-actions' }, + success: function ( data ) { + var new_actions = $( data ); + bind_subfield_actions( new_actions ); + new_actions.appendTo( '#subfield-actions' ) + .find( 'select' ).change().end(); + $( '#profile-actions-info' ).hide(); + $( '#modify-subfields input[name=include_actions_from_profile]' ).val( 0 ); + } + } ); + return false; + } ); +} ); +function set_to_today(id, force) { + if (! id) { alert("Bad id " + id + " sent to set_to_today()"); return 0; } + if ($("#" + id).val() == '' || $("#" + id).val() == '0000-00-00' || force) { + $("#" + id).val(""); + } +} //]]> + @@ -43,6 +254,7 @@ function CheckForm(f) {

    MARC Staging results :

      +
    • Saved settings as profile ""
    • records in file
    • records not staged because of MARC error
    • records staged
    • @@ -88,7 +300,6 @@ function CheckForm(f) {
      " enctype="multipart/form-data">
      -
      1. @@ -105,6 +316,28 @@ function CheckForm(f) {
      +
      + Import profiles +
        + +
      1. + + +
      2. + +

        +

        +
      3. + + +
      4. +
      +
      Look for existing records in catalog?
      1. @@ -142,6 +375,34 @@ function CheckForm(f) {
      +
      + Add more items to each record? +
        + +
      1. NOTE: if you select to add new items here, any item records in the incoming bibliographic record will also be unavoidably added!
      + + +
        +
      1. +
      2. If you make changes to this form, and want to save them, be sure to save the profile!
      3. + +
      +
      +
      + Modify subfields of each record? + + +
        + +
      +
      Job progress:
      0%
      diff --git a/tools/stage-marc-import.pl b/tools/stage-marc-import.pl index d8de9055f8..21ab3a2540 100755 --- a/tools/stage-marc-import.pl +++ b/tools/stage-marc-import.pl @@ -40,6 +40,8 @@ use C4::Matcher; use C4::UploadedFile; use C4::BackgroundJob; +use C4::Form::AddItem; +use C4::Dates; my $input = new CGI; my $dbh = C4::Context->dbh; @@ -55,15 +57,45 @@ my $item_action = $input->param('item_action'); my $comments = $input->param('comments'); my $syntax = $input->param('syntax'); +my $profile = $input->param('profile'); +my $new_profile_name = $input->param('new_profile_name'); +my $retrieve = $input->param('retrieve') || ''; + +my $template_name = "tools/stage-marc-import.tmpl"; +if ( $retrieve eq 'profile-items' ) { + $template_name = 'tools/pieces/import-profile-items.tmpl'; +} elsif ( $retrieve eq 'profile-actions' ) { + $template_name = 'tools/pieces/import-profile-actions.tmpl'; +} + my ($template, $loggedinuser, $cookie) - = get_template_and_user({template_name => "tools/stage-marc-import.tmpl", + = get_template_and_user({template_name => $template_name, query => $input, type => "intranet", - authnotrequired => 0, + authnotrequired => 1, flagsrequired => {tools => 'stage_marc_import'}, debug => 1, }); +my $tagslib = &GetMarcStructure(1, ''); # Assuming default framework + +if ( $retrieve ) { + if ( $retrieve eq 'profile-items' ) { + $template->param( items => [ map { + my $item_index = int(rand(10000000)); + +{ item_index => $item_index, item => C4::Form::AddItem::get_form_values( $tagslib, $item_index, { + item => $_, + omit => [ 'items.barcode' ], + allow_repeatable => 0, + } ) }; + } GetImportProfileItems( $profile ) ] ); + } elsif ( $retrieve eq 'profile-actions' ) { + $template->param( actions => [ GetImportProfileSubfieldActions( $profile ) ] ); + } + output_html_with_http_headers $input, $cookie, $template->output; + exit; +} + $template->param(SCRIPT_NAME => $ENV{'SCRIPT_NAME'}, uploadmarc => $fileID); @@ -88,6 +120,30 @@ my $job = undef; my $staging_callback = sub { }; my $matching_callback = sub { }; + + my @additional_items; + my @subfield_actions; + + my @added_items = C4::Form::AddItem::get_all_items($input, ''); + + if ( $profile ) { + if ( $input->param( 'include_items_from_profile' ) && !@added_items) { + push @additional_items, GetImportProfileItems( $profile ); + } + if ( $input->param( 'include_actions_from_profile' ) ) { + push @subfield_actions, GetImportProfileSubfieldActions( $profile ); + } + } + + push @additional_items, @added_items; +# push @additional_items, C4::Form::AddItem::get_all_items( $input, '' ); + push @subfield_actions, get_subfield_actions( $input ); + + if ( $new_profile_name ) { + $new_profile_name =~ s/^\s+|\s+$//g; + AddImportProfile( $new_profile_name, $matcher_id, undef, $overlay_action, $nomatch_action, $parse_items, $item_action, \@additional_items, \@subfield_actions ); + } + if ($runinbackground) { my $job_size = () = $marcrecord =~ /\035/g; # if we're matching, job size is doubled @@ -123,15 +179,16 @@ # if we get here, we're a child that has detached # itself from Apache - $staging_callback = staging_progress_callback($job, $dbh); - $matching_callback = matching_progress_callback($job, $dbh); + # $staging_callback = staging_progress_callback($job, $dbh); + # $matching_callback = matching_progress_callback($job, $dbh); } # FIXME branch code my ($batch_id, $num_valid, $num_items, @import_errors) = BatchStageMarcRecords($syntax, $marcrecord, $filename, $comments, '', $parse_items, 0, - 50, staging_progress_callback($job, $dbh)); + \@additional_items, \@subfield_actions); + # 50, staging_progress_callback($job, $dbh)); $dbh->commit(); my $num_with_matches = 0; my $checked_matches = 0; @@ -142,12 +199,17 @@ if (defined $matcher) { $checked_matches = 1; $matcher_code = $matcher->code(); - $num_with_matches = BatchFindBibDuplicates($batch_id, $matcher, - 10, 50, matching_progress_callback($job, $dbh)); + #$num_with_matches = BatchFindBibDuplicates($batch_id, $matcher, + # 10, 50, matching_progress_callback($job, $dbh)); + $num_with_matches = BatchFindBibDuplicates($batch_id, $matcher); SetImportBatchMatcher($batch_id, $matcher_id); SetImportBatchOverlayAction($batch_id, $overlay_action); SetImportBatchNoMatchAction($batch_id, $nomatch_action); - SetImportBatchItemAction($batch_id, $item_action); + if ( @additional_items) { + SetImportBatchItemAction($batch_id, 'always_add'); + } else { + SetImportBatchItemAction($batch_id, $item_action); + } $dbh->commit(); } else { $matcher_failed = 1; @@ -165,20 +227,14 @@ matcher_code => $matcher_code, import_batch_id => $batch_id }; + + $results->{'saved_profile'} = $new_profile_name if ($new_profile_name); + if ($runinbackground) { $job->finish($results); } else { - $template->param(staged => $num_valid, - matched => $num_with_matches, - num_items => $num_items, - import_errors => scalar(@import_errors), - total => $num_valid + scalar(@import_errors), - checked_matches => $checked_matches, - matcher_failed => $matcher_failed, - matcher_code => $matcher_code, - import_batch_id => $batch_id - ); - } + $template->param($results); + } } else { # initial form @@ -186,7 +242,12 @@ $template->param("UNIMARC" => 1); } my @matchers = C4::Matcher::GetMatcherList(); - $template->param(available_matchers => \@matchers); + $template->param( + available_matchers => \@matchers, + available_profiles => GetImportProfileLoop(), + item => C4::Form::AddItem::get_form_values( $tagslib, 0, { omit => [ 'items.barcode' ], allow_repeatable => 0 } ), + today_iso => C4::Dates->today( 'iso' ), + ); } output_html_with_http_headers $input, $cookie, $template->output; @@ -213,3 +274,25 @@ sub matching_progress_callback { $dbh->commit(); } } + +sub get_subfield_actions { + my ( $input ) = @_; + + my @types = $input->param( 'action_type' ); + my @tags = $input->param( 'action_tag' ); + my @subfields = $input->param( 'action_subfield' ); + my @contents = $input->param( 'action_contents' ); + my @results; + + foreach my $i ( 0..$#types ) { + next unless ( $tags[$i] ); + push @results, { + action => $types[$i], + tag => $tags[$i], + subfield => $subfields[$i], + contents => $contents[$i], + }; + } + + return @results; +}