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

(-)a/C4/Installer/PerlDependencies.pm (+20 lines)
Lines 712-717 our $PERL_DEPS = { Link Here
712
        'required' => '0',
712
        'required' => '0',
713
        'min_ver'  => '0.03',
713
        'min_ver'  => '0.03',
714
    },
714
    },
715
    'SOAP::Lite' => {
716
        'usage'    => 'Norwegian national library card',
717
        'required' => '0',
718
        'min_ver'  => '0.712',
719
    },
720
    'Crypt::GCrypt' => {
721
        'usage'    => 'Norwegian national library card',
722
        'required' => '0',
723
        'min_ver'  => '1.24',
724
    },
725
    'Convert::BaseN' => {
726
        'usage'    => 'Norwegian national library card',
727
        'required' => '0',
728
        'min_ver'  => '0.01',
729
    },
730
    'Digest::SHA' => {
731
        'usage'    => 'Norwegian national library card',
732
        'required' => '0',
733
        'min_ver'  => '5.61',
734
    },
715
};
735
};
716
736
717
1;
737
1;
(-)a/C4/Members.pm (-1 / +43 lines)
Lines 33-47 use C4::Accounts; Link Here
33
use C4::Biblio;
33
use C4::Biblio;
34
use C4::Letters;
34
use C4::Letters;
35
use C4::SQLHelper qw(InsertInTable UpdateInTable SearchInTable);
35
use C4::SQLHelper qw(InsertInTable UpdateInTable SearchInTable);
36
use C4::Members::Attributes qw(SearchIdMatchingAttribute);
36
use C4::Members::Attributes qw(SearchIdMatchingAttribute UpdateBorrowerAttribute);
37
use C4::NewsChannels; #get slip news
37
use C4::NewsChannels; #get slip news
38
use DateTime;
38
use DateTime;
39
use DateTime::Format::DateParse;
39
use DateTime::Format::DateParse;
40
use Koha::Database;
40
use Koha::DateUtils;
41
use Koha::DateUtils;
41
use Koha::Borrower::Debarments qw(IsDebarred);
42
use Koha::Borrower::Debarments qw(IsDebarred);
42
use Text::Unaccent qw( unac_string );
43
use Text::Unaccent qw( unac_string );
43
use Koha::AuthUtils qw(hash_password);
44
use Koha::AuthUtils qw(hash_password);
44
use Koha::Database;
45
use Koha::Database;
46
use Module::Load;
47
if ( C4::Context->preference('NorwegianPatronDBEnable') == 1 ) {
48
    load Koha::NorwegianPatronDB, qw( NLUpdateHashedPIN NLEncryptPIN NLSync );
49
}
45
50
46
our ($VERSION,@ISA,@EXPORT,@EXPORT_OK,$debug);
51
our ($VERSION,@ISA,@EXPORT,@EXPORT_OK,$debug);
47
52
Lines 784-789 sub ModMember { Link Here
784
        if ($data{password} eq '****' or $data{password} eq '') {
789
        if ($data{password} eq '****' or $data{password} eq '') {
785
            delete $data{password};
790
            delete $data{password};
786
        } else {
791
        } else {
792
            if ( C4::Context->preference('NorwegianPatronDBEnable') == 1 ) {
793
                # Update the hashed PIN in borrower_sync.hashed_pin, before Koha hashes it
794
                NLUpdateHashedPIN( $data{'borrowernumber'}, $data{password} );
795
            }
787
            $data{password} = hash_password($data{password});
796
            $data{password} = hash_password($data{password});
788
        }
797
        }
789
    }
798
    }
Lines 804-809 sub ModMember { Link Here
804
            AddEnrolmentFeeIfNeeded( $data{categorycode}, $data{borrowernumber} );
813
            AddEnrolmentFeeIfNeeded( $data{categorycode}, $data{borrowernumber} );
805
        }
814
        }
806
815
816
        # If NorwegianPatronDBEnable is enabled, we set syncstatus to something that a
817
        # cronjob will use for syncing with NL
818
        if ( C4::Context->preference('NorwegianPatronDBEnable') == 1 ) {
819
            my $borrowersync = Koha::Database->new->schema->resultset('BorrowerSync')->find({
820
                'synctype'       => 'norwegianpatrondb',
821
                'borrowernumber' => $data{'borrowernumber'}
822
            });
823
            # Do not set to "edited" if syncstatus is "new". We need to sync as new before
824
            # we can sync as changed. And the "new sync" will pick up all changes since
825
            # the patron was created anyway.
826
            if ( $borrowersync->syncstatus ne 'new' && $borrowersync->syncstatus ne 'delete' ) {
827
                $borrowersync->update( { 'syncstatus' => 'edited' } );
828
            }
829
            # Set the value of 'sync'
830
            $borrowersync->update( { 'sync' => $data{'sync'} } );
831
            # Try to do the live sync
832
            NLSync({ 'borrowernumber' => $data{'borrowernumber'} });
833
        }
834
807
        logaction("MEMBERS", "MODIFY", $data{'borrowernumber'}, "UPDATE (executed w/ arg: $data{'borrowernumber'})") if C4::Context->preference("BorrowersLog");
835
        logaction("MEMBERS", "MODIFY", $data{'borrowernumber'}, "UPDATE (executed w/ arg: $data{'borrowernumber'})") if C4::Context->preference("BorrowersLog");
808
    }
836
    }
809
    return $execute_success;
837
    return $execute_success;
