From 7a3228357ab1b2f58a4da4ab9013b08a5085cc03 Mon Sep 17 00:00:00 2001
From: Bouzid Fergani <bouzid.fergani@inlibro.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.

 add authority type in the form to create the missing  authority.
 when authority was found, the 600$9 field have the authid.

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.
   * if matching authority was found,  The message box should now show "600 - A matching authority was found in the local database".
   * if no matching authority was found, the message box should now show  "600 - No matching authority 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 will appear, containing a form to create the missing  authority. 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.
 * Fill in the form and 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.".

Signed-off-by: Katrin Fischer <katrin.fischer.83@web.de>

https://bugs.koha-community.org/show_bug.cgi?id=12299
Signed-off-by: Michal Denar <black23@gmail.com>
---
 C4/AuthoritiesMarc.pm                              |  64 +++++++
 C4/Biblio.pm                                       |  18 +-
 C4/Linker.pm                                       |   5 +-
 authorities/authorities.pl                         |   6 +-
 cataloguing/automatic_linker.pl                    |  75 ++++++++
 cataloguing/z3950_auth_search.pl                   |   3 +
 .../prog/en/modules/authorities/authorities.tt     |   7 +-
 .../authorities/blinddetail-biblio-search.tt       |  12 +-
 .../prog/en/modules/cataloguing/addbiblio.tt       | 207 ++++++++++++++++++++-
 .../en/modules/cataloguing/z3950_auth_search.tt    |   3 +-
 koha-tmpl/intranet-tmpl/prog/js/cataloging.js      |   6 +
 koha-tmpl/intranet-tmpl/prog/js/z3950_search.js    |   7 +-
 t/Biblio.t                                         |   2 +
 13 files changed, 399 insertions(+), 16 deletions(-)
 create mode 100755 cataloguing/automatic_linker.pl

diff --git a/C4/AuthoritiesMarc.pm b/C4/AuthoritiesMarc.pm
index bd25837c36..59874c518e 100644
--- a/C4/AuthoritiesMarc.pm
+++ b/C4/AuthoritiesMarc.pm
@@ -47,6 +47,7 @@ BEGIN {
     	&GetAuthMARCFromKohaField 
 
     	&AddAuthority
+        &AddAuthorityFromHeading
     	&ModAuthority
     	&DelAuthority
     	&GetAuthority
@@ -1570,6 +1571,69 @@ sub get_auth_type_location {
     }
 }
 
+=head2 AddAuthorityFromHeading
+
+  my $authid = AddAuthorityFromHeading($marcrecordauth, $heading);
+
+Creates a new authority from an heading and its associated field.
+
+Returns the id of the newly created authority.
+
+=cut
+
+sub AddAuthorityFromHeading {
+    my $marcrecordauth = shift;
+    my $heading = shift;
+
+    if(!defined $heading || !defined $marcrecordauth){
+       return 0;
+    }
+    # 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 (not $heading->{'subject_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)
+    );
+
+    my $authid = C4::AuthoritiesMarc::AddAuthority( $marcrecordauth, '', $heading->auth_type() );
+    return $authid;
+}
+
+
+
 END { }       # module clean-up code here (global destructor)
 
 1;
diff --git a/C4/Biblio.pm b/C4/Biblio.pm
index da86e62fdd..9c4793fe3a 100644
--- a/C4/Biblio.pm
+++ b/C4/Biblio.pm
@@ -460,7 +460,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>
@@ -482,6 +482,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';
@@ -489,7 +490,6 @@ sub LinkBibHeadingsToAuthorities {
     }
     require C4::Heading;
     require C4::AuthoritiesMarc;
-
     $allowrelink = 1 unless defined $allowrelink;
     my $num_headings_changed = 0;
     foreach my $field ( $bib->fields() ) {
@@ -502,6 +502,7 @@ 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;
         }
 
