MT 1059 : Records merging

Give the user the ability to merge two records, one being kept and the other deleted.
Selection of the records to merge can be done from virtualshelves.
This commit is contained in:
Matthias Meusburger 2009-11-16 17:44:33 +01:00 committed by Henri-Damien LAURENT
parent 91ac35d517
commit 8d536b8ecf
4 changed files with 638 additions and 0 deletions

248
cataloguing/merge.pl Executable file
View File

@ -0,0 +1,248 @@
#!/usr/bin/perl
# Copyright 2009 BibLibre
#
# This file is part of Koha.
#
# Koha is free software; you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free Software
# Foundation; either version 2 of the License, or (at your option) any later
# version.
#
# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along with
# Koha; if not, write to the Free Software Foundation, Inc., 59 Temple Place,
# Suite 330, Boston, MA 02111-1307 USA
use strict;
use CGI;
use C4::Output;
use C4::Auth;
use C4::Items;
use C4::Biblio;
use C4::Serials;
use YAML;
my $input = new CGI;
my @biblionumber = $input->param('biblionumber');
my $merge = $input->param('merge');
my @errors;
my ( $template, $loggedinuser, $cookie ) = get_template_and_user(
{
template_name => "cataloguing/merge.tmpl",
query => $input,
type => "intranet",
authnotrequired => 0,
flagsrequired => { editcatalogue => 1 },
}
);
#------------------------
# Merging
#------------------------
if ($merge) {
my @params = $input->param();
my $dbh = C4::Context->dbh;
my $sth;
# Creating a new record from the html code
my $record = TransformHtmlToMarc( \@params , $input );
my $tobiblio = $input->param('biblio1');
my $frombiblio = $input->param('biblio2');
my $frameworkcode = &GetFrameworkCode($tobiblio);
my @notmoveditems;
# Modifying the reference record
ModBiblio($record, $tobiblio, $frameworkcode);
# Moving items from the other record to the reference record
my $itemnumbers = get_itemnumbers_of($frombiblio);
use Data::Dumper;
foreach my $itloop ($itemnumbers->{$frombiblio}) {
foreach my $itemnumber (@$itloop) {
my $res = MoveItemFromBiblio($itemnumber, $frombiblio, $tobiblio);
if (not defined $res) {
push @notmoveditems, $itemnumber;
}
}
}
# If some items could not be moved :
if (scalar(@notmoveditems) > 0) {
my $itemlist = join(' ',@notmoveditems);
push @errors, "The following items could not be moved from the old record to the new one: $itemlist";
}
# Moving subscriptions from the other record to the reference record
my $subcount = CountSubscriptionFromBiblionumber($frombiblio);
if ($subcount > 0) {
$sth = $dbh->prepare("UPDATE subscription SET biblionumber = ? WHERE biblionumber = ?");
$sth->execute($tobiblio, $frombiblio);
$sth = $dbh->prepare("UPDATE subscriptionhistory SET biblionumber = ? WHERE biblionumber = ?");
$sth->execute($tobiblio, $frombiblio);
}
# Moving serials
$sth = $dbh->prepare("UPDATE serial SET biblionumber = ? WHERE biblionumber = ?");
$sth->execute($tobiblio, $frombiblio);
# TODO : Moving reserves
# Deleting the other record
if (scalar(@errors) == 0) {
my $error = DelBiblio($frombiblio);
push @errors, $error if ($error);
}
# Errors
my @errors_loop = map{{error => $_}}@errors;
# Parameters
$template->param({
errors => \@errors_loop,
result => 1,
biblio1 => $input->param('biblio1')
});
#-------------------------
# Show records to merge
#-------------------------
} else {
my $mergereference = $input->param('mergereference');
my $biblionumber = $input->param('biblionumber');
my $data1 = GetBiblioData($biblionumber[0]);
my $data2 = GetBiblioData($biblionumber[1]);
# Ask the user to choose which record will be the kept
if (not $mergereference) {
$template->param({
choosereference => 1,
biblio1 => $biblionumber[0],
biblio2 => $biblionumber[1],
title1 => $data1->{'title'},
title2 => $data2->{'title'}
});
} else {
if (scalar(@biblionumber) != 2) {
push @errors, "An unexpected number of records was provided for merging. Currently only two records at a time can be merged.";
}
# Checks if both records use the same framework
my $frameworkcode1 = &GetFrameworkCode($biblionumber[0]);
my $frameworkcode2 = &GetFrameworkCode($biblionumber[1]);
my $framework;
if ($frameworkcode1 ne $frameworkcode2) {
push @errors, "The records selected for merging are using different frameworks. Currently merging is only available for records using the same framework.";
} else {
$framework = $frameworkcode1;
}
# Getting MARC Structure
my $tagslib = GetMarcStructure(1, $framework);
my $notreference = ($biblionumber[0] == $mergereference) ? $biblionumber[1] : $biblionumber[0];
# Creating a loop for display
my @record1 = _createMarcHash(GetMarcBiblio($mergereference), $tagslib);
my @record2 = _createMarcHash(GetMarcBiblio($notreference), $tagslib);
# Errors
my @errors_loop = map{{error => $_}}@errors;
# Parameters
$template->param({
errors => \@errors_loop,
biblio1 => $mergereference,
biblio2 => $notreference,
mergereference => $mergereference,
record1 => @record1,
record2 => @record2,
framework => $framework
});
}
}
output_html_with_http_headers $input, $cookie, $template->output;
exit;
# ------------------------
# Functions
# ------------------------
sub _createMarcHash {
my $record = shift;
my $tagslib = shift;
my @array;
my @fields = $record->fields();
foreach my $field (@fields) {
my $fieldtag = $field->tag();
if ($fieldtag < 10) {
if ($tagslib->{$fieldtag}->{'@'}->{'tab'} >= 0) {
push @array, {
field => [
{
tag => $fieldtag,
key => createKey(),
value => $field->data(),
}
]
};
}
} else {
my @subfields = $field->subfields();
my @subfield_array;
foreach my $subfield (@subfields) {
if ($tagslib->{$fieldtag}->{@$subfield[0]}->{'tab'} >= 0) {
push @subfield_array, {
subtag => @$subfield[0],
subkey => createKey(),
value => @$subfield[1],
};
}
}
if ($tagslib->{$fieldtag}->{'tab'} >= 0 && $fieldtag ne '995') {
push @array, {
field => [
{
tag => $fieldtag,
key => createKey(),
indicator1 => $field->indicator(1),
indicator2 => $field->indicator(2),
subfield => [@subfield_array],
}
]
};
}
}
}
return [@array];
}
=item CreateKey
Create a random value to set it into the input name
=cut
sub createKey(){
return int(rand(1000000));
}

