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

(-)a/Koha/OAI/Server/ListBase.pm (-16 / +14 lines)
Lines 45-57 sub GetRecords { Link Here
45
    if ( defined $token->{'set'} ) {
45
    if ( defined $token->{'set'} ) {
46
        $set = GetOAISetBySpec($token->{'set'});
46
        $set = GetOAISetBySpec($token->{'set'});
47
    }
47
    }
48
    my $offset = $token->{offset};
49
    my $deleted = defined $token->{deleted} ? $token->{deleted} : 0;
48
    my $deleted = defined $token->{deleted} ? $token->{deleted} : 0;
50
    my $deleted_count = defined $token->{deleted_count} ? $token->{deleted_count} : 0;
51
    my $max = $repository->{koha_max_count};
49
    my $max = $repository->{koha_max_count};
52
    my $count = 0;
50
    my $count = 0;
53
    my $format = $args{metadataPrefix} || $token->{metadata_prefix};
51
    my $format = $args{metadataPrefix} || $token->{metadata_prefix};
54
    my $include_items = $repository->items_included( $format );
52
    my $include_items = $repository->items_included( $format );
53
    my $from = $token->{from_arg};
54
    my $until = $token->{until_arg};
55
    my $next_id = $token->{next_id};
55
56
56
    # Since creating a union of normal and deleted record tables would be a heavy
57
    # Since creating a union of normal and deleted record tables would be a heavy
57
    # operation in a large database, build results in two stages:
58
    # operation in a large database, build results in two stages:
Lines 62-81 sub GetRecords { Link Here
62
        my $sql = "
63
        my $sql = "
63
            SELECT biblionumber
64
            SELECT biblionumber
64
            FROM $table
65
            FROM $table
65
            WHERE (timestamp >= ? AND timestamp <= ?)
66
            WHERE (biblionumber >= ? AND timestamp >= ? AND timestamp <= ?)
66
        ";
67
        ";
67
        my @bind_params = ($token->{'from_arg'}, $token->{'until_arg'});
68
        my @bind_params = ($next_id, $from, $until);
68
69
69
        if ($include_items) {
70
        if ($include_items) {
70
            $sql .= "
71
            $sql .= "
71
                OR biblionumber IN (SELECT biblionumber from deleteditems WHERE timestamp >= ? AND timestamp <= ?)
72
                OR biblionumber IN (SELECT biblionumber from deleteditems WHERE biblionumber >= ? AND timestamp >= ? AND timestamp <= ?)
72
            ";
73
            ";
73
            push @bind_params, ($token->{'from_arg'}, $token->{'until_arg'});
74
            push @bind_params, ($next_id, $from, $until);
74
            if (!$deleted) {
75
            if (!$deleted) {
75
                $sql .= "
76
                $sql .= "
76
                    OR biblionumber IN (SELECT biblionumber from items WHERE timestamp >= ? AND timestamp <= ?)
77
                    OR biblionumber IN (SELECT biblionumber from items WHERE biblionumber >= ? AND timestamp >= ? AND timestamp <= ?)
77
                ";
78
                ";
78
                push @bind_params, ($token->{'from_arg'}, $token->{'until_arg'});
79
                push @bind_params, ($next_id, $from, $until);
79
            }
80
            }
80
        }
81
        }
81
82
Lines 93-101 sub GetRecords { Link Here
93
            push @bind_params, $set->{'id'};
94
            push @bind_params, $set->{'id'};
94
        }
95
        }
95
96
96
        $sql .= "
97
        $sql .= " LIMIT " . ($max + 1);
97
            LIMIT " . ($max + 1) . "
98
            OFFSET " . ($offset - $deleted_count);
99
98
100
        my $sth = $dbh->prepare( $sql ) || die( 'Could not prepare statement: ' . $dbh->errstr );
99
        my $sth = $dbh->prepare( $sql ) || die( 'Could not prepare statement: ' . $dbh->errstr );
101
100
Lines 131-140 sub GetRecords { Link Here
131
                        metadataPrefix  => $token->{metadata_prefix},
130
                        metadataPrefix  => $token->{metadata_prefix},
132
                        from            => $token->{from},
131
                        from            => $token->{from},
133
                        until           => $token->{until},
132
                        until           => $token->{until},
134
                        offset          => $token->{offset} + $max,
133
                        cursor          => $token->{cursor} + $max,
135
                        set             => $token->{set},
134
                        set             => $token->{set},
136
                        deleted         => $deleted,
135
                        deleted         => $deleted,
137
                        deleted_count   => $deleted_count
136
                        next_id         => $biblionumber
138
                    )
137
                    )
