| 
      
            Lines 1-27
          
      
      
        Link Here
      
     | 
  
        
          | 1 | 
          #!/usr/bin/perl  | 
          1 | 
          #!/usr/bin/perl  | 
        
        
          | 2 | 
           | 
          2 | 
           | 
        
        
          | 3 | 
          # Copyright Biblibre 2008  | 
          3 | 
          # Copyright Biblibre 2008  | 
        
            
               | 
               | 
              4 | 
              # Copyright The National Library of Finland 2015  | 
            
        
          | 4 | 
          #  | 
          5 | 
          #  | 
        
        
          | 5 | 
          # This file is part of Koha.  | 
          6 | 
          # This file is part of Koha.  | 
        
        
          | 6 | 
          #  | 
          7 | 
          #  | 
        
          
            
              | 7 | 
              # Koha is free software; you can redistribute it and/or modify it under the  | 
              8 | 
              # Koha is free software; you can redistribute it and/or modify it  | 
            
            
              | 8 | 
              # terms of the GNU General Public License as published by the Free Software  | 
              9 | 
              # under the terms of the GNU General Public License as published by  | 
            
            
              | 9 | 
              # Foundation; either version 2 of the License, or (at your option) any later  | 
              10 | 
              # the Free Software Foundation; either version 3 of the License, or  | 
            
            
              | 10 | 
              # version.  | 
              11 | 
              # (at your option) any later version.  | 
            
        
          | 11 | 
          #  | 
          12 | 
          #  | 
        
          
            
              | 12 | 
              # Koha is distributed in the hope that it will be useful, but WITHOUT ANY  | 
              13 | 
              # Koha is distributed in the hope that it will be useful, but  | 
            
            
              | 13 | 
              # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR  | 
              14 | 
              # WITHOUT ANY WARRANTY; without even the implied warranty of  | 
            
            
              | 14 | 
              # A PARTICULAR PURPOSE.  See the GNU General Public License for more details.  | 
              15 | 
              # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the  | 
            
            
               | 
               | 
              16 | 
              # GNU General Public License for more details.  | 
            
        
          | 15 | 
          #  | 
          17 | 
          #  | 
        
          
            
              | 16 | 
              # You should have received a copy of the GNU General Public License along  | 
              18 | 
              # You should have received a copy of the GNU General Public License  | 
            
            
              | 17 | 
              # with Koha; if not, write to the Free Software Foundation, Inc.,  | 
              19 | 
              # along with Koha; if not, see <http://www.gnu.org/licenses>.  | 
            
            
              | 18 | 
              # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.  | 
               | 
               | 
            
        
          | 19 | 
           | 
          20 | 
           | 
        
        
          | 20 | 
           | 
          21 | 
           | 
        
        
          | 21 | 
          use strict;  | 
          22 | 
          use strict;  | 
        
        
          | 22 | 
          use warnings;  | 
          23 | 
          use warnings;  | 
        
        
          | 23 | 
           | 
          24 | 
           | 
        
          
            
              | 24 | 
              use CGI qw/:standard -oldstyle_urls/;  | 
              25 | 
              use CGI qw( :standard -oldstyle_urls -utf8 );  | 
            
        
          | 25 | 
          use vars qw( $GZIP );  | 
          26 | 
          use vars qw( $GZIP );  | 
        
        
          | 26 | 
          use C4::Context;  | 
          27 | 
          use C4::Context;  | 
        
        
          | 27 | 
           | 
          28 | 
           | 
        
  
    | 
      
            Lines 59-64
          else {
      
      
        Link Here
      
     | 
  
        
          | 59 | 
          }  | 
          60 | 
          }  | 
        
        
          | 60 | 
           | 
          61 | 
           | 
        
        
          | 61 | 
          binmode STDOUT, ':encoding(UTF-8)';  | 
          62 | 
          binmode STDOUT, ':encoding(UTF-8)';  | 
        
            
               | 
               | 
              63 | 
              # OAI-PMH handles dates in UTC, so do that on the database level to avoid the  | 
            
            
              | 64 | 
              # need for any conversions  | 
            
            
              | 65 | 
              C4::Context->dbh->prepare("SET time_zone='+00:00'")->execute(); | 
            
        
          | 62 | 
          my $repository = C4::OAI::Repository->new();  | 
          66 | 
          my $repository = C4::OAI::Repository->new();  | 
        
        
          | 63 | 
           | 
          67 | 
           | 
        
        
          | 64 | 
          # __END__ Main Prog  | 
          68 | 
          # __END__ Main Prog  | 
        
  
    | 
      
            Lines 71-77
          my $repository = C4::OAI::Repository->new();
      
      
        Link Here
      
     | 
  
        
          | 71 | 
          # - from  | 
          75 | 
          # - from  | 
        
        
          | 72 | 
          # - until  | 
          76 | 
          # - until  | 
        
        
          | 73 | 
          # - offset  | 
          77 | 
          # - offset  | 
        
          
            
              | 74 | 
              #   | 
              78 | 
              #  | 
            
        
          | 75 | 
          package C4::OAI::ResumptionToken;  | 
          79 | 
          package C4::OAI::ResumptionToken;  | 
        
        
          | 76 | 
           | 
          80 | 
           | 
        
        
          | 77 | 
          use strict;  | 
          81 | 
          use strict;  | 
        
  
    | 
      
            Lines 86-94
          sub new {
      
      
        Link Here
      
     | 
  
        
          | 86 | 
           | 
          90 | 
           | 
        
        
          | 87 | 
              my $self = $class->SUPER::new(%args);  | 
          91 | 
              my $self = $class->SUPER::new(%args);  | 
        
        
          | 88 | 
           | 
          92 | 
           | 
        
          
            
              | 89 | 
                  my ($metadata_prefix, $offset, $from, $until, $set);  | 
              93 | 
                  my ($metadata_prefix, $offset, $from, $until, $set, $stage);  | 
            
        
          | 90 | 
              if ( $args{ resumptionToken } ) { | 
          94 | 
              if ( $args{ resumptionToken } ) { | 
        
          
            
              | 91 | 
                      ($metadata_prefix, $offset, $from, $until, $set)  | 
              95 | 
                      ($metadata_prefix, $offset, $from, $until, $set, $stage)  | 
            
        
          | 92 | 
                      = split( '/', $args{resumptionToken} ); | 
          96 | 
                      = split( '/', $args{resumptionToken} ); | 
        
        
          | 93 | 
              }  | 
          97 | 
              }  | 
        
        
          | 94 | 
              else { | 
          98 | 
              else { | 
        
  
    | 
      
            Lines 101-106
          sub new {
      
      
        Link Here
      
     | 
  
        
          | 101 | 
                  }  | 
          105 | 
                  }  | 
        
        
          | 102 | 
                  $offset = $args{ offset } || 0; | 
          106 | 
                  $offset = $args{ offset } || 0; | 
        
        
          | 103 | 
                  $set = $args{set}; | 
          107 | 
                  $set = $args{set}; | 
        
            
               | 
               | 
              108 | 
                      $stage = (defined $args{stage}) ? $args{stage} : 0; | 
            
        
          | 104 | 
              }  | 
          109 | 
              }  | 
        
        
          | 105 | 
           | 
          110 | 
           | 
        
        
          | 106 | 
              $self->{ metadata_prefix } = $metadata_prefix; | 
          111 | 
              $self->{ metadata_prefix } = $metadata_prefix; | 
        
  
    | 
      
            Lines 108-116
          sub new {
      
      
        Link Here
      
     | 
  
        
          | 108 | 
              $self->{ from            } = $from; | 
          113 | 
              $self->{ from            } = $from; | 
        
        
          | 109 | 
              $self->{ until           } = $until; | 
          114 | 
              $self->{ until           } = $until; | 
        
        
          | 110 | 
              $self->{ set             } = $set; | 
          115 | 
              $self->{ set             } = $set; | 
        
            
               | 
               | 
              116 | 
                  $self->{ stage           } = $stage; | 
            
        
          | 111 | 
           | 
          117 | 
           | 
        
        
          | 112 | 
              $self->resumptionToken(  | 
          118 | 
              $self->resumptionToken(  | 
        
          
            
              | 113 | 
                      join( '/', $metadata_prefix, $offset, $from, $until, $set ) );  | 
              119 | 
                      join( '/', $metadata_prefix, $offset, $from, $until, $set, $stage ) );  | 
            
        
          | 114 | 
              $self->cursor( $offset );  | 
          120 | 
              $self->cursor( $offset );  | 
        
        
          | 115 | 
           | 
          121 | 
           | 
        
        
          | 116 | 
              return $self;  | 
          122 | 
              return $self;  | 
        
  
    | 
      
            Lines 138-146
          sub new {
      
      
        Link Here
      
     | 
  
        
          | 138 | 
                  repositoryName      => C4::Context->preference("LibraryName"), | 
          144 | 
                  repositoryName      => C4::Context->preference("LibraryName"), | 
        
        
          | 139 | 
                  adminEmail          => C4::Context->preference("KohaAdminEmailAddress"), | 
          145 | 
                  adminEmail          => C4::Context->preference("KohaAdminEmailAddress"), | 
        
        
          | 140 | 
                  MaxCount            => C4::Context->preference("OAI-PMH:MaxCount"), | 
          146 | 
                  MaxCount            => C4::Context->preference("OAI-PMH:MaxCount"), | 
        
          
            
              | 141 | 
                      granularity         => 'YYYY-MM-DD',  | 
              147 | 
                      granularity         => 'YYYY-MM-DDThh:mm:ssZ',  | 
            
            
              | 142 | 
                      earliestDatestamp   => '0001-01-01',  | 
              148 | 
                      earliestDatestamp   => '0001-01-01T00:00:00Z',  | 
            
            
              | 143 | 
                      deletedRecord       => 'no',  | 
              149 | 
                      deletedRecord       => 'persistent',  | 
            
        
          | 144 | 
              );  | 
          150 | 
              );  | 
        
        
          | 145 | 
           | 
          151 | 
           | 
        
        
          | 146 | 
              # FIXME - alas, the description element is not so simple; to validate  | 
          152 | 
              # FIXME - alas, the description element is not so simple; to validate  | 
        
  
    | 
      
            Lines 187-192
          sub new {
      
      
        Link Here
      
     | 
  
        
          | 187 | 
                      metadataNamespace => 'http://www.openarchives.org/OAI/2.0/oai_dc/'  | 
          193 | 
                      metadataNamespace => 'http://www.openarchives.org/OAI/2.0/oai_dc/'  | 
        
        
          | 188 | 
                  ) );  | 
          194 | 
                  ) );  | 
        
        
          | 189 | 
                  $self->metadataFormat( HTTP::OAI::MetadataFormat->new(  | 
          195 | 
                  $self->metadataFormat( HTTP::OAI::MetadataFormat->new(  | 
        
            
               | 
               | 
              196 | 
                          metadataPrefix    => 'marc21',  | 
            
            
              | 197 | 
                          schema            => 'http://www.loc.gov/MARC21/slim http://www.loc.gov/ standards/marcxml/schema/MARC21slim.xsd',  | 
            
            
              | 198 | 
                          metadataNamespace => 'http://www.loc.gov/MARC21/slim http://www.loc.gov/ standards/marcxml/schema/MARC21slim'  | 
            
            
              | 199 | 
                      ) );  | 
            
            
              | 200 | 
                      $self->metadataFormat( HTTP::OAI::MetadataFormat->new(  | 
            
        
          | 190 | 
                      metadataPrefix    => 'marcxml',  | 
          201 | 
                      metadataPrefix    => 'marcxml',  | 
        
        
          | 191 | 
                      schema            => 'http://www.loc.gov/MARC21/slim http://www.loc.gov/ standards/marcxml/schema/MARC21slim.xsd',  | 
          202 | 
                      schema            => 'http://www.loc.gov/MARC21/slim http://www.loc.gov/ standards/marcxml/schema/MARC21slim.xsd',  | 
        
        
          | 192 | 
                      metadataNamespace => 'http://www.loc.gov/MARC21/slim http://www.loc.gov/ standards/marcxml/schema/MARC21slim'  | 
          203 | 
                      metadataNamespace => 'http://www.loc.gov/MARC21/slim http://www.loc.gov/ standards/marcxml/schema/MARC21slim'  | 
        
  
    | 
      
            Lines 210-216
          use HTTP::OAI::Metadata::OAI_DC;
      
      
        Link Here
      
     | 
  
        
          | 210 | 
          use base ("HTTP::OAI::Record"); | 
          221 | 
          use base ("HTTP::OAI::Record"); | 
        
        
          | 211 | 
           | 
          222 | 
           | 
        
        
          | 212 | 
          sub new { | 
          223 | 
          sub new { | 
        
          
            
              | 213 | 
                  my ($class, $repository, $marcxml, $timestamp, $setSpecs, %args) = @_;  | 
              224 | 
                  my ($class, $repository, $marcxml, $timestamp, $setSpecs, $deleted, %args) = @_;  | 
            
        
          | 214 | 
           | 
          225 | 
           | 
        
        
          | 215 | 
              my $self = $class->SUPER::new(%args);  | 
          226 | 
              my $self = $class->SUPER::new(%args);  | 
        
        
          | 216 | 
           | 
          227 | 
           | 
        
  
    | 
      
            Lines 218-239
          sub new {
      
      
        Link Here
      
     | 
  
        
          | 218 | 
              $self->header( new HTTP::OAI::Header(  | 
          229 | 
              $self->header( new HTTP::OAI::Header(  | 
        
        
          | 219 | 
                  identifier  => $args{identifier}, | 
          230 | 
                  identifier  => $args{identifier}, | 
        
        
          | 220 | 
                  datestamp   => $timestamp,  | 
          231 | 
                  datestamp   => $timestamp,  | 
        
            
               | 
               | 
              232 | 
                      status => $deleted ? 'deleted' : undef  | 
            
        
          | 221 | 
              ) );  | 
          233 | 
              ) );  | 
        
        
          | 222 | 
           | 
          234 | 
           | 
        
        
          | 223 | 
              foreach my $setSpec (@$setSpecs) { | 
          235 | 
              foreach my $setSpec (@$setSpecs) { | 
        
        
          | 224 | 
                  $self->header->setSpec($setSpec);  | 
          236 | 
                  $self->header->setSpec($setSpec);  | 
        
        
          | 225 | 
              }  | 
          237 | 
              }  | 
        
        
          | 226 | 
           | 
          238 | 
           | 
        
          
            
              | 227 | 
                  my $parser = XML::LibXML->new();  | 
              239 | 
                  if (!$deleted) { | 
            
            
              | 228 | 
                  my $record_dom = $parser->parse_string( $marcxml );  | 
              240 | 
                      my $parser = XML::LibXML->new();  | 
            
            
              | 229 | 
                  my $format =  $args{metadataPrefix}; | 
              241 | 
                      my $record_dom = $parser->parse_string( $marcxml );  | 
            
            
              | 230 | 
                  if ( $format ne 'marcxml' ) { | 
              242 | 
                      my $format =  $args{metadataPrefix}; | 
            
            
              | 231 | 
                      my %args = (  | 
              243 | 
                      if ( $format ne 'marcxml' && $format ne 'marc21' ) { | 
            
            
              | 232 | 
                          OPACBaseURL => "'" . C4::Context->preference('OPACBaseURL') . "'" | 
              244 | 
                          my %args = (  | 
            
            
              | 233 | 
                      );  | 
              245 | 
                              OPACBaseURL => "'" . C4::Context->preference('OPACBaseURL') . "'" | 
            
            
              | 234 | 
                      $record_dom = $repository->stylesheet($format)->transform($record_dom, %args);  | 
              246 | 
                          );  | 
            
            
               | 
               | 
              247 | 
                          $record_dom = $repository->stylesheet($format)->transform($record_dom, %args);  | 
            
            
              | 248 | 
                      }  | 
            
            
              | 249 | 
                      $self->metadata( HTTP::OAI::Metadata->new( dom => $record_dom ) );  | 
            
        
          | 235 | 
              }  | 
          250 | 
              }  | 
        
            
              | 236 | 
                  $self->metadata( HTTP::OAI::Metadata->new( dom => $record_dom ) );  | 
               | 
               | 
            
        
          | 237 | 
           | 
          251 | 
           | 
        
        
          | 238 | 
              return $self;  | 
          252 | 
              return $self;  | 
        
        
          | 239 | 
          }  | 
          253 | 
          }  | 
        
  
    | 
      
            Lines 314-353
          sub new {
      
      
        Link Here
      
     | 
  
        
          | 314 | 
              if(defined $token->{'set'}) { | 
          328 | 
              if(defined $token->{'set'}) { | 
        
        
          | 315 | 
                  $set = GetOAISetBySpec($token->{'set'}); | 
          329 | 
                  $set = GetOAISetBySpec($token->{'set'}); | 
        
        
          | 316 | 
              }  | 
          330 | 
              }  | 
        
          
            
              | 317 | 
                  my $sql = "  | 
              331 | 
                  my $max = $repository->{'koha_max_count'}; | 
            
            
              | 318 | 
                      SELECT biblioitems.biblionumber, biblioitems.timestamp  | 
              332 | 
                  my $offset = $token->{'offset'}; | 
            
            
              | 319 | 
                      FROM biblioitems  | 
              333 | 
               | 
            
            
              | 320 | 
                  ";  | 
              334 | 
                  my $count = 0;  | 
            
            
              | 321 | 
                  $sql .= " JOIN oai_sets_biblios ON biblioitems.biblionumber = oai_sets_biblios.biblionumber " if defined $set;  | 
              335 | 
                  # Since creating a union of normal and deleted record tables would be a heavy  | 
            
            
              | 322 | 
                  $sql .= " WHERE DATE(timestamp) >= ? AND DATE(timestamp) <= ? ";  | 
              336 | 
                  # operation, build results in two stages: first deleted records, then normal records  | 
            
            
              | 323 | 
                  $sql .= " AND oai_sets_biblios.set_id = ? " if defined $set;  | 
              337 | 
                  MAINLOOP:  | 
            
            
              | 324 | 
                  $sql .= "  | 
              338 | 
                  for ( my $stage = $token->{'stage'}; $stage < 2; $stage++ ) { | 
            
            
              | 325 | 
                      LIMIT $repository->{'koha_max_count'} | 
              339 | 
                      my $sql = C4::OAI::Utils->build_list_sql($stage == 0, $set, $max, $offset);  | 
            
            
              | 326 | 
                      OFFSET $token->{'offset'} | 
              340 | 
                      my $sth = $dbh->prepare( $sql ) || die('Query preparation failed: ' . $dbh->errstr); | 
            
            
              | 327 | 
                  ";  | 
              341 | 
                      my @bind_params = (C4::OAI::Utils->fix_date($token->{'from'}, 0), C4::OAI::Utils->fix_date($token->{'until'}, 1)); | 
            
            
              | 328 | 
                  my $sth = $dbh->prepare( $sql );  | 
              342 | 
                      push @bind_params, $set->{'id'} if defined $set; | 
            
            
              | 329 | 
                  my @bind_params = ($token->{'from'}, $token->{'until'}); | 
              343 | 
                      $sth->execute( @bind_params ) || die('Query failed: ' . $dbh->errstr); | 
            
            
              | 330 | 
                  push @bind_params, $set->{'id'} if defined $set; | 
              344 | 
               | 
            
            
              | 331 | 
                  $sth->execute( @bind_params );  | 
              345 | 
                      while ( my ($biblionumber, $timestamp) = $sth->fetchrow ) { | 
            
            
              | 332 | 
               | 
              346 | 
                          $count++;  | 
            
            
              | 333 | 
                  my $pos = $token->{offset}; | 
              347 | 
                          if ( $count > $max ) { | 
            
            
              | 334 | 
                  while ( my ($biblionumber, $timestamp) = $sth->fetchrow ) { | 
              348 | 
                              $self->resumptionToken(  | 
            
            
              | 335 | 
                      $timestamp =~ s/ /T/, $timestamp .= 'Z';  | 
              349 | 
                                  new C4::OAI::ResumptionToken(  | 
            
            
              | 336 | 
                      $self->identifier( new HTTP::OAI::Header(  | 
              350 | 
                                      metadataPrefix  => $token->{metadata_prefix}, | 
            
            
              | 337 | 
                          identifier => $repository->{ koha_identifier} . ':' . $biblionumber, | 
              351 | 
                                      from            => $token->{from}, | 
            
            
              | 338 | 
                          datestamp  => $timestamp,  | 
              352 | 
                                      until           => $token->{until}, | 
            
            
              | 339 | 
                      ) );  | 
              353 | 
                                      offset          => $offset + $max,  | 
            
            
              | 340 | 
                      $pos++;  | 
              354 | 
                                      set             => $token->{set}, | 
            
            
               | 
               | 
              355 | 
                                      stage           => $stage  | 
            
            
              | 356 | 
                                  )  | 
            
            
              | 357 | 
                              );  | 
            
            
              | 358 | 
                              last MAINLOOP;  | 
            
            
              | 359 | 
                          }  | 
            
            
              | 360 | 
                          $timestamp =~ s/ /T/, $timestamp .= 'Z';  | 
            
            
              | 361 | 
                          my $header = new HTTP::OAI::Header(  | 
            
            
              | 362 | 
                              identifier => $repository->{ koha_identifier} . ':' . $biblionumber, | 
            
            
              | 363 | 
                              datestamp  => $timestamp,  | 
            
            
              | 364 | 
                          );  | 
            
            
              | 365 | 
                          if ($stage == 0) { | 
            
            
              | 366 | 
                              $header->status( 'deleted' );  | 
            
            
              | 367 | 
                          }  | 
            
            
              | 368 | 
                          $self->identifier( $header );  | 
            
            
              | 369 | 
                      }  | 
            
            
              | 370 | 
                      # Reset offset for next stage  | 
            
            
              | 371 | 
                      $offset = 0;  | 
            
        
          | 341 | 
              }  | 
          372 | 
              }  | 
        
            
              | 342 | 
                  $self->resumptionToken(  | 
               | 
               | 
            
            
              | 343 | 
                      new C4::OAI::ResumptionToken(  | 
            
            
              | 344 | 
                          metadataPrefix  => $token->{metadata_prefix}, | 
            
            
              | 345 | 
                          from            => $token->{from}, | 
            
            
              | 346 | 
                          until           => $token->{until}, | 
            
            
              | 347 | 
                          offset          => $pos,  | 
            
            
              | 348 | 
                          set             => $token->{set} | 
            
            
              | 349 | 
                      )  | 
            
            
              | 350 | 
                  ) if ($pos > $token->{offset}); | 
            
        
          | 351 | 
           | 
          373 | 
           | 
        
        
          | 352 | 
              return $self;  | 
          374 | 
              return $self;  | 
        
        
          | 353 | 
          }  | 
          375 | 
          }  | 
        
  
    | 
      
            Lines 467-518
          sub new {
      
      
        Link Here
      
     | 
  
        
          | 467 | 
              if(defined $token->{'set'}) { | 
          489 | 
              if(defined $token->{'set'}) { | 
        
        
          | 468 | 
                  $set = GetOAISetBySpec($token->{'set'}); | 
          490 | 
                  $set = GetOAISetBySpec($token->{'set'}); | 
        
        
          | 469 | 
              }  | 
          491 | 
              }  | 
        
          
            
              | 470 | 
                  my $sql = "  | 
              492 | 
                  my $offset = $token->{'offset'}; | 
            
            
              | 471 | 
                      SELECT biblioitems.biblionumber, biblioitems.marcxml, biblioitems.timestamp  | 
              493 | 
                  my $max = $repository->{'koha_max_count'}; | 
            
            
              | 472 | 
                      FROM biblioitems  | 
              494 | 
                  my $query = '  | 
            
            
              | 473 | 
                  ";  | 
              495 | 
              SELECT biblioitems.marcxml  | 
            
            
              | 474 | 
                  $sql .= " JOIN oai_sets_biblios ON biblioitems.biblionumber = oai_sets_biblios.biblionumber " if defined $set;  | 
              496 | 
              FROM biblioitems  | 
            
            
              | 475 | 
                  $sql .= " WHERE DATE(timestamp) >= ? AND DATE(timestamp) <= ? ";  | 
              497 | 
              WHERE biblionumber = ?  | 
            
            
              | 476 | 
                  $sql .= " AND oai_sets_biblios.set_id = ? " if defined $set;  | 
              498 | 
              ';  | 
            
            
              | 477 | 
                  $sql .= "  | 
              499 | 
                  my $marc_sth = $dbh->prepare($query) || die('Query preparation failed: ' . $dbh->errstr); | 
            
            
              | 478 | 
                      LIMIT $repository->{'koha_max_count'} | 
              500 | 
               | 
            
            
              | 479 | 
                      OFFSET $token->{'offset'} | 
              501 | 
                  my $count = 0;  | 
            
            
              | 480 | 
                  ";  | 
              502 | 
                  # Since creating a union of normal and deleted record tables would be a heavy  | 
            
            
              | 481 | 
               | 
              503 | 
                  # operation, build results in two stages: first deleted records, then normal records  | 
            
            
              | 482 | 
                  my $sth = $dbh->prepare( $sql );  | 
              504 | 
                  MAINLOOP:  | 
            
            
              | 483 | 
                  my @bind_params = ($token->{'from'}, $token->{'until'}); | 
              505 | 
                  for ( my $stage = $token->{'stage'}; $stage < 2; $stage++ ) { | 
            
            
              | 484 | 
                  push @bind_params, $set->{'id'} if defined $set; | 
              506 | 
                      my $sql = C4::OAI::Utils->build_list_sql($stage == 0, $set, $max, $offset);  | 
            
            
              | 485 | 
                  $sth->execute( @bind_params );  | 
              507 | 
                      my $sth = $dbh->prepare( $sql ) || die('Query preparation failed: ' . $dbh->errstr); | 
            
            
              | 486 | 
               | 
              508 | 
                      my @bind_params = (C4::OAI::Utils->fix_date($token->{'from'}, 0), C4::OAI::Utils->fix_date($token->{'until'}, 1)); | 
            
            
              | 487 | 
                  my $pos = $token->{offset}; | 
              509 | 
                      push @bind_params, $set->{'id'} if defined $set; | 
            
            
              | 488 | 
                  while ( my ($biblionumber, $marcxml, $timestamp) = $sth->fetchrow ) { | 
              510 | 
                      $sth->execute( @bind_params ) || die('Query failed: ' . $dbh->errstr); | 
            
            
              | 489 | 
                      my $oai_sets = GetOAISetsBiblio($biblionumber);  | 
              511 | 
               | 
            
            
              | 490 | 
                      my @setSpecs;  | 
              512 | 
                      while ( my ($biblionumber, $timestamp) = $sth->fetchrow ) { | 
            
            
              | 491 | 
                      foreach (@$oai_sets) { | 
              513 | 
                          $count++;  | 
            
            
              | 492 | 
                          push @setSpecs, $_->{spec}; | 
              514 | 
                          if ( $count > $max ) { | 
            
            
               | 
               | 
              515 | 
                              $self->resumptionToken(  | 
            
            
              | 516 | 
                                  new C4::OAI::ResumptionToken(  | 
            
            
              | 517 | 
                                      metadataPrefix  => $token->{metadata_prefix}, | 
            
            
              | 518 | 
                                      from            => $token->{from}, | 
            
            
              | 519 | 
                                      until           => $token->{until}, | 
            
            
              | 520 | 
                                      offset          => $offset + $max,  | 
            
            
              | 521 | 
                                      set             => $token->{set}, | 
            
            
              | 522 | 
                                      stage           => $stage  | 
            
            
              | 523 | 
                                  )  | 
            
            
              | 524 | 
                              );  | 
            
            
              | 525 | 
                              last MAINLOOP;  | 
            
            
              | 526 | 
                          }  | 
            
            
              | 527 | 
                          my $marcxml = '';  | 
            
            
              | 528 | 
                          if ($stage == 1) { | 
            
            
              | 529 | 
                              $marc_sth->execute(($biblionumber)) || die('Query failed: ' . $dbh->errstr); | 
            
            
              | 530 | 
                              my @marc_results = $marc_sth->fetchrow_array();  | 
            
            
              | 531 | 
                              if (@marc_results) { | 
            
            
              | 532 | 
                                  $marcxml = $marc_results[0];  | 
            
            
              | 533 | 
                              }  | 
            
            
              | 534 | 
                          }  | 
            
            
              | 535 | 
                          my $oai_sets = GetOAISetsBiblio($biblionumber);  | 
            
            
              | 536 | 
                          my @setSpecs;  | 
            
            
              | 537 | 
                          foreach (@$oai_sets) { | 
            
            
              | 538 | 
                              push @setSpecs, $_->{spec}; | 
            
            
              | 539 | 
                          }  | 
            
            
              | 540 | 
                          $self->record( C4::OAI::Record->new(  | 
            
            
              | 541 | 
                              $repository, $marcxml, $timestamp, \@setSpecs, $stage == 0 || !$marcxml,  | 
            
            
              | 542 | 
                              identifier      => $repository->{ koha_identifier } . ':' . $biblionumber, | 
            
            
              | 543 | 
                              metadataPrefix  => $token->{metadata_prefix} | 
            
            
              | 544 | 
                          ) );  | 
            
        
          | 493 | 
                  }  | 
          545 | 
                  }  | 
        
          
            
              | 494 | 
                      $self->record( C4::OAI::Record->new(  | 
              546 | 
                      # Reset offset for next stage  | 
            
            
              | 495 | 
                          $repository, $marcxml, $timestamp, \@setSpecs,  | 
              547 | 
                      $offset = 0;  | 
            
            
              | 496 | 
                          identifier      => $repository->{ koha_identifier } . ':' . $biblionumber, | 
               | 
               | 
            
            
              | 497 | 
                          metadataPrefix  => $token->{metadata_prefix} | 
            
            
              | 498 | 
                      ) );  | 
            
            
              | 499 | 
                      $pos++;  | 
            
        
          | 500 | 
              }  | 
          548 | 
              }  | 
        
            
              | 501 | 
                  $self->resumptionToken(  | 
               | 
               | 
            
            
              | 502 | 
                      new C4::OAI::ResumptionToken(  | 
            
            
              | 503 | 
                          metadataPrefix  => $token->{metadata_prefix}, | 
            
            
              | 504 | 
                          from            => $token->{from}, | 
            
            
              | 505 | 
                          until           => $token->{until}, | 
            
            
              | 506 | 
                          offset          => $pos,  | 
            
            
              | 507 | 
                          set             => $token->{set} | 
            
            
              | 508 | 
                      )  | 
            
            
              | 509 | 
                  ) if ($pos > $token->{offset}); | 
            
        
          | 510 | 
           | 
          549 | 
           | 
        
        
          | 511 | 
              return $self;  | 
          550 | 
              return $self;  | 
        
        
          | 512 | 
          }  | 
          551 | 
          }  | 
        
        
          | 513 | 
           | 
          552 | 
           | 
        
        
          | 514 | 
          # __END__ C4::OAI::ListRecords  | 
          553 | 
          # __END__ C4::OAI::ListRecords  | 
        
        
          | 515 | 
           | 
          554 | 
           | 
        
            
               | 
               | 
              555 | 
              package C4::OAI::Utils;  | 
            
            
              | 556 | 
               | 
            
            
              | 557 | 
              use strict;  | 
            
            
              | 558 | 
               | 
            
            
              | 559 | 
              sub fix_date { | 
            
            
              | 560 | 
                  my ($class, $date, $end) = @_;  | 
            
            
              | 561 | 
               | 
            
            
              | 562 | 
                  if (length($date) == 10) { | 
            
            
              | 563 | 
                      if ($end) { | 
            
            
              | 564 | 
                          $date .= ' 23:59:59';  | 
            
            
              | 565 | 
                      } else { | 
            
            
              | 566 | 
                          $date .= ' 00:00:00';  | 
            
            
              | 567 | 
                      }  | 
            
            
              | 568 | 
                  } else { | 
            
            
              | 569 | 
                      $date = substr($date, 0, 10) . ' ' . substr($date, 11, 8);  | 
            
            
              | 570 | 
                  }  | 
            
            
              | 571 | 
                  return $date;  | 
            
            
              | 572 | 
              }  | 
            
            
              | 573 | 
               | 
            
            
              | 574 | 
              sub build_list_sql { | 
            
            
              | 575 | 
                  my ($class, $deleted, $set, $max, $offset) = @_;  | 
            
            
              | 576 | 
               | 
            
            
              | 577 | 
                  my $table = $deleted ? 'deletedbiblioitems' : 'biblioitems';  | 
            
            
              | 578 | 
               | 
            
            
              | 579 | 
                  my $sql = "  | 
            
            
              | 580 | 
              SELECT bi.biblionumber, bi.timestamp  | 
            
            
              | 581 | 
                FROM $table bi  | 
            
            
              | 582 | 
                WHERE bi.timestamp >= ? AND bi.timestamp <= ?  | 
            
            
              | 583 | 
                ORDER BY bi.biblionumber  | 
            
            
              | 584 | 
              ";  | 
            
            
              | 585 | 
               | 
            
            
              | 586 | 
                  # Use a subquery for sets since it allows us to use an index for the subquery in  | 
            
            
              | 587 | 
                  # biblioitems table and is quite a bit faster than a join.  | 
            
            
              | 588 | 
                  if (defined $set) { | 
            
            
              | 589 | 
                      $sql = "  | 
            
            
              | 590 | 
              SELECT bi.* FROM ($sql) bi  | 
            
            
              | 591 | 
                WHERE bi.biblionumber in (SELECT osb.biblionumber FROM oai_sets_biblios osb WHERE osb.set_id = ?)  | 
            
            
              | 592 | 
              ";  | 
            
            
              | 593 | 
                  }  | 
            
            
              | 594 | 
                  $sql .= '  | 
            
            
              | 595 | 
                LIMIT ' . ($max + 1) . "  | 
            
            
              | 596 | 
                OFFSET $offset  | 
            
            
              | 597 | 
              ";  | 
            
            
              | 598 | 
               | 
            
            
              | 599 | 
                  return $sql;  | 
            
            
              | 600 | 
              }  | 
            
            
              | 601 | 
               | 
            
            
              | 602 | 
              # __END__ C4::OAI::Utils;  | 
            
            
              | 603 | 
               | 
            
        
          | 516 | 
           | 
          604 | 
           | 
        
        
          | 517 | 
           | 
          605 | 
           | 
        
        
          | 518 | 
          package C4::OAI::Repository;  | 
          606 | 
          package C4::OAI::Repository;  | 
        
  
    | 
      
            Lines 541-547
          sub new {
      
      
        Link Here
      
     | 
  
        
          | 541 | 
           | 
          629 | 
           | 
        
        
          | 542 | 
              $self->{ koha_identifier      } = C4::Context->preference("OAI-PMH:archiveID"); | 
          630 | 
              $self->{ koha_identifier      } = C4::Context->preference("OAI-PMH:archiveID"); | 
        
        
          | 543 | 
              $self->{ koha_max_count       } = C4::Context->preference("OAI-PMH:MaxCount"); | 
          631 | 
              $self->{ koha_max_count       } = C4::Context->preference("OAI-PMH:MaxCount"); | 
        
          
            
              | 544 | 
                  $self->{ koha_metadata_format } = ['oai_dc', 'marcxml']; | 
              632 | 
                  $self->{ koha_metadata_format } = ['oai_dc', 'marc21', 'marcxml']; | 
            
        
          | 545 | 
              $self->{ koha_stylesheet      } = { }; # Build when needed | 
          633 | 
              $self->{ koha_stylesheet      } = { }; # Build when needed | 
        
        
          | 546 | 
           | 
          634 | 
           | 
        
        
          | 547 | 
              # Load configuration file if defined in OAI-PMH:ConfFile syspref  | 
          635 | 
              # Load configuration file if defined in OAI-PMH:ConfFile syspref  | 
        
  
    | 
      
            Lines 639-645
          C4::OAI::Repository - Handles OAI-PMH requests for a Koha database.
      
      
        Link Here
      
     | 
  
        
          | 639 | 
          This object extend HTTP::OAI::Repository object.  | 
          727 | 
          This object extend HTTP::OAI::Repository object.  | 
        
        
          | 640 | 
          It accepts OAI-PMH HTTP requests and returns result.  | 
          728 | 
          It accepts OAI-PMH HTTP requests and returns result.  | 
        
        
          | 641 | 
           | 
          729 | 
           | 
        
          
            
              | 642 | 
              This OAI-PMH server can operate in a simple mode and extended one.   | 
              730 | 
              This OAI-PMH server can operate in a simple mode and extended one.  | 
            
        
          | 643 | 
           | 
          731 | 
           | 
        
        
          | 644 | 
          In simple mode, repository configuration comes entirely from Koha system  | 
          732 | 
          In simple mode, repository configuration comes entirely from Koha system  | 
        
        
          | 645 | 
          preferences (OAI-PMH:archiveID and OAI-PMH:MaxCount) and the server returns  | 
          733 | 
          preferences (OAI-PMH:archiveID and OAI-PMH:MaxCount) and the server returns  | 
        
  
    | 
      
            Lines 662-669
          configuration file koha-oai.conf can look like that:
      
      
        Link Here
      
     | 
  
        
          | 662 | 
                metadataNamespace: http://veryspecial.tamil.fr/vs/format-pivot/1.1/vs  | 
          750 | 
                metadataNamespace: http://veryspecial.tamil.fr/vs/format-pivot/1.1/vs  | 
        
        
          | 663 | 
                schema: http://veryspecial.tamil.fr/vs/format-pivot/1.1/vs.xsd  | 
          751 | 
                schema: http://veryspecial.tamil.fr/vs/format-pivot/1.1/vs.xsd  | 
        
        
          | 664 | 
                xsl_file: /usr/local/koha/xslt/vs.xsl  | 
          752 | 
                xsl_file: /usr/local/koha/xslt/vs.xsl  | 
        
          
            
              | 665 | 
                  marcxml:  | 
              753 | 
                  marc21 or marcxml:  | 
            
            
              | 666 | 
                    metadataPrefix: marxml  | 
              754 | 
                    metadataPrefix: marc21 or marxml  | 
            
        
          | 667 | 
                metadataNamespace: http://www.loc.gov/MARC21/slim http://www.loc.gov/standards/marcxml/schema/MARC21slim  | 
          755 | 
                metadataNamespace: http://www.loc.gov/MARC21/slim http://www.loc.gov/standards/marcxml/schema/MARC21slim  | 
        
        
          | 668 | 
                schema: http://www.loc.gov/MARC21/slim http://www.loc.gov/standards/marcxml/schema/MARC21slim.xsd  | 
          756 | 
                schema: http://www.loc.gov/MARC21/slim http://www.loc.gov/standards/marcxml/schema/MARC21slim.xsd  | 
        
        
          | 669 | 
              oai_dc:  | 
          757 | 
              oai_dc:  | 
        
            
              | 670 | 
              -   | 
               | 
               |