View | Details | Raw Unified | Return to bug 35659
Collapse All | Expand All

(-)a/Koha/Import/Oaipmh/Authorities.pm (+51 lines)
Line 0 Link Here
1
package Koha::Import::Oaipmh::Authorities;
2
3
# This file is part of Koha.
4
#
5
# Koha is free software; you can redistribute it and/or modify it
6
# under the terms of the GNU General Public License as published by
7
# the Free Software Foundation; either version 3 of the License, or
8
# (at your option) any later version.
9
#
10
# Koha is distributed in the hope that it will be useful, but
11
# WITHOUT ANY WARRANTY; without even the implied warranty of
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
# GNU General Public License for more details.
14
#
15
# You should have received a copy of the GNU General Public License
16
17
use Modern::Perl;
18
19
use Koha::Import::Oaipmh::Authority;
20
21
use base qw(Koha::Objects);
22
23
=head1 NAME
24
25
Koha::Import::Oaipmh::Authorities
26
27
This object represents a collection of OAI-PMH records being imported as authorities
28
29
=head1 API
30
31
=head2 Methods
32
33
34
=head3 type
35
36
=cut
37
38
sub _type {
39
    return 'ImportOaipmhAuthority';
40
}
41
42
=head3 object_class
43
44
Koha::Object class
45
46
=cut
47
48
sub object_class {
49
    return 'Koha::Import::Oaipmh::Authority';
50
}
51
1;
(-)a/Koha/Import/Oaipmh/Authority.pm (+41 lines)
Line 0 Link Here
1
package Koha::Import::Oaipmh::Authority;
2
3
# This file is part of Koha.
4
#
5
# Koha is free software; you can redistribute it and/or modify it
6
# under the terms of the GNU General Public License as published by
7
# the Free Software Foundation; either version 3 of the License, or
8
# (at your option) any later version.
9
#
10
# Koha is distributed in the hope that it will be useful, but
11
# WITHOUT ANY WARRANTY; without even the implied warranty of
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
# GNU General Public License for more details.
14
#
15
# You should have received a copy of the GNU General Public License
16
17
use Modern::Perl;
18
19
use base qw(Koha::Object);
20
21
=head1 NAME
22
23
Koha::Import::Oaipmh::Authority
24
25
This object represents an OAI-PMH record being imported as an authority
26
27
=head1 API
28
29
=head2 Class Methods
30
31
=cut
32
33
=head3 _type
34
35
=cut
36
37
sub _type {
38
    return 'ImportOaipmhAuthority';
39
}
40
41
1;
(-)a/Koha/Import/Oaipmh/Biblio.pm (+40 lines)
Line 0 Link Here
1
package Koha::Import::Oaipmh::Biblio;
2
3
# This file is part of Koha.
4
#
5
# Koha is free software; you can redistribute it and/or modify it
6
# under the terms of the GNU General Public License as published by
7
# the Free Software Foundation; either version 3 of the License, or
8
# (at your option) any later version.
9
#
10
# Koha is distributed in the hope that it will be useful, but
11
# WITHOUT ANY WARRANTY; without even the implied warranty of
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
# GNU General Public License for more details.
14
#
15
# You should have received a copy of the GNU General Public License
16
17
use Modern::Perl;
18
19
use base qw(Koha::Object);
20
21
=head1 NAME
22
23
Koha::Import::Oaipmh::Biblio
24
25
This object represents an OAI-PMH record being imported as a bibliographic record
26
27
=head1 API
28
29
=head2 Methods
30
31
32
=head3 _type
33
34
=cut
35
36
sub _type {
37
    return 'ImportOaipmhBiblio';
38
}
39
40
1;
(-)a/Koha/Import/Oaipmh/Biblios.pm (+51 lines)
Line 0 Link Here
1
package Koha::Import::Oaipmh::Biblios;
2
3
# This file is part of Koha.
4
#
5
# Koha is free software; you can redistribute it and/or modify it
6
# under the terms of the GNU General Public License as published by
7
# the Free Software Foundation; either version 3 of the License, or
8
# (at your option) any later version.
9
#
10
# Koha is distributed in the hope that it will be useful, but
11
# WITHOUT ANY WARRANTY; without even the implied warranty of
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
# GNU General Public License for more details.
14
#
15
# You should have received a copy of the GNU General Public License
16
17
use Modern::Perl;
18
19
use Koha::Import::Oaipmh::Biblio;
20
21
use base qw(Koha::Objects);
22
23
=head1 NAME
24
25
Koha::Import::Oaipmh::Biblios
26
27
This object represents a collection of OAI-PMH records being imported as bibliographic records
28
29
=head1 API
30
31
=head2 Methods
32
33
34
=head3 type
35
36
=cut
37
38
sub _type {
39
    return 'ImportOaipmhBiblio';
40
}
41
42
=head3 object_class
43
44
Koha::Object class
45
46
=cut
47
48
sub object_class {
49
    return 'Koha::Import::Oaipmh::Biblio';
50
}
51
1;
(-)a/Koha/OAI/Client/Harvester.pm (+372 lines)
Line 0 Link Here
1
# Copyright BibLibre 2023
2
#
3
# This file is part of Koha.
4
#
5
# Koha is free software; you can redistribute it and/or modify it
6
# under the terms of the GNU General Public License as published by
7
# the Free Software Foundation; either version 3 of the License, or
8
# (at your option) any later version.
9
#
10
# Koha is distributed in the hope that it will be useful, but
11
# WITHOUT ANY WARRANTY; without even the implied warranty of
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
# GNU General Public License for more details.
14
#
15
# You should have received a copy of the GNU General Public License
16
# along with Koha; if not, see <http://www.gnu.org/licenses>.
17
18
package Koha::OAI::Client::Harvester;
19
20
=head1 NAME
21
22
Koha::OAI::Client::Harvester - OAI Harvester
23
24
=head1 DESCRIPTION
25
26
Koha::OAI::Client::Harvester contains OAI-PMH harvester main functions
27
28
=cut
29
30
use utf8;
31
use open qw( :std :utf8 );
32
33
use C4::Biblio qw( AddBiblio GetFrameworkCode ModBiblio DelBiblio );
34
use C4::AuthoritiesMarc qw (AddAuthority GuessAuthTypeCode ModAuthority DelAuthority );
35
use C4::Log qw( cronlogaction );
36
use HTTP::OAI;
37
use HTTP::OAI::Metadata::OAI_DC;
38
use Koha::DateUtils qw( dt_from_string );
39
use Koha::OaiServers;
40
use Koha::Import::Oaipmh::Biblio;
41
use Koha::Import::Oaipmh::Biblios;
42
use Koha::Import::Oaipmh::Authority;
43
use Koha::Import::Oaipmh::Authorities;
44
use Koha::XSLT::Base;
45
use MARC::File::XML;
46
use MARC::Record;
47
use Modern::Perl;
48
use DateTime;
49
use DateTime::Format::Strptime;
50
use Try::Tiny qw( catch try );
51
use Date::Calc qw(
52
    Add_Delta_Days
53
    Today
54
);
55
56
my $strp = DateTime::Format::Strptime->new(
57
    pattern   => '%FT%TZ',
58
    time_zone => 'floating',
59
);
60
61
our $verbose = 0;
62
our $server;
63
our $force       = 0;
64
our $days        = 0;
65
our $xslt_engine = Koha::XSLT::Base->new;
66
67
=head2 new
68
69
$harvester = Koha::OAI::Client::Harvester->new( { server => $server, verbose => $verbose, days => $days, force => $force } );
70
71
New instance of Koha::OAI::Client::Harvester
72
73
C<$server> An OAI repository (Koha::OaiServer)
74
75
C<$verbose> print log messages to stdout when enabled
76
77
C<$days> number of days to harvest from (optional)
78
79
C<$force> force harvesting (ignore records datestamps)
80
81
=cut
82
83
sub new {
84
85
    my ( $self, $args ) = @_;
86
87
    $server  = $args->{'server'};
88
    $verbose = $args->{'verbose'};
89
    $days    = $args->{'days'};
90
    $force   = $args->{'force'};
91
    return $self;
92
}
93
94
=head2 init
95
96
Starts harvesting
97
98
=cut
99
100
sub init {
101
    my ( $self, $args ) = @_;
102
103
    printlog("Starting OAI Harvest from repository");
104
105
    my $h = HTTP::OAI::Harvester->new(
106
        repository => HTTP::OAI::Identify->new(
107
            baseURL => $server->endpoint,
108
            version => '2.0',
109
        )
110
    );
111
112
    printlog( "Using HTTP::OAI::Harvester version " . $HTTP::OAI::Harvester::VERSION );
113
114
    printlog( "Using endpoint "
115
            . $server->endpoint
116
            . ", set "
117
            . $server->oai_set
118
            . ", dataformat "
119
            . $server->dataformat
120
            . ", recordtype "
121
            . $server->recordtype );
122
123
    try {
124
        my $lmdf    = $h->ListMetadataFormats();
125
        my $lmdflog = "";
126
        for ( $lmdf->metadataFormat ) {
127
            $lmdflog .= $_->metadataPrefix . " ";
128
        }
129
        printlog("Available metadata formats for this repository: $lmdflog");
130
    } catch {
131
        printlog("ListMetadataFormats failed");
132
    };
133
134
    if ( $server->add_xslt ) {
135
        printlog( "Using XSLT file " . $server->add_xslt );
136
    } else {
137
        printlog("Not using an XSLT file");
138
    }
139
140
    my $start_date = '';
141
    my $today_date = '';
142
143
    if ($days) {
144
145
        # Change this to yyyy-mm-dd
146
        my $dt_today = dt_from_string();
147
        my ( $nowyear, $nowmonth, $nowday ) = Today();
148
        my @date     = Add_Delta_Days( $nowyear, $nowmonth, $nowday, -$days );
149
        my $dt_start = DateTime->new( year => $date[0], month => $date[1], day => $date[2] );
150
        $start_date = $dt_start->ymd();
151
        printlog("Harvesting from $start_date");
152
    }
153
154
    printlog("Asking for records");
155
    my $response = $h->ListRecords(
156
        metadataPrefix => $server->dataformat,
157
        set            => $server->oai_set,
158
        from           => $start_date,
159
    );
160
    if ( $response->is_error ) {
161
        printlog( "Error requesting ListRecords: " . $response->code . " " . $response->message );
162
        exit;
163
    }
164
    printlog( "Request URL: " . $response->requestURL );
165
166
    my %stats;
167
    my @statuses = qw(added updated deleted in_error skipped total);
168
    foreach my $status (@statuses) {
169
        $stats{$status} = 0;
170
    }
171
172
    printlog("Starting processing results");
173
    while ( my $oai_record = $response->next ) {
174
        $stats{'total'}++;
175
        my $status = $self->processRecord($oai_record);
176
        $stats{$status}++;
177
    }
178
179
    my $results = '';
180
    foreach my $status (@statuses) {
181
        $results .= $stats{$status} . " $status\n";
182
    }
183
    printlog( "Harvest results:\n" . $results );
184
185
    if ( C4::Context->preference("OAI-PMH:HarvestEmailReport") ) {
186
187
        my $letter = C4::Letters::GetPreparedLetter(
188
            (
189
                module      => 'catalogue',
190
                letter_code => 'OAI_HARVEST_REPORT',
191
                substitute  => {
192
                    servername => $server->servername,
193
                    endpoint   => $server->endpoint,
194
                    set        => $server->oai_set,
195
                    dataformat => $server->dataformat,
196
                    recordtype => $server->recordtype,
197
                    added      => $stats{'added'},
198
                    updated    => $stats{'updated'},
199
                    deleted    => $stats{'deleted'},
200
                    skipped    => $stats{'skipped'},
201
                    in_error   => $stats{'in_error'},
202
                    total      => $stats{'total'}
203
                },
204
            )
205
        ) or printlog("OAI_HARVEST_REPORT letter not found");
206
207
        my $success = C4::Letters::EnqueueLetter(
208
            {
209
                letter     => $letter,
210
                to_address => C4::Context->preference("OAI-PMH:HarvestEmailReport"), message_transport_type => 'email'
211
            }
212
        );
213
        if ($success) {
214
            printlog("Email report enqueued");
215
        } else {
216
            printlog("Unable to enqueue report email");
217
        }
218
    }
219
220
    printlog("Ending OAI Harvest from repository");
221
}
222
223
=head2 processRecord
224
225
This method processes an incoming OAI record
226
227
=cut
228
229
sub processRecord {
230
    my $self       = shift;
231
    my $oai_record = shift;
232
    my $status     = '';
233
    unless ( $oai_record->identifier ) {
234
        printlog("No identifier found");
235
        $status = 'skipped';
236
        return $status;
237
    }
238
239
    my $to_delete;
240
    my $outputUnimarc;
241
    my $marcrecord;
242
    if ( $oai_record->status && $oai_record->status eq "deleted" ) {
243
        $to_delete = 1;
244
    } else {
245
        if ( $server->add_xslt ) {
246
            $outputUnimarc = $xslt_engine->transform( $oai_record->metadata->dom, $server->add_xslt );
247
            my $err = $xslt_engine->err;
248
            if ($err) {
249
                printlog("XSLT::Base error: $err");
250
                $status = 'in_error';
251
                return $status;
252
            }
253
        } else {
254
            $outputUnimarc = $oai_record->metadata->dom;
255
        }
256
        $marcrecord = MARC::Record->new_from_xml( $outputUnimarc, 'UTF-8' );
257
    }
258
    my $imported_record;
259
    if ( $server->recordtype eq "biblio" ) {
260
        $imported_record = Koha::Import::Oaipmh::Biblios->find(
261
            {
262
                repository => $server->endpoint,
263
                identifier => $oai_record->identifier,
264
                recordtype => $server->recordtype,
265
            }
266
        );
267
    } else {
268
        $imported_record = Koha::Import::Oaipmh::Authorities->find(
269
            {
270
                repository => $server->endpoint,
271
                identifier => $oai_record->identifier,
272
                recordtype => $server->recordtype,
273
            }
274
        );
275
    }
276
277
    if ($imported_record) {
278
        if ($to_delete) {
279
            if ( $server->recordtype eq "biblio" ) {
280
                my $biblionumber = $imported_record->biblionumber;
281
                my $error        = DelBiblio($biblionumber);
282
                if ($error) {
283
                    printlog(
284
                        "Record " . $oai_record->identifier . " not deleted, biblionumber: $biblionumber ($error)" );
285
                    $status = 'in_error';
286
                } else {
287
                    $imported_record->delete;
288
                    printlog( "Record " . $oai_record->identifier . " deleted, biblionumber: $biblionumber" );
289
                    $status = 'deleted';
290
                }
291
            } else {
292
                my $authid = $imported_record->authid;
293
                DelAuthority( { authid => $authid } );
294
                $imported_record->delete;
295
                printlog( "Record " . $oai_record->identifier . " deleted, authid: $authid" );
296
                $status = 'deleted';
297
            }
298
        } else {
299
            my $existing_dt = $strp->parse_datetime( $imported_record->datestamp );
300
            my $incoming_dt = $strp->parse_datetime( $oai_record->datestamp );
301
302
            if ( $force || !$incoming_dt || !$existing_dt || $incoming_dt > $existing_dt ) {
303
                if ( $server->recordtype eq "biblio" ) {
304
                    my $biblionumber = $imported_record->biblionumber;
305
                    my $result       = ModBiblio( $marcrecord, $biblionumber, GetFrameworkCode($biblionumber) );
306
                    printlog( "Record " . $oai_record->identifier . " updated, biblionumber: $biblionumber" );
307
                } else {
308
                    my $authid = $imported_record->authid;
309
                    my $result = ModAuthority( $authid, $marcrecord, GuessAuthTypeCode($marcrecord) );
310
                    printlog( "Record " . $oai_record->identifier . " updated, authid: $authid" );
311
                }
312
                $imported_record->update(
313
                    {
314
                        datestamp => $imported_record->datestamp,
315
                    }
316
                );
317
                $status = 'updated';
318
            } else {
319
                printlog( "Record "
320
                        . $oai_record->identifier
321
                        . " skipped (incoming record was not newer than existing record)" );
322
                $status = 'skipped';
323
            }
324
        }
325
    } elsif ( !$to_delete ) {
326
        if ( $server->recordtype eq "biblio" ) {
327
            my ( $biblionumber, $biblioitemnumber ) = AddBiblio($marcrecord);
328
            printlog( $oai_record->identifier . " added, biblionumber: $biblionumber" );
329
            Koha::Import::Oaipmh::Biblio->new(
330
                {
331
                    repository   => $server->endpoint,
332
                    identifier   => $oai_record->identifier,
333
                    biblionumber => $biblionumber,
334
                    recordtype   => $server->recordtype,
335
                    datestamp    => $oai_record->datestamp,
336
                }
337
            )->store;
338
        } else {
339
            my $authid = AddAuthority( $marcrecord, "", GuessAuthTypeCode($marcrecord) );
340
            printlog( $oai_record->identifier . " added, authid: $authid" );
341
            Koha::Import::Oaipmh::Authority->new(
342
                {
343
                    repository => $server->endpoint,
344
                    identifier => $oai_record->identifier,
345
                    authid     => $authid,
346
                    recordtype => $server->recordtype,
347
                    datestamp  => $oai_record->datestamp,
348
                }
349
            )->store;
350
        }
351
        $status = 'added';
352
    } else {
353
        printlog( "Record " . $oai_record->identifier . " skipped (record not present or already deleted)" );
354
        $status = 'skipped';
355
    }
356
    return $status;
357
}
358
359
=head2 printlog
360
361
This method adds a cronlog and prints to stdout if verbose is enabled
362
363
=cut
364
365
sub printlog {
366
    my $message = shift;
367
    $message = $server->servername . ": " . $message;
368
    print $message . "\n" if ($verbose);
369
    cronlogaction( { info => $message } );
370
}
371
372
1;
(-)a/Koha/OaiServer.pm (+45 lines)
Line 0 Link Here
1
package Koha::OaiServer;
2
3
# This file is part of Koha.
4
#
5
# Koha is free software; you can redistribute it and/or modify it
6
# under the terms of the GNU General Public License as published by
7
# the Free Software Foundation; either version 3 of the License, or
8
# (at your option) any later version.
9
#
10
# Koha is distributed in the hope that it will be useful, but
11
# WITHOUT ANY WARRANTY; without even the implied warranty of
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
# GNU General Public License for more details.
14
#
15
# You should have received a copy of the GNU General Public License
16
# along with Koha; if not, see <http://www.gnu.org/licenses>.
17
18
use Modern::Perl;
19
20
21
use Koha::Database;
22
23
use base qw(Koha::Object);
24
25
=head1 NAME
26
27
Koha::OaiServer - Koha OaiServer Object class
28
29
=head1 API
30
31
=head2 Class Methods
32
33
=cut
34
35
=head3 _type
36
37
Return type of Object relating to Schema ResultSet
38
39
=cut
40
41
sub _type {
42
    return 'Oaiserver';
43
}
44
45
1;
(-)a/Koha/OaiServers.pm (+57 lines)
Line 0 Link Here
1
package Koha::OaiServers;
2
3
# This file is part of Koha.
4
#
5
# Koha is free software; you can redistribute it and/or modify it
6
# under the terms of the GNU General Public License as published by
7
# the Free Software Foundation; either version 3 of the License, or
8
# (at your option) any later version.
9
#
10
# Koha is distributed in the hope that it will be useful, but
11
# WITHOUT ANY WARRANTY; without even the implied warranty of
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
# GNU General Public License for more details.
14
#
15
# You should have received a copy of the GNU General Public License
16
# along with Koha; if not, see <http://www.gnu.org/licenses>.
17
18
use Modern::Perl;
19
20
21
use Koha::Database;
22
23
use Koha::OaiServer;
24
25
use base qw(Koha::Objects);
26
27
=head1 NAME
28
29
Koha::OaiServers - Koha OaiServer Object set class
30
31
=head1 API
32
33
=head2 Class Methods
34
35
=cut
36
37
=head3 _type
38
39
Return type of object, relating to Schema ResultSet
40
41
=cut
42
43
sub _type {
44
    return 'Oaiserver';
45
}
46
47
=head3 object_class
48
49
Return object class
50
51
=cut
52
53
sub object_class {
54
    return 'Koha::OaiServer';
55
}
56
57
1;
(-)a/admin/oaiservers.pl (+122 lines)
Line 0 Link Here
1
#!/usr/bin/perl
2
3
# This file is part of Koha.
4
#
5
# Koha is free software; you can redistribute it and/or modify it
6
# under the terms of the GNU General Public License as published by
7
# the Free Software Foundation; either version 3 of the License, or
8
# (at your option) any later version.
9
#
10
# Koha is distributed in the hope that it will be useful, but
11
# WITHOUT ANY WARRANTY; without even the implied warranty of
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
# GNU General Public License for more details.
14
#
15
# You should have received a copy of the GNU General Public License
16
# along with Koha; if not, see <http://www.gnu.org/licenses>.
17
18
# This script is used to maintain the OAI servers table.
19
# Parameter $op is operation: list, new, edit, add_validated, delete_confirmed.
20
# add_validated saves a validated record and goes to list view.
21
# delete_confirmed deletes a record and goes to list view.
22
23
use Modern::Perl;
24
use CGI qw ( -utf8 );
25
use C4::Context;
26
use C4::Auth qw( get_template_and_user );
27
use C4::Output qw( output_html_with_http_headers );
28
use Koha::Database;
29
use Koha::OaiServers;
30
31
# Initialize CGI, template, database
32
33
my $input       = CGI->new;
34
my $op          = $input->param('op') || 'list';
35
my $id          = $input->param('id') || 0;
36
my $type        = $input->param('type') || '';
37
my $searchfield = '';
38
39
my ( $template, $loggedinuser, $cookie ) = get_template_and_user(
40
    {
41
        template_name => "admin/oaiservers.tt",
42
        query         => $input,
43
        type          => "intranet",
44
        flagsrequired => { parameters => 'manage_search_targets' },
45
    }
46
);
47
my $script_name = "/cgi-bin/koha/admin/oaiservers.pl";
48
my $path        = C4::Context->config('intrahtdocs') . "/prog/";
49
50
$template->param( script_name => $script_name, xslt_path => $path );
51
52
my $schema = Koha::Database->new()->schema();
53
54
# Main code
55
# First process a confirmed delete, or save a validated record
56
57
if ( $op eq 'delete_confirmed' && $id ) {
58
    my $server = Koha::OaiServers->find($id);
59
    if ($server) {
60
        $server->delete;
61
        $template->param( msg_deleted => 1, msg_add => $server->servername );
62
    } else {
63
        $template->param( msg_notfound => 1, msg_add => $id );
64
    }
65
    $id = 0;
66
} elsif ( $op eq 'add_validated' ) {
67
    my @fields = qw/endpoint oai_set dataformat
68
        recordtype servername
69
        add_xslt/;
70
    my $formdata = _form_data_hashref( $input, \@fields );
71
    if ($id) {
72
        my $server = Koha::OaiServers->find($id);
73
        if ($server) {
74
            $server->set($formdata)->store;
75
            $template->param( msg_updated => 1, msg_add => $formdata->{servername} );
76
        } else {
77
            $template->param( msg_notfound => 1, msg_add => $id );
78
        }
79
        $id = 0;
80
    } else {
81
        Koha::OaiServer->new($formdata)->store;
82
        $template->param( msg_added => 1, msg_add => $formdata->{servername} );
83
    }
84
} else {
85
86
    #use searchfield only in remaining operations
87
    $searchfield = $input->param('searchfield') || '';
88
}
89
90
# Now list multiple records, or edit one record
91
92
my $data = [];
93
if ( $op eq 'add' || $op eq 'edit' ) {
94
    $data = ServerSearch( $schema, $id, $searchfield ) if $searchfield || $id;
95
    delete $data->[0]->{id} if @$data && $op eq 'add';    #cloning record
96
    $template->param(
97
        add_form => 1, server => @$data ? $data->[0] : undef,
98
        op => $op, type => $op eq 'add' ? lc $type : ''
99
    );
100
} else {
101
    $data = ServerSearch( $schema, $id, $searchfield );
102
    $template->param(
103
        loop => \@$data, searchfield => $searchfield, id => $id,
104
        op   => 'list'
105
    );
106
}
107
output_html_with_http_headers $input, $cookie, $template->output;
108
109
# End of main code
110
111
sub ServerSearch {    #find server(s) by id or name
112
    my ( $schema, $id, $searchstring ) = @_;
113
114
    return Koha::OaiServers->search(
115
        $id ? { id => $id } : { servername => { like => $searchstring . '%' } },
116
    )->unblessed;
117
}
118
119
sub _form_data_hashref {
120
    my ( $input, $fieldref ) = @_;
121
    return { map { ( $_ => scalar $input->param($_) // '' ) } @$fieldref };
122
}
(-)a/installer/data/mysql/atomicupdate/Bug-35659.pl (+85 lines)
Line 0 Link Here
1
use Modern::Perl;
2
3
return {
4
    bug_number  => "35659",
5
    description => "OAI-PMH Harvester",
6
    up          => sub {
7
        my ($args) = @_;
8
        my ( $dbh, $out ) = @$args{qw(dbh out)};
9
        $dbh->do(
10
            q{
11
            CREATE TABLE IF NOT EXISTS `oaiservers` (
12
              `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'unique identifier assigned by Koha',
13
              `endpoint` varchar(255) NOT NULL COMMENT 'OAI endpoint (host + port + path)',
14
              `oai_set` varchar(255) DEFAULT NULL COMMENT 'OAI set to harvest',
15
              `servername` longtext NOT NULL COMMENT 'name given to the target by the library',
16
              `dataformat` enum('oai_dc','marc-xml', 'marcxml') NOT NULL DEFAULT 'oai_dc' COMMENT 'data format',
17
              `recordtype` enum('authority','biblio') NOT NULL DEFAULT 'biblio' COMMENT 'server contains bibliographic or authority records',
18
              `add_xslt` longtext DEFAULT NULL COMMENT 'zero or more paths to XSLT files to be processed on the search results',
19
              PRIMARY KEY (`id`)
20
            ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
21
        }
22
        );
23
        say $out "Added new table 'oaiservers'";
24
25
        $dbh->do(
26
            q{
27
            CREATE TABLE IF NOT EXISTS `import_oaipmh_biblios` (
28
              `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'unique identifier assigned by Koha',
29
              `biblionumber` int(11) NOT NULL COMMENT 'unique identifier assigned to each koha record',
30
              `identifier` varchar(255) NOT NULL COMMENT 'OAI record identifier',
31
              `repository` varchar(255) NOT NULL COMMENT 'OAI repository',
32
              `recordtype` enum('authority','biblio') NOT NULL DEFAULT 'biblio' COMMENT 'is the record bibliographic or authority',
33
              `datestamp` varchar(255) DEFAULT NULL COMMENT 'OAI set to harvest',
34
              `last_modified` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
35
              PRIMARY KEY (id),
36
              KEY biblionumber (biblionumber),
37
              CONSTRAINT FK_import_oaipmh_biblios_1 FOREIGN KEY (biblionumber) REFERENCES biblio (biblionumber) ON DELETE CASCADE ON UPDATE NO ACTION
38
            ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
39
        }
40
        );
41
42
        $dbh->do(
43
            q{
44
            CREATE TABLE IF NOT EXISTS `import_oaipmh_authorities` (
45
              `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'unique identifier assigned by Koha',
46
              `authid` bigint(20) unsigned NOT NULL COMMENT 'unique identifier assigned to each koha record',
47
              `identifier` varchar(255) NOT NULL COMMENT 'OAI record identifier',
48
              `repository` varchar(255) NOT NULL COMMENT 'OAI repository',
49
              `recordtype` enum('authority','biblio') NOT NULL DEFAULT 'biblio' COMMENT 'is the record bibliographic or authority',
50
              `datestamp` varchar(255) DEFAULT NULL COMMENT 'OAI set to harvest',
51
              `last_modified` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
52
              PRIMARY KEY (id),
53
              KEY authid (authid),
54
              CONSTRAINT FK_import_oaipmh_authorities_1 FOREIGN KEY (authid) REFERENCES auth_header (authid) ON DELETE CASCADE ON UPDATE NO ACTION
55
            ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
56
        }
57
        );
58
        say $out "Added new tables 'import_oaipmh_biblios' and import_oaipmh_authorities";
59
60
        $dbh->do(
61
            q{
62
            UPDATE `permissions` SET description='Manage Z39.50 and SRU servers, OAI repositories configuration' WHERE code='manage_search_targets';
63
        }
64
        );
65
        say $out "Updated manage_search_targets permission description";
66
67
        $dbh->do(
68
            q{
69
            INSERT IGNORE INTO systempreferences (variable, value, options, explanation, type) VALUES
70
            ('OAI-PMH:HarvestEmailReport','','','If set, a report email will be sent to this address after an OAI harvest','Free');
71
        }
72
        );
73
        say $out "Added OAI-PMH:HarvestEmailReport system preference";
74
75
        $dbh->do(
76
            q{
77
            INSERT IGNORE INTO letter
78
            (module, code, branchcode, name, is_html, title, content, message_transport_type, lang)
79
            VALUES ('catalogue','OAI_HARVEST_REPORT','','OAI Harvest Report',0,'OAI Harvest report for [% servername %]','OAI Harvest report for [% servername %]:\n\nEndpoint: [% endpoint %]\nSet: [% set %]\nData format: [% dataformat %]\nRecord type: [% recordtype %]\n\n[% added %] records added\n[% updated %] records updated\n[% deleted %] records deleted\n[% skipped %] records skipped\n[% in_error %] records in error\n[% total %] total','email','default');
80
        }
81
        );
82
        say $out "Added OAI_HARVEST_REPORT letter";
83
84
    },
85
};
(-)a/installer/data/mysql/en/mandatory/sample_notices.yml (+23 lines)
Lines 2471-2473 tables: Link Here
2471
            - "Your hold on [% hold.biblio.title %] ([% hold.biblio.id %]) has been confirmed."
2471
            - "Your hold on [% hold.biblio.title %] ([% hold.biblio.id %]) has been confirmed."
2472
            - "You will be notified by the library when your item is available for pickup."
2472
            - "You will be notified by the library when your item is available for pickup."
2473
            - "Thank you!"
2473
            - "Thank you!"
2474
2475
        - module: catalogue
2476
          code: OAI_HARVEST_REPORT
2477
          branchcode: ""
2478
          name: "OAI Harvest Report"
2479
          is_html: 0
2480
          title: "OAI Harvest report for [% servername %]"
2481
          message_transport_type: email
2482
          lang: default
2483
          content:
2484
            - "OAI Harvest report for [% servername %]:"
2485
            - ""
2486
            - "Endpoint: [% endpoint %]"
2487
            - "Set: [% set %]"
2488
            - "Data format: [% dataformat %]"
2489
            - "Record type: [% recordtype %]"
2490
            - ""
2491
            - "[% added %] records added"
2492
            - "[% updated %] records updated"
2493
            - "[% deleted %] records deleted"
2494
            - "[% skipped %] records skipped"
2495
            - "[% in_error %] records in error"
2496
            - "[% total %] total"
(-)a/installer/data/mysql/kohastructure.sql (+58 lines)
Lines 4859-4864 CREATE TABLE `oai_sets_mappings` ( Link Here
4859
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
4859
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
4860
/*!40101 SET character_set_client = @saved_cs_client */;
4860
/*!40101 SET character_set_client = @saved_cs_client */;
4861
4861
4862
--
4863
-- Table structure for table `oaiservers`
4864
--
4865
4866
DROP TABLE IF EXISTS `oaiservers`;
4867
/*!40101 SET @saved_cs_client     = @@character_set_client */;
4868
/*!40101 SET character_set_client = utf8 */;
4869
CREATE TABLE `oaiservers` (
4870
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'unique identifier assigned by Koha',
4871
  `endpoint` varchar(255) NOT NULL COMMENT 'OAI endpoint (host + port + path)',
4872
  `oai_set` varchar(255) DEFAULT NULL COMMENT 'OAI set to harvest',
4873
  `servername` longtext NOT NULL COMMENT 'name given to the target by the library',
4874
  `dataformat` enum('oai_dc','marc-xml', 'marcxml') NOT NULL DEFAULT 'oai_dc' COMMENT 'data format',
4875
  `recordtype` enum('authority','biblio') NOT NULL DEFAULT 'biblio' COMMENT 'server contains bibliographic or authority records',
4876
  `add_xslt` longtext DEFAULT NULL COMMENT 'zero or more paths to XSLT files to be processed on the search results',
4877
  PRIMARY KEY (`id`)
4878
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
4879
4880
--
4881
-- Table structure for table `import_oaipmh_biblios`
4882
--
4883
4884
DROP TABLE IF EXISTS `import_oaipmh_biblios`;
4885
/*!40101 SET @saved_cs_client     = @@character_set_client */;
4886
/*!40101 SET character_set_client = utf8 */;
4887
CREATE TABLE `import_oaipmh_biblios` (
4888
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'unique identifier assigned by Koha',
4889
  `biblionumber` int(11) NOT NULL COMMENT 'unique identifier assigned to each koha record',
4890
  `identifier` varchar(255) NOT NULL COMMENT 'OAI record identifier',
4891
  `repository` varchar(255) NOT NULL COMMENT 'OAI repository',
4892
  `recordtype` enum('authority','biblio') NOT NULL DEFAULT 'biblio' COMMENT 'is the record bibliographic or authority',
4893
  `datestamp` varchar(255) DEFAULT NULL COMMENT 'OAI set to harvest',
4894
  `last_modified` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
4895
  PRIMARY KEY (id),
4896
  KEY biblionumber (biblionumber),
4897
  CONSTRAINT FK_import_oaipmh_biblios_1 FOREIGN KEY (biblionumber) REFERENCES biblio (biblionumber) ON DELETE CASCADE ON UPDATE NO ACTION
4898
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
4899
4900
--
4901
-- Table structure for table `import_oaipmh_authorities`
4902
--
4903
4904
DROP TABLE IF EXISTS `import_oaipmh_authorities`;
4905
/*!40101 SET @saved_cs_client     = @@character_set_client */;
4906
/*!40101 SET character_set_client = utf8 */;
4907
CREATE TABLE `import_oaipmh_authorities` (
4908
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'unique identifier assigned by Koha',
4909
  `authid` bigint(20) unsigned NOT NULL COMMENT 'unique identifier assigned to each koha record',
4910
  `identifier` varchar(255) NOT NULL COMMENT 'OAI record identifier',
4911
  `repository` varchar(255) NOT NULL COMMENT 'OAI repository',
4912
  `recordtype` enum('authority','biblio') NOT NULL DEFAULT 'biblio' COMMENT 'is the record bibliographic or authority',
4913
  `datestamp` varchar(255) DEFAULT NULL COMMENT 'OAI set to harvest',
4914
  `last_modified` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
4915
  PRIMARY KEY (id),
4916
  KEY authid (authid),
4917
  CONSTRAINT FK_import_oaipmh_authorities_1 FOREIGN KEY (authid) REFERENCES auth_header (authid) ON DELETE CASCADE ON UPDATE NO ACTION
4918
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
4919
4862
--
4920
--
4863
-- Table structure for table `oauth_access_tokens`
4921
-- Table structure for table `oauth_access_tokens`
4864
--
4922
--
(-)a/installer/data/mysql/mandatory/sysprefs.sql (+1 lines)
Lines 445-450 INSERT INTO systempreferences ( `variable`, `value`, `options`, `explanation`, ` Link Here
445
('OAI-PMH:AutoUpdateSetsEmbedItemData', '0', '', 'Embed item information when automatically updating OAI sets. Requires OAI-PMH:AutoUpdateSets syspref to be enabled', 'YesNo'),
445
('OAI-PMH:AutoUpdateSetsEmbedItemData', '0', '', 'Embed item information when automatically updating OAI sets. Requires OAI-PMH:AutoUpdateSets syspref to be enabled', 'YesNo'),
446
('OAI-PMH:ConfFile','',NULL,'If empty, Koha OAI Server operates in normal mode, otherwise it operates in extended mode.','File'),
446
('OAI-PMH:ConfFile','',NULL,'If empty, Koha OAI Server operates in normal mode, otherwise it operates in extended mode.','File'),
447
('OAI-PMH:DeletedRecord','persistent','Koha\'s deletedbiblio table will never be deleted (persistent), might be deleted (transient), or will never have any data in it (no)','transient|persistent|no','Choice'),
447
('OAI-PMH:DeletedRecord','persistent','Koha\'s deletedbiblio table will never be deleted (persistent), might be deleted (transient), or will never have any data in it (no)','transient|persistent|no','Choice'),
448
('OAI-PMH:HarvestEmailReport','','','If set, a report email will be sent to this address after an OAI harvest','Free'),
448
('OAI-PMH:MaxCount','50',NULL,'OAI-PMH maximum number of records by answer to ListRecords and ListIdentifiers queries','Integer'),
449
('OAI-PMH:MaxCount','50',NULL,'OAI-PMH maximum number of records by answer to ListRecords and ListIdentifiers queries','Integer'),
449
('OnSiteCheckoutAutoCheck','0','','Enable/Do not enable onsite checkout by default if last checkout was an onsite checkout','YesNo'),
450
('OnSiteCheckoutAutoCheck','0','','Enable/Do not enable onsite checkout by default if last checkout was an onsite checkout','YesNo'),
450
('OnSiteCheckouts','0','','Enable/Disable the on-site checkouts feature','YesNo'),
451
('OnSiteCheckouts','0','','Enable/Disable the on-site checkouts feature','YesNo'),
(-)a/installer/data/mysql/mandatory/userpermissions.sql (-1 / +1 lines)
Lines 29-35 INSERT INTO permissions (module_bit, code, description) VALUES Link Here
29
   ( 3, 'manage_item_search_fields', 'Manage item search fields'),
29
   ( 3, 'manage_item_search_fields', 'Manage item search fields'),
30
   ( 3, 'manage_search_engine_config', 'Manage search engine configuration'),
30
   ( 3, 'manage_search_engine_config', 'Manage search engine configuration'),
31
   ( 3, 'manage_marc_overlay_rules', 'Manage MARC overlay rules configuration'),
31
   ( 3, 'manage_marc_overlay_rules', 'Manage MARC overlay rules configuration'),
32
   ( 3, 'manage_search_targets', 'Manage Z39.50 and SRU server configuration'),
32
   ( 3, 'manage_search_targets', 'Manage Z39.50 and SRU servers, OAI repositories configuration'),
33
   ( 3, 'manage_didyoumean', 'Manage Did you mean? configuration'),
33
   ( 3, 'manage_didyoumean', 'Manage Did you mean? configuration'),
34
   ( 3, 'manage_column_config', 'Manage column configuration'),
34
   ( 3, 'manage_column_config', 'Manage column configuration'),
35
   ( 3, 'manage_sms_providers', 'Manage SMS cellular providers'),
35
   ( 3, 'manage_sms_providers', 'Manage SMS cellular providers'),
(-)a/koha-tmpl/intranet-tmpl/prog/en/includes/admin-menu.inc (+1 lines)
Lines 152-157 Link Here
152
            [% END %]
152
            [% END %]
153
            [% IF ( CAN_user_parameters_manage_search_targets ) %]
153
            [% IF ( CAN_user_parameters_manage_search_targets ) %]
154
                <li><a href="/cgi-bin/koha/admin/z3950servers.pl">Z39.50/SRU servers</a></li>
154
                <li><a href="/cgi-bin/koha/admin/z3950servers.pl">Z39.50/SRU servers</a></li>
155
                <li><a href="/cgi-bin/koha/admin/oaiservers.pl">OAI repositories</a></li>
155
            [% END %]
156
            [% END %]
156
            [% IF ( CAN_user_parameters_manage_smtp_servers ) %]
157
            [% IF ( CAN_user_parameters_manage_smtp_servers ) %]
157
                <li><a href="/cgi-bin/koha/admin/smtp_servers.pl">SMTP servers</a></li>
158
                <li><a href="/cgi-bin/koha/admin/smtp_servers.pl">SMTP servers</a></li>
(-)a/koha-tmpl/intranet-tmpl/prog/en/includes/oai-admin-search.inc (+43 lines)
Line 0 Link Here
1
[% USE Koha %]
2
<!-- oai-admin-search.inc -->
3
<div id="header_search" role="tablist">
4
    <div class="tab-content">
5
        <div id="oai_search" role="tabpanel" class="tab-pane active">
6
            <form action="/cgi-bin/koha/admin/oaiservers.pl" method="post">
7
                <div class="form-title">
8
                    <label class="control-label">Search OAI repositories</label>
9
                </div>
10
11
                <div class="form-content">
12
                    <input class="head-searchbox form-control" type="text" name="searchfield" id="searchfield" value="[% searchfield | html %]" placeholder="OAI repository search" />
13
                </div>
14
15
                <button type="submit"><i class="fa fa-arrow-right"></i></button>
16
            </form>
17
        </div>
18
19
        [% IF ( CAN_user_circulate_circulate_remaining_permissions ) %]
20
            [% INCLUDE 'patron-search-box.inc' %]
21
        [% END %]
22
        [% IF ( CAN_user_catalogue ) %]
23
            [% INCLUDE 'catalogue-search-box.inc' %]
24
        [% END %]
25
26
    </div><!-- /.tab-content -->
27
    <ul class="nav nav-tabs" role="tablist">
28
        <li role="presentation" class="active">
29
            <a title="Search OAI repositories" href="#oai_search" aria-controls="z3950_search" role="tab" aria-expanded="true" data-toggle="tab" class="keep_text"><i class="fa fa-search"></i> <span class="tab-title">Search OAI repositories</span></a>
30
        </li>
31
        [% IF ( CAN_user_circulate_circulate_remaining_permissions ) %]
32
            <li role="presentation">
33
                <a title="Check out" href="#circ_search" aria-controls="circ_search" role="tab" data-toggle="tab" class="keep_text"><i class="fa fa-upload"></i> <span class="tab-title">Check out</span></a>
34
            </li>
35
        [% END %]
36
        [% IF ( CAN_user_catalogue ) %]
37
            <li role="presentation">
38
                <a title="Search catalog" href="#catalog_search" aria-controls="catalog_search" role="tab" data-toggle="tab" class="keep_text"><i class="fa fa-fw fa-search"></i> <span class="tab-title">Search catalog</span></a>
39
            </li>
40
        [% END %]
41
    </ul>
42
</div><!-- /#header_search -->
43
<!-- /oai-admin-search.inc -->
(-)a/koha-tmpl/intranet-tmpl/prog/en/includes/permissions.inc (-1 / +1 lines)
Lines 244-250 Link Here
244
        <span class="permissioncode">([% name | html %])</span>
244
        <span class="permissioncode">([% name | html %])</span>
245
    [%- CASE 'manage_search_targets' -%]
245
    [%- CASE 'manage_search_targets' -%]
246
        <span class="sub_permission manage_search_targets_subpermission">
246
        <span class="sub_permission manage_search_targets_subpermission">
247
            Manage Z39.50 and SRU server configuration
247
            Manage Z39.50 and SRU servers, OAI repositories configuration
248
        </span>
248
        </span>
249
        <span class="permissioncode">([% name | html %])</span>
249
        <span class="permissioncode">([% name | html %])</span>
250
    [%- CASE 'manage_didyoumean' -%]
250
    [%- CASE 'manage_didyoumean' -%]
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/admin-home.tt (+2 lines)
Lines 261-266 Link Here
261
                    [% IF ( CAN_user_parameters_manage_search_targets ) %]
261
                    [% IF ( CAN_user_parameters_manage_search_targets ) %]
262
                        <dt><a href="/cgi-bin/koha/admin/z3950servers.pl">Z39.50/SRU servers</a></dt>
262
                        <dt><a href="/cgi-bin/koha/admin/z3950servers.pl">Z39.50/SRU servers</a></dt>
263
                        <dd>Define which external servers to query for MARC data</dd>
263
                        <dd>Define which external servers to query for MARC data</dd>
264
                        <dt><a href="/cgi-bin/koha/admin/oaiservers.pl">OAI repositories</a></dt>
265
                        <dd>Define which OAI repositories to harvest data from</dd>
264
                    [% END %]
266
                    [% END %]
265
                    [% IF ( CAN_user_parameters_manage_smtp_servers ) %]
267
                    [% IF ( CAN_user_parameters_manage_smtp_servers ) %]
266
                        <dt><a href="/cgi-bin/koha/admin/smtp_servers.pl">SMTP servers</a></dt>
268
                        <dt><a href="/cgi-bin/koha/admin/smtp_servers.pl">SMTP servers</a></dt>
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/oaiservers.tt (+229 lines)
Line 0 Link Here
1
[% USE raw %]
2
[% USE Asset %]
3
[% SET footerjs = 1 %]
4
[% INCLUDE 'doc-head-open.inc' %]
5
6
<title>
7
    [% IF op == 'edit' %]
8
        Modify OAI repository [% server.servername | html %] &rsaquo; [% END %]
9
10
    [% IF op ==  'add' %]
11
        New OAI repository &rsaquo; [% END %]
12
13
    OAI repositories &rsaquo; Administration &rsaquo; Koha
14
</title>
15
[% INCLUDE 'doc-head-close.inc' %]
16
</head>
17
18
<body id="admin_oaiservers" class="admin">
19
[% WRAPPER 'header.inc' %]
20
    [% INCLUDE 'oai-admin-search.inc' %]
21
[% END %]
22
23
[% WRAPPER 'sub-header.inc' %]
24
    [% WRAPPER breadcrumbs %]
25
        [% WRAPPER breadcrumb_item %]
26
            <a href="/cgi-bin/koha/admin/admin-home.pl">Administration</a>
27
        [% END %]
28
29
        [% IF op == 'edit' || op == 'add' %]
30
            [% WRAPPER breadcrumb_item %]
31
                <a href="/cgi-bin/koha/admin/oaiservers.pl">OAI repositories</a>
32
           [% END %]
33
        [% END %]
34
35
        [% IF op == 'edit' %]
36
            [% WRAPPER breadcrumb_item bc_active= 1 %]
37
                <span>Modify OAI repository [% server.servername | html %]</span>
38
           [% END %]
39
40
        [% ELSIF op == 'add' %]
41
            [% WRAPPER breadcrumb_item bc_active= 1 %]
42
                <span>New OAI repository</span>
43
           [% END %]
44
45
        [% ELSE %]
46
            [% WRAPPER breadcrumb_item bc_active= 1 %]
47
                <span>OAI repositories</span>
48
           [% END %]
49
        [% END %]
50
    [% END #/ WRAPPER breadcrumbs %]
51
[% END #/ WRAPPER sub-header.inc %]
52
53
<div class="main container-fluid">
54
    <div class="row">
55
        <div class="col-sm-10 col-sm-push-2">
56
            <main>
57
58
[% IF msg_deleted %]
59
    <div class="dialog alert">OAI repository deleted ([% msg_add | html %])</div>
60
[% ELSIF msg_updated %]
61
    <div class="dialog message">OAI repository updated ([% msg_add | html %])</div>
62
[% ELSIF msg_added %]
63
    <div class="dialog message">OAI repository added ([% msg_add | html %])</div>
64
[% ELSIF msg_notfound %]
65
    <div class="dialog alert">Error: Server with ID [% msg_add | html %] not found</div>
66
[% END %]
67
68
[% IF ( add_form ) %]
69
    <form action="[% script_name | html %]" name="Aform" method="post" id="serverentry">
70
        <input type="hidden" name="op" value="add_validated" />
71
        [% IF op == 'edit' %]
72
            <h1>Modify OAI repository</h1>
73
            <input type="hidden" name="id" value="[% server.id | html %]" />
74
        [% ELSE %]
75
            <h1>New OAI repository</h1>
76
        [% END %]
77
        <fieldset class="rows">
78
        <ol>
79
        <li><label for="name" class="required">Repository name: </label>
80
            [% IF server.servername %]
81
                <input type="text" name="servername" id="servername" size="65" maxlength="100" value="[% server.servername | html %]" required="required" />
82
            [% ELSE %]
83
                <input type="text" name="servername" id="servername" class="focus" size="65" maxlength="100" value="[% server.servername | html %]" required="required" />
84
            [% END %]
85
             <span class="required">Required</span>
86
        </li>
87
88
        <li><label for="endpoint" class="required">Endpoint: </label> <input type="text" name="endpoint" id="endpoint" size="30" value="[% server.endpoint | html %]" required="required" /> <span class="required">Required</span>
89
        </li>
90
        <li><label for="oai_set">Set: </label> <input type="text" name="oai_set" id="oai_set" value="[% server.oai_set | html %]" />
91
        </li>
92
        <li>
93
            <label for="dataformat">Data format: </label>
94
        <select name="dataformat" id="dataformat">
95
            <option value="oai_dc">Dublin Core (oai_dc)</option>
96
            <option value="marc-xml">Marcxml (marc-xml)</option>
97
            <option value="marcxml">Marcxml (marcxml)</option>
98
        </select>
99
            <div class="hint">OAI can send records in various formats. Choose one.</div>
100
        </li>
101
102
        <li><label for="recordtype">Record type: </label>
103
        <select name="recordtype" id="recordtype">
104
            <option value="biblio">Bibliographic</option>
105
            <option value="authority">Authority</option>
106
        </select>
107
        </li>
108
        <li>
109
        <label for="add_xslt">XSLT file for transforming results: </label>
110
        <input type="text" name="add_xslt" id="add_xslt" size="100" placeholder="[% xslt_path | html %]" value="[% server.add_xslt | html %]"/>
111
        </li>
112
113
        </ol>
114
        </fieldset>
115
116
        <fieldset class="action"><input type="submit" class="btn btn-primary" value="Save" /> <a class="cancel" href="/cgi-bin/koha/admin/oaiservers.pl">Cancel</a></fieldset>
117
    </form>
118
[% END %]
119
120
[% IF op == 'list' %]
121
    <div id="toolbar" class="btn-toolbar">
122
        <a id="newserver" class="btn btn-default" href="/cgi-bin/koha/admin/oaiservers.pl?op=add"><i class="fa fa-plus"></i> New OAI repository</a>
123
    </div>
124
    <h1>OAI repositories administration</h1>
125
    [% IF id %]
126
        You searched for record [% id | html %]
127
    [% ELSIF searchfield %]
128
        You searched for [% searchfield | html %]
129
    [% END %]
130
131
    <div class="page-section">
132
    <table id="serverst">
133
134
        <thead><tr><th>Id</th><th>Repository name</th><th>Endpoint</th><th>Set</th><th>Data format</th><th>Record type</th><th class="noExport">Actions</th>
135
        </tr></thead>
136
        <tbody>
137
        [% FOREACH loo IN loop %]
138
            <tr>
139
            <td>[% loo.id | html %]</td>
140
            <td><a href="/cgi-bin/koha/admin/oaiservers.pl?op=edit&amp;id=[% loo.id | uri %]">[% loo.servername | html %]</a></td><td>[% loo.endpoint | html %]</td><td>[% loo.oai_set | html %]</td>
141
            <td>[% loo.dataformat | html %]</td>
142
            <td>[% IF ( loo.recordtype == 'biblio' )  %]
143
                <span>Bibliographic</span>
144
                [% ELSIF ( loo.recordtype == 'authority' ) %]
145
                <span>Authority</span>
146
                [% END %]
147
            </td>
148
            <td>
149
                <div class="btn-group dropup">
150
                    <a class="btn btn-default btn-xs dropdown-toggle" id="reportactions[% savedreport.id | html %]" role="button" data-toggle="dropdown" href="#">
151
                        Actions <b class="caret"></b>
152
                    </a>
153
                    <ul class="dropdown-menu pull-right" role="menu" aria-labelledby="reportactions[% savedreport.id | html %]">
154
                        <li><a href="/cgi-bin/koha/admin/oaiservers.pl?op=edit&amp;id=[% loo.id | uri %]"><i class="fa-solid fa-pencil" aria-hidden="true"></i> Edit</a></li>
155
                        <li><a href="/cgi-bin/koha/admin/oaiservers.pl?op=add&amp;id=[% loo.id | uri %]"><i class="fa fa-copy"></i> Copy</a></li>
156
                        <li><a href="/cgi-bin/koha/admin/oaiservers.pl?op=delete_confirmed&amp;id=[% loo.id | uri %]" class="delete" data-servername="[% loo.servername | html %]"><i class="fa fa-trash-can"></i> Delete</a></li>
157
                    </ul>
158
                </div>
159
            </td>
160
            </tr>
161
        [% END %]
162
        </tbody>
163
    </table>
164
    </div> <!-- /.page-section -->
165
[% END %]
166
167
            </main>
168
        </div> <!-- /.col-sm-10.col-sm-push-2 -->
169
170
        <div class="col-sm-2 col-sm-pull-10">
171
            <aside>
172
                [% INCLUDE 'admin-menu.inc' %]
173
            </aside>
174
        </div> <!-- /.col-sm-2.col-sm-pull-10 -->
175
     </div> <!-- /.row -->
176
177
[% MACRO jsinclude BLOCK %]
178
    [% Asset.js("js/admin-menu.js") | $raw %]
179
    [% IF op == 'list' %]
180
        [% INCLUDE 'datatables.inc' %]
181
    [% END %]
182
183
    <script>
184
        [% IF ( add_form ) %]
185
            $(document).ready(function(){
186
                // Update selects for dataformat and recordtype
187
                [% IF server %]
188
                    $("#dataformat").val('[% server.dataformat | html %]');
189
                    $("#recordtype").val('[% server.recordtype | html %]');
190
                [% END %]
191
                $( "#serverentry" ).validate({
192
                    rules: {
193
                        servername: { required: true },
194
                        host: { required: true },
195
                        port: {
196
                            number: true
197
                        },
198
                        rank: { number: true },
199
                    }
200
                });
201
                $("#serverentry").submit(function( event ) {
202
                    // enable recordtype to include field in post
203
                    $('#recordtype').prop('disabled',false);
204
                });
205
                $("#servername").on("blur",function(){
206
                    toUC(this);
207
                });
208
            });
209
        [% ELSE %]
210
            $(document).ready(function() {
211
                $("#serverst").dataTable($.extend(true, {}, dataTablesDefaults, {
212
                    "aoColumnDefs": [
213
                        { "aTargets": [-1], "bSortable": false, "bSearchable": false },
214
                    ],
215
                    "sPaginationType": "full"
216
                }));
217
                $(".delete").on("click",function(e){
218
                    var servername = $(this).data("servername");
219
                    if( confirm( _("Are you sure you want to delete repository %s?").format(servername) ) ) {
220
                        return true;
221
                    } else {
222
                        e.preventDefault();
223
                    }
224
                });
225
            });
226
        [% END %]
227
    </script>
228
[% END %]
229
[% INCLUDE 'intranet-bottom.inc' %]
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/web_services.pref (+5 lines)
Lines 91-96 Web services: Link Here
91
                  transient: might be emptied or truncated at some point (transient)
91
                  transient: might be emptied or truncated at some point (transient)
92
                  no: will never have any data in it (no)
92
                  no: will never have any data in it (no)
93
            - "."
93
            - "."
94
        -
95
            - If set, a report email will be sent to this address after an OAI harvest
96
            - pref: "OAI-PMH:HarvestEmailReport"
97
              class: Text
98
94
    ILS-DI:
99
    ILS-DI:
95
        -
100
        -
96
            - pref: ILS-DI
101
            - pref: ILS-DI
(-)a/koha-tmpl/intranet-tmpl/prog/en/xslt/AuthOAIDCtoMARC21XML.xsl (+35 lines)
Line 0 Link Here
1
<?xml version="1.0" encoding="UTF-8"?>
2
<xsl:stylesheet version="1.0"
3
    xmlns:dc="http://purl.org/dc/elements/1.1/"
4
    xmlns:dcterms="http://purl.org/dc/terms/1.1"
5
    xmlns:oai_dc="http://www.openarchives.org/OAI/2.0/oai_dc/"
6
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
7
    xsi:schemaLocation="http://www.openarchives.org/OAI/2.0/oai_dc/
8
        http://www.openarchives.org/OAI/2.0/oai_dc.xsd"
9
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
10
    xmlns="http://www.loc.gov/MARC21/slim"  exclude-result-prefixes="dc dcterms oai_dc">
11
12
    <xsl:output method="xml" encoding="UTF-8" indent="yes"/>
13
14
    <xsl:template match="/">
15
        <collection xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.loc.gov/MARC21/slim http://www.loc.gov/standards/marcxml/schema/MARC21slim.xsd" >
16
            <xsl:apply-templates />
17
        </collection>
18
    </xsl:template>
19
20
    <xsl:template match="oai_dc:dc">
21
        <record>
22
23
            <xsl:variable name="FamilyName" select="dc:FamilyName"/>
24
            <xsl:variable name="GivenName" select="dc:GivenName"/>
25
26
            <datafield tag="100" ind1="0" ind2=" ">
27
                <subfield code="a">
28
                    <xsl:value-of select="concat($FamilyName,', ',$GivenName)"/>
29
                </subfield>
30
            </datafield>
31
32
        </record>
33
   </xsl:template>
34
35
</xsl:stylesheet>
(-)a/koha-tmpl/intranet-tmpl/prog/en/xslt/AuthOAIDCtoUNIMARCXML.xsl (+42 lines)
Line 0 Link Here
1
<?xml version="1.0" encoding="UTF-8"?>
2
<xsl:stylesheet version="1.0"
3
    xmlns:dc="http://purl.org/dc/elements/1.1/"
4
    xmlns:dcterms="http://purl.org/dc/terms/1.1"
5
    xmlns:oai_dc="http://www.openarchives.org/OAI/2.0/oai_dc/"
6
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
7
    xsi:schemaLocation="http://www.openarchives.org/OAI/2.0/oai_dc/
8
        http://www.openarchives.org/OAI/2.0/oai_dc.xsd"
9
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
10
    xmlns="http://www.loc.gov/MARC21/slim"  exclude-result-prefixes="dc dcterms oai_dc">
11
12
    <xsl:output method="xml" encoding="UTF-8" indent="yes"/>
13
14
    <xsl:template match="/">
15
        <collection xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.loc.gov/MARC21/slim http://www.loc.gov/standards/marcxml/schema/MARC21slim.xsd" >
16
            <xsl:apply-templates />
17
        </collection>
18
    </xsl:template>
19
20
    <xsl:template match="oai_dc:dc">
21
        <record>
22
23
            <xsl:for-each select="dc:FamilyName">
24
                <datafield tag="200" ind1="0" ind2=" ">
25
                    <subfield code="a">
26
                        <xsl:value-of select="."/>
27
                    </subfield>
28
                </datafield>
29
            </xsl:for-each>
30
31
            <xsl:for-each select="dc:GivenName">
32
                <datafield tag="200" ind1="0" ind2=" ">
33
                    <subfield code="b">
34
                        <xsl:value-of select="."/>
35
                    </subfield>
36
                </datafield>
37
            </xsl:for-each>
38
39
        </record>
40
   </xsl:template>
41
42
</xsl:stylesheet>
(-)a/koha-tmpl/intranet-tmpl/prog/en/xslt/OAIDCtoMARC21XML.xsl (+367 lines)
Line 0 Link Here
1
<?xml version="1.0" encoding="UTF-8"?>
2
<xsl:stylesheet version="1.0"
3
    xmlns:dc="http://purl.org/dc/elements/1.1/"
4
    xmlns:dcterms="http://purl.org/dc/terms/1.1"
5
    xmlns:oai_dc="http://www.openarchives.org/OAI/2.0/oai_dc/"
6
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
7
    xsi:schemaLocation="http://www.openarchives.org/OAI/2.0/oai_dc/
8
        http://www.openarchives.org/OAI/2.0/oai_dc.xsd"
9
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
10
    xmlns="http://www.loc.gov/MARC21/slim"  exclude-result-prefixes="dc dcterms oai_dc">
11
12
    <xsl:import href="MARC21slimUtils.xsl"/>
13
    <xsl:output method="xml" encoding="UTF-8" indent="yes"/>
14
15
16
    <xsl:template match="/">
17
        <collection xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.loc.gov/MARC21/slim http://www.loc.gov/standards/marcxml/schema/MARC21slim.xsd" >
18
            <xsl:apply-templates />
19
        </collection>
20
    </xsl:template>
21
22
    <xsl:template name="OAI-PMH">
23
             <xsl:for-each select = "ListRecords/record/metadata/oai_dc:dc">
24
                <xsl:apply-templates  />
25
             </xsl:for-each>
26
             <xsl:for-each select = "GetRecord/record/metadata/oai_dc:dc">
27
                <xsl:apply-templates  />
28
             </xsl:for-each>
29
    </xsl:template>
30
31
    <xsl:template match="text()" />
32
    <xsl:template match="oai_dc:dc">
33
        <record>
34
            <xsl:element name="leader">
35
                <xsl:variable name="type" select="dc:type"/>
36
                <xsl:variable name="leader06">
37
                    <xsl:choose>
38
                        <xsl:when test="$type='collection'">p</xsl:when>
39
                        <xsl:when test="$type='dataset'">m</xsl:when>
40
                        <xsl:when test="$type='event'">r</xsl:when>
41
                        <xsl:when test="$type='image'">k</xsl:when>
42
                        <xsl:when test="$type='interactive resource'">m</xsl:when>
43
                        <xsl:when test="$type='service'">m</xsl:when>
44
                        <xsl:when test="$type='software'">m</xsl:when>
45
                        <xsl:when test="$type='sound'">i</xsl:when>
46
                        <xsl:when test="$type='text'">a</xsl:when>
47
                        <xsl:otherwise>a</xsl:otherwise>
48
                    </xsl:choose>
49
                </xsl:variable>
50
                <xsl:variable name="leader07">
51
                    <xsl:choose>
52
                        <xsl:when test="$type='collection'">c</xsl:when>
53
                        <xsl:otherwise>m</xsl:otherwise>
54
                    </xsl:choose>
55
                </xsl:variable>
56
                <xsl:value-of select="concat('      ',$leader06,$leader07,'         3u     ')"/>
57
            </xsl:element>
58
59
            <datafield tag="042" ind1=" " ind2=" ">
60
                <subfield code="a">dc</subfield>
61
            </datafield>
62
63
64
65
            <xsl:for-each select="dc:creator">
66
                <xsl:choose>
67
                    <xsl:when test="(.!='') and (position()=1)">
68
                        <xsl:call-template name="persname_template">
69
                            <xsl:with-param name="string" select="." />
70
                            <xsl:with-param name="field" select="'100'" />
71
                            <xsl:with-param name="ind1" select = "'1'" />
72
                            <xsl:with-param name="ind2" select = "'0'" />
73
                            <xsl:with-param name="type" select="'author'" />
74
                        </xsl:call-template>
75
                    </xsl:when>
76
                    <xsl:otherwise>
77
                        <xsl:if test=".!=''">
78
                            <xsl:call-template name="persname_template">
79
                                <xsl:with-param name="string" select="." />
80
                                <xsl:with-param name="field" select="'700'" />
81
                                <xsl:with-param name="ind1" select = "'1'" />
82
                                <xsl:with-param name="ind2" select = "'0'" />
83
                                <xsl:with-param name="type" select="'author'" />
84
                            </xsl:call-template>
85
                        </xsl:if>
86
                    </xsl:otherwise>
87
                </xsl:choose>
88
            </xsl:for-each>
89
90
91
            <xsl:for-each select="dc:title[1]">
92
                <datafield tag="245" ind1="0" ind2="0">
93
                    <subfield code="a">
94
                        <xsl:value-of select="."/>
95
                    </subfield>
96
                </datafield>
97
            </xsl:for-each>
98
99
            <xsl:for-each select="dc:title[position()>1]">
100
                <xsl:if test=".!=''">
101
                    <datafield tag="246" ind1="3" ind2="3">
102
                        <subfield code="a">
103
                            <xsl:value-of select="."/>
104
                        </subfield>
105
                    </datafield>
106
                </xsl:if>
107
            </xsl:for-each>
108
109
            <xsl:choose>
110
                <xsl:when test="dc:publisher">
111
                    <xsl:if test="translate(dc:publisher/.,'.,:;','')!=''">
112
                        <datafield tag="260" ind1=" " ind2=" ">
113
                        <xsl:choose>
114
                            <xsl:when test="dc:date">
115
                                <subfield code="b"><xsl:value-of select="dc:publisher[1]"/>, </subfield>
116
                                <xsl:if test="translate(dc:date[1]/., '.,:;','')!=''">
117
                                    <subfield code="c"><xsl:value-of select="dc:date[1]" />.</subfield>
118
                                </xsl:if>
119
                            </xsl:when>
120
                            <xsl:otherwise>
121
                                <subfield code="b"><xsl:value-of select="dc:publisher[1]"/>.</subfield>
122
                            </xsl:otherwise>
123
                        </xsl:choose>
124
                        </datafield>
125
                    </xsl:if>
126
                </xsl:when>
127
                <xsl:otherwise>
128
                    <xsl:if test="translate(dc:date[1],'.,:;','')!=''">
129
                        <datafield tag="260" ind1=" " ind2=" ">
130
                            <subfield code="c"><xsl:value-of select="dc:date[1]" />.</subfield>
131
                        </datafield>
132
                    </xsl:if>
133
                </xsl:otherwise>
134
            </xsl:choose>
135
136
            <xsl:for-each select="dc:coverage">
137
                <xsl:choose>
138
                    <xsl:when test="translate(., '0123456789-.?','')=''">
139
                        <!--Likely;this is a date-->
140
                        <datafield tag="500" ind1=" " ind2=" ">
141
                            <subfield code="a"><xsl:value-of select="."/></subfield>
142
                        </datafield>
143
                    </xsl:when>
144
                    <xsl:otherwise>
145
                        <!--likely a geographic subject, we will print this later-->
146
                    </xsl:otherwise>
147
                </xsl:choose>
148
            </xsl:for-each>
149
150
            <xsl:for-each select="dc:identifier">
151
                <xsl:if test="position()!=last()">
152
                    <datafield tag="500" ind1=" " ind2=" ">
153
                        <subfield code="a"><xsl:value-of select="." /></subfield>
154
                    </datafield>
155
                </xsl:if>
156
            </xsl:for-each>
157
158
            <xsl:for-each select="dc:description">
159
                <datafield tag="520" ind1=" " ind2=" ">
160
                    <subfield code="a">
161
                        <xsl:value-of select="normalize-space(.)"/>
162
                    </subfield>
163
                </datafield>
164
            </xsl:for-each>
165
166
167
            <xsl:for-each select="dc:rights">
168
                <datafield tag="540" ind1=" " ind2=" ">
169
                    <subfield code="a">
170
                        <xsl:value-of select="."/>
171
                    </subfield>
172
                </datafield>
173
            </xsl:for-each>
174
175
176
            <xsl:for-each select="dc:language">
177
                <datafield tag="546" ind1=" " ind2=" ">
178
                    <subfield code="a">
179
                        <xsl:value-of select="."/>
180
                    </subfield>
181
                </datafield>
182
            </xsl:for-each>
183
184
185
186
            <xsl:for-each select="dc:subject">
187
                <datafield tag="690" ind1=" " ind2=" ">
188
                    <subfield code="a">
189
                        <xsl:value-of select="."/>
190
                    </subfield>
191
                </datafield>
192
            </xsl:for-each>
193
194
            <xsl:for-each select="dc:coverage">
195
                <xsl:choose>
196
                    <xsl:when test="translate(., '0123456789-.?','')=''">
197
                        <!--Likely; this is a date-->
198
                    </xsl:when>
199
                    <xsl:otherwise>
200
                        <!--likely a geographic subject-->
201
                        <datafield tag="691" ind1=" " ind2=" ">
202
                            <subfield code="a"><xsl:value-of select="." /></subfield>
203
                        </datafield>
204
                    </xsl:otherwise>
205
                </xsl:choose>
206
            </xsl:for-each>
207
208
209
            <xsl:for-each select="dc:type">
210
                <datafield tag="655" ind1="7" ind2=" ">
211
                    <subfield code="a">
212
                        <xsl:value-of select="."/>
213
                    </subfield>
214
                    <subfield code="2">local</subfield>
215
                </datafield>
216
            </xsl:for-each>
217
218
219
220
            <xsl:for-each select="dc:contributer">
221
                    <xsl:call-template name="persname_template">
222
                        <xsl:with-param name="string" select="." />
223
                        <xsl:with-param name="field" select="'100'" />
224
                        <xsl:with-param name="ind1" select = "'1'" />
225
                        <xsl:with-param name="ind2" select = "'0'" />
226
                        <xsl:with-param name="type" select="'contributor'" />
227
                    </xsl:call-template>
228
            </xsl:for-each>
229
230
            <xsl:for-each select="dc:source">
231
                <datafield tag="786" ind1="0" ind2=" ">
232
                    <subfield code="n">
233
                        <xsl:value-of select="."/>
234
                    </subfield>
235
                </datafield>
236
            </xsl:for-each>
237
238
            <xsl:for-each select="dc:relation">
239
                <datafield tag="787" ind1="0" ind2=" ">
240
                    <subfield code="n">
241
                        <xsl:value-of select="."/>
242
                    </subfield>
243
                </datafield>
244
            </xsl:for-each>
245
246
            <xsl:if test="dc:identifier">
247
                <datafield tag="856" ind1="4" ind2="1">
248
                    <subfield code="u"><xsl:value-of select="dc:identifier[last()]" /></subfield>
249
                    <subfield code="z">Connect to this object online.</subfield>
250
                </datafield>
251
            </xsl:if>
252
        </record>
253
254
    </xsl:template>
255
<xsl:template name="persname_template">
256
      <xsl:param name="string" />
257
      <xsl:param name="field" />
258
      <xsl:param name="ind1" />
259
      <xsl:param name="ind2" />
260
      <xsl:param name="type" />
261
      <datafield>
262
         <xsl:attribute name="tag">
263
            <xsl:value-of select="$field" />
264
         </xsl:attribute>
265
         <xsl:attribute name="ind1">
266
            <xsl:value-of select="$ind1" />
267
         </xsl:attribute>
268
         <xsl:attribute name="ind2">
269
            <xsl:value-of select="$ind2" />
270
         </xsl:attribute>
271
272
         <!-- Sample input: Brightman, Samuel C. (Samuel Charles), 1911-1992 -->
273
         <!-- Sample output: $aBrightman, Samuel C. $q(Samuel Charles), $d1911-. -->
274
         <!-- will handle names with dashes e.g. Bourke-White, Margaret -->
275
276
         <!-- CAPTURE PRIMARY NAME BY LOOKING FOR A PAREN OR A DASH OR NEITHER -->
277
         <xsl:choose>
278
            <!-- IF A PAREN, STOP AT AN OPENING PAREN -->
279
            <xsl:when test="contains($string, '(')!=0">
280
               <subfield code="a">
281
                  <xsl:value-of select="substring-before($string, '(')" />
282
               </subfield>
283
            </xsl:when>
284
            <!-- IF A DASH, CHECK IF IT'S A DATE OR PART OF THE NAME -->
285
            <xsl:when test="contains($string, '-')!=0">
286
               <xsl:variable name="name_1" select="substring-before($string, '-')" />
287
               <xsl:choose>
288
                  <!-- IF IT'S A DATE REMOVE IT -->
289
                  <xsl:when test="translate(substring($name_1, (string-length($name_1)), 1), '0123456789', '9999999999') = '9'">
290
                     <xsl:variable name="name" select="substring($name_1, 1, (string-length($name_1)-6))" />
291
                     <subfield code="a">
292
                        <xsl:value-of select="$name" />
293
                     </subfield>
294
                  </xsl:when>
295
                  <!-- IF IT'S NOT A DATE, CHECK WHETHER THERE IS A DATE LATER -->
296
                  <xsl:otherwise>
297
                     <xsl:variable name="remainder" select="substring-after($string, '-')" />
298
                     <xsl:choose>
299
                        <!-- IF THERE'S A DASH, ASSUME IT'S A DATE AND REMOVE IT -->
300
                        <xsl:when test="contains($remainder, '-')!=0">
301
                           <xsl:variable name="tmp" select="substring-before($remainder, '-')" />
302
                           <xsl:variable name="name_2" select="substring($tmp, 1, (string-length($tmp)-6))" />
303
                           <subfield code="a">
304
                              <xsl:value-of select="$name_1" />-<xsl:value-of select="$name_2" />
305
                           </subfield>
306
                        </xsl:when>
307
                        <!-- IF THERE'S NO DASH IN THE REMAINDER, OUTPUT IT -->
308
                        <xsl:otherwise>
309
                           <subfield code="a">
310
                              <xsl:value-of select="$string" />
311
                           </subfield>
312
                        </xsl:otherwise>
313
                     </xsl:choose>
314
                  </xsl:otherwise>
315
               </xsl:choose>
316
            </xsl:when>
317
            <!-- NO DASHES, NO PARENS, JUST OUTPUT THE NAME -->
318
            <xsl:otherwise>
319
               <subfield code="a">
320
                  <xsl:value-of select="$string" />
321
               </subfield>
322
            </xsl:otherwise>
323
         </xsl:choose>
324
325
         <!-- CAPTURE SECONDARY NAME IN PARENS FOR SUBFIELD Q -->
326
         <xsl:if test="contains($string, '(')!=0">
327
            <xsl:variable name="subq_tmp" select="substring-after($string, '(')" />
328
            <xsl:variable name="subq" select="substring-before($subq_tmp, ')')" />
329
            <subfield code="q">
330
               (<xsl:value-of select="$subq" />)
331
            </subfield>
332
         </xsl:if>
333
334
         <!-- CAPTURE DATE FOR SUBFIELD D, ASSUME DATE IS LAST ITEM IN FIELD -->
335
         <!-- Note: does not work if name has a dash in it -->
336
         <xsl:if test="contains($string, '-')!=0">
337
            <xsl:variable name="date_tmp" select="substring-before($string, '-')" />
338
            <xsl:variable name="remainder" select="substring-after($string, '-')" />
339
            <xsl:choose>
340
               <!-- CHECK SECOND HALF FOR ANOTHER DASH; IF PRESENT, ASSUME THAT IS DATE -->
341
               <xsl:when test="contains($remainder, '-')!=0">
342
                  <xsl:variable name="tmp" select="substring-before($remainder, '-')" />
343
                  <xsl:variable name="date_1" select="substring($remainder, (string-length($tmp)-3))" />
344
                  <!-- CHECK WHETHER IT HAS A NUMBER BEFORE IT AND IF SO, OUTPUT IT AS DATE -->
345
                  <xsl:if test="translate(substring($date_1, 1, 1), '0123456789', '9999999999') = '9'">
346
                     <subfield code="d">
347
                        <xsl:value-of select="$date_1" />.
348
                     </subfield>
349
                  </xsl:if>
350
               </xsl:when>
351
               <!-- OTHERWISE THIS IS THE ONLY DASH SO TAKE IT -->
352
               <xsl:otherwise>
353
                  <xsl:variable name="date_2" select="substring($string, (string-length($date_tmp)-3))" />
354
                  <!-- CHECK WHETHER IT HAS A NUMBER BEFORE IT AND IF SO, OUTPUT IT AS DATE -->
355
                  <xsl:if test="translate(substring($date_2, 1, 1), '0123456789', '9999999999') = '9'">
356
                     <subfield code="d">
357
                        <xsl:value-of select="$date_2" />.
358
                     </subfield>
359
                  </xsl:if>
360
               </xsl:otherwise>
361
            </xsl:choose>
362
         </xsl:if>
363
         <subfield code="e"><xsl:value-of select="$type" /></subfield>
364
      </datafield>
365
   </xsl:template>
366
367
</xsl:stylesheet>
(-)a/koha-tmpl/intranet-tmpl/prog/en/xslt/OAIDCtoUNIMARCXML.xsl (+341 lines)
Line 0 Link Here
1
<?xml version="1.0" encoding="UTF-8"?>
2
<xsl:stylesheet version="1.0"
3
    xmlns:dc="http://purl.org/dc/elements/1.1/"
4
    xmlns:dcterms="http://purl.org/dc/terms/1.1"
5
    xmlns:oai_dc="http://www.openarchives.org/OAI/2.0/oai_dc/"
6
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
7
    xsi:schemaLocation="http://www.openarchives.org/OAI/2.0/oai_dc/
8
        http://www.openarchives.org/OAI/2.0/oai_dc.xsd"
9
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
10
    xmlns="http://www.loc.gov/MARC21/slim"  exclude-result-prefixes="dc dcterms oai_dc">
11
12
    <xsl:import href="MARC21slimUtils.xsl"/>
13
    <xsl:output method="xml" encoding="UTF-8" indent="yes"/>
14
15
16
    <xsl:template match="/">
17
        <collection xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.loc.gov/MARC21/slim http://www.loc.gov/standards/marcxml/schema/MARC21slim.xsd" >
18
            <xsl:apply-templates />
19
        </collection>
20
    </xsl:template>
21
22
    <xsl:template name="OAI-PMH">
23
             <xsl:for-each select = "ListRecords/record/metadata/oai_dc:dc">
24
                <xsl:apply-templates  />
25
             </xsl:for-each>
26
             <xsl:for-each select = "GetRecord/record/metadata/oai_dc:dc">
27
                <xsl:apply-templates  />
28
             </xsl:for-each>
29
    </xsl:template>
30
31
    <xsl:template match="text()" />
32
    <xsl:template match="oai_dc:dc">
33
        <record>
34
35
            <xsl:for-each select="dc:creator">
36
                <xsl:call-template name="persname_template">
37
                    <xsl:with-param name="string" select="." />
38
                    <xsl:with-param name="field" select="'700'" />
39
                    <xsl:with-param name="ind1" select = "'1'" />
40
                    <xsl:with-param name="ind2" select = "'0'" />
41
                    <xsl:with-param name="type" select="'author'" />
42
                </xsl:call-template>
43
            </xsl:for-each>
44
45
46
            <xsl:for-each select="dc:title[1]">
47
                <datafield tag="200" ind1="0" ind2=" ">
48
                    <subfield code="a">
49
                        <xsl:value-of select="."/>
50
                    </subfield>
51
                </datafield>
52
            </xsl:for-each>
53
54
            <xsl:for-each select="dc:title[position()>1]">
55
                <xsl:if test=".!=''">
56
                    <datafield tag="532" ind1="3" ind2="0">
57
                        <subfield code="a">
58
                            <xsl:value-of select="."/>
59
                        </subfield>
60
                    </datafield>
61
                </xsl:if>
62
            </xsl:for-each>
63
64
            <xsl:choose>
65
                <xsl:when test="dc:publisher">
66
                    <xsl:if test="translate(dc:publisher/.,'.,:;','')!=''">
67
                        <datafield tag="214" ind1=" " ind2=" ">
68
                        <xsl:choose>
69
                            <xsl:when test="dc:date">
70
                                <subfield code="c"><xsl:value-of select="dc:publisher[1]"/>, </subfield>
71
                                <xsl:if test="translate(dc:date[1]/., '.,:;','')!=''">
72
                                    <subfield code="d"><xsl:value-of select="dc:date[1]" />.</subfield>
73
                                </xsl:if>
74
                            </xsl:when>
75
                            <xsl:otherwise>
76
                                <subfield code="c"><xsl:value-of select="dc:publisher[1]"/>.</subfield>
77
                            </xsl:otherwise>
78
                        </xsl:choose>
79
                        </datafield>
80
                    </xsl:if>
81
                </xsl:when>
82
                <xsl:otherwise>
83
                    <xsl:if test="translate(dc:date[1],'.,:;','')!=''">
84
                        <datafield tag="214" ind1=" " ind2=" ">
85
                            <subfield code="d"><xsl:value-of select="dc:date[1]" />.</subfield>
86
                        </datafield>
87
                    </xsl:if>
88
                </xsl:otherwise>
89
            </xsl:choose>
90
91
92
            <xsl:for-each select="dc:contributor">
93
                <datafield tag="701" ind1=" " ind2=" ">
94
                    <subfield code="a">
95
                        <xsl:value-of select="."/>
96
                    </subfield>
97
                </datafield>
98
            </xsl:for-each>
99
100
101
            <xsl:for-each select="dc:coverage">
102
                <xsl:choose>
103
                    <xsl:when test="translate(., '0123456789-.?','')=''">
104
                        <!--Likely;this is a date-->
105
                        <datafield tag="215" ind1=" " ind2=" ">
106
                            <subfield code="a"><xsl:value-of select="."/></subfield>
107
                        </datafield>
108
                    </xsl:when>
109
                    <xsl:otherwise>
110
                        <!--likely a geographic subject, we will print this later-->
111
                    </xsl:otherwise>
112
                </xsl:choose>
113
            </xsl:for-each>
114
115
116
            <xsl:for-each select="dc:description">
117
                <datafield tag="330" ind1=" " ind2=" ">
118
                    <subfield code="a">
119
                        <xsl:value-of select="normalize-space(.)"/>
120
                    </subfield>
121
                </datafield>
122
            </xsl:for-each>
123
124
125
            <!-- no match -->
126
            <xsl:for-each select="dc:rights">
127
                <datafield tag="371" ind1=" " ind2=" ">
128
                    <subfield code="a">
129
                        <xsl:value-of select="."/>
130
                    </subfield>
131
                </datafield>
132
            </xsl:for-each>
133
134
135
            <xsl:for-each select="dc:language">
136
                <datafield tag="101" ind1=" " ind2=" ">
137
                    <subfield code="a">
138
                        <xsl:value-of select="."/>
139
                    </subfield>
140
                </datafield>
141
            </xsl:for-each>
142
143
144
            <xsl:for-each select="dc:subject">
145
                <datafield tag="610" ind1=" " ind2=" ">
146
                    <subfield code="a">
147
                        <xsl:value-of select="."/>
148
                    </subfield>
149
                </datafield>
150
            </xsl:for-each>
151
152
153
            <!-- Resource type will most likely need a specific instance-based mapping -->
154
            <!--
155
            <xsl:for-each select="dc:type">
156
                <datafield tag="099" ind1=" " ind2=" ">
157
                    <subfield code="t">
158
                        <xsl:value-of select="."/>
159
                    </subfield>
160
                </datafield>
161
            </xsl:for-each>
162
            -->
163
164
165
            <!-- no match -->
166
            <xsl:for-each select="dc:coverage">
167
                <xsl:choose>
168
                    <xsl:when test="translate(., '0123456789-.?','')=''">
169
                        <!--Likely; this is a date-->
170
                    </xsl:when>
171
                    <xsl:otherwise>
172
                        <!--likely a geographic subject-->
173
                        <datafield tag="691" ind1=" " ind2=" ">
174
                            <subfield code="a"><xsl:value-of select="." /></subfield>
175
                        </datafield>
176
                    </xsl:otherwise>
177
                </xsl:choose>
178
            </xsl:for-each>
179
180
181
            <!-- no match -->
182
            <xsl:for-each select="dc:type">
183
                <datafield tag="655" ind1="7" ind2=" ">
184
                    <subfield code="a">
185
                        <xsl:value-of select="."/>
186
                    </subfield>
187
                    <subfield code="2">local</subfield>
188
                </datafield>
189
            </xsl:for-each>
190
191
192
            <xsl:for-each select="dc:contributer">
193
                    <xsl:call-template name="persname_template">
194
                        <xsl:with-param name="string" select="." />
195
                        <xsl:with-param name="field" select="'702'" />
196
                        <xsl:with-param name="ind1" select = "'1'" />
197
                        <xsl:with-param name="ind2" select = "'0'" />
198
                        <xsl:with-param name="type" select="'contributor'" />
199
                    </xsl:call-template>
200
            </xsl:for-each>
201
202
            <!-- no match -->
203
            <xsl:for-each select="dc:source">
204
                <datafield tag="786" ind1="0" ind2=" ">
205
                    <subfield code="n">
206
                        <xsl:value-of select="."/>
207
                    </subfield>
208
                </datafield>
209
            </xsl:for-each>
210
211
            <!-- TODO: 455 456 470 488-->
212
            <xsl:for-each select="dc:relation">
213
                <datafield tag="488" ind1="0" ind2=" ">
214
                    <subfield code="e">
215
                        <xsl:value-of select="."/>
216
                    </subfield>
217
                </datafield>
218
            </xsl:for-each>
219
220
            <xsl:if test="dc:identifier">
221
                <datafield tag="856" ind1="4" ind2="1">
222
                    <subfield code="u"><xsl:value-of select="dc:identifier[last()]" /></subfield>
223
                    <subfield code="z">Connect to this object online.</subfield>
224
                </datafield>
225
            </xsl:if>
226
        </record>
227
228
    </xsl:template>
229
<xsl:template name="persname_template">
230
      <xsl:param name="string" />
231
      <xsl:param name="field" />
232
      <xsl:param name="ind1" />
233
      <xsl:param name="ind2" />
234
      <xsl:param name="type" />
235
      <datafield>
236
         <xsl:attribute name="tag">
237
            <xsl:value-of select="$field" />
238
         </xsl:attribute>
239
         <xsl:attribute name="ind1">
240
            <xsl:value-of select="$ind1" />
241
         </xsl:attribute>
242
         <xsl:attribute name="ind2">
243
            <xsl:value-of select="$ind2" />
244
         </xsl:attribute>
245
246
         <!-- Sample input: Brightman, Samuel C. (Samuel Charles), 1911-1992 -->
247
         <!-- Sample output: $aBrightman, Samuel C. $q(Samuel Charles), $d1911-. -->
248
         <!-- will handle names with dashes e.g. Bourke-White, Margaret -->
249
250
         <!-- CAPTURE PRIMARY NAME BY LOOKING FOR A PAREN OR A DASH OR NEITHER -->
251
         <xsl:choose>
252
            <!-- IF A PAREN, STOP AT AN OPENING PAREN -->
253
            <xsl:when test="contains($string, '(')!=0">
254
               <subfield code="a">
255
                  <xsl:value-of select="substring-before($string, '(')" />
256
               </subfield>
257
            </xsl:when>
258
            <!-- IF A DASH, CHECK IF IT'S A DATE OR PART OF THE NAME -->
259
            <xsl:when test="contains($string, '-')!=0">
260
               <xsl:variable name="name_1" select="substring-before($string, '-')" />
261
               <xsl:choose>
262
                  <!-- IF IT'S A DATE REMOVE IT -->
263
                  <xsl:when test="translate(substring($name_1, (string-length($name_1)), 1), '0123456789', '9999999999') = '9'">
264
                     <xsl:variable name="name" select="substring($name_1, 1, (string-length($name_1)-6))" />
265
                     <subfield code="a">
266
                        <xsl:value-of select="$name" />
267
                     </subfield>
268
                  </xsl:when>
269
                  <!-- IF IT'S NOT A DATE, CHECK WHETHER THERE IS A DATE LATER -->
270
                  <xsl:otherwise>
271
                     <xsl:variable name="remainder" select="substring-after($string, '-')" />
272
                     <xsl:choose>
273
                        <!-- IF THERE'S A DASH, ASSUME IT'S A DATE AND REMOVE IT -->
274
                        <xsl:when test="contains($remainder, '-')!=0">
275
                           <xsl:variable name="tmp" select="substring-before($remainder, '-')" />
276
                           <xsl:variable name="name_2" select="substring($tmp, 1, (string-length($tmp)-6))" />
277
                           <subfield code="a">
278
                              <xsl:value-of select="$name_1" />-<xsl:value-of select="$name_2" />
279
                           </subfield>
280
                        </xsl:when>
281
                        <!-- IF THERE'S NO DASH IN THE REMAINDER, OUTPUT IT -->
282
                        <xsl:otherwise>
283
                           <subfield code="a">
284
                              <xsl:value-of select="$string" />
285
                           </subfield>
286
                        </xsl:otherwise>
287
                     </xsl:choose>
288
                  </xsl:otherwise>
289
               </xsl:choose>
290
            </xsl:when>
291
            <!-- NO DASHES, NO PARENS, JUST OUTPUT THE NAME -->
292
            <xsl:otherwise>
293
               <subfield code="a">
294
                  <xsl:value-of select="$string" />
295
               </subfield>
296
            </xsl:otherwise>
297
         </xsl:choose>
298
299
         <!-- CAPTURE SECONDARY NAME IN PARENS FOR SUBFIELD Q -->
300
         <xsl:if test="contains($string, '(')!=0">
301
            <xsl:variable name="subq_tmp" select="substring-after($string, '(')" />
302
            <xsl:variable name="subq" select="substring-before($subq_tmp, ')')" />
303
            <subfield code="q">
304
               (<xsl:value-of select="$subq" />)
305
            </subfield>
306
         </xsl:if>
307
308
         <!-- CAPTURE DATE FOR SUBFIELD D, ASSUME DATE IS LAST ITEM IN FIELD -->
309
         <!-- Note: does not work if name has a dash in it -->
310
         <xsl:if test="contains($string, '-')!=0">
311
            <xsl:variable name="date_tmp" select="substring-before($string, '-')" />
312
            <xsl:variable name="remainder" select="substring-after($string, '-')" />
313
            <xsl:choose>
314
               <!-- CHECK SECOND HALF FOR ANOTHER DASH; IF PRESENT, ASSUME THAT IS DATE -->
315
               <xsl:when test="contains($remainder, '-')!=0">
316
                  <xsl:variable name="tmp" select="substring-before($remainder, '-')" />
317
                  <xsl:variable name="date_1" select="substring($remainder, (string-length($tmp)-3))" />
318
                  <!-- CHECK WHETHER IT HAS A NUMBER BEFORE IT AND IF SO, OUTPUT IT AS DATE -->
319
                  <xsl:if test="translate(substring($date_1, 1, 1), '0123456789', '9999999999') = '9'">
320
                     <subfield code="d">
321
                        <xsl:value-of select="$date_1" />.
322
                     </subfield>
323
                  </xsl:if>
324
               </xsl:when>
325
               <!-- OTHERWISE THIS IS THE ONLY DASH SO TAKE IT -->
326
               <xsl:otherwise>
327
                  <xsl:variable name="date_2" select="substring($string, (string-length($date_tmp)-3))" />
328
                  <!-- CHECK WHETHER IT HAS A NUMBER BEFORE IT AND IF SO, OUTPUT IT AS DATE -->
329
                  <xsl:if test="translate(substring($date_2, 1, 1), '0123456789', '9999999999') = '9'">
330
                     <subfield code="d">
331
                        <xsl:value-of select="$date_2" />.
332
                     </subfield>
333
                  </xsl:if>
334
               </xsl:otherwise>
335
            </xsl:choose>
336
         </xsl:if>
337
         <subfield code="e"><xsl:value-of select="$type" /></subfield>
338
      </datafield>
339
   </xsl:template>
340
341
</xsl:stylesheet>
(-)a/koha-tmpl/intranet-tmpl/prog/en/xslt/OAIMarcxml2KohaMarcxml.xsl (+23 lines)
Line 0 Link Here
1
<?xml version="1.0" encoding="UTF-8"?>
2
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:marc="http://www.loc.gov/MARC21/slim">
3
  <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
4
5
 <!-- keep comments -->
6
  <xsl:template match="*[name()='metadata']">
7
    <xsl:apply-templates />
8
  </xsl:template>
9
10
  <xsl:template match="*">
11
    <!-- remove element prefix -->
12
    <xsl:element name="{local-name()}">
13
      <!-- process attributes -->
14
      <xsl:for-each select="@*">
15
        <!-- remove attribute prefix -->
16
        <xsl:attribute name="{local-name()}">
17
          <xsl:value-of select="."/>
18
        </xsl:attribute>
19
      </xsl:for-each>
20
      <xsl:apply-templates/>
21
    </xsl:element>
22
  </xsl:template>
23
</xsl:stylesheet>
(-)a/misc/cronjobs/harvest_oai.pl (+110 lines)
Line 0 Link Here
1
#!/usr/bin/perl
2
#
3
# Copyright 2023 BibLibre
4
#
5
# This file is part of Koha.
6
#
7
# Koha is free software; you can redistribute it and/or modify it
8
# under the terms of the GNU General Public License as published by
9
# the Free Software Foundation; either version 3 of the License, or
10
# (at your option) any later version.
11
#
12
# Koha is distributed in the hope that it will be useful, but
13
# WITHOUT ANY WARRANTY; without even the implied warranty of
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
# GNU General Public License for more details.
16
#
17
# You should have received a copy of the GNU General Public License
18
# along with Koha; if not, see <http://www.gnu.org/licenses>.
19
20
use warnings;
21
use strict;
22
use utf8;
23
24
use Getopt::Long qw( GetOptions );
25
use Koha::Script -cron;
26
use Koha::OAI::Client::Harvester;
27
use Koha::OaiServers;
28
use C4::Log qw( cronlogaction );
29
use Try::Tiny qw( catch try );
30
31
my $command_line_options = join( " ", @ARGV );
32
33
my ( $help, $verbose, $id, $days, $list, $force );
34
35
GetOptions(
36
    'h|help'         => \$help,
37
    'v|verbose'      => \$verbose,
38
    'r|repository:i' => \$id,
39
    'd|days:i'       => \$days,
40
    'l|list'         => \$list,
41
    'f|force'        => \$force,
42
);
43
my $usage = << 'ENDUSAGE';
44
45
This script starts an OAI Harvest
46
47
This script has the following parameters :
48
    -h --help: this message
49
    -v --verbose
50
    -r --repository: id of the OAI repository
51
    -d --days: number of days to harvest from (optional)
52
    -l --list: list the OAI repositories
53
    -f --force: force harvesting (ignore records datestamps)
54
55
ENDUSAGE
56
57
if ($help) {
58
    print $usage;
59
    exit;
60
}
61
62
if ($list) {
63
    my $servers = Koha::OaiServers->search( {}, { order_by => { -asc => 'id' } } )->unblessed;
64
    print "The following repositories are available: \n\n";
65
    foreach my $server (@$servers) {
66
        print $server->{'id'} . ": "
67
            . $server->{'servername'}
68
            . ", endpoint: "
69
            . $server->{'endpoint'}
70
            . ", set: "
71
            . $server->{'oai_set'}
72
            . ", recordtype: "
73
            . $server->{'recordtype'} . "\n";
74
    }
75
    print "\n";
76
    exit;
77
}
78
79
if ( !$id ) {
80
    print "The repository parameter is mandatory.\n";
81
    print $usage . "\n";
82
}
83
84
my $server = Koha::OaiServers->find($id);
85
86
unless ($server) {
87
    print "OAI Server $id unknown\n";
88
    exit;
89
}
90
91
my $script_handler = Koha::Script->new( { script => $0 } );
92
93
try {
94
    $script_handler->lock_exec;
95
} catch {
96
    my $message = "Skipping execution of $0";
97
    print "$message\n" if $verbose;
98
    cronlogaction( { info => $message } );
99
    exit;
100
};
101
102
cronlogaction( { action => 'Start', info => "Starting OAI Harvest" } );
103
cronlogaction( { info   => "Command line: $command_line_options" } );
104
105
my $harvester =
106
    Koha::OAI::Client::Harvester->new( { server => $server, verbose => $verbose, days => $days, force => $force } );
107
$harvester->init();
108
109
cronlogaction( { action => 'End', info => "Ending OAI Harvest" } );
110
exit(0);
(-)a/t/db_dependent/Koha/OAIHarvester.t (+78 lines)
Line 0 Link Here
1
#!/usr/bin/perl
2
3
# Copyright 2024 BibLibre
4
#
5
# This file is part of Koha
6
#
7
# Koha is free software; you can redistribute it and/or modify it
8
# under the terms of the GNU General Public License as published by
9
# the Free Software Foundation; either version 3 of the License, or
10
# (at your option) any later version.
11
#
12
# Koha is distributed in the hope that it will be useful, but
13
# WITHOUT ANY WARRANTY; without even the implied warranty of
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
# GNU General Public License for more details.
16
#
17
# You should have received a copy of the GNU General Public License
18
# along with Koha; if not, see <http://www.gnu.org/licenses>.
19
20
use Modern::Perl;
21
use Test::More tests => 5;
22
use Test::Exception;
23
24
use t::lib::TestBuilder;
25
use HTTP::OAI;
26
use HTTP::OAI::Metadata::OAI_DC;
27
use HTTP::OAI::Record;
28
use HTTP::OAI::Encapsulation;
29
use Koha::Database;
30
use Koha::OaiServer;
31
use Koha::OaiServers;
32
use Koha::OAI::Client::Harvester;
33
34
my $schema  = Koha::Database->new->schema;
35
my $builder = t::lib::TestBuilder->new;
36
37
$schema->storage->txn_begin;
38
39
my $new_oai_1 = Koha::OaiServer->new(
40
    {
41
        endpoint   => 'my_host1.org',
42
        oai_set    => 'set1',
43
        servername => 'my_test_1',
44
        dataformat => 'oai_dc',
45
        recordtype => 'biblio',
46
        add_xslt   => '',
47
    }
48
)->store;
49
50
my $harvester = Koha::OAI::Client::Harvester->new( { server => $new_oai_1, verbose => 1, days => 1, force => 1 } );
51
52
my $record =
53
    '<metadata xmlns="http://www.openarchives.org/OAI/2.0/"><oai_dc:dc xmlns:oai_dc="http://www.openarchives.org/OAI/2.0/oai_dc/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.openarchives.org/OAI/2.0/oai_dc/ http://www.openarchives.org/OAI/2.0/oai_dc.xsd"><dc:title>Pièces diverses </dc:title><dc:identifier>ARCH/0320 [cote]</dc:identifier><dc:relation>FR-920509801 [RCR établissement]</dc:relation><dc:relation>Central obrera boliviana [Fonds ou collection]</dc:relation><dc:format>1 carton</dc:format><dc:date>1971/2000</dc:date><dc:type>Archives et manuscrits</dc:type></oai_dc:dc></metadata>';
54
55
my $r = HTTP::OAI::Record->new();
56
$r->header->datestamp('2017-05-10T09:18:13Z');
57
$r->metadata( HTTP::OAI::Metadata->new( dom => $record ) );
58
59
my $status = $harvester->processRecord($r);
60
is( $status, 'skipped', 'Record with no identifier is skipped' );
61
62
$r->header->identifier('oai:myarchive.org:oid-233');
63
64
$status = $harvester->processRecord($r);
65
is( $status, 'added', 'Record is added' );
66
67
$status = $harvester->processRecord($r);
68
is( $status, 'updated', 'When force is used, record is updated' );
69
70
$harvester = Koha::OAI::Client::Harvester->new( { server => $new_oai_1, verbose => 1, days => 1, force => 0 } );
71
$status    = $harvester->processRecord($r);
72
is( $status, 'skipped', 'When force is not used, record is skipped (already up to date)' );
73
74
$r->header->datestamp('2018-05-10T09:18:13Z');
75
$status = $harvester->processRecord($r);
76
is( $status, 'updated', 'When force is not used, record is updated if newer' );
77
78
$schema->storage->txn_rollback;
(-)a/t/db_dependent/Koha/OAIServers.t (-1 / +95 lines)
Line 0 Link Here
0
- 
1
#!/usr/bin/perl
2
3
# Copyright 2015 Koha Development team
4
#
5
# This file is part of Koha
6
#
7
# Koha is free software; you can redistribute it and/or modify it
8
# under the terms of the GNU General Public License as published by
9
# the Free Software Foundation; either version 3 of the License, or
10
# (at your option) any later version.
11
#
12
# Koha is distributed in the hope that it will be useful, but
13
# WITHOUT ANY WARRANTY; without even the implied warranty of
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
# GNU General Public License for more details.
16
#
17
# You should have received a copy of the GNU General Public License
18
# along with Koha; if not, see <http://www.gnu.org/licenses>.
19
20
use Modern::Perl;
21
use Test::More tests => 2;
22
use Test::Exception;
23
24
use t::lib::TestBuilder;
25
use Koha::Database;
26
use Koha::OaiServer;
27
use Koha::OaiServers;
28
29
my $schema  = Koha::Database->new->schema;
30
my $builder = t::lib::TestBuilder->new;
31
32
subtest 'new, find and delete tests' => sub {
33
    plan tests => 4;
34
    $schema->storage->txn_begin;
35
    my $nb_of_oais = Koha::OaiServers->search->count;
36
    my $new_oai_1  = Koha::OaiServer->new(
37
        {
38
            endpoint   => 'my_host1.org',
39
            oai_set    => 'set1',
40
            servername => 'my_test_1',
41
            dataformat => 'oai_dc',
42
            recordtype => 'biblio',
43
        }
44
    )->store;
45
    my $new_oai_2 = Koha::OaiServer->new(
46
        {
47
            endpoint   => 'my_host2.org',
48
            oai_set    => 'set2',
49
            servername => 'my_test_2',
50
            dataformat => 'marcxml',
51
            recordtype => 'authority',
52
        }
53
    )->store;
54
55
    like( $new_oai_1->id, qr|^\d+$|, 'Adding a new oai server should have set the id' );
56
    is( Koha::OaiServers->search->count, $nb_of_oais + 2, 'The 2 oai servers should have been added' );
57
58
    my $retrieved_oai_1 = Koha::OaiServers->find( $new_oai_1->id );
59
    is(
60
        $retrieved_oai_1->servername, $new_oai_1->servername,
61
        'Find a oai server by id should return the correct oai server'
62
    );
63
64
    $retrieved_oai_1->delete;
65
    is( Koha::OaiServers->search->count, $nb_of_oais + 1, 'Delete should have deleted the oai server' );
66
67
    $schema->storage->txn_rollback;
68
};
69
70
subtest 'Check NOT NULL without default values' => sub {
71
    plan tests => 5;
72
    $schema->storage->txn_begin;
73
    local $SIG{__WARN__} = sub { };    # TODO Needed it for suppressing DBIx warns
74
75
    my $server = Koha::OaiServer->new(
76
        {
77
            oai_set    => 'set3',
78
            dataformat => 'marcxml',
79
            recordtype => 'biblio',
80
        }
81
    );
82
83
    throws_ok { $server->store } 'DBIx::Class::Exception', 'Exception on empty endpoint';
84
    like( $@->{msg}, qr/'endpoint' doesn't have a default value/, 'Verified that DBIx blamed endpoint' );
85
86
    $server->endpoint('endpoint_added');
87
    throws_ok { $server->store } 'DBIx::Class::Exception', 'Exception on empty servername';
88
    like( $@->{msg}, qr/'servername' doesn't have a default value/, 'Verified that DBIx blamed servername' );
89
90
    $server->servername('servername_added');
91
92
    lives_ok { $server->store } 'No exceptions anymore';
93
94
    $schema->storage->txn_rollback;
95
};

Return to bug 35659