From 92b15564ea6bd5eb8f7df728abcee0c06cc9199b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fr=C3=A9d=C3=A9rick?= <frederick.capovilla@libeo.com>
Date: Wed, 6 Nov 2013 10:49:09 -0500
Subject: [PATCH] Bug 11299 - Add a button to the biblio edition page to
 automatically add authority links in the current biblio record via AJAX. Also
 adds a button to easily create missing authority records.

Testing scenario (Creating an authority record for a failed automatic link) :

* In you system preferences, deactivate the "AutoCreateAuthorities" preference.
* Go to the biblio record creation form to create a new biblio record. (koha/cataloguing/addbiblio.pl)
* Click the "Link authorities automatically" button. A message should appear, telling the user "No authority link was changed."
* Add random informations in field 600$a of the biblio record.
* Click the "Link authorities automatically" button. The message box should now show "600 - No matching authority found." if no matching authority was found. For this scenario, we want the authority match to have failed.
* The 600$9 field should now be red and icons should have appeared next to it. Click on the "create authority" icon.
* A popup should appear, containing a "Modify authority" form. The form should be pre-filled, a the informations in the 600$a field of the biblio should be in the 100$a field of this new authority record.
* Click the "Save" button
* The 600$9 field in the biblio record should now be back to normal and have the authid of the authority record that was just created.
* Click the "Link authorities automatically" button again. The message box should now show "No authority link was changed.".
---
 C4/Biblio.pm                                       | 181 ++++++++++++++------
 C4/Linker.pm                                       |   5 +-
 authorities/authorities.pl                         |  22 ++-
 cataloguing/automatic_linker.pl                    |  70 ++++++++
 koha-tmpl/intranet-tmpl/prog/en/js/cataloging.js   |   6 +
 .../prog/en/modules/authorities/authorities.tt     |   3 +
 .../authorities/blinddetail-biblio-search.tt       |  24 ++-
 .../prog/en/modules/cataloguing/addbiblio.tt       | 185 +++++++++++++++++++++
 8 files changed, 434 insertions(+), 62 deletions(-)
 create mode 100755 cataloguing/automatic_linker.pl

