]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/src/perlmods/OpenILS/Application/Actor.pm
full-path bugfix
[working/Evergreen.git] / Open-ILS / src / perlmods / OpenILS / Application / Actor.pm
1 package OpenILS::Application::Actor;
2 use base qw/OpenSRF::Application/;
3 use strict; use warnings;
4 use Data::Dumper;
5 use OpenILS::Event;
6
7 use Digest::MD5 qw(md5_hex);
8
9 use OpenSRF::EX qw(:try);
10 use OpenILS::EX;
11 use OpenILS::Perm;
12
13 use OpenILS::Application::AppUtils;
14 use OpenILS::Utils::Fieldmapper;
15 use OpenILS::Application::Search::Actor;
16 use OpenILS::Utils::ModsParser;
17 use OpenSRF::Utils::Logger;
18 my $logger = "OpenSRF::Utils::Logger";
19
20 use OpenSRF::Utils::Cache;
21
22
23 use OpenILS::Application::Actor::Container;
24 sub initialize {
25         OpenILS::Application::Actor::Container->initialize();
26 }
27
28 my $apputils = "OpenILS::Application::AppUtils";
29
30 sub _d { warn "Patron:\n" . Dumper(shift()); }
31
32 my $cache_client;
33
34
35 my $set_user_settings;
36 my $set_ou_settings;
37
38 __PACKAGE__->register_method(
39         method  => "set_user_settings",
40         api_name        => "open-ils.actor.patron.settings.update",
41 );
42 sub set_user_settings {
43         my( $self, $client, $user_session, $uid, $settings ) = @_;
44         
45         $logger->debug("Setting user settings: $user_session, $uid, " . Dumper($settings));
46
47         my( $staff, $user, $evt ) = 
48                 $apputils->checkses_requestor( $user_session, $uid, 'UPDATE_USER' );    
49         return $evt if $evt;
50         
51
52         my ($params) = map { 
53                 [{ usr => $user->id, name => $_}, {value => $$settings{$_}}] } keys %$settings;
54
55         $logger->activity("User " . $staff->id . " updating user $uid settings with: " . Dumper($params));
56
57         return $apputils->simplereq(
58                 'open-ils.storage',
59                 'open-ils.storage.direct.actor.user_setting.batch.merge', $params );
60                 
61 }
62
63
64
65 __PACKAGE__->register_method(
66         method  => "set_ou_settings",
67         api_name        => "open-ils.actor.org_unit.settings.update",
68 );
69 sub set_ou_settings {
70         my( $self, $client, $user_session, $ouid, $settings ) = @_;
71         
72         my( $staff, $evt ) = $apputils->checkses( $user_session );
73         return $evt if $evt;
74         $evt = $apputils->check_perms( $staff->id, $ouid, 'UPDATE_ORG_UNIT' );
75         return $evt if $evt;
76
77
78         my ($params) = 
79                 map { [{ org_unit => $ouid, name => $_}, {value => $$settings{$_}}] } keys %$settings;
80
81         $logger->activity("Updating org unit [$ouid] settings with: " . Dumper($params));
82
83         return $apputils->simplereq(
84                 'open-ils.storage',
85                 'open-ils.storage.direct.actor.org_unit_setting.merge', @$params );
86 }
87
88
89 my $fetch_user_settings;
90 my $fetch_ou_settings;
91
92 __PACKAGE__->register_method(
93         method  => "user_settings",
94         api_name        => "open-ils.actor.patron.settings.retrieve",
95 );
96 sub user_settings {
97         my( $self, $client, $user_session, $uid ) = @_;
98         
99         my( $staff, $user, $evt ) = 
100                 $apputils->checkses_requestor( $user_session, $uid, 'VIEW_USER' );
101         return $evt if $evt;
102
103         $logger->debug("User " . $staff->id . " fetching user $uid\n");
104         my $s = $apputils->simplereq(
105                 'open-ils.storage',
106                 'open-ils.storage.direct.actor.user_setting.search.usr.atomic',$uid );
107
108         return { map { ($_->name,$_->value) } @$s };
109 }
110
111
112
113 __PACKAGE__->register_method(
114         method  => "ou_settings",
115         api_name        => "open-ils.actor.org_unit.settings.retrieve",
116 );
117 sub ou_settings {
118         my( $self, $client, $ouid ) = @_;
119         
120         my $s = $apputils->simplereq(
121                 'open-ils.storage',
122                 'open-ils.storage.direct.actor.org_unit_setting.search.org_unit.atomic', $ouid);
123
124         return { map { ($_->name,$_->value) } @$s };
125 }
126
127
128
129 __PACKAGE__->register_method(
130         method  => "update_patron",
131         api_name        => "open-ils.actor.patron.update",);
132
133 sub update_patron {
134         my( $self, $client, $user_session, $patron ) = @_;
135
136         my $session = $apputils->start_db_session();
137         my $err = undef;
138
139         warn $user_session . " " . $patron . "\n";
140         _d($patron);
141
142         my $user_obj = 
143                 OpenILS::Application::AppUtils->check_user_session( 
144                                 $user_session ); #throws EX on error
145
146         # XXX does this user have permission to add/create users.  Granularity?
147         # $new_patron is the patron in progress.  $patron is the original patron
148         # passed in with the method.  new_patron will change as the components
149         # of patron are added/updated.
150
151         my $new_patron;
152
153         if(ref($patron->card)) { $patron->card( $patron->card->id ); }
154         if(ref($patron->billing_address)) { $patron->billing_address( $patron->billing_address->id ); }
155         if(ref($patron->mailing_address)) { $patron->mailing_address( $patron->mailing_address->id ); }
156
157         # create/update the patron first so we can use his id
158         if($patron->isnew()) {
159
160                 $new_patron = _add_patron($session, _clone_patron($patron), $user_obj);
161
162                 if(UNIVERSAL::isa($new_patron, "OpenILS::EX") || 
163                         UNIVERSAL::isa($new_patron, "OpenILS::Perm")) {
164                         $client->respond_complete($new_patron->ex);
165                         return undef;
166                 }
167
168         } else { $new_patron = $patron; }
169
170         $new_patron = _add_update_addresses($session, $patron, $new_patron, $user_obj);
171
172         if(UNIVERSAL::isa($new_patron, "OpenILS::EX") || 
173                 UNIVERSAL::isa($new_patron, "OpenILS::Perm")) {
174                 $client->respond_complete($new_patron->ex);
175                 return undef;
176         }
177
178         $new_patron = _add_update_cards($session, $patron, $new_patron, $user_obj);
179
180         if(UNIVERSAL::isa($new_patron, "OpenILS::EX") || 
181                 UNIVERSAL::isa($new_patron, "OpenILS::Perm")) {
182                 $client->respond_complete($new_patron->ex);
183                 return undef;
184         }
185
186         $new_patron = _add_survey_responses($session, $patron, $new_patron, $user_obj);
187         if(UNIVERSAL::isa($new_patron, "OpenILS::EX") || 
188                 UNIVERSAL::isa($new_patron, "OpenILS::Perm")) {
189                 $client->respond_complete($new_patron->ex);
190                 return undef;
191         }
192
193
194         # re-update the patron if anything has happened to him during this process
195         if($new_patron->ischanged()) {
196                 $new_patron = _update_patron($session, $new_patron, $user_obj);
197
198                 if(UNIVERSAL::isa($new_patron, "OpenILS::EX") || 
199                         UNIVERSAL::isa($new_patron, "OpenILS::Perm")) {
200                         $client->respond_complete($new_patron->ex);
201                         return undef;
202                 }
203         }
204
205         $session = OpenSRF::AppSession->create("open-ils.storage");
206         $new_patron     = _create_stat_maps($session, $user_session, $patron, $new_patron, $user_obj);
207         if(UNIVERSAL::isa($new_patron, "OpenILS::EX") || 
208                 UNIVERSAL::isa($new_patron, "OpenILS::Perm")) {
209                 $client->respond_complete($new_patron->ex);
210                 return undef;
211         }
212
213         $new_patron     = _create_perm_maps($session, $user_session, $patron, $new_patron, $user_obj);
214         if(UNIVERSAL::isa($new_patron, "OpenILS::EX") || 
215                 UNIVERSAL::isa($new_patron, "OpenILS::Perm")) {
216                 $client->respond_complete($new_patron->ex);
217                 return undef;
218         }
219
220         $apputils->commit_db_session($session);
221
222         warn "Patron Update/Create complete\n";
223         return flesh_user($new_patron->id());
224 }
225
226
227
228
229 __PACKAGE__->register_method(
230         method  => "user_retrieve_fleshed_by_id",
231         api_name        => "open-ils.actor.user.fleshed.retrieve",);
232
233 sub user_retrieve_fleshed_by_id {
234         my( $self, $client, $user_session, $user_id ) = @_;
235
236         my( $requestor, $target, $evt ) = $apputils->
237                 checkses_requestor( $user_session, $user_id, 'VIEW_USER' );
238         return $evt if $evt;
239
240         return flesh_user($user_id);
241 }
242
243
244 sub flesh_user {
245         my $id = shift;
246         my $session = shift;
247
248         my $kill = 0;
249
250         if(!$session) {
251                 $session = OpenSRF::AppSession->create("open-ils.storage");
252                 $kill = 1;
253         }
254
255         # grab the user with the given id 
256         my $ureq = $session->request(
257                         "open-ils.storage.direct.actor.user.retrieve", $id);
258         my $user = $ureq->gather(1);
259
260         if(!$user) { return undef; }
261
262         # grab the cards
263         my $cards_req = $session->request(
264                         "open-ils.storage.direct.actor.card.search.usr.atomic",
265                         $user->id() );
266         $user->cards( $cards_req->gather(1) );
267
268         for my $c(@{$user->cards}) {
269                 if($c->id == $user->card || $c->id eq $user->card ) {
270                         warn "Setting my card to " . $c->id . "\n";
271                         $user->card($c);
272                 }
273         }
274
275         my $add_req = $session->request(
276                         "open-ils.storage.direct.actor.user_address.search.usr.atomic",
277                         $user->id() );
278         $user->addresses( $add_req->gather(1) );
279
280         for my $c(@{$user->addresses}) {
281                 if($c->id eq $user->billing_address ) { $user->billing_address($c); }
282                 if($c->id eq $user->mailing_address ) { $user->mailing_address($c); }
283         }
284
285         my $stat_req = $session->request(
286                 "open-ils.storage.direct.actor.stat_cat_entry_user_map.search.target_usr.atomic",
287                 $user->id() );
288         $user->stat_cat_entries($stat_req->gather(1));
289
290         if($kill) { $session->disconnect(); }
291         $user->clear_passwd();
292
293         return $user;
294 }
295
296
297 # clone and clear stuff that would break the database
298 sub _clone_patron {
299         my $patron = shift;
300
301         my $new_patron = $patron->clone;
302
303         # Using the Fieldmapper clone method
304         #my $new_patron = Fieldmapper::actor::user->new();
305
306         #my $fmap = $Fieldmapper::fieldmap;
307         #no strict; # shallow clone, may be useful in the fieldmapper
308         #for my $field 
309         #       (keys %{$fmap->{"Fieldmapper::actor::user"}->{'fields'}}) {
310         #               $new_patron->$field( $patron->$field() );
311         #}
312         #use strict;
313
314         # clear these
315         $new_patron->clear_billing_address();
316         $new_patron->clear_mailing_address();
317         $new_patron->clear_addresses();
318         $new_patron->clear_card();
319         $new_patron->clear_cards();
320         $new_patron->clear_id();
321         $new_patron->clear_isnew();
322         $new_patron->clear_ischanged();
323         $new_patron->clear_isdeleted();
324         $new_patron->clear_stat_cat_entries();
325
326         return $new_patron;
327 }
328
329
330 sub _add_patron {
331         my $session             = shift;
332         my $patron              = shift;
333         my $user_obj    = shift;
334
335
336         if($apputils->check_user_perms(
337                                 $user_obj->id, $user_obj->home_ou, "CREATE_USER")) {
338                 return OpenILS::Perm->new("CREATE_USER");
339         }
340
341         warn "Creating new patron\n";
342         _d($patron);
343
344         my $req = $session->request(
345                 "open-ils.storage.direct.actor.user.create",$patron);
346         my $id = $req->gather(1);
347         if(!$id) { 
348                 return OpenILS::EX->new("DUPLICATE_USER_USERNAME");
349         }
350
351         # retrieve the patron from the db to collect defaults
352         my $ureq = $session->request(
353                         "open-ils.storage.direct.actor.user.retrieve",
354                         $id);
355
356         warn "Created new patron with id $id\n";
357
358         return $ureq->gather(1);
359 }
360
361
362 sub _update_patron {
363         my( $session, $patron, $user_obj) = @_;
364
365
366         if($patron->id ne $user_obj->id) {
367                 if($apputils->check_user_perms(
368                                         $user_obj->id, $user_obj->home_ou, "UPDATE_USER")) {
369                         return OpenILS::Perm->new("UPDATE_USER");
370                 }
371         }
372
373         warn "updating patron " . Dumper($patron) . "\n";
374
375         my $req = $session->request(
376                 "open-ils.storage.direct.actor.user.update",$patron );
377         my $status = $req->gather(1);
378         if(!defined($status)) { 
379                 throw OpenSRF::EX::ERROR 
380                         ("Unknown error updating patron"); 
381         }
382         return $patron;
383 }
384
385
386 sub _add_update_addresses {
387         my $session = shift;
388         my $patron = shift;
389         my $new_patron = shift;
390
391         my $current_id; # id of the address before creation
392
393         for my $address (@{$patron->addresses()}) {
394
395                 $address->usr($new_patron->id());
396
397                 if(ref($address) and $address->isnew()) {
398                         warn "Adding new address at street " . $address->street1() . "\n";
399
400                         $current_id = $address->id();
401                         $address = _add_address($session,$address);
402
403                         if( $patron->billing_address() and 
404                                         $patron->billing_address() == $current_id ) {
405                                 $new_patron->billing_address($address->id());
406                                 $new_patron->ischanged(1);
407                         }
408
409                         if( $patron->mailing_address() and
410                                         $patron->mailing_address() == $current_id ) {
411                                 $new_patron->mailing_address($address->id());
412                                 $new_patron->ischanged(1);
413                         }
414
415                 } elsif( ref($address) and $address->ischanged() ) {
416                         warn "Updating address at street " . $address->street1();
417                         $address->usr($new_patron->id());
418                         _update_address($session,$address);
419
420                 } elsif( ref($address) and $address->isdeleted() ) {
421                         warn "Deleting address at street " . $address->street1();
422
423                         if( $address->id() == $new_patron->mailing_address() ) {
424                                 $new_patron->clear_mailing_address();
425                                 _update_patron($session, $new_patron);
426                         }
427
428                         if( $address->id() == $new_patron->billing_address() ) {
429                                 $new_patron->clear_billing_address();
430                                 _update_patron($session, $new_patron);
431                         }
432
433                         _delete_address($session,$address);
434                 }
435         }
436
437         return $new_patron;
438 }
439
440
441 # adds an address to the db and returns the address with new id
442 sub _add_address {
443         my($session, $address) = @_;
444         $address->clear_id();
445
446         use Data::Dumper;
447         warn "Adding Address:\n";
448         warn Dumper($address);
449
450         # put the address into the database
451         my $req = $session->request(
452                 "open-ils.storage.direct.actor.user_address.create",
453                 $address );
454
455         #update the id
456         my $id = $req->gather(1);
457         if(!$id) { 
458                 throw OpenSRF::EX::ERROR 
459                         ("Unable to create new user address"); 
460         }
461
462         warn "Created address with id $id\n";
463
464         # update all the necessary id's
465         $address->id( $id );
466         return $address;
467 }
468
469
470 sub _update_address {
471         my( $session, $address ) = @_;
472         my $req = $session->request(
473                 "open-ils.storage.direct.actor.user_address.update",
474                 $address );
475         my $status = $req->gather(1);
476         if(!defined($status)) { 
477                 throw OpenSRF::EX::ERROR 
478                         ("Unknown error updating address"); 
479         }
480         return $address;
481 }
482
483
484
485 sub _add_update_cards {
486
487         my $session = shift;
488         my $patron = shift;
489         my $new_patron = shift;
490
491         my $virtual_id; #id of the card before creation
492         for my $card (@{$patron->cards()}) {
493
494                 $card->usr($new_patron->id());
495
496                 if(ref($card) and $card->isnew()) {
497
498                         $virtual_id = $card->id();
499                         $card = _add_card($session,$card);
500                         if(UNIVERSAL::isa($card,"OpenILS::EX")) {
501                                 return $card;
502                         }
503
504                         #if(ref($patron->card)) { $patron->card($patron->card->id); }
505                         if($patron->card() == $virtual_id) {
506                                 $new_patron->card($card->id());
507                                 $new_patron->ischanged(1);
508                         }
509
510                 } elsif( ref($card) and $card->ischanged() ) {
511                         $card->usr($new_patron->id());
512                         _update_card($session, $card);
513                 }
514         }
515         return $new_patron;
516 }
517
518
519 # adds an card to the db and returns the card with new id
520 sub _add_card {
521         my( $session, $card ) = @_;
522         $card->clear_id();
523
524         warn "Adding card with barcode " . $card->barcode() . "\n";
525         my $req = $session->request(
526                 "open-ils.storage.direct.actor.card.create",
527                 $card );
528
529         my $id = $req->gather(1);
530         if(!$id) { 
531                 return OpenILS::EX->new("DUPLICATE_INVALID_USER_BARCODE");
532         }
533
534         $card->id($id);
535         warn "Created patron card with id $id\n";
536         return $card;
537 }
538
539
540 sub _update_card {
541         my( $session, $card ) = @_;
542         warn Dumper $card;
543
544         my $req = $session->request(
545                 "open-ils.storage.direct.actor.card.update",
546                 $card );
547         my $status = $req->gather(1);
548         if(!defined($status)) { 
549                 throw OpenSRF::EX::ERROR 
550                         ("Unknown error updating card"); 
551         }
552         return $card;
553 }
554
555
556
557
558 sub _delete_address {
559         my( $session, $address ) = @_;
560
561         warn "Deleting address " . $address->street1() . "\n";
562
563         my $req = $session->request(
564                 "open-ils.storage.direct.actor.user_address.delete",
565                 $address );
566         my $status = $req->gather(1);
567         if(!defined($status)) { 
568                 throw OpenSRF::EX::ERROR 
569                         ("Unknown error updating address"); 
570         }
571         warn "Delete address status is $status\n";
572 }
573
574
575
576 sub _add_survey_responses {
577         my ($session, $patron, $new_patron) = @_;
578
579         warn "updating responses for user " . $new_patron->id . "\n";
580
581         my $responses = $patron->survey_responses;
582
583         if($responses) {
584
585                 for my $resp( @$responses ) {
586                         $resp->usr($new_patron->id);
587                 }
588
589                 my $status = $apputils->simple_scalar_request(
590                         "open-ils.circ", 
591                         "open-ils.circ.survey.submit.user_id",
592                         $responses );
593
594         }
595
596         return $new_patron;
597 }
598
599
600 sub _create_stat_maps {
601
602         my($session, $user_session, $patron, $new_patron) = @_;
603
604         my $maps = $patron->stat_cat_entries();
605
606         for my $map (@$maps) {
607
608                 next unless($map->isnew() || $map->ischanged());
609
610                 my $method = "open-ils.storage.direct.actor.stat_cat_entry_user_map.update";
611                 if($map->isnew()) {
612                         $method = "open-ils.storage.direct.actor.stat_cat_entry_user_map.create";
613                         $map->clear_id;
614                 }
615
616                 $map->target_usr($new_patron->id);
617
618                 warn "Updating stat entry with method $method and session $user_session and map $map\n";
619
620                 my $req = $session->request($method, $map);
621                 my $status = $req->gather(1);
622
623                 warn "Updated\n";
624
625                 if(!$status) {
626                         throw OpenSRF::EX::ERROR 
627                                 ("Error updating stat map with method $method");        
628                 }
629
630         }
631
632         return $new_patron;
633 }
634
635 sub _create_perm_maps {
636
637         my($session, $user_session, $patron, $new_patron) = @_;
638
639         my $maps = $patron->permissions;
640
641         for my $map (@$maps) {
642
643                 my $method = "open-ils.storage.direct.permission.usr_perm_map.update";
644                 if ($map->isdeleted()) {
645                         $method = "open-ils.storage.direct.permission.usr_perm_map.delete";
646                 } elsif ($map->isnew()) {
647                         $method = "open-ils.storage.direct.permission.usr_perm_map.create";
648                         $map->clear_id;
649                 }
650
651
652                 $map->usr($new_patron->id);
653
654                 warn( "Updating permissions with method $method and session $user_session and map $map" );
655                 $logger->debug( "Updating permissions with method $method and session $user_session and map $map" );
656
657                 my $req = $session->request($method, $map);
658                 my $status = $req->gather(1);
659
660                 warn "Updated\n";
661
662                 if(!$status) {
663                         throw OpenSRF::EX::ERROR 
664                                 ("Error updating permission map with method $method");  
665                 }
666
667         }
668
669         return $new_patron;
670 }
671
672
673
674 __PACKAGE__->register_method(
675         method  => "search_username",
676         api_name        => "open-ils.actor.user.search.username",
677 );
678
679 sub search_username {
680         my($self, $client, $username) = @_;
681         my $users = OpenILS::Application::AppUtils->simple_scalar_request(
682                         "open-ils.storage", 
683                         "open-ils.storage.direct.actor.user.search.usrname.atomic",
684                         $username );
685         return $users;
686 }
687
688
689
690
691 __PACKAGE__->register_method(
692         method  => "user_retrieve_by_barcode",
693         api_name        => "open-ils.actor.user.fleshed.retrieve_by_barcode",);
694
695 sub user_retrieve_by_barcode {
696         my($self, $client, $user_session, $barcode) = @_;
697
698         $logger->debug("Searching for user with barcode $barcode");
699         #my $user_obj = $apputils->check_user_session( $user_session ); 
700         my ($user_obj, $evt) = $apputils->check_ses($user_session);
701         return $evt if $evt;
702
703         my $session = OpenSRF::AppSession->create("open-ils.storage");
704
705         # find the card with the given barcode
706         my $creq        = $session->request(
707                         "open-ils.storage.direct.actor.card.search.barcode.atomic",
708                         $barcode );
709         my $card = $creq->gather(1);
710
711         if(!$card || !$card->[0]) {
712                 $session->disconnect();
713                 return OpenILS::Event->new( 'USER_NOT_FOUND' );
714         }
715
716         $card = $card->[0];
717         my $user = flesh_user($card->usr(), $session);
718         $session->disconnect();
719         if(!$user) { return OpenILS::Event->new( 'USER_NOT_FOUND' ); }
720         return $user;
721
722 }
723
724
725
726 __PACKAGE__->register_method(
727         method  => "get_user_by_id",
728         api_name        => "open-ils.actor.user.retrieve",);
729
730 sub get_user_by_id {
731         my ($self, $client, $user_session, $id) = @_;
732
733         my $user_obj = $apputils->check_user_session( $user_session ); 
734
735         return $apputils->simple_scalar_request(
736                 "open-ils.storage",
737                 "open-ils.storage.direct.actor.user.retrieve",
738                 $id );
739 }
740
741
742
743 __PACKAGE__->register_method(
744         method  => "get_org_types",
745         api_name        => "open-ils.actor.org_types.retrieve",);
746
747 my $org_types;
748 sub get_org_types {
749         my($self, $client) = @_;
750
751         return $org_types if $org_types;
752          return $org_types = 
753                  $apputils->simple_scalar_request(
754                         "open-ils.storage",
755                         "open-ils.storage.direct.actor.org_unit_type.retrieve.all.atomic" );
756 }
757
758
759
760 __PACKAGE__->register_method(
761         method  => "get_user_profiles",
762         api_name        => "open-ils.actor.user.profiles.retrieve",
763 );
764
765 my $user_profiles;
766 sub get_user_profiles {
767         return $user_profiles if $user_profiles;
768
769         return $user_profiles = 
770                 $apputils->simple_scalar_request(
771                         "open-ils.storage",
772                         "open-ils.storage.direct.actor.profile.retrieve.all.atomic");
773 }
774
775
776
777 __PACKAGE__->register_method(
778         method  => "get_user_ident_types",
779         api_name        => "open-ils.actor.user.ident_types.retrieve",
780 );
781 my $ident_types;
782 sub get_user_ident_types {
783         return $ident_types if $ident_types;
784         return $ident_types = 
785                 $apputils->simple_scalar_request(
786                 "open-ils.storage",
787                 "open-ils.storage.direct.config.identification_type.retrieve.all.atomic" );
788 }
789
790
791
792
793 __PACKAGE__->register_method(
794         method  => "get_org_unit",
795         api_name        => "open-ils.actor.org_unit.retrieve",
796 );
797
798 sub get_org_unit {
799
800         my( $self, $client, $user_session, $org_id ) = @_;
801
802         if(defined($user_session) && !defined($org_id)) {
803                 my $user_obj = 
804                         OpenILS::Application::AppUtils->check_user_session( $user_session ); #throws EX on error
805                 if(!defined($org_id)) {
806                         $org_id = $user_obj->home_ou;
807                 }
808         }
809
810
811         my $home_ou = OpenILS::Application::AppUtils->simple_scalar_request(
812                 "open-ils.storage",
813                 "open-ils.storage.direct.actor.org_unit.retrieve", 
814                 $org_id );
815
816         return $home_ou;
817 }
818
819
820 # build the org tree
821
822 __PACKAGE__->register_method(
823         method  => "get_org_tree",
824         api_name        => "open-ils.actor.org_tree.retrieve",
825         argc            => 0, 
826         note            => "Returns the entire org tree structure",
827 );
828
829 sub get_org_tree {
830         my( $self, $client) = @_;
831
832         if(!$cache_client) {
833                 $cache_client = OpenSRF::Utils::Cache->new("global", 0);
834         }
835         # see if it's in the cache
836         warn "Getting ORG Tree\n";
837         my $tree = $cache_client->get_cache('orgtree');
838         if($tree) { 
839                 warn "Found orgtree in cache. returning...\n";
840                 return $tree; 
841         }
842
843         my $orglist = $apputils->simple_scalar_request( 
844                 "open-ils.storage", 
845                 "open-ils.storage.direct.actor.org_unit.retrieve.all.atomic" );
846
847         if($orglist) {
848                 warn "found org list\n";
849         }
850
851         $tree = $self->build_org_tree($orglist);
852         $cache_client->put_cache('orgtree', $tree);
853
854         return $tree;
855
856 }
857
858 # turns an org list into an org tree
859 sub build_org_tree {
860
861         my( $self, $orglist) = @_;
862
863         return $orglist unless ( 
864                         ref($orglist) and @$orglist > 1 );
865
866         my @list = sort { 
867                 $a->ou_type <=> $b->ou_type ||
868                 $a->name cmp $b->name } @$orglist;
869
870         for my $org (@list) {
871
872                 next unless ($org and defined($org->parent_ou));
873                 my ($parent) = grep { $_->id == $org->parent_ou } @list;
874                 next unless $parent;
875
876                 $parent->children([]) unless defined($parent->children); 
877                 push( @{$parent->children}, $org );
878         }
879
880         return $list[0];
881
882 }
883
884
885 __PACKAGE__->register_method(
886         method  => "get_org_descendants",
887         api_name        => "open-ils.actor.org_tree.descendants.retrieve"
888 );
889
890 # depth is optional.  org_unit is the id
891 sub get_org_descendants {
892         my( $self, $client, $org_unit, $depth ) = @_;
893         my $orglist = $apputils->simple_scalar_request(
894                         "open-ils.storage", 
895                         "open-ils.storage.actor.org_unit.descendants.atomic",
896                         $org_unit, $depth );
897         return $self->build_org_tree($orglist);
898 }
899
900
901 __PACKAGE__->register_method(
902         method  => "get_org_ancestors",
903         api_name        => "open-ils.actor.org_tree.ancestors.retrieve"
904 );
905
906 # depth is optional.  org_unit is the id
907 sub get_org_ancestors {
908         my( $self, $client, $org_unit, $depth ) = @_;
909         my $orglist = $apputils->simple_scalar_request(
910                         "open-ils.storage", 
911                         "open-ils.storage.actor.org_unit.ancestors.atomic",
912                         $org_unit, $depth );
913         return $self->build_org_tree($orglist);
914 }
915
916
917 __PACKAGE__->register_method(
918         method  => "get_standings",
919         api_name        => "open-ils.actor.standings.retrieve"
920 );
921
922 my $user_standings;
923 sub get_standings {
924         return $user_standings if $user_standings;
925         return $user_standings = 
926                 $apputils->simple_scalar_request(
927                         "open-ils.storage",
928                         "open-ils.storage.direct.config.standing.retrieve.all.atomic" );
929 }
930
931
932
933 __PACKAGE__->register_method(
934         method  => "get_my_org_path",
935         api_name        => "open-ils.actor.org_unit.full_path.retrieve"
936 );
937
938 sub get_my_org_path {
939         my( $self, $client, $user_session, $org_id ) = @_;
940         my $user_obj = $apputils->check_user_session($user_session); 
941         if(!defined($org_id)) { $org_id = $user_obj->home_ou; }
942
943         return $apputils->simple_scalar_request(
944                 "open-ils.storage",
945                 "open-ils.storage.actor.org_unit.full_path.atomic",
946                 $org_id );
947 }
948
949
950 __PACKAGE__->register_method(
951         method  => "patron_adv_search",
952         api_name        => "open-ils.actor.patron.search.advanced" );
953
954 sub patron_adv_search {
955         my( $self, $client, $staff_login, $search_hash ) = @_;
956
957         use Data::Dumper;
958         warn "patron adv with $staff_login and search " . 
959                 Dumper($search_hash) . "\n";
960
961         my $session = OpenSRF::AppSession->create("open-ils.storage");
962         my $req = $session->request(
963                 "open-ils.storage.actor.user.crazy_search", $search_hash);
964
965         my $ans = $req->gather(1);
966
967         my %hash = map { ($_ =>1) } @$ans;
968         $ans = [ keys %hash ];
969
970         warn "Returning @$ans\n";
971
972         $session->disconnect();
973         return $ans;
974
975 }
976
977
978
979 sub _verify_password {
980         my($user_session, $password) = @_;
981         my $user_obj = $apputils->check_user_session($user_session); 
982
983         #grab the user with password
984         $user_obj = $apputils->simple_scalar_request(
985                 "open-ils.storage", 
986                 "open-ils.storage.direct.actor.user.retrieve",
987                 $user_obj->id );
988
989         if($user_obj->passwd eq $password) {
990                 return 1;
991         }
992
993         return 0;
994 }
995
996
997 __PACKAGE__->register_method(
998         method  => "update_password",
999         api_name        => "open-ils.actor.user.password.update");
1000
1001 __PACKAGE__->register_method(
1002         method  => "update_password",
1003         api_name        => "open-ils.actor.user.username.update");
1004
1005 __PACKAGE__->register_method(
1006         method  => "update_password",
1007         api_name        => "open-ils.actor.user.email.update");
1008
1009 sub update_password {
1010         my( $self, $client, $user_session, $new_value, $current_password ) = @_;
1011
1012         warn "Updating user with method " .$self->api_name . "\n";
1013         my $user_obj = $apputils->check_user_session($user_session); 
1014
1015         if($self->api_name =~ /password/) {
1016
1017                 #make sure they know the current password
1018                 if(!_verify_password($user_session, md5_hex($current_password))) {
1019                         return OpenILS::EX->new("USER_WRONG_PASSWORD")->ex;
1020                 }
1021
1022                 $user_obj->passwd($new_value);
1023         } 
1024         elsif($self->api_name =~ /username/) {
1025                 my $users = search_username(undef, undef, $new_value); 
1026                 if( $users and $users->[0] ) {
1027                         return OpenILS::Event->new('USERNAME_EXISTS');
1028                 }
1029                 $user_obj->usrname($new_value);
1030         }
1031
1032         elsif($self->api_name =~ /email/) {
1033                 warn "Updating email to $new_value\n";
1034                 $user_obj->email($new_value);
1035         }
1036
1037         my $session = $apputils->start_db_session();
1038         $user_obj = _update_patron($session, $user_obj, $user_obj);
1039         $apputils->commit_db_session($session);
1040
1041         if($user_obj) { return 1; }
1042         return undef;
1043 }
1044
1045
1046 __PACKAGE__->register_method(
1047         method  => "check_user_perms",
1048         api_name        => "open-ils.actor.user.perm.check",
1049         notes           => <<"  NOTES");
1050         Takes a login session, user id, an org id, and an array of perm type strings.  For each
1051         perm type, if the user does *not* have the given permission it is added
1052         to a list which is returned from the method.  If all permissions
1053         are allowed, an empty list is returned
1054         if the logged in user does not match 'user_id', then the logged in user must
1055         have VIEW_PERMISSION priveleges.
1056         NOTES
1057
1058 sub check_user_perms {
1059         my( $self, $client, $login_session, $user_id, $org_id, $perm_types ) = @_;
1060
1061         my( $staff, $evt ) = $apputils->checkses($login_session);
1062         return $evt if $evt;
1063
1064         if($staff->id ne $user_id) {
1065                 if( my $evt = $apputils->check_perms(
1066                         $staff->id, $org_id, 'VIEW_PERMISSION') ) {
1067                         return $evt;
1068                 }
1069         }
1070
1071         my @not_allowed;
1072         for my $perm (@$perm_types) {
1073                 if($apputils->check_perms($user_id, $org_id, $perm)) {
1074                         push @not_allowed, $perm;
1075                 }
1076         }
1077
1078         return \@not_allowed
1079 }
1080
1081 __PACKAGE__->register_method(
1082         method  => "check_user_perms2",
1083         api_name        => "open-ils.actor.user.perm.check.multi_org",
1084         notes           => q/
1085                 Checks the permissions on a list of perms and orgs for a user
1086                 @param authtoken The login session key
1087                 @param user_id The id of the user to check
1088                 @param orgs The array of org ids
1089                 @param perms The array of permission names
1090                 @return An array of  [ orgId, permissionName ] arrays that FAILED the check
1091                 if the logged in user does not match 'user_id', then the logged in user must
1092                 have VIEW_PERMISSION priveleges.
1093         /);
1094
1095 sub check_user_perms2 {
1096         my( $self, $client, $authtoken, $user_id, $orgs, $perms ) = @_;
1097
1098         my( $staff, $target, $evt ) = $apputils->checkses_requestor(
1099                 $authtoken, $user_id, 'VIEW_PERMISSION' );
1100         return $evt if $evt;
1101
1102         my @not_allowed;
1103         for my $org (@$orgs) {
1104                 for my $perm (@$perms) {
1105                         if($apputils->check_perms($user_id, $org, $perm)) {
1106                                 push @not_allowed, [ $org, $perm ];
1107                         }
1108                 }
1109         }
1110
1111         return \@not_allowed
1112 }
1113
1114
1115 __PACKAGE__->register_method(
1116         method => 'check_user_perms3',
1117         api_name        => 'open-ils.actor.user.perm.highest_org',
1118         notes           => q/
1119                 Returns the highest org unit id at which a user has a given permission
1120                 If the requestor does not match the target user, the requestor must have
1121                 'VIEW_PERMISSION' rights at the home org unit of the target user
1122                 @param authtoken The login session key
1123                 @param userid The id of the user in question
1124                 @param perm The permission to check
1125                 @return The org unit highest in the org tree within which the user has
1126                 the requested permission
1127         /);
1128
1129 sub check_user_perms3 {
1130         my( $self, $client, $authtoken, $userid, $perm ) = @_;
1131
1132         my( $staff, $target, $org, $evt );
1133
1134         ( $staff, $target, $evt ) = $apputils->checkses_requestor(
1135                 $authtoken, $userid, 'VIEW_PERMISSION' );
1136         return $evt if $evt;
1137
1138         my $tree = $self->get_org_tree();
1139         return _find_highest_perm_org( $perm, $userid, $target->home_ou, $tree );
1140 }
1141
1142
1143 sub _find_highest_perm_org {
1144         my ( $perm, $userid, $start_org, $org_tree ) = @_;
1145         my $org = $apputils->find_org($org_tree, $start_org );
1146
1147         my $lastid = undef;
1148         while( $org ) {
1149                 last if ($apputils->check_perms( $userid, $org->id, $perm )); # perm failed
1150                 $lastid = $org->id;
1151                 $org = $apputils->find_org( $org_tree, $org->parent_ou() );
1152         }
1153
1154         return $lastid;
1155 }
1156
1157 __PACKAGE__->register_method(
1158         method => 'check_user_perms4',
1159         api_name        => 'open-ils.actor.user.perm.highest_org.batch',
1160         notes           => q/
1161                 Returns the highest org unit id at which a user has a given permission
1162                 If the requestor does not match the target user, the requestor must have
1163                 'VIEW_PERMISSION' rights at the home org unit of the target user
1164                 @param authtoken The login session key
1165                 @param userid The id of the user in question
1166                 @param perms An array of perm names to check 
1167                 @return An array of orgId's  representing the org unit 
1168                 highest in the org tree within which the user has the requested permission
1169                 The arrah of orgId's has matches the order of the perms array
1170         /);
1171
1172 sub check_user_perms4 {
1173         my( $self, $client, $authtoken, $userid, $perms ) = @_;
1174         
1175         my( $staff, $target, $org, $evt );
1176
1177         ( $staff, $target, $evt ) = $apputils->checkses_requestor(
1178                 $authtoken, $userid, 'VIEW_PERMISSION' );
1179         return $evt if $evt;
1180
1181         my @arr;
1182         return [] unless ref($perms);
1183         my $tree = $self->get_org_tree();
1184
1185         for my $p (@$perms) {
1186                 push( @arr, _find_highest_perm_org( $p, $userid, $target->home_ou, $tree ) );
1187         }
1188         return \@arr;
1189 }
1190
1191
1192
1193
1194 __PACKAGE__->register_method(
1195         method  => "user_fines_summary",
1196         api_name        => "open-ils.actor.user.fines.summary",
1197         notes           => <<"  NOTES");
1198         Returns a short summary of the users total open fines, excluding voided fines
1199         Params are login_session, user_id
1200         Returns a 'mous' object.
1201         NOTES
1202
1203 sub user_fines_summary {
1204         my( $self, $client, $login_session, $user_id ) = @_;
1205
1206         my $user_obj = $apputils->check_user_session($login_session); 
1207         if($user_obj->id ne $user_id) {
1208                 if($apputils->check_user_perms($user_obj->id, $user_obj->home_ou, "VIEW_USER_FINES_SUMMARY")) {
1209                         return OpenILS::Perm->new("VIEW_USER_FINES_SUMMARY"); 
1210                 }
1211         }
1212
1213         return $apputils->simple_scalar_request( 
1214                 "open-ils.storage",
1215                 "open-ils.storage.direct.money.open_user_summary.search.usr",
1216                 $user_id );
1217
1218 }
1219
1220
1221
1222
1223 __PACKAGE__->register_method(
1224         method  => "user_transactions",
1225         api_name        => "open-ils.actor.user.transactions",
1226         notes           => <<"  NOTES");
1227         Returns a list of open user transactions (mbts objects);
1228         Params are login_session, user_id
1229         Optional third parameter is the transactions type.  defaults to all
1230         NOTES
1231
1232 __PACKAGE__->register_method(
1233         method  => "user_transactions",
1234         api_name        => "open-ils.actor.user.transactions.have_charge",
1235         notes           => <<"  NOTES");
1236         Returns a list of all open user transactions (mbts objects) that have an initial charge
1237         Params are login_session, user_id
1238         Optional third parameter is the transactions type.  defaults to all
1239         NOTES
1240
1241 __PACKAGE__->register_method(
1242         method  => "user_transactions",
1243         api_name        => "open-ils.actor.user.transactions.have_balance",
1244         notes           => <<"  NOTES");
1245         Returns a list of all open user transactions (mbts objects) that have a balance
1246         Params are login_session, user_id
1247         Optional third parameter is the transactions type.  defaults to all
1248         NOTES
1249
1250 __PACKAGE__->register_method(
1251         method  => "user_transactions",
1252         api_name        => "open-ils.actor.user.transactions.fleshed",
1253         notes           => <<"  NOTES");
1254         Returns an object/hash of transaction, circ, title where transaction = an open 
1255         user transactions (mbts objects), circ is the attached circluation, and title
1256         is the title the circ points to
1257         Params are login_session, user_id
1258         Optional third parameter is the transactions type.  defaults to all
1259         NOTES
1260
1261 __PACKAGE__->register_method(
1262         method  => "user_transactions",
1263         api_name        => "open-ils.actor.user.transactions.have_charge.fleshed",
1264         notes           => <<"  NOTES");
1265         Returns an object/hash of transaction, circ, title where transaction = an open 
1266         user transactions that has an initial charge (mbts objects), circ is the 
1267         attached circluation, and title is the title the circ points to
1268         Params are login_session, user_id
1269         Optional third parameter is the transactions type.  defaults to all
1270         NOTES
1271
1272 __PACKAGE__->register_method(
1273         method  => "user_transactions",
1274         api_name        => "open-ils.actor.user.transactions.have_balance.fleshed",
1275         notes           => <<"  NOTES");
1276         Returns an object/hash of transaction, circ, title where transaction = an open 
1277         user transaction that has a balance (mbts objects), circ is the attached 
1278         circluation, and title is the title the circ points to
1279         Params are login_session, user_id
1280         Optional third parameter is the transaction type.  defaults to all
1281         NOTES
1282
1283
1284
1285 sub user_transactions {
1286         my( $self, $client, $login_session, $user_id, $type ) = @_;
1287
1288         my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1289                 $login_session, $user_id, 'VIEW_USER_TRANSACTIONS' );
1290         return $evt if $evt;
1291         
1292         my $api = $self->api_name();
1293         my $trans;
1294         my @xact;
1295
1296         if(defined($type)) { @xact = (xact_type =>  $type); 
1297
1298         } else { @xact = (); }
1299
1300         if($api =~ /have_charge/) {
1301
1302                 $trans = $apputils->simple_scalar_request( 
1303                         "open-ils.storage",
1304                         "open-ils.storage.direct.money.open_billable_transaction_summary.search_where.atomic",
1305                         { usr => $user_id, total_owed => { ">" => 0 }, @xact });
1306
1307         } elsif($api =~ /have_balance/) {
1308
1309                 $trans =  $apputils->simple_scalar_request( 
1310                         "open-ils.storage",
1311                         "open-ils.storage.direct.money.open_billable_transaction_summary.search_where.atomic",
1312                         { usr => $user_id, balance_owed => { ">" => 0 }, @xact });
1313
1314         } else {
1315
1316                 $trans =  $apputils->simple_scalar_request( 
1317                         "open-ils.storage",
1318                         "open-ils.storage.direct.money.open_billable_transaction_summary.search_where.atomic",
1319                         { usr => $user_id, @xact });
1320         }
1321
1322         if($api !~ /fleshed/) { return $trans; }
1323
1324         warn "API: $api\n";
1325
1326         my @resp;
1327         for my $t (@$trans) {
1328                         
1329                 warn $t->id . "\n";
1330
1331                 my $circ = $apputils->simple_scalar_request(
1332                                 "open-ils.storage",
1333                                 "open-ils.storage.direct.action.circulation.retrieve",
1334                                 $t->id );
1335
1336                 next unless $circ;
1337
1338                 my $title = $apputils->simple_scalar_request(
1339                         "open-ils.storage", 
1340                         "open-ils.storage.fleshed.biblio.record_entry.retrieve_by_copy",
1341                         $circ->target_copy );
1342
1343                 next unless $title;
1344
1345                 my $u = OpenILS::Utils::ModsParser->new();
1346                 $u->start_mods_batch($title->marc());
1347                 my $mods = $u->finish_mods_batch();
1348
1349                 push @resp, {transaction => $t, circ => $circ, record => $mods };
1350
1351         }
1352
1353         return \@resp; 
1354
1355
1356
1357
1358
1359 __PACKAGE__->register_method(
1360         method  => "user_perms",
1361         api_name        => "open-ils.actor.permissions.user_perms.retrieve",
1362         argc            => 1,
1363         notes           => <<"  NOTES");
1364         Returns a list of permissions
1365         NOTES
1366 sub user_perms {
1367         my( $self, $client, $authtoken, $user ) = @_;
1368
1369         my( $staff, $evt ) = $apputils->checkses($authtoken);
1370         return $evt if $evt;
1371
1372         $user ||= $staff->id;
1373
1374         if( $user != $staff->id and $evt = $apputils->check_perms( $staff->id, $staff->home_ou, 'VIEW_PERMISSION') ) {
1375                 return $evt;
1376         }
1377
1378         return $apputils->simple_scalar_request(
1379                 "open-ils.storage",
1380                 "open-ils.storage.permission.user_perms.atomic",
1381                 $user);
1382 }
1383
1384 __PACKAGE__->register_method(
1385         method  => "retrieve_perms",
1386         api_name        => "open-ils.actor.permissions.retrieve",
1387         notes           => <<"  NOTES");
1388         Returns a list of permissions
1389         NOTES
1390 sub retrieve_perms {
1391         my( $self, $client ) = @_;
1392         return $apputils->simple_scalar_request(
1393                 "open-ils.storage",
1394                 "open-ils.storage.direct.permission.perm_list.retrieve.all.atomic");
1395 }
1396
1397 __PACKAGE__->register_method(
1398         method  => "retrieve_groups",
1399         api_name        => "open-ils.actor.groups.retrieve",
1400         notes           => <<"  NOTES");
1401         Returns a list of user groupss
1402         NOTES
1403 sub retrieve_groups {
1404         my( $self, $client ) = @_;
1405         return $apputils->simple_scalar_request(
1406                 "open-ils.storage",
1407                 "open-ils.storage.direct.permission.grp_tree.retrieve.all.atomic");
1408 }
1409
1410 __PACKAGE__->register_method(
1411         method  => "retrieve_groups_tree",
1412         api_name        => "open-ils.actor.groups.tree.retrieve",
1413         notes           => <<"  NOTES");
1414         Returns a list of user groups
1415         NOTES
1416 sub retrieve_groups_tree {
1417         my( $self, $client ) = @_;
1418         my $groups = $apputils->simple_scalar_request(
1419                 "open-ils.storage",
1420                 "open-ils.storage.direct.permission.grp_tree.retrieve.all.atomic");
1421         return $self->build_group_tree($groups);        
1422 }
1423
1424
1425 # turns an org list into an org tree
1426 sub build_group_tree {
1427
1428         my( $self, $grplist) = @_;
1429
1430         return $grplist unless ( 
1431                         ref($grplist) and @$grplist > 1 );
1432
1433         my @list = sort { $a->name cmp $b->name } @$grplist;
1434
1435         my $root;
1436         for my $grp (@list) {
1437
1438                 if ($grp and !defined($grp->parent)) {
1439                         $root = $grp;
1440                         next;
1441                 }
1442                 my ($parent) = grep { $_->id == $grp->parent} @list;
1443
1444                 $parent->children([]) unless defined($parent->children); 
1445                 push( @{$parent->children}, $grp );
1446         }
1447
1448         return $root;
1449
1450 }
1451
1452
1453 __PACKAGE__->register_method(
1454         method  => "add_user_to_groups",
1455         api_name        => "open-ils.actor.user.set_groups",
1456         notes           => <<"  NOTES");
1457         Adds a user to one or more permission groups
1458         NOTES
1459
1460 sub add_user_to_groups {
1461         my( $self, $client, $authtoken, $userid, $groups ) = @_;
1462
1463         my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
1464                 $authtoken, $userid, 'CREATE_USER_GROUP_LINK' );
1465         return $evt if $evt;
1466
1467         ( $requestor, $target, $evt ) = $apputils->checkses_requestor(
1468                 $authtoken, $userid, 'REMOVE_USER_GROUP_LINK' );
1469         return $evt if $evt;
1470
1471         $apputils->simplereq(
1472                 'open-ils.storage',
1473                 'open-ils.storage.direct.permission.usr_grp_map.mass_delete', { usr => $userid } );
1474                 
1475         for my $group (@$groups) {
1476                 my $link = Fieldmapper::permission::usr_grp_map->new;
1477                 $link->grp($group);
1478                 $link->usr($userid);
1479
1480                 my $id = $apputils->simplereq(
1481                         'open-ils.storage',
1482                         'open-ils.storage.direct.permission.usr_grp_map.create', $link );
1483         }
1484
1485         return 1;
1486 }
1487
1488 __PACKAGE__->register_method(
1489         method  => "get_user_perm_groups",
1490         api_name        => "open-ils.actor.user.get_groups",
1491         notes           => <<"  NOTES");
1492         Retrieve a user's permission groups.
1493         NOTES
1494
1495
1496 sub get_user_perm_groups {
1497         my( $self, $client, $authtoken, $userid ) = @_;
1498
1499         my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
1500                 $authtoken, $userid, 'VIEW_PERM_GROUPS' );
1501         return $evt if $evt;
1502
1503         return $apputils->simplereq(
1504                 'open-ils.storage',
1505                 'open-ils.storage.direct.permission.usr_grp_map.search.usr.atomic', $userid );
1506 }       
1507
1508
1509
1510
1511 1;
1512