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