@@ -509,17 +510,23 @@ sub LinkBibHeadingsToAuthorities {
         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 => 'LOCAL_FOUND'}) if $verbose;
         }
         else {
+            my $authority_type = Koha::Authority::Types->find( $heading->auth_type() );
             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 ) ) {
@@ -595,24 +602,29 @@ sub LinkBibHeadingsToAuthorities {
                     $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', auth_type => $heading->auth_type(), tag_to_report => $authority_type->auth_tag_to_report}) if $verbose;
                 }
             }
             else {
                 $results{'unlinked'}->{ $heading->display_form() }++;
+                push(@{$results{'details'}}, { tag => $field->tag(), authid => undef, status => 'NONE_FOUND', auth_type => $heading->auth_type(), tag_to_report => $authority_type->auth_tag_to_report}) if $verbose;
             }
         }
 
     }
+    push(@{$results{'details'}}, { tag => '', authid => undef, status => 'UNCHANGED'}) unless %results;
     return $num_headings_changed, \%results;
 }
 
diff --git a/C4/Linker.pm b/C4/Linker.pm
index 969d2eac99..ec7b0358e2 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 e422cafb36..506997394d 100755
--- a/authorities/authorities.pl
+++ b/authorities/authorities.pl
@@ -33,6 +33,7 @@ use MARC::File::XML;
 use C4::Biblio;
 use Koha::Authority::Types;
 use Koha::ItemTypes;
+use C4::Heading;
 use vars qw( $tagslib);
 use vars qw( $authorised_values_sth);
 use vars qw( $is_a_modif );
@@ -287,6 +288,9 @@ sub create_input {
 
         }
     }
+    if ($cgi->param('tagreport') && $subfield_data{tag} == $cgi->param('tagreport')) {
+        $subfield_data{marc_value}{value} = $cgi->param('tag'. $cgi->param('tagbiblio') . 'subfield' . $subfield_data{subfield});
+    }
     $subfield_data{'index_subfield'} = $index_subfield;
     return \%subfield_data;
 }
@@ -575,7 +579,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);
 
 $tagslib = GetTagsLabels(1,$authtypecode);
 $mandatory_z3950 = GetMandatoryFieldZ3950($authtypecode);
diff --git a/cataloguing/automatic_linker.pl b/cataloguing/automatic_linker.pl
new file mode 100755
index 0000000000..753fc80c32
--- /dev/null
+++ b/cataloguing/automatic_linker.pl
@@ -0,0 +1,75 @@
+#!/usr/bin/perl
+
+# Frédérick Capovilla, 2011 - Libéo
+# Bouzid Fergani, 2020   - inLibro
+#
+# This file is part of Koha.
+#
+# Koha is free software; you can redistribute it and/or modify it under the
+# terms of the GNU General Public License as published by the Free Software
+# Foundation; either version 3 of the License, or (at your option) any later
+# version.
+#
+# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with Koha; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+use Modern::Perl;
+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 = CGI::Cookie->fetch;
+my $sessid = $cookies{'CGISESSID'}->value || $input->param('CGISESSID');
+my ( $auth_status, $auth_sessid ) =
+  C4::Auth::check_cookie_auth( $sessid, { editauthorities => 1, editcatalogue => 1 } );
+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 $record = TransformHtmlToMarc($input,1);
+
+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,
+    $input->param('frameworkcode'),
+    C4::Context->preference("CatalogModuleRelink") || '', 1
+);
+
+$json->{status} = 'OK';
+$json->{links} = $results->{details} || '';
+
+print to_json($json);
diff --git a/cataloguing/z3950_auth_search.pl b/cataloguing/z3950_auth_search.pl
index 1e25468785..ec8266d037 100755
--- a/cataloguing/z3950_auth_search.pl
+++ b/cataloguing/z3950_auth_search.pl
@@ -42,6 +42,7 @@ my $subjectsubdiv       = $input->param('subjectsubdiv');
 my $srchany       = $input->param('srchany');
 my $op            = $input->param('op')||'';
 my $page            = $input->param('current_page') || 1;
+my $index =$input->param('index');
 $page = $input->param('goto_page') if $input->param('changepage_goto');
 my $controlnumber    = $input->param('controlnumber');
 
