1 package OpenILS::Application::Actor;
2 use base qw/OpenSRF::Application/;
3 use strict; use warnings;
6 use Digest::MD5 qw(md5_hex);
8 use OpenSRF::EX qw(:try);
11 use OpenILS::Application::AppUtils;
12 use OpenILS::Utils::Fieldmapper;
13 use OpenILS::Application::Search::Actor;
15 my $apputils = "OpenILS::Application::AppUtils";
16 sub _d { warn "Patron:\n" . Dumper(shift()); }
17 my $cache_client = OpenSRF::Utils::Cache->new("global", 0);
20 __PACKAGE__->register_method(
21 method => "update_patron",
22 api_name => "open-ils.actor.patron.update",);
25 my( $self, $client, $user_session, $patron ) = @_;
27 my $session = $apputils->start_db_session();
30 warn $user_session . " " . $patron . "\n";
34 OpenILS::Application::AppUtils->check_user_session(
35 $user_session ); #throws EX on error
37 # XXX does this user have permission to add/create users. Granularity?
39 # $new_patron is the patron in progress. $patron is the original patron
40 # passed in with the method. new_patron will change as the components
41 # of patron are added/updated.
46 # create/update the patron first so we can use his id
47 if($patron->isnew()) {
48 $new_patron = _add_patron(
49 $session, _clone_patron($patron));
50 if(UNIVERSAL::isa($new_patron, "OpenILS::EX")) {
51 $client->respond_complete($new_patron->ex);
56 $new_patron = $patron;
59 $new_patron = _add_update_addresses($session, $patron, $new_patron);
60 $new_patron = _add_update_cards($session, $patron, $new_patron);
62 if(UNIVERSAL::isa($new_patron,"OpenILS::EX")) {
63 $client->respond_complete($new_patron->ex);
67 $new_patron = _add_survey_responses($session, $patron, $new_patron);
68 $new_patron = _create_stat_maps($session, $user_session, $patron, $new_patron);
70 # re-update the patron if anything has happened to him during this process
71 if($new_patron->ischanged()) {
72 $new_patron = _update_patron($session, $new_patron);
74 $apputils->commit_db_session($session);
79 $err = "-*- Failure adding user: $e";
80 $apputils->rollback_db_session($session);
84 if($err) { throw OpenSRF::EX::ERROR ($err); }
87 warn "Patron Update/Create complete\n";
88 return flesh_user($new_patron->id());
94 __PACKAGE__->register_method(
95 method => "user_retrieve_fleshed_by_id",
96 api_name => "open-ils.actor.user.fleshed.retrieve",);
98 sub user_retrieve_fleshed_by_id {
99 my( $self, $client, $user_session, $user_id ) = @_;
100 my $user_obj = $apputils->check_user_session( $user_session );
101 return flesh_user($user_id);
112 $session = OpenSRF::AppSession->create("open-ils.storage");
116 # grab the user with the given id
117 my $ureq = $session->request(
118 "open-ils.storage.direct.actor.user.retrieve", $id);
119 my $user = $ureq->gather(1);
121 if(!$user) { return undef; }
124 my $cards_req = $session->request(
125 "open-ils.storage.direct.actor.card.search.usr.atomic",
127 $user->cards( $cards_req->gather(1) );
129 for my $c(@{$user->cards}) {
130 if($c->id == $user->card || $c->id eq $user->card ) {
131 warn "Setting my card to " . $c->id . "\n";
136 my $add_req = $session->request(
137 "open-ils.storage.direct.actor.user_address.search.usr.atomic",
139 $user->addresses( $add_req->gather(1) );
141 for my $c(@{$user->addresses}) {
142 if($c->id == $user->billing_address || $c->id eq $user->billing_address ) {
143 warn "Setting my address to " . $c->id . "\n";
144 $user->billing_address($c);
148 for my $c(@{$user->addresses}) {
149 if($c->id == $user->mailing_address || $c->id eq $user->mailing_address ) {
150 warn "Setting my address to " . $c->id . "\n";
151 $user->mailing_address($c);
155 my $stat_req = $session->request(
156 "open-ils.storage.direct.actor.stat_cat_entry_user_map.search.target_usr.atomic",
158 $user->stat_cat_entries($stat_req->gather(1));
160 if($kill) { $session->disconnect(); }
161 $user->clear_passwd();
169 # clone and clear stuff that would break the database
173 my $new_patron = Fieldmapper::actor::user->new();
175 my $fmap = $Fieldmapper::fieldmap;
176 no strict; # shallow clone, may be useful in the fieldmapper
178 (keys %{$fmap->{"Fieldmapper::actor::user"}->{'fields'}}) {
179 $new_patron->$field( $patron->$field() );
184 $new_patron->clear_billing_address();
185 $new_patron->clear_mailing_address();
186 $new_patron->clear_addresses();
187 $new_patron->clear_card();
188 $new_patron->clear_cards();
189 $new_patron->clear_id();
190 $new_patron->clear_isnew();
191 $new_patron->clear_changed();
192 $new_patron->clear_deleted();
193 $new_patron->clear_stat_cat_entries();
203 warn "Creating new patron\n";
206 my $req = $session->request(
207 "open-ils.storage.direct.actor.user.create",$patron);
208 my $id = $req->gather(1);
210 return OpenILS::EX->new("DUPLICATE_USER_USERNAME");
213 # retrieve the patron from the db to collect defaults
214 my $ureq = $session->request(
215 "open-ils.storage.direct.actor.user.retrieve",
218 warn "Created new patron with id $id\n";
220 return $ureq->gather(1);
225 my( $session, $patron) = @_;
227 warn "updating patron " . Dumper($patron) . "\n";
229 my $req = $session->request(
230 "open-ils.storage.direct.actor.user.update",$patron );
231 my $status = $req->gather(1);
232 if(!defined($status)) {
233 throw OpenSRF::EX::ERROR
234 ("Unknown error updating patron");
240 sub _add_update_addresses {
243 my $new_patron = shift;
245 my $current_id; # id of the address before creation
247 for my $address (@{$patron->addresses()}) {
249 $address->usr($new_patron->id());
251 if(ref($address) and $address->isnew()) {
252 warn "Adding new address at street " . $address->street1() . "\n";
254 $current_id = $address->id();
255 $address = _add_address($session,$address);
257 if( $patron->billing_address() and
258 $patron->billing_address() == $current_id ) {
259 $new_patron->billing_address($address->id());
260 $new_patron->ischanged(1);
263 if( $patron->mailing_address() and
264 $patron->mailing_address() == $current_id ) {
265 $new_patron->mailing_address($address->id());
266 $new_patron->ischanged(1);
269 } elsif( ref($address) and $address->ischanged() ) {
270 warn "Updating address at street " . $address->street1();
271 $address->usr($new_patron->id());
272 _update_address($session,$address);
274 } elsif( ref($address) and $address->isdeleted() ) {
275 warn "Deleting address at street " . $address->street1();
277 if( $address->id() == $new_patron->mailing_address() ) {
278 $new_patron->clear_mailing_address();
279 _update_patron($session, $new_patron);
282 if( $address->id() == $new_patron->billing_address() ) {
283 $new_patron->clear_billing_address();
284 _update_patron($session, $new_patron);
287 _delete_address($session,$address);
295 # adds an address to the db and returns the address with new id
297 my($session, $address) = @_;
298 $address->clear_id();
300 # put the address into the database
301 my $req = $session->request(
302 "open-ils.storage.direct.actor.user_address.create",
306 my $id = $req->gather(1);
308 throw OpenSRF::EX::ERROR
309 ("Unable to create new user address");
312 warn "Created address with id $id\n";
314 # update all the necessary id's
320 sub _update_address {
321 my( $session, $address ) = @_;
322 my $req = $session->request(
323 "open-ils.storage.direct.actor.user_address.update",
325 my $status = $req->gather(1);
326 if(!defined($status)) {
327 throw OpenSRF::EX::ERROR
328 ("Unknown error updating address");
335 sub _add_update_cards {
339 my $new_patron = shift;
341 my $virtual_id; #id of the card before creation
342 for my $card (@{$patron->cards()}) {
344 $card->usr($new_patron->id());
346 if(ref($card) and $card->isnew()) {
348 $virtual_id = $card->id();
349 $card = _add_card($session,$card);
350 if(UNIVERSAL::isa($card,"OpenILS::EX")) {
354 if($patron->card() == $virtual_id) {
355 $new_patron->card($card->id());
356 $new_patron->ischanged(1);
359 } elsif( ref($card) and $card->ischanged() ) {
360 $card->usr($new_patron->id());
361 _update_card($session, $card);
368 # adds an card to the db and returns the card with new id
370 my( $session, $card ) = @_;
373 warn "Adding card with barcode " . $card->barcode() . "\n";
374 my $req = $session->request(
375 "open-ils.storage.direct.actor.card.create",
378 my $id = $req->gather(1);
380 return OpenILS::EX->new("DUPLICATE_INVALID_USER_BARCODE");
384 warn "Created patron card with id $id\n";
390 my( $session, $card ) = @_;
393 my $req = $session->request(
394 "open-ils.storage.direct.actor.card.update",
396 my $status = $req->gather(1);
397 if(!defined($status)) {
398 throw OpenSRF::EX::ERROR
399 ("Unknown error updating card");
407 sub _delete_address {
408 my( $session, $address ) = @_;
410 warn "Deleting address " . $address->street1() . "\n";
412 my $req = $session->request(
413 "open-ils.storage.direct.actor.user_address.delete",
415 my $status = $req->gather(1);
416 if(!defined($status)) {
417 throw OpenSRF::EX::ERROR
418 ("Unknown error updating address");
420 warn "Delete address status is $status\n";
425 sub _add_survey_responses {
426 my ($session, $patron, $new_patron) = @_;
428 warn "updating responses for user " . $new_patron->id . "\n";
430 my $responses = $patron->survey_responses;
431 for my $resp( @$responses ) {
432 $resp->usr($new_patron->id);
435 my $status = $apputils->simple_scalar_request(
437 "open-ils.circ.survey.submit.user_id",
444 sub _create_stat_maps {
446 my($session, $user_session, $patron, $new_patron) = @_;
448 my $maps = $patron->stat_cat_entries();
450 for my $map (@$maps) {
452 next unless($map->isnew() || $map->ischanged());
454 my $method = "open-ils.storage.direct.actor.stat_cat_entry_user_map.update";
456 $method = "open-ils.storage.direct.actor.stat_cat_entry_user_map.create";
459 $map->target_usr($new_patron->id);
461 warn "Updating stat entry with method $method and session $user_session and map $map\n";
463 my $req = $session->request($method, $map);
464 my $status = $req->gather(1);
469 throw OpenSRF::EX::ERROR
470 ("Error updating stat map with method $method");
479 __PACKAGE__->register_method(
480 method => "search_username",
481 api_name => "open-ils.actor.user.search.username",
484 sub search_username {
485 my($self, $client, $username) = @_;
486 my $users = OpenILS::Application::AppUtils->simple_scalar_request(
488 "open-ils.storage.direct.actor.user.search.usrname.atomic",
496 __PACKAGE__->register_method(
497 method => "user_retrieve_by_barcode",
498 api_name => "open-ils.actor.user.fleshed.retrieve_by_barcode",);
500 sub user_retrieve_by_barcode {
501 my($self, $client, $user_session, $barcode) = @_;
502 warn "Searching for user with barcode $barcode\n";
503 my $user_obj = $apputils->check_user_session( $user_session );
505 my $session = OpenSRF::AppSession->create("open-ils.storage");
507 # find the card with the given barcode
508 my $creq = $session->request(
509 "open-ils.storage.direct.actor.card.search.barcode.atomic",
511 my $card = $creq->gather(1);
513 if(!$card || !$card->[0]) {
514 $session->disconnect();
519 my $user = flesh_user($card->usr(), $session);
520 $session->disconnect();
527 __PACKAGE__->register_method(
528 method => "get_user_by_id",
529 api_name => "open-ils.actor.user.retrieve",);
532 my ($self, $client, $user_session, $id) = @_;
534 my $user_obj = $apputils->check_user_session( $user_session );
536 return $apputils->simple_scalar_request(
538 "open-ils.storage.direct.actor.user.retrieve",
544 __PACKAGE__->register_method(
545 method => "get_org_types",
546 api_name => "open-ils.actor.org_types.retrieve",);
550 my($self, $client) = @_;
552 return $org_types if $org_types;
554 $apputils->simple_scalar_request(
556 "open-ils.storage.direct.actor.org_unit_type.retrieve.all.atomic" );
561 __PACKAGE__->register_method(
562 method => "get_user_profiles",
563 api_name => "open-ils.actor.user.profiles.retrieve",
567 sub get_user_profiles {
568 return $user_profiles if $user_profiles;
570 return $user_profiles =
571 $apputils->simple_scalar_request(
573 "open-ils.storage.direct.actor.profile.retrieve.all.atomic");
578 __PACKAGE__->register_method(
579 method => "get_user_ident_types",
580 api_name => "open-ils.actor.user.ident_types.retrieve",
583 sub get_user_ident_types {
584 return $ident_types if $ident_types;
585 return $ident_types =
586 $apputils->simple_scalar_request(
588 "open-ils.storage.direct.config.identification_type.retrieve.all.atomic" );
594 __PACKAGE__->register_method(
595 method => "get_org_unit",
596 api_name => "open-ils.actor.org_unit.retrieve",
601 my( $self, $client, $user_session, $org_id ) = @_;
603 if(defined($user_session) && !defined($org_id)) {
605 OpenILS::Application::AppUtils->check_user_session( $user_session ); #throws EX on error
606 if(!defined($org_id)) {
607 $org_id = $user_obj->home_ou;
612 my $home_ou = OpenILS::Application::AppUtils->simple_scalar_request(
614 "open-ils.storage.direct.actor.org_unit.retrieve",
623 __PACKAGE__->register_method(
624 method => "get_org_tree",
625 api_name => "open-ils.actor.org_tree.retrieve",
627 note => "Returns the entire org tree structure",
631 my( $self, $client) = @_;
633 # see if it's in the cache
634 warn "Getting ORG Tree\n";
635 my $tree = $cache_client->get_cache('orgtree');
637 warn "Found orgtree in cache. returning...\n";
641 my $orglist = $apputils->simple_scalar_request(
643 "open-ils.storage.direct.actor.org_unit.retrieve.all.atomic" );
645 $tree = $self->build_org_tree($orglist);
646 $cache_client->put_cache('orgtree', $tree);
652 # turns an org list into an org tree
655 my( $self, $orglist) = @_;
657 return $orglist unless (
658 ref($orglist) and @$orglist > 1 );
661 $a->ou_type <=> $b->ou_type ||
662 $a->name cmp $b->name } @$orglist;
664 for my $org (@list) {
666 next unless ($org and defined($org->parent_ou));
667 my ($parent) = grep { $_->id == $org->parent_ou } @list;
670 $parent->children([]) unless defined($parent->children);
671 push( @{$parent->children}, $org );
679 __PACKAGE__->register_method(
680 method => "get_org_descendants",
681 api_name => "open-ils.actor.org_tree.descendants.retrieve"
684 # depth is optional. org_unit is the id
685 sub get_org_descendants {
686 my( $self, $client, $org_unit, $depth ) = @_;
687 my $orglist = $apputils->simple_scalar_request(
689 "open-ils.storage.actor.org_unit.descendants.atomic",
691 return $self->build_org_tree($orglist);
695 __PACKAGE__->register_method(
696 method => "get_org_ancestors",
697 api_name => "open-ils.actor.org_tree.ancestors.retrieve"
700 # depth is optional. org_unit is the id
701 sub get_org_ancestors {
702 my( $self, $client, $org_unit, $depth ) = @_;
703 my $orglist = $apputils->simple_scalar_request(
705 "open-ils.storage.actor.org_unit.ancestors.atomic",
707 return $self->build_org_tree($orglist);
711 __PACKAGE__->register_method(
712 method => "get_standings",
713 api_name => "open-ils.actor.standings.retrieve"
718 return $user_standings if $user_standings;
719 return $user_standings =
720 $apputils->simple_scalar_request(
722 "open-ils.storage.direct.config.standing.retrieve.all.atomic" );
727 __PACKAGE__->register_method(
728 method => "get_my_org_path",
729 api_name => "open-ils.actor.org_unit.full_path.retrieve"
732 sub get_my_org_path {
733 my( $self, $client, $user_session, $org_id ) = @_;
734 my $user_obj = $apputils->check_user_session($user_session);
735 if(!defined($org_id)) { $org_id = $user_obj->home_ou; }
737 return $apputils->simple_scalar_request(
739 "open-ils.storage.actor.org_unit.full_path.atomic",
744 __PACKAGE__->register_method(
745 method => "patron_adv_search",
746 api_name => "open-ils.actor.patron.search.advanced" );
748 sub patron_adv_search {
749 my( $self, $client, $staff_login, $search_hash ) = @_;
752 warn "patron adv with $staff_login and search " .
753 Dumper($search_hash) . "\n";
755 my $session = OpenSRF::AppSession->create("open-ils.storage");
756 my $req = $session->request(
757 "open-ils.storage.actor.user.crazy_search", $search_hash);
759 my $ans = $req->gather(1);
761 my %hash = map { ($_ =>1) } @$ans;
762 $ans = [ keys %hash ];
764 warn "Returning @$ans\n";
766 $session->disconnect();
773 sub _verify_password {
774 my($user_session, $password) = @_;
775 my $user_obj = $apputils->check_user_session($user_session);
777 #grab the user with password
778 $user_obj = $apputils->simple_scalar_request(
780 "open-ils.storage.direct.actor.user.retrieve",
783 if($user_obj->passwd eq $password) {
791 __PACKAGE__->register_method(
792 method => "update_password",
793 api_name => "open-ils.actor.user.password.update");
795 __PACKAGE__->register_method(
796 method => "update_password",
797 api_name => "open-ils.actor.user.username.update");
799 __PACKAGE__->register_method(
800 method => "update_password",
801 api_name => "open-ils.actor.user.email.update");
803 sub update_password {
804 my( $self, $client, $user_session, $new_value, $current_password ) = @_;
806 warn "Updating user with method " .$self->api_name . "\n";
807 my $user_obj = $apputils->check_user_session($user_session);
809 if($self->api_name =~ /password/) {
811 #make sure they know the current password
812 if(!_verify_password($user_session, md5_hex($current_password))) {
813 return OpenILS::EX->new("USER_WRONG_PASSWORD")->ex;
816 $user_obj->passwd($new_value);
818 elsif($self->api_name =~ /username/) {
819 $user_obj->usrname($new_value);
822 elsif($self->api_name =~ /email/) {
823 warn "Updating email to $new_value\n";
824 $user_obj->email($new_value);
827 my $session = $apputils->start_db_session();
828 $user_obj = _update_patron($session, $user_obj);
829 $apputils->commit_db_session($session);
831 if($user_obj) { return 1; }
836 # returns undef on success, the first perm_type that failed
837 # on permission error
839 __PACKAGE__->register_method(
840 method => "check_user_perms",
841 api_name => "open-ils.actor.user.perm.check",
843 Takes a user id, an org id, and an array of perm type strings. For each
844 perm type, if the user does *not* have the given permission it is added
845 to a list which is returned from the method. If all permissions
846 are allowed, an empty list is returned
850 sub check_user_perms {
851 my( $self, $client, $user_id, $org_id, $perm_types ) = @_;
854 for my $perm (@$perm_types) {
855 if($apputils->check_user_perms($user_id, $org_id, $perm)) {
856 push @not_allowed, $perm;
909 some old methods that may be good to keep around for now
912 my( $session, $card ) = @_;
914 warn "Deleting card with barcode " . $card->barcode() . "\n";
915 my $req = $session->request(
916 "open-ils.storage.direct.actor.card.delete",
918 my $status = $req->gather(1);
919 if(!defined($status)) {
920 throw OpenSRF::EX::ERROR
921 ("Unknown error updating card");
927 # deletes the patron and any attached addresses and cards
928 __PACKAGE__->register_method(
929 method => "delete_patron",
930 api_name => "open-ils.actor.patron.delete",
935 my( $self, $client, $patron ) = @_;
936 my $session = $apputils->start_db_session();
941 $patron->clear_mailing_address();
942 $patron->clear_billing_address();
943 $patron->ischanged(1);
945 _update_patron($session, $patron);
946 _delete_address($session,$_) for (@{$patron->addresses()});
947 _delete_card($session,$_) for (@{$patron->cards()});
948 _delete_patron($session,$patron);
949 $apputils->commit_db_session($session);
953 $err = "-*- Failure deleting user: $e";
954 $apputils->rollback_db_session($session);
958 if($err) { throw OpenSRF::EX::ERROR ($err); }
959 warn "Patron Delete complete\n";
964 my( $session, $patron ) = @_;
966 warn "Deleting patron " . $patron->usrname() . "\n";
968 my $req = $session->request(
969 "open-ils.storage.direct.actor.user.delete",
971 my $status = $req->gather(1);
972 if(!defined($status)) {
973 throw OpenSRF::EX::ERROR
974 ("Unknown error updating patron");