139
                );
138
                );
140
                last STAGELOOP;
139
                last STAGELOOP;
Lines 172-180 sub GetRecords { Link Here
172
                ) );
171
                ) );
173
            }
172
            }
174
        }
173
        }
175
        # Store offset and deleted record count
174
        # Reset $next_id for the next stage
176
        $offset += $count;
175
        $next_id = 0;
177
        $deleted_count = $offset if ($deleted);
178
    }
176
    }
179
    return $count;
177
    return $count;
180
}
178
}
(-)a/Koha/OAI/Server/ResumptionToken.pm (-10 / +14 lines)
Lines 1-6 Link Here
1
# Copyright Tamil s.a.r.l. 2008-2015
1
# Copyright Tamil s.a.r.l. 2008-2015
2
# Copyright Biblibre 2008-2015
2
# Copyright Biblibre 2008-2015
3
# Copyright The National Library of Finland, University of Helsinki 2016-2017
3
# Copyright The National Library of Finland, University of Helsinki 2016-2021
4
#
4
#
5
# This file is part of Koha.
5
# This file is part of Koha.
6
#
6
#
Lines 31-47 use base ("HTTP::OAI::ResumptionToken"); Link Here
31
# - metadataPrefix
31
# - metadataPrefix
32
# - from
32
# - from
33
# - until
33
# - until
34
# - offset
34
# - cursor
35
# - deleted
35
# - deleted
36
# - deleted count (not used anymore, but preserved for back-compatibility)
37
# - nextId
36
38
37
sub new {
39
sub new {
38
    my ($class, %args) = @_;
40
    my ($class, %args) = @_;
39
41
40
    my $self = $class->SUPER::new(%args);
42
    my $self = $class->SUPER::new(%args);
41
43
42
    my ($metadata_prefix, $offset, $from, $until, $set, $deleted, $deleted_count);
44
    my ($metadata_prefix, $cursor, $from, $until, $set, $deleted, $next_id);
43
    if ( $args{ resumptionToken } ) {
45
    if ( $args{ resumptionToken } ) {
44
        ($metadata_prefix, $offset, $from, $until, $set, $deleted, $deleted_count)
46
        # Note: undef in place of deleted record count for back-compatibility
47
        ($metadata_prefix, $cursor, $from, $until, $set, $deleted, undef, $next_id)
45
            = split( '/', $args{resumptionToken} );
48
            = split( '/', $args{resumptionToken} );
46
    }
49
    }
47
    else {
50
    else {
Lines 55-79 sub new { Link Here
55
        #Add times to the arguments, when necessary, so they correctly match against the DB timestamps
58
        #Add times to the arguments, when necessary, so they correctly match against the DB timestamps
56
        $from .= 'T00:00:00Z' if length($from) == 10;
59
        $from .= 'T00:00:00Z' if length($from) == 10;
57
        $until .= 'T23:59:59Z' if length($until) == 10;
60
        $until .= 'T23:59:59Z' if length($until) == 10;
58
        $offset = $args{ offset } || 0;
61
        $cursor = $args{ cursor } // 0;
59
        $set = $args{ set } || '';
62
        $set = $args{ set } || '';
60
        $deleted = defined $args{ deleted } ? $args{ deleted } : 1;
63
        $deleted = defined $args{ deleted } ? $args{ deleted } : 1;
61
        $deleted_count = defined $args{ deleted_count } ? $args{ deleted_count } : 0;
64
        $next_id = $args{ next_id } // 0;
62
    }
65
    }
63
66
64
    $self->{ metadata_prefix } = $metadata_prefix;
67
    $self->{ metadata_prefix } = $metadata_prefix;
65
    $self->{ offset          } = $offset;
68
    $self->{ cursor          } = $cursor;
66
    $self->{ from            } = $from;
69
    $self->{ from            } = $from;
67
    $self->{ until           } = $until;
70
    $self->{ until           } = $until;
68
    $self->{ set             } = $set;
71
    $self->{ set             } = $set;
69
    $self->{ from_arg        } = _strip_UTC_designators($from);
72
    $self->{ from_arg        } = _strip_UTC_designators($from);
70
    $self->{ until_arg       } = _strip_UTC_designators($until);
73
    $self->{ until_arg       } = _strip_UTC_designators($until);
71
    $self->{ deleted         } = $deleted;
74
    $self->{ deleted         } = $deleted;
72
    $self->{ deleted_count   } = $deleted_count;
75
    $self->{ next_id         } = $next_id // 0;
73
76
77
    # Note: put zero where deleted record count used to be for back-compatibility
74
    $self->resumptionToken(
78
    $self->resumptionToken(
75
        join( '/', $metadata_prefix, $offset, $from, $until, $set, $deleted, $deleted_count ) );
79
        join( '/', $metadata_prefix, $cursor, $from, $until, $set, $deleted, 0, $next_id // 0 ) );
76
    $self->cursor( $offset );
80
    $self->cursor( $cursor );
77
81
78
    return $self;
82
    return $self;
79
}
83
}
(-)a/t/db_dependent/OAI/Server.t (-23 / +25 lines)
Lines 18-23 Link Here
18
# along with Koha; if not, see <http://www.gnu.org/licenses>.
18
# along with Koha; if not, see <http://www.gnu.org/licenses>.
19
19
20
use Modern::Perl;
20
use Modern::Perl;
21
use Test::Deep qw( cmp_deeply re );
21
use Test::MockTime qw/set_fixed_time restore_time/;
22
use Test::MockTime qw/set_fixed_time restore_time/;
22
23
23
use Test::More tests => 31;
24
use Test::More tests => 31;
Lines 74-85 $dbh->do('DELETE FROM oai_sets'); Link Here
74
75
75
set_fixed_time(CORE::time());
76
set_fixed_time(CORE::time());
76
77
77
my $base_datetime = dt_from_string();
78
my $base_datetime = dt_from_string(undef, undef, 'UTC');
78
my $date_added = $base_datetime->ymd . ' ' .$base_datetime->hms . 'Z';
79
my $date_added = $base_datetime->ymd . ' ' .$base_datetime->hms . 'Z';
79
my $date_to = substr($date_added, 0, 10) . 'T23:59:59Z';
80
my $date_to = substr($date_added, 0, 10) . 'T23:59:59Z';
80
my (@header, @marcxml, @oaidc, @marcxml_transformed);
81
my (@header, @marcxml, @oaidc, @marcxml_transformed);
81
my $sth = $dbh->prepare('UPDATE biblioitems     SET timestamp=? WHERE biblionumber=?');
82
my $sth = $dbh->prepare('UPDATE biblioitems     SET timestamp=? WHERE biblionumber=?');
82
my $sth2 = $dbh->prepare('UPDATE biblio_metadata SET timestamp=? WHERE biblionumber=?');
83
my $sth2 = $dbh->prepare('UPDATE biblio_metadata SET timestamp=? WHERE biblionumber=?');
84
my $first_bn = 0;
83
85
84
# Add biblio records
86
# Add biblio records
85
foreach my $index ( 0 .. NUMBER_OF_MARC_RECORDS - 1 ) {
87
foreach my $index ( 0 .. NUMBER_OF_MARC_RECORDS - 1 ) {
Lines 94-99 foreach my $index ( 0 .. NUMBER_OF_MARC_RECORDS - 1 ) { Link Here
94
        $record->append_fields( MARC::Field->new('952', '', '', 'a' => "Code" ) );
96
        $record->append_fields( MARC::Field->new('952', '', '', 'a' => "Code" ) );
95
    }
97
    }
96
    my ($biblionumber) = AddBiblio($record, '');
98
    my ($biblionumber) = AddBiblio($record, '');
99
    $first_bn = $biblionumber unless $first_bn;
97
    my $timestamp = $base_datetime->ymd . ' ' .$base_datetime->hms;
100
    my $timestamp = $base_datetime->ymd . ' ' .$base_datetime->hms;
98
    $sth->execute($timestamp,$biblionumber);
101
    $sth->execute($timestamp,$biblionumber);
99
    $sth2->execute($timestamp,$biblionumber);
102
    $sth2->execute($timestamp,$biblionumber);
Lines 174-180 sub test_query { Link Here
174
    }
177
    }
175
178
176
    delete $response->{responseDate};
179
    delete $response->{responseDate};
177
    unless (is_deeply($response, \%full_expected, $test)) {
180
    unless (cmp_deeply($response, \%full_expected, $test)) {
178
        diag
181
        diag
179
            "PARAM:" . Dump($param) .
182
            "PARAM:" . Dump($param) .
180
            "EXPECTED:" . Dump(\%full_expected) .
183
            "EXPECTED:" . Dump(\%full_expected) .
Lines 215-221 test_query('ListIdentifiers', {verb => 'ListIdentifiers', metadataPrefix => 'mar Link Here
215
    ListIdentifiers => {
218
    ListIdentifiers => {
216
        header => [ @header[0..2] ],
219
        header => [ @header[0..2] ],
217
        resumptionToken => {
220
        resumptionToken => {
218
            content => "marcxml/3/1970-01-01T00:00:00Z/$date_to//0/0",
221
            content => re( qr{^marcxml/3/1970-01-01T00:00:00Z/$date_to//0/0/\d+$} ),
219
            cursor  => 3,
222
            cursor  => 3,
220
        },
223
        },
221
    },
224
    },
Lines 225-231 test_query('ListIdentifiers', {verb => 'ListIdentifiers', metadataPrefix => 'mar Link Here
225
    ListIdentifiers => {
228
    ListIdentifiers => {
226
        header => [ @header[0..2] ],
229
        header => [ @header[0..2] ],
227
        resumptionToken => {
230
        resumptionToken => {
228
            content => "marcxml/3/1970-01-01T00:00:00Z/$date_to//0/0",
231
            content => re( qr{^marcxml/3/1970-01-01T00:00:00Z/$date_to//0/0/\d+$} ),
229
            cursor  => 3,
232
            cursor  => 3,
230
        },
233
        },
231
    },
234
    },
Lines 233-244 test_query('ListIdentifiers', {verb => 'ListIdentifiers', metadataPrefix => 'mar Link Here
233
236
234
test_query(
237
test_query(
235
    'ListIdentifiers with resumptionToken 1',
238
    'ListIdentifiers with resumptionToken 1',
236
    { verb => 'ListIdentifiers', resumptionToken => "marcxml/3/1970-01-01T00:00:00Z/$date_to//0/0" },
239
    { verb => 'ListIdentifiers', resumptionToken => "marcxml/3/1970-01-01T00:00:00Z/$date_to//0/0/" . ($first_bn + 3) },
237
    {
240
    {
238
        ListIdentifiers => {
241
        ListIdentifiers => {
239
            header => [ @header[3..5] ],
242
            header => [ @header[3..5] ],
240
            resumptionToken => {
243
            resumptionToken => {
241
              content => "marcxml/6/1970-01-01T00:00:00Z/$date_to//0/0",
244
              content => re( qr{^marcxml/6/1970-01-01T00:00:00Z/$date_to//0/0/\d+$} ),
242
              cursor  => 6,
245
              cursor  => 6,
243
            },
246
            },
244
          },
247
          },
Lines 247-258 test_query( Link Here
247
250
248
test_query(
251
test_query(
249
    'ListIdentifiers with resumptionToken 2',
252
    'ListIdentifiers with resumptionToken 2',
250
    { verb => 'ListIdentifiers', resumptionToken => "marcxml/6/1970-01-01T00:00:00Z/$date_to//0/0" },
253
    { verb => 'ListIdentifiers', resumptionToken => "marcxml/6/1970-01-01T00:00:00Z/$date_to//0/0/" . ($first_bn + 6) },
251
    {
254
    {
252
        ListIdentifiers => {
255
        ListIdentifiers => {
253
            header => [ @header[6..8] ],
256
            header => [ @header[6..8] ],
254
            resumptionToken => {
257
            resumptionToken => {
255
              content => "marcxml/9/1970-01-01T00:00:00Z/$date_to//0/0",
258
              content => re( qr{^marcxml/9/1970-01-01T00:00:00Z/$date_to//0/0/\d+$} ),
256
              cursor  => 9,
259
              cursor  => 9,
257
            },
260
            },
258
          },
261
          },
Lines 261-267 test_query( Link Here
261
264
262
test_query(
265
test_query(
263
    'ListIdentifiers with resumptionToken 3, response without resumption',
266
    'ListIdentifiers with resumptionToken 3, response without resumption',
264
    { verb => 'ListIdentifiers', resumptionToken => "marcxml/9/1970-01-01T00:00:00Z/$date_to//0/0" },
267
    { verb => 'ListIdentifiers', resumptionToken => "marcxml/9/1970-01-01T00:00:00Z/$date_to//0/0/" . ($first_bn + 9) },
265
    {
268
    {
266
        ListIdentifiers => {
269
        ListIdentifiers => {
267
            header => $header[9],
270
            header => $header[9],
Lines 280-286 test_query('ListRecords marcxml', {verb => 'ListRecords', metadataPrefix => 'mar Link Here
280
    ListRecords => {
283
    ListRecords => {
281
        record => [ @marcxml[0..2] ],
284
        record => [ @marcxml[0..2] ],
282
        resumptionToken => {
285
        resumptionToken => {
283
          content => "marcxml/3/1970-01-01T00:00:00Z/$date_to//0/0",
286
          content => re( qr{^marcxml/3/1970-01-01T00:00:00Z/$date_to//0/0/\d+$} ),
284
          cursor  => 3,
287
          cursor  => 3,
285
        },
288
        },
286
    },
289
    },
Lines 288-298 test_query('ListRecords marcxml', {verb => 'ListRecords', metadataPrefix => 'mar Link Here
288
291
289
test_query(
292
test_query(
290
    'ListRecords marcxml with resumptionToken 1',
293
    'ListRecords marcxml with resumptionToken 1',
291
    { verb => 'ListRecords', resumptionToken => "marcxml/3/1970-01-01T00:00:00Z/$date_to//0/0" },
294
    { verb => 'ListRecords', resumptionToken => "marcxml/3/1970-01-01T00:00:00Z/$date_to//0/0/" . ($first_bn + 3) },
292
    { ListRecords => {
295
    { ListRecords => {
293
        record => [ @marcxml[3..5] ],
296
        record => [ @marcxml[3..5] ],
294
        resumptionToken => {
297
        resumptionToken => {
295
          content => "marcxml/6/1970-01-01T00:00:00Z/$date_to//0/0",
298
          content => re( qr{^marcxml/6/1970-01-01T00:00:00Z/$date_to//0/0/\d+$} ),
296
          cursor  => 6,
299
          cursor  => 6,
297
        },
300
        },
298
    },
301
    },
Lines 300-310 test_query( Link Here
300
303
301
test_query(
304
test_query(
302
    'ListRecords marcxml with resumptionToken 2',
305
    'ListRecords marcxml with resumptionToken 2',
303
    { verb => 'ListRecords', resumptionToken => "marcxml/6/1970-01-01T00:00:00Z/$date_to//0/0" },
306
    { verb => 'ListRecords', resumptionToken => "marcxml/6/1970-01-01T00:00:00Z/$date_to//0/0/" . ($first_bn + 6) },
304
    { ListRecords => {
307
    { ListRecords => {
305
        record => [ @marcxml[6..8] ],
308
        record => [ @marcxml[6..8] ],
306
        resumptionToken => {
309
        resumptionToken => {
307
          content => "marcxml/9/1970-01-01T00:00:00Z/$date_to//0/0",
310
          content => re( qr{^marcxml/9/1970-01-01T00:00:00Z/$date_to//0/0/\d+$} ),
308
          cursor  => 9,
311
          cursor  => 9,
309
        },
312
        },
310
    },
313
    },
Lines 313-319 test_query( Link Here
313
# Last record, so no resumption token
316
# Last record, so no resumption token
314
test_query(
317
test_query(
315
    'ListRecords marcxml with resumptionToken 3, response without resumption',
318
    'ListRecords marcxml with resumptionToken 3, response without resumption',
316
    { verb => 'ListRecords', resumptionToken => "marcxml/9/1970-01-01T00:00:00Z/$date_to//0/0" },
319
    { verb => 'ListRecords', resumptionToken => "marcxml/9/1970-01-01T00:00:00Z/$date_to//0/0/" . ($first_bn + 9) },
317
    { ListRecords => {
320
    { ListRecords => {
318
        record => $marcxml[9],
321
        record => $marcxml[9],
319
    },
322
    },
Lines 323-329 test_query('ListRecords oai_dc', {verb => 'ListRecords', metadataPrefix => 'oai_ Link Here
323
    ListRecords => {
326
    ListRecords => {
324
        record => [ @oaidc[0..2] ],
327
        record => [ @oaidc[0..2] ],
325
        resumptionToken => {
328
        resumptionToken => {
326
          content => "oai_dc/3/1970-01-01T00:00:00Z/$date_to//0/0",
329
          content => re( qr{^oai_dc/3/1970-01-01T00:00:00Z/$date_to//0/0/\d+$} ),
327
          cursor  => 3,
330
          cursor  => 3,
328
        },
331
        },
329
    },
332
    },
Lines 331-341 test_query('ListRecords oai_dc', {verb => 'ListRecords', metadataPrefix => 'oai_ Link Here
331
334
332
test_query(
335
test_query(
333
    'ListRecords oai_dc with resumptionToken 1',
336
    'ListRecords oai_dc with resumptionToken 1',
334
    { verb => 'ListRecords', resumptionToken => "oai_dc/3/1970-01-01T00:00:00Z/$date_to//0/0" },
337
    { verb => 'ListRecords', resumptionToken => "oai_dc/3/1970-01-01T00:00:00Z/$date_to//0/0/" . ($first_bn + 3) },
335
    { ListRecords => {
338
    { ListRecords => {
336
        record => [ @oaidc[3..5] ],
339
        record => [ @oaidc[3..5] ],
337
        resumptionToken => {
340
        resumptionToken => {
338
          content => "oai_dc/6/1970-01-01T00:00:00Z/$date_to//0/0",
341
          content => re( qr{^oai_dc/6/1970-01-01T00:00:00Z/$date_to//0/0/\d+$} ),
339
          cursor  => 6,
342
          cursor  => 6,
340
        },
343
        },
341
    },
344
    },
Lines 343-353 test_query( Link Here
343
346
344
test_query(
347
test_query(
345
    'ListRecords oai_dc with resumptionToken 2',
348
    'ListRecords oai_dc with resumptionToken 2',
346
    { verb => 'ListRecords', resumptionToken => "oai_dc/6/1970-01-01T00:00:00Z/$date_to//0/0" },
349
    { verb => 'ListRecords', resumptionToken => "oai_dc/6/1970-01-01T00:00:00Z/$date_to//0/0/" . ($first_bn + 6) },
347
    { ListRecords => {
350
    { ListRecords => {
348
        record => [ @oaidc[6..8] ],
351
        record => [ @oaidc[6..8] ],
349
        resumptionToken => {
352
        resumptionToken => {
350
          content => "oai_dc/9/1970-01-01T00:00:00Z/$date_to//0/0",
353
          content => re( qr{^oai_dc/9/1970-01-01T00:00:00Z/$date_to//0/0/\d+$} ),
351
          cursor  => 9,
354
          cursor  => 9,
352
        },
355
        },
353
    },
356
    },
Lines 356-362 test_query( Link Here
356
# Last record, so no resumption token
359
# Last record, so no resumption token
357
test_query(
360
test_query(
358
    'ListRecords oai_dc with resumptionToken 3, response without resumption',
361
    'ListRecords oai_dc with resumptionToken 3, response without resumption',
359
    { verb => 'ListRecords', resumptionToken => "oai_dc/9/1970-01-01T00:00:00Z/$date_to//0/0" },
362
    { verb => 'ListRecords', resumptionToken => "oai_dc/9/1970-01-01T00:00:00Z/$date_to//0/0/" . ($first_bn + 9) },
360
    { ListRecords => {
363
    { ListRecords => {
361
        record => $oaidc[9],
364
        record => $oaidc[9],
362
    },
365
    },
Lines 369-375 test_query('ListRecords marcxml with xsl transformation', Link Here
369
    { ListRecords => {
372
    { ListRecords => {
370
        record => [ @marcxml_transformed[0..2] ],
373
        record => [ @marcxml_transformed[0..2] ],
371
        resumptionToken => {
374
        resumptionToken => {
372
            content => "marcxml/3/1970-01-01T00:00:00Z/$date_to//0/0",
375
            content => re( qr{^marcxml/3/1970-01-01T00:00:00Z/$date_to//0/0/\d+$} ),
373
            cursor => 3,
376
            cursor => 3,
374
        }
377
        }
375
    },
378
    },
376
- 

Return to bug 27584