@@ -65,6 +66,7 @@ $template->param(
     srchany      => $srchany,
     authid => $authid,
     controlnumber => $controlnumber,
+    index => $index,
 );
 
 if ( $op ne "do_search" ) {
@@ -74,6 +76,7 @@ if ( $op ne "do_search" ) {
     $template->param(
         serverloop   => $serverloop,
         opsearch     => "search",
+        index        => $index,
     );
     output_html_with_http_headers $input, $cookie, $template->output;
     exit;
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 026f48560f..db4a2cbcb2 100644
--- a/koha-tmpl/intranet-tmpl/prog/en/modules/authorities/authorities.tt
+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/authorities/authorities.tt
@@ -28,8 +28,9 @@
 		});
         $("#z3950submit").click(function(){
             var strQuery = GetZ3950Terms();
+            var index = "&index=[% index | html %]";
             if(strQuery){
-                window.open("/cgi-bin/koha/cataloguing/z3950_auth_search.pl?authid=[% authid | html %]"+strQuery,"z3950search",'width=740,height=450,location=yes,toolbar=no,scrollbars=yes,resize=yes');
+                window.open("/cgi-bin/koha/cataloguing/z3950_auth_search.pl?authid=[% authid | html %]"+strQuery+index,"z3950search",'width=740,height=450,location=yes,toolbar=no,scrollbars=yes,resize=yes');
             }
             return false;
         });
@@ -207,9 +208,9 @@ function confirmnotdup(redirect){
     <input type="hidden" name="op" value="add" />
     <input type="hidden" name="addfield_field" value="" />
     <input type="hidden" name="repeat_field" value="" />
-    <input type="hidden" name="authtypecode" value="[% authtypecode | html %]" />
+    <input type="hidden" name="authtypecode" value="[% authtypecode  | html%]" />
     <input type="hidden" name="authid" value="[% authid | html %]" />
-    <input type="hidden" name="index" value="[% index | html %]" />
+    <input type="hidden" name="index" value="[% index | html  %]" />
     <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 083eda7c18..97a8432741 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
@@ -44,11 +44,12 @@
             var whichfield;
             try {
                 whichfield = opener.opener.document.getElementById(index_start);
+                autolinker_dialog = opener.opener.document.getElementById("autolinker_dialog");
             } catch(e) {
-                return;
+                whichfield = opener.document.getElementById(index_start);
+                autolinker_dialog = opener.document.getElementById("autolinker_dialog");
             }
             var field_start = whichfield.parentNode.parentNode.parentNode;
-
             // Sets the good number of form fields for the specified subfield
             // Returns false if the cloning failed
             function SetSubfieldNumber(subfield_name, nb) {
@@ -138,6 +139,8 @@
                 [% ELSE %]
                     if(code.value=='9'){
                         subfield.value = "[% To.json(authid) | $raw %]";
+                        subfield.style.backgroundColor = "#99FF99";
+
                         break;
                     }
                 [% END %]
@@ -145,6 +148,11 @@
             }
             [% END %]
 
+            //Delete create authority button and status
+            var linkinfos = field_start.getElementsByTagName('i');
+            for (index = linkinfos.length - 1; index >= 0; index--) {
+                linkinfos[index].parentNode.removeChild(linkinfos[index]);
+            }
             opener.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 66d253e363..ce03f331ea 100644
--- a/koha-tmpl/intranet-tmpl/prog/en/modules/cataloguing/addbiblio.tt
+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/cataloguing/addbiblio.tt
@@ -87,6 +87,10 @@
             PopupZ3950();
         });
 
