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