Lines 846-856 sub AddMember { Link Here
846
      : $patron_category->default_privacy() eq 'never'   ? 2
874
      : $patron_category->default_privacy() eq 'never'   ? 2
847
      : $patron_category->default_privacy() eq 'forever' ? 0
875
      : $patron_category->default_privacy() eq 'forever' ? 0
848
      :                                                    undef;
876
      :                                                    undef;
877
    # Make a copy of the plain text password for later use
878
    my $plain_text_password = $data{'password'};
849
879
850
    # create a disabled account if no password provided
880
    # create a disabled account if no password provided
851
    $data{'password'} = ($data{'password'})? hash_password($data{'password'}) : '!';
881
    $data{'password'} = ($data{'password'})? hash_password($data{'password'}) : '!';
852
    $data{'borrowernumber'}=InsertInTable("borrowers",\%data);
882
    $data{'borrowernumber'}=InsertInTable("borrowers",\%data);
853
883
884
    # If NorwegianPatronDBEnable is enabled, we set syncstatus to something that a
885
    # cronjob will use for syncing with NL
886
    if ( exists $data{'borrowernumber'} && C4::Context->preference('NorwegianPatronDBEnable') == 1 ) {
887
        Koha::Database->new->schema->resultset('BorrowerSync')->create({
888
            'borrowernumber' => $data{'borrowernumber'},
889
            'synctype'       => 'norwegianpatrondb',
890
            'sync'           => 1,
891
            'syncstatus'     => 'new',
892
            'hashed_pin'     => NLEncryptPIN( $plain_text_password ),
893
        });
894
    }
895
854
    # mysql_insertid is probably bad.  not necessarily accurate and mysql-specific at best.
896
    # mysql_insertid is probably bad.  not necessarily accurate and mysql-specific at best.
855
    logaction("MEMBERS", "CREATE", $data{'borrowernumber'}, "") if C4::Context->preference("BorrowersLog");
897
    logaction("MEMBERS", "CREATE", $data{'borrowernumber'}, "") if C4::Context->preference("BorrowersLog");
856
898
(-)a/Koha/NorwegianPatronDB.pm (+679 lines)
Line 0 Link Here
1
package Koha::NorwegianPatronDB;
2
3
# Copyright 2014 Oslo Public Library
4
#
5
# This file is part of Koha.
6
#
7
# Koha is free software; you can redistribute it and/or modify it under the
8
# terms of the GNU General Public License as published by the Free Software
9
# Foundation; either version 3 of the License, or (at your option) any later
10
# version.
11
#
12
# Koha is distributed in the hope that it will be useful, but WITHOUT ANY
13
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
14
# A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License along
17
# with Koha; if not, write to the Free Software Foundation, Inc.,
18
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19
20
=head1 NAME
21
22
Koha::NorwegianPatronDB
23
24
=head1 SYNOPSIS
25
26
  use Koha::NorwegianPatronDB;
27
28
=head1 CONDITIONAL LOADING
29
30
This module depends on some Perl modules that have not been marked as required.
31
This is because the module only will be of interest to Norwegian libraries, and
32
it seems polite not to bother the rest of the world with these modules. It is
33
also good practice to check that the module is actually needed before loading
34
it. So in a NorwegianPatronDB page or script it will be OK to just do:
35
36
  use Koha::NorwegianPatronDB qw(...);
37
38
But in scripts that are also used by others (like e.g. moremember.pl), it will
39
be polite to only load the module at runtime, if it is needed:
40
41
  use Module::Load;
42
  if ( C4::Context->preference('NorwegianPatronDBEnable') == 1 ) {
43
      load Koha::NorwegianPatronDB, qw( NLGetSyncDataFromBorrowernumber );
44
  }
45
46
(Module::Load::Conditional is used for this in other parts of Koha, but it does
47
not seem to allow for a list of subroutines to import, so Module::Load looks
48
like a better candidate.)
49
50
=head1 FUNCTIONS
51
52
=cut
53
54
use Modern::Perl;
55
use C4::Context;
56
use C4::Members::Attributes qw( UpdateBorrowerAttribute );
57
use SOAP::Lite;
58
use Crypt::GCrypt;
59
use Digest::SHA qw( sha256_hex );
60
use Convert::BaseN;
61
use DateTime;
62
63
use base 'Exporter';
64
use version; our $VERSION = qv('1.0.0');
65
66
our %EXPORT_TAGS = ( all => [qw(
67
        NLCheckSysprefs
68
        NLSearch
69
        NLSync
70
        NLGetChanged
71
        NLMarkForDeletion
72
        NLDecodePin
73
        NLEncryptPIN
74
        NLUpdateHashedPIN
75
        NLGetFirstname
76
        NLGetSurname
77
        NLGetSyncDataFromBorrowernumber
78
)] );
79
Exporter::export_ok_tags('all');
80
81
my $nl_uri   = 'http://lanekortet.no';
82
my $nl_proxy =  C4::Context->preference("NorwegianPatronDBEndpoint");
83
84
=head2 SOAP::Transport::HTTP::Client::get_basic_credentials
85
86
This is included to set the username and password used by SOAP::Lite.
87
88
=cut
89
90
sub SOAP::Transport::HTTP::Client::get_basic_credentials {
91
    # Library username and password from Base Bibliotek (stored as system preferences)
92
    my $library_username = C4::Context->preference("NorwegianPatronDBUsername");
93
    my $library_password = C4::Context->preference("NorwegianPatronDBPassword");
94
    # Vendor username and password (stored in koha-conf.xml)
95
    my $vendor_username = C4::Context->config( 'nlvendoruser' );
96
    my $vendor_password = C4::Context->config( 'nlvendorpass' );
97
    # Combine usernames and passwords, and encrypt with SHA256
98
    my $combined_username = "$vendor_username-$library_username";
99
    my $combined_password = sha256_hex( "$library_password-$vendor_password" );
100
    warn "$combined_username => $combined_password";
101
    return $combined_username => $combined_password;
102
}
103
104
=head2 NLCheckSysprefs
105
106
Check that sysprefs relevant to NL are set.
107
108
=cut
109
110
sub NLCheckSysprefs {
111
112
    my $response = {
113
        'error'     => 0,
114
        'nlenabled' => 0,
115
        'endpoint'  => 0,
116
        'userpass'  => 0,
117
    };
118
119
    # Check that the Norwegian national paron database is enabled
120
    if ( C4::Context->preference("NorwegianPatronDBEnable") == 1 ) {
121
        $response->{ 'nlenabled' } = 1;
122
    } else {
123
        $response->{ 'error' } = 1;
124
    }
125
126
    # Check that an endpoint is specified
127
    if ( C4::Context->preference("NorwegianPatronDBEndpoint") ne '' ) {
128
        $response->{ 'endpoint' } = 1;
129
    } else {
130
        $response->{ 'error' } = 1;
131
    }
132
133
    # Check that the username and password for the patron database is set
134
    if ( C4::Context->preference("NorwegianPatronDBUsername") ne '' && C4::Context->preference("NorwegianPatronDBPassword") ne '' ) {
135
        $response->{ 'userpass' } = 1;
136
    } else {
137
        $response->{ 'error' } = 1;
138
    }
139
140
    return $response;
141
142
}
143
144
=head2 NLSearch
145
146
Search the NL patron database.
147
148
SOAP call: "hent" (fetch)
149
150
=cut
151
152
sub NLSearch {
153
154
    my ( $identifier ) = @_;
155
156
    my $client = SOAP::Lite
157
        ->on_action( sub { return '""';})
158
        ->uri( $nl_uri )
159
        ->proxy( $nl_proxy );
160
161
    my $id = SOAP::Data->type('string');
162
    $id->name('identifikator');
163
    $id->value( $identifier );
164
    my $som = $client->hent( $id );
165
166
    return $som;
167
168
}
169
170
=head2 NLSync
171
172
Sync a patron that has been changed or created in Koha "upstream" to NL.
173
174
Input is a hashref with one of two possible elements, either a patron retrieved
175
from the database:
176
177
    my $result = NLSync({ 'patron' => $borrower_from_dbic });
178
179
or a plain old borrowernumber:
180
181
    my $result = NLSync({ 'borrowernumber' => $borrowernumber });
182
183
In the latter case, this function will retrieve the patron record from the
184
database using DBIC.
185
186
Which part of the API is called depends on the value of the "syncstatus" column:
187
188
=over 4
189
190
=item * B<new> = The I<nyPost> ("new record") method is called.
191
192
=item * B<edited> = The I<endre> ("change/update") method is called.
193
194
=item * B<delete> = The I<slett> ("delete") method is called.
195
196
=back
197
198
Required values for B<new> and B<edited>:
199
200
=over 4
201
202
=item * sist_endret (last updated)
203
204
=item * adresse, postnr eller sted (address, zip or city)
205
206
=item * fdato (birthdate)
207
208
=item * fnr_hash (social security number, but not hashed...)
209
210
=item * kjonn (gender, M/F)
211
212
=back
213
214
=cut
215
216
sub NLSync {
217
218
    my ( $input ) = @_;
219
220
    my $patron;
221
    if ( defined $input->{'borrowernumber'} ) {
222
        $patron = Koha::Database->new->schema->resultset('Borrower')->find( $input->{'borrowernumber'} );
223
    } elsif ( defined $input->{'patron'} ) {
224
        $patron = $input->{'patron'};
225
    }
226
227
    # There should only be one sync, so we use the first one
228
    my @syncs = $patron->borrower_syncs;
229
    my $sync;
230
    foreach my $this_sync ( @syncs ) {
231
        if ( $this_sync->synctype eq 'norwegianpatrondb' ) {
232
            $sync = $this_sync;
233
        }
234
    }
235
236
    my $client = SOAP::Lite
237
        ->on_action( sub { return '""';})
238
        ->uri( $nl_uri )
239
        ->proxy( $nl_proxy );
240
241
    my $cardnumber = SOAP::Data->name( 'lnr' => $patron->cardnumber );
242
243
    # Call the appropriate method based on syncstatus
244
    my $response;
245
    if ( $sync->syncstatus eq 'edited' || $sync->syncstatus eq 'new' ) {
246
        my $soap_patron = _koha_patron_to_soap( $patron );
247
        if ( $sync->syncstatus eq 'edited' ) {
248
            $response = $client->endre( $cardnumber, $soap_patron );
249
        } elsif ( $sync->syncstatus eq 'new' ) {
250
            $response = $client->nyPost( $soap_patron );
251
        }
252
    }
253
    if ( $sync->syncstatus eq 'delete' ) {
254
        $response = $client->slett( $cardnumber );
255
    }
256
257
    # Update the sync data according to the results
258
    if ( $response->{'status'} && $response->{'status'} == 1 ) {
259
        if ( $sync->syncstatus eq 'delete' ) {
260
            # Turn off any further syncing
261
            $sync->update( { 'sync' => 0 } );
262
        }
263
        # Update the syncstatus to 'synced'
264
        $sync->update( { 'syncstatus' => 'synced' } );
265
        # Update the 'synclast' attribute with the "server time" ("server_tid") returned by the method
266
        $sync->update( { 'lastsync' => $response->{'server_tid'} } );
267
    }
268
    return $response;
269
270
}
271
272
=head2 NLGetChanged
273
274
Fetches patrons from NL that have been changed since a given timestamp. This includes
275
patrons that have been changed by the library that runs the sync, so we have to
276
check which library was the last one to change a patron, before we update patrons
277
locally.
278
279
This is supposed to be executed once per night.
280
281
SOAP call: soekEndret
282
283
=cut
284
285
sub NLGetChanged {
286
287
    my ( $from_arg ) = @_;
288
289
    my $client = SOAP::Lite
290
        ->on_action( sub { return '""';})
291
        ->uri( $nl_uri )
292
        ->proxy( $nl_proxy );
293
294
    my $from_string;
295
    if ( $from_arg && $from_arg ne '' ) {
296
        $from_string = $from_arg;
297
    } else {
298
        # Calculate 1 second past midnight of the day before
299
        my $dt = DateTime->now( time_zone => 'Europe/Oslo' );
300
        $dt->subtract( days => 1 );
301
        my $from = DateTime->new(
302
            year       => $dt->year(),
303
            month      => $dt->month(),
304
            day        => $dt->day(),
305
            hour       => 0,
306
            minute     => 0,
307
            second     => 1,
308
            time_zone  => 'Europe/Oslo',
309
        );
310
        $from_string = $from->ymd . "T" . $from->hms;
311
    }
312
313
    my $timestamp   = SOAP::Data->name( 'tidspunkt'    => $from_string );
314
    my $max_results = SOAP::Data->name( 'max_antall'   => 0 ); # 0 = no limit
315
    my $start_index = SOAP::Data->name( 'start_indeks' => 0 ); # 1 is the first record
316
317
    # Call the appropriate method based on syncstatus
318
    my $som = $client->soekEndret( $timestamp, $max_results, $start_index );
319
320
    # Extract and massage patron data
321
    my $result = $som->result;
322
    foreach my $patron ( @{ $result->{'respons_poster'} } ) {
323
        # Only handle patrons that have lnr (barcode) and fnr_hash (social security number)
324
        # Patrons that lack these two have been deleted from NL
325
        if ( $patron->{'lnr'} && $patron->{'fnr_hash'} ) {
326
            push @{ $result->{'kohapatrons'} }, _soap_to_kohapatron( $patron );
327
        }
328
    }
329
    return $result;
330
331
}
332
333
=head2 NLMarkForDeletion
334
335
Mark a borrower for deletion, but do not do the actual deletion. Deleting the
336
borrower from NL will be done later by the nl-sync-from-koha.pl script.
337
338
=cut
339
340
sub NLMarkForDeletion {
341
342
    my ( $borrowernumber ) = @_;
343
344
    my $borrowersync = Koha::Database->new->schema->resultset('BorrowerSync')->find({
345
        'synctype'       => 'norwegianpatrondb',
346
        'borrowernumber' => $borrowernumber,
347
    });
348
    return $borrowersync->update( { 'syncstatus' => 'delete' } );
349
350
}
351
352
=head2 NLDecodePin
353
354
Takes a string encoded with AES/ECB/PKCS5PADDING and a 128-bits key, and returns
355
the decoded string as plain text.
356
357
The key needs to be stored in koha-conf.xml, like so:
358
359
<yazgfs>
360
  ...
361
  <config>
362
    ...
363
    <nlkey>xyz</nlkey>
364
  </config>
365
</yazgfs>
366
367
=cut
368
369
sub NLDecodePin {
370
371
    my ( $hash ) = @_;
372
    my $key = C4::Context->config( 'nlkey' );
373
374
    # Convert the hash from Base16
375
    my $cb = Convert::BaseN->new( base => 16 );
376
    my $decoded_hash = $cb->decode( $hash );
377
378
    # Do the decryption
379
    my $cipher = Crypt::GCrypt->new(
380
        type      => 'cipher',
381
        algorithm => 'aes',
382
        mode      => 'ecb',
383
        padding   => 'standard', # "This is also known as PKCS#5"
384
    );
385
    $cipher->start( 'decrypting' );
386
    $cipher->setkey( $key ); # Must be called after start()
387
    my $plaintext  = $cipher->decrypt( $decoded_hash );
388
    $plaintext .= $cipher->finish;
389
390
    return $plaintext;
391
392
}
393
394
=head2 NLEncryptPIN
395
396
Takes a plain text PIN as argument, returns the encrypted PIN, according to the
397
NL specs.
398
399
    my $encrypted_pin = NLEncryptPIN( $plain_text_pin );
400
401
=cut
402
403
sub NLEncryptPIN {
404
405
    my ( $pin ) = @_;
406
    return _encrypt_pin( $pin );
407
408
}
409
410
=head2 NLUpdateHashedPIN
411
412
Takes two arguments:
413
414
=over 4
415
416
=item * Borrowernumber
417
418
=item * Clear text PIN code
419
420
=back
421
422
Hashes the password and saves it in borrower_sync.hashed_pin.
423
424
=cut
425
426
sub NLUpdateHashedPIN {
427
428
    my ( $borrowernumber, $pin ) = @_;
429
    my $borrowersync = Koha::Database->new->schema->resultset('BorrowerSync')->find({
430
        'synctype'       => 'norwegianpatrondb',
431
        'borrowernumber' => $borrowernumber,
432
        });
433
    return $borrowersync->update({ 'hashed_pin', _encrypt_pin( $pin ) });
434
435
}
436
437
=head2 _encrypt_pin
438
439
Takes a plain text PIN and returns the encrypted version, according to the NL specs.
440
441
=cut
442
443
sub _encrypt_pin {
444
445
    my ( $pin ) = @_;
446
    my $key = C4::Context->config( 'nlkey' );
447
448
    # Do the encryption
449
    my $cipher = Crypt::GCrypt->new(
450
        type      => 'cipher',
451
        algorithm => 'aes',
452
        mode      => 'ecb',
453
        padding   => 'standard', # "This is also known as PKCS#5"
454
    );
455
    $cipher->start( 'encrypting' );
456
    $cipher->setkey( $key ); # Must be called after start()
457
    my $ciphertext  = $cipher->encrypt( $pin );
458
    $ciphertext .= $cipher->finish;
459
460
    # Encode as Bas16
461
    my $cb = Convert::BaseN->new( base => 16 );
462
    my $encoded_ciphertext = $cb->encode( $ciphertext );
463
464
    return $encoded_ciphertext;
465
466
}
467
468
=head2 NLGetSyncDataFromBorrowernumber
469
470
Takes a borrowernumber as argument, returns a Koha::Schema::Result::BorrowerSync
471
object.
472
473
    my $syncdata = NLGetSyncDataFromBorrowernumber( $borrowernumber );
474
475
=cut
476
477
sub NLGetSyncDataFromBorrowernumber {
478
479
    my ( $borrowernumber ) = @_;
480
    my $data = Koha::Database->new->schema->resultset('BorrowerSync')->find({
481
        'synctype'       => 'norwegianpatrondb',
482
        'borrowernumber' => $borrowernumber,
483
    });
484
    return $data;
485
486
}
487
488
=head2 NLGetFirstname
489
490
Takes a string like "Surname, Firstname" and returns the "Firstname" part.
491
492
If there is no comma, the string is returned unaltered.
493
494
    my $firstname = NLGetFirstname( $name );
495
496
=cut
497
498
sub NLGetFirstname {
499
500
    my ( $s ) = @_;
501
    my ( $surname, $firstname ) = _split_name( $s );
502
    if ( $surname eq $s ) {
503
        return $s;
504
    } else {
505
        return $firstname;
506
    }
507
508
}
509
510
=head2 NLGetSurname
511
512
Takes a string like "Surname, Firstname" and returns the "Surname" part.
513
514
If there is no comma, the string is returned unaltered.
515
516
    my $surname = NLGetSurname( $name );
517
518
=cut
519
520
sub NLGetSurname {
521
522
    my ( $s ) = @_;
523
    my ( $surname, $firstname ) = _split_name( $s );
524
    return $surname;
525
526
}
527
528
=head2 _split_name
529
530
Takes a string like "Surname, Firstname" and returns a list of surname and firstname.
531
532
If there is no comma, the string is returned unaltered.
533
534
    my ( $surname, $firstname ) = _split_name( $name );
535
536
=cut
537
538
sub _split_name {
539
540
    my ( $s ) = @_;
541
542
    # Return the string if there is no comma
543
    unless ( $s =~ m/,/ ) {
544
        return $s;
545
    }
546
547
    my ( $surname, $firstname ) = split /, /, $s;
548
549
    return ( $surname, $firstname );
550
551
}
552
553
=head2 _format_soap_error
554
555
Takes a soap result object as input and returns a formatted string containing SOAP error data.
556
557
=cut
558
559
sub _format_soap_error {
560
561
    my ( $result ) = @_;
562
    if ( $result ) {
563
        return join ', ', $result->faultcode, $result->faultstring, $result->faultdetail;
564
    } else {
565
        return 'No result';
566
    }
567
568
}
569
570
=head2 _soap_to_koha_patron
571
572
Convert a SOAP object of type "Laaner" into a hash that can be sent to AddMember or ModMember.
573
574
=cut
575
576
sub _soap_to_kohapatron {
577
578
    my ( $soap ) = @_;
579
580
    return {
581
        'cardnumber'      => $soap->{ 'lnr' },
582
        'surname'         => NLGetSurname(   $soap->{ 'navn' } ),
583
        'firstname'       => NLGetFirstname( $soap->{ 'navn' } ),
584
        'sex'             => $soap->{ 'kjonn' },
585
        'dateofbirth'     => $soap->{ 'fdato' },
586
        'address'         => $soap->{ 'p_adresse1' },
587
        'address2'        => $soap->{ 'p_adresse2' },
588
        'zipcode'         => $soap->{ 'p_postnr' },
589
        'city'            => $soap->{ 'p_sted' },
590
        'country'         => $soap->{ 'p_land' },
591
        'b_address'       => $soap->{ 'm_adresse1' },
592
        'b_address2'      => $soap->{ 'm_adresse2' },
593
        'b_zipcode'       => $soap->{ 'm_postnr' },
594
        'b_city'          => $soap->{ 'm_sted' },
595
        'b_country'       => $soap->{ 'm_land' },
596
        'password'        => $soap->{ 'pin' },
597
        'dateexpiry'      => $soap->{ 'gyldig_til' },
598
        'email'           => $soap->{ 'epost' },
599
        'mobile'          => $soap->{ 'tlf_mobil' },
600
        'phone'           => $soap->{ 'tlf_hjemme' },
601
        'phonepro'        => $soap->{ 'tlf_jobb' },
602
        '_extra'          => { # Data that should not go in the borrowers table
603
            'socsec'         => $soap->{ 'fnr_hash' },
604
            'created'        => $soap->{ 'opprettet' },
605
            'created_by'     => $soap->{ 'opprettet_av' },
606
            'last_change'    => $soap->{ 'sist_endret' },
607
            'last_change_by' => $soap->{ 'sist_endret_av' },
608
        },
609
    };
610
611
}
612
613
=head2 _koha_patron_to_soap
614
615
Convert a patron (in the form of a Koha::Schema::Result::Borrower) into a SOAP
616
object that can be sent to NL.
617
618
=cut
619
620
sub _koha_patron_to_soap {
621
622
    my ( $patron ) = @_;
623
624
    # Extract attributes
625
    my $patron_attributes = {};
626
    foreach my $attribute ( $patron->borrower_attributes ) {
627
        $patron_attributes->{ $attribute->code->code } = $attribute->attribute;
628
    }
629
630
    # There should only be one sync, so we use the first one
631
    my @syncs = $patron->borrower_syncs;
632
    my $sync = $syncs[0];
633
634
    # Create SOAP::Data object
635
    my $soap_patron = SOAP::Data->name(
636
        'post' => \SOAP::Data->value(
637
            SOAP::Data->name( 'lnr'         => $patron->cardnumber ),
638
            SOAP::Data->name( 'fnr_hash'    => $patron_attributes->{ 'fnr' } )->type( 'string' )->type( 'string' ),
639
            SOAP::Data->name( 'navn'        => $patron->surname . ', ' . $patron->firstname    )->type( 'string' ),
640
            SOAP::Data->name( 'sist_endret' => $sync->lastsync      )->type( 'string' ),
641
            SOAP::Data->name( 'kjonn'       => $patron->sex         )->type( 'string' ),
642
            SOAP::Data->name( 'fdato'       => $patron->dateofbirth )->type( 'string' ),
643
            SOAP::Data->name( 'p_adresse1'  => $patron->address     )->type( 'string' ),
644
            SOAP::Data->name( 'p_adresse2'  => $patron->address2    )->type( 'string' ),
645
            SOAP::Data->name( 'p_postnr'    => $patron->zipcode     )->type( 'string' ),
646
            SOAP::Data->name( 'p_sted'      => $patron->city        )->type( 'string' ),
647
            SOAP::Data->name( 'p_land'      => $patron->country     )->type( 'string' ),
648
            SOAP::Data->name( 'm_adresse1'  => $patron->b_address   )->type( 'string' ),
649
            SOAP::Data->name( 'm_adresse2'  => $patron->b_address2  )->type( 'string' ),
650
            SOAP::Data->name( 'm_postnr'    => $patron->b_zipcode   )->type( 'string' ),
651
            SOAP::Data->name( 'm_sted'      => $patron->b_city      )->type( 'string' ),
652
            SOAP::Data->name( 'm_land'      => $patron->b_country   )->type( 'string' ),
653
            # Do not send the PIN code as it has been hashed by Koha, but use the version hashed according to NL
654
            SOAP::Data->name( 'pin'         => $sync->hashed_pin    )->type( 'string' ),
655
            SOAP::Data->name( 'gyldig_til'  => $patron->dateexpiry  )->type( 'string' ),
656
            SOAP::Data->name( 'epost'       => $patron->email       )->type( 'string' ),
657
            SOAP::Data->name( 'tlf_mobil'   => $patron->mobile      )->type( 'string' ),
658
            SOAP::Data->name( 'tlf_hjemme'  => $patron->phone       )->type( 'string' ),
659
            SOAP::Data->name( 'tlf_jobb'    => $patron->phonepro    )->type( 'string' ),
660
        ),
661
    )->type("Laaner");
662
663
    return $soap_patron;
664
665
}
666
667
=head1 EXPORT
668
669
None by default.
670
671
=head1 AUTHOR
672
673
Magnus Enger <digitalutvikling@gmail.com>
674
675
=cut
676
677
1;
678
679
__END__
(-)a/installer/data/mysql/kohastructure.sql (+18 lines)
Lines 357-362 CREATE TABLE `branch_item_rules` ( -- information entered in the circulation and Link Here
357
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
357
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
358
358
359
--
359
--
360
-- Table structure for table borrower_sync
361
--
362
363
CREATE TABLE borrower_sync (
364
  borrowersyncid int(11) NOT NULL AUTO_INCREMENT, -- Primary key, unique identifier
365
  borrowernumber int(11) NOT NULL, -- Connects data about synchronisations to a borrower
366
  synctype varchar(32) NOT NULL, -- There could potentially be more than one kind of syncing going on, a text string here can be used to tell them apart. E.g.: The Norwegian national patron database uses 'norwegianpatrondb' in this column
367
  sync tinyint(1) NOT NULL DEFAULT '0', -- A boolean (1/0) for turning syncing off and on for individual borrowers
368
  syncstatus varchar(10) DEFAULT NULL, -- The sync status for any given borrower. Could be text strings like 'new', 'edited', 'synced' etc. The values used here will depend on the actual syncing being done.
369
  lastsync varchar(50) DEFAULT NULL, -- Date of the last successfull sync. The date format might be different depending on the service that is being used, so no special date format is being enforced here.
370
  hashed_pin varchar(64) DEFAULT NULL, -- Perhaps specific to The Norwegian national patron database, this column holds a hashed PIN code
371
  PRIMARY KEY (borrowersyncid),
372
  KEY borrowernumber (borrowernumber),
373
  CONSTRAINT borrower_sync_ibfk_1 FOREIGN KEY (borrowernumber) REFERENCES borrowers (borrowernumber) ON DELETE CASCADE ON UPDATE CASCADE
374
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
375
376
377
--
360
-- Table structure for table `branchcategories`
378
-- Table structure for table `branchcategories`
361
--
379
--
362
380
(-)a/installer/data/mysql/sysprefs.sql (+5 lines)
Lines 197-202 INSERT INTO systempreferences ( `variable`, `value`, `options`, `explanation`, ` Link Here
197
('noissuescharge','5','','Define maximum amount withstanding before check outs are blocked','Integer'),
197
('noissuescharge','5','','Define maximum amount withstanding before check outs are blocked','Integer'),
198
('noItemTypeImages','0',NULL,'If ON, disables item-type images','YesNo'),
198
('noItemTypeImages','0',NULL,'If ON, disables item-type images','YesNo'),
199
('NoLoginInstructions', '', '60|10', 'Instructions to display on the OPAC login form when a patron is not logged in', 'Textarea'),
199
('NoLoginInstructions', '', '60|10', 'Instructions to display on the OPAC login form when a patron is not logged in', 'Textarea'),
200
('NorwegianPatronDBEnable','0',NULL,'Enable communication with the Norwegian national patron database.', 'YesNo'),
201
('NorwegianPatronDBEndpoint','',NULL,'Which NL endpoint to use.', 'Free'),
202
('NorwegianPatronDBUsername','',NULL,'Username for communication with the Norwegian national patron database.','Free'),
203
('NorwegianPatronDBPassword','',NULL,'Password for communication with the Norwegian national patron database.','Free'),
204
('NorwegianPatronDBSearchNLAfterLocalHit','0',NULL,'Search NL if a search has already given one or more local hits?.','YesNo'),
200
('NotesBlacklist','',NULL,'List of notes fields that should not appear in the title notes/description separator of details','free'),
205
('NotesBlacklist','',NULL,'List of notes fields that should not appear in the title notes/description separator of details','free'),
201
('NotHighlightedWords','and|or|not',NULL,'List of words to NOT highlight when OpacHitHighlight is enabled','free'),
206
('NotHighlightedWords','and|or|not',NULL,'List of words to NOT highlight when OpacHitHighlight is enabled','free'),
202
('NoticeCSS','',NULL,'Notices CSS url.','free'),
207
('NoticeCSS','',NULL,'Notices CSS url.','free'),
(-)a/installer/data/mysql/updatedatabase.pl (+25 lines)
Lines 8835-8840 if ( CheckVersion($DBversion) ) { Link Here
8835
        ('UsageStats', 0, NULL, 'Share anonymous usage data on the Hea Koha community website.', 'YesNo')
8835
        ('UsageStats', 0, NULL, 'Share anonymous usage data on the Hea Koha community website.', 'YesNo')
8836
    });
8836
    });
8837
    print "Upgrade to $DBversion done (Bug 11926: Add UsageStats systempreferences (HEA))\n";
8837
    print "Upgrade to $DBversion done (Bug 11926: Add UsageStats systempreferences (HEA))\n";
8838
    SetVersion ($DBversion);
8839
}
8840
8841
$DBversion = "3.17.00.XXX";
8842
if ( C4::Context->preference("Version") < TransformToNum($DBversion) ) {
8843
    $dbh->do("INSERT IGNORE INTO systempreferences (variable,value,options,explanation,type) VALUES ('NorwegianPatronDBEnable', '0', NULL, 'Enable communication with the Norwegian national patron database.', 'YesNo')");
8844
    $dbh->do("INSERT IGNORE INTO systempreferences (variable,value,options,explanation,type) VALUES ('NorwegianPatronDBEndpoint', '', NULL, 'Which NL endpoint to use.', 'Free')");
8845
    $dbh->do("INSERT IGNORE INTO systempreferences (variable,value,options,explanation,type) VALUES ('NorwegianPatronDBUsername', '', NULL, 'Username for communication with the Norwegian national patron database.', 'Free')");
8846
    $dbh->do("INSERT IGNORE INTO systempreferences (variable,value,options,explanation,type) VALUES ('NorwegianPatronDBPassword', '', NULL, 'Password for communication with the Norwegian national patron database.', 'Free')");
8847
    $dbh->do("INSERT IGNORE INTO systempreferences (variable,value,options,explanation,type) VALUES ('NorwegianPatronDBSearchNLAfterLocalHit','0',NULL,'Search NL if a search has already given one or more local hits?.','YesNo')");
8848
    $dbh->do("
8849
CREATE TABLE borrower_sync (
8850
    borrowersyncid int(11) NOT NULL AUTO_INCREMENT,
8851
    borrowernumber int(11) NOT NULL,
8852
    synctype varchar(32) NOT NULL,
8853
    sync tinyint(1) NOT NULL DEFAULT '0',
8854
    syncstatus varchar(10) DEFAULT NULL,
8855
    lastsync varchar(50) DEFAULT NULL,
8856
    hashed_pin varchar(64) DEFAULT NULL,
8857
    PRIMARY KEY (borrowersyncid),
8858
    KEY borrowernumber (borrowernumber),
8859
    CONSTRAINT borrower_sync_ibfk_1 FOREIGN KEY (borrowernumber) REFERENCES borrowers (borrowernumber) ON DELETE CASCADE ON UPDATE CASCADE
8860
) ENGINE=InnoDB DEFAULT CHARSET=utf8"
8861
);
8862
    print "Upgrade to $DBversion done (Bug 11401 - Add support for Norwegian national library card)\n";
8838
    SetVersion($DBversion);
8863
    SetVersion($DBversion);
8839
}
8864
}
8840
8865
(-)a/koha-tmpl/intranet-tmpl/prog/en/includes/members-toolbar.inc (-2 / +46 lines)
Lines 1-12 Link Here
1
[% USE Koha %]
2
[% SET NorwegianPatronDBEnable = Koha.Preference( 'NorwegianPatronDBEnable' ) %]
1
<script type="text/javascript">
3
<script type="text/javascript">
2
//<![CDATA[
4
//<![CDATA[
3
$(document).ready(function(){
5
$(document).ready(function(){
4
    [% IF ( CAN_user_borrowers ) %]
6
    [% IF ( CAN_user_borrowers ) %]
5
        $("#deletepatron").click(function(){
7
        [% IF ( NorwegianPatronDBEnable == 1 ) %]
8
            $("#deletepatronlocal").click(function(){
9
                confirm_local_deletion();
10
                $(".btn-group").removeClass("open");
11
                return false;
12
            });
13
            $("#deletepatronremote").click(function(){
14
                confirm_remote_deletion();
15
                $(".btn-group").removeClass("open");
16
                return false;
17
            });
18
            $("#deletepatronboth").click(function(){
19
                confirm_both_deletion();
20
                $(".btn-group").removeClass("open");
21
                return false;
22
            });
23
        [% ELSE %]
24
            $("#deletepatron").click(function(){
6
                confirm_deletion();
25
                confirm_deletion();
7
                $(".btn-group").removeClass("open");
26
                $(".btn-group").removeClass("open");
8
                return false;
27
                return false;
9
            });
28
            });
29
        [% END %]
10
        $("#renewpatron").click(function(){
30
        $("#renewpatron").click(function(){
11
            confirm_reregistration();
31
            confirm_reregistration();
12
            $(".btn-group").removeClass("open");
32
            $(".btn-group").removeClass("open");
Lines 49-54 function confirm_deletion() { Link Here
49
        window.location='/cgi-bin/koha/members/deletemem.pl?member=[% borrowernumber %]';
69
        window.location='/cgi-bin/koha/members/deletemem.pl?member=[% borrowernumber %]';
50
    }
70
    }
51
}
71
}
72
function confirm_local_deletion() {
73
    var is_confirmed = window.confirm(_("Are you sure you want to delete this patron from the local database? This cannot be undone."));
74
    if (is_confirmed) {
75
        window.location='/cgi-bin/koha/members/deletemem.pl?member=[% borrowernumber %]&deletelocal=true&deleteremote=false';
76
    }
77
}
78
function confirm_remote_deletion() {
79
    var is_confirmed = window.confirm(_("Are you sure you want to delete this patron from the Norwegian national patron database? This cannot be undone."));
80
    if (is_confirmed) {
81
        window.location='/cgi-bin/koha/members/deletemem.pl?member=[% borrowernumber %]&deletelocal=false&deleteremote=true';
82
    }
83
}
84
function confirm_both_deletion() {
85
    var is_confirmed = window.confirm(_("Are you sure you want to delete this patron both from the local database and from the Norwegian national patron database? This cannot be undone."));
86
    if (is_confirmed) {
87
        window.location='/cgi-bin/koha/members/deletemem.pl?member=[% borrowernumber %]&deletelocal=true&deleteremote=true';
88
    }
89
}
52
90
53
[% IF ( is_child ) %]function confirm_updatechild() {
91
[% IF ( is_child ) %]function confirm_updatechild() {
54
    var is_confirmed = window.confirm(_("Are you sure you want to update this child to an Adult category?  This cannot be undone."));
92
    var is_confirmed = window.confirm(_("Are you sure you want to update this child to an Adult category?  This cannot be undone."));
Lines 141-147 function searchToHold(){ Link Here
141
                    <li class="disabled"><a data-toggle="tooltip" data-placement="left" title="You are not authorized to set permissions" id="patronflags" href="#">Set permissions</a></li>
179
                    <li class="disabled"><a data-toggle="tooltip" data-placement="left" title="You are not authorized to set permissions" id="patronflags" href="#">Set permissions</a></li>
142
                [% END %]
180
                [% END %]
143
                [% IF ( CAN_user_borrowers ) %]
181
                [% IF ( CAN_user_borrowers ) %]
144
                    <li><a id="deletepatron" href="#">Delete</a></li>
182
                    [% IF ( NorwegianPatronDBEnable == 1 ) %]
183
                        <li><a id="deletepatronlocal" href="#">Delete local</a></li>
184
                        <li><a id="deletepatronremote" href="#">Delete remote</a></li>
185
                        <li><a id="deletepatronboth" href="#">Delete local and remote</a></li>
186
                    [% ELSE %]
187
                        <li><a id="deletepatron" href="#">Delete</a></li>
188
                    [% END %]
145
                [% ELSE %]
189
                [% ELSE %]
146
                    <li class="disabled"><a data-toggle="tooltip" data-placement="left" title="You are not authorized to delete patrons" id="deletepatron" href="#">Delete</a></li>
190
                    <li class="disabled"><a data-toggle="tooltip" data-placement="left" title="You are not authorized to delete patrons" id="deletepatron" href="#">Delete</a></li>
147
                [% END %]
191
                [% END %]
(-)a/koha-tmpl/intranet-tmpl/prog/en/includes/nl-search-form.tt (+9 lines)
Line 0 Link Here
1
<form method="POST" action="nl-search.pl" autocomplete="off"> <!-- This form will be used for things like social security numbers, so it makes sense not to remember them. -->
2
<input type="hidden" name="op" value="search" />
3
    <fieldset>
4
        <legend>[% nl_search_form_title %]</legend>
5
        <label for="q">Social security or card number: </label>
6
        <input type="text" name="q" value="[% q %]">
7
        <input type="submit" value="Search">
8
    </fieldset>
9
</form>
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/admin/preferences/patrons.pref (+22 lines)
Lines 1-4 Link Here
1
Patrons:
1
Patrons:
2
    General:
2
     -
3
     -
3
         - List
4
         - List
4
         - pref: AddPatronLists
5
         - pref: AddPatronLists
Lines 138-140 Patrons: Link Here
138
         - pref: CardnumberLength
139
         - pref: CardnumberLength
139
         - "characters long. The length can be a single number to specify an exact length, a range separated by a comma (i.e., 'Min,Max'), or a maximum with no minimum (i.e., ',Max')."
140
         - "characters long. The length can be a single number to specify an exact length, a range separated by a comma (i.e., 'Min,Max'), or a maximum with no minimum (i.e., ',Max')."
140
         - "If 'cardnumber' is included in the BorrowerMandatoryField list, the minimum length, if not specified here, defaults to one."
141
         - "If 'cardnumber' is included in the BorrowerMandatoryField list, the minimum length, if not specified here, defaults to one."
142
    Norwegian patron database:
143
    -
144
         - pref: NorwegianPatronDBEnable
145
           choices:
146
               yes: Enable
147
               no: Disable
148
         - the ability to communicate with the Norwegian national patron database via the
149
         - pref: NorwegianPatronDBEndpoint
150
         - endpoint.
151
    -
152
         - Communicate with the Norwegian national patron database using the username
153
         - pref: NorwegianPatronDBUsername
154
         - and the password
155
         - pref: NorwegianPatronDBPassword
156
         - . You can get these from "Base Bibliotek", which is maintained by the Norwegian National Library.
157
    -
158
         - pref: NorwegianPatronDBSearchNLAfterLocalHit
159
           choices:
160
               yes: Do
161
               no: "Don't"
162
         - search the Norwegian national patron database after a local search result was found.
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/members/deletemem.tt (-16 / +26 lines)
Lines 10-30 Link Here
10
<div id="doc3" class="yui-t2">
10
<div id="doc3" class="yui-t2">
11
   
11
   
12
   <div id="bd">
12
   <div id="bd">
13
	<div id="yui-main">
13
    <div id="yui-main">
14
	<div class="yui-b">
14
    <div class="yui-b">
15
	[% INCLUDE 'members-toolbar.inc' %]
15
    [% INCLUDE 'members-toolbar.inc' %]
16
	<div class="dialog alert">
16
    [% IF ( ItemsOnIssues || charges || guarantees ) %]
17
    <h3>Cannot delete patron</h3>
17
        <div class="dialog alert">
18
		<ul>[% IF ( ItemsOnIssues ) %]
18
        <h3>Cannot delete patron</h3>
19
			<li>Patron has [% ItemsOnIssues %] item(s) checked out.</li>
19
            <ul>
20
		[% END %]
20
            [% IF ( ItemsOnIssues ) %]
21
		[% IF ( charges ) %]
21
                <li>Patron has [% ItemsOnIssues %] item(s) checked out.</li>
22
			<li>Patron has [% charges %] in fines.</li>
22
            [% END %]
23
		[% END %]
23
            [% IF ( charges ) %]
24
		[% IF ( guarantees ) %]
24
                <li>Patron has [% charges %] in fines.</li>
25
			<li>Patron's record has guaranteed accounts attached.</li>
25
            [% END %]
26
		[% END %]</ul>
26
            [% IF ( guarantees ) %]
27
	</div>
27
                <li>Patron's record has guaranteed accounts attached.</li>
28
            [% END %]
29
            </ul>
30
    </div>
31
    [% END %]
32
    [% IF ( keeplocal ) %]
33
        <div class="dialog message">
34
        <h3>Remote record deleted, local record kept</h3>
35
        <p>Patron was marked for deletion from Norwegian national patron database, but the local record was kept.</p>
36
        </div>
37
    [% END %]
28
</div>
38
</div>
29
</div>
39
</div>
30
40
Lines 32-35 Link Here
32
[% INCLUDE 'circ-menu.inc' %]
42
[% INCLUDE 'circ-menu.inc' %]
33
</div>
43
</div>
34
</div>
44
</div>
35
[% INCLUDE 'intranet-bottom.inc' %]
45
[% INCLUDE 'intranet-bottom.inc' %]
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/members/member.tt (+6 lines)
Lines 1-3 Link Here
1
[% USE Koha %]
1
[% INCLUDE 'doc-head-open.inc' %]
2
[% INCLUDE 'doc-head-open.inc' %]
2
<title>Koha &rsaquo; Patrons [% IF ( searching ) %]&rsaquo; Search results[% END %]</title>
3
<title>Koha &rsaquo; Patrons [% IF ( searching ) %]&rsaquo; Search results[% END %]</title>
3
[% INCLUDE 'doc-head-close.inc' %]
4
[% INCLUDE 'doc-head-close.inc' %]
Lines 293-298 function filterByFirstLetterSurname(letter) { Link Here
293
            </div>
294
            </div>
294
          [% END %]
295
          [% END %]
295
296
297
          [% IF Koha.Preference( 'NorwegianPatronDBEnable' ) == 1 %]
298
            [% SET nl_search_form_title='Search the Norwegian national patron database' %]
299
            [% INCLUDE 'nl-search-form.tt' %]
300
          [% END %]
301
296
          [% INCLUDE 'patron-toolbar.inc' %]
302
          [% INCLUDE 'patron-toolbar.inc' %]
297
          [% IF ( no_add ) %]
303
          [% IF ( no_add ) %]
298
            <div class="dialog alert">
304
            <div class="dialog alert">
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/members/memberentrygen.tt (+13 lines)
Lines 1-4 Link Here
1
[% IF ( opduplicate ) %][% SET focusAction = "clearDupe" %][% END %]
1
[% IF ( opduplicate ) %][% SET focusAction = "clearDupe" %][% END %]
2
[% USE Koha %]
2
[% USE KohaDates %]
3
[% USE KohaDates %]
3
[% INCLUDE 'doc-head-open.inc' %]
4
[% INCLUDE 'doc-head-open.inc' %]
4
<title>Koha &rsaquo; Patrons &rsaquo; 
5
<title>Koha &rsaquo; Patrons &rsaquo; 
Lines 973-978 Link Here
973
    [% IF ( mandatorysort2 ) %]<span class="required">Required</span>[% END %]
974
    [% IF ( mandatorysort2 ) %]<span class="required">Required</span>[% END %]
974
    </li>
975
    </li>
975
        [% END %]
976
        [% END %]
977
    [% IF ( Koha.Preference( 'NorwegianPatronDBEnable' ) == 1 ) %]
978
        <li>
979
            <label for="sort2">Sync with the Norwegian national patron database:</label>
980
            [% IF ( sync == 0 ) %]
981
                <input type="radio" id="sync" name="sync" value="1"> Yes
982
                <input type="radio" id="sync" name="sync" value="0" checked> No
983
            [% ELSE %]
984
                <input type="radio" id="sync" name="sync" value="1" checked> Yes
985
                <input type="radio" id="sync" name="sync" value="0"> No
986
            [% END %]
987
        </li>
988
    [% END %]
976
	</ol>
989
	</ol>
977
  </fieldset>
990
  </fieldset>
978
    [% UNLESS nodateenrolled &&  noopacnote && noborrowernotes %]
991
    [% UNLESS nodateenrolled &&  noopacnote && noborrowernotes %]
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/members/moremember.tt (+9 lines)
Lines 352-357 function validate1(date) { Link Here
352
    </li>
352
    </li>
353
    [% IF ( borrowernotes ) %]<li><span class="label">Circulation note: </span>[% borrowernotes %]</li>[% END %]
353
    [% IF ( borrowernotes ) %]<li><span class="label">Circulation note: </span>[% borrowernotes %]</li>[% END %]
354
    [% IF ( opacnote ) %]<li><span class="label">OPAC note:</span>[% opacnote %]</li>[% END %]
354
    [% IF ( opacnote ) %]<li><span class="label">OPAC note:</span>[% opacnote %]</li>[% END %]
355
    [% IF Koha.Preference( 'NorwegianPatronDBEnable' ) == 1 %]
356
        [% IF ( sync == 1 ) %]
357
            <li><span class="label">Activate sync: </span>Yes</li>
358
            [% IF ( syncstatus ) %]<li><span class="label">Sync status: </span>[% syncstatus %]</li>[% END %]
359
            [% IF ( lastsync ) %]<li><span class="label">Last sync: </span>[% lastsync | $KohaDates %]</li>[% END %]
360
        [% ELSE %]
361
            <li><span class="label">Activate sync: </span>No</li>
362
        [% END %]
363
    [% END %]
355
	</ol>
364
	</ol>
356
	</div>
365
	</div>
357
 </div>
366
 </div>
(-)a/koha-tmpl/intranet-tmpl/prog/en/modules/members/nl-search.tt (+176 lines)
Line 0 Link Here
1
[% USE KohaDates %]
2
[% INCLUDE 'doc-head-open.inc' %]
3
<title>Search the Norwegian national patron database</title>
4
[% INCLUDE 'doc-head-close.inc' %]
5
</head>
6
<body>
7
[% INCLUDE 'header.inc' %]
8
[% INCLUDE 'patron-search.inc' %]
9
10
<div id="breadcrumbs"><a href="/cgi-bin/koha/mainpage.pl">Home</a> &rsaquo; <a href="/cgi-bin/koha/members/members-home.pl">Patrons</a>  &rsaquo; Search the Norwegian national patron database</div>
11
12
<div id="doc3" class="yui-t2">
13
    <div id="bd">
14
        <div id="yui-main">
15
            <div class="yui-b">
16
17
                <h1>Search the Norwegian national patron database</h1>
18
19
                [% IF (error) %]
20
                    <div class="dialog alert">
21
                    [% IF ( error.nlenabled == 0 ) %]<p>You need to activate this function with the NorwegianPatronDBEnable system preference in order to use it.</p>[% END %]
22
                    [% IF ( error.endpoint  == 0 ) %]<p>You need to specify an endpoint with the NorwegianPatronDBEndpoint system preference.</p>[% END %]
23
                    [% IF ( error.userpass  == 0 ) %]<p>You need to fill in the NorwegianPatronDBUsername and NorwegianPatronDBPassword system preferences in order to use this function.</p>[% END %]
24
                    [% IF ( error == 'COULD_NOT_ADD_PATRON' ) %]<p>Could not add a new patron.</p>[% END %]
25
                    </div>
26
                [% ELSE %]
27
                    [% SET nl_search_form_title='Search' %]
28
                    [% INCLUDE 'nl-search-form.tt' %]
29
                [% END %]
30
31
                [% IF ( local_result ) %]
32
                    <h3>Existing patrons</h3>
33
                    <ul>
34
                        [% FOREACH patron IN local_result %]
35
                            [%# Find the category_type %]
36
                            [% FOREACH category IN categories %]
37
                                [% IF category.categorycode == patron.categorycode %]
38
                                    [% patron.category_type = category.category_type %]
39
                                [% END %]
40
                            [% END %]
41
                            <li>[% patron.firstname %] [% patron.surname %] [% patron.cardnumber %] |
42
                                <a href="/cgi-bin/koha/members/moremember.pl?borrowernumber=[% patron.borrowernumber %]">Details</a> |
43
                                <a href="/cgi-bin/koha/members/memberentry.pl?op=modify&destination=circ&borrowernumber=[% patron.borrowernumber %]&category_type=[% patron.category_type %]">Edit</a> |
44
                                <a href="/cgi-bin/koha/circ/circulation.pl?borrowernumber=[% patron.borrowernumber %]">Check out</a>
45
                            </li>
46
                        [% END %]
47
                    </ul>
48
                [% END %]
49
50
                [% IF ( result ) %]
51
52
                    [% IF result.antall_poster_returnert == 0 %]
53
54
                        <div class="dialog alert">
55
                            <p>No results found in the Norwegian national patron database. Message: "[% result.melding %]"</p>
56
                        </div>
57
58
                    [% ELSE %]
59
60
                        <h3>Results from the Norwegian national patron database</h3>
61
                        <div class="yui-g">
62
                        <div class="yui-u first">
63
                        [% PROCESS patron_detail p=result.respons_poster.0 %]
64
                        </div>
65
                        [% IF ( result.respons_poster.1 ) %]
66
                            <div class="yui-u">
67
                            [% PROCESS patron_detail p=result.respons_poster.1 %]
68
                            </div>
69
                        [% END %]
70
                        </div>
71
72
                    [% END %]
73
74
                [% END %]
75
76
            </div>
77
        </div>
78
79
        <div class="yui-b">
80
            [% INCLUDE 'circ-menu.inc' %]
81
        </div>
82
    </div>
83
[% INCLUDE 'intranet-bottom.inc' %]
84
85
[% BLOCK patron_detail %]
86
<div class="rows">
87
<h4>[% p.navn | html_entity %]</h4>
88
<ol>
89
[% IF ( p.kjonn ) %]<li><span class="label">kjonn: </span>[% p.kjonn | html_entity %]</li>[% END %]
90
[% IF ( p.fdato ) %]<li><span class="label">fdato: </span>[% p.fdato | html_entity %]</li>[% END %]
91
[% IF ( p.lnr ) %]<li><span class="label">lnr: </span>[% p.lnr | html_entity %]</li>[% END %]
92
[% IF ( p.fnr_hash ) %]<li><span class="label">fnr_hash: </span>[% p.fnr_hash | html_entity %]</li>[% END %]
93
94
[% IF ( p.epost ) %]<li><span class="label">epost: </span>[% p.epost | html_entity %]</li>[% END %]
95
[% IF ( p.epost_sjekk ) %]<li><span class="label">epost_sjekk: </span>[% p.epost_sjekk | html_entity %]</li>[% END %]
96
97
[% IF ( p.tlf_mobil ) %]<li><span class="label">tlf_mobil: </span>[% p.tlf_mobil | html_entity %]</li>[% END %]
98
[% IF ( p.tlf_hjemme ) %]<li><span class="label">tlf_hjemme: </span>[% p.tlf_hjemme | html_entity %]</li>[% END %]
99
[% IF ( p.tlf_jobb ) %]<li><span class="label">tlf_jobb: </span>[% p.tlf_jobb | html_entity %]</li>[% END %]
100
101
[% IF ( p.p_adresse1 ) %]<li><span class="label">p_adresse1: </span>[% p.p_adresse1 | html_entity %]</li>[% END %]
102
[% IF ( p.p_adresse2 ) %]<li><span class="label">p_adresse2: </span>[% p.p_adresse2 | html_entity %]</li>[% END %]
103
[% IF ( p.p_postnr ) %]<li><span class="label">p_postnr: </span>[% p.p_postnr | html_entity %]</li>[% END %]
104
[% IF ( p.p_sted ) %]<li><span class="label">p_sted: </span>[% p.p_sted | html_entity %]</li>[% END %]
105
[% IF ( p.p_land ) %]<li><span class="label">p_land: </span>[% p.p_land | html_entity %]</li>[% END %]
106
[% IF ( p.p_sjekk ) %]<li><span class="label">p_sjekk: </span>[% p.p_sjekk | html_entity %]</li>[% END %]
107
108
[% IF ( p.m_adresse1 ) %]<li><span class="label">m_adresse1: </span>[% p.m_adresse1 | html_entity %]</li>[% END %]
109
[% IF ( p.m_adresse2 ) %]<li><span class="label">m_adresse2: </span>[% p.m_adresse2 | html_entity %]</li>[% END %]
110
[% IF ( p.m_postnr ) %]<li><span class="label">m_postnr: </span>[% p.m_postnr | html_entity %]</li>[% END %]
111
[% IF ( p.m_sted ) %]<li><span class="label">m_sted: </span>[% p.m_sted | html_entity %]</li>[% END %]
112
[% IF ( p.m_land ) %]<li><span class="label">m_land: </span>[% p.m_land | html_entity %]</li>[% END %]
113
[% IF ( p.m_sjek ) %]<li><span class="label">m_sjekk: </span>[% p.m_sjekk | html_entity %]</li>[% END %]
114
[% IF ( p.m_gyldig_til ) %]<li><span class="label">m_gyldig_til: </span>[% p.m_gyldig_til | html_entity %]</li>[% END %]
115
116
[% IF ( p.pin ) %]<li><span class="label">pin: </span>[% p.pin | html_entity %]</li>[% END %]
117
[% IF ( p.passord ) %]<li><span class="label">passsord: </span>[% p.passord | html_entity %]</li>[% END %]
118
[% IF ( p.feide ) %]<li><span class="label">feide: </span>[% p.feide | html_entity %]</li>[% END %]
119
[% IF ( p.folkeregsjekk_dato ) %]<li><span class="label">folkeregsjekk_dato: </span>[% p.folkeregsjekk_dato | html_entity %]</li>[% END %]
120
121
[% IF ( p.hjemmebibliotek ) %]<li><span class="label">hjemmebibliotek: </span>[% p.hjemmebibliotek | html_entity %]</li>[% END %]
122
[% IF ( p.opprettet ) %]<li><span class="label">opprettet: </span>[% p.opprettet | html_entity %]</li>[% END %]
123
[% IF ( p.opprettet_av ) %]<li><span class="label">opprettet_av: </span>[% p.opprettet_av | html_entity %]</li>[% END %]
124
[% IF ( p.sist_endret ) %]<li><span class="label">sist_endret: </span>[% p.sist_endret | html_entity %]</li>[% END %]
125
[% IF ( p.sist_endret_av ) %]<li><span class="label">sist_endret_av: </span>[% p.sist_endret_av | html_entity %]</li>[% END %]
126
[% IF ( p.gyldig_til ) %]<li><span class="label">gyldig_til: </span>[% p.gyldig_til | html_entity %]</li>[% END %]
127
128
[% IF ( p.prim_kontakt ) %]<li><span class="label">prim_kontakt: </span>[% p.prim_kontakt | html_entity %]</li>[% END %]
129
</ol>
130
131
<form action="nl-search.pl" method="POST">
132
<input type="hidden" name="op" value="save" />
133
<input type="hidden" name="navn" value="[% p.navn | html_entity %]" />
134
<input type="hidden" name="kjonn" value="[% p.kjonn | html_entity %]" />
135
<input type="hidden" name="fdato" value="[% p.fdato | html_entity %]" />
136
<input type="hidden" name="lnr" value="[% p.lnr | html_entity %]" />
137
<input type="hidden" name="fnr_hash" value="[% p.fnr_hash | html_entity %]" />
138
<input type="hidden" name="p_adresse1" value="[% p.p_adresse1 | html_entity %]" />
139
<input type="hidden" name="p_adresse2" value="[% p.p_adresse2 | html_entity %]" />
140
<input type="hidden" name="p_postnr" value="[% p.p_postnr | html_entity %]" />
141
<input type="hidden" name="p_sted" value="[% p.p_sted | html_entity %]" />
142
<input type="hidden" name="p_land" value="[% p.p_land | html_entity %]" />
143
<input type="hidden" name="p_sjekk" value="[% p.p_sjekk | html_entity %]" />
144
<input type="hidden" name="m_adresse1" value="[% p.m_adresse1 | html_entity %]" />
145
<input type="hidden" name="m_adresse2" value="[% p.m_adresse2 | html_entity %]" />
146
<input type="hidden" name="m_postnr" value="[% p.m_postnr | html_entity %]" />
147
<input type="hidden" name="m_sted" value="[% p.m_sted | html_entity %]" />
148
<input type="hidden" name="m_land" value="[% p.m_land | html_entity %]" />
149
<input type="hidden" name="m_sjekk" value="[% p.m_sjekk | html_entity %]" />
150
<input type="hidden" name="m_gyldig_til" value="[% p.m_gyldig_til | html_entity %]" />
151
<input type="hidden" name="pin" value="[% p.pin %]" />
152
<input type="hidden" name="passord" value="[% p.passord | html_entity %]" />
153
<input type="hidden" name="feide" value="[% p.feide | html_entity %]" />
154
<input type="hidden" name="folkeregsjekk_dato" value="[% p.folkeregsjekk_dato | html_entity %]" />
155
<input type="hidden" name="hjemmebibliotek" value="[% p.hjemmebibliotek | html_entity %]" />
156
<input type="hidden" name="opprettet" value="[% p.opprettet | html_entity %]" />
157
<input type="hidden" name="opprettet_av" value="[% p.opprettet_av | html_entity %]" />
158
<input type="hidden" name="sist_endret" value="[% p.sist_endret | html_entity %]" />
159
<input type="hidden" name="sist_endret_av" value="[% p.sist_endret_av | html_entity %]" />
160
<input type="hidden" name="gyldig_til" value="[% p.gyldig_til | html_entity %]" />
161
<input type="hidden" name="epost" value="[% p.epost | html_entity %]" />
162
<input type="hidden" name="epost_sjekk" value="[% p.epost_sjekk | html_entity %]" />
163
<input type="hidden" name="tlf_mobil" value="[% p.tlf_mobil | html_entity %]" />
164
<input type="hidden" name="tlf_hjemme" value="[% p.tlf_hjemme | html_entity %]" />
165
<input type="hidden" name="tlf_jobb" value="[% p.tlf_jobb | html_entity %]" />
166
<input type="hidden" name="prim_kontakt" value="[% p.prim_kontakt | html_entity %]" />
167
<input type="submit" value="Import this patron" />
168
as
169
<select name="categorycode">
170
[% FOREACH c IN categories %]
171
    <option value="[% c.categorycode %]">[% c.description %]</option>
172
[% END %]
173
</select>
174
</form>
175
</div>
176
[% END %]
(-)a/members/deletemem.pl (-3 / +25 lines)
Lines 31-36 use C4::Auth; Link Here
31
use C4::Members;
31
use C4::Members;
32
use C4::Branch; # GetBranches
32
use C4::Branch; # GetBranches
33
use C4::VirtualShelves (); #no import
33
use C4::VirtualShelves (); #no import
34
use Module::Load;
35
if ( C4::Context->preference('NorwegianPatronDBEnable') == 1 ) {
36
    load Koha::NorwegianPatronDB, qw( NLMarkForDeletion NLSync );
37
}
34
38
35
my $input = new CGI;
39
my $input = new CGI;
36
40
Lines 44-50 my ($template, $borrowernumber, $cookie) Link Here
44
                                        });
48
                                        });
45
49
46
#print $input->header;
50
#print $input->header;
47
my $member=$input->param('member');
51
my $member       = $input->param('member');
52
53
# Handle deletion from the Norwegian national patron database, if it is enabled
54
# If the "deletelocal" parameter is set to "false", the regular deletion will be
55
# short circuited, and only a deletion from the national database can be carried
56
# out. If "deletelocal" is set to "true", or not set to anything normal
57
# deletion will be done.
58
my $deletelocal  = $input->param('deletelocal')  eq 'false' ? 0 : 1; # Deleting locally is the default
59
if ( C4::Context->preference('NorwegianPatronDBEnable') == 1 ) {
60
    if ( $input->param('deleteremote') eq 'true' ) {
61
        # Mark for deletion, then try a live sync
62
        NLMarkForDeletion( $member );
63
        NLSync({ 'borrowernumber' => $member });
64
    }
65
}
66
48
my $issues = GetPendingIssues($member);     # FIXME: wasteful call when really, we only want the count
67
my $issues = GetPendingIssues($member);     # FIXME: wasteful call when really, we only want the count
49
my $countissues = scalar(@$issues);
68
my $countissues = scalar(@$issues);
50
69
Lines 80-86 my $dbh = C4::Context->dbh; Link Here
80
my $sth=$dbh->prepare("Select * from borrowers where guarantorid=?");
99
my $sth=$dbh->prepare("Select * from borrowers where guarantorid=?");
81
$sth->execute($member);
100
$sth->execute($member);
82
my $data=$sth->fetchrow_hashref;
101
my $data=$sth->fetchrow_hashref;
83
if ($countissues > 0 or $flags->{'CHARGES'}  or $data->{'borrowernumber'}){
102
if ($countissues > 0 or $flags->{'CHARGES'}  or $data->{'borrowernumber'} or $deletelocal == 0){
84
    #   print $input->header;
103
    #   print $input->header;
85
104
86
    my ($picture, $dberror) = GetPatronImage($bor->{'borrowernumber'});
105
    my ($picture, $dberror) = GetPatronImage($bor->{'borrowernumber'});
Lines 112-120 if ($countissues > 0 or $flags->{'CHARGES'} or $data->{'borrowernumber'}){ Link Here
112
    if ($flags->{'CHARGES'} ne '') {
131
    if ($flags->{'CHARGES'} ne '') {
113
        $template->param(charges => $flags->{'CHARGES'}->{'amount'});
132
        $template->param(charges => $flags->{'CHARGES'}->{'amount'});
114
    }
133
    }
115
    if ($data) {
134
    if ($data->{'borrowernumber'}) {
116
        $template->param(guarantees => 1);
135
        $template->param(guarantees => 1);
117
    }
136
    }
137
    if ($deletelocal == 0) {
138
        $template->param(keeplocal => 1);
139
    }
118
output_html_with_http_headers $input, $cookie, $template->output;
140
output_html_with_http_headers $input, $cookie, $template->output;
119
141
120
} else {
142
} else {
(-)a/members/memberentry.pl (-1 / +18 lines)
Lines 43-48 use C4::Branch; # GetBranches Link Here
43
use C4::Form::MessagingPreferences;
43
use C4::Form::MessagingPreferences;
44
use Koha::Borrower::Debarments;
44
use Koha::Borrower::Debarments;
45
use Koha::DateUtils;
45
use Koha::DateUtils;
46
use Module::Load;
47
if ( C4::Context->preference('NorwegianPatronDBEnable') == 1 ) {
48
    load Koha::NorwegianPatronDB, qw( NLGetSyncDataFromBorrowernumber );
49
}
46
50
47
use vars qw($debug);
51
use vars qw($debug);
48
52
Lines 416-427 if ((!$nok) and $nodouble and ($op eq 'insert' or $op eq 'save')){ Link Here
416
        if (C4::Context->preference('EnhancedMessagingPreferences') and $input->param('setting_messaging_prefs')) {
420
        if (C4::Context->preference('EnhancedMessagingPreferences') and $input->param('setting_messaging_prefs')) {
417
            C4::Form::MessagingPreferences::handle_form_action($input, { borrowernumber => $borrowernumber }, $template, 1, $newdata{'categorycode'});
421
            C4::Form::MessagingPreferences::handle_form_action($input, { borrowernumber => $borrowernumber }, $template, 1, $newdata{'categorycode'});
418
        }
422
        }
423
        # Try to do the live sync with the Norwegian national patron database, if it is enabled
424
        if ( exists $data{'borrowernumber'} && C4::Context->preference('NorwegianPatronDBEnable') == 1 ) {
425
            NLSync({ 'borrowernumber' => $borrowernumber });
426
        }
419
	} elsif ($op eq 'save'){ 
427
	} elsif ($op eq 'save'){ 
420
		if ($NoUpdateLogin) {
428
		if ($NoUpdateLogin) {
421
			delete $newdata{'password'};
429
			delete $newdata{'password'};
422
			delete $newdata{'userid'};
430
			delete $newdata{'userid'};
423
		}
431
		}
424
		&ModMember(%newdata) unless scalar(keys %newdata) <= 1; # bug 4508 - avoid crash if we're not
432
        &ModMember(%newdata) unless scalar(keys %newdata) <= 1; # bug 4508 - avoid crash if we're not
425
                                                                # updating any columns in the borrowers table,
433
                                                                # updating any columns in the borrowers table,
426
                                                                # which can happen if we're only editing the
434
                                                                # which can happen if we're only editing the
427
                                                                # patron attributes or messaging preferences sections
435
                                                                # patron attributes or messaging preferences sections
Lines 470-475 if ($op eq "modify") { Link Here
470
    if ( $step == 4 ) {
478
    if ( $step == 4 ) {
471
        $template->param( categorycode => $borrower_data->{'categorycode'} );
479
        $template->param( categorycode => $borrower_data->{'categorycode'} );
472
    }
480
    }
481
    # Add sync data to the user data
482
    if ( C4::Context->preference('NorwegianPatronDBEnable') == 1 ) {
483
        my $sync = NLGetSyncDataFromBorrowernumber( $borrowernumber );
484
        if ( $sync ) {
485
            $template->param(
486
                sync => $sync->sync,
487
            );
488
        }
489
    }
473
}
490
}
474
if ( $op eq "duplicate" ) {
491
if ( $op eq "duplicate" ) {
475
    $template->param( updtype => 'I' );
492
    $template->param( updtype => 'I' );
(-)a/members/moremember.pl (+14 lines)
Lines 53-58 use C4::Form::MessagingPreferences; Link Here
53
use List::MoreUtils qw/uniq/;
53
use List::MoreUtils qw/uniq/;
54
use C4::Members::Attributes qw(GetBorrowerAttributes);
54
use C4::Members::Attributes qw(GetBorrowerAttributes);
55
use Koha::Borrower::Debarments qw(GetDebarments IsDebarred);
55
use Koha::Borrower::Debarments qw(GetDebarments IsDebarred);
56
use Module::Load;
57
if ( C4::Context->preference('NorwegianPatronDBEnable') == 1 ) {
58
    load Koha::NorwegianPatronDB, qw( NLGetSyncDataFromBorrowernumber );
59
}
56
#use Smart::Comments;
60
#use Smart::Comments;
57
#use Data::Dumper;
61
#use Data::Dumper;
58
use DateTime;
62
use DateTime;
Lines 290-295 else { Link Here
290
    }
294
    }
291
}
295
}
292
296
297
# Add sync data to the user data
298
if ( C4::Context->preference('NorwegianPatronDBEnable') == 1 ) {
299
    my $sync = NLGetSyncDataFromBorrowernumber( $borrowernumber );
300
    if ( $sync ) {
301
        $data->{'sync'}       = $sync->sync;
302
        $data->{'syncstatus'} = $sync->syncstatus;
303
        $data->{'lastsync'}   = $sync->lastsync;
304
    }
305
}
306
293
# check to see if patron's image exists in the database
307
# check to see if patron's image exists in the database
294
# basically this gives us a template var to condition the display of
308
# basically this gives us a template var to condition the display of
295
# patronimage related interface on
309
# patronimage related interface on
(-)a/members/nl-search.pl (+161 lines)
Line 0 Link Here
1
#!/usr/bin/perl
2
3
# Copyright 2013 Oslo Public Library
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
=head1 NAME
21
22
nl-search.pl - Script for searching the Norwegian national patron database
23
24
=head1 DESCRIPTION
25
26
This script will search the Norwegian national patron database, and let staff
27
import patrons from the natial database into the local database.
28
29
In order to use this, a username/password from the Norwegian national database
30
of libraries ("Base Bibliotek") is needed. A special key is also needed, in
31
order to decrypt and encrypt PIN-codes/passwords.
32
33
See http://www.lanekortet.no/ for more information (in Norwegian).
34
35
=cut
36
37
use Modern::Perl;
38
use CGI;
39
use C4::Auth;
40
use C4::Category;
41
use C4::Context;
42
use C4::Output;
43
use C4::Members;
44
use C4::Members::Attributes qw( SetBorrowerAttributes );
45
use Koha::NorwegianPatronDB qw( NLCheckSysprefs NLSearch NLDecodePin NLGetFirstname NLGetSurname NLSync );
46
use Koha::Database;
47
use Koha::DateUtils;
48
49
my $cgi = CGI->new;
50
my $dbh = C4::Context->dbh;
51
my $op  = $cgi->param('op');
52
53
my ( $template, $loggedinuser, $cookie ) = get_template_and_user(
54
    {
55
        template_name   => "members/nl-search.tt",
56
        query           => $cgi,
57
        type            => "intranet",
58
        authnotrequired => 0,
59
        flagsrequired   => { borrowers => 1 },
60
        debug           => 1,
61
    }
62
);
63
64
my $userenv = C4::Context->userenv;
65
66
# Check sysprefs
67
my $check_result = NLCheckSysprefs();
68
if ( $check_result->{'error'} == 1 ) {
69
    $template->param( 'error' => $check_result );
70
    output_html_with_http_headers $cgi, $cookie, $template->output;
71
    exit 0;
72
}
73
74
if ( $op && $op eq 'search' ) {
75
76
    # Get the string we are searching for
77
    my $identifier = $cgi->param('q');
78
    if ( $identifier ) {
79
        # Local search
80
        my $local_results = Search( $identifier );
81
        $template->param( 'local_result' => $local_results );
82
        # Search NL, unless we got at least one hit and further searching is
83
        # disabled
84
        if ( scalar @{ $local_results } == 0 || C4::Context->preference("NorwegianPatronDBSearchNLAfterLocalHit") == 1 ) {
85
            # TODO Check the format of the identifier before searching NL
86
            my $result = NLSearch( $identifier );
87
            unless ($result->fault) {
88
                my $r = $result->result();
89
                # Send the data to the template
90
                my @categories = C4::Category->all;
91
                $template->param(
92
                    'result'     => $r,
93
                    'categories' => \@categories,
94
                );
95
            } else {
96
                $template->param( 'error' => join ', ', $result->faultcode, $result->faultstring, $result->faultdetail );
97
            }
98
        }
99
        $template->param( 'q' => $identifier );
100
    }
101
102
} elsif ( $op && $op eq 'save' ) {
103
104
    # This is where we map from fields in NL to fields in Koha
105
    my %borrower = (
106
        'surname'      => NLGetSurname( $cgi->param('navn') ),
107
        'firstname'    => NLGetFirstname( $cgi->param('navn') ),
108
        'sex'          => $cgi->param('kjonn'),
109
        'dateofbirth'  => $cgi->param('fdato'),
110
        'cardnumber'   => $cgi->param('lnr'),
111
        'userid'       => $cgi->param('lnr'),
112
        'address'      => $cgi->param('p_adresse1'),
113
        'address2'     => $cgi->param('p_adresse2'),
114
        'zipcode'      => $cgi->param('p_postnr'),
115
        'city'         => $cgi->param('p_sted'),
116
        'country'      => $cgi->param('p_land'),
117
        'B_address'    => $cgi->param('m_adresse1'),
118
        'B_address2'   => $cgi->param('m_adresse2'),
119
        'B_zipcode'    => $cgi->param('m_postnr'),
120
        'B_city'       => $cgi->param('m_sted'),
121
        'B_country'    => $cgi->param('m_land'),
122
        'password'     => NLDecodePin( $cgi->param('pin') ),
123
        'dateexpiry'   => $cgi->param('gyldig_til'),
124
        'email'        => $cgi->param('epost'),
125
        'mobile'       => $cgi->param('tlf_mobil'),
126
        'phone'        => $cgi->param('tlf_hjemme'),
127
        'phonepro'     => $cgi->param('tlf_jobb'),
128
        'branchcode'   => $userenv->{'branch'},
129
        'categorycode' => $cgi->param('categorycode'),
130
    );
131
    # Add the new patron
132
    my $borrowernumber = &AddMember(%borrower);
133
    if ( $borrowernumber ) {
134
        # Add extended patron attributes
135
        SetBorrowerAttributes($borrowernumber, [
136
            { code => 'fnr', value => $cgi->param('fnr_hash') },
137
        ] );
138
        # Override the default sync data created by AddMember
139
        my $borrowersync = Koha::Database->new->schema->resultset('BorrowerSync')->find({
140
            'synctype'       => 'norwegianpatrondb',
141
            'borrowernumber' => $borrowernumber,
142
        });
143
        $borrowersync->update({ 'syncstatus', 'synced' });
144
        $borrowersync->update({ 'lastsync',   $cgi->param('sist_endret') });
145
        $borrowersync->update({ 'hashed_pin', $cgi->param('pin') });
146
        # Try to sync in real time. If this fails it will be picked up by the cronjob
147
        NLSync({ 'borrowernumber' => $borrowernumber });
148
        # Redirect to the edit screen
149
        print $cgi->redirect( "/cgi-bin/koha/members/memberentry.pl?op=modify&destination=circ&borrowernumber=$borrowernumber" );
150
    } else {
151
        $template->param( 'error' => 'COULD_NOT_ADD_PATRON' );
152
    }
153
}
154
155
output_html_with_http_headers $cgi, $cookie, $template->output;
156
157
=head1 AUTHOR
158
159
Magnus Enger <digitalutvikling@gmail.com>
160
161
=cut
(-)a/misc/cronjobs/nl-sync-from-koha.pl (+184 lines)
Line 0 Link Here
1
#!/usr/bin/perl
2
3
# Copyright 2014 Oslo Public Library
4
5
=head1 NAME
6
7
nl-sync-from-koha.pl - Sync patrons from Koha to the Norwegian national patron database (NL).
8
9
=head1 SYNOPSIS
10
11
 perl nl-sync-from-koha.pl -v --run
12
13
=cut
14
15
use Koha::NorwegianPatronDB qw( NLCheckSysprefs NLSync );
16
use Koha::Database;
17
use Getopt::Long;
18
use Pod::Usage;
19
use Modern::Perl;
20
21
# Get options
22
my ( $run, $verbose, $debug ) = get_options();
23
24
=head1 ACTIONS
25
26
=head2
27
28
Find local patrons that have been changed and need to be sent upstream to NL.
29
These patrons will be distinguished by two borrower attributes:
30
31
=over 4
32
33
=item * The "nlstatus" attribute will have a value of "needsync". (Which means
34
that the patron has been changed in Koha, but not yet successfully synced
35
upstream.)
36
37
=item * The "nlsync" attribute will have a value of 1. (Which means that this
38
patron has accepted to be synced with NL, as opposed to a value of 0 which
39
would indicate that the patron has asked not to be synced with NL.)
40
41
=back
42
43
=head1 STEPS
44
45
This script performs the following steps:
46
47
=head2 Check sysprefs
48
49
=cut
50
51
my $check_result = NLCheckSysprefs();
52
if ( $check_result->{'error'} == 1 ) {
53
    if ( $check_result->{'nlenabled'} == 0 ) { say "* Please activate this function with the NorwegianPatronDBEnable system preference." };
54
    if ( $check_result->{'endpoint'}  == 0 ) { say "* Please specify an endpoint with the NorwegianPatronDBEndpoint system preference." };
55
    if ( $check_result->{'userpass'}  == 0 ) { say "* Please fill in the NorwegianPatronDBUsername and NorwegianPatronDBPassword system preferences." };
56
    exit 0;
57
}
58
59
unless ( $run ) {
60
    say "* You have not specified --run, no real syncing will be done.";
61
}
62
63
=head2 Find patrons that need to be synced
64
65
=cut
66
67
my @needs_sync = Koha::Database->new->schema->resultset('BorrowerSync')->search({
68
    -and => [
69
      sync     => 1,
70
      synctype => 'norwegianpatrondb',
71
      -or => [
72
        syncstatus => 'edited',
73
        syncstatus => 'new',
74
        syncstatus => 'delete',
75
      ],
76
    ],
77
});
78
79
=head2 Do the actual sync
80
81
=cut
82
83
my $sync_success = 0;
84
my $sync_failed  = 0;
85
foreach my $borrower ( @needs_sync ) {
86
    my $cardnumber = $borrower->borrowernumber->cardnumber;
87
    my $firstname  = $borrower->borrowernumber->firstname;
88
    my $surname    = $borrower->borrowernumber->surname;
89
    my $syncstatus = $borrower->syncstatus;
90
    say "*** Syncing patron: $cardnumber - $firstname $surname ($syncstatus)" if $verbose;
91
    if ( $run ) {
92
        my $response = NLSync({ 'patron' => $borrower->borrowernumber });
93
        if ( $response ) {
94
            my $result = $response->result;
95
            if ( $result->{'status'} && $result->{'status'} == 1 ) {
96
                $sync_success++;
97
            } else {
98
                $sync_failed++;
99
            }
100
            if ( $result->{'melding'} && $verbose ) {
101
                say $result->{'melding'};
102
            }
103
        }
104
    }
105
}
106
107
=head2 Summarize if verbose mode is enabled
108
109
=cut
110
111
if ( $verbose ) {
112
    say "-----------------------------";
113
    say "Sync succeeded: $sync_success";
114
    say "Sync failed   : $sync_failed";
115
}
116
117
=head1 OPTIONS
118
119
=over 4
120
121
=item B<-r, --run>
122
123
Actually carry out syncing operations. Without this option, the script will
124
only report what it would have done, but not change any data, locally or
125
remotely.
126
127
=item B<-v --verbose>
128
129
Report on the progress of the script.
130
131
=item B<-d --debug>
132
133
Even more output.
134
135
=item B<-h, -?, --help>
136
137
Prints this help message and exits.
138
139
=back
140
141
=cut
142
143
sub get_options {
144
145
  # Options
146
  my $run     = '',
147
  my $verbose = '';
148
  my $debug   = '';
149
  my $help    = '';
150
151
  GetOptions (
152
    'r|run'     => \$run,
153
    'v|verbose' => \$verbose,
154
    'd|debug'   => \$debug,
155
    'h|?|help'  => \$help
156
  );
157
158
  pod2usage( -exitval => 0 ) if $help;
159
160
  return ( $run, $verbose, $debug );
161
162
}
163
164
=head1 AUTHOR
165
166
Magnus Enger <digitalutvikling@gmail.com>
167
168
=head1 COPYRIGHT
169
170
Copyright 2014 Oslo Public Library
171
172
=head1 LICENSE
173
174
This file is part of Koha.
175
176
Koha is free software; you can redistribute it and/or modify it under the terms
177
of the GNU General Public License as published by the Free Software Foundation;
178
either version 3 of the License, or (at your option) any later version.
179
180
You should have received a copy of the GNU General Public License along with
181
Koha; if not, write to the Free Software Foundation, Inc., 51 Franklin Street,
182
Fifth Floor, Boston, MA 02110-1301 USA.
183
184
=cut
(-)a/misc/cronjobs/nl-sync-to-koha.pl (+192 lines)
Line 0 Link Here
1
#!/usr/bin/perl
2
3
# Copyright 2014 Oslo Public Library
4
5
=head1 NAME
6
7
nl-sync-to-koha.pl - Sync patrons from the Norwegian national patron database (NL) to Koha.
8
9
=head1 SYNOPSIS
10
11
 perl nl-sync-to-koha.pl -v --run
12
13
=cut
14
15
use C4::Members;
16
use C4::Members::Attributes qw( UpdateBorrowerAttribute );
17
use Koha::NorwegianPatronDB qw( NLCheckSysprefs NLGetChanged );
18
use Koha::Database;
19
use Getopt::Long;
20
use Pod::Usage;
21
use Modern::Perl;
22
23
# Get options
24
my ( $run, $from, $verbose, $debug ) = get_options();
25
26
my $check_result = NLCheckSysprefs();
27
if ( $check_result->{'error'} == 1 ) {
28
    if ( $check_result->{'nlenabled'} == 0 ) { say "* Please activate this function with the NorwegianPatronDBEnable system preference." };
29
    if ( $check_result->{'endpoint'}  == 0 ) { say "* Please specify an endpoint with the NorwegianPatronDBEndpoint system preference." };
30
    if ( $check_result->{'userpass'}  == 0 ) { say "* Please fill in the NorwegianPatronDBUsername and NorwegianPatronDBPassword system preferences." };
31
    exit 0;
32
}
33
34
unless ( $run ) {
35
    say "* You have not specified --run, no real syncing will be done.";
36
}
37
38
# Do the sync
39
my $sync_success         = 0;
40
my $sync_failed          = 0;
41
my $skipped_local_change = 0;
42
43
# Get the borrowers that have been changed
44
my $result = NLGetChanged( $from );
45
46
if ( $verbose ) {
47
    say 'Number of records: ' . $result->{'antall_poster_returnert'};
48
    say 'Number of hits:    ' . $result->{'antall_treff'};
49
    say 'Message:           ' . $result->{'melding'};
50
    say 'Status:            ' . $result->{'status'};
51
    say 'Server time:       ' . $result->{'server_tid'};
52
    say "-----------------------------";
53
}
54
55
# Loop through the patrons
56
foreach my $patron ( @{ $result->{'kohapatrons'} } ) {
57
    if ( $verbose ) {
58
        if ( $patron->{'surname'} ) {
59
            say "*** Name: " . $patron->{'surname'};
60
        } else {
61
            say "*** No name";
62
        }
63
        say 'Created by:     ' . $patron->{'_extra'}->{'created_by'};
64
        say 'Last change by: ' . $patron->{'_extra'}->{'last_change_by'};
65
    }
66
    # Only sync in changes made by other libraries
67
    if ( C4::Context->preference("NorwegianPatronDBUsername") ne $patron->{'_extra'}->{'last_change_by'} ) {
68
        # Make a copy of the data in the hashref and store it as a hash
69
        my %clean_patron = %$patron;
70
        # Delete the extra data from the copy of the hashref
71
        delete $clean_patron{'_extra'};
72
        # Find the borrowernumber based on cardnumber
73
        my $stored_patron = Koha::Database->new->schema->resultset('Borrower')->find({
74
            'cardnumber' => $patron->{'cardnumber'}
75
        });
76
        my $borrowernumber = $stored_patron->borrowernumber;
77
        if ( $run ) {
78
            # Call ModMember
79
            my $success = ModMember(
80
                'borrowernumber' => $borrowernumber,
81
                %clean_patron,
82
            );
83
            if ( $success ) {
84
                # Get the sync object
85
                my $sync = Koha::Database->new->schema->resultset('BorrowerSync')->find({
86
                    'synctype'       => 'norwegianpatrondb',
87
                    'borrowernumber' => $borrowernumber,
88
                });
89
                # Update the syncstatus to 'synced'
90
                $sync->update( { 'syncstatus' => 'synced' } );
91
                # Update the 'synclast' attribute with the "server time" ("server_tid") returned by the method
92
                $sync->update( { 'lastsync' => $result->{'result'}->{'server_tid'} } );
93
                # Save social security number as attribute
94
                UpdateBorrowerAttribute(
95
                    $borrowernumber,
96
                    { code => 'fnr', attribute => $patron->{'_extra'}->{'socsec'} },
97
                );
98
                $sync_success++;
99
            } else {
100
                $sync_failed++;
101
            }
102
        }
103
    } else {
104
        say "Skipped, local change" if $verbose;
105
        $skipped_local_change++;
106
    }
107
}
108
109
if ( $verbose ) {
110
    say "-----------------------------";
111
    say "Sync succeeded:       $sync_success";
112
    say "Sync failed   :       $sync_failed";
113
    say "Skipped local change: $skipped_local_change";
114
}
115
116
=head1 OPTIONS
117
118
=over 4
119
120
=item B<-r, --run>
121
122
Actually carry out syncing operations. Without this option, the script will
123
only report what it would have done, but not change any data, locally or
124
remotely.
125
126
=item B<-v --verbose>
127
128
Report on the progress of the script.
129
130
=item B<-f --from>
131
132
Date and time to sync from, if this should be different from "1 second past
133
midnight of the day before". The date should be in this format:
134
135
    2014-06-03T00:00:01
136
137
=item B<-d --debug>
138
139
Even more output.
140
141
=item B<-h, -?, --help>
142
143
Prints this help message and exits.
144
145
=back
146
147
=cut
148
149
sub get_options {
150
151
  # Options
152
  my $run     = '',
153
  my $from    = '',
154
  my $verbose = '';
155
  my $debug   = '';
156
  my $help    = '';
157
158
  GetOptions (
159
    'r|run'     => \$run,
160
    'f|from=s'  => \$from,
161
    'v|verbose' => \$verbose,
162
    'd|debug'   => \$debug,
163
    'h|?|help'  => \$help
164
  );
165
166
  pod2usage( -exitval => 0 ) if $help;
167
168
  return ( $run, $from, $verbose, $debug );
169
170
}
171
172
=head1 AUTHOR
173
174
Magnus Enger <digitalutvikling@gmail.com>
175
176
=head1 COPYRIGHT
177
178
Copyright 2014 Oslo Public Library
179
180
=head1 LICENSE
181
182
This file is part of Koha.
183
184
Koha is free software; you can redistribute it and/or modify it under the terms
185
of the GNU General Public License as published by the Free Software Foundation;
186
either version 3 of the License, or (at your option) any later version.
187
188
You should have received a copy of the GNU General Public License along with
189
Koha; if not, write to the Free Software Foundation, Inc., 51 Franklin Street,
190
Fifth Floor, Boston, MA 02110-1301 USA.
191
192
=cut
(-)a/t/00-load.t (+1 lines)
Lines 44-49 find( Link Here
44
            return unless $m =~ s/[.]pm$//;
44
            return unless $m =~ s/[.]pm$//;
45
            $m =~ s{^.*/Koha/}{Koha/};
45
            $m =~ s{^.*/Koha/}{Koha/};
46
            $m =~ s{/}{::}g;
46
            $m =~ s{/}{::}g;
47
            return if $m =~ /Koha::NorwegianPatronDB/; # uses non-mandatory modules
47
            use_ok($m) || BAIL_OUT("***** PROBLEMS LOADING FILE '$m'");
48
            use_ok($m) || BAIL_OUT("***** PROBLEMS LOADING FILE '$m'");
48
        },
49
        },
49
    },
50
    },
(-)a/t/NorwegianPatronDB.t (-1 / +595 lines)
Line 0 Link Here
0
- 
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
use Modern::Perl;
19
use Test::More;
20
use Test::MockModule;
21
use t::lib::Mocks;
22
use Data::Dumper;
23
24
# Check that all the modules we need are installed, or bail out
25
BEGIN {
26
    eval {
27
        require Test::DBIx::Class;
28
        1;
29
    } or do {
30
        plan skip_all => "Test::DBIx::Class is not available";
31
    };
32
}
33
BEGIN {
34
    eval {
35
        require SOAP::Lite;
36
        1;
37
    } or do {
38
        plan skip_all => "SOAP::Lite is not available";
39
    };
40
}
41
BEGIN {
42
    eval {
43
        require Crypt::GCrypt;
44
        1;
45
    } or do {
46
        plan skip_all => "Crypt::GCrypt is not available";
47
    };
48
}
49
BEGIN {
50
    eval {
51
        require Convert::BaseN;
52
        1;
53
    } or do {
54
        plan skip_all => "Convert::BaseN is not available";
55
    };
56
}
57
58
use Test::DBIx::Class {
59
    schema_class => 'Koha::Schema',
60
    connect_info => ['dbi:SQLite:dbname=:memory:','',''],
61
    connect_opts => { name_sep => '.', quote_char => '`', },
62
    fixture_class => '::Populate',
63
}, 'Borrower', 'BorrowerSync';
64
65
# Make the code in the module use our mocked Koha::Schema/Koha::Database
66
my $db = Test::MockModule->new('Koha::Database');
67
$db->mock(
68
    # Schema() gives us the DB connection set up by Test::DBIx::Class
69
    _new_schema => sub { return Schema(); }
70
);
71
72
fixtures_ok [
73
    'Borrower' => [
74
        [qw/firstname surname borrowernumber address city/],
75
        ['Test', 'Borrower', 1, 'Test road', 'Test city'],
76
        ['Test', 'Borrower', 2, 'Test road', 'Test city'],
77
        ['Test', 'Borrower', 3, 'Test road', 'Test city'],
78
        ['Test', 'Borrower', 4, 'Test road', 'Test city'],
79
    ],
80
    'BorrowerSync' => [
81
        [qw/borrowernumber sync syncstatus lastsync hashed_pin synctype/],
82
        [1, 1, 'new',    '2014-03-31T12:35:14', 'abc', 'norwegianpatrondb' ],
83
        [2, 1, 'edited', '2014-03-31T12:35:14', 'abc', 'norwegianpatrondb' ],
84
        [3, 1, 'new',    '2014-03-31T12:35:14', 'abc', 'norwegianpatrondb' ],
85
        [4, 1, 'new',    '2014-03-31T12:35:14', 'abc', 'norwegianpatrondb' ],
86
    ],
87
], 'installed some fixtures';
88
89
=head1 LOADING THE MODULE
90
91
=cut
92
93
BEGIN { use_ok( 'Koha::NorwegianPatronDB', ':all' ) }
94
95
96
=head1 UTILITY SUBROUTINES
97
98
=head2 NLCheckSysprefs
99
100
Relevant sysprefs:
101
102
=over 4
103
104
=item * NorwegianPatronDBEnable
105
106
=item * NorwegianPatronDBEndpoint
107
108
=item * NorwegianPatronDBUsername
109
110
=item * NorwegianPatronDBPassword
111
112
=back
113
114
=cut
115
116
t::lib::Mocks::mock_preference('NorwegianPatronDBEnable',   0);
117
t::lib::Mocks::mock_preference('NorwegianPatronDBEndpoint', '');
118
t::lib::Mocks::mock_preference('NorwegianPatronDBUsername', '');
119
t::lib::Mocks::mock_preference('NorwegianPatronDBPassword', '');
120
ok( my $result = NLCheckSysprefs(), 'call NLCheckSysprefs() ok' );
121
is( $result->{ 'error' },     1, 'error detected' );
122
is( $result->{ 'nlenabled' }, 0, 'NL is not enabled' );
123
is( $result->{ 'endpoint' },  0, 'an endpoint is not specified' );
124
is( $result->{ 'userpass' },  0, 'username and/or password is missing' );
125
126
t::lib::Mocks::mock_preference('NorwegianPatronDBEnable',   1);
127
ok( $result = NLCheckSysprefs(), 'call NLCheckSysprefs() ok' );
128
is( $result->{ 'error' },     1, 'error detected' );
129
is( $result->{ 'nlenabled' }, 1, 'NL is enabled' );
130
is( $result->{ 'endpoint' },  0, 'an endpoint is not specified' );
131
is( $result->{ 'userpass' },  0, 'username and/or password is missing' );
132
133
t::lib::Mocks::mock_preference('NorwegianPatronDBEnable',   0);
134
t::lib::Mocks::mock_preference('NorwegianPatronDBUsername', 'user');
135
t::lib::Mocks::mock_preference('NorwegianPatronDBPassword', 'pass');
136
ok( $result = NLCheckSysprefs(), 'call NLCheckSysprefs() ok' );
137
is( $result->{ 'error' },     1, 'error detected' );
138
is( $result->{ 'nlenabled' }, 0, 'NL is not enabled' );
139
is( $result->{ 'endpoint' },  0, 'an endpoint is not specified' );
140
is( $result->{ 'userpass' },  1, 'username and/or password is present' );
141
142
t::lib::Mocks::mock_preference('NorwegianPatronDBEnable',   1);
143
t::lib::Mocks::mock_preference('NorwegianPatronDBEndpoint', 'http://example.com/');
144
ok( $result = NLCheckSysprefs(), 'call NLCheckSysprefs() ok' );
145
is( $result->{ 'error' },     0, 'no error detected' );
146
is( $result->{ 'nlenabled' }, 1, 'NL is enabled' );
147
is( $result->{ 'endpoint' },  1, 'an endpoint is specified' );
148
is( $result->{ 'userpass' },  1, 'username and/or password is present' );
149
150
=head2 NLGetFirstname and NLGetSurname
151
152
=cut
153
154
my $firstname = 'Firstname';
155
my $surname   = 'Surname';
156
my $fullname  = "$surname, $firstname";
157
my $wrongname = "$surname $firstname";
158
159
is( NLGetFirstname( $fullname  ), $firstname, 'can get firstname from name' );
160
is( NLGetSurname(   $fullname  ), $surname,   'can get surname from name' );
161
is( NLGetFirstname( $wrongname ), $wrongname, 'returns full string when name misses comma' );
162
is( NLGetSurname(   $wrongname ), $wrongname, 'returns full string when name misses comma' );
163
164
=head2 NLDecodePin and NLEncryptPIN
165
166
=cut
167
168
my $pin  = '1234';
169
my $hash = NLEncryptPIN( $pin );
170
171
is( NLEncryptPIN( $pin ), $hash, 'NLEncryptPIN works' );
172
is( NLDecodePin( $hash ), $pin, 'NLDecodePin works' );
173
174
=head2 NLUpdateHashedPIN
175
176
=cut
177
178
is ( BorrowerSync->find({ 'borrowernumber' => 1 })->get_column('hashed_pin'), 'abc', 'hashed_pin is "abc"' );
179
# Set a new pin
180
my $new_pin = 'bcd';
181
ok( NLUpdateHashedPIN( 1, $new_pin ), 'NLUpdateHashedPIN runs ok' );
182
# Hash the new pin and compare it to the one stored in the database
183
my $hashed_pin = Koha::NorwegianPatronDB::_encrypt_pin( $new_pin );
184
is ( BorrowerSync->find({ 'borrowernumber' => 1 })->get_column('hashed_pin'), $hashed_pin, 'hashed_pin was updated' );
185
186
=head2 NLMarkForDeletion
187
188
=cut
189
190
is ( BorrowerSync->find({ 'borrowernumber' => 3 })->get_column('syncstatus'), 'new', 'syncstatus is "new"' );
191
ok( NLMarkForDeletion( 3 ), 'NLMarkForDeletion runs ok' );
192
# Check that the syncstatus was updated. Note: We will use this status later, to check syncing of deleted borrowers
193
is ( BorrowerSync->find({ 'borrowernumber' => 3 })->get_column('syncstatus'), 'delete', 'syncstatus is "delete"' );
194
195
=head2 NLGetSyncDataFromBorrowernumber
196
197
=cut
198
199
ok( my $sync_data = NLGetSyncDataFromBorrowernumber( 1 ), 'NLGetSyncDataFromBorrowernumber runs ok' );
200
isa_ok( $sync_data, 'Koha::Schema::Result::BorrowerSync' );
201
is( $sync_data->sync, 1, 'the sync is on' );
202
is( $sync_data->syncstatus, 'new', 'syncstatus is "new"' );
203
is( $sync_data->lastsync, '2014-03-31T12:35:14', 'lastsync is ok' );
204
is( $sync_data->hashed_pin, $hashed_pin, 'hashed_pin is ok' );
205
206
=head1 SUBROUTINES THAT TALK TO SOAP
207
208
=head2 NLSearch
209
210
=cut
211
212
my $lite = Test::MockModule->new('SOAP::Lite');
213
214
# Mock a successfull call to the "hent" method
215
$lite->mock(
216
    hent => sub { return SOAP::Deserializer->deserialize( hent_success() )->result; }
217
);
218
ok( my $res = NLSearch( '12345678910' ), 'successfull call to NLSearch' );
219
is( $res->{'antall_poster_returnert'}, 1, 'got 1 record' );
220
isa_ok( $res, "Resultat" );
221
isa_ok( $res->{'respons_poster'}, "LaanerListe" );
222
isa_ok( $res->{'respons_poster'}[0], "Laaner" );
223
224
# Mock an unsuccessfull call to the "hent" method
225
$lite->mock(
226
    hent => sub { return SOAP::Deserializer->deserialize( hent_failure() )->result; }
227
);
228
ok( $res = NLSearch( '12345678910' ), 'call to NLSearch with an illegal argument' );
229
is( $res->{'antall_poster_returnert'}, 0, 'got 0 records' );
230
isa_ok( $res, "Resultat" );
231
like( $res->{'melding'}, qr/Ulovlig argument: hverken LNR eller FNR_HASH/, "got expected error message for an illegal identifier" );
232
233
=head2 NLSync
234
235
=head3 New patron
236
237
=cut
238
239
my $borrower = Borrower->find({ 'borrowernumber' => 1 });
240
$lite->mock(
241
    nyPost => sub { return SOAP::Deserializer->deserialize( nyPost_success() )->result; }
242
);
243
is ( BorrowerSync->find({ 'borrowernumber' => 1 })->get_column('syncstatus'), 'new', 'patron is new' );
244
ok ( $result = NLSync({ 'patron' => $borrower }), 'successfull call to NLSync via patron ("nyPost")' );
245
is ( BorrowerSync->find({ 'borrowernumber' => 1 })->get_column('syncstatus'), 'synced', 'patron is synced' );
246
247
# Now do the same test, but pass in a borrowernumber, not a Koha::Schema::Result::Borrower
248
is ( BorrowerSync->find({ 'borrowernumber' => 4 })->get_column('syncstatus'), 'new', 'patron is new' );
249
ok ( $result = NLSync({ 'borrowernumber' => 4 }), 'successfull call to NLSync via borrowernumber ("nyPost")' );
250
is ( BorrowerSync->find({ 'borrowernumber' => 4 })->get_column('syncstatus'), 'synced', 'patron is synced' );
251
252
=head3 Edited patron
253
254
=cut
255
256
ok ( $borrower = Borrower->find({ 'borrowernumber' => 2 }), 'find our "edited" mock patron' );
257
$lite->mock(
258
    endre => sub { return SOAP::Deserializer->deserialize( endre_success() )->result; }
259
);
260
is ( BorrowerSync->find({ 'borrowernumber' => 2 })->get_column('syncstatus'), 'edited', 'patron is edited' );
261
ok ( $result = NLSync({ 'patron' => $borrower }), 'successfull call to NLSync ("endre")' );
262
is ( BorrowerSync->find({ 'borrowernumber' => 2 })->get_column('syncstatus'), 'synced', 'patron is synced' );
263
264
=head3 Deleted patron
265
266
=cut
267
268
ok ( $borrower = Borrower->find({ 'borrowernumber' => 3 }), 'find our "deleted" mock patron' );
269
$lite->mock(
270
    slett => sub { return SOAP::Deserializer->deserialize( endre_success() )->result; }
271
);
272
is ( BorrowerSync->find({ 'borrowernumber' => 3 })->get_column('syncstatus'), 'delete', 'patron is marked for deletion' );
273
ok ( $result = NLSync({ 'patron' => $borrower }), 'successfull call to NLSync ("slett")' );
274
is ( BorrowerSync->find({ 'borrowernumber' => 3 })->get_column('sync'), 0, 'sync is now disabled' );
275
276
=head2 NLGetChanged
277
278
=cut
279
280
# Mock a successfull call to the "soekEndret" method
281
$lite->mock(
282
    soekEndret => sub { return SOAP::Deserializer->deserialize( soekEndret_success() ); }
283
);
284
ok( $res = NLGetChanged(), 'successfull call to NLGetChanged - 2 results' );
285
is( $res->{'melding'}, 'OK', 'got "OK"' );
286
is( $res->{'antall_poster_returnert'}, 2, 'got 2 records' );
287
isa_ok( $res, "Resultat" );
288
isa_ok( $res->{'respons_poster'}, "LaanerListe" );
289
isa_ok( $res->{'respons_poster'}[0], "Laaner" );
290
291
292
# Mock a successfull call to the "soekEndret" method, but with zero new records
293
$lite->mock(
294
    soekEndret => sub { return SOAP::Deserializer->deserialize( soekEndret_zero_new() ); }
295
);
296
ok( $res = NLGetChanged(), 'successfull call to NLGetChanged - 0 results' );
297
is( $res->{'melding'}, 'ingen treff', 'got "ingen treff"' );
298
is( $res->{'antall_poster_returnert'}, 0, 'got 0 records' );
299
is( $res->{'antall_treff'}, 0, 'got 0 records' );
300
301
done_testing();
302
303
=head1 SAMPLE SOAP XML RESPONSES
304
305
These responses can be gathered by setting "outputxml()" to true on the SOAP
306
client:
307
308
    my $client = SOAP::Lite
309
        ->on_action( sub { return '""';})
310
        ->uri('http://lanekortet.no')
311
        ->proxy('https://fl.lanekortet.no/laanekort/fl_test.php')
312
        ->outputxml(1);
313
    my $response = $client->slett( $x );
314
    say $response;
315
316
Pretty formatting can be achieved by piping the output from a test script
317
through xmllint:
318
319
    perl my_test_script.pl > xmllint --format -
320
321
=cut
322
323
sub slett_success {
324
325
    return <<'ENDRESPONSE';
326
<?xml version="1.0" encoding="UTF-8"?>
327
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://lanekortet.no" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
328
  <SOAP-ENV:Body>
329
    <ns1:slettResponse>
330
      <return xsi:type="ns1:Svar">
331
        <status xsi:type="xsd:boolean">true</status>
332
        <melding xsi:type="xsd:string">Test Testersen (1973-08-11) er slettet fra registeret</melding>
333
        <lnr xsi:type="xsd:string">N000106188</lnr>
334
        <server_tid xsi:type="xsd:string">2014-06-02T16:51:58</server_tid>
335
      </return>
336
    </ns1:slettResponse>
337
  </SOAP-ENV:Body>
338
</SOAP-ENV:Envelope>
339
ENDRESPONSE
340
341
}
342
343
sub endre_success {
344
345
    return <<'ENDRESPONSE';
346
<?xml version="1.0" encoding="UTF-8"?>
347
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://lanekortet.no" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
348
  <SOAP-ENV:Body>
349
    <ns1:endreResponse>
350
      <return xsi:type="ns1:Svar">
351
        <status xsi:type="xsd:boolean">true</status>
352
        <melding xsi:type="xsd:string">Oppdaterte felt: navn, p_adresse1, p_postnr, p_sted, p_land, fdato, fnr_hash, kjonn, pin, sist_endret, sist_endret_av</melding>
353
        <lnr xsi:type="xsd:string">N000106188</lnr>
354
        <server_tid xsi:type="xsd:string">2014-06-02T16:42:32</server_tid>
355
      </return>
356
    </ns1:endreResponse>
357
  </SOAP-ENV:Body>
358
</SOAP-ENV:Envelope>
359
ENDRESPONSE
360
361
}
362
363
sub nyPost_success {
364
365
    return <<'ENDRESPONSE';
366
<?xml version="1.0" encoding="UTF-8"?>
367
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://lanekortet.no" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
368
  <SOAP-ENV:Body>
369
    <ns1:nyPostResponse>
370
      <return xsi:type="ns1:Svar">
371
        <status xsi:type="xsd:boolean">true</status>
372
        <melding xsi:type="xsd:string">Ny post er opprettet</melding>
373
        <lnr xsi:type="xsd:string">N000106188</lnr>
374
        <server_tid xsi:type="xsd:string">2014-06-02T14:10:09</server_tid>
375
      </return>
376
    </ns1:nyPostResponse>
377
  </SOAP-ENV:Body>
378
</SOAP-ENV:Envelope>
379
ENDRESPONSE
380
381
}
382
383
sub soekEndret_success {
384
385
return <<'ENDRESPONSE';
386
<?xml version="1.0" encoding="UTF-8"?>
387
<SOAP-ENV:Envelope
388
    xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
389
    xmlns:ns1="http://lanekortet.no"
390
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
391
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
392
    xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
393
    SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
394
  <SOAP-ENV:Body>
395
    <ns1:soekEndretResponse>
396
      <return xsi:type="ns1:Resultat">
397
        <status xsi:type="xsd:boolean">true</status>
398
        <melding xsi:type="xsd:string">OK</melding>
399
        <antall_treff xsi:type="xsd:int">2</antall_treff>
400
        <antall_poster_returnert xsi:type="xsd:int">2</antall_poster_returnert>
401
        <neste_indeks xsi:type="xsd:int">0</neste_indeks>
402
        <respons_poster SOAP-ENC:arrayType="ns1:Laaner[2]" xsi:type="ns1:LaanerListe">
403
          <item xsi:type="ns1:Laaner">
404
            <lnr xsi:type="xsd:string">N000106186</lnr>
405
            <navn xsi:type="xsd:string">Hansen, Hanne</navn>
406
            <p_adresse1 xsi:type="xsd:string"/>
407
            <p_adresse2 xsi:type="xsd:string"/>
408
            <p_postnr xsi:type="xsd:string"/>
409
            <p_sted xsi:type="xsd:string">BØDØ</p_sted>
410
            <p_land xsi:type="xsd:string">no</p_land>
411
            <p_sjekk xsi:type="xsd:string">0</p_sjekk>
412
            <m_adresse1 xsi:type="xsd:string"/>
413
            <m_adresse2 xsi:type="xsd:string"/>
414
            <m_postnr xsi:type="xsd:string"/>
415
            <m_sted xsi:type="xsd:string"/>
416
            <m_land xsi:type="xsd:string"/>
417
            <m_sjekk xsi:type="xsd:string">0</m_sjekk>
418
            <m_gyldig_til xsi:type="xsd:string">0000-00-00</m_gyldig_til>
419
            <tlf_hjemme xsi:type="xsd:string"/>
420
            <tlf_jobb xsi:type="xsd:string"/>
421
            <tlf_mobil xsi:type="xsd:string"/>
422
            <epost xsi:type="xsd:string"/>
423
            <epost_sjekk xsi:type="xsd:string"/>
424
            <prim_kontakt xsi:type="xsd:string"/>
425
            <hjemmebibliotek xsi:type="xsd:string">5180401</hjemmebibliotek>
426
            <fdato xsi:type="xsd:string">1994-04-08</fdato>
427
            <fnr_hash xsi:type="xsd:string">11087395628</fnr_hash>
428
            <kjonn xsi:type="xsd:string">F</kjonn>
429
            <pin xsi:type="xsd:string">89308dfc85ee7a5826ae14e2d8efad1e</pin>
430
            <passord xsi:type="xsd:string"/>
431
            <feide xsi:type="xsd:string">0</feide>
432
            <opprettet xsi:type="xsd:string">2014-04-28T15:20:38</opprettet>
433
            <opprettet_av xsi:type="xsd:string">5180401</opprettet_av>
434
            <sist_endret xsi:type="xsd:string">2014-04-28T15:20:38</sist_endret>
435
            <sist_endret_av xsi:type="xsd:string">5180401</sist_endret_av>
436
            <folkeregsjekk_dato xsi:type="xsd:string">0000-00-00</folkeregsjekk_dato>
437
          </item>
438
          <item xsi:type="ns1:Laaner">
439
            <lnr xsi:type="xsd:string">N000106184</lnr>
440
            <navn xsi:type="xsd:string">Enger, Magnus</navn>
441
            <p_adresse1 xsi:type="xsd:string">Svarthammarveien 633333</p_adresse1>
442
            <p_adresse2 xsi:type="xsd:string"/>
443
            <p_postnr xsi:type="xsd:string">8015</p_postnr>
444
            <p_sted xsi:type="xsd:string">Bodø</p_sted>
445
            <p_land xsi:type="xsd:string">no</p_land>
446
            <p_sjekk xsi:type="xsd:string">0</p_sjekk>
447
            <m_adresse1 xsi:type="xsd:string"/>
448
            <m_adresse2 xsi:type="xsd:string"/>
449
            <m_postnr xsi:type="xsd:string"/>
450
            <m_sted xsi:type="xsd:string"/>
451
            <m_land xsi:type="xsd:string">no</m_land>
452
            <m_sjekk xsi:type="xsd:string">0</m_sjekk>
453
            <m_gyldig_til xsi:type="xsd:string">0000-00-00</m_gyldig_til>
454
            <tlf_hjemme xsi:type="xsd:string">95158548</tlf_hjemme>
455
            <tlf_jobb xsi:type="xsd:string"/>
456
            <tlf_mobil xsi:type="xsd:string"/>
457
            <epost xsi:type="xsd:string">magnus@enger.priv.no</epost>
458
            <epost_sjekk xsi:type="xsd:string"/>
459
            <prim_kontakt xsi:type="xsd:string"/>
460
            <hjemmebibliotek xsi:type="xsd:string">5180401</hjemmebibliotek>
461
            <fdato xsi:type="xsd:string">1973-08-11</fdato>
462
            <fnr_hash xsi:type="xsd:string">11087345795</fnr_hash>
463
            <kjonn xsi:type="xsd:string">M</kjonn>
464
            <pin xsi:type="xsd:string">a632c504b8c4fba3149115cb07e0796c</pin>
465
            <passord xsi:type="xsd:string"/>
466
            <feide xsi:type="xsd:string">0</feide>
467
            <opprettet xsi:type="xsd:string">2014-04-28T14:52:02</opprettet>
468
            <opprettet_av xsi:type="xsd:string">5180401</opprettet_av>
469
            <sist_endret xsi:type="xsd:string">2014-05-13T11:01:33</sist_endret>
470
            <sist_endret_av xsi:type="xsd:string">5180401</sist_endret_av>
471
            <folkeregsjekk_dato xsi:type="xsd:string">0000-00-00</folkeregsjekk_dato>
472
          </item>
473
        </respons_poster>
474
        <server_tid xsi:type="xsd:string">2014-05-16T14:44:44</server_tid>
475
      </return>
476
    </ns1:soekEndretResponse>
477
  </SOAP-ENV:Body>
478
</SOAP-ENV:Envelope>
479
ENDRESPONSE
480
}
481
482
sub soekEndret_zero_new {
483
    return <<'ENDRESPONSE';
484
<?xml version="1.0" encoding="UTF-8"?>
485
    <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://lanekortet.no" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
486
      <SOAP-ENV:Body>
487
        <ns1:soekEndretResponse>
488
          <return xsi:type="ns1:Resultat">
489
            <status xsi:type="xsd:boolean">false</status>
490
            <melding xsi:type="xsd:string">ingen treff</melding>
491
            <antall_treff xsi:type="xsd:int">0</antall_treff>
492
            <antall_poster_returnert xsi:type="xsd:int">0</antall_poster_returnert>
493
            <neste_indeks xsi:type="xsd:int">0</neste_indeks>
494
            <respons_poster SOAP-ENC:arrayType="ns1:Laaner[0]" xsi:type="ns1:LaanerListe"/>
495
            <server_tid xsi:type="xsd:string">2014-05-20T13:02:02</server_tid>
496
          </return>
497
        </ns1:soekEndretResponse>
498
      </SOAP-ENV:Body>
499
    </SOAP-ENV:Envelope>
500
ENDRESPONSE
501
}
502
503
sub hent_failure {
504
    return <<'ENDRESPONSE';
505
<?xml version="1.0" encoding="UTF-8"?>
506
<SOAP-ENV:Envelope
507
    xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
508
    xmlns:ns1="http://lanekortet.no"
509
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
510
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
511
    xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
512
    SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
513
  <SOAP-ENV:Body>
514
    <ns1:hentResponse>
515
      <return xsi:type="ns1:Resultat">
516
        <status xsi:type="xsd:boolean">false</status>
517
        <melding xsi:type="xsd:string">hent: Ulovlig argument: hverken LNR eller FNR_HASH</melding>
518
        <antall_treff xsi:type="xsd:int">0</antall_treff>
519
        <antall_poster_returnert xsi:type="xsd:int">0</antall_poster_returnert>
520
        <neste_indeks xsi:type="xsd:int">0</neste_indeks>
521
        <respons_poster SOAP-ENC:arrayType="ns1:Laaner[0]" xsi:type="ns1:LaanerListe"/>
522
        <server_tid xsi:type="xsd:string">2014-05-15T10:56:24</server_tid>
523
      </return>
524
    </ns1:hentResponse>
525
  </SOAP-ENV:Body>
526
</SOAP-ENV:Envelope>
527
ENDRESPONSE
528
529
}
530
531
sub hent_success {
532
533
return <<'ENDRESPONSE';
534
<?xml version="1.0" encoding="UTF-8"?>
535
<SOAP-ENV:Envelope
536
    xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
537
    xmlns:ns1="http://lanekortet.no"
538
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
539
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
540
    xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
541
    SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
542
  <SOAP-ENV:Body>
543
    <ns1:hentResponse>
544
      <return xsi:type="ns1:Resultat">
545
        <status xsi:type="xsd:boolean">true</status>
546
        <melding xsi:type="xsd:string">OK</melding>
547
        <antall_treff xsi:type="xsd:int">1</antall_treff>
548
        <antall_poster_returnert xsi:type="xsd:int">1</antall_poster_returnert>
549
        <neste_indeks xsi:type="xsd:int">0</neste_indeks>
550
        <respons_poster SOAP-ENC:arrayType="ns1:Laaner[1]" xsi:type="ns1:LaanerListe">
551
          <item xsi:type="ns1:Laaner">
552
            <lnr xsi:type="xsd:string">N000123456</lnr>
553
            <navn xsi:type="xsd:string">Test, Testersen</navn>
554
            <p_adresse1 xsi:type="xsd:string">Bibliotekveien 6</p_adresse1>
555
            <p_adresse2 xsi:type="xsd:string"/>
556
            <p_postnr xsi:type="xsd:string">1234</p_postnr>
557
            <p_sted xsi:type="xsd:string">Lillevik</p_sted>
558
            <p_land xsi:type="xsd:string">no</p_land>
559
            <p_sjekk xsi:type="xsd:string">0</p_sjekk>
560
            <m_adresse1 xsi:type="xsd:string"/>
561
            <m_adresse2 xsi:type="xsd:string"/>
562
            <m_postnr xsi:type="xsd:string"/>
563
            <m_sted xsi:type="xsd:string"/>
564
            <m_land xsi:type="xsd:string">no</m_land>
565
            <m_sjekk xsi:type="xsd:string">0</m_sjekk>
566
            <m_gyldig_til xsi:type="xsd:string">0000-00-00</m_gyldig_til>
567
            <tlf_hjemme xsi:type="xsd:string"/>
568
            <tlf_jobb xsi:type="xsd:string"/>
569
            <tlf_mobil xsi:type="xsd:string">12345678</tlf_mobil>
570
            <epost xsi:type="xsd:string">test@example.com</epost>
571
            <epost_sjekk xsi:type="xsd:string">0</epost_sjekk>
572
            <prim_kontakt xsi:type="xsd:string"/>
573
            <hjemmebibliotek xsi:type="xsd:string">2060000</hjemmebibliotek>
574
            <fdato xsi:type="xsd:string">1964-05-22</fdato>
575
            <fnr_hash xsi:type="xsd:string">22056412345</fnr_hash>
576
            <kjonn xsi:type="xsd:string">F</kjonn>
577
            <pin xsi:type="xsd:string">g345abc123dab567abc78900abc123ab</pin>
578
            <passord xsi:type="xsd:string"/>
579
            <feide xsi:type="xsd:string"/>
580
            <opprettet xsi:type="xsd:string">2005-10-20</opprettet>
581
            <opprettet_av xsi:type="xsd:string">2060000</opprettet_av>
582
            <sist_endret xsi:type="xsd:string">2013-05-13T13:51:24</sist_endret>
583
            <sist_endret_av xsi:type="xsd:string">2060000</sist_endret_av>
584
            <gyldig_til xsi:type="xsd:string"/>
585
            <folkeregsjekk_dato xsi:type="xsd:string">0000-00-00</folkeregsjekk_dato>
586
          </item>
587
        </respons_poster>
588
        <server_tid xsi:type="xsd:string">2014-01-07T14:43:18</server_tid>
589
      </return>
590
    </ns1:hentResponse>
591
  </SOAP-ENV:Body>
592
</SOAP-ENV:Envelope>
593
ENDRESPONSE
594
595
}

Return to bug 11401