[Koha-patches] [PATCH] MARC Modifications Templates

Kyle M Hall kyle.m.hall at gmail.com
Fri Apr 30 16:24:56 CEST 2010


The MARC Modification Templates system gives Koha users
the power to make alterations to MARC records automatically
while staging MARC records for import.

This tool is useful for altering MARC records from
various venders work with your MARC framework.

The system essentially allows one to create a basic script
using actions to Copy, Move, Add, Update and Delete fields.

Each action can also have an optional condition to check
the value or existance of another field.

The Copy & Move actions also support Regular Expressions,
which can be used to automatically modify field values during the
copy/move. An example would be to strip out the '$' character
in field 020$c.

Furthermore, the value for an update can include variables
that change each time the template is used. Currently,
the system supports two variables, __BRANCHCODE__ which
is replaced with the branchcode of the library currently
using the template, and __CURRENTDATE__ which is replaced
with the current date in ISO format ( YYYY-MM-DD ).

At its simplist, it can perform functions such as:
Copy field 092$a to 952$c
At its most complex it can run actions like:
Copy field 020$c to 020$c using RegEx s/\$// if 020$c equals RegEx m/^\$/

This patch also includes SimpleMARC. A module to facilitate
easy scripting of marc record modifications.
---
 C4/ImportBatch.pm                                  |    8 +-
 C4/MarcModificationTemplates.pm                    |  521 ++++++++++++++++++++
 C4/SimpleMARC.pm                                   |  313 ++++++++++++
 admin/marc_modification_templates.pl               |  146 ++++++
 installer/data/mysql/updatedatabase.pl             |   34 ++
 .../prog/en/modules/admin/admin-home.tmpl          |    2 +
 .../modules/admin/marc_modification_templates.tmpl |  450 +++++++++++++++++
 .../prog/en/modules/catalogue/results.tmpl         |   11 +-
 .../prog/en/modules/tools/stage-marc-import.tmpl   |   14 +
 tools/stage-marc-import.pl                         |   10 +-
 10 files changed, 1500 insertions(+), 9 deletions(-)
 create mode 100644 C4/MarcModificationTemplates.pm
 create mode 100644 C4/SimpleMARC.pm
 create mode 100755 admin/marc_modification_templates.pl
 create mode 100644 koha-tmpl/intranet-tmpl/prog/en/modules/admin/marc_modification_templates.tmpl

diff --git a/C4/ImportBatch.pm b/C4/ImportBatch.pm
index ede33dd..560832b 100644
--- a/C4/ImportBatch.pm
+++ b/C4/ImportBatch.pm
@@ -25,6 +25,7 @@ use C4::Koha;
 use C4::Biblio;
 use C4::Items;
 use C4::Charset;
+use C4::MarcModificationTemplates;
 
 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
 