+        $("#linkerbutton").click(function(){
+            AutomaticLinker();
+        });
+
         $("#saverecord").click(function(){
             $(".btn-group").removeClass("open");
             onOption();
@@ -173,7 +177,7 @@
         var onOption = function () {
             return Check();
         }
-    [% END %]
+[% END %]
 
     function confirmnotdup(redirect){
         $("#confirm_not_duplicate").attr("value","1");
@@ -196,6 +200,204 @@
         }
     }
 
+    /**
+    * this function append button for create new authority if not found
+    */
+
+    function addCreateAuthorityButton(tag_subfield_line, heading, tag_index) {
+        var title = _("Create authority");
+        var elem = $('<a href="#" title="' + title + '"><i class="fa fa-plus-circle"></i></a>');
+        tag_subfield_line.append(elem);
+        var tag_subfield_line_a = $('.subfield_line[id^=subfield' + heading.tag + 'a]').eq(tag_index);
+        var subfield_a = tag_subfield_line_a.children('.input_marceditor').eq(0);
+        var index = subfield_a.attr('id');
+        elem.click(function() {
+            var popup = window.open("", "new_auth_popup",'fullscreen,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 authtypecode
+                form.append($('<input>').attr({
+                    type: 'hidden',
+                    name: 'authtypecode',
+                    value: heading.auth_type
+                }));
+                form.append($('<input>').attr({
+                    type: 'hidden',
+                    name: 'tagreport',
+                    value: heading.tag_to_report
+                }));
+                form.append($('<input>').attr({
+                    type: 'hidden',
+                    name: 'tagbiblio',
+                    value: heading.tag
+                }));
+                form.append($('<input>').attr({
+                    type: 'hidden',
+                    name: 'index',
+                    value: index
+                }));
+                $('.tag[id^=tag_' + heading.tag + '_]').eq(tag_index).find(':input').each(function(){
+                    form.append($('<input>').attr({
+                        type: 'hidden',
+                        name: this.name.split('_',4).join(''),
+                        value: $(this).val()
+                    }));
+                });
+
+                $('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 = '<i class="fa fa-close" style="color: #FFAAAA"></i> ';
+            var message = '';
+            var field_color = '#FFAAAA';
+            switch(heading.status) {
+                case 'LOCAL_FOUND':
+                    image = '<i class="fa fa-check" style="color: #99FF99"></i> ';
+                    message = _("A matching authority was found in the local database.");
+                    field_color = '#99FF99';
+                    break;
+                case 'CREATED':
+                    image = '<i class="fa fa-check" style="color: #99FF99"></i> ';
+                    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.find('i').each(function() {
+                this.remove();
+            });
+            tag_subfield_line.append(image);
+
+            // 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' && tag_subfield_line.find('i').length == 1) {
+                addCreateAuthorityButton(tag_subfield_line, heading , 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 Koha.Preference('marcfielddocurl') %]
             var docurl = "[% Koha.Preference('marcfielddocurl').replace('"','&quot;') | html %]";
@@ -585,6 +787,9 @@
                         [% ELSE %]
                             <div class="btn-group"><a class="btn btn-default" href="#" id="z3950search"><i class="fa fa-search"></i> Z39.50/SRU search</a></div>
                         [% END %]
+                        [% IF ( CAN_user_editauthorities ) %]
+                            <div class="btn-group"><a class="btn btn-default" href="#" id="linkerbutton"><i class="fa fa-refresh"></i> Link authorities automatically</a></div>
+                        [% END %]
                     [% END %]
 
                     <div class="btn-group">
diff --git a/koha-tmpl/intranet-tmpl/prog/en/modules/cataloguing/z3950_auth_search.tt b/koha-tmpl/intranet-tmpl/prog/en/modules/cataloguing/z3950_auth_search.tt
index b66e9d6d99..4bb0efb828 100644
--- a/koha-tmpl/intranet-tmpl/prog/en/modules/cataloguing/z3950_auth_search.tt
+++ b/koha-tmpl/intranet-tmpl/prog/en/modules/cataloguing/z3950_auth_search.tt
@@ -20,6 +20,7 @@
         <form method="post" action="z3950_auth_search.pl" name="f" class="checkboxed">
         <input type="hidden" name="op" id="op" value="do_search" />
         <input type="hidden" name="authid" value="[% authid | html %]" />
+        <input type="hidden" name="index" value="[% index | html %]" />
             <div class="row">
             <div class="col-xs-6">
                 <fieldset class="rows">
@@ -94,7 +95,7 @@
                     </a>
                     <ul class="dropdown-menu pull-right" role="menu" aria-labelledby="cataloguesearchactions[% breeding_loo.breedingid | html %]">
                         <li><a href="/cgi-bin/koha/catalogue/showmarc.pl?importid=[% breeding_loo.breedingid | uri %]" class="previewMARC"><i class="fa fa-eye"></i> MARC preview</a></li>
-                        <li><a href="#" class="import_record" data-breedingid="[% breeding_loo.breedingid | html %]" data-heading_code="[% breeding_loo.heading_code | html %]" data-authid="[% breeding_loo.authid | html %]"><i class="fa fa-download"></i> Import</a></li>
+                        <li><a href="#" class="import_record" data-breedingid="[% breeding_loo.breedingid | html %]" data-heading_code="[% breeding_loo.heading_code | html %]" data-authid="[% breeding_loo.authid | html %]" data-index="[% index | html %]" ><i class="fa fa-download"></i> Import</a></li>
                     </ul>
                 </div>
             </td>
diff --git a/koha-tmpl/intranet-tmpl/prog/js/cataloging.js b/koha-tmpl/intranet-tmpl/prog/js/cataloging.js
index c74d953979..7bf727e510 100644
--- a/koha-tmpl/intranet-tmpl/prog/js/cataloging.js
+++ b/koha-tmpl/intranet-tmpl/prog/js/cataloging.js
@@ -137,6 +137,9 @@ 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:""});
                 }
             }
             var textareas = divs[i].getElementsByTagName('textarea');
@@ -146,6 +149,9 @@ function CloneField(index, hideMarc, advancedMARCEditor) {
                 }
             }
 
+            // 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);
 
diff --git a/koha-tmpl/intranet-tmpl/prog/js/z3950_search.js b/koha-tmpl/intranet-tmpl/prog/js/z3950_search.js
index 7ee94e5aea..3f179adef3 100644
--- a/koha-tmpl/intranet-tmpl/prog/js/z3950_search.js
+++ b/koha-tmpl/intranet-tmpl/prog/js/z3950_search.js
@@ -1,10 +1,10 @@
 //z3950_search.js for Authorities, Bib records and Acquisitions module
-function Import(Breeding, recordid, AuthType, FrameworkCode) {
+function Import(Breeding, recordid, AuthType, FrameworkCode, index) {
 
     if ( AuthType == false ) {
         opener.document.location="../cataloguing/addbiblio.pl?biblionumber="+recordid+"&z3950=1&frameworkcode="+FrameworkCode+"&breedingid="+Breeding;
     } else {
-        opener.document.location="../authorities/authorities.pl?breedingid="+Breeding+"&authtypecode="+AuthType+"&authid="+recordid;
+        opener.document.location="../authorities/authorities.pl?breedingid="+Breeding+"&authtypecode="+AuthType+"&authid="+recordid+"&index="+index;
     }
     window.close();
     return false;
@@ -127,10 +127,11 @@ $( document ).ready( function() {
         var data_authid = $( this ).data( "authid" );
         var data_biblionumber = $( this ).data( "biblionumber" );
         var data_frameworkcode = $( this ).data( "frameworkcode" );
+        var data_index = $( this ).data( "index" );
         if ( data_headingcode == undefined ) {
             Import( data_breedingid, data_biblionumber, false , data_frameworkcode );
         } else {
-            Import( data_breedingid, data_authid, data_headingcode );
+            Import( data_breedingid, data_authid, data_headingcode, "", data_index );
         }
         return false;
     });
diff --git a/t/Biblio.t b/t/Biblio.t
index 5596b4bf19..8e4e713375 100755
--- a/t/Biblio.t
+++ b/t/Biblio.t
@@ -158,3 +158,5 @@ warnings_like { $ret = UpdateTotalIssues() }
 
 ok( !defined $ret, 'UpdateTotalIssues returns carped warning if biblio record does not exist');
 
+is(C4::Biblio::AddAuthorityFromHeading(), 0, 'AddAuthorityFromHeading returns zero if not passed heading, field or bib');
+1;
-- 
2.11.0