29
cataloguing/merge_ajax.pl Executable file
View File

@ -0,0 +1,29 @@
#!/usr/bin/perl
use strict;
# standard or CPAN modules used
use IO::File;
use CGI;
use CGI::Session;
use C4::Context;
use C4::Biblio;
use C4::Auth qw/check_cookie_auth/;
use C4::UploadedFile;
use JSON;
use CGI::Cookie; # need to check cookies before
# having CGI parse the POST request
my %cookies = fetch CGI::Cookie;
my ($auth_status, $sessionID) = check_cookie_auth($cookies{'CGISESSID'}->value, { editcatalogue => '1' });
if ($auth_status ne "ok") {
my $reply = CGI->new("");
print $reply->header(-type => 'text/html');
exit 0;
}
my $reply = new CGI;
my $framework = $reply->param('frameworkcode');
my $tagslib = GetMarcStructure(1, $framework);
print $reply->header(-type => 'text/html');
print encode_json $tagslib;

View File

@ -0,0 +1,345 @@
<!-- TMPL_INCLUDE NAME="doc-head-open.inc" -->
<title>Koha &rsaquo; Cataloging &rsaquo; Merging records</title>
<!-- TMPL_INCLUDE NAME="doc-head-close.inc" -->
<style type="text/css">
div.record ul, div.record li { float:none; display:block; }
/* We use this style "against" the li ui-tabs-nav style automatically applied */
</style>
<script type="text/javascript">
//<![CDATA[
// When submiting the form
function mergeformsubmit() {
$("ul#ulrecord1").remove();
$("ul#ulrecord2").remove();
}
$(document).ready(function(){
// Creating tabs
$("#tabs").tabs();
// Getting marc structure via ajax
tagslib = [];
$.getJSON("/cgi-bin/koha/cataloguing/merge_ajax.pl", {frameworkcode : "<!-- TMPL_VAR name="framework" -->" }, function(json) {
tagslib = json;
});
// Toggle a field / subfield
function toggleField(pField) {
// Getting the key of the clicked checkbox
var ckid = $(pField).attr("id");
var tab = ckid.split('_');
var source = tab[1]; // From which record the click came from
var key = tab[2];
var type = $(pField).attr("class");
// Getting field/subfield
var field;
var subfield;
if (type == "subfieldpick") {
field = $(pField).parent().parent().parent().find("span.field").text();
subfield = $(pField).parent().find("span.subfield").text();
} else {
field = $(pField).parent().find("span.field").text();
}
// If the field has just been checked
if (pField.checked) {
// We check for repeatability
var canbeadded = true;
if (type == "subfieldpick") {
var repeatable = 1;
var alreadyexists = 0;
if (tagslib[field] && tagslib[field][subfield]) {
repeatable = tagslib[field][subfield].repeatable; // Note : we can't use the dot notation here (tagslib.021) because the key is a number
// TODO : Checking for subfields
}
} else {
if (tagslib[field]) {
repeatable = tagslib[field].repeatable;
alreadyexists = $("#resultul span.field:contains(" + field + ")");
if (repeatable == 0 && alreadyexists.length != 0) {
canbeadded = false;
}
}
}
// If the field is not repeatable, we check if it already exists in the result table
if (canbeadded == false) {
alert(_('The field is non-repeatable and already exists in the destination record. Therefore, you cannot add it.'));
pField.checked = 0;
} else {
// Cloning the field or subfield we picked
var clone = $(pField).parent().clone();
// Removing the checkboxes from it
$(clone).find("input.subfieldpick, input.fieldpick").each(function() {
$(this).remove();
});
// If we are a subfield
if (type == "subfieldpick") {
// then we need to find who is our parent field...
fieldkey = $(pField).parent().parent().parent().attr("id");
// Find where to add the subfield
// First, check if the field is not already in the destination record
if ($("#resultul li#" + fieldkey).length > 0) {
// If so, we add our field to it
$("#resultul li#" + fieldkey + " ul").append(clone);
} else {
// If not, we add the subfield to the first matching field
var where = 0;
$("#resultul li span.field").each(function() {
if (where == 0 && $(this).text() == field) {
where = this;
}
});
// If there is no matching field in the destination record
if (where == 0) {
// TODO:
// We select the whole field and removing non-selected subfields, instead of...
// Alerting the user
alert(_('This subfield cannot be added: there is no ' + field + ' field in the destination record.'));
pField.checked = false;
} else {
$(where).nextAll("ul").append(clone);
}
}
} else {
// If we are a field
var where = 0;
// Find where to add the field
$("#resultul li span.field").each(function() {
if (where == 0 && $(this).text() > field) {
where = this;
}
});
$(where).parent().before(clone);
}
}
} else {
// Else, we remove it from the results tab
$("ul#resultul li#k" + key).remove();
}
}
// When a field is checked / unchecked
$('input.fieldpick').click(function() {
toggleField(this);
// (un)check all subfields
var ischecked = this.checked;
$(this).parent().find("input.subfieldpick").each(function() {
this.checked = ischecked;
});
});
// When a field or subfield is checked / unchecked
$("input.subfieldpick").click(function() {
toggleField(this);
});
});
//]]>
</script>
</head>
<body>
<!-- TMPL_INCLUDE NAME="header.inc" -->
<div id="breadcrumbs"><a href="/cgi-bin/koha/mainpage.pl">Home</a> &rsaquo; <a href="/cgi-bin/koha/cataloguing/addbooks.pl">Cataloging</a> &rsaquo; <!-- TMPL_IF NAME="biblionumber" -->Editing <em><!-- TMPL_VAR NAME="title" escape="html" --></em> (Record Number <!-- TMPL_VAR name="biblionumber" -->)<!-- TMPL_ELSE -->Add MARC Record<!-- /TMPL_IF --></div>
<div id="doc" class="yui-t7">
<div id="bd">
<div id="yui-main">
<div class="yui-g">
<h1>Merging records</h1>
<!-- TMPL_IF name="result" -->
<!-- TMPL_IF name="errors" -->
<!-- TMPL_LOOP name="errors" -->
<div class="error"><!-- TMPL_VAR name="error" -->.<br />Therefore, the record to be merged has not been deleted.</div>
<!-- /TMPL_LOOP -->
<!-- TMPL_ELSE -->
<script type="text/javascript">window.location.href="/cgi-bin/koha/catalogue/MARCdetail.pl?biblionumber=<!-- TMPL_VAR NAME="biblio1" -->"</script>
<p>The merging was successful. <a href="/cgi-bin/koha/catalogue/MARCdetail.pl?biblionumber=<!-- TMPL_VAR NAME="biblio1" -->">Click here to see the merged record.</a></p>
<!-- /TMPL_IF -->
<!-- TMPL_ELSE -->
<!-- TMPL_IF NAME="choosereference" -->
<p>Please choose which record will be the reference for the merge (the record chosen as reference will be kept, and the other will be deleted) : </p>
<form id="mergeform" action="/cgi-bin/koha/cataloguing/merge.pl" method="post">
<fieldset>
<legend>Merge reference</legend>
<input type="radio" value="<!-- TMPL_VAR NAME="biblio1" -->" checked="checked" id="mergereference1" name="mergereference" /><label for="mergereference1"><!-- TMPL_VAR NAME="title1" --> (<!-- TMPL_VAR NAME="biblio1" -->)</label><br />
<input type="radio" value="<!-- TMPL_VAR NAME="biblio2" -->" id="mergereference2" name="mergereference" /><label for="mergereference2"><!-- TMPL_VAR NAME="title2" --> (<!-- TMPL_VAR NAME="biblio2" -->)</label><br />
<input type="hidden" name="biblionumber" value="<!-- TMPL_VAR name="biblio1" -->" />
<input type="hidden" name="biblionumber" value="<!-- TMPL_VAR name="biblio2" -->" />
<input type="submit" />
</fieldset>
</form>
<!-- TMPL_ELSE -->
<!-- TMPL_IF name="errors" -->
<!-- TMPL_LOOP name="errors" -->
<div class="error"><!-- TMPL_VAR name="error" --></div>
<!-- /TMPL_LOOP -->
<!-- TMPL_ELSE -->
<form id="mergeform" action="/cgi-bin/koha/cataloguing/merge.pl" method="post" onsubmit="return mergeformsubmit()">
<div id="tabs" class="yui-u first">
<h2>Source records</h2>
<ul>
<li><a href="#tabrecord1"><!-- TMPL_VAR name="biblio1" --></a></li>
<li><a href="#tabrecord2"><!-- TMPL_VAR name="biblio2" --></a></li>
</ul>
<div id="tabrecord1">
<!-- TMPL_IF name="record1" -->
<br />
<div class="record">
<ul id="ulrecord1">
<!-- TMPL_LOOP NAME="record1" -->
<!-- TMPL_LOOP NAME="field" -->
<li id="k<!-- TMPL_VAR name="key" -->">
<input type="checkbox" checked="checked" class="fieldpick" id="rec_1_<!-- TMPL_VAR name="key" -->" />
<span class="field"><!-- TMPL_VAR NAME="tag" --></span>
<input type="hidden" name="tag_<!-- TMPL_VAR name="tag" -->_indicator1_<!-- TMPL_VAR NAME="key" -->" value="<!-- TMPL_VAR NAME="indicator1" -->" />
<input type="hidden" name="tag_<!-- TMPL_VAR name="tag" -->_indicator2_<!-- TMPL_VAR NAME="key" -->" value="<!-- TMPL_VAR NAME="indicator2" -->" />
<!-- TMPL_IF NAME="value" --> / <!-- TMPL_VAR NAME="value" -->
<input type="hidden" name="tag_<!-- TMPL_VAR name="tag" -->_code_00_<!-- TMPL_VAR NAME="key" -->" value="00" />
<input type="hidden" name="tag_<!-- TMPL_VAR name="tag" -->_subfield_00_<!-- TMPL_VAR NAME="key" -->" value="<!-- TMPL_VAR NAME="value" -->" />
<!-- /TMPL_IF -->
<!-- TMPL_IF NAME="subfield" -->
<ul>
<!-- TMPL_LOOP NAME="subfield" -->
<li id="k<!-- TMPL_VAR name="subkey" -->">
<input type="checkbox" checked="checked" class="subfieldpick" id="rec_1_<!-- TMPL_VAR name="subkey" -->" />
<span class="subfield"><!-- TMPL_VAR NAME="subtag" --></span> / <!-- TMPL_VAR NAME="value" -->
<input type="hidden" name="tag_<!-- TMPL_VAR name="tag" -->_code_<!-- TMPL_VAR NAME="subtag" -->_<!-- TMPL_VAR NAME="key" -->_<!-- TMPL_VAR NAME="subkey" -->" value="<!-- TMPL_VAR NAME="subtag" -->" />
<input type="hidden" name="tag_<!-- TMPL_VAR name="tag" -->_subfield_<!-- TMPL_VAR NAME="subtag" -->_<!-- TMPL_VAR NAME="key" -->_<!-- TMPL_VAR NAME="subkey" -->" value="<!-- TMPL_VAR NAME="value" -->" />
</li>
<!-- /TMPL_LOOP -->
</ul>
<!-- /TMPL_IF -->
<!-- /TMPL_LOOP -->
</li>
<!-- /TMPL_LOOP -->
</ul>
</div>
</div>
<div id="tabrecord2">
<!-- TMPL_IF name="record2" -->
<br />
<div class="record">
<ul id="ulrecord2">
<!-- TMPL_LOOP NAME="record2" -->
<!-- TMPL_LOOP NAME="field" -->
<li id="k<!-- TMPL_VAR name="key" -->">
<input type="checkbox" class="fieldpick" id="rec_2_<!-- TMPL_VAR name="key" -->" />
<span class="field"><!-- TMPL_VAR NAME="tag" --></span>
<input type="hidden" name="tag_<!-- TMPL_VAR name="tag" -->_indicator1_<!-- TMPL_VAR NAME="key" -->" value="<!-- TMPL_VAR NAME="indicator1" -->" />
<input type="hidden" name="tag_<!-- TMPL_VAR name="tag" -->_indicator2_<!-- TMPL_VAR NAME="key" -->" value="<!-- TMPL_VAR NAME="indicator2" -->" />
<!-- TMPL_IF NAME="value" --> / <!-- TMPL_VAR NAME="value" -->
<input type="hidden" name="tag_<!-- TMPL_VAR name="tag" -->_code_00_<!-- TMPL_VAR NAME="key" -->" value="00" />
<input type="hidden" name="tag_<!-- TMPL_VAR name="tag" -->_subfield_00_<!-- TMPL_VAR NAME="key" -->" value="<!-- TMPL_VAR NAME="value" -->" />
<!-- /TMPL_IF -->
<!-- TMPL_IF NAME="subfield" -->
<ul>
<!-- TMPL_LOOP NAME="subfield" -->
<li id="k<!-- TMPL_VAR name="subkey" -->">
<input type="checkbox" class="subfieldpick" id="rec_2_<!-- TMPL_VAR name="subkey" -->" />
<span class="subfield"><!-- TMPL_VAR NAME="subtag" --></span> / <!-- TMPL_VAR NAME="value" -->
<input type="hidden" name="tag_<!-- TMPL_VAR name="tag" -->_code_<!-- TMPL_VAR NAME="subtag" -->_<!-- TMPL_VAR NAME="key" -->_<!-- TMPL_VAR NAME="subkey" -->" value="<!-- TMPL_VAR NAME="subtag" -->" />
<input type="hidden" name="tag_<!-- TMPL_VAR name="tag" -->_subfield_<!-- TMPL_VAR NAME="subtag" -->_<!-- TMPL_VAR NAME="key" -->_<!-- TMPL_VAR NAME="subkey" -->" value="<!-- TMPL_VAR NAME="value" -->" />
</li>
<!-- /TMPL_LOOP -->
</ul>
<!-- /TMPL_IF -->
<!-- /TMPL_LOOP -->
</li>
<!-- /TMPL_LOOP -->
</ul>
</div>
<!-- /TMPL_IF -->
</div>
</div> <!-- // #tabs -->
<div id="result" class="yui-u">
<h2>Destination record</h2>
<br /><br />
<ul id="resultul">
<!-- TMPL_LOOP NAME="record1" -->
<!-- TMPL_LOOP NAME="field" --><li id="k<!-- TMPL_VAR name="key" -->"><span class="field"><!-- TMPL_VAR NAME="tag" --></span>
<input type="hidden" name="tag_<!-- TMPL_VAR name="tag" -->_indicator1_<!-- TMPL_VAR NAME="key" -->" value="<!-- TMPL_VAR NAME="indicator1" -->" />
<input type="hidden" name="tag_<!-- TMPL_VAR name="tag" -->_indicator2_<!-- TMPL_VAR NAME="key" -->" value="<!-- TMPL_VAR NAME="indicator2" -->" />
<!-- TMPL_IF NAME="value" --> /
<!-- TMPL_VAR NAME="value" -->
<input type="hidden" name="tag_<!-- TMPL_VAR name="tag" -->_code_00_<!-- TMPL_VAR NAME="key" -->" value="00" />
<input type="hidden" name="tag_<!-- TMPL_VAR name="tag" -->_subfield_00_<!-- TMPL_VAR NAME="key" -->" value="<!-- TMPL_VAR NAME="value" -->" />
<!-- /TMPL_IF -->
<!-- TMPL_IF NAME="subfield" -->
<ul>
<!-- TMPL_LOOP NAME="subfield" -->
<li id="k<!-- TMPL_VAR name="subkey" -->">
<span class="subfield"><!-- TMPL_VAR NAME="subtag" --></span> / <!-- TMPL_VAR NAME="value" -->
<input type="hidden" name="tag_<!-- TMPL_VAR name="tag" -->_code_<!-- TMPL_VAR NAME="subtag" -->_<!-- TMPL_VAR NAME="key" -->_<!-- TMPL_VAR NAME="subkey" -->" value="<!-- TMPL_VAR NAME="subtag" -->" />
<input type="hidden" name="tag_<!-- TMPL_VAR name="tag" -->_subfield_<!-- TMPL_VAR NAME="subtag" -->_<!-- TMPL_VAR NAME="key" -->_<!-- TMPL_VAR NAME="subkey" -->" value="<!-- TMPL_VAR NAME="value" -->" />
</li>
<!-- /TMPL_LOOP -->
</ul>
<!-- /TMPL_IF -->
<!-- /TMPL_LOOP -->
</li>
<!-- /TMPL_LOOP -->
</ul>
<input type="hidden" name="biblio1" value="<!-- TMPL_VAR name="biblio1" -->" />
<input type="hidden" name="biblio2" value="<!-- TMPL_VAR name="biblio2" -->" />
<input type="hidden" name="mergereference" value="<!-- TMPL_VAR name="mergereference" -->" />
<p><input type="submit" name="merge" value="merge" /></p>
</div> <!-- // #result -->
</form>
<!-- /TMPL_IF -->
<!-- /TMPL_IF -->
<!-- /TMPL_IF -->
</div>
</div>
</div>
<!-- TMPL_INCLUDE NAME="intranet-bottom.inc" -->

View File

@ -29,6 +29,21 @@ $(document).ready(function(){
return false;
}
}
/**
* This function checks if the adequate number of records are checked for merging
*/
function MergeItems() {
var checkboxes = $("input:checkbox:checked");
var nbCheckbox = checkboxes.length;
if (nbCheckbox != 2) {
alert(_('Two records must be selected for merging.'));
} else {
location.href='/cgi-bin/koha/cataloguing/merge.pl?biblionumber=' + checkboxes[0].value + '&amp;biblionumber=' + checkboxes[1].value;
}
return false;
}
/**
* This function checks all checkboxes if all are empty,
* or unchecks all if any already checked.
@ -189,6 +204,7 @@ function placeHold () {
<!-- TMPL_IF name="manageshelf" -->
<input type="button" id="placehold" style="display:none" onclick="placeHold(); return false;" value="Place Hold"/>
<input type="submit" value="Remove selected Items" onclick='return confirm(_("Are you sure you want to remove these items from the shelf?"))' />
<input type="submit" value="Merge selected Items" onclick='return MergeItems();' />
<!-- /TMPL_IF -->
</fieldset>
</form>