@@ -238,7 +239,7 @@ sub ModBiblioInBatch {
 =over 4
 
 ($batch_id, $num_records, $num_items, @invalid_records) = 
-    BatchStageMarcRecords($marc_flavor, $marc_records, $file_name, 
+    BatchStageMarcRecords($marc_flavor, $marc_records, $file_name, $marc_modification_template,
                           $comments, $branch_code, $parse_items,
                           $leave_as_staging, 
                           $progress_interval, $progress_callback);
@@ -247,10 +248,11 @@ sub ModBiblioInBatch {
 
 =cut
 
-sub  BatchStageMarcRecords {
+sub BatchStageMarcRecords {
     my $marc_flavor = shift;
     my $marc_records = shift;
     my $file_name = shift;
+    my $marc_modification_template = shift;
     my $comments = shift;
     my $branch_code = shift;
     my $parse_items = shift;
@@ -289,12 +291,14 @@ sub  BatchStageMarcRecords {
         }
         my ($marc_record, $charset_guessed, $char_errors) =
             MarcToUTF8Record($marc_blob, C4::Context->preference("marcflavour"));
+        ModifyRecordWithTemplate( $marc_modification_template, $marc_record ) if ( $marc_modification_template );
         my $import_record_id;
         if (scalar($marc_record->fields()) == 0) {
             push @invalid_records, $marc_blob;
         } else {
             $num_valid++;
             $import_record_id = AddBiblioToBatch($batch_id, $rec_num, $marc_record, $marc_flavor, int(rand(99999)), 0);
+            warn "test";
             if ($parse_items) {
                 my @import_items_ids = AddItemsToImportBiblio($batch_id, $import_record_id, $marc_record, 0);
                 $num_items += scalar(@import_items_ids);
diff --git a/C4/MarcModificationTemplates.pm b/C4/MarcModificationTemplates.pm
new file mode 100644
index 0000000..e690232
--- /dev/null
+++ b/C4/MarcModificationTemplates.pm
@@ -0,0 +1,521 @@
+package C4::MarcModificationTemplates;
+
+# Copyright 2010 Kyle M Hall <kyle.m.hall at gmail.com>
+#
+# 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 strict;
+use warnings;
+
+use C4::Context;
+use C4::Dates qw(format_date);
+
+use C4::SimpleMARC;
+
+use vars qw($VERSION @ISA @EXPORT);
+
+BEGIN { 
+	$VERSION = 1.00;	# set the version for version checking
+	@ISA = qw(Exporter);
+	@EXPORT = qw(
+		&GetModificationTemplates
+		&AddModificationTemplate
+		&DelModificationTemplate
+		
+		&GetModificationTemplateAction
+		&GetModificationTemplateActions
+		
+		&AddModificationTemplateAction
+		&ModModificationTemplateAction
+		&DelModificationTemplateAction
+		&MoveModificationTemplateAction
+		
+                &ModifyRecordsWithTemplate
+		&ModifyRecordWithTemplate
+	);
+}
+
+=head1 NAME
+
+C4::MarcModificationTemplates - Module to manage MARC Modification Templates
+
+=head1 DESCRIPTION
+
+MARC Modification Templates are a tool for marc batch imports, 
+so that librarians can set up templates for various vendors'
+files telling Koha what fields to insert data into.
+
+=head1 FUNCTIONS
+
+=cut
+
+=head2
+  GetModificationTemplates
+
+  my @templates = GetModificationTemplates( [ $template_id ] );
+  
+  Passing a $template_id will mark the given id as the selected template.
+  Useful for TMPL_LOOPs.
+=cut
+
+sub GetModificationTemplates {
+  my ( $template_id ) = @_;
+
+  my $dbh = C4::Context->dbh;
+  my $sth = $dbh->prepare("SELECT * FROM marc_modification_templates");
+  $sth->execute();
+  
+  my @templates;
+  while ( my $template = $sth->fetchrow_hashref() ) {
+    $template->{'selected'} = 1 if ( $template->{'template_id'} eq $template_id );
+    push( @templates, $template );
+  }
+  
+  return @templates;
+}
+
+=head2
+  AddModificationTemplate
+
+  $template_id = AddModificationTemplate( $template_name[, $template_id ] );
+  
+  If $template_id is supplied, the actions from that template will be copied
+  into the newly created template.
+=cut
+
+sub AddModificationTemplate {
+  my ( $template_name, $template_id_copy ) = @_;
+
+  my $dbh = C4::Context->dbh;
+  my $sth = $dbh->prepare("INSERT INTO marc_modification_templates ( name ) VALUES ( ? )");
+  $sth->execute( $template_name );
+
+  $sth = $dbh->prepare("SELECT * FROM marc_modification_templates WHERE name = ?");
+  $sth->execute( $template_name );
+  my $row = $sth->fetchrow_hashref();
+  my $template_id = $row->{'template_id'};
+  
+  if ( $template_id_copy ) {
+    my @actions = GetModificationTemplateActions( $template_id_copy );
+    foreach my $action ( @actions ) {
+      AddModificationTemplateAction(
+        $template_id,
+        $action->{'action'},
+        $action->{'from_field'},
+        $action->{'from_subfield'},
+        $action->{'field_value'},  
+        $action->{'to_field'},     
+        $action->{'to_subfield'},
+        $action->{'to_regex'},
+        $action->{'conditional'},  
+        $action->{'conditional_field'},
+        $action->{'conditional_subfield'},
+        $action->{'conditional_comparison'},
+        $action->{'conditional_value'}
+      );
+    
+    }
+  }
+  
+  return $template_id;
+}
+
+=head2
+  DelModificationTemplate
+
+  DelModificationTemplate( $template_id );
+=cut
+
+sub DelModificationTemplate {
+  my ( $template_id ) = @_;
+
+  my $dbh = C4::Context->dbh;
+  my $sth = $dbh->prepare("DELETE FROM marc_modification_templates WHERE template_id = ?");
+  $sth->execute( $template_id );
+
+  $sth = $dbh->prepare("DELETE FROM marc_modification_template_actions WHERE template_id = ?");
+  $sth->execute( $template_id );
+}
+
+=head2
+  GetModificationTemplateAction
+  
+  my $action = GetModificationTemplateAction( $mmta_id );
+=cut
+
+sub GetModificationTemplateAction {
+  my ( $mmta_id ) = @_;
+
+  my $dbh = C4::Context->dbh;
+  my $sth = $dbh->prepare("SELECT * FROM marc_modification_template_actions WHERE mmta_id = ?");
+  $sth->execute( $mmta_id );
+  my $action = $sth->fetchrow_hashref();
+  
+  return $action;  
+}
+
+=head2
+  GetModificationTemplateActions
+  
+  my @actions = GetModificationTemplateActions( $template_id );
+=cut
+
+sub GetModificationTemplateActions {
+  my ( $template_id ) = @_;
+
+  my $dbh = C4::Context->dbh;
+  my $sth = $dbh->prepare("SELECT * FROM marc_modification_template_actions WHERE template_id = ? ORDER BY `order`");
+  $sth->execute( $template_id );
+  
+  my @actions;
+  while ( my $action = $sth->fetchrow_hashref() ) {
+    push( @actions, $action );
+  }
+  
+  return @actions;
+}
+
+=head2
+  AddModificationTemplateAction
+  
+  AddModificationTemplateAction(
+    $template_id, $action, $from_field,
+    $from_subfield, $field_value, $to_field,     
+    $to_subfield, $to_regex, $conditional,  
+    $conditional_field, $conditional_subfield,  
+    $conditional_comparison, $conditional_value,
+    $conditional_regex
+  );
+  
+  Adds a new action to the given modification template.
+
+=cut
+
+sub AddModificationTemplateAction {
+  my (
+    $template_id,
+    $action, 
+    $from_field,
+    $from_subfield,
+    $field_value,  
+    $to_field,     
+    $to_subfield,
+    $to_regex,
+    $conditional,  
+    $conditional_field,
+    $conditional_subfield,
+    $conditional_comparison,
+    $conditional_value,
+    $conditional_regex
+  ) = @_;
+
+  my $dbh = C4::Context->dbh;
+  my $sth = $dbh->prepare( 'SELECT MAX(`order`) + 1 AS next_order FROM `marc_modification_template_actions` WHERE template_id = ?' );
+  $sth->execute( $template_id );
+  my $row = $sth->fetchrow_hashref;
+  my $order = $row->{'next_order'} || 1;
+  
+  my $query = "
+  INSERT INTO `marc_modification_template_actions` (
+  `mmta_id`,
+  `template_id`,
+  `order`,
+  `action`,
+  `from_field`,
+  `from_subfield`,
+  `field_value`,
+  `to_field`,
+  `to_subfield`,
+  `to_regex`,
+  `conditional`,
+  `conditional_field`,
+  `conditional_subfield`,
+  `conditional_comparison`,
+  `conditional_value`,
+  `conditional_regex`
+  )
+  VALUES ( NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ? )";
+  
+  $sth = $dbh->prepare( $query );
+  
+  $sth->execute(
+    $template_id,
+    $order,
+    $action, 
+    $from_field,
+    $from_subfield,
+    $field_value,  
+    $to_field,     
+    $to_subfield,  
+    $to_regex,
+    $conditional,  
+    $conditional_field,
+    $conditional_subfield,
+    $conditional_comparison,
+    $conditional_value,
+    $conditional_regex
+  );  
+}
+
+=head2
+  ModModificationTemplateAction
+  
+  ModModificationTemplateAction(
+    $mmta_id, $action, $from_field,
+    $from_subfield, $field_value, $to_field,     
+    $to_subfield, $to_regex, $conditional,  
+    $conditional_field, $conditional_subfield,  
+    $conditional_comparison, $conditional_value,
+    $conditional_regex
+  );
+  
+  Modifies an existing action.
+
+=cut
+
+sub ModModificationTemplateAction {
+  my (
+    $mmta_id,
+    $action, 
+    $from_field,
+    $from_subfield,
+    $field_value,  
+    $to_field,     
+    $to_subfield,
+    $to_regex,
+    $conditional,  
+    $conditional_field,
+    $conditional_subfield,
+    $conditional_comparison,
+    $conditional_value,
+    $conditional_regex
+  ) = @_;
+
+  my $dbh = C4::Context->dbh;
+  
+  my $query = "
+  UPDATE `marc_modification_template_actions` SET
+  `action` = ?,
+  `from_field` = ?,
+  `from_subfield` = ?,
+  `field_value` = ?,
+  `to_field` = ?,
+  `to_subfield` = ?,
+  `to_regex` = ?,
+  `conditional` = ?,
+  `conditional_field` = ?,
+  `conditional_subfield` = ?,
+  `conditional_comparison` = ?,
+  `conditional_value` = ?,
+  `conditional_regex` = ?
+  WHERE `mmta_id` = ?";
+  
+  my $sth = $dbh->prepare( $query );
+  
+  $sth->execute(
+    $action, 
+    $from_field,
+    $from_subfield,
+    $field_value,  
+    $to_field,     
+    $to_subfield,  
+    $to_regex,
+    $conditional,  
+    $conditional_field,
+    $conditional_subfield,
+    $conditional_comparison,
+    $conditional_value,
+    $conditional_regex,
+    $mmta_id
+  );  
+}
+
+
+=head2
+  DelModificationTemplateAction
+
+  DelModificationTemplateAction( $mmta_id );
+  
+  Deletes the given template action.
+=cut
+
+sub DelModificationTemplateAction {
+  my ( $mmta_id ) = @_;
+  
+  my $action = GetModificationTemplateAction( $mmta_id );
+  
+  my $dbh = C4::Context->dbh;
+  my $sth = $dbh->prepare("DELETE FROM marc_modification_template_actions WHERE mmta_id = ?");
+  $sth->execute( $mmta_id );
+
+  $sth = $dbh->prepare("UPDATE marc_modification_template_actions SET `order` = `order` - 1 WHERE template_id = ? AND `order` > ?");
+  $sth->execute( $action->{'template_id'}, $action->{'order'} );
+}
+
+=head2
+  MoveModificationTemplateAction
+
+  MoveModificationTemplateAction( $mmta_id, $where );
+  
+  Changes the order for the given action.
+  Options for $where are 'up', 'down', 'top' and 'bottom'
+=cut
+sub MoveModificationTemplateAction {
+  my ( $mmta_id, $where ) = @_;
+  
+  my $action = GetModificationTemplateAction( $mmta_id );
+  
+  return if ( $action->{'order'} eq '1' && ( $where eq 'up' || $where eq 'top' ) );
+  return if ( $action->{'order'} eq GetModificationTemplateActions( $action->{'template_id'} ) && ( $where eq 'down' || $where eq 'bottom' ) );
+
+  my $dbh = C4::Context->dbh;
+  my ( $sth, $query );
+    
+  if ( $where eq 'up' || $where eq 'down' ) {
+
+    ## For up and down, we just swap the order number with the one above or below it.
+
+    ## Change the order for the other action  
+    $query = "UPDATE marc_modification_template_actions SET `order` = ? WHERE template_id = ? AND `order` = ?";
+    
+    my $order = $action->{'order'};
+    $order-- if ( $where eq 'up' );
+    $order++ if ( $where eq 'down' );
+      
+    $sth = $dbh->prepare( $query );
+    $sth->execute( $action->{'order'}, $action->{'template_id'}, $order );
+    
+    ## Change the order for this action
+    $query = "UPDATE marc_modification_template_actions SET `order` = ? WHERE mmta_id = ?";
+    $sth = $dbh->prepare( $query );
+    $sth->execute( $order, $action->{'mmta_id'} );
+
+  } elsif ( $where eq 'top' ) {
+
+    $sth = $dbh->prepare('UPDATE marc_modification_template_actions SET `order` = `order` + 1 WHERE template_id = ? AND `order` < ?');
+    $sth->execute( $action->{'template_id'}, $action->{'order'} );
+    
+    $sth = $dbh->prepare('UPDATE marc_modification_template_actions SET `order` = 1 WHERE mmta_id = ?');
+    $sth->execute( $mmta_id );
+
+  } elsif ( $where eq 'bottom' ) {
+
+    my $order = GetModificationTemplateActions( $action->{'template_id'} );
+
+    $sth = $dbh->prepare('UPDATE marc_modification_template_actions SET `order` = `order` - 1 WHERE template_id = ? AND `order` > ?');
+    $sth->execute( $action->{'template_id'}, $action->{'order'} );
+    
+    $sth = $dbh->prepare('UPDATE marc_modification_template_actions SET `order` = ? WHERE mmta_id = ?');
+    $sth->execute( $order, $mmta_id );
+  
+  }
+  
+}
+
+=head2
+  ModifyRecordsWithTemplate
+
+  ModifyRecordsWithTemplate( $template_id, $batch );
+  
+  Accepts a template id and a MARC::Batch object.
+=cut
+
+sub ModifyRecordsWithTemplate {
+  my ( $template_id, $batch ) = @_;
+  
+  while ( my $record = $batch->next() ) {
+    ModifyRecordWithTemplate( $template_id, $record );
+  }
+}
+
+=head2
+  ModifyRecordWithTemplate
+
+  ModifyRecordWithTemplate( $template_id, $record )
+  
+  Accepts a MARC::Record object ( $record ) and modifies
+  it based on the actions for the given $template_id
+=cut
+
+sub ModifyRecordWithTemplate {
+  my ( $template_id, $record ) = @_;
+
+  my $current_date = C4::Dates->new()->output('iso');
+  my $branchcode;# = C4::Context->userenv->{branch};  
+  
+  my @actions = GetModificationTemplateActions( $template_id );
+  
+  foreach my $a ( @actions ) {
+    my $action = $a->{'action'};
+    my $from_field = $a->{'from_field'};
+    my $from_subfield = $a->{'from_subfield'};
+    my $field_value = $a->{'field_value'};
+    my $to_field = $a->{'to_field'};
+    my $to_subfield = $a->{'to_subfield'};
+    my $to_regex = $a->{'to_regex'};
+    my $conditional = $a->{'conditional'};
+    my $conditional_field = $a->{'conditional_field'};
+    my $conditional_subfield = $a->{'conditional_subfield'};
+    my $conditional_comparison = $a->{'conditional_comparison'};
+    my $conditional_value = $a->{'conditional_value'};
+    my $conditional_regex = $a->{'conditional_regex'};
+    
+    my $eval = "$action( \$record, '$from_field', '$from_subfield', ";
+    
+    if ( $field_value ) {
+      $field_value =~ s/__CURRENTDATE__/$current_date/g;
+      $field_value =~ s/__BRANCHCODE__/$branchcode/g;
+
+      $eval .= " '$field_value' ";
+    } elsif ( $to_field ) {
+      $eval .= " '$to_field', '$to_subfield', '$to_regex' ";
+    }
+
+    $eval .= ') ';
+
+    if ( $conditional ) {
+      $eval .= " $conditional ( ";
+      
+      if ( $conditional_comparison eq 'exists' ) {
+        $eval .= "field_exists( \$record, '$conditional_field', '$conditional_subfield' )";
+        
+      } elsif ( $conditional_comparison eq 'not_exists' ) {
+        $eval .= "!field_exists( \$record, '$conditional_field', '$conditional_subfield' )";
+        
+      } elsif ( $conditional_comparison eq 'equals' ) {
+        $eval .= "field_equals( \$record, '$conditional_value', '$conditional_field', '$conditional_subfield', '$conditional_regex' ) ";
+        
+      } elsif ( $conditional_comparison eq 'not_equals' ) {
+        $eval .= "!field_equals( \$record, '$conditional_value', '$conditional_field', '$conditional_subfield', '$conditional_regex' ) ";
+      }
+      
+      $eval .= " )";
+    }
+    
+    $eval .= ";";
+    
+    eval $eval;
+
+  }
+}
+1;
+__END__
+
+=head1 AUTHOR
+
+Kyle M Hall
+
+=cut
diff --git a/C4/SimpleMARC.pm b/C4/SimpleMARC.pm
new file mode 100644
index 0000000..35bc09d
--- /dev/null
+++ b/C4/SimpleMARC.pm
@@ -0,0 +1,313 @@
+package C4::SimpleMARC;
+
+# Copyright 2009 Kyle M. Hall <kyle.m.hall at gmail.com>
+
+use strict;
+use warnings;
+
+#use MARC::Record;
+
+require Exporter;
+
+our @ISA = qw(Exporter);
+our %EXPORT_TAGS = ( 'all' => [ qw(
+	
+) ] );
+
+our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
+
+our @EXPORT = qw(
+  read_field
+  update_field
+  copy_field
+  move_field
+  delete_field
+  field_exists
+  field_equals
+);
+
+our $VERSION = '0.01';
+
+=head1 NAME
+
+SimpleMARC - Perl extension for making simple MARC record alterations.
+
+=head1 SYNOPSIS
+
+  use SimpleMARC;
+
+=head1 DESCRIPTION
+
+SimpleMARC is designed to make writing scripts
+to modify MARC records simple and easy.
+
+Every function in the modules requires a 
+MARC::Record object as its first parameter.
+
+=head1 AUTHOR
+
+Kyle Hall <lt>kyle.m.hall at gmail.com<gt>
+
+=head1 COPYRIGHT AND LICENSE
+
+Copyright (C) 2009 by Kyle Hall
+
+This library is free software; you can redistribute it and/or modify
+it under the same terms as Perl itself, either Perl version 5.8.7 or,
+at your option, any later version of Perl 5 you may have available.
+
+=head1 FUNCTIONS
+
+=head2
+
+  copy_field( $record, $fromFieldName, $fromSubfieldName, $toFieldName, $toSubfieldName[, $regex ] );
+
+  Copies a value from one field to another. If a regular expression ( $regex ) is supplied,
+  the value will be transformed by the given regex before being copied into the new field.
+  Example: $regex = 's/Old Text/Replacement Text/'
+
+=cut
+
+sub copy_field {
+  my ( $record, $fromFieldName, $fromSubfieldName, $toFieldName, $toSubfieldName, $regex ) = @_;
+  
+  if ( ! ( $record && $fromFieldName && $toFieldName ) ) { return; }
+  
+  my @values = read_field( $record, $fromFieldName, $fromSubfieldName );
+
+  if ( $regex ) {
+    foreach my $value ( @values ) {
+      $value =~ $regex;
+    }
+  }
+  
+  update_field( $record, $toFieldName, $toSubfieldName, @values );
+ 
+}
+
+=head2
+
+  update_field( $record, $fieldName, $subfieldName, $value[, $value,[ $value ... ] ] );
+
+  Updates a field with the given value, creating it if neccessary.  
+  
+  If multiple values are supplied, they will be used to update a list of repeatable fields
+  until either the fields or the values are all used.
+  
+  If a single value is supllied for a repeated field, that value will be used to update
+  each of the repeated fields.
+  
+=cut
+
+sub update_field {
+  my ( $record, $fieldName, $subfieldName, @values ) = @_;
+
+  #warn "update_field( $record, $fieldName, $subfieldName, @values )";
+  
+  if ( ! ( $record && $fieldName ) ) { return; }
+
+  if ( @values eq 1 ) {
+    _update_repeatable_field_with_single_value( $record, $fieldName, $subfieldName, @values );
+    return;
+  }
+
+  my $i = 0;  
+  my $field; 
+  if ( $subfieldName ) {
+    if ( my @fields = $record->field( $fieldName ) ) {
+#      warn "Field exists. With subfield";
+      foreach my $field ( @fields ) {
+        $field->update( "$subfieldName" => $values[$i++] );
+      }
+    } else {
+#      warn "Field doesn't exist. With subfield";
+      ## Field does not exist, create it.
+      foreach my $value ( @values ) {
+        $field = MARC::Field->new( $fieldName, '', '', "$subfieldName" => $values[$i++] );
+        $record->append_fields( $field );
+      }
+    }
+  } else { ## No subfield    
+    if ( my @fields = $record->field( $fieldName ) ) {
+#      warn "Field exists. No subfield";
+      foreach my $field ( @fields ) {
+        $field->update( $values[$i++] );
+      }
+    } else {
+      ## Field does not exists, create it
+#      warn "Field doesn't exist. No subfield";
+      foreach my $value ( @values ) {
+        $field = MARC::Field->new( $fieldName, $value );
+        $record->append_fields( $field );
+      }
+    }
+  }
+}                            
+
+=head2
+
+  my @values = read_field( $record, $fieldName[, $subfieldName ] );
+  
+  Returns an array of field values for the given field and subfield
+
+=cut
+
+sub read_field {
+  my ( $record, $fieldName, $subfieldName ) = @_;
+
+  my @fields = $record->field( $fieldName );
+
+  return @fields unless $subfieldName;
+  
+  my @subfields;
+  foreach my $field ( @fields ) {
+    my @sf = $field->subfield( $subfieldName );
+    push( @subfields, @sf );
+  }
+  
+  return @subfields;
+}
+
+=head2
+
+  $bool = field_exists( $record, $fieldName[, $subfieldName ]);
+  
+  Returns true if the field exits, false otherwise.
+
+=cut
+
+sub field_exists {
+  my ( $record, $fieldName, $subfieldName ) = @_;
+  
+  if ( ! $record ) { return; }
+  
+  if ( $fieldName && $subfieldName ) {
+    return $record->field( $fieldName ) && $record->subfield( $fieldName, $subfieldName );
+  } elsif ( $fieldName ) {
+    return $record->field( $fieldName ) && 1;
+  } else {
+    return;
+  }
+}
+
+=head2
+
+  $bool = field_equals( $record, $value, $fieldName[, $subfieldName[, $regex ] ]);
+  
+  Returns true if the field equals the given value, false otherwise.
+  
+  If a regular expression ( $regex ) is supplied, the value will be compared using
+  the given regex. Example: $regex = 'm/sought_text/'
+  
+  Caveat: field_equals only checks the first instance of a repeatable field.
+
+=cut
+
+sub field_equals {
+  my ( $record, $value, $fieldName, $subfieldName, $regex ) = @_;
+  
+  if ( ! $record ) { return; }
+  
+  my @field_values = read_field( $record, $fieldName, $subfieldName );
+  my $field_value = $field_values[0];
+  
+  if ( $regex ) {
+    return $field_value =~ $regex;
+  } else {
+    return $field_value eq $value;
+  }
+}
+
+=head2
+
+  move_field( $record, $fromFieldName, $fromSubfieldName, $toFieldName, $toSubfieldName[, $regex ] );
+  
+  Moves a value from one field to another. If a regular expression ( $regex ) is supplied,
+  the value will be transformed by the given regex before being moved into the new field.
+  Example: $regex = 's/Old Text/Replacement Text/'
+
+=cut
+
+sub move_field {
+  my ( $record, $fromFieldName, $fromSubfieldName, $toFieldName, $toSubfieldName, $regex ) = @_;
+  copy_field( $record, $fromFieldName, $fromSubfieldName, $toFieldName, $toSubfieldName, $regex );
+  delete_field( $record, $fromFieldName, $fromSubfieldName );
+}
+
+=head2
+
+  delete_field( $record, $fieldName, $subfieldName );
+  
+  Deletes the given field.
+  
+=cut
+
+sub delete_field {
+  my ( $record, $fieldName, $subfieldName ) = @_;
+  
+#  warn "delete_field( $record, $fieldName, $subfieldName )";
+ 
+  my @fields = $record->field( $fieldName );
+  
+#  warn "delete_field found " . @fields . " fields";
+  
+  if ( @fields && !$subfieldName ) {
+    foreach my $field ( @fields ) {
+      $record->delete_field( $field );
+    }
+  } elsif ( @fields && $subfieldName ) {
+    foreach my $field ( @fields ) {
+      $field->delete_subfield( code => $subfieldName );
+#      warn "Deleted subfield code $subfieldName";
+    }
+  }
+}
+
+=head2
+
+  _update_repeatable_field_with_single_value( $record, $fieldName, $subfieldName, $value );
+  
+  Updates a repeatable field, giving all existing copies of that field the given value.
+  
+  This is an internal function, and thus is not exported.
+  
+=cut
+
+sub _update_repeatable_field_with_single_value {
+  my ( $record, $fieldName, $subfieldName, $value ) = @_;
+
+#  warn "_update_repeatable_field_with_single_value( $record, $fieldName, $subfieldName, $value )";
+
+  if ( ! ( $record && $fieldName ) ) { return; }
+
+  my $field; 
+  if ( $subfieldName ) {
+    if ( my @fields = $record->field( $fieldName ) ) {
+#      warn "Field exists. With subfield";
+      foreach my $field ( @fields ) {
+        $field->update( "$subfieldName" => $value );
+      }
+    } else {
+#      warn "Field doesn't exist. With subfield";
+      ## Field does not exist, create it.
+      $field = MARC::Field->new( $fieldName, '', '', "$subfieldName" => $value );
+      $record->append_fields( $field );
+    }
+  } else { ## No subfield    
+    if ( my @fields = $record->field( $fieldName ) ) {
+#      warn "Field exists. No subfield";
+      foreach my $field ( @fields ) {
+        $field->update( $value );
+      }
+    } else {
+      ## Field does not exists, create it
+#      warn "Field doesn't exist. No subfield";
+      $field = MARC::Field->new( $fieldName, $value );
+      $record->append_fields( $field );
+    }
+  }
+}                            
+
+1;
+__END__
+
diff --git a/admin/marc_modification_templates.pl b/admin/marc_modification_templates.pl
new file mode 100755
index 0000000..ed984cb
--- /dev/null
+++ b/admin/marc_modification_templates.pl
@@ -0,0 +1,146 @@
+#!/usr/bin/perl
+# Copyright 2010 Kyle M Hall <kyle.m.hall at gmail.com>
+#
+# 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 strict;
+use warnings;
+
+use CGI;
+
+use C4::Auth;
+use C4::Koha;
+use C4::Output;
+use C4::MarcModificationTemplates;
+
+my $cgi = new CGI;
+
+my $op = $cgi->param('op');
+my $template_id = $cgi->param('template_id');
+
+my ($template, $loggedinuser, $cookie)
+    = get_template_and_user({template_name => "admin/marc_modification_templates.tmpl",
+			     query => $cgi,
+			     type => "intranet",
+			     authnotrequired => 0,
+			     flagsrequired => {parameters => 1},
+			     debug => 1,
+			     });
+
+if ( $op eq "create_template" ) {
+
+  $template_id = '' unless $cgi->param('duplicate_current_template');
+  $template_id = AddModificationTemplate( $cgi->param('template_name'), $template_id );
+
+} elsif ( $op eq "delete_template" ) {
+
+  DelModificationTemplate( $template_id );
+  $template_id = '';
+
+} elsif ( $op eq "add_action" ) {
+
+  my $mmta_id = $cgi->param('mmta_id');
+  my $action = $cgi->param('action');
+  my $from_field = $cgi->param('from_field');
+  my $from_subfield = $cgi->param('from_subfield');
+  my $field_value = $cgi->param('field_value');
+  my $to_field = $cgi->param('to_field');
+  my $to_subfield = $cgi->param('to_subfield');
+  my $to_regex = $cgi->param('to_regex');
+  my $conditional = $cgi->param('conditional');
+  my $conditional_field = $cgi->param('conditional_field');
+  my $conditional_subfield = $cgi->param('conditional_subfield');
+  my $conditional_comparison = $cgi->param('conditional_comparison');
+  my $conditional_value = $cgi->param('conditional_value');
+  my $conditional_regex = $cgi->param('conditional_regex') eq 'on';
+
+  unless ( $mmta_id ) {
+    AddModificationTemplateAction(
+      $template_id,
+      $action, 
+      $from_field, 
+      $from_subfield, 
+      $field_value, 
+      $to_field, 
+      $to_subfield, 
+      $to_regex,
+      $conditional, 
+      $conditional_field, 
+      $conditional_subfield,
+      $conditional_comparison,
+      $conditional_value,
+      $conditional_regex
+    );
+  } else {
+    ModModificationTemplateAction(
+      $mmta_id,
+      $action, 
+      $from_field, 
+      $from_subfield, 
+      $field_value, 
+      $to_field, 
+      $to_subfield, 
+      $to_regex,
+      $conditional, 
+      $conditional_field, 
+      $conditional_subfield,
+      $conditional_comparison,
+      $conditional_value,
+      $conditional_regex
+    );
+  
+  }
+  
+} elsif ( $op eq "delete_action" ) {
+  DelModificationTemplateAction( $cgi->param('mmta_id') );
+
+} elsif ( $op eq "move_action" ) {
+
+  MoveModificationTemplateAction( $cgi->param('mmta_id'), $cgi->param('where') );
+
+}
+
+my @templates = GetModificationTemplates( $template_id );
+
+unless ( $template_id ) {
+  $template_id = $templates[0]->{'template_id'};
+  @templates = GetModificationTemplates( $template_id );
+}
+
+my @actions = GetModificationTemplateActions( $template_id );
+foreach my $action ( @actions ) {
+  $action->{'action_delete_field'} = ( $action->{'action'} eq 'delete_field' );
+  $action->{'action_update_field'} = ( $action->{'action'} eq 'update_field' );
+  $action->{'action_move_field'} = ( $action->{'action'} eq 'move_field' );
+  $action->{'action_copy_field'} = ( $action->{'action'} eq 'copy_field' );
+  
+  $action->{'conditional_if'} = ( $action->{'conditional'} eq 'if' );
+  $action->{'conditional_unless'} = ( $action->{'conditional'} eq 'unless' );
+  
+  $action->{'conditional_comparison_exists'} = ( $action->{'conditional_comparison'} eq 'exists' );
+  $action->{'conditional_comparison_not_exists'} = ( $action->{'conditional_comparison'} eq 'not_exists' );
+  $action->{'conditional_comparison_equals'} = ( $action->{'conditional_comparison'} eq 'equals' );
+  $action->{'conditional_comparison_not_equals'} = ( $action->{'conditional_comparison'} eq 'not_equals' );
+}
+
+$template->param(
+  TemplatesLoop => \@templates, 
+  ActionsLoop => \@actions,
+  
+  template_id => $template_id,
+);
+
+output_html_with_http_headers $cgi, $cookie, $template->output;
diff --git a/installer/data/mysql/updatedatabase.pl b/installer/data/mysql/updatedatabase.pl
index eb364d3..5ae8385 100755
--- a/installer/data/mysql/updatedatabase.pl
+++ b/installer/data/mysql/updatedatabase.pl
@@ -3555,6 +3555,40 @@ if (C4::Context->preference('Version') < TransformToNum($DBversion)){
     SetVersion ($DBversion);
 }
 
+$DBversion = "3.01.00.129";
+if (C4::Context->preference("Version") < TransformToNum($DBversion)) {
+    $dbh->do("CREATE TABLE IF NOT EXISTS `marc_modification_templates` (
+              `template_id` int(11) NOT NULL auto_increment,
+              `name` text NOT NULL,
+              PRIMARY KEY  (`template_id`)
+              ) ENGINE=InnoDB  DEFAULT CHARSET=utf8;"
+    );
+    
+    $dbh->do("CREATE TABLE IF NOT EXISTS `marc_modification_template_actions` (
+              `mmta_id` int(11) NOT NULL auto_increment,
+              `template_id` int(11) NOT NULL,
+              `order` int(3) NOT NULL,
+              `action` enum('delete_field','update_field','move_field','copy_field') NOT NULL,
+              `from_field` varchar(3) NOT NULL,
+              `from_subfield` varchar(1) NOT NULL,
+              `field_value` varchar(100) default NULL,
+              `to_field` varchar(3) default NULL,
+              `to_subfield` varchar(1) default NULL,
+              `to_regex` text,
+              `conditional` enum('if','unless') default NULL,
+              `conditional_field` varchar(3) default NULL,
+              `conditional_subfield` varchar(1) default NULL,
+              `conditional_comparison` enum('exists','not_exists','equals','not_equals') default NULL,
+              `conditional_value` text,
+              `conditional_regex` tinyint(1) NOT NULL default '0',
+              PRIMARY KEY  (`mmta_id`)
+              ) ENGINE=InnoDB  DEFAULT CHARSET=utf8;"
+    );
+    
+    print "Upgrade to $DBversion done ( Added tables for MARC Modification Framework )\n";
+    SetVersion($DBversion);
+}
+
 =item DropAllForeignKeys($table)
 
   Drop all foreign keys of the table $table
diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/admin-home.tmpl b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/admin-home.tmpl
index 05ca4e4..03e6e7a 100644
--- a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/admin-home.tmpl
+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/admin-home.tmpl
@@ -66,6 +66,8 @@
 	<dd>Define the mapping between the Koha transactional database (SQL) and the MARC Bibliographic records. Note that the mapping can be defined through MARC Bibliographic Framework. This tool is just a shortcut to speed up linkage.</dd>
 	<dt><a href="/cgi-bin/koha/admin/fieldmapping.pl">Keywords to MARC mapping</a></dt>
 	<dd>Define the mapping between keywords and MARC fields, those keywords are used to find some datas independently of the framework.</dd>
+	<dt><a href="/cgi-bin/koha/admin/marc_modification_templates.pl">MARC Modification Templates</a></dt>
+	<dd>A templating tool for marc batch imports, so that librarians can set up templates for various vendors' files telling Koha what fields to insert data into.</dd>
 	<dt><a href="/cgi-bin/koha/admin/checkmarc.pl">MARC Bibliographic framework test</a></dt>
 	<dd>Checks the MARC structure. If you change your MARC Bibliographic framework it's recommended that you run this tool to test for errors in your definition.</dd>
     <dt><a href="/cgi-bin/koha/admin/authtypes.pl">Authority types</a></dt>
diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/marc_modification_templates.tmpl b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/marc_modification_templates.tmpl
new file mode 100644
index 0000000..11c8d33
--- /dev/null
+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/admin/marc_modification_templates.tmpl
@@ -0,0 +1,450 @@
+<!-- TMPL_INCLUDE NAME="doc-head-open.inc" -->
+<title>Koha &rsaquo; Administration &rsaquo; MARC Modification Templates</title>
+<!-- TMPL_INCLUDE NAME="doc-head-close.inc" -->
+
+<script type="text/javascript">
+//<![CDATA[
+$(document).ready(function() {
+        $('#select_template').find("input:submit").hide();
+        $('#select_template').change(function() {
+                $('#select_template').submit();
+        });
+});
+//]]>
+</script>
+
+<script>
+function onActionChange(selectObj) { 
+	// get the index of the selected option 
+	var idx = selectObj.selectedIndex; 
+
+	// get the value of the selected option 
+	var action = selectObj.options[idx].value; 
+
+	switch( action ) {
+		case 'delete_field':
+			hide('with_value_block');
+			hide('to_field_block');
+			break;
+
+		case 'update_field':
+			show('with_value_block');
+			hide('to_field_block');
+			break;
+
+		case 'move_field':
+			hide('with_value_block');
+			show('to_field_block');
+			break;
+			
+		case 'copy_field':
+			hide('with_value_block');
+			show('to_field_block');
+			break;
+
+	}
+} 
+
+function onConditionalChange(selectObj) {
+        // get the index of the selected option
+        var idx = selectObj.selectedIndex;
+
+        // get the value of the selected option
+        var action = selectObj.options[idx].value;
+
+        switch( action ) {
+                case '':
+                        hide('conditional_block');
+                        break;   
+ 
+                case 'if':
+                case 'unless':
+                        show('conditional_block');
+                        break;
+        }
+} 
+
+function onConditionalComparisonChange(selectObj) {
+        // get the index of the selected option
+        var idx = selectObj.selectedIndex;
+
+        // get the value of the selected option
+        var action = selectObj.options[idx].value;
+
+        switch( action ) {
+                case 'equals':
+                case 'not_equals':
+                        show('conditional_comparison_block');
+                        break;
+
+		default:
+                        hide('conditional_comparison_block');
+                        break;   
+        }
+} 
+
+function onToFieldRegexChange( checkboxObj ) {
+	if ( checkboxObj.checked ) {
+		show('to_field_regex_value_block');
+	} else {
+		hide('to_field_regex_value_block');
+	}
+}
+
+function show(eltId) {
+	elt = document.getElementById( eltId );
+	elt.style.display='inline';
+}
+
+function hide(eltId) {
+	clearFormElements( eltId );
+	elt = document.getElementById( eltId );
+	elt.style.display='none';
+}
+
+function clearFormElements(divId) {
+	myBlock = document.getElementById( divId );
+
+	var inputElements = myBlock.getElementsByTagName( "input" );
+	for (var i = 0; i < inputElements.length; i++) {
+		switch( inputElements[i].type ) {
+			case "text":
+				inputElements[i].value = '';
+				break;
+			case "checkbox":
+				inputElements[i].checked = false;
+				break;
+		}
+	}
+
+	var selectElements = myBlock.getElementsByTagName( "select" );
+	for (var i = 0; i < selectElements.length; i++) {
+		selectElements[i].selectedIndex = 0;
+	}
+
+}
+
+function confirmDelete() {
+	var agree = confirm("Are you sure you wish to delete this template?");
+	return agree;
+}
+
+var modaction_legend_innerhtml;
+var action_submit_value;
+
+function editAction( mmta_id, order, action, from_field, from_subfield, field_value, to_field,
+	to_subfield, to_regex, conditional, conditional_field, conditional_subfield, 
+	conditional_comparison, conditional_value, conditional_regex 
+) {
+	document.getElementById('mmta_id').value = mmta_id;
+
+	setSelectByValue( 'action', action );
+	document.getElementById('action').onchange();
+
+	document.getElementById('from_field').value = from_field;
+	document.getElementById('from_subfield').value = from_subfield;
+	document.getElementById('field_value').value = field_value;
+	document.getElementById('to_field').value = to_field;
+	document.getElementById('to_subfield').value = to_subfield;
+	document.getElementById('to_regex').value = to_regex;
+
+	document.getElementById('to_field_regex').checked = to_regex.length;
+	document.getElementById('to_field_regex').onchange();
+
+	setSelectByValue( 'conditional', conditional );
+	document.getElementById('conditional').onchange();
+
+	document.getElementById('conditional_field').value = conditional_field;
+	document.getElementById('conditional_subfield').value = conditional_subfield;
+
+	setSelectByValue( 'conditional_comparison', conditional_comparison );
+	document.getElementById('conditional_comparison').onchange();
+
+	document.getElementById('conditional_value').value = conditional_value;
+
+	document.getElementById('conditional_regex').checked = parseInt( conditional_regex );
+
+	window.modaction_legend_innerhtml = document.getElementById('modaction_legend').innerHTML;
+	document.getElementById('modaction_legend').innerHTML = "Edit Action " + order;
+
+	window.action_submit_value = document.getElementById('action_submit').value;
+	document.getElementById('action_submit').value = "Update Action";
+
+	show('cancel_edit');
+}
+
+function cancelEditAction() {
+	document.getElementById('mmta_id').value = '';
+
+	setSelectByValue( 'action', 'delete_field' );
+	document.getElementById('action').onchange();
+
+	document.getElementById('from_field').value = '';
+	document.getElementById('from_subfield').value = '';
+	document.getElementById('field_value').value = '';
+	document.getElementById('to_field').value = '';
+	document.getElementById('to_subfield').value = '';
+	document.getElementById('to_regex').value = '';
+
+	document.getElementById('to_field_regex').checked = false;
+	document.getElementById('to_field_regex').onchange();
+
+	setSelectByValue( 'conditional', '' );
+	document.getElementById('conditional').onchange();
+
+	document.getElementById('conditional_field').value = '';
+	document.getElementById('conditional_subfield').value = '';
+
+	setSelectByValue( 'conditional_comparison', '' );
+	document.getElementById('conditional_comparison').onchange();
+
+	document.getElementById('conditional_value').value = '';
+
+	document.getElementById('conditional_regex').checked = false;
+
+	document.getElementById('modaction_legend').innerHTML = window.modaction_legend_innerhtml;
+	document.getElementById('action_submit').value = window.action_submit_value;
+
+	hide('cancel_edit');
+}
+
+function setSelectByValue( selectId, value ) {
+	s = document.getElementById( selectId );
+
+	for ( i = 0; i < s.options.length; i++ ) {
+		if ( s.options[i].value == value ) {
+			s.selectedIndex = i;
+		}
+	}
+}
+
+</script>
+
+</head>
+
+<body>
+<!-- TMPL_INCLUDE NAME="header.inc" -->
+<!-- TMPL_INCLUDE NAME="cat-search.inc" -->
+
+<div id="doc3" class="yui-t2">
+	<div id="yui-main">
+		<div class="yui-b">
+			<h2>MARC Modification Templates</h2>
+
+			<!-- TMPL_IF NAME="TemplatesLoop" -->
+
+				<form method="get" action="/cgi-bin/koha/admin/marc_modification_templates.pl" id="select_template">
+					<label for="select_template">Template: </label>
+					<select name="template_id" id="select_template" style="width:20em;">
+						<!-- TMPL_LOOP NAME="TemplatesLoop" -->
+							<option value="<!-- TMPL_VAR NAME="template_id" -->" <!-- TMPL_IF NAME="selected" --> selected <!-- /TMPL_IF --> > <!--TMPL_VAR NAME="name" --></option>
+						<!-- /TMPL_LOOP -->
+					</select>
+					<input type="hidden" name="op" value="select_template">
+					<input type="submit" value="Go" />
+				</form>
+
+				<form method="get" action="/cgi-bin/koha/admin/marc_modification_templates.pl" id="delete_template">
+					<input type="hidden" name="template_id" value="<!-- TMPL_VAR NAME="template_id" -->" />
+					<input type="hidden" name="op" value="delete_template">
+					<input type="submit" value="Delete Template" onClick="return confirmDelete()" />
+				</form>
+
+
+				<!-- TMPL_IF NAME="ActionsLoop" -->
+					<table>
+						<caption>Actions for this template</caption>
+		
+						<tr>
+							<th>Change Order</th>
+							<th>Order</th>
+							<th>Action</th>
+							<th>&nbsp</th>
+							<th>&nbsp</th>
+						</tr>
+	
+						<!-- TMPL_LOOP NAME="ActionsLoop" -->
+							<tr>
+        <td style="white-space:nowrap;">
+	        <a title="Move Action Up" href="marc_modification_templates.pl?op=move_action&amp;where=up&amp;template_id=<!-- TMPL_VAR Name="template_id" -->&amp;mmta_id=<!-- TMPL_VAR Name="mmta_id" -->">
+			<img src="/intranet-tmpl/<!-- TMPL_VAR NAME='theme' -->/img/go-up.png" border="0" alt="Go up" />
+                </a>
+
+		<a title="Move Action To Top" href="marc_modification_templates.pl?op=move_action&amp;where=top&amp;template_id=<!-- TMPL_VAR Name="template_id" -->&amp;mmta_id=<!-- TMPL_VAR Name="mmta_id" -->">
+                	<img src="/intranet-tmpl/<!-- TMPL_VAR NAME='theme' -->/img/go-top.png" border="0" alt="Go top" />
+                </a>
+
+                <a title="Move Action To Bottom" href="marc_modification_templates.pl?op=move_action&amp;where=bottom&amp;template_id=<!-- TMPL_VAR Name="template_id" -->&amp;mmta_id=<!-- TMPL_VAR Name="mmta_id" -->">
+                	<img src="/intranet-tmpl/<!-- TMPL_VAR NAME='theme' -->/img/go-bottom.png" border="0" alt="Go bottom" />
+                </a>
+
+                <a title="Move Action Down" href="marc_modification_templates.pl?op=move_action&amp;where=down&amp;template_id=<!-- TMPL_VAR Name="template_id" -->&amp;mmta_id=<!-- TMPL_VAR Name="mmta_id" -->">
+                	<img src="/intranet-tmpl/<!-- TMPL_VAR NAME='theme' -->/img/go-down.png" border="0" alt="Go down" />
+                </a>
+        </td>
+
+								<td><!-- TMPL_VAR NAME="order" --></td>
+								<td>
+									<!-- TMPL_IF NAME="action_delete_field" --> Delete field <!-- /TMPL_IF -->
+									<!-- TMPL_IF NAME="action_update_field" --> Update field <!-- /TMPL_IF -->
+									<!-- TMPL_IF NAME="action_move_field" --> Move field <!-- /TMPL_IF -->
+									<!-- TMPL_IF NAME="action_copy_field" --> Copy field <!-- /TMPL_IF -->
+
+									<!-- TMPL_VAR NAME="from_field" --><!-- TMPL_IF NAME="from_subfield" -->$<!-- TMPL_VAR NAME="from_subfield" --><!-- /TMPL_IF -->
+
+									<!-- TMPL_IF NAME="field_value" -->
+										with value <!-- TMPL_VAR NAME="field_value" -->
+									<!-- /TMPL_IF -->
+
+									<!-- TMPL_IF NAME="to_field" -->
+										to <!-- TMPL_VAR NAME="to_field"--><!-- TMPL_IF NAME="to_subfield" -->$<!-- TMPL_VAR NAME="to_subfield" --><!-- /TMPL_IF -->
+
+										<!-- TMPL_IF NAME="to_regex" -->
+											 using RegEx <strong><!-- TMPL_VAR NAME="to_regex" --></strong>
+										<!-- /TMPL_IF -->
+									<!-- /TMPL_IF -->
+
+									<!-- TMPL_IF NAME="conditional" -->
+										<!-- TMPL_IF NAME="conditional_if" --> if <!-- /TMPL_IF -->
+										<!-- TMPL_IF NAME="conditional_unless" --> unless <!-- /TMPL_IF -->
+
+										<!-- TMPL_VAR NAME="conditional_field" --><!-- TMPL_IF NAME="conditional_subfield" -->$<!-- TMPL_VAR NAME="conditional_subfield" --><!-- /TMPL_IF -->
+
+										<!-- TMPL_IF NAME="conditional_comparison_exists" --> exists <!-- /TMPL_IF -->
+										<!-- TMPL_IF NAME="conditional_comparison_not_exists" --> does not exist <!-- /TMPL_IF -->
+										<!-- TMPL_IF NAME="conditional_comparison_equals" --> equals <!-- /TMPL_IF -->
+										<!-- TMPL_IF NAME="conditional_comparison_not_equals" --> does not equal <!-- /TMPL_IF -->
+
+										<!-- TMPL_IF NAME="conditional_regex" --> RegEx <!-- /TMPL_IF -->
+										<strong><!-- TMPL_VAR NAME="conditional_value" --></strong>
+									<!-- /TMPL_IF -->
+								</td>
+								<td><a href="#modaction" onclick='editAction(
+													"<!-- TMPL_VAR ESCAPE=JS NAME="mmta_id" -->",
+													"<!-- TMPL_VAR ESCAPE=JS NAME="order" -->",
+													"<!-- TMPL_VAR ESCAPE=JS NAME="action" -->", 
+													"<!-- TMPL_VAR ESCAPE=JS NAME="from_field" -->", 
+													"<!-- TMPL_VAR ESCAPE=JS NAME="from_subfield" -->", 
+													"<!-- TMPL_VAR ESCAPE=JS NAME="field_value" -->", 
+													"<!-- TMPL_VAR ESCAPE=JS NAME="to_field" -->", 
+													"<!-- TMPL_VAR ESCAPE=JS NAME="to_subfield" -->", 
+													"<!-- TMPL_VAR ESCAPE=JS NAME="to_regex" -->",
+													"<!-- TMPL_VAR ESCAPE=JS NAME="conditional" -->",
+													"<!-- TMPL_VAR ESCAPE=JS NAME="conditional_field" -->",
+													"<!-- TMPL_VAR ESCAPE=JS NAME="conditional_subfield" -->",
+													"<!-- TMPL_VAR ESCAPE=JS NAME="conditional_comparison" -->",
+													"<!-- TMPL_VAR ESCAPE=JS NAME="conditional_value" -->",
+													"<!-- TMPL_VAR ESCAPE=JS NAME="conditional_regex" -->"
+												)'>Edit</a></td>
+								<td><a href="marc_modification_templates.pl?template_id=<!-- TMPL_VAR NAME="template_id" -->&op=delete_action&mmta_id=<!-- TMPL_VAR NAME="mmta_id" -->">Delete</a></td>
+							</tr>
+						<!-- /TMPL_LOOP -->
+					</table>
+				<!-- TMPL_ELSE -->
+					<div class="dialog message"><p>There are no defined actions for this template.</p></div>
+				<!-- /TMPL_IF -->
+
+				<form method="post" action="/cgi-bin/koha/admin/marc_modification_templates.pl" id="add_action" >
+					<a name="modaction"></a>
+					<fieldset>
+						<legend id="modaction_legend">Add A New Action</legend>
+
+						<select name="action" id="action" onchange="onActionChange(this);">
+							<option value="delete_field">Delete</option>
+							<option value="update_field">Add/Update</option>
+							<option value="move_field">Move</option>
+							<option value="copy_field">Copy</option>
+						</select>
+
+						field <input type="text" name="from_field" id="from_field" size="3" maxlength="3" /> <input type="text" name="from_subfield" id="from_subfield" size="1" maxlength="1" />
+
+						<span name="with_value_block" id="with_value_block" style="display:none;">
+							with value <input type="text" name="field_value" id="field_value" />
+						</span>
+
+						<span name="to_field_block" id="to_field_block" style="display:none;">
+							to field <input type="text" name="to_field" id="to_field" size="3" maxlength="3" /> <input type="text" name="to_subfield" id="to_subfield" size="1" maxlength="1" />
+
+							<span name="to_field_regex_block" id="to_field_regex_block">
+								<sup>
+									<label for="to_field_regex">RegEx</label>
+									<input type="checkbox" name="to_field_regex" id="to_field_regex" onchange="onToFieldRegexChange(this);" />
+
+									<span name="to_field_regex_value_block" id="to_field_regex_value_block" style="display:none;">
+										<input type="text" name="to_regex" id="to_regex" />
+									</span>
+								</sup>
+							</span>
+						</span>
+
+						<p/>
+
+						<select name="conditional" id="conditional" onchange="onConditionalChange(this);">
+							<option value="" selected />
+							<option value="if">if</option>
+							<option value="unless">unless</option>
+						</select>
+
+						<span name="conditional_block" id="conditional_block" style="display:none;">
+							field <input type="text" name="conditional_field" id="conditional_field" size="3" maxlength="3" /> <input type="text" name="conditional_subfield" id="conditional_subfield" size="1" maxlength="1" />
+
+							<select name="conditional_comparison" id="conditional_comparison" onchange="onConditionalComparisonChange(this);">
+								<option value="" />
+								<option value="exists">exists</option>
+								<option value="not_exists">doesn't exist</option>
+								<option value="equals">equals</option>
+								<option value="not_equals">doesn't equal</option>
+							</select>
+
+							<span name="conditional_comparison_block" id="conditional_comparison_block" style="display:none;">
+								<input type="text" id="conditional_value" name="conditional_value" />
+
+								<sup>
+									<label for="conditional_regex">RegEx</label>
+									<input type="checkbox" name="conditional_regex" id="conditional_regex" />
+								</sup>
+
+							</span>
+						</span>
+
+						<input type="hidden" name="template_id" value="<!-- TMPL_VAR NAME="template_id" -->" />
+						<input type="hidden" name="mmta_id" id="mmta_id" />
+						<input type="hidden" name="op" value="add_action" />
+
+						<br/><br/>
+						<input id="action_submit" type="submit" value="Add Action" /> <a href="#modaction" id="cancel_edit" onclick="cancelEditAction();" style="display:none;">Cancel</a>
+					</fieldset>
+				</form>
+
+			<!-- TMPL_ELSE -->
+				<div class="dialog message"><p>There are no defined templates. Please create a template first.</p></div>
+			<!-- /TMPL_IF -->
+
+			<form method="post" action="/cgi-bin/koha/admin/marc_modification_templates.pl" id="add_template" >
+				<fieldset>
+					<legend>Create A New Template</legend>
+
+					<label for="template_name">Name: </label>
+					<input name="template_name" id="template_name" type="text" size="30" />
+
+					<input type="hidden" name="op" value="create_template" />
+					<input type="submit" value="Create Template" />
+
+					<!-- TMPL_IF NAME="template_id" -->
+						<input type="hidden" name="template_id" value="<!-- TMPL_VAR NAME="template_id" -->" />
+						<input type="checkbox" name="duplicate_current_template" id="duplicate_current_template" />
+						<label for="duplicate_current_template">Duplicate Current Template</label>
+					<!-- /TMPL_IF -->
+				</fieldset>
+			</form>
+
+
+
+		</div>
+	</div>
+
+	<div class="yui-b">
+		<!-- TMPL_INCLUDE NAME="admin-menu.inc" -->
+	</div>
+</div>
+<!-- TMPL_INCLUDE NAME="intranet-bottom.inc" -->
diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/results.tmpl b/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/results.tmpl
index b81970a..b9b6a7d 100644
--- a/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/results.tmpl
+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/catalogue/results.tmpl
@@ -371,23 +371,22 @@ function GetZ3950Terms(){
                 <!-- /TMPL_IF -->
 
                     <!-- TABLE RESULTS START -->
-
                 <table>
                     <tr>
-                        <!-- TMPL_IF NAME="AmazonEnabled" --><th>&nbsp;</th><!-- /TMPL_IF -->
+                        <!-- TMPL_IF NAME="AmazonEnabled" --><!-- TMPL_IF NAME="AmazonCoverImages" --><th>&nbsp;</th><!-- /TMPL_IF --><!-- /TMPL_IF -->
                         <th colspan="2">Results</th>
                         <th>Location</th>
                     </tr>
                         <!-- Actual Search Results -->
                         <!-- TMPL_LOOP NAME="SEARCH_RESULTS" -->
                          <!-- TMPL_IF NAME="__odd__" --><tr><!-- TMPL_ELSE --><tr class="highlight"><!-- /TMPL_IF -->
-                            <!-- TMPL_IF NAME="AmazonEnabled" -->
+                            <!-- TMPL_IF NAME="AmazonEnabled" --><!-- TMPL_IF NAME="AmazonCoverImages" -->
                                 <td>
                                     <a class="p1" href="/cgi-bin/koha/catalogue/detail.pl?biblionumber=<!-- TMPL_VAR NAME="biblionumber" ESCAPE="URL" -->">
-									<!-- TMPL_IF NAME="AmazonCoverImages" -->
-                                        <img src="<!-- TMPL_IF NAME="normalized_isbn" -->http://images.amazon.com/images/P/<!-- TMPL_VAR name="normalized_isbn" -->.01.TZZZZZZZ.jpg<!-- TMPL_ELSE -->http://g-images.amazon.com/images/G/01/x-site/icons/no-img-sm.gif<!-- /TMPL_IF -->" alt="image" class="thumbnail" /> <!-- /TMPL_IF -->
+									
+                                        <img src="<!-- TMPL_IF NAME="normalized_isbn" -->http://images.amazon.com/images/P/<!-- TMPL_VAR name="normalized_isbn" -->.01.TZZZZZZZ.jpg<!-- TMPL_ELSE -->http://g-images.amazon.com/images/G/01/x-site/icons/no-img-sm.gif<!-- /TMPL_IF -->" alt="image" class="thumbnail" /> 
                                     </a></td>
-                            <!-- /TMPL_IF -->
+                            <!-- /TMPL_IF --><!-- /TMPL_IF -->
                             <td>
                                 <input type="checkbox" class="selection" id="bib<!-- TMPL_VAR NAME="biblionumber" -->" name="biblionumber" value="<!-- TMPL_VAR NAME="biblionumber" -->" style="display:none" />
                             </td>
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 8660db0..4184c31 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
@@ -105,6 +105,20 @@ function CheckForm(f) {
 		
 	</li>
 </ol></fieldset>
+<fieldset class="rows">
+	<legend>Use MARC Modification Template:</legend>
+	<ol>
+		<li>
+			<select name="marc_modification_template_id" id="marc_modification_template_id">
+				<option value="">Do not use.</option>
+					<!-- TMPL_LOOP NAME="MarcModificationTemplatesLoop" -->
+						<option value="<!-- TMPL_VAR NAME="template_id" -->"> <!--TMPL_VAR NAME="name" --> </option>
+					<!-- /TMPL_LOOP -->
+			</select>
+		</li>
+	</ol>
+    </select>
+  </fieldset>
   <fieldset class="rows">
     <legend>Look for existing records in catalog?</legend>
     <ol><li><label for="matcher">Record matching rule:</label>
diff --git a/tools/stage-marc-import.pl b/tools/stage-marc-import.pl
index 2cd77c5..095ff9c 100755
--- a/tools/stage-marc-import.pl
+++ b/tools/stage-marc-import.pl
@@ -40,6 +40,7 @@ use C4::ImportBatch;
 use C4::Matcher;
 use C4::UploadedFile;
 use C4::BackgroundJob;
+use C4::MarcModificationTemplates;
 
 my $input = new CGI;
 my $dbh = C4::Context->dbh;
@@ -55,6 +56,7 @@ my $parse_items = $input->param('parse_items');
 my $item_action = $input->param('item_action');
 my $comments = $input->param('comments');
 my $syntax = $input->param('syntax');
+my $marc_modification_template = $input->param('marc_modification_template_id');
 my ($template, $loggedinuser, $cookie)
 	= get_template_and_user({template_name => "tools/stage-marc-import.tmpl",
 					query => $input,
@@ -129,7 +131,7 @@ if ($completedJobID) {
     }
 
     # FIXME branch code
-    my ($batch_id, $num_valid, $num_items, @import_errors) = BatchStageMarcRecords($syntax, $marcrecord, $filename, 
+    my ($batch_id, $num_valid, $num_items, @import_errors) = BatchStageMarcRecords($syntax, $marcrecord, $filename, $marc_modification_template,
                                                                                    $comments, '', $parse_items, 0,
                                                                                    50, staging_progress_callback($job, $dbh));
     $dbh->commit();
@@ -178,6 +180,8 @@ if ($completedJobID) {
                          matcher_code => $matcher_code,
                          import_batch_id => $batch_id
                         );
+
+
     }
 
 } else {
@@ -187,6 +191,10 @@ if ($completedJobID) {
     }
     my @matchers = C4::Matcher::GetMatcherList();
     $template->param(available_matchers => \@matchers);
+
+    my @templates = GetModificationTemplates();
+    $template->param( MarcModificationTemplatesLoop => \@templates );
+
 }
 
 output_html_with_http_headers $input, $cookie, $template->output;
-- 
1.5.6.5



More information about the Koha-patches mailing list