]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/perlmods/OpenILS/Application/Actor.pm
248a743adcf16c8b0d71b836806dc03e1a28099a
[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         my $method;
693
694         for my $map (@$maps) {
695
696                 if ($map->isdeleted()) {
697                         $method = "open-ils.storage.direct.actor.user_standing_penalty.delete";
698                 } elsif ($map->isnew()) {
699                         $method = "open-ils.storage.direct.actor.user_standing_penalty.create";
700                         $map->clear_id;
701                 } else {
702                         next;
703                 }
704
705                 $map->usr($new_patron->id);
706
707                 $logger->debug( "Updating standing penalty with method $method and session $user_session and map $map" );
708
709                 my $req = $session->request($method, $map);
710                 my $status = $req->gather(1);
711
712
713                 if(!$status) {
714                         throw OpenSRF::EX::ERROR 
715                                 ("Error updating standing penalty with method $method");        
716                 }
717
718         }
719
720         return $new_patron;
721 }
722
723
724
725 __PACKAGE__->register_method(
726         method  => "search_username",
727         api_name        => "open-ils.actor.user.search.username",
728 );
729
730 sub search_username {
731         my($self, $client, $username) = @_;
732         my $users = OpenILS::Application::AppUtils->simple_scalar_request(
733                         "open-ils.storage", 
734                         "open-ils.storage.direct.actor.user.search.usrname.atomic",
735                         $username );
736         return $users;
737 }
738
739
740
741
742 __PACKAGE__->register_method(
743         method  => "user_retrieve_by_barcode",
744         api_name        => "open-ils.actor.user.fleshed.retrieve_by_barcode",);
745
746 sub user_retrieve_by_barcode {
747         my($self, $client, $user_session, $barcode) = @_;
748
749         $logger->debug("Searching for user with barcode $barcode");
750         #my $user_obj = $apputils->check_user_session( $user_session ); 
751         my ($user_obj, $evt) = $apputils->check_ses($user_session);
752         return $evt if $evt;
753
754         my $session = OpenSRF::AppSession->create("open-ils.storage");
755
756         # find the card with the given barcode
757         my $creq        = $session->request(
758                         "open-ils.storage.direct.actor.card.search.barcode.atomic",
759                         $barcode );
760         my $card = $creq->gather(1);
761
762         if(!$card || !$card->[0]) {
763                 $session->disconnect();
764                 return OpenILS::Event->new( 'USER_NOT_FOUND' );
765         }
766
767         $card = $card->[0];
768         my $user = flesh_user($card->usr(), $session);
769         $session->disconnect();
770         if(!$user) { return OpenILS::Event->new( 'USER_NOT_FOUND' ); }
771         return $user;
772
773 }
774
775
776
777 __PACKAGE__->register_method(
778         method  => "get_user_by_id",
779         api_name        => "open-ils.actor.user.retrieve",);
780
781 sub get_user_by_id {
782         my ($self, $client, $user_session, $id) = @_;
783
784         my $user_obj = $apputils->check_user_session( $user_session ); 
785
786         return $apputils->simple_scalar_request(
787                 "open-ils.storage",
788                 "open-ils.storage.direct.actor.user.retrieve",
789                 $id );
790 }
791
792
793
794 __PACKAGE__->register_method(
795         method  => "get_org_types",
796         api_name        => "open-ils.actor.org_types.retrieve",);
797
798 my $org_types;
799 sub get_org_types {
800         my($self, $client) = @_;
801
802         return $org_types if $org_types;
803          return $org_types = 
804                  $apputils->simple_scalar_request(
805                         "open-ils.storage",
806                         "open-ils.storage.direct.actor.org_unit_type.retrieve.all.atomic" );
807 }
808
809
810
811 __PACKAGE__->register_method(
812         method  => "get_user_profiles",
813         api_name        => "open-ils.actor.user.profiles.retrieve",
814 );
815
816 my $user_profiles;
817 sub get_user_profiles {
818         return $user_profiles if $user_profiles;
819
820         return $user_profiles = 
821                 $apputils->simple_scalar_request(
822                         "open-ils.storage",
823                         "open-ils.storage.direct.actor.profile.retrieve.all.atomic");
824 }
825
826
827
828 __PACKAGE__->register_method(
829         method  => "get_user_ident_types",
830         api_name        => "open-ils.actor.user.ident_types.retrieve",
831 );
832 my $ident_types;
833 sub get_user_ident_types {
834         return $ident_types if $ident_types;
835         return $ident_types = 
836                 $apputils->simple_scalar_request(
837                 "open-ils.storage",
838                 "open-ils.storage.direct.config.identification_type.retrieve.all.atomic" );
839 }
840
841
842
843
844 __PACKAGE__->register_method(
845         method  => "get_org_unit",
846         api_name        => "open-ils.actor.org_unit.retrieve",
847 );
848
849 sub get_org_unit {
850
851         my( $self, $client, $user_session, $org_id ) = @_;
852
853         if(defined($user_session) && !defined($org_id)) {
854                 my $user_obj = 
855                         OpenILS::Application::AppUtils->check_user_session( $user_session ); #throws EX on error
856                 if(!defined($org_id)) {
857                         $org_id = $user_obj->home_ou;
858                 }
859         }
860
861
862         my $home_ou = OpenILS::Application::AppUtils->simple_scalar_request(
863                 "open-ils.storage",
864                 "open-ils.storage.direct.actor.org_unit.retrieve", 
865                 $org_id );
866
867         return $home_ou;
868 }
869
870
871 # build the org tree
872
873 __PACKAGE__->register_method(
874         method  => "get_org_tree",
875         api_name        => "open-ils.actor.org_tree.retrieve",
876         argc            => 0, 
877         note            => "Returns the entire org tree structure",
878 );
879
880 sub get_org_tree {
881         my( $self, $client) = @_;
882
883         if(!$cache_client) {
884                 $cache_client = OpenSRF::Utils::Cache->new("global", 0);
885         }
886         # see if it's in the cache
887         #warn "Getting ORG Tree\n";
888         my $tree = $cache_client->get_cache('orgtree');
889         if($tree) { 
890                 #warn "Found orgtree in cache. returning...\n";
891                 return $tree; 
892         }
893
894         my $orglist = $apputils->simple_scalar_request( 
895                 "open-ils.storage", 
896                 "open-ils.storage.direct.actor.org_unit.retrieve.all.atomic" );
897
898         #if($orglist) {
899                 #warn "found org list\n";
900         #}
901
902         $tree = $self->build_org_tree($orglist);
903         $cache_client->put_cache('orgtree', $tree);
904
905         return $tree;
906
907 }
908
909 # turns an org list into an org tree
910 sub build_org_tree {
911
912         my( $self, $orglist) = @_;
913
914         return $orglist unless ( 
915                         ref($orglist) and @$orglist > 1 );
916
917         my @list = sort { 
918                 $a->ou_type <=> $b->ou_type ||
919                 $a->name cmp $b->name } @$orglist;
920
921         for my $org (@list) {
922
923                 next unless ($org and defined($org->parent_ou));
924                 my ($parent) = grep { $_->id == $org->parent_ou } @list;
925                 next unless $parent;
926
927                 $parent->children([]) unless defined($parent->children); 
928                 push( @{$parent->children}, $org );
929         }
930
931         return $list[0];
932
933 }
934
935
936 __PACKAGE__->register_method(
937         method  => "get_org_descendants",
938         api_name        => "open-ils.actor.org_tree.descendants.retrieve"
939 );
940
941 # depth is optional.  org_unit is the id
942 sub get_org_descendants {
943         my( $self, $client, $org_unit, $depth ) = @_;
944         my $orglist = $apputils->simple_scalar_request(
945                         "open-ils.storage", 
946                         "open-ils.storage.actor.org_unit.descendants.atomic",
947                         $org_unit, $depth );
948         return $self->build_org_tree($orglist);
949 }
950
951
952 __PACKAGE__->register_method(
953         method  => "get_org_ancestors",
954         api_name        => "open-ils.actor.org_tree.ancestors.retrieve"
955 );
956
957 # depth is optional.  org_unit is the id
958 sub get_org_ancestors {
959         my( $self, $client, $org_unit, $depth ) = @_;
960         my $orglist = $apputils->simple_scalar_request(
961                         "open-ils.storage", 
962                         "open-ils.storage.actor.org_unit.ancestors.atomic",
963                         $org_unit, $depth );
964         return $self->build_org_tree($orglist);
965 }
966
967
968 __PACKAGE__->register_method(
969         method  => "get_standings",
970         api_name        => "open-ils.actor.standings.retrieve"
971 );
972
973 my $user_standings;
974 sub get_standings {
975         return $user_standings if $user_standings;
976         return $user_standings = 
977                 $apputils->simple_scalar_request(
978                         "open-ils.storage",
979                         "open-ils.storage.direct.config.standing.retrieve.all.atomic" );
980 }
981
982
983
984 __PACKAGE__->register_method(
985         method  => "get_my_org_path",
986         api_name        => "open-ils.actor.org_unit.full_path.retrieve"
987 );
988
989 sub get_my_org_path {
990         my( $self, $client, $user_session, $org_id ) = @_;
991         my $user_obj = $apputils->check_user_session($user_session); 
992         if(!defined($org_id)) { $org_id = $user_obj->home_ou; }
993
994         return $apputils->simple_scalar_request(
995                 "open-ils.storage",
996                 "open-ils.storage.actor.org_unit.full_path.atomic",
997                 $org_id );
998 }
999
1000
1001 __PACKAGE__->register_method(
1002         method  => "patron_adv_search",
1003         api_name        => "open-ils.actor.patron.search.advanced" );
1004
1005 sub patron_adv_search {
1006         my( $self, $client, $staff_login, $search_hash ) = @_;
1007
1008         use Data::Dumper;
1009         #warn "patron adv with $staff_login and search " . 
1010                 #Dumper($search_hash) . "\n";
1011
1012         my $session = OpenSRF::AppSession->create("open-ils.storage");
1013         my $req = $session->request(
1014                 "open-ils.storage.actor.user.crazy_search", $search_hash);
1015
1016         my $ans = $req->gather(1);
1017
1018         my %hash = map { ($_ =>1) } @$ans;
1019         $ans = [ keys %hash ];
1020
1021         #warn "Returning @$ans\n";
1022
1023         $session->disconnect();
1024         return $ans;
1025
1026 }
1027
1028
1029
1030 sub _verify_password {
1031         my($user_session, $password) = @_;
1032         my $user_obj = $apputils->check_user_session($user_session); 
1033
1034         #grab the user with password
1035         $user_obj = $apputils->simple_scalar_request(
1036                 "open-ils.storage", 
1037                 "open-ils.storage.direct.actor.user.retrieve",
1038                 $user_obj->id );
1039
1040         if($user_obj->passwd eq $password) {
1041                 return 1;
1042         }
1043
1044         return 0;
1045 }
1046
1047
1048 __PACKAGE__->register_method(
1049         method  => "update_password",
1050         api_name        => "open-ils.actor.user.password.update");
1051
1052 __PACKAGE__->register_method(
1053         method  => "update_password",
1054         api_name        => "open-ils.actor.user.username.update");
1055
1056 __PACKAGE__->register_method(
1057         method  => "update_password",
1058         api_name        => "open-ils.actor.user.email.update");
1059
1060 sub update_password {
1061         my( $self, $client, $user_session, $new_value, $current_password ) = @_;
1062
1063         #warn "Updating user with method " .$self->api_name . "\n";
1064         my $user_obj = $apputils->check_user_session($user_session); 
1065
1066         if($self->api_name =~ /password/) {
1067
1068                 #make sure they know the current password
1069                 if(!_verify_password($user_session, md5_hex($current_password))) {
1070                         return OpenILS::EX->new("USER_WRONG_PASSWORD")->ex;
1071                 }
1072
1073                 $user_obj->passwd($new_value);
1074         } 
1075         elsif($self->api_name =~ /username/) {
1076                 my $users = search_username(undef, undef, $new_value); 
1077                 if( $users and $users->[0] ) {
1078                         return OpenILS::Event->new('USERNAME_EXISTS');
1079                 }
1080                 $user_obj->usrname($new_value);
1081         }
1082
1083         elsif($self->api_name =~ /email/) {
1084                 #warn "Updating email to $new_value\n";
1085                 $user_obj->email($new_value);
1086         }
1087
1088         my $session = $apputils->start_db_session();
1089         $user_obj = _update_patron($session, $user_obj, $user_obj);
1090         $apputils->commit_db_session($session);
1091
1092         if($user_obj) { return 1; }
1093         return undef;
1094 }
1095
1096
1097 __PACKAGE__->register_method(
1098         method  => "check_user_perms",
1099         api_name        => "open-ils.actor.user.perm.check",
1100         notes           => <<"  NOTES");
1101         Takes a login session, user id, an org id, and an array of perm type strings.  For each
1102         perm type, if the user does *not* have the given permission it is added
1103         to a list which is returned from the method.  If all permissions
1104         are allowed, an empty list is returned
1105         if the logged in user does not match 'user_id', then the logged in user must
1106         have VIEW_PERMISSION priveleges.
1107         NOTES
1108
1109 sub check_user_perms {
1110         my( $self, $client, $login_session, $user_id, $org_id, $perm_types ) = @_;
1111
1112         my( $staff, $evt ) = $apputils->checkses($login_session);
1113         return $evt if $evt;
1114
1115         if($staff->id ne $user_id) {
1116                 if( my $evt = $apputils->check_perms(
1117                         $staff->id, $org_id, 'VIEW_PERMISSION') ) {
1118                         return $evt;
1119                 }
1120         }
1121
1122         my @not_allowed;
1123         for my $perm (@$perm_types) {
1124                 if($apputils->check_perms($user_id, $org_id, $perm)) {
1125                         push @not_allowed, $perm;
1126                 }
1127         }
1128
1129         return \@not_allowed
1130 }
1131
1132 __PACKAGE__->register_method(
1133         method  => "check_user_perms2",
1134         api_name        => "open-ils.actor.user.perm.check.multi_org",
1135         notes           => q/
1136                 Checks the permissions on a list of perms and orgs for a user
1137                 @param authtoken The login session key
1138                 @param user_id The id of the user to check
1139                 @param orgs The array of org ids
1140                 @param perms The array of permission names
1141                 @return An array of  [ orgId, permissionName ] arrays that FAILED the check
1142                 if the logged in user does not match 'user_id', then the logged in user must
1143                 have VIEW_PERMISSION priveleges.
1144         /);
1145
1146 sub check_user_perms2 {
1147         my( $self, $client, $authtoken, $user_id, $orgs, $perms ) = @_;
1148
1149         my( $staff, $target, $evt ) = $apputils->checkses_requestor(
1150                 $authtoken, $user_id, 'VIEW_PERMISSION' );
1151         return $evt if $evt;
1152
1153         my @not_allowed;
1154         for my $org (@$orgs) {
1155                 for my $perm (@$perms) {
1156                         if($apputils->check_perms($user_id, $org, $perm)) {
1157                                 push @not_allowed, [ $org, $perm ];
1158                         }
1159                 }
1160         }
1161
1162         return \@not_allowed
1163 }
1164
1165
1166 __PACKAGE__->register_method(
1167         method => 'check_user_perms3',
1168         api_name        => 'open-ils.actor.user.perm.highest_org',
1169         notes           => q/
1170                 Returns the highest org unit id at which a user has a given permission
1171                 If the requestor does not match the target user, the requestor must have
1172                 'VIEW_PERMISSION' rights at the home org unit of the target user
1173                 @param authtoken The login session key
1174                 @param userid The id of the user in question
1175                 @param perm The permission to check
1176                 @return The org unit highest in the org tree within which the user has
1177                 the requested permission
1178         /);
1179
1180 sub check_user_perms3 {
1181         my( $self, $client, $authtoken, $userid, $perm ) = @_;
1182
1183         my( $staff, $target, $org, $evt );
1184
1185         ( $staff, $target, $evt ) = $apputils->checkses_requestor(
1186                 $authtoken, $userid, 'VIEW_PERMISSION' );
1187         return $evt if $evt;
1188
1189         my $tree = $self->get_org_tree();
1190         return _find_highest_perm_org( $perm, $userid, $target->home_ou, $tree );
1191 }
1192
1193
1194 sub _find_highest_perm_org {
1195         my ( $perm, $userid, $start_org, $org_tree ) = @_;
1196         my $org = $apputils->find_org($org_tree, $start_org );
1197
1198         my $lastid = undef;
1199         while( $org ) {
1200                 last if ($apputils->check_perms( $userid, $org->id, $perm )); # perm failed
1201                 $lastid = $org->id;
1202                 $org = $apputils->find_org( $org_tree, $org->parent_ou() );
1203         }
1204
1205         return $lastid;
1206 }
1207
1208 __PACKAGE__->register_method(
1209         method => 'check_user_perms4',
1210         api_name        => 'open-ils.actor.user.perm.highest_org.batch',
1211         notes           => q/
1212                 Returns the highest org unit id at which a user has a given permission
1213                 If the requestor does not match the target user, the requestor must have
1214                 'VIEW_PERMISSION' rights at the home org unit of the target user
1215                 @param authtoken The login session key
1216                 @param userid The id of the user in question
1217                 @param perms An array of perm names to check 
1218                 @return An array of orgId's  representing the org unit 
1219                 highest in the org tree within which the user has the requested permission
1220                 The arrah of orgId's has matches the order of the perms array
1221         /);
1222
1223 sub check_user_perms4 {
1224         my( $self, $client, $authtoken, $userid, $perms ) = @_;
1225         
1226         my( $staff, $target, $org, $evt );
1227
1228         ( $staff, $target, $evt ) = $apputils->checkses_requestor(
1229                 $authtoken, $userid, 'VIEW_PERMISSION' );
1230         return $evt if $evt;
1231
1232         my @arr;
1233         return [] unless ref($perms);
1234         my $tree = $self->get_org_tree();
1235
1236         for my $p (@$perms) {
1237                 push( @arr, _find_highest_perm_org( $p, $userid, $target->home_ou, $tree ) );
1238         }
1239         return \@arr;
1240 }
1241
1242
1243
1244
1245 __PACKAGE__->register_method(
1246         method  => "user_fines_summary",
1247         api_name        => "open-ils.actor.user.fines.summary",
1248         notes           => <<"  NOTES");
1249         Returns a short summary of the users total open fines, excluding voided fines
1250         Params are login_session, user_id
1251         Returns a 'mous' object.
1252         NOTES
1253
1254 sub user_fines_summary {
1255         my( $self, $client, $login_session, $user_id ) = @_;
1256
1257         my $user_obj = $apputils->check_user_session($login_session); 
1258         if($user_obj->id ne $user_id) {
1259                 if($apputils->check_user_perms($user_obj->id, $user_obj->home_ou, "VIEW_USER_FINES_SUMMARY")) {
1260                         return OpenILS::Perm->new("VIEW_USER_FINES_SUMMARY"); 
1261                 }
1262         }
1263
1264         return $apputils->simple_scalar_request( 
1265                 "open-ils.storage",
1266                 "open-ils.storage.direct.money.open_user_summary.search.usr",
1267                 $user_id );
1268
1269 }
1270
1271
1272
1273
1274 __PACKAGE__->register_method(
1275         method  => "user_transactions",
1276         api_name        => "open-ils.actor.user.transactions",
1277         notes           => <<"  NOTES");
1278         Returns a list of open user transactions (mbts objects);
1279         Params are login_session, user_id
1280         Optional third parameter is the transactions type.  defaults to all
1281         NOTES
1282
1283 __PACKAGE__->register_method(
1284         method  => "user_transactions",
1285         api_name        => "open-ils.actor.user.transactions.have_charge",
1286         notes           => <<"  NOTES");
1287         Returns a list of all open user transactions (mbts objects) that have an initial charge
1288         Params are login_session, user_id
1289         Optional third parameter is the transactions type.  defaults to all
1290         NOTES
1291
1292 __PACKAGE__->register_method(
1293         method  => "user_transactions",
1294         api_name        => "open-ils.actor.user.transactions.have_balance",
1295         notes           => <<"  NOTES");
1296         Returns a list of all open user transactions (mbts objects) that have a balance
1297         Params are login_session, user_id
1298         Optional third parameter is the transactions type.  defaults to all
1299         NOTES
1300
1301 __PACKAGE__->register_method(
1302         method  => "user_transactions",
1303         api_name        => "open-ils.actor.user.transactions.fleshed",
1304         notes           => <<"  NOTES");
1305         Returns an object/hash of transaction, circ, title where transaction = an open 
1306         user transactions (mbts objects), circ is the attached circluation, and title
1307         is the title the circ points to
1308         Params are login_session, user_id
1309         Optional third parameter is the transactions type.  defaults to all
1310         NOTES
1311
1312 __PACKAGE__->register_method(
1313         method  => "user_transactions",
1314         api_name        => "open-ils.actor.user.transactions.have_charge.fleshed",
1315         notes           => <<"  NOTES");
1316         Returns an object/hash of transaction, circ, title where transaction = an open 
1317         user transactions that has an initial charge (mbts objects), circ is the 
1318         attached circluation, and title is the title the circ points to
1319         Params are login_session, user_id
1320         Optional third parameter is the transactions type.  defaults to all
1321         NOTES
1322
1323 __PACKAGE__->register_method(
1324         method  => "user_transactions",
1325         api_name        => "open-ils.actor.user.transactions.have_balance.fleshed",
1326         notes           => <<"  NOTES");
1327         Returns an object/hash of transaction, circ, title where transaction = an open 
1328         user transaction that has a balance (mbts objects), circ is the attached 
1329         circluation, and title is the title the circ points to
1330         Params are login_session, user_id
1331         Optional third parameter is the transaction type.  defaults to all
1332         NOTES
1333
1334
1335
1336 sub user_transactions {
1337         my( $self, $client, $login_session, $user_id, $type ) = @_;
1338
1339         my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1340                 $login_session, $user_id, 'VIEW_USER_TRANSACTIONS' );
1341         return $evt if $evt;
1342         
1343         my $api = $self->api_name();
1344         my $trans;
1345         my @xact;
1346
1347         if(defined($type)) { @xact = (xact_type =>  $type); 
1348
1349         } else { @xact = (); }
1350
1351         if($api =~ /have_charge/) {
1352
1353                 $trans = $apputils->simple_scalar_request( 
1354                         "open-ils.storage",
1355                         "open-ils.storage.direct.money.open_billable_transaction_summary.search_where.atomic",
1356                         { usr => $user_id, total_owed => { ">" => 0 }, @xact });
1357
1358         } elsif($api =~ /have_balance/) {
1359
1360                 $trans =  $apputils->simple_scalar_request( 
1361                         "open-ils.storage",
1362                         "open-ils.storage.direct.money.open_billable_transaction_summary.search_where.atomic",
1363                         { usr => $user_id, balance_owed => { ">" => 0 }, @xact });
1364
1365         } else {
1366
1367                 $trans =  $apputils->simple_scalar_request( 
1368                         "open-ils.storage",
1369                         "open-ils.storage.direct.money.open_billable_transaction_summary.search_where.atomic",
1370                         { usr => $user_id, @xact });
1371         }
1372
1373         if($api !~ /fleshed/) { return $trans; }
1374
1375         #warn "API: $api\n";
1376
1377         my @resp;
1378         for my $t (@$trans) {
1379                         
1380                 #warn $t->id . "\n";
1381
1382
1383                 if( $t->xact_type ne 'circulation' ) {
1384                         push @resp, {transaction => $t};
1385                         next;
1386                 }
1387
1388                 my $circ = $apputils->simple_scalar_request(
1389                                 "open-ils.storage",
1390                                 "open-ils.storage.direct.action.circulation.retrieve",
1391                                 $t->id );
1392
1393                 next unless $circ;
1394
1395                 my $title = $apputils->simple_scalar_request(
1396                         "open-ils.storage", 
1397                         "open-ils.storage.fleshed.biblio.record_entry.retrieve_by_copy",
1398                         $circ->target_copy );
1399
1400                 next unless $title;
1401
1402                 my $u = OpenILS::Utils::ModsParser->new();
1403                 $u->start_mods_batch($title->marc());
1404                 my $mods = $u->finish_mods_batch();
1405
1406                 push @resp, {transaction => $t, circ => $circ, record => $mods };
1407
1408         }
1409
1410         return \@resp; 
1411
1412
1413
1414
1415
1416 __PACKAGE__->register_method(
1417         method  => "user_perms",
1418         api_name        => "open-ils.actor.permissions.user_perms.retrieve",
1419         argc            => 1,
1420         notes           => <<"  NOTES");
1421         Returns a list of permissions
1422         NOTES
1423 sub user_perms {
1424         my( $self, $client, $authtoken, $user ) = @_;
1425
1426         my( $staff, $evt ) = $apputils->checkses($authtoken);
1427         return $evt if $evt;
1428
1429         $user ||= $staff->id;
1430
1431         if( $user != $staff->id and $evt = $apputils->check_perms( $staff->id, $staff->home_ou, 'VIEW_PERMISSION') ) {
1432                 return $evt;
1433         }
1434
1435         return $apputils->simple_scalar_request(
1436                 "open-ils.storage",
1437                 "open-ils.storage.permission.user_perms.atomic",
1438                 $user);
1439 }
1440
1441 __PACKAGE__->register_method(
1442         method  => "retrieve_perms",
1443         api_name        => "open-ils.actor.permissions.retrieve",
1444         notes           => <<"  NOTES");
1445         Returns a list of permissions
1446         NOTES
1447 sub retrieve_perms {
1448         my( $self, $client ) = @_;
1449         return $apputils->simple_scalar_request(
1450                 "open-ils.storage",
1451                 "open-ils.storage.direct.permission.perm_list.retrieve.all.atomic");
1452 }
1453
1454 __PACKAGE__->register_method(
1455         method  => "retrieve_groups",
1456         api_name        => "open-ils.actor.groups.retrieve",
1457         notes           => <<"  NOTES");
1458         Returns a list of user groupss
1459         NOTES
1460 sub retrieve_groups {
1461         my( $self, $client ) = @_;
1462         return $apputils->simple_scalar_request(
1463                 "open-ils.storage",
1464                 "open-ils.storage.direct.permission.grp_tree.retrieve.all.atomic");
1465 }
1466
1467 __PACKAGE__->register_method(
1468         method  => "retrieve_groups_tree",
1469         api_name        => "open-ils.actor.groups.tree.retrieve",
1470         notes           => <<"  NOTES");
1471         Returns a list of user groups
1472         NOTES
1473 sub retrieve_groups_tree {
1474         my( $self, $client ) = @_;
1475         my $groups = $apputils->simple_scalar_request(
1476                 "open-ils.storage",
1477                 "open-ils.storage.direct.permission.grp_tree.retrieve.all.atomic");
1478         return $self->build_group_tree($groups);        
1479 }
1480
1481
1482 # turns an org list into an org tree
1483 sub build_group_tree {
1484
1485         my( $self, $grplist) = @_;
1486
1487         return $grplist unless ( 
1488                         ref($grplist) and @$grplist > 1 );
1489
1490         my @list = sort { $a->name cmp $b->name } @$grplist;
1491
1492         my $root;
1493         for my $grp (@list) {
1494
1495                 if ($grp and !defined($grp->parent)) {
1496                         $root = $grp;
1497                         next;
1498                 }
1499                 my ($parent) = grep { $_->id == $grp->parent} @list;
1500
1501                 $parent->children([]) unless defined($parent->children); 
1502                 push( @{$parent->children}, $grp );
1503         }
1504
1505         return $root;
1506
1507 }
1508
1509
1510 __PACKAGE__->register_method(
1511         method  => "add_user_to_groups",
1512         api_name        => "open-ils.actor.user.set_groups",
1513         notes           => <<"  NOTES");
1514         Adds a user to one or more permission groups
1515         NOTES
1516
1517 sub add_user_to_groups {
1518         my( $self, $client, $authtoken, $userid, $groups ) = @_;
1519
1520         my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
1521                 $authtoken, $userid, 'CREATE_USER_GROUP_LINK' );
1522         return $evt if $evt;
1523
1524         ( $requestor, $target, $evt ) = $apputils->checkses_requestor(
1525                 $authtoken, $userid, 'REMOVE_USER_GROUP_LINK' );
1526         return $evt if $evt;
1527
1528         $apputils->simplereq(
1529                 'open-ils.storage',
1530                 'open-ils.storage.direct.permission.usr_grp_map.mass_delete', { usr => $userid } );
1531                 
1532         for my $group (@$groups) {
1533                 my $link = Fieldmapper::permission::usr_grp_map->new;
1534                 $link->grp($group);
1535                 $link->usr($userid);
1536
1537                 my $id = $apputils->simplereq(
1538                         'open-ils.storage',
1539                         'open-ils.storage.direct.permission.usr_grp_map.create', $link );
1540         }
1541
1542         return 1;
1543 }
1544
1545 __PACKAGE__->register_method(
1546         method  => "get_user_perm_groups",
1547         api_name        => "open-ils.actor.user.get_groups",
1548         notes           => <<"  NOTES");
1549         Retrieve a user's permission groups.
1550         NOTES
1551
1552
1553 sub get_user_perm_groups {
1554         my( $self, $client, $authtoken, $userid ) = @_;
1555
1556         my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
1557                 $authtoken, $userid, 'VIEW_PERM_GROUPS' );
1558         return $evt if $evt;
1559
1560         return $apputils->simplereq(
1561                 'open-ils.storage',
1562                 'open-ils.storage.direct.permission.usr_grp_map.search.usr.atomic', $userid );
1563 }       
1564
1565
1566
1567
1568 1;
1569