diff --git a/C4/Biblio.pm b/C4/Biblio.pm
index 8d62a61..6613ad4 100644
--- a/C4/Biblio.pm
+++ b/C4/Biblio.pm
@@ -507,7 +507,7 @@ sub BiblioAutoLink {
 
 =head2 LinkBibHeadingsToAuthorities
 
-  my $num_headings_changed, %results = LinkBibHeadingsToAuthorities($linker, $marc, $frameworkcode, [$allowrelink]);
+  my $num_headings_changed, %results = LinkBibHeadingsToAuthorities($linker, $marc, $frameworkcode, [$allowrelink, $verbose]);
 
 Links bib headings to authority records by checking
 each authority-controlled field in the C<MARC::Record>
@@ -529,6 +529,7 @@ sub LinkBibHeadingsToAuthorities {
     my $bib           = shift;
     my $frameworkcode = shift;
     my $allowrelink = shift;
+    my $verbose = shift;
     my %results;
     if (!$bib) {
         carp 'LinkBibHeadingsToAuthorities called on undefined bib record';
@@ -549,100 +550,60 @@ sub LinkBibHeadingsToAuthorities {
         if ( defined $current_link && (!$allowrelink || !C4::Context->preference('LinkerRelink')) )
         {
             $results{'linked'}->{ $heading->display_form() }++;
+            push(@{$results{'details'}}, { tag => $field->tag(), authid => $current_link, status => 'UNCHANGED'}) if $verbose;
             next;
         }
 
-        my ( $authid, $fuzzy ) = $linker->get_link($heading);
+        my ( $authid, $fuzzy, $status ) = $linker->get_link($heading);
         if ($authid) {
             $results{ $fuzzy ? 'fuzzy' : 'linked' }
               ->{ $heading->display_form() }++;
-            next if defined $current_link and $current_link == $authid;
+            if(defined $current_link and $current_link == $authid) {
+                push(@{$results{'details'}}, { tag => $field->tag(), authid => $current_link, status => 'UNCHANGED'}) if $verbose;
+                next;
+            }
 
             $field->delete_subfield( code => '9' ) if defined $current_link;
             $field->add_subfields( '9', $authid );
             $num_headings_changed++;
+            push(@{$results{'details'}}, { tag => $field->tag(), authid => $authid, status => $status || 'LOCAL_FOUND'}) if $verbose;
         }
         else {
             if ( defined $current_link
                 && (!$allowrelink || C4::Context->preference('LinkerKeepStale')) )
             {
                 $results{'fuzzy'}->{ $heading->display_form() }++;
+                push(@{$results{'details'}}, { tag => $field->tag(), authid => $current_link, status => 'UNCHANGED'}) if $verbose;
             }
             elsif ( C4::Context->preference('AutoCreateAuthorities') ) {
                 if ( _check_valid_auth_link( $current_link, $field ) ) {
                     $results{'linked'}->{ $heading->display_form() }++;
                 }
                 else {
-                    my $authtypedata =
-                      C4::AuthoritiesMarc::GetAuthType( $heading->auth_type() );
-                    my $marcrecordauth = MARC::Record->new();
-                    if ( C4::Context->preference('marcflavour') eq 'MARC21' ) {
-                        $marcrecordauth->leader('     nz  a22     o  4500');
-                        SetMarcUnicodeFlag( $marcrecordauth, 'MARC21' );
-                    }
-                    $field->delete_subfield( code => '9' )
-                      if defined $current_link;
-                    my $authfield =
-                      MARC::Field->new( $authtypedata->{auth_tag_to_report},
-                        '', '', "a" => "" . $field->subfield('a') );
-                    map {
-                        $authfield->add_subfields( $_->[0] => $_->[1] )
-                          if ( $_->[0] =~ /[A-z]/ && $_->[0] ne "a" )
-                    } $field->subfields();
-                    $marcrecordauth->insert_fields_ordered($authfield);
-
-# bug 2317: ensure new authority knows it's using UTF-8; currently
-# only need to do this for MARC21, as MARC::Record->as_xml_record() handles
-# automatically for UNIMARC (by not transcoding)
-# FIXME: AddAuthority() instead should simply explicitly require that the MARC::Record
-# use UTF-8, but as of 2008-08-05, did not want to introduce that kind
-# of change to a core API just before the 3.0 release.
-
-                    if ( C4::Context->preference('marcflavour') eq 'MARC21' ) {
-                        $marcrecordauth->insert_fields_ordered(
-                            MARC::Field->new(
-                                '667', '', '',
-                                'a' => "Machine generated authority record."
-                            )
-                        );
-                        my $cite =
-                            $bib->author() . ", "
-                          . $bib->title_proper() . ", "
-                          . $bib->publication_date() . " ";
-                        $cite =~ s/^[\s\,]*//;
-                        $cite =~ s/[\s\,]*$//;
-                        $cite =
-                            "Work cat.: ("
-                          . C4::Context->preference('MARCOrgCode') . ")"
-                          . $bib->subfield( '999', 'c' ) . ": "
-                          . $cite;
-                        $marcrecordauth->insert_fields_ordered(
-                            MARC::Field->new( '670', '', '', 'a' => $cite ) );
-                    }
-
-           #          warn "AUTH RECORD ADDED : ".$marcrecordauth->as_formatted;
+                    $authid = AddAuthorityFromHeading($heading, $field, $bib);
 
-                    $authid =
-                      C4::AuthoritiesMarc::AddAuthority( $marcrecordauth, '',
-                        $heading->auth_type() );
                     $field->add_subfields( '9', $authid );
                     $num_headings_changed++;
                     $linker->update_cache($heading, $authid);
                     $results{'added'}->{ $heading->display_form() }++;
+                    push(@{$results{'details'}}, { tag => $field->tag(), authid => $authid, status => 'CREATED'}) if $verbose;
                 }
             }
             elsif ( defined $current_link ) {
                 if ( _check_valid_auth_link( $current_link, $field ) ) {
                     $results{'linked'}->{ $heading->display_form() }++;
+                    push(@{$results{'details'}}, { tag => $field->tag(), authid => $authid, status => 'UNCHANGED'}) if $verbose;
                 }
                 else {
                     $field->delete_subfield( code => '9' );
                     $num_headings_changed++;
                     $results{'unlinked'}->{ $heading->display_form() }++;
+                    push(@{$results{'details'}}, { tag => $field->tag(), authid => undef, status => 'NONE_FOUND'}) if $verbose;
                 }
             }
             else {
                 $results{'unlinked'}->{ $heading->display_form() }++;
+                push(@{$results{'details'}}, { tag => $field->tag(), authid => undef, status => 'NONE_FOUND'}) if $verbose;
             }
         }
 
@@ -674,6 +635,118 @@ sub _check_valid_auth_link {
    return ($field->as_string('abcdefghijklmnopqrstuvwxyz') eq $authorized_heading);
 }
 
+=head2 AddAuthorityFromHeading
+
+  my $authid = AddAuthorityFromHeading($heading, $field, [$bib]);
+
+Creates a new authority from an heading and its associated field.
+
+Returns the id of the newly created authority.
+
+=cut
+
+sub AddAuthorityFromHeading {
+    my $heading = shift;
+    my $field = shift;
+    my $bib = shift;
+
+    my $authtypedata =
+      C4::AuthoritiesMarc::GetAuthType( $heading->auth_type() );
+    my $marcrecordauth = MARC::Record->new();
+    if ( C4::Context->preference('marcflavour') eq 'MARC21' ) {
+        $marcrecordauth->leader('     nz  a22     o  4500');
+        SetMarcUnicodeFlag( $marcrecordauth, 'MARC21' );
+    }
+    my $authfield =
+      MARC::Field->new( $authtypedata->{auth_tag_to_report},
+        '', '', "a" => "" . $field->subfield('a') );
+    map {
+        $authfield->add_subfields( $_->[0] => $_->[1] )
+          if ( $_->[0] =~ /[A-z]/ && $_->[0] ne "a" )
+    } $field->subfields();
+    $marcrecordauth->insert_fields_ordered($authfield);
+
+# bug 2317: ensure new authority knows it's using UTF-8; currently
+# only need to do this for MARC21, as MARC::Record->as_xml_record() handles
+# automatically for UNIMARC (by not transcoding)
+# FIXME: AddAuthority() instead should simply explicitly require that the MARC::Record
+# use UTF-8, but as of 2008-08-05, did not want to introduce that kind
+# of change to a core API just before the 3.0 release.
+
+    if ( C4::Context->preference('marcflavour') eq 'MARC21' ) {
+        $marcrecordauth->insert_fields_ordered(
+            MARC::Field->new(
+                '667', '', '',
+                'a' => "Machine generated authority record."
+            )
+        );
+        if(defined $bib) {
+            my $cite =
+                $bib->author() . ", "
+              . $bib->title_proper() . ", "
+              . $bib->publication_date() . " ";
+            $cite =~ s/^[\s\,]*//;
+            $cite =~ s/[\s\,]*$//;
+            $cite =
+                "Work cat.: ("
+              . C4::Context->preference('MARCOrgCode') . ")"
+              . $bib->subfield( '999', 'c' ) . ": "
+              . $cite;
+            $marcrecordauth->insert_fields_ordered(
+                MARC::Field->new( '670', '', '', 'a' => $cite ) );
+        }
+
+        # Add correct informations in field 008 and 040 so the authority can be linked correctly in future linkings
+        my $date=POSIX::strftime("%y%m%d",localtime);
+        my @heading_use = qw(b b b);
+        my $thesaurus = '|';
+
+        if ($heading->{'subject_added_entry'}) {
+            $heading_use[1] = 'a';
+            # Thesaurus treatment
+            my %thesaurus_conv = ('lcsh'=>'a','lcac'=>'b','mesh'=>'c','nal'=>'d','notspecified'=>'n','cash'=>'k','rvm'=>'v');
+            $thesaurus = 'z';
+            $thesaurus = $thesaurus_conv{$heading->{'thesaurus'}} if exists $thesaurus_conv{$heading->{'thesaurus'}};
+            if($thesaurus eq 'z')
+            {
+                $marcrecordauth->insert_fields_ordered(
+                        MARC::Field->new('040','','','f'=>$heading->{'thesaurus'})
+                );
+            }
+        }
+        if ($heading->{'series_added_entry'}) {
+            $heading_use[2] = 'a';
+        }
+        if (not $heading->{'subject_added_entry'} and not $heading->{'series_added_entry'}) {
+            $heading_use[0] = 'a';
+        }
+
+        # Get a valid default value for field 008
+        my $default_008 = C4::Context->preference('MARCAuthorityControlField008');
+        if(!$default_008 or length($default_008)<34) {
+            $default_008 = '|| aca||aabn           | a|a     d';
+        }
+        else {
+            $default_008 = substr($default_008,0,34)
+        }
+
+        # Insert the date, thesaurus and heading_use into field 008
+        my $f008 = $date . $default_008;
+        substr($f008, 11, 1) = $thesaurus;
+        substr($f008, 14, 3) = join('', @heading_use);
+
+        $marcrecordauth->insert_fields_ordered(
+            MARC::Field->new('008',$f008)
+        );
+    }
+
+#          warn "AUTH RECORD ADDED : ".$marcrecordauth->as_formatted;
+
+    my $authid = C4::AuthoritiesMarc::AddAuthority( $marcrecordauth, '', $heading->auth_type() );
+    return $authid;
+}
+
+
 =head2 GetRecordValue
 
   my $values = GetRecordValue($field, $record, $frameworkcode);
diff --git a/C4/Linker.pm b/C4/Linker.pm
index 2708859..9ba4986 100644
--- a/C4/Linker.pm
+++ b/C4/Linker.pm
@@ -30,8 +30,9 @@ C4::Linker - Base class for linking authorities to bibliographic records
 Base class for C4::Linker::X. Subclasses need to provide the following methods
 
 B<get_link ($field)> - return the authid for the authority that should be
-linked to the provided MARC::Field object, and a boolean to indicate whether
-the match is "fuzzy" (the semantics of "fuzzy" are up to the individual plugin).
+linked to the provided MARC::Field object, a boolean to indicate whether
+the match is "fuzzy" (the semantics of "fuzzy" are up to the individual plugin),
+and an optional 'status' string giving more informations on the link status.
 In order to handle authority limits, get_link should always end with:
     return $self->SUPER::_handle_auth_limit($authid), $fuzzy;
 
diff --git a/authorities/authorities.pl b/authorities/authorities.pl
index f9a79ad..05569c7 100755
--- a/authorities/authorities.pl
+++ b/authorities/authorities.pl
@@ -571,6 +571,22 @@ my $myindex = $input->param('index');
 my $linkid=$input->param('linkid');
 my $authtypecode = $input->param('authtypecode');
 my $breedingid    = $input->param('breedingid');
+my $biblioindex = $input->param('biblioindex'); # Used when in popup mode to send the new autority infos to the parent biblio
+
+if($biblioindex and !$authid){
+    require C4::Heading;
+
+    my $record = TransformHtmlToMarc( $input );
+
+    # Get the first heading found in the biblio informations
+    foreach my $field ( $record->fields() ) {
+        my $heading = C4::Heading->new_from_bib_field($field);
+        next unless defined $heading;
+
+        # Create a new authority using the heading informations
+        $authid = C4::Biblio::AddAuthorityFromHeading($heading, $field);
+    }
+}
 
 my $dbh = C4::Context->dbh;
 if(!$authtypecode) {
@@ -585,7 +601,7 @@ my ($template, $loggedinuser, $cookie)
                             flagsrequired => {editauthorities => 1},
                             debug => 1,
                             });
-$template->param(nonav   => $nonav,index=>$myindex,authtypecode=>$authtypecode,breedingid=>$breedingid,);
+$template->param(nonav   => $nonav,index=>$myindex,authtypecode=>$authtypecode,breedingid=>$breedingid,biblioindex=>$biblioindex);
 
 $tagslib = GetTagsLabels(1,$authtypecode);
 $mandatory_z3950 = GetMandatoryFieldZ3950($authtypecode);
@@ -630,7 +646,9 @@ if ($op eq "add") {
         } else {
             ($authid) = AddAuthority($record,$authid,$authtypecode);
         }
-        if ($myindex) {
+        if ($biblioindex) {
+            print $input->redirect("blinddetail-biblio-search.pl?authid=$authid&index=$biblioindex");
+        } elsif ($myindex) {
             print $input->redirect("blinddetail-biblio-search.pl?authid=$authid&index=$myindex");
         } else {
             print $input->redirect("detail.pl?authid=$authid");
diff --git a/cataloguing/automatic_linker.pl b/cataloguing/automatic_linker.pl
new file mode 100755
index 0000000..d99e9f3
--- /dev/null
+++ b/cataloguing/automatic_linker.pl
@@ -0,0 +1,70 @@
+#!/usr/bin/perl
+
+# Frédérick Capovilla, 2011 - Libéo
+#
+# 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 CGI::Cookie;
+use JSON;
+use C4::Auth;
+use C4::Biblio;
+use C4::Context;
+
+my $input        = new CGI;
+print $input->header('application/json');
+
+# Check the user's permissions
+my %cookies = fetch CGI::Cookie;
+my $sessid = $cookies{'CGISESSID'}->value || $input->param('CGISESSID');
+my ($auth_status, $auth_sessid) = C4::Auth::check_cookie_auth($sessid, {"catalogue"});
+if ($auth_status ne "ok") {
+    print to_json({status => 'UNAUTHORIZED'});
+    exit 0;
+}
+
+# Link the biblio headings to authorities and return a json containing the status of all the links.
+# Example : {"status":"OK","links":[{"authid":"123","status":"LINK_CHANGED","tag":"650"}]}
+#
+# tag = the tag number of the field
+# authid = the value of the $9 subfield for this tag
+# status = The status of the link (LOCAL_FOUND, NONE_FOUND, MULTIPLE_MATCH, UNCHANGED, CREATED)
+
+my $json;
+
+my @params = $input->param();
+my $record = TransformHtmlToMarc( $input );
+
+
+my $linker_module = "C4::Linker::" . ( C4::Context->preference("LinkerModule") || 'Default' );
+eval { eval "require $linker_module"; };
+if ($@) {
+    $linker_module = 'C4::Linker::Default';
+    eval "require $linker_module";
+}
+if ($@) {
+    return 0, 0;
+}
+my $linker = $linker_module->new( { 'options' => C4::Context->preference("LinkerOptions") } );
+
+my ( $headings_changed, $results ) = LinkBibHeadingsToAuthorities( $linker, $record, $params['frameworkcode'], C4::Context->preference("CatalogModuleRelink") || '', 1 );
+
+$json->{status} = 'OK';
+$json->{links} = $results->{details} || '';
+
+print to_json($json);
diff --git a/koha-tmpl/intranet-tmpl/prog/en/js/cataloging.js b/koha-tmpl/intranet-tmpl/prog/en/js/cataloging.js
index 70ae6f9..f2c7693 100644
--- a/koha-tmpl/intranet-tmpl/prog/en/js/cataloging.js
+++ b/koha-tmpl/intranet-tmpl/prog/en/js/cataloging.js
@@ -107,9 +107,15 @@ function CloneField(index, hideMarc, advancedMARCEditor) {
             for( j = 0 ; j < inputs.length ; j++ ) {
                 if(inputs[j].getAttribute("id") && inputs[j].getAttribute("id").match(/^tag_/) ){
                     inputs[j].value = "";
+
+                    //Remove the color added by the automatic linker
+                    $(inputs[j]).css({backgroundColor:""});
                 }
             }
 
+            // Remove the status icons added by the automatic linker
+            $(divs[i]).find('.subfield_status').remove();
+
             inputs[0].setAttribute('id',inputs[0].getAttribute('id')+new_key);
             inputs[0].setAttribute('name',inputs[0].getAttribute('name')+new_key);
             var id_input;
diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/authorities/authorities.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/authorities/authorities.tt
index 693b61e..cf12e97 100644
--- a/koha-tmpl/intranet-tmpl/prog/en/modules/authorities/authorities.tt
+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/authorities/authorities.tt
@@ -201,6 +201,9 @@ function confirmnotdup(redirect){
     <input type="hidden" name="authtypecode" value="[% authtypecode %]" />
     <input type="hidden" name="authid" value="[% authid %]" />
     <input type="hidden" name="index" value="[% index %]" />
+    [% IF ( biblioindex ) %]
+    <input type="hidden" name="biblioindex" value="[% biblioindex %]" />
+    [% END %]
     <input type="hidden" value="0" id="confirm_not_duplicate" name="confirm_not_duplicate" />
 
     <div id="toolbar" class="btn-toolbar">
diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/authorities/blinddetail-biblio-search.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/authorities/blinddetail-biblio-search.tt
index c95a29b..cf536b4 100644
--- a/koha-tmpl/intranet-tmpl/prog/en/modules/authorities/blinddetail-biblio-search.tt
+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/authorities/blinddetail-biblio-search.tt
@@ -5,9 +5,19 @@
 //<![CDATA[
     $(document).ready(function(){
         var index_start = "[% index %]";
-        var whichfield;
+        var whichfield = null;
+
+        // Loop through opener windows and try to find the basewindow and the form field we need to fill
+        var basewindow = window;
+        var parents_to_close = [];
         try {
-            whichfield = opener.opener.document.getElementById(index_start);
+            while(whichfield === null) {
+                basewindow = basewindow.opener;
+                whichfield = basewindow.document.getElementById(index_start);
+                if(whichfield === null) {
+                    parents_to_close.unshift(basewindow);
+                }
+            }
         } catch(e) {
             return;
         }
@@ -99,13 +109,19 @@
             [% ELSE %]
                 if(code.value=='9'){
                     subfield.value = "[% authid |replace("'", "\'") |replace('"', '\"') |replace('\n', '\\n') |replace('\r', '\\r') %]";
-                    break;
+                    // Clean the field if it was modified by the automatic linker
+                    $(subfield).siblings('.subfield_status').remove();
+                    $(subfield).css({backgroundColor:''});
                 }
             [% END %]
             }
         }
 
-      	opener.close();
+        // Close all parent windows, but not the basewindow
+        while(parents_to_close.length != 0) {
+            parents_to_close.shift().close();
+        }
+
        	window.close();
             
        	return false;
diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/cataloguing/addbiblio.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/cataloguing/addbiblio.tt
index c530ce0..b6ea6f4 100644
--- a/koha-tmpl/intranet-tmpl/prog/en/modules/cataloguing/addbiblio.tt
+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/cataloguing/addbiblio.tt
@@ -80,6 +80,10 @@
             [% END %]
         });
 
+        $("#linkerbutton").click(function(){
+            AutomaticLinker();
+        });
+
         $("#saverecord").click(function(){
             $(".btn-group").removeClass("open");
             onOption();
@@ -139,6 +143,187 @@ function PopupZ3950() {
     } 
 }
 
+function addCreateAuthorityButton(tag_subfield_line, tag, index) {
+	var title = _("Create authority");
+	var elem = $('<a class="subfield_status" href="#"><img src="/intranet-tmpl/[% theme %]/img/edit-tag.png" title="' + title + '" /></a>');
+	tag_subfield_line.append(elem);
+
+	elem.click(function() {
+        var biblioindex = $(this).parents('.subfield_line').find('input').eq(1).attr('id');
+		var popup = window.open("", "new_auth_popup",'width=700,height=550,toolbar=false,scrollbars=yes');
+
+		if(popup !== null) {
+			// Create a new form that will be POSTed in the new window
+			var form = $('<form>').attr({
+				method: 'post',
+				action: "../authorities/authorities.pl",
+				target: "new_auth_popup"
+			});
+
+			// Add the biblioindex
+			form.append($('<input>').attr({
+				type: 'hidden',
+				name: 'biblioindex',
+				value: biblioindex
+			}));
+
+			// Get all form datas for the current heading field
+			$('.tag[id^=tag_' + tag + '_]').eq(index).find(':input').each(function(){
+				form.append($('<input>').attr({
+					type: 'hidden',
+					name: this.name,
+					value: $(this).val()
+				}));
+			});
+
+			// We need to add the temporary form to the body so we can submit it
+			$('body').append(form);
+			form.submit();
+			form.remove();
+		}
+
+		return false;
+	});
+}
+
+/**
+ * Updates the authid for every heading field
+ * Adds visual feedback for the changes made on the form.
+ */
+function updateHeadingLinks(links) {
+    var current_tag = '';
+    var tag_index = 0;
+
+    // Delete the old message dialog and create a new one
+    $('#autolinker_dialog').remove();
+    var message_dialog = $('<div id="autolinker_dialog" class="dialog"><strong>' + _("Automatic authority link results:") + '</strong><ul></ul></div>');
+    var message_dialog_ul = message_dialog.find('ul');
+
+    $.each(links, function(index, heading) {
+        if(current_tag == heading.tag) {
+            tag_index++;
+        }
+        else {
+            current_tag = heading.tag;
+            tag_index = 0;
+        }
+
+        // Find the $9 field to update
+        var tag_subfield_line = $('.subfield_line[id^=subfield' + heading.tag + '9]').eq(tag_index);
+        var subfield = tag_subfield_line.children('.input_marceditor').eq(0);
+
+        // Delete the old status if one exists
+        tag_subfield_line.children('.subfield_status').remove();
+        subfield.css({backgroundColor:''});
+
+        // If the field wasn't modified. Skip it.
+        if(heading.status == 'UNCHANGED') {
+            return;
+        }
+
+
+        // Make the subfield line visible and update its value
+        tag_subfield_line.show();
+        subfield.val(heading.authid);
+
+        // Add the new status
+        var image = 'deny.gif';
+        var message = '';
+        var field_color = '#FFAAAA';
+        switch(heading.status) {
+            case 'LOCAL_FOUND':
+                image = 'approve.gif';
+                message = _("A matching authority was found in the local database.");
+                field_color = '#99FF99';
+                break;
+            case 'CREATED':
+                image = 'approve.gif';
+                message = _("No matching authority found. A new authority was created automatically.");
+                field_color = '#99FF99';
+                break;
+            case 'MULTIPLE_MATCH':
+                message = _("More than one local match found. Possibly a duplicate authority!");
+                break;
+            case 'NONE_FOUND':
+                message = _("No matching authority found.");
+                break;
+            default:
+                message = heading.status;
+                break;
+        }
+
+        subfield.css({backgroundColor:field_color});
+        tag_subfield_line.append('<img class="subfield_status" src="/intranet-tmpl/[% theme %]/img/' + image + '" title="' + message + '" />');
+
+        // Add the message to the dialog
+        message_dialog_ul.append('<li><strong>' + heading.tag + '</strong> - ' + message + '</li>');
+
+        // Add a link to create a new authority if none was found
+        if(heading.status == 'NONE_FOUND') {
+			addCreateAuthorityButton(tag_subfield_line, heading.tag, tag_index);
+        }
+    });
+
+    if(message_dialog.find('li').length == 0) {
+        message_dialog_ul.append("<li>" + _("No authority link was changed.") + "</li>");
+    }
+    $('#addbibliotabs').before(message_dialog);
+}
+
+/**
+ * Use an ajax request to automatically find authority links for the current record
+ */
+function AutomaticLinker() {
+    // Show the Loading overlay
+    $("#loading").show();
+
+    // Remove fields that are completely empty
+    $('#f').find('.tag').each(function() {
+        var empty = true;
+        $(this).find('.input_marceditor').each(function() {
+            if($(this).val() != '') {
+                empty = false;
+                return false;
+            }
+        });
+        if(empty) {
+            UnCloneField($(this).attr('id'));
+        }
+    });
+
+    // Get all the form values to post via AJAX
+    var form_data = {};
+    $('#f').find(':input').each(function(){
+        form_data[this.name] = $(this).val();
+    });
+    delete form_data[''];
+
+    // Send the data to automatic_linker.pl
+    $.ajax({
+        url:'/cgi-bin/koha/cataloguing/automatic_linker.pl',
+        type:'post',
+        data: form_data,
+        dataType: 'json',
+        error: function(xhr) {
+            alert("Error : \n" + xhr.responseText);
+        },
+        success: function(json) {
+            switch(json.status) {
+                case 'UNAUTHORIZED':
+                    alert(_("Error : You do not have the permissions necessary to use this functionality."));
+                    break;
+                case 'OK':
+                    updateHeadingLinks(json.links);
+                    break;
+            }
+        },
+        complete: function() {
+            $("#loading").hide();
+        }
+    });
+}
+
+
 function PopupMARCFieldDoc(field) {
     [% IF ( marcflavour == 'MARC21' ) %]
         _MARC21FieldDoc(field);
-- 
2.1.0