1 package OpenILS::Application::Actor;
2 use OpenILS::Application;
3 use base qw/OpenILS::Application/;
4 use strict; use warnings;
6 $Data::Dumper::Indent = 0;
9 use Digest::MD5 qw(md5_hex);
11 use OpenSRF::EX qw(:try);
14 use OpenILS::Application::AppUtils;
16 use OpenILS::Utils::Fieldmapper;
17 use OpenILS::Utils::ModsParser;
18 use OpenSRF::Utils::Logger qw/$logger/;
19 use OpenSRF::Utils qw/:datetime/;
20 use OpenSRF::Utils::SettingsClient;
22 use OpenSRF::Utils::Cache;
24 use OpenSRF::Utils::JSON;
26 use DateTime::Format::ISO8601;
27 use OpenILS::Const qw/:const/;
29 use OpenILS::Application::Actor::Container;
30 use OpenILS::Application::Actor::ClosedDates;
31 use OpenILS::Application::Actor::UserGroups;
32 use OpenILS::Application::Actor::Friends;
33 use OpenILS::Application::Actor::Stage;
35 use OpenILS::Utils::CStoreEditor qw/:funcs/;
36 use OpenILS::Utils::Penalty;
37 use List::Util qw/max reduce/;
39 use UUID::Tiny qw/:std/;
42 OpenILS::Application::Actor::Container->initialize();
43 OpenILS::Application::Actor::UserGroups->initialize();
44 OpenILS::Application::Actor::ClosedDates->initialize();
47 my $apputils = "OpenILS::Application::AppUtils";
50 sub _d { warn "Patron:\n" . Dumper(shift()); }
53 my $set_user_settings;
57 #__PACKAGE__->register_method(
58 # method => "allowed_test",
59 # api_name => "open-ils.actor.allowed_test",
62 # my($self, $conn, $auth, $orgid, $permcode) = @_;
63 # my $e = new_editor(authtoken => $auth);
64 # return $e->die_event unless $e->checkauth;
68 # permcode => $permcode,
69 # result => $e->allowed($permcode, $orgid)
73 __PACKAGE__->register_method(
74 method => "update_user_setting",
75 api_name => "open-ils.actor.patron.settings.update",
77 sub update_user_setting {
78 my($self, $conn, $auth, $user_id, $settings) = @_;
79 my $e = new_editor(xact => 1, authtoken => $auth);
80 return $e->die_event unless $e->checkauth;
82 $user_id = $e->requestor->id unless defined $user_id;
84 unless($e->requestor->id == $user_id) {
85 my $user = $e->retrieve_actor_user($user_id) or return $e->die_event;
86 return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
89 for my $name (keys %$settings) {
90 my $val = $$settings{$name};
91 my $set = $e->search_actor_user_setting({usr => $user_id, name => $name})->[0];
94 $val = OpenSRF::Utils::JSON->perl2JSON($val);
97 $e->update_actor_user_setting($set) or return $e->die_event;
99 $set = Fieldmapper::actor::user_setting->new;
103 $e->create_actor_user_setting($set) or return $e->die_event;
106 $e->delete_actor_user_setting($set) or return $e->die_event;
115 __PACKAGE__->register_method(
116 method => "set_ou_settings",
117 api_name => "open-ils.actor.org_unit.settings.update",
119 desc => "Updates the value for a given org unit setting. The permission to update " .
120 "an org unit setting is either the UPDATE_ORG_UNIT_SETTING_ALL, or a specific " .
121 "permission specified in the update_perm column of the config.org_unit_setting_type " .
122 "table's row corresponding to the setting being changed." ,
124 {desc => 'Authentication token', type => 'string'},
125 {desc => 'Org unit ID', type => 'number'},
126 {desc => 'Hash of setting name-value pairs', type => 'object'}
128 return => {desc => '1 on success, Event on error'}
132 sub set_ou_settings {
133 my( $self, $client, $auth, $org_id, $settings ) = @_;
135 my $e = new_editor(authtoken => $auth, xact => 1);
136 return $e->die_event unless $e->checkauth;
138 my $all_allowed = $e->allowed("UPDATE_ORG_UNIT_SETTING_ALL", $org_id);
140 for my $name (keys %$settings) {
141 my $val = $$settings{$name};
143 my $type = $e->retrieve_config_org_unit_setting_type([
145 {flesh => 1, flesh_fields => {'coust' => ['update_perm']}}
146 ]) or return $e->die_event;
147 my $set = $e->search_actor_org_unit_setting({org_unit => $org_id, name => $name})->[0];
149 # If there is no relevant permission, the default assumption will
150 # be, "no, the caller cannot change that value."
151 return $e->die_event unless ($all_allowed ||
152 ($type->update_perm && $e->allowed($type->update_perm->code, $org_id)));
155 $val = OpenSRF::Utils::JSON->perl2JSON($val);
158 $e->update_actor_org_unit_setting($set) or return $e->die_event;
160 $set = Fieldmapper::actor::org_unit_setting->new;
161 $set->org_unit($org_id);
164 $e->create_actor_org_unit_setting($set) or return $e->die_event;
167 $e->delete_actor_org_unit_setting($set) or return $e->die_event;
175 __PACKAGE__->register_method(
176 method => "user_settings",
178 api_name => "open-ils.actor.patron.settings.retrieve",
181 my( $self, $client, $auth, $user_id, $setting ) = @_;
183 my $e = new_editor(authtoken => $auth);
184 return $e->event unless $e->checkauth;
185 $user_id = $e->requestor->id unless defined $user_id;
187 my $patron = $e->retrieve_actor_user($user_id) or return $e->event;
188 if($e->requestor->id != $user_id) {
189 return $e->event unless $e->allowed('VIEW_USER', $patron->home_ou);
193 my($e, $user_id, $setting) = @_;
194 my $val = $e->search_actor_user_setting({usr => $user_id, name => $setting})->[0];
195 return undef unless $val; # XXX this should really return undef, but needs testing
196 return OpenSRF::Utils::JSON->JSON2perl($val->value);
200 if(ref $setting eq 'ARRAY') {
202 $settings{$_} = get_setting($e, $user_id, $_) for @$setting;
205 return get_setting($e, $user_id, $setting);
208 my $s = $e->search_actor_user_setting({usr => $user_id});
209 return { map { ( $_->name => OpenSRF::Utils::JSON->JSON2perl($_->value) ) } @$s };
214 __PACKAGE__->register_method(
215 method => "ranged_ou_settings",
216 api_name => "open-ils.actor.org_unit_setting.values.ranged.retrieve",
218 desc => "Retrieves all org unit settings for the given org_id, up to whatever limit " .
219 "is implied for retrieving OU settings by the authenticated users' permissions.",
221 {desc => 'Authentication token', type => 'string'},
222 {desc => 'Org unit ID', type => 'number'},
224 return => {desc => 'A hashref of "ranged" settings, event on error'}
227 sub ranged_ou_settings {
228 my( $self, $client, $auth, $org_id ) = @_;
230 my $e = new_editor(authtoken => $auth);
231 return $e->event unless $e->checkauth;
234 my $org_list = $U->get_org_ancestors($org_id);
235 my $settings = $e->search_actor_org_unit_setting({org_unit => $org_list});
236 $org_list = [ reverse @$org_list ];
238 # start at the context org and capture the setting value
239 # without clobbering settings we've already captured
240 for my $this_org_id (@$org_list) {
242 my @sets = grep { $_->org_unit == $this_org_id } @$settings;
244 for my $set (@sets) {
245 my $type = $e->retrieve_config_org_unit_setting_type([
247 {flesh => 1, flesh_fields => {coust => ['view_perm']}}
250 # If there is no relevant permission, the default assumption will
251 # be, "yes, the caller can have that value."
252 if ($type && $type->view_perm) {
253 next if not $e->allowed($type->view_perm->code, $org_id);
256 $ranged_settings{$set->name} = OpenSRF::Utils::JSON->JSON2perl($set->value)
257 unless defined $ranged_settings{$set->name};
261 return \%ranged_settings;
266 __PACKAGE__->register_method(
267 api_name => 'open-ils.actor.ou_setting.ancestor_default',
268 method => 'ou_ancestor_setting',
270 desc => 'Get the org unit setting value associated with the setting name as seen from the specified org unit. ' .
271 'IF AND ONLY IF an authentication token is provided, this method will make sure that the given ' .
272 'user has permission to view that setting, if there is a permission associated with the setting.' ,
274 { desc => 'Org unit ID', type => 'number' },
275 { desc => 'setting name', type => 'string' },
276 { desc => 'authtoken (optional)', type => 'string' }
278 return => {desc => 'A value for the org unit setting, or undef'}
282 # ------------------------------------------------------------------
283 # Attempts to find the org setting value for a given org. if not
284 # found at the requested org, searches up the org tree until it
285 # finds a parent that has the requested setting.
286 # when found, returns { org => $id, value => $value }
287 # otherwise, returns NULL
288 # ------------------------------------------------------------------
289 sub ou_ancestor_setting {
290 my( $self, $client, $orgid, $name, $auth ) = @_;
291 return $U->ou_ancestor_setting($orgid, $name, undef, $auth);
294 __PACKAGE__->register_method(
295 api_name => 'open-ils.actor.ou_setting.ancestor_default.batch',
296 method => 'ou_ancestor_setting_batch',
298 desc => 'Get org unit setting name => value pairs for a list of names, as seen from the specified org unit. ' .
299 'IF AND ONLY IF an authentication token is provided, this method will make sure that the given ' .
300 'user has permission to view that setting, if there is a permission associated with the setting.' ,
302 { desc => 'Org unit ID', type => 'number' },
303 { desc => 'setting name list', type => 'array' },
304 { desc => 'authtoken (optional)', type => 'string' }
306 return => {desc => 'A hash with name => value pairs for the org unit settings'}
309 sub ou_ancestor_setting_batch {
310 my( $self, $client, $orgid, $name_list, $auth ) = @_;
312 $values{$_} = $U->ou_ancestor_setting($orgid, $_, undef, $auth) for @$name_list;
318 __PACKAGE__->register_method(
319 method => "update_patron",
320 api_name => "open-ils.actor.patron.update",
323 Update an existing user, or create a new one. Related objects,
324 like cards, addresses, survey responses, and stat cats,
325 can be updated by attaching them to the user object in their
326 respective fields. For examples, the billing address object
327 may be inserted into the 'billing_address' field, etc. For each
328 attached object, indicate if the object should be created,
329 updated, or deleted using the built-in 'isnew', 'ischanged',
330 and 'isdeleted' fields on the object.
333 { desc => 'Authentication token', type => 'string' },
334 { desc => 'Patron data object', type => 'object' }
336 return => {desc => 'A fleshed user object, event on error'}
341 my( $self, $client, $user_session, $patron ) = @_;
343 my $session = $apputils->start_db_session();
345 $logger->info($patron->isnew ? "Creating new patron..." : "Updating Patron: " . $patron->id);
347 my( $user_obj, $evt ) = $U->checkses($user_session);
350 $evt = check_group_perm($session, $user_obj, $patron);
354 # $new_patron is the patron in progress. $patron is the original patron
355 # passed in with the method. new_patron will change as the components
356 # of patron are added/updated.
360 # unflesh the real items on the patron
361 $patron->card( $patron->card->id ) if(ref($patron->card));
362 $patron->billing_address( $patron->billing_address->id )
363 if(ref($patron->billing_address));
364 $patron->mailing_address( $patron->mailing_address->id )
365 if(ref($patron->mailing_address));
367 # create/update the patron first so we can use his id
368 if($patron->isnew()) {
369 ( $new_patron, $evt ) = _add_patron($session, _clone_patron($patron), $user_obj);
371 } else { $new_patron = $patron; }
373 ( $new_patron, $evt ) = _add_update_addresses($session, $patron, $new_patron, $user_obj);
376 ( $new_patron, $evt ) = _add_update_cards($session, $patron, $new_patron, $user_obj);
379 ( $new_patron, $evt ) = _add_survey_responses($session, $patron, $new_patron, $user_obj);
382 # re-update the patron if anything has happened to him during this process
383 if($new_patron->ischanged()) {
384 ( $new_patron, $evt ) = _update_patron($session, $new_patron, $user_obj);
388 ($new_patron, $evt) = _create_stat_maps($session, $user_session, $patron, $new_patron, $user_obj);
391 ($new_patron, $evt) = _create_perm_maps($session, $user_session, $patron, $new_patron, $user_obj);
394 $apputils->commit_db_session($session);
396 $evt = apply_invalid_addr_penalty($patron);
399 my $tses = OpenSRF::AppSession->create('open-ils.trigger');
401 $tses->request('open-ils.trigger.event.autocreate', 'au.create', $new_patron, $new_patron->home_ou);
403 $tses->request('open-ils.trigger.event.autocreate', 'au.update', $new_patron, $new_patron->home_ou);
406 return flesh_user($new_patron->id(), new_editor(requestor => $user_obj, xact => 1));
409 sub apply_invalid_addr_penalty {
411 my $e = new_editor(xact => 1);
413 # grab the invalid address penalty if set
414 my $penalties = OpenILS::Utils::Penalty->retrieve_usr_penalties($e, $patron->id, $patron->home_ou);
416 my ($addr_penalty) = grep
417 { $_->standing_penalty->name eq 'INVALID_PATRON_ADDRESS' } @$penalties;
419 # do we enforce invalid address penalty
420 my $enforce = $U->ou_ancestor_setting_value(
421 $patron->home_ou, 'circ.patron_invalid_address_apply_penalty') || 0;
423 my $addrs = $e->search_actor_user_address(
424 {usr => $patron->id, valid => 'f', id => {'>' => 0}}, {idlist => 1});
425 my $addr_count = scalar(@$addrs);
427 if($addr_count == 0 and $addr_penalty) {
429 # regardless of any settings, remove the penalty when the user has no invalid addresses
430 $e->delete_actor_user_standing_penalty($addr_penalty) or return $e->die_event;
433 } elsif($enforce and $addr_count > 0 and !$addr_penalty) {
435 my $ptype = $e->retrieve_config_standing_penalty(29) or return $e->die_event;
436 my $depth = $ptype->org_depth;
437 my $ctx_org = $U->org_unit_ancestor_at_depth($patron->home_ou, $depth) if defined $depth;
438 $ctx_org = $patron->home_ou unless defined $ctx_org;
440 my $penalty = Fieldmapper::actor::user_standing_penalty->new;
441 $penalty->usr($patron->id);
442 $penalty->org_unit($ctx_org);
443 $penalty->standing_penalty(OILS_PENALTY_INVALID_PATRON_ADDRESS);
445 $e->create_actor_user_standing_penalty($penalty) or return $e->die_event;
464 "standing_penalties",
470 push @$fields, "home_ou" if $home_ou;
471 return new_flesh_user($id, $fields, $e );
479 # clone and clear stuff that would break the database
483 my $new_patron = $patron->clone;
485 $new_patron->clear_billing_address();
486 $new_patron->clear_mailing_address();
487 $new_patron->clear_addresses();
488 $new_patron->clear_card();
489 $new_patron->clear_cards();
490 $new_patron->clear_id();
491 $new_patron->clear_isnew();
492 $new_patron->clear_ischanged();
493 $new_patron->clear_isdeleted();
494 $new_patron->clear_stat_cat_entries();
495 $new_patron->clear_permissions();
496 $new_patron->clear_standing_penalties();
506 my $user_obj = shift;
508 my $evt = $U->check_perms($user_obj->id, $patron->home_ou, 'CREATE_USER');
509 return (undef, $evt) if $evt;
511 my $ex = $session->request(
512 'open-ils.storage.direct.actor.user.search.usrname', $patron->usrname())->gather(1);
514 return (undef, OpenILS::Event->new('USERNAME_EXISTS'));
517 $logger->info("Creating new user in the DB with username: ".$patron->usrname());
519 my $id = $session->request(
520 "open-ils.storage.direct.actor.user.create", $patron)->gather(1);
521 return (undef, $U->DB_UPDATE_FAILED($patron)) unless $id;
523 $logger->info("Successfully created new user [$id] in DB");
525 return ( $session->request(
526 "open-ils.storage.direct.actor.user.retrieve", $id)->gather(1), undef );
530 sub check_group_perm {
531 my( $session, $requestor, $patron ) = @_;
534 # first let's see if the requestor has
535 # priveleges to update this user in any way
536 if( ! $patron->isnew ) {
537 my $p = $session->request(
538 'open-ils.storage.direct.actor.user.retrieve', $patron->id )->gather(1);
540 # If we are the requestor (trying to update our own account)
541 # and we are not trying to change our profile, we're good
542 if( $p->id == $requestor->id and
543 $p->profile == $patron->profile ) {
548 $evt = group_perm_failed($session, $requestor, $p);
552 # They are allowed to edit this patron.. can they put the
553 # patron into the group requested?
554 $evt = group_perm_failed($session, $requestor, $patron);
560 sub group_perm_failed {
561 my( $session, $requestor, $patron ) = @_;
565 my $grpid = $patron->profile;
569 $logger->debug("user update looking for group perm for group $grpid");
570 $grp = $session->request(
571 'open-ils.storage.direct.permission.grp_tree.retrieve', $grpid )->gather(1);
572 return OpenILS::Event->new('PERMISSION_GRP_TREE_NOT_FOUND') unless $grp;
574 } while( !($perm = $grp->application_perm) and ($grpid = $grp->parent) );
576 $logger->info("user update checking perm $perm on user ".
577 $requestor->id." for update/create on user username=".$patron->usrname);
579 my $evt = $U->check_perms($requestor->id, $patron->home_ou, $perm);
587 my( $session, $patron, $user_obj, $noperm) = @_;
589 $logger->info("Updating patron ".$patron->id." in DB");
594 $evt = $U->check_perms($user_obj->id, $patron->home_ou, 'UPDATE_USER');
595 return (undef, $evt) if $evt;
598 # update the password by itself to avoid the password protection magic
599 if( $patron->passwd ) {
600 my $s = $session->request(
601 'open-ils.storage.direct.actor.user.remote_update',
602 {id => $patron->id}, {passwd => $patron->passwd})->gather(1);
603 return (undef, $U->DB_UPDATE_FAILED($patron)) unless defined($s);
604 $patron->clear_passwd;
607 if(!$patron->ident_type) {
608 $patron->clear_ident_type;
609 $patron->clear_ident_value;
612 $evt = verify_last_xact($session, $patron);
613 return (undef, $evt) if $evt;
615 my $stat = $session->request(
616 "open-ils.storage.direct.actor.user.update",$patron )->gather(1);
617 return (undef, $U->DB_UPDATE_FAILED($patron)) unless defined($stat);
622 sub verify_last_xact {
623 my( $session, $patron ) = @_;
624 return undef unless $patron->id and $patron->id > 0;
625 my $p = $session->request(
626 'open-ils.storage.direct.actor.user.retrieve', $patron->id)->gather(1);
627 my $xact = $p->last_xact_id;
628 return undef unless $xact;
629 $logger->info("user xact = $xact, saving with xact " . $patron->last_xact_id);
630 return OpenILS::Event->new('XACT_COLLISION')
631 if $xact != $patron->last_xact_id;
636 sub _check_dup_ident {
637 my( $session, $patron ) = @_;
639 return undef unless $patron->ident_value;
642 ident_type => $patron->ident_type,
643 ident_value => $patron->ident_value,
646 $logger->debug("patron update searching for dup ident values: " .
647 $patron->ident_type . ':' . $patron->ident_value);
649 $search->{id} = {'!=' => $patron->id} if $patron->id and $patron->id > 0;
651 my $dups = $session->request(
652 'open-ils.storage.direct.actor.user.search_where.atomic', $search )->gather(1);
655 return OpenILS::Event->new('PATRON_DUP_IDENT1', payload => $patron )
662 sub _add_update_addresses {
666 my $new_patron = shift;
670 my $current_id; # id of the address before creation
672 for my $address (@{$patron->addresses()}) {
674 next unless ref $address;
675 $current_id = $address->id();
677 if( $patron->billing_address() and
678 $patron->billing_address() == $current_id ) {
679 $logger->info("setting billing addr to $current_id");
680 $new_patron->billing_address($address->id());
681 $new_patron->ischanged(1);
684 if( $patron->mailing_address() and
685 $patron->mailing_address() == $current_id ) {
686 $new_patron->mailing_address($address->id());
687 $logger->info("setting mailing addr to $current_id");
688 $new_patron->ischanged(1);
692 if($address->isnew()) {
694 $address->usr($new_patron->id());
696 ($address, $evt) = _add_address($session,$address);
697 return (undef, $evt) if $evt;
699 # we need to get the new id
700 if( $patron->billing_address() and
701 $patron->billing_address() == $current_id ) {
702 $new_patron->billing_address($address->id());
703 $logger->info("setting billing addr to $current_id");
704 $new_patron->ischanged(1);
707 if( $patron->mailing_address() and
708 $patron->mailing_address() == $current_id ) {
709 $new_patron->mailing_address($address->id());
710 $logger->info("setting mailing addr to $current_id");
711 $new_patron->ischanged(1);
714 } elsif($address->ischanged() ) {
716 ($address, $evt) = _update_address($session, $address);
717 return (undef, $evt) if $evt;
719 } elsif($address->isdeleted() ) {
721 if( $address->id() == $new_patron->mailing_address() ) {
722 $new_patron->clear_mailing_address();
723 ($new_patron, $evt) = _update_patron($session, $new_patron);
724 return (undef, $evt) if $evt;
727 if( $address->id() == $new_patron->billing_address() ) {
728 $new_patron->clear_billing_address();
729 ($new_patron, $evt) = _update_patron($session, $new_patron);
730 return (undef, $evt) if $evt;
733 $evt = _delete_address($session, $address);
734 return (undef, $evt) if $evt;
738 return ( $new_patron, undef );
742 # adds an address to the db and returns the address with new id
744 my($session, $address) = @_;
745 $address->clear_id();
747 $logger->info("Creating new address at street ".$address->street1);
749 # put the address into the database
750 my $id = $session->request(
751 "open-ils.storage.direct.actor.user_address.create", $address )->gather(1);
752 return (undef, $U->DB_UPDATE_FAILED($address)) unless $id;
755 return ($address, undef);
759 sub _update_address {
760 my( $session, $address ) = @_;
762 $logger->info("Updating address ".$address->id." in the DB");
764 my $stat = $session->request(
765 "open-ils.storage.direct.actor.user_address.update", $address )->gather(1);
767 return (undef, $U->DB_UPDATE_FAILED($address)) unless defined($stat);
768 return ($address, undef);
773 sub _add_update_cards {
777 my $new_patron = shift;
781 my $virtual_id; #id of the card before creation
782 for my $card (@{$patron->cards()}) {
784 $card->usr($new_patron->id());
786 if(ref($card) and $card->isnew()) {
788 $virtual_id = $card->id();
789 ( $card, $evt ) = _add_card($session,$card);
790 return (undef, $evt) if $evt;
792 #if(ref($patron->card)) { $patron->card($patron->card->id); }
793 if($patron->card() == $virtual_id) {
794 $new_patron->card($card->id());
795 $new_patron->ischanged(1);
798 } elsif( ref($card) and $card->ischanged() ) {
799 $evt = _update_card($session, $card);
800 return (undef, $evt) if $evt;
804 return ( $new_patron, undef );
808 # adds an card to the db and returns the card with new id
810 my( $session, $card ) = @_;
813 $logger->info("Adding new patron card ".$card->barcode);
815 my $id = $session->request(
816 "open-ils.storage.direct.actor.card.create", $card )->gather(1);
817 return (undef, $U->DB_UPDATE_FAILED($card)) unless $id;
818 $logger->info("Successfully created patron card $id");
821 return ( $card, undef );
825 # returns event on error. returns undef otherwise
827 my( $session, $card ) = @_;
828 $logger->info("Updating patron card ".$card->id);
830 my $stat = $session->request(
831 "open-ils.storage.direct.actor.card.update", $card )->gather(1);
832 return $U->DB_UPDATE_FAILED($card) unless defined($stat);
839 # returns event on error. returns undef otherwise
840 sub _delete_address {
841 my( $session, $address ) = @_;
843 $logger->info("Deleting address ".$address->id." from DB");
845 my $stat = $session->request(
846 "open-ils.storage.direct.actor.user_address.delete", $address )->gather(1);
848 return $U->DB_UPDATE_FAILED($address) unless defined($stat);
854 sub _add_survey_responses {
855 my ($session, $patron, $new_patron) = @_;
857 $logger->info( "Updating survey responses for patron ".$new_patron->id );
859 my $responses = $patron->survey_responses;
863 $_->usr($new_patron->id) for (@$responses);
865 my $evt = $U->simplereq( "open-ils.circ",
866 "open-ils.circ.survey.submit.user_id", $responses );
868 return (undef, $evt) if defined($U->event_code($evt));
872 return ( $new_patron, undef );
876 sub _create_stat_maps {
878 my($session, $user_session, $patron, $new_patron) = @_;
880 my $maps = $patron->stat_cat_entries();
882 for my $map (@$maps) {
884 my $method = "open-ils.storage.direct.actor.stat_cat_entry_user_map.update";
886 if ($map->isdeleted()) {
887 $method = "open-ils.storage.direct.actor.stat_cat_entry_user_map.delete";
889 } elsif ($map->isnew()) {
890 $method = "open-ils.storage.direct.actor.stat_cat_entry_user_map.create";
895 $map->target_usr($new_patron->id);
898 $logger->info("Updating stat entry with method $method and map $map");
900 my $stat = $session->request($method, $map)->gather(1);
901 return (undef, $U->DB_UPDATE_FAILED($map)) unless defined($stat);
905 return ($new_patron, undef);
908 sub _create_perm_maps {
910 my($session, $user_session, $patron, $new_patron) = @_;
912 my $maps = $patron->permissions;
914 for my $map (@$maps) {
916 my $method = "open-ils.storage.direct.permission.usr_perm_map.update";
917 if ($map->isdeleted()) {
918 $method = "open-ils.storage.direct.permission.usr_perm_map.delete";
919 } elsif ($map->isnew()) {
920 $method = "open-ils.storage.direct.permission.usr_perm_map.create";
925 $map->usr($new_patron->id);
927 #warn( "Updating permissions with method $method and session $user_session and map $map" );
928 $logger->info( "Updating permissions with method $method and map $map" );
930 my $stat = $session->request($method, $map)->gather(1);
931 return (undef, $U->DB_UPDATE_FAILED($map)) unless defined($stat);
935 return ($new_patron, undef);
939 __PACKAGE__->register_method(
940 method => "set_user_work_ous",
941 api_name => "open-ils.actor.user.work_ous.update",
944 sub set_user_work_ous {
950 my( $requestor, $evt ) = $apputils->checksesperm( $ses, 'ASSIGN_WORK_ORG_UNIT' );
953 my $session = $apputils->start_db_session();
955 for my $map (@$maps) {
957 my $method = "open-ils.storage.direct.permission.usr_work_ou_map.update";
958 if ($map->isdeleted()) {
959 $method = "open-ils.storage.direct.permission.usr_work_ou_map.delete";
960 } elsif ($map->isnew()) {
961 $method = "open-ils.storage.direct.permission.usr_work_ou_map.create";
965 #warn( "Updating permissions with method $method and session $ses and map $map" );
966 $logger->info( "Updating work_ou map with method $method and map $map" );
968 my $stat = $session->request($method, $map)->gather(1);
969 $logger->warn( "update failed: ".$U->DB_UPDATE_FAILED($map) ) unless defined($stat);
973 $apputils->commit_db_session($session);
975 return scalar(@$maps);
979 __PACKAGE__->register_method(
980 method => "set_user_perms",
981 api_name => "open-ils.actor.user.permissions.update",
990 my $session = $apputils->start_db_session();
992 my( $user_obj, $evt ) = $U->checkses($ses);
995 my $perms = $session->request('open-ils.storage.permission.user_perms.atomic', $user_obj->id)->gather(1);
998 $all = 1 if ($U->is_true($user_obj->super_user()));
999 $all = 1 unless ($U->check_perms($user_obj->id, $user_obj->home_ou, 'EVERYTHING'));
1001 for my $map (@$maps) {
1003 my $method = "open-ils.storage.direct.permission.usr_perm_map.update";
1004 if ($map->isdeleted()) {
1005 $method = "open-ils.storage.direct.permission.usr_perm_map.delete";
1006 } elsif ($map->isnew()) {
1007 $method = "open-ils.storage.direct.permission.usr_perm_map.create";
1011 next if (!$all and !grep { $_->perm eq $map->perm and $U->is_true($_->grantable) and $_->depth <= $map->depth } @$perms);
1012 #warn( "Updating permissions with method $method and session $ses and map $map" );
1013 $logger->info( "Updating permissions with method $method and map $map" );
1015 my $stat = $session->request($method, $map)->gather(1);
1016 $logger->warn( "update failed: ".$U->DB_UPDATE_FAILED($map) ) unless defined($stat);
1020 $apputils->commit_db_session($session);
1022 return scalar(@$maps);
1026 __PACKAGE__->register_method(
1027 method => "user_retrieve_by_barcode",
1029 api_name => "open-ils.actor.user.fleshed.retrieve_by_barcode",);
1031 sub user_retrieve_by_barcode {
1032 my($self, $client, $auth, $barcode, $flesh_home_ou) = @_;
1034 my $e = new_editor(authtoken => $auth);
1035 return $e->event unless $e->checkauth;
1037 my $card = $e->search_actor_card({barcode => $barcode})->[0]
1038 or return $e->event;
1040 my $user = flesh_user($card->usr, $e, $flesh_home_ou);
1041 return $e->event unless $e->allowed(
1042 "VIEW_USER", $flesh_home_ou ? $user->home_ou->id : $user->home_ou
1049 __PACKAGE__->register_method(
1050 method => "get_user_by_id",
1052 api_name => "open-ils.actor.user.retrieve",
1055 sub get_user_by_id {
1056 my ($self, $client, $auth, $id) = @_;
1057 my $e = new_editor(authtoken=>$auth);
1058 return $e->event unless $e->checkauth;
1059 my $user = $e->retrieve_actor_user($id) or return $e->event;
1060 return $e->event unless $e->allowed('VIEW_USER', $user->home_ou);
1065 __PACKAGE__->register_method(
1066 method => "get_org_types",
1067 api_name => "open-ils.actor.org_types.retrieve",
1070 return $U->get_org_types();
1074 __PACKAGE__->register_method(
1075 method => "get_user_ident_types",
1076 api_name => "open-ils.actor.user.ident_types.retrieve",
1079 sub get_user_ident_types {
1080 return $ident_types if $ident_types;
1081 return $ident_types =
1082 new_editor()->retrieve_all_config_identification_type();
1086 __PACKAGE__->register_method(
1087 method => "get_org_unit",
1088 api_name => "open-ils.actor.org_unit.retrieve",
1092 my( $self, $client, $user_session, $org_id ) = @_;
1093 my $e = new_editor(authtoken => $user_session);
1095 return $e->event unless $e->checkauth;
1096 $org_id = $e->requestor->ws_ou;
1098 my $o = $e->retrieve_actor_org_unit($org_id)
1099 or return $e->event;
1103 __PACKAGE__->register_method(
1104 method => "search_org_unit",
1105 api_name => "open-ils.actor.org_unit_list.search",
1108 sub search_org_unit {
1110 my( $self, $client, $field, $value ) = @_;
1112 my $list = OpenILS::Application::AppUtils->simple_scalar_request(
1114 "open-ils.cstore.direct.actor.org_unit.search.atomic",
1115 { $field => $value } );
1121 # build the org tree
1123 __PACKAGE__->register_method(
1124 method => "get_org_tree",
1125 api_name => "open-ils.actor.org_tree.retrieve",
1127 note => "Returns the entire org tree structure",
1133 return $U->get_org_tree($client->session->session_locale);
1137 __PACKAGE__->register_method(
1138 method => "get_org_descendants",
1139 api_name => "open-ils.actor.org_tree.descendants.retrieve"
1142 # depth is optional. org_unit is the id
1143 sub get_org_descendants {
1144 my( $self, $client, $org_unit, $depth ) = @_;
1146 if(ref $org_unit eq 'ARRAY') {
1149 for my $i (0..scalar(@$org_unit)-1) {
1150 my $list = $U->simple_scalar_request(
1152 "open-ils.storage.actor.org_unit.descendants.atomic",
1153 $org_unit->[$i], $depth->[$i] );
1154 push(@trees, $U->build_org_tree($list));
1159 my $orglist = $apputils->simple_scalar_request(
1161 "open-ils.storage.actor.org_unit.descendants.atomic",
1162 $org_unit, $depth );
1163 return $U->build_org_tree($orglist);
1168 __PACKAGE__->register_method(
1169 method => "get_org_ancestors",
1170 api_name => "open-ils.actor.org_tree.ancestors.retrieve"
1173 # depth is optional. org_unit is the id
1174 sub get_org_ancestors {
1175 my( $self, $client, $org_unit, $depth ) = @_;
1176 my $orglist = $apputils->simple_scalar_request(
1178 "open-ils.storage.actor.org_unit.ancestors.atomic",
1179 $org_unit, $depth );
1180 return $U->build_org_tree($orglist);
1184 __PACKAGE__->register_method(
1185 method => "get_standings",
1186 api_name => "open-ils.actor.standings.retrieve"
1191 return $user_standings if $user_standings;
1192 return $user_standings =
1193 $apputils->simple_scalar_request(
1195 "open-ils.cstore.direct.config.standing.search.atomic",
1196 { id => { "!=" => undef } }
1201 __PACKAGE__->register_method(
1202 method => "get_my_org_path",
1203 api_name => "open-ils.actor.org_unit.full_path.retrieve"
1206 sub get_my_org_path {
1207 my( $self, $client, $auth, $org_id ) = @_;
1208 my $e = new_editor(authtoken=>$auth);
1209 return $e->event unless $e->checkauth;
1210 $org_id = $e->requestor->ws_ou unless defined $org_id;
1212 return $apputils->simple_scalar_request(
1214 "open-ils.storage.actor.org_unit.full_path.atomic",
1219 __PACKAGE__->register_method(
1220 method => "patron_adv_search",
1221 api_name => "open-ils.actor.patron.search.advanced"
1223 sub patron_adv_search {
1224 my( $self, $client, $auth, $search_hash,
1225 $search_limit, $search_sort, $include_inactive, $search_ou ) = @_;
1227 my $e = new_editor(authtoken=>$auth);
1228 return $e->event unless $e->checkauth;
1229 return $e->event unless $e->allowed('VIEW_USER');
1231 # depth boundary outside of which patrons must opt-in, default to 0
1232 my $opt_boundary = 0;
1233 $opt_boundary = $U->ou_ancestor_setting_value($e->requestor->ws_ou,'org.patron_opt_boundary') if user_opt_in_enabled($self);
1235 return $U->storagereq(
1236 "open-ils.storage.actor.user.crazy_search", $search_hash,
1237 $search_limit, $search_sort, $include_inactive, $e->requestor->ws_ou, $search_ou, $opt_boundary);
1241 __PACKAGE__->register_method(
1242 method => "update_passwd",
1243 api_name => "open-ils.actor.user.password.update",
1245 desc => "Update the operator's password",
1247 { desc => 'Authentication token', type => 'string' },
1248 { desc => 'New password', type => 'string' },
1249 { desc => 'Current password', type => 'string' }
1251 return => {desc => '1 on success, Event on error or incorrect current password'}
1255 __PACKAGE__->register_method(
1256 method => "update_passwd",
1257 api_name => "open-ils.actor.user.username.update",
1259 desc => "Update the operator's username",
1261 { desc => 'Authentication token', type => 'string' },
1262 { desc => 'New username', type => 'string' },
1263 { desc => 'Current password', type => 'string' }
1265 return => {desc => '1 on success, Event on error or incorrect current password'}
1269 __PACKAGE__->register_method(
1270 method => "update_passwd",
1271 api_name => "open-ils.actor.user.email.update",
1273 desc => "Update the operator's email address",
1275 { desc => 'Authentication token', type => 'string' },
1276 { desc => 'New email address', type => 'string' },
1277 { desc => 'Current password', type => 'string' }
1279 return => {desc => '1 on success, Event on error or incorrect current password'}
1284 my( $self, $conn, $auth, $new_val, $orig_pw ) = @_;
1285 my $e = new_editor(xact=>1, authtoken=>$auth);
1286 return $e->die_event unless $e->checkauth;
1288 my $db_user = $e->retrieve_actor_user($e->requestor->id)
1289 or return $e->die_event;
1290 my $api = $self->api_name;
1292 # make sure the original password matches the in-database password
1293 if (md5_hex($orig_pw) ne $db_user->passwd) {
1295 return new OpenILS::Event('INCORRECT_PASSWORD');
1298 if( $api =~ /password/o ) {
1300 $db_user->passwd($new_val);
1304 # if we don't clear the password, the user will be updated with
1305 # a hashed version of the hashed version of their password
1306 $db_user->clear_passwd;
1308 if( $api =~ /username/o ) {
1310 # make sure no one else has this username
1311 my $exist = $e->search_actor_user({usrname=>$new_val},{idlist=>1});
1314 return new OpenILS::Event('USERNAME_EXISTS');
1316 $db_user->usrname($new_val);
1318 } elsif( $api =~ /email/o ) {
1319 $db_user->email($new_val);
1323 $e->update_actor_user($db_user) or return $e->die_event;
1326 # update the cached user to pick up these changes
1327 $U->simplereq('open-ils.auth', 'open-ils.auth.session.reset_timeout', $auth, 1);
1333 __PACKAGE__->register_method(
1334 method => "check_user_perms",
1335 api_name => "open-ils.actor.user.perm.check",
1336 notes => <<" NOTES");
1337 Takes a login session, user id, an org id, and an array of perm type strings. For each
1338 perm type, if the user does *not* have the given permission it is added
1339 to a list which is returned from the method. If all permissions
1340 are allowed, an empty list is returned
1341 if the logged in user does not match 'user_id', then the logged in user must
1342 have VIEW_PERMISSION priveleges.
1345 sub check_user_perms {
1346 my( $self, $client, $login_session, $user_id, $org_id, $perm_types ) = @_;
1348 my( $staff, $evt ) = $apputils->checkses($login_session);
1349 return $evt if $evt;
1351 if($staff->id ne $user_id) {
1352 if( $evt = $apputils->check_perms(
1353 $staff->id, $org_id, 'VIEW_PERMISSION') ) {
1359 for my $perm (@$perm_types) {
1360 if($apputils->check_perms($user_id, $org_id, $perm)) {
1361 push @not_allowed, $perm;
1365 return \@not_allowed
1368 __PACKAGE__->register_method(
1369 method => "check_user_perms2",
1370 api_name => "open-ils.actor.user.perm.check.multi_org",
1372 Checks the permissions on a list of perms and orgs for a user
1373 @param authtoken The login session key
1374 @param user_id The id of the user to check
1375 @param orgs The array of org ids
1376 @param perms The array of permission names
1377 @return An array of [ orgId, permissionName ] arrays that FAILED the check
1378 if the logged in user does not match 'user_id', then the logged in user must
1379 have VIEW_PERMISSION priveleges.
1382 sub check_user_perms2 {
1383 my( $self, $client, $authtoken, $user_id, $orgs, $perms ) = @_;
1385 my( $staff, $target, $evt ) = $apputils->checkses_requestor(
1386 $authtoken, $user_id, 'VIEW_PERMISSION' );
1387 return $evt if $evt;
1390 for my $org (@$orgs) {
1391 for my $perm (@$perms) {
1392 if($apputils->check_perms($user_id, $org, $perm)) {
1393 push @not_allowed, [ $org, $perm ];
1398 return \@not_allowed
1402 __PACKAGE__->register_method(
1403 method => 'check_user_perms3',
1404 api_name => 'open-ils.actor.user.perm.highest_org',
1406 Returns the highest org unit id at which a user has a given permission
1407 If the requestor does not match the target user, the requestor must have
1408 'VIEW_PERMISSION' rights at the home org unit of the target user
1409 @param authtoken The login session key
1410 @param userid The id of the user in question
1411 @param perm The permission to check
1412 @return The org unit highest in the org tree within which the user has
1413 the requested permission
1416 sub check_user_perms3 {
1417 my($self, $client, $authtoken, $user_id, $perm) = @_;
1418 my $e = new_editor(authtoken=>$authtoken);
1419 return $e->event unless $e->checkauth;
1421 my $tree = $U->get_org_tree();
1423 unless($e->requestor->id == $user_id) {
1424 my $user = $e->retrieve_actor_user($user_id)
1425 or return $e->event;
1426 return $e->event unless $e->allowed('VIEW_PERMISSION', $user->home_ou);
1427 return $U->find_highest_perm_org($perm, $user_id, $user->home_ou, $tree );
1430 return $U->find_highest_perm_org($perm, $user_id, $e->requestor->ws_ou, $tree);
1433 __PACKAGE__->register_method(
1434 method => 'user_has_work_perm_at',
1435 api_name => 'open-ils.actor.user.has_work_perm_at',
1439 Returns a set of org unit IDs which represent the highest orgs in
1440 the org tree where the user has the requested permission. The
1441 purpose of this method is to return the smallest set of org units
1442 which represent the full expanse of the user's ability to perform
1443 the requested action. The user whose perms this method should
1444 check is implied by the authtoken. /,
1446 {desc => 'authtoken', type => 'string'},
1447 {desc => 'permission name', type => 'string'},
1448 {desc => q/user id, optional. If present, check perms for
1449 this user instead of the logged in user/, type => 'number'},
1451 return => {desc => 'An array of org IDs'}
1455 sub user_has_work_perm_at {
1456 my($self, $conn, $auth, $perm, $user_id) = @_;
1457 my $e = new_editor(authtoken=>$auth);
1458 return $e->event unless $e->checkauth;
1459 if(defined $user_id) {
1460 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
1461 return $e->event unless $e->allowed('VIEW_PERMISSION', $user->home_ou);
1463 return $U->user_has_work_perm_at($e, $perm, undef, $user_id);
1466 __PACKAGE__->register_method(
1467 method => 'user_has_work_perm_at_batch',
1468 api_name => 'open-ils.actor.user.has_work_perm_at.batch',
1472 sub user_has_work_perm_at_batch {
1473 my($self, $conn, $auth, $perms, $user_id) = @_;
1474 my $e = new_editor(authtoken=>$auth);
1475 return $e->event unless $e->checkauth;
1476 if(defined $user_id) {
1477 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
1478 return $e->event unless $e->allowed('VIEW_PERMISSION', $user->home_ou);
1481 $map->{$_} = $U->user_has_work_perm_at($e, $_) for @$perms;
1487 __PACKAGE__->register_method(
1488 method => 'check_user_perms4',
1489 api_name => 'open-ils.actor.user.perm.highest_org.batch',
1491 Returns the highest org unit id at which a user has a given permission
1492 If the requestor does not match the target user, the requestor must have
1493 'VIEW_PERMISSION' rights at the home org unit of the target user
1494 @param authtoken The login session key
1495 @param userid The id of the user in question
1496 @param perms An array of perm names to check
1497 @return An array of orgId's representing the org unit
1498 highest in the org tree within which the user has the requested permission
1499 The arrah of orgId's has matches the order of the perms array
1502 sub check_user_perms4 {
1503 my( $self, $client, $authtoken, $userid, $perms ) = @_;
1505 my( $staff, $target, $org, $evt );
1507 ( $staff, $target, $evt ) = $apputils->checkses_requestor(
1508 $authtoken, $userid, 'VIEW_PERMISSION' );
1509 return $evt if $evt;
1512 return [] unless ref($perms);
1513 my $tree = $U->get_org_tree();
1515 for my $p (@$perms) {
1516 push( @arr, $U->find_highest_perm_org( $p, $userid, $target->home_ou, $tree ) );
1522 __PACKAGE__->register_method(
1523 method => "user_fines_summary",
1524 api_name => "open-ils.actor.user.fines.summary",
1527 desc => 'Returns a short summary of the users total open fines, ' .
1528 'excluding voided fines Params are login_session, user_id' ,
1530 {desc => 'Authentication token', type => 'string'},
1531 {desc => 'User ID', type => 'string'} # number?
1534 desc => "a 'mous' object, event on error",
1539 sub user_fines_summary {
1540 my( $self, $client, $auth, $user_id ) = @_;
1542 my $e = new_editor(authtoken=>$auth);
1543 return $e->event unless $e->checkauth;
1545 if( $user_id ne $e->requestor->id ) {
1546 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
1547 return $e->event unless
1548 $e->allowed('VIEW_USER_FINES_SUMMARY', $user->home_ou);
1551 return $e->search_money_open_user_summary({usr => $user_id})->[0];
1555 __PACKAGE__->register_method(
1556 method => "user_opac_vitals",
1557 api_name => "open-ils.actor.user.opac.vital_stats",
1561 desc => 'Returns a short summary of the users vital stats, including ' .
1562 'identification information, accumulated balance, number of holds, ' .
1563 'and current open circulation stats' ,
1565 {desc => 'Authentication token', type => 'string'},
1566 {desc => 'Optional User ID, for use in the staff client', type => 'number'} # number?
1569 desc => "An object with four properties: user, fines, checkouts and holds."
1574 sub user_opac_vitals {
1575 my( $self, $client, $auth, $user_id ) = @_;
1577 my $e = new_editor(authtoken=>$auth);
1578 return $e->event unless $e->checkauth;
1580 $user_id ||= $e->requestor->id;
1582 my $user = $e->retrieve_actor_user( $user_id );
1585 ->method_lookup('open-ils.actor.user.fines.summary')
1586 ->run($auth => $user_id);
1587 return $fines if (defined($U->event_code($fines)));
1590 $fines = new Fieldmapper::money::open_user_summary ();
1591 $fines->balance_owed(0.00);
1592 $fines->total_owed(0.00);
1593 $fines->total_paid(0.00);
1594 $fines->usr($user_id);
1598 ->method_lookup('open-ils.actor.user.hold_requests.count')
1599 ->run($auth => $user_id);
1600 return $holds if (defined($U->event_code($holds)));
1603 ->method_lookup('open-ils.actor.user.checked_out.count')
1604 ->run($auth => $user_id);
1605 return $out if (defined($U->event_code($out)));
1607 $out->{"total_out"} = reduce { $a + $out->{$b} } 0, qw/out overdue long_overdue/;
1611 first_given_name => $user->first_given_name,
1612 second_given_name => $user->second_given_name,
1613 family_name => $user->family_name,
1614 alias => $user->alias,
1615 usrname => $user->usrname
1617 fines => $fines->to_bare_hash,
1624 ##### a small consolidation of related method registrations
1625 my $common_params = [
1626 { desc => 'Authentication token', type => 'string' },
1627 { desc => 'User ID', type => 'string' },
1628 { desc => 'Transactions type (optional, defaults to all)', type => 'string' },
1629 { desc => 'Options hash. May contain limit and offset for paged results.', type => 'object' },
1632 'open-ils.actor.user.transactions' => '',
1633 'open-ils.actor.user.transactions.fleshed' => '',
1634 'open-ils.actor.user.transactions.have_charge' => ' that have an initial charge',
1635 'open-ils.actor.user.transactions.have_charge.fleshed' => ' that have an initial charge',
1636 'open-ils.actor.user.transactions.have_balance' => ' that have an outstanding balance',
1637 'open-ils.actor.user.transactions.have_balance.fleshed' => ' that have an outstanding balance',
1640 foreach (keys %methods) {
1642 method => "user_transactions",
1645 desc => 'For a given user, retrieve a list of '
1646 . (/\.fleshed/ ? 'fleshed ' : '')
1647 . 'transactions' . $methods{$_}
1648 . ' optionally limited to transactions of a given type.',
1649 params => $common_params,
1651 desc => "List of objects, or event on error. Each object is a hash containing: transaction, circ, record. "
1652 . 'These represent the relevant (mbts) transaction, attached circulation and title pointed to in the circ, respectively.',
1656 $args{authoritative} = 1;
1657 __PACKAGE__->register_method(%args);
1660 # Now for the counts
1662 'open-ils.actor.user.transactions.count' => '',
1663 'open-ils.actor.user.transactions.have_charge.count' => ' that have an initial charge',
1664 'open-ils.actor.user.transactions.have_balance.count' => ' that have an outstanding balance',
1667 foreach (keys %methods) {
1669 method => "user_transactions",
1672 desc => 'For a given user, retrieve a count of open '
1673 . 'transactions' . $methods{$_}
1674 . ' optionally limited to transactions of a given type.',
1675 params => $common_params,
1676 return => { desc => "Integer count of transactions, or event on error" }
1679 /\.have_balance/ and $args{authoritative} = 1; # FIXME: I don't know why have_charge isn't authoritative
1680 __PACKAGE__->register_method(%args);
1683 __PACKAGE__->register_method(
1684 method => "user_transactions",
1685 api_name => "open-ils.actor.user.transactions.have_balance.total",
1688 desc => 'For a given user, retrieve the total balance owed for open transactions,'
1689 . ' optionally limited to transactions of a given type.',
1690 params => $common_params,
1691 return => { desc => "Decimal balance value, or event on error" }
1696 sub user_transactions {
1697 my( $self, $client, $auth, $user_id, $type, $options ) = @_;
1700 my $e = new_editor(authtoken => $auth);
1701 return $e->event unless $e->checkauth;
1703 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
1705 return $e->event unless
1706 $e->requestor->id == $user_id or
1707 $e->allowed('VIEW_USER_TRANSACTIONS', $user->home_ou);
1709 my $api = $self->api_name();
1711 my $filter = ($api =~ /have_balance/o) ?
1712 { 'balance_owed' => { '<>' => 0 } }:
1713 { 'total_owed' => { '>' => 0 } };
1715 my $method = 'open-ils.actor.user.transactions.history.still_open';
1716 $method = "$method.authoritative" if $api =~ /authoritative/;
1717 my ($trans) = $self->method_lookup($method)->run($auth, $user_id, $type, $filter, $options);
1719 if($api =~ /total/o) {
1721 $total += $_->balance_owed for @$trans;
1725 ($api =~ /count/o ) and return scalar @$trans;
1726 ($api !~ /fleshed/o) and return $trans;
1729 for my $t (@$trans) {
1731 if( $t->xact_type ne 'circulation' ) {
1732 push @resp, {transaction => $t};
1736 my $circ_data = flesh_circ($e, $t->id);
1737 push @resp, {transaction => $t, %$circ_data};
1744 __PACKAGE__->register_method(
1745 method => "user_transaction_retrieve",
1746 api_name => "open-ils.actor.user.transaction.fleshed.retrieve",
1749 notes => "Returns a fleshed transaction record"
1752 __PACKAGE__->register_method(
1753 method => "user_transaction_retrieve",
1754 api_name => "open-ils.actor.user.transaction.retrieve",
1757 notes => "Returns a transaction record"
1760 sub user_transaction_retrieve {
1761 my($self, $client, $auth, $bill_id) = @_;
1763 my $e = new_editor(authtoken => $auth);
1764 return $e->event unless $e->checkauth;
1766 my $trans = $e->retrieve_money_billable_transaction_summary(
1767 [$bill_id, {flesh => 1, flesh_fields => {mbts => ['usr']}}]) or return $e->event;
1769 return $e->event unless $e->allowed('VIEW_USER_TRANSACTIONS', $trans->usr->home_ou);
1771 $trans->usr($trans->usr->id); # de-flesh for backwards compat
1773 return $trans unless $self->api_name =~ /flesh/;
1774 return {transaction => $trans} if $trans->xact_type ne 'circulation';
1776 my $circ_data = flesh_circ($e, $trans->id, 1);
1778 return {transaction => $trans, %$circ_data};
1783 my $circ_id = shift;
1784 my $flesh_copy = shift;
1786 my $circ = $e->retrieve_action_circulation([
1790 circ => ['target_copy'],
1791 acp => ['call_number'],
1798 my $copy = $circ->target_copy;
1800 if($circ->target_copy->call_number->id == OILS_PRECAT_CALL_NUMBER) {
1801 $mods = new Fieldmapper::metabib::virtual_record;
1802 $mods->doc_id(OILS_PRECAT_RECORD);
1803 $mods->title($copy->dummy_title);
1804 $mods->author($copy->dummy_author);
1807 $mods = $U->record_to_mvr($circ->target_copy->call_number->record);
1811 $circ->target_copy($circ->target_copy->id);
1812 $copy->call_number($copy->call_number->id);
1814 return {circ => $circ, record => $mods, copy => ($flesh_copy) ? $copy : undef };
1818 __PACKAGE__->register_method(
1819 method => "hold_request_count",
1820 api_name => "open-ils.actor.user.hold_requests.count",
1823 notes => 'Returns hold ready/total counts'
1826 sub hold_request_count {
1827 my( $self, $client, $authtoken, $user_id ) = @_;
1828 my $e = new_editor(authtoken => $authtoken);
1829 return $e->event unless $e->checkauth;
1831 $user_id = $e->requestor->id unless defined $user_id;
1833 if($e->requestor->id ne $user_id) {
1834 my $user = $e->retrieve_actor_user($user_id);
1835 return $e->event unless $e->allowed('VIEW_HOLD', $user->home_ou);
1838 my $holds = $e->json_query({
1839 select => {ahr => ['shelf_time']},
1843 fulfillment_time => {"=" => undef },
1844 cancel_time => undef,
1849 total => scalar(@$holds),
1850 ready => scalar(grep { $_->{shelf_time} } @$holds)
1854 __PACKAGE__->register_method(
1855 method => "checked_out",
1856 api_name => "open-ils.actor.user.checked_out",
1860 desc => "For a given user, returns a structure of circulations objects sorted by out, overdue, lost, claims_returned, long_overdue. "
1861 . "A list of IDs are returned of each type. Circs marked lost, long_overdue, and claims_returned will not be 'finished' "
1862 . "(i.e., outstanding balance or some other pending action on the circ). "
1863 . "The .count method also includes a 'total' field which sums all open circs.",
1865 { desc => 'Authentication Token', type => 'string'},
1866 { desc => 'User ID', type => 'string'},
1869 desc => 'Returns event on error, or an object with ID lists, like: '
1870 . '{"out":[12552,451232], "claims_returned":[], "long_overdue":[23421] "overdue":[], "lost":[]}'
1875 __PACKAGE__->register_method(
1876 method => "checked_out",
1877 api_name => "open-ils.actor.user.checked_out.count",
1880 signature => q/@see open-ils.actor.user.checked_out/
1884 my( $self, $conn, $auth, $userid ) = @_;
1886 my $e = new_editor(authtoken=>$auth);
1887 return $e->event unless $e->checkauth;
1889 if( $userid ne $e->requestor->id ) {
1890 my $user = $e->retrieve_actor_user($userid) or return $e->event;
1891 unless($e->allowed('VIEW_CIRCULATIONS', $user->home_ou)) {
1893 # see if there is a friend link allowing circ.view perms
1894 my $allowed = OpenILS::Application::Actor::Friends->friend_perm_allowed(
1895 $e, $userid, $e->requestor->id, 'circ.view');
1896 return $e->event unless $allowed;
1900 my $count = $self->api_name =~ /count/;
1901 return _checked_out( $count, $e, $userid );
1905 my( $iscount, $e, $userid ) = @_;
1911 claims_returned => [],
1914 my $meth = 'retrieve_action_open_circ_';
1922 claims_returned => 0,
1929 my $data = $e->$meth($userid);
1933 $result{$_} += $data->$_() for (keys %result);
1934 $result{total} += $data->$_() for (keys %result);
1936 for my $k (keys %result) {
1937 $result{$k} = [ grep { $_ > 0 } split( ',', $data->$k()) ];
1947 __PACKAGE__->register_method(
1948 method => "checked_in_with_fines",
1949 api_name => "open-ils.actor.user.checked_in_with_fines",
1952 signature => q/@see open-ils.actor.user.checked_out/
1955 sub checked_in_with_fines {
1956 my( $self, $conn, $auth, $userid ) = @_;
1958 my $e = new_editor(authtoken=>$auth);
1959 return $e->event unless $e->checkauth;
1961 if( $userid ne $e->requestor->id ) {
1962 return $e->event unless $e->allowed('VIEW_CIRCULATIONS');
1965 # money is owed on these items and they are checked in
1966 my $open = $e->search_action_circulation(
1969 xact_finish => undef,
1970 checkin_time => { "!=" => undef },
1975 my( @lost, @cr, @lo );
1976 for my $c (@$open) {
1977 push( @lost, $c->id ) if $c->stop_fines eq 'LOST';
1978 push( @cr, $c->id ) if $c->stop_fines eq 'CLAIMSRETURNED';
1979 push( @lo, $c->id ) if $c->stop_fines eq 'LONGOVERDUE';
1984 claims_returned => \@cr,
1985 long_overdue => \@lo
1991 my ($api, $desc, $auth) = @_;
1992 $desc = $desc ? (" " . $desc) : '';
1993 my $ids = ($api =~ /ids$/) ? 1 : 0;
1996 method => "user_transaction_history",
1997 api_name => "open-ils.actor.user.transactions.$api",
1999 desc => "For a given User ID, returns a list of billable transaction" .
2000 ($ids ? " id" : '') .
2001 "s$desc, optionally filtered by type and/or fields in money.billable_xact_summary. " .
2002 "The VIEW_USER_TRANSACTIONS permission is required to view another user's transactions",
2004 {desc => 'Authentication token', type => 'string'},
2005 {desc => 'User ID', type => 'number'},
2006 {desc => 'Transaction type (optional)', type => 'number'},
2007 {desc => 'Hash of Billable Transaction Summary filters (optional)', type => 'object'}
2010 desc => 'List of transaction' . ($ids ? " id" : '') . 's, Event on error'
2014 $auth and push @sig, (authoritative => 1);
2018 my %auth_hist_methods = (
2020 'history.have_charge' => 'that have an initial charge',
2021 'history.still_open' => 'that are not finished',
2022 'history.have_balance' => 'that have a balance',
2023 'history.have_bill' => 'that have billings',
2024 'history.have_bill_or_payment' => 'that have non-zero-sum billings or at least 1 payment',
2025 'history.have_payment' => 'that have at least 1 payment',
2028 foreach (keys %auth_hist_methods) {
2029 __PACKAGE__->register_method(_sigmaker($_, $auth_hist_methods{$_}, 1));
2030 __PACKAGE__->register_method(_sigmaker("$_.ids", $auth_hist_methods{$_}, 1));
2031 __PACKAGE__->register_method(_sigmaker("$_.fleshed", $auth_hist_methods{$_}, 1));
2034 sub user_transaction_history {
2035 my( $self, $conn, $auth, $userid, $type, $filter, $options ) = @_;
2039 my $e = new_editor(authtoken=>$auth);
2040 return $e->die_event unless $e->checkauth;
2042 if ($e->requestor->id ne $userid) {
2043 return $e->die_event unless $e->allowed('VIEW_USER_TRANSACTIONS');
2046 my $api = $self->api_name;
2047 my @xact_finish = (xact_finish => undef ) if ($api =~ /history\.still_open$/); # What about history.still_open.ids?
2049 if(defined($type)) {
2050 $filter->{'xact_type'} = $type;
2053 if($api =~ /have_bill_or_payment/o) {
2055 # transactions that have a non-zero sum across all billings or at least 1 payment
2056 $filter->{'-or'} = {
2057 'balance_owed' => { '<>' => 0 },
2058 'last_payment_ts' => { '<>' => undef }
2061 } elsif($api =~ /have_payment/) {
2063 $filter->{last_payment_ts} ||= {'<>' => undef};
2065 } elsif( $api =~ /have_balance/o) {
2067 # transactions that have a non-zero overall balance
2068 $filter->{'balance_owed'} = { '<>' => 0 };
2070 } elsif( $api =~ /have_charge/o) {
2072 # transactions that have at least 1 billing, regardless of whether it was voided
2073 $filter->{'last_billing_ts'} = { '<>' => undef };
2075 } elsif( $api =~ /have_bill/o) { # needs to be an elsif, or we double-match have_bill_or_payment!
2077 # transactions that have non-zero sum across all billings. This will exclude
2078 # xacts where all billings have been voided
2079 $filter->{'total_owed'} = { '<>' => 0 };
2082 my $options_clause = { order_by => { mbt => 'xact_start DESC' } };
2083 $options_clause->{'limit'} = $options->{'limit'} if $options->{'limit'};
2084 $options_clause->{'offset'} = $options->{'offset'} if $options->{'offset'};
2086 my $mbts = $e->search_money_billable_transaction_summary(
2087 [ { usr => $userid, @xact_finish, %$filter },
2092 return [map {$_->id} @$mbts] if $api =~ /\.ids/;
2093 return $mbts unless $api =~ /fleshed/;
2096 for my $t (@$mbts) {
2098 if( $t->xact_type ne 'circulation' ) {
2099 push @resp, {transaction => $t};
2103 my $circ_data = flesh_circ($e, $t->id);
2104 push @resp, {transaction => $t, %$circ_data};
2112 __PACKAGE__->register_method(
2113 method => "user_perms",
2114 api_name => "open-ils.actor.permissions.user_perms.retrieve",
2116 notes => "Returns a list of permissions"
2120 my( $self, $client, $authtoken, $user ) = @_;
2122 my( $staff, $evt ) = $apputils->checkses($authtoken);
2123 return $evt if $evt;
2125 $user ||= $staff->id;
2127 if( $user != $staff->id and $evt = $apputils->check_perms( $staff->id, $staff->home_ou, 'VIEW_PERMISSION') ) {
2131 return $apputils->simple_scalar_request(
2133 "open-ils.storage.permission.user_perms.atomic",
2137 __PACKAGE__->register_method(
2138 method => "retrieve_perms",
2139 api_name => "open-ils.actor.permissions.retrieve",
2140 notes => "Returns a list of permissions"
2142 sub retrieve_perms {
2143 my( $self, $client ) = @_;
2144 return $apputils->simple_scalar_request(
2146 "open-ils.cstore.direct.permission.perm_list.search.atomic",
2147 { id => { '!=' => undef } }
2151 __PACKAGE__->register_method(
2152 method => "retrieve_groups",
2153 api_name => "open-ils.actor.groups.retrieve",
2154 notes => "Returns a list of user groups"
2156 sub retrieve_groups {
2157 my( $self, $client ) = @_;
2158 return new_editor()->retrieve_all_permission_grp_tree();
2161 __PACKAGE__->register_method(
2162 method => "retrieve_org_address",
2163 api_name => "open-ils.actor.org_unit.address.retrieve",
2164 notes => <<' NOTES');
2165 Returns an org_unit address by ID
2166 @param An org_address ID
2168 sub retrieve_org_address {
2169 my( $self, $client, $id ) = @_;
2170 return $apputils->simple_scalar_request(
2172 "open-ils.cstore.direct.actor.org_address.retrieve",
2177 __PACKAGE__->register_method(
2178 method => "retrieve_groups_tree",
2179 api_name => "open-ils.actor.groups.tree.retrieve",
2180 notes => "Returns a list of user groups"
2183 sub retrieve_groups_tree {
2184 my( $self, $client ) = @_;
2185 return new_editor()->search_permission_grp_tree(
2190 flesh_fields => { pgt => ["children"] },
2191 order_by => { pgt => 'name'}
2198 __PACKAGE__->register_method(
2199 method => "add_user_to_groups",
2200 api_name => "open-ils.actor.user.set_groups",
2201 notes => "Adds a user to one or more permission groups"
2204 sub add_user_to_groups {
2205 my( $self, $client, $authtoken, $userid, $groups ) = @_;
2207 my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2208 $authtoken, $userid, 'CREATE_USER_GROUP_LINK' );
2209 return $evt if $evt;
2211 ( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2212 $authtoken, $userid, 'REMOVE_USER_GROUP_LINK' );
2213 return $evt if $evt;
2215 $apputils->simplereq(
2217 'open-ils.storage.direct.permission.usr_grp_map.mass_delete', { usr => $userid } );
2219 for my $group (@$groups) {
2220 my $link = Fieldmapper::permission::usr_grp_map->new;
2222 $link->usr($userid);
2224 my $id = $apputils->simplereq(
2226 'open-ils.storage.direct.permission.usr_grp_map.create', $link );
2232 __PACKAGE__->register_method(
2233 method => "get_user_perm_groups",
2234 api_name => "open-ils.actor.user.get_groups",
2235 notes => "Retrieve a user's permission groups."
2239 sub get_user_perm_groups {
2240 my( $self, $client, $authtoken, $userid ) = @_;
2242 my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2243 $authtoken, $userid, 'VIEW_PERM_GROUPS' );
2244 return $evt if $evt;
2246 return $apputils->simplereq(
2248 'open-ils.cstore.direct.permission.usr_grp_map.search.atomic', { usr => $userid } );
2252 __PACKAGE__->register_method(
2253 method => "get_user_work_ous",
2254 api_name => "open-ils.actor.user.get_work_ous",
2255 notes => "Retrieve a user's work org units."
2258 __PACKAGE__->register_method(
2259 method => "get_user_work_ous",
2260 api_name => "open-ils.actor.user.get_work_ous.ids",
2261 notes => "Retrieve a user's work org units."
2264 sub get_user_work_ous {
2265 my( $self, $client, $auth, $userid ) = @_;
2266 my $e = new_editor(authtoken=>$auth);
2267 return $e->event unless $e->checkauth;
2268 $userid ||= $e->requestor->id;
2270 if($e->requestor->id != $userid) {
2271 my $user = $e->retrieve_actor_user($userid)
2272 or return $e->event;
2273 return $e->event unless $e->allowed('ASSIGN_WORK_ORG_UNIT', $user->home_ou);
2276 return $e->search_permission_usr_work_ou_map({usr => $userid})
2277 unless $self->api_name =~ /.ids$/;
2279 # client just wants a list of org IDs
2280 return $U->get_user_work_ou_ids($e, $userid);
2285 __PACKAGE__->register_method(
2286 method => 'register_workstation',
2287 api_name => 'open-ils.actor.workstation.register.override',
2288 signature => q/@see open-ils.actor.workstation.register/
2291 __PACKAGE__->register_method(
2292 method => 'register_workstation',
2293 api_name => 'open-ils.actor.workstation.register',
2295 Registers a new workstion in the system
2296 @param authtoken The login session key
2297 @param name The name of the workstation id
2298 @param owner The org unit that owns this workstation
2299 @return The workstation id on success, WORKSTATION_NAME_EXISTS
2300 if the name is already in use.
2304 sub register_workstation {
2305 my( $self, $conn, $authtoken, $name, $owner ) = @_;
2307 my $e = new_editor(authtoken=>$authtoken, xact=>1);
2308 return $e->die_event unless $e->checkauth;
2309 return $e->die_event unless $e->allowed('REGISTER_WORKSTATION', $owner);
2310 my $existing = $e->search_actor_workstation({name => $name})->[0];
2314 if( $self->api_name =~ /override/o ) {
2315 # workstation with the given name exists.
2317 if($owner ne $existing->owning_lib) {
2318 # if necessary, update the owning_lib of the workstation
2320 $logger->info("changing owning lib of workstation ".$existing->id.
2321 " from ".$existing->owning_lib." to $owner");
2322 return $e->die_event unless
2323 $e->allowed('UPDATE_WORKSTATION', $existing->owning_lib);
2325 return $e->die_event unless $e->allowed('UPDATE_WORKSTATION', $owner);
2327 $existing->owning_lib($owner);
2328 return $e->die_event unless $e->update_actor_workstation($existing);
2334 "attempt to register an existing workstation. returning existing ID");
2337 return $existing->id;
2340 return OpenILS::Event->new('WORKSTATION_NAME_EXISTS')
2344 my $ws = Fieldmapper::actor::workstation->new;
2345 $ws->owning_lib($owner);
2347 $e->create_actor_workstation($ws) or return $e->die_event;
2349 return $ws->id; # note: editor sets the id on the new object for us
2352 __PACKAGE__->register_method(
2353 method => 'workstation_list',
2354 api_name => 'open-ils.actor.workstation.list',
2356 Returns a list of workstations registered at the given location
2357 @param authtoken The login session key
2358 @param ids A list of org_unit.id's for the workstation owners
2362 sub workstation_list {
2363 my( $self, $conn, $authtoken, @orgs ) = @_;
2365 my $e = new_editor(authtoken=>$authtoken);
2366 return $e->event unless $e->checkauth;
2371 unless $e->allowed('REGISTER_WORKSTATION', $o);
2372 $results{$o} = $e->search_actor_workstation({owning_lib=>$o});
2378 __PACKAGE__->register_method(
2379 method => 'fetch_patron_note',
2380 api_name => 'open-ils.actor.note.retrieve.all',
2383 Returns a list of notes for a given user
2384 Requestor must have VIEW_USER permission if pub==false and
2385 @param authtoken The login session key
2386 @param args Hash of params including
2387 patronid : the patron's id
2388 pub : true if retrieving only public notes
2392 sub fetch_patron_note {
2393 my( $self, $conn, $authtoken, $args ) = @_;
2394 my $patronid = $$args{patronid};
2396 my($reqr, $evt) = $U->checkses($authtoken);
2397 return $evt if $evt;
2400 ($patron, $evt) = $U->fetch_user($patronid);
2401 return $evt if $evt;
2404 if( $patronid ne $reqr->id ) {
2405 $evt = $U->check_perms($reqr->id, $patron->home_ou, 'VIEW_USER');
2406 return $evt if $evt;
2408 return $U->cstorereq(
2409 'open-ils.cstore.direct.actor.usr_note.search.atomic',
2410 { usr => $patronid, pub => 't' } );
2413 $evt = $U->check_perms($reqr->id, $patron->home_ou, 'VIEW_USER');
2414 return $evt if $evt;
2416 return $U->cstorereq(
2417 'open-ils.cstore.direct.actor.usr_note.search.atomic', { usr => $patronid } );
2420 __PACKAGE__->register_method(
2421 method => 'create_user_note',
2422 api_name => 'open-ils.actor.note.create',
2424 Creates a new note for the given user
2425 @param authtoken The login session key
2426 @param note The note object
2429 sub create_user_note {
2430 my( $self, $conn, $authtoken, $note ) = @_;
2431 my $e = new_editor(xact=>1, authtoken=>$authtoken);
2432 return $e->die_event unless $e->checkauth;
2434 my $user = $e->retrieve_actor_user($note->usr)
2435 or return $e->die_event;
2437 return $e->die_event unless
2438 $e->allowed('UPDATE_USER',$user->home_ou);
2440 $note->creator($e->requestor->id);
2441 $e->create_actor_usr_note($note) or return $e->die_event;
2447 __PACKAGE__->register_method(
2448 method => 'delete_user_note',
2449 api_name => 'open-ils.actor.note.delete',
2451 Deletes a note for the given user
2452 @param authtoken The login session key
2453 @param noteid The note id
2456 sub delete_user_note {
2457 my( $self, $conn, $authtoken, $noteid ) = @_;
2459 my $e = new_editor(xact=>1, authtoken=>$authtoken);
2460 return $e->die_event unless $e->checkauth;
2461 my $note = $e->retrieve_actor_usr_note($noteid)
2462 or return $e->die_event;
2463 my $user = $e->retrieve_actor_user($note->usr)
2464 or return $e->die_event;
2465 return $e->die_event unless
2466 $e->allowed('UPDATE_USER', $user->home_ou);
2468 $e->delete_actor_usr_note($note) or return $e->die_event;
2474 __PACKAGE__->register_method(
2475 method => 'update_user_note',
2476 api_name => 'open-ils.actor.note.update',
2478 @param authtoken The login session key
2479 @param note The note
2483 sub update_user_note {
2484 my( $self, $conn, $auth, $note ) = @_;
2485 my $e = new_editor(authtoken=>$auth, xact=>1);
2486 return $e->die_event unless $e->checkauth;
2487 my $patron = $e->retrieve_actor_user($note->usr)
2488 or return $e->die_event;
2489 return $e->die_event unless
2490 $e->allowed('UPDATE_USER', $patron->home_ou);
2491 $e->update_actor_user_note($note)
2492 or return $e->die_event;
2499 __PACKAGE__->register_method(
2500 method => 'create_closed_date',
2501 api_name => 'open-ils.actor.org_unit.closed_date.create',
2503 Creates a new closing entry for the given org_unit
2504 @param authtoken The login session key
2505 @param note The closed_date object
2508 sub create_closed_date {
2509 my( $self, $conn, $authtoken, $cd ) = @_;
2511 my( $user, $evt ) = $U->checkses($authtoken);
2512 return $evt if $evt;
2514 $evt = $U->check_perms($user->id, $cd->org_unit, 'CREATE_CLOSEING');
2515 return $evt if $evt;
2517 $logger->activity("user ".$user->id." creating library closing for ".$cd->org_unit);
2519 my $id = $U->storagereq(
2520 'open-ils.storage.direct.actor.org_unit.closed_date.create', $cd );
2521 return $U->DB_UPDATE_FAILED($cd) unless $id;
2526 __PACKAGE__->register_method(
2527 method => 'delete_closed_date',
2528 api_name => 'open-ils.actor.org_unit.closed_date.delete',
2530 Deletes a closing entry for the given org_unit
2531 @param authtoken The login session key
2532 @param noteid The close_date id
2535 sub delete_closed_date {
2536 my( $self, $conn, $authtoken, $cd ) = @_;
2538 my( $user, $evt ) = $U->checkses($authtoken);
2539 return $evt if $evt;
2542 ($cd_obj, $evt) = fetch_closed_date($cd);
2543 return $evt if $evt;
2545 $evt = $U->check_perms($user->id, $cd->org_unit, 'DELETE_CLOSEING');
2546 return $evt if $evt;
2548 $logger->activity("user ".$user->id." deleting library closing for ".$cd->org_unit);
2550 my $stat = $U->storagereq(
2551 'open-ils.storage.direct.actor.org_unit.closed_date.delete', $cd );
2552 return $U->DB_UPDATE_FAILED($cd) unless $stat;
2557 __PACKAGE__->register_method(
2558 method => 'usrname_exists',
2559 api_name => 'open-ils.actor.username.exists',
2561 desc => 'Check if a username is already taken (by an undeleted patron)',
2563 {desc => 'Authentication token', type => 'string'},
2564 {desc => 'Username', type => 'string'}
2567 desc => 'id of existing user if username exists, undef otherwise. Event on error'
2572 sub usrname_exists {
2573 my( $self, $conn, $auth, $usrname ) = @_;
2574 my $e = new_editor(authtoken=>$auth);
2575 return $e->event unless $e->checkauth;
2576 my $a = $e->search_actor_user({usrname => $usrname, deleted=>'f'}, {idlist=>1});
2577 return $$a[0] if $a and @$a;
2581 __PACKAGE__->register_method(
2582 method => 'barcode_exists',
2583 api_name => 'open-ils.actor.barcode.exists',
2585 signature => 'Returns 1 if the requested barcode exists, returns 0 otherwise'
2588 sub barcode_exists {
2589 my( $self, $conn, $auth, $barcode ) = @_;
2590 my $e = new_editor(authtoken=>$auth);
2591 return $e->event unless $e->checkauth;
2592 my $card = $e->search_actor_card({barcode => $barcode});
2598 #return undef unless @$card;
2599 #return $card->[0]->usr;
2603 __PACKAGE__->register_method(
2604 method => 'retrieve_net_levels',
2605 api_name => 'open-ils.actor.net_access_level.retrieve.all',
2608 sub retrieve_net_levels {
2609 my( $self, $conn, $auth ) = @_;
2610 my $e = new_editor(authtoken=>$auth);
2611 return $e->event unless $e->checkauth;
2612 return $e->retrieve_all_config_net_access_level();
2615 # Retain the old typo API name just in case
2616 __PACKAGE__->register_method(
2617 method => 'fetch_org_by_shortname',
2618 api_name => 'open-ils.actor.org_unit.retrieve_by_shorname',
2620 __PACKAGE__->register_method(
2621 method => 'fetch_org_by_shortname',
2622 api_name => 'open-ils.actor.org_unit.retrieve_by_shortname',
2624 sub fetch_org_by_shortname {
2625 my( $self, $conn, $sname ) = @_;
2626 my $e = new_editor();
2627 my $org = $e->search_actor_org_unit({ shortname => uc($sname)})->[0];
2628 return $e->event unless $org;
2633 __PACKAGE__->register_method(
2634 method => 'session_home_lib',
2635 api_name => 'open-ils.actor.session.home_lib',
2638 sub session_home_lib {
2639 my( $self, $conn, $auth ) = @_;
2640 my $e = new_editor(authtoken=>$auth);
2641 return undef unless $e->checkauth;
2642 my $org = $e->retrieve_actor_org_unit($e->requestor->home_ou);
2643 return $org->shortname;
2646 __PACKAGE__->register_method(
2647 method => 'session_safe_token',
2648 api_name => 'open-ils.actor.session.safe_token',
2650 Returns a hashed session ID that is safe for export to the world.
2651 This safe token will expire after 1 hour of non-use.
2652 @param auth Active authentication token
2656 sub session_safe_token {
2657 my( $self, $conn, $auth ) = @_;
2658 my $e = new_editor(authtoken=>$auth);
2659 return undef unless $e->checkauth;
2661 my $safe_token = md5_hex($auth);
2663 $cache ||= OpenSRF::Utils::Cache->new("global", 0);
2665 # Add more like the following if needed...
2667 "safe-token-home_lib-shortname-$safe_token",
2668 $e->retrieve_actor_org_unit(
2669 $e->requestor->home_ou
2678 __PACKAGE__->register_method(
2679 method => 'safe_token_home_lib',
2680 api_name => 'open-ils.actor.safe_token.home_lib.shortname',
2682 Returns the home library shortname from the session
2683 asscociated with a safe token from generated by
2684 open-ils.actor.session.safe_token.
2685 @param safe_token Active safe token
2689 sub safe_token_home_lib {
2690 my( $self, $conn, $safe_token ) = @_;
2692 $cache ||= OpenSRF::Utils::Cache->new("global", 0);
2693 return $cache->get_cache( 'safe-token-home_lib-shortname-'. $safe_token );
2698 __PACKAGE__->register_method(
2699 method => 'slim_tree',
2700 api_name => "open-ils.actor.org_tree.slim_hash.retrieve",
2703 my $tree = new_editor()->search_actor_org_unit(
2705 {"parent_ou" => undef },
2708 flesh_fields => { aou => ['children'] },
2709 order_by => { aou => 'name'},
2710 select => { aou => ["id","shortname", "name"]},
2715 return trim_tree($tree);
2721 return undef unless $tree;
2723 code => $tree->shortname,
2724 name => $tree->name,
2726 if( $tree->children and @{$tree->children} ) {
2727 $htree->{children} = [];
2728 for my $c (@{$tree->children}) {
2729 push( @{$htree->{children}}, trim_tree($c) );
2737 __PACKAGE__->register_method(
2738 method => "update_penalties",
2739 api_name => "open-ils.actor.user.penalties.update"
2742 sub update_penalties {
2743 my($self, $conn, $auth, $user_id) = @_;
2744 my $e = new_editor(authtoken=>$auth, xact => 1);
2745 return $e->die_event unless $e->checkauth;
2746 my $user = $e->retrieve_actor_user($user_id) or return $e->die_event;
2747 return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
2748 my $evt = OpenILS::Utils::Penalty->calculate_penalties($e, $user_id, $e->requestor->ws_ou);
2749 return $evt if $evt;
2755 __PACKAGE__->register_method(
2756 method => "apply_penalty",
2757 api_name => "open-ils.actor.user.penalty.apply"
2761 my($self, $conn, $auth, $penalty) = @_;
2763 my $e = new_editor(authtoken=>$auth, xact => 1);
2764 return $e->die_event unless $e->checkauth;
2766 my $user = $e->retrieve_actor_user($penalty->usr) or return $e->die_event;
2767 return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
2769 my $ptype = $e->retrieve_config_standing_penalty($penalty->standing_penalty) or return $e->die_event;
2772 (defined $ptype->org_depth) ?
2773 $U->org_unit_ancestor_at_depth($penalty->org_unit, $ptype->org_depth) :
2776 $penalty->org_unit($ctx_org);
2777 $penalty->staff($e->requestor->id);
2778 $e->create_actor_user_standing_penalty($penalty) or return $e->die_event;
2781 return $penalty->id;
2784 __PACKAGE__->register_method(
2785 method => "remove_penalty",
2786 api_name => "open-ils.actor.user.penalty.remove"
2789 sub remove_penalty {
2790 my($self, $conn, $auth, $penalty) = @_;
2791 my $e = new_editor(authtoken=>$auth, xact => 1);
2792 return $e->die_event unless $e->checkauth;
2793 my $user = $e->retrieve_actor_user($penalty->usr) or return $e->die_event;
2794 return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
2796 $e->delete_actor_user_standing_penalty($penalty) or return $e->die_event;
2801 __PACKAGE__->register_method(
2802 method => "update_penalty_note",
2803 api_name => "open-ils.actor.user.penalty.note.update"
2806 sub update_penalty_note {
2807 my($self, $conn, $auth, $penalty_ids, $note) = @_;
2808 my $e = new_editor(authtoken=>$auth, xact => 1);
2809 return $e->die_event unless $e->checkauth;
2810 for my $penalty_id (@$penalty_ids) {
2811 my $penalty = $e->search_actor_user_standing_penalty( { id => $penalty_id } )->[0];
2812 if (! $penalty ) { return $e->die_event; }
2813 my $user = $e->retrieve_actor_user($penalty->usr) or return $e->die_event;
2814 return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
2816 $penalty->note( $note ); $penalty->ischanged( 1 );
2818 $e->update_actor_user_standing_penalty($penalty) or return $e->die_event;
2824 __PACKAGE__->register_method(
2825 method => "ranged_penalty_thresholds",
2826 api_name => "open-ils.actor.grp_penalty_threshold.ranged.retrieve",
2830 sub ranged_penalty_thresholds {
2831 my($self, $conn, $auth, $context_org) = @_;
2832 my $e = new_editor(authtoken=>$auth);
2833 return $e->event unless $e->checkauth;
2834 return $e->event unless $e->allowed('VIEW_GROUP_PENALTY_THRESHOLD', $context_org);
2835 my $list = $e->search_permission_grp_penalty_threshold([
2836 {org_unit => $U->get_org_ancestors($context_org)},
2837 {order_by => {pgpt => 'id'}}
2839 $conn->respond($_) for @$list;
2845 __PACKAGE__->register_method(
2846 method => "user_retrieve_fleshed_by_id",
2848 api_name => "open-ils.actor.user.fleshed.retrieve",
2851 sub user_retrieve_fleshed_by_id {
2852 my( $self, $client, $auth, $user_id, $fields ) = @_;
2853 my $e = new_editor(authtoken => $auth);
2854 return $e->event unless $e->checkauth;
2856 if( $e->requestor->id != $user_id ) {
2857 return $e->event unless $e->allowed('VIEW_USER');
2863 "standing_penalties",
2867 "stat_cat_entries" ];
2868 return new_flesh_user($user_id, $fields, $e);
2872 sub new_flesh_user {
2875 my $fields = shift || [];
2878 my $fetch_penalties = 0;
2879 if(grep {$_ eq 'standing_penalties'} @$fields) {
2880 $fields = [grep {$_ ne 'standing_penalties'} @$fields];
2881 $fetch_penalties = 1;
2884 my $user = $e->retrieve_actor_user(
2889 "flesh_fields" => { "au" => $fields }
2892 ) or return $e->die_event;
2895 if( grep { $_ eq 'addresses' } @$fields ) {
2897 $user->addresses([]) unless @{$user->addresses};
2898 # don't expose "replaced" addresses by default
2899 $user->addresses([grep {$_->id >= 0} @{$user->addresses}]);
2901 if( ref $user->billing_address ) {
2902 unless( grep { $user->billing_address->id == $_->id } @{$user->addresses} ) {
2903 push( @{$user->addresses}, $user->billing_address );
2907 if( ref $user->mailing_address ) {
2908 unless( grep { $user->mailing_address->id == $_->id } @{$user->addresses} ) {
2909 push( @{$user->addresses}, $user->mailing_address );
2914 if($fetch_penalties) {
2915 # grab the user penalties ranged for this location
2916 $user->standing_penalties(
2917 $e->search_actor_user_standing_penalty([
2920 {stop_date => undef},
2921 {stop_date => {'>' => 'now'}}
2923 org_unit => $U->get_org_full_path($e->requestor->ws_ou)
2926 flesh_fields => {ausp => ['standing_penalty']}
2933 $user->clear_passwd();
2940 __PACKAGE__->register_method(
2941 method => "user_retrieve_parts",
2942 api_name => "open-ils.actor.user.retrieve.parts",
2945 sub user_retrieve_parts {
2946 my( $self, $client, $auth, $user_id, $fields ) = @_;
2947 my $e = new_editor(authtoken => $auth);
2948 return $e->event unless $e->checkauth;
2949 $user_id ||= $e->requestor->id;
2950 if( $e->requestor->id != $user_id ) {
2951 return $e->event unless $e->allowed('VIEW_USER');
2954 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
2955 push(@resp, $user->$_()) for(@$fields);
2961 __PACKAGE__->register_method(
2962 method => 'user_opt_in_enabled',
2963 api_name => 'open-ils.actor.user.org_unit_opt_in.enabled',
2964 signature => '@return 1 if user opt-in is globally enabled, 0 otherwise.'
2967 sub user_opt_in_enabled {
2968 my($self, $conn) = @_;
2969 my $sc = OpenSRF::Utils::SettingsClient->new;
2970 return 1 if lc($sc->config_value(share => user => 'opt_in')) eq 'true';
2975 __PACKAGE__->register_method(
2976 method => 'user_opt_in_at_org',
2977 api_name => 'open-ils.actor.user.org_unit_opt_in.check',
2979 @param $auth The auth token
2980 @param user_id The ID of the user to test
2981 @return 1 if the user has opted in at the specified org,
2982 event on error, and 0 otherwise. /
2984 sub user_opt_in_at_org {
2985 my($self, $conn, $auth, $user_id) = @_;
2987 # see if we even need to enforce the opt-in value
2988 return 1 unless user_opt_in_enabled($self);
2990 my $e = new_editor(authtoken => $auth);
2991 return $e->event unless $e->checkauth;
2993 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
2994 return $e->event unless $e->allowed('VIEW_USER', $user->home_ou);
2996 my $ws_org = $e->requestor->ws_ou;
2997 # user is automatically opted-in if they are from the local org
2998 return 1 if $user->home_ou eq $ws_org;
3000 # get the boundary setting
3001 my $opt_boundary = $U->ou_ancestor_setting_value($e->requestor->ws_ou,'org.patron_opt_boundary');
3003 # auto opt in if user falls within the opt boundary
3004 my $opt_orgs = $U->get_org_descendants($ws_org, $opt_boundary);
3006 return 1 if grep $_ eq $user->home_ou, @$opt_orgs;
3008 my $vals = $e->search_actor_usr_org_unit_opt_in(
3009 {org_unit=>$opt_orgs, usr=>$user_id},{idlist=>1});
3015 __PACKAGE__->register_method(
3016 method => 'create_user_opt_in_at_org',
3017 api_name => 'open-ils.actor.user.org_unit_opt_in.create',
3019 @param $auth The auth token
3020 @param user_id The ID of the user to test
3021 @return The ID of the newly created object, event on error./
3024 sub create_user_opt_in_at_org {
3025 my($self, $conn, $auth, $user_id, $org_id) = @_;
3027 my $e = new_editor(authtoken => $auth, xact=>1);
3028 return $e->die_event unless $e->checkauth;
3030 # if a specific org unit wasn't passed in, get one based on the defaults;
3032 my $wsou = $e->requestor->ws_ou;
3033 # get the default opt depth
3034 my $opt_depth = $U->ou_ancestor_setting_value($wsou,'org.patron_opt_default');
3035 # get the org unit at that depth
3036 my $org = $e->json_query({
3037 from => [ 'actor.org_unit_ancestor_at_depth', $wsou, $opt_depth ]})->[0];
3039 $org_id = $org->{id};
3042 my $user = $e->retrieve_actor_user($user_id) or return $e->die_event;
3043 return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
3045 my $opt_in = Fieldmapper::actor::usr_org_unit_opt_in->new;
3047 $opt_in->org_unit($org_id);
3048 $opt_in->usr($user_id);
3049 $opt_in->staff($e->requestor->id);
3050 $opt_in->opt_in_ts('now');
3051 $opt_in->opt_in_ws($e->requestor->wsid);
3053 $opt_in = $e->create_actor_usr_org_unit_opt_in($opt_in)
3054 or return $e->die_event;
3062 __PACKAGE__->register_method (
3063 method => 'retrieve_org_hours',
3064 api_name => 'open-ils.actor.org_unit.hours_of_operation.retrieve',
3066 Returns the hours of operation for a specified org unit
3067 @param authtoken The login session key
3068 @param org_id The org_unit ID
3072 sub retrieve_org_hours {
3073 my($self, $conn, $auth, $org_id) = @_;
3074 my $e = new_editor(authtoken => $auth);
3075 return $e->die_event unless $e->checkauth;
3076 $org_id ||= $e->requestor->ws_ou;
3077 return $e->retrieve_actor_org_unit_hours_of_operation($org_id);
3081 __PACKAGE__->register_method (
3082 method => 'verify_user_password',
3083 api_name => 'open-ils.actor.verify_user_password',
3085 Given a barcode or username and the MD5 encoded password,
3086 returns 1 if the password is correct. Returns 0 otherwise.
3090 sub verify_user_password {
3091 my($self, $conn, $auth, $barcode, $username, $password) = @_;
3092 my $e = new_editor(authtoken => $auth);
3093 return $e->die_event unless $e->checkauth;
3095 my $user_by_barcode;
3096 my $user_by_username;
3098 my $card = $e->search_actor_card([
3099 {barcode => $barcode},
3100 {flesh => 1, flesh_fields => {ac => ['usr']}}])->[0] or return 0;
3101 $user_by_barcode = $card->usr;
3102 $user = $user_by_barcode;
3105 $user_by_username = $e->search_actor_user({usrname => $username})->[0] or return 0;
3106 $user = $user_by_username;
3108 return 0 if (!$user);
3109 return 0 if ($user_by_username && $user_by_barcode && $user_by_username->id != $user_by_barcode->id);
3110 return $e->event unless $e->allowed('VIEW_USER', $user->home_ou);
3111 return 1 if $user->passwd eq $password;
3115 __PACKAGE__->register_method (
3116 method => 'retrieve_usr_id_via_barcode_or_usrname',
3117 api_name => "open-ils.actor.user.retrieve_id_by_barcode_or_username",
3119 Given a barcode or username returns the id for the user or
3124 sub retrieve_usr_id_via_barcode_or_usrname {
3125 my($self, $conn, $auth, $barcode, $username) = @_;
3126 my $e = new_editor(authtoken => $auth);
3127 return $e->die_event unless $e->checkauth;
3128 my $id_as_barcode= OpenSRF::Utils::SettingsClient->new->config_value(apps => 'open-ils.actor' => app_settings => 'id_as_barcode');
3130 my $user_by_barcode;
3131 my $user_by_username;
3132 $logger->info("$id_as_barcode is the ID as BARCODE");
3134 my $card = $e->search_actor_card([
3135 {barcode => $barcode},
3136 {flesh => 1, flesh_fields => {ac => ['usr']}}])->[0];
3137 if ($id_as_barcode =~ /^t/i) {
3139 $user = $e->retrieve_actor_user($barcode);
3140 return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' ) if(!$user);
3142 $user_by_barcode = $card->usr;
3143 $user = $user_by_barcode;
3146 return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' ) if(!$card);
3147 $user_by_barcode = $card->usr;
3148 $user = $user_by_barcode;
3153 $user_by_username = $e->search_actor_user({usrname => $username})->[0] or return OpenILS::Event->new( 'ACTOR_USR_NOT_FOUND' );
3155 $user = $user_by_username;
3157 return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' ) if (!$user);
3158 return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' ) if ($user_by_username && $user_by_barcode && $user_by_username->id != $user_by_barcode->id);
3159 return $e->event unless $e->allowed('VIEW_USER', $user->home_ou);
3164 __PACKAGE__->register_method (
3165 method => 'merge_users',
3166 api_name => 'open-ils.actor.user.merge',
3169 Given a list of source users and destination user, transfer all data from the source
3170 to the dest user and delete the source user. All user related data is
3171 transferred, including circulations, holds, bookbags, etc.
3177 my($self, $conn, $auth, $master_id, $user_ids, $options) = @_;
3178 my $e = new_editor(xact => 1, authtoken => $auth);
3179 return $e->die_event unless $e->checkauth;
3181 # disallow the merge if any subordinate accounts are in collections
3182 my $colls = $e->search_money_collections_tracker({usr => $user_ids}, {idlist => 1});
3183 return OpenILS::Event->new('MERGED_USER_IN_COLLECTIONS', payload => $user_ids) if @$colls;
3185 my $master_user = $e->retrieve_actor_user($master_id) or return $e->die_event;
3186 my $del_addrs = ($U->ou_ancestor_setting_value(
3187 $master_user->home_ou, 'circ.user_merge.delete_addresses', $e)) ? 't' : 'f';
3188 my $del_cards = ($U->ou_ancestor_setting_value(
3189 $master_user->home_ou, 'circ.user_merge.delete_cards', $e)) ? 't' : 'f';
3190 my $deactivate_cards = ($U->ou_ancestor_setting_value(
3191 $master_user->home_ou, 'circ.user_merge.deactivate_cards', $e)) ? 't' : 'f';
3193 for my $src_id (@$user_ids) {
3194 my $src_user = $e->retrieve_actor_user($src_id) or return $e->die_event;
3196 return $e->die_event unless $e->allowed('MERGE_USERS', $src_user->home_ou);
3197 if($src_user->home_ou ne $master_user->home_ou) {
3198 return $e->die_event unless $e->allowed('MERGE_USERS', $master_user->home_ou);
3201 return $e->die_event unless
3202 $e->json_query({from => [
3217 __PACKAGE__->register_method (
3218 method => 'approve_user_address',
3219 api_name => 'open-ils.actor.user.pending_address.approve',
3226 sub approve_user_address {
3227 my($self, $conn, $auth, $addr) = @_;
3228 my $e = new_editor(xact => 1, authtoken => $auth);
3229 return $e->die_event unless $e->checkauth;
3231 # if the caller passes an address object, assume they want to
3232 # update it first before approving it
3233 $e->update_actor_user_address($addr) or return $e->die_event;
3235 $addr = $e->retrieve_actor_user_address($addr) or return $e->die_event;
3237 my $user = $e->retrieve_actor_user($addr->usr);
3238 return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
3239 my $result = $e->json_query({from => ['actor.approve_pending_address', $addr->id]})->[0]
3240 or return $e->die_event;
3242 return [values %$result]->[0];
3246 __PACKAGE__->register_method (
3247 method => 'retrieve_friends',
3248 api_name => 'open-ils.actor.friends.retrieve',
3251 returns { confirmed: [], pending_out: [], pending_in: []}
3252 pending_out are users I'm requesting friendship with
3253 pending_in are users requesting friendship with me
3258 sub retrieve_friends {
3259 my($self, $conn, $auth, $user_id, $options) = @_;
3260 my $e = new_editor(authtoken => $auth);
3261 return $e->event unless $e->checkauth;
3262 $user_id ||= $e->requestor->id;
3264 if($user_id != $e->requestor->id) {
3265 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
3266 return $e->event unless $e->allowed('VIEW_USER', $user->home_ou);
3269 return OpenILS::Application::Actor::Friends->retrieve_friends(
3270 $e, $user_id, $options);
3275 __PACKAGE__->register_method (
3276 method => 'apply_friend_perms',
3277 api_name => 'open-ils.actor.friends.perms.apply',
3283 sub apply_friend_perms {
3284 my($self, $conn, $auth, $user_id, $delegate_id, @perms) = @_;
3285 my $e = new_editor(authtoken => $auth, xact => 1);
3286 return $e->die_event unless $e->checkauth;
3288 if($user_id != $e->requestor->id) {
3289 my $user = $e->retrieve_actor_user($user_id) or return $e->die_event;
3290 return $e->die_event unless $e->allowed('VIEW_USER', $user->home_ou);
3293 for my $perm (@perms) {
3295 OpenILS::Application::Actor::Friends->apply_friend_perm(
3296 $e, $user_id, $delegate_id, $perm);
3297 return $evt if $evt;
3305 __PACKAGE__->register_method (
3306 method => 'update_user_pending_address',
3307 api_name => 'open-ils.actor.user.address.pending.cud'
3310 sub update_user_pending_address {
3311 my($self, $conn, $auth, $addr) = @_;
3312 my $e = new_editor(authtoken => $auth, xact => 1);
3313 return $e->die_event unless $e->checkauth;
3315 if($addr->usr != $e->requestor->id) {
3316 my $user = $e->retrieve_actor_user($addr->usr) or return $e->die_event;
3317 return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
3321 $e->create_actor_user_address($addr) or return $e->die_event;
3322 } elsif($addr->isdeleted) {
3323 $e->delete_actor_user_address($addr) or return $e->die_event;
3325 $e->update_actor_user_address($addr) or return $e->die_event;
3333 __PACKAGE__->register_method (
3334 method => 'user_events',
3335 api_name => 'open-ils.actor.user.events.circ',
3338 __PACKAGE__->register_method (
3339 method => 'user_events',
3340 api_name => 'open-ils.actor.user.events.ahr',
3345 my($self, $conn, $auth, $user_id, $filters) = @_;
3346 my $e = new_editor(authtoken => $auth);
3347 return $e->event unless $e->checkauth;
3349 (my $obj_type = $self->api_name) =~ s/.*\.([a-z]+)$/$1/;
3350 my $user_field = 'usr';
3353 $filters->{target} = {
3354 select => { $obj_type => ['id'] },
3356 where => {usr => $user_id}
3359 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
3360 if($e->requestor->id != $user_id) {
3361 return $e->event unless $e->allowed('VIEW_USER', $user->home_ou);
3364 my $ses = OpenSRF::AppSession->create('open-ils.trigger');
3365 my $req = $ses->request('open-ils.trigger.events_by_target',
3366 $obj_type, $filters, {atevdef => ['reactor', 'validator']}, 2);
3368 while(my $resp = $req->recv) {
3369 my $val = $resp->content;
3370 my $tgt = $val->target;
3372 if($obj_type eq 'circ') {
3373 $tgt->target_copy($e->retrieve_asset_copy($tgt->target_copy));
3375 } elsif($obj_type eq 'ahr') {
3376 $tgt->current_copy($e->retrieve_asset_copy($tgt->current_copy))
3377 if $tgt->current_copy;
3380 $conn->respond($val) if $val;
3386 __PACKAGE__->register_method (
3387 method => 'copy_events',
3388 api_name => 'open-ils.actor.copy.events.circ',
3391 __PACKAGE__->register_method (
3392 method => 'copy_events',
3393 api_name => 'open-ils.actor.copy.events.ahr',
3398 my($self, $conn, $auth, $copy_id, $filters) = @_;
3399 my $e = new_editor(authtoken => $auth);
3400 return $e->event unless $e->checkauth;
3402 (my $obj_type = $self->api_name) =~ s/.*\.([a-z]+)$/$1/;
3404 my $copy = $e->retrieve_asset_copy($copy_id) or return $e->event;
3406 my $copy_field = 'target_copy';
3407 $copy_field = 'current_copy' if $obj_type eq 'ahr';
3410 $filters->{target} = {
3411 select => { $obj_type => ['id'] },
3413 where => {$copy_field => $copy_id}
3417 my $ses = OpenSRF::AppSession->create('open-ils.trigger');
3418 my $req = $ses->request('open-ils.trigger.events_by_target',
3419 $obj_type, $filters, {atevdef => ['reactor', 'validator']}, 2);
3421 while(my $resp = $req->recv) {
3422 my $val = $resp->content;
3423 my $tgt = $val->target;
3425 my $user = $e->retrieve_actor_user($tgt->usr);
3426 if($e->requestor->id != $user->id) {
3427 return $e->event unless $e->allowed('VIEW_USER', $user->home_ou);
3430 $tgt->$copy_field($copy);
3433 $conn->respond($val) if $val;
3442 __PACKAGE__->register_method (
3443 method => 'update_events',
3444 api_name => 'open-ils.actor.user.event.cancel.batch',
3447 __PACKAGE__->register_method (
3448 method => 'update_events',
3449 api_name => 'open-ils.actor.user.event.reset.batch',
3454 my($self, $conn, $auth, $event_ids) = @_;
3455 my $e = new_editor(xact => 1, authtoken => $auth);
3456 return $e->die_event unless $e->checkauth;
3459 for my $id (@$event_ids) {
3461 # do a little dance to determine what user we are ultimately affecting
3462 my $event = $e->retrieve_action_trigger_event([
3465 flesh_fields => {atev => ['event_def'], atevdef => ['hook']}
3467 ]) or return $e->die_event;
3470 if($event->event_def->hook->core_type eq 'circ') {
3471 $user_id = $e->retrieve_action_circulation($event->target)->usr;
3472 } elsif($event->event_def->hook->core_type eq 'ahr') {
3473 $user_id = $e->retrieve_action_hold_request($event->target)->usr;
3478 my $user = $e->retrieve_actor_user($user_id);
3479 return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
3481 if($self->api_name =~ /cancel/) {
3482 $event->state('invalid');
3483 } elsif($self->api_name =~ /reset/) {
3484 $event->clear_start_time;
3485 $event->clear_update_time;
3486 $event->state('pending');
3489 $e->update_action_trigger_event($event) or return $e->die_event;
3490 $conn->respond({maximum => scalar(@$event_ids), progress => $x++});
3494 return {complete => 1};
3498 __PACKAGE__->register_method (
3499 method => 'really_delete_user',
3500 api_name => 'open-ils.actor.user.delete',
3502 It anonymizes all personally identifiable information in actor.usr. By calling actor.usr_purge_data()
3503 it also purges related data from other tables, sometimes by transferring it to a designated destination user.
3504 The usrname field (along with first_given_name and family_name) is updated to id '-PURGED-' now().
3505 dest_usr_id is only required when deleting a user that performs staff functions.
3509 sub really_delete_user {
3510 my($self, $conn, $auth, $user_id, $dest_user_id) = @_;
3511 my $e = new_editor(authtoken => $auth, xact => 1);
3512 return $e->die_event unless $e->checkauth;
3513 my $user = $e->retrieve_actor_user($user_id) or return $e->die_event;
3514 # No deleting yourself - UI is supposed to stop you first, though.
3515 return $e->die_event unless $e->requestor->id != $user->id;
3516 return $e->die_event unless $e->allowed('DELETE_USER', $user->home_ou);
3517 # Check if you are allowed to mess with this patron permission group at all
3518 my $session = OpenSRF::AppSession->create( "open-ils.storage" );
3519 my $evt = group_perm_failed($session, $e->requestor, $user);
3520 return $e->die_event($evt) if $evt;
3521 my $stat = $e->json_query(
3522 {from => ['actor.usr_delete', $user_id, $dest_user_id]})->[0]
3523 or return $e->die_event;
3530 __PACKAGE__->register_method (
3531 method => 'user_payments',
3532 api_name => 'open-ils.actor.user.payments.retrieve',
3535 Returns all payments for a given user. Default order is newest payments first.
3536 @param auth Authentication token
3537 @param user_id The user ID
3538 @param filters An optional hash of filters, including limit, offset, and order_by definitions
3543 my($self, $conn, $auth, $user_id, $filters) = @_;
3546 my $e = new_editor(authtoken => $auth);
3547 return $e->die_event unless $e->checkauth;
3549 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
3550 return $e->event unless
3551 $e->requestor->id == $user_id or
3552 $e->allowed('VIEW_USER_TRANSACTIONS', $user->home_ou);
3554 # Find all payments for all transactions for user $user_id
3556 select => {mp => ['id']},
3561 select => {mbt => ['id']},
3563 where => {usr => $user_id}
3567 order_by => [{ # by default, order newest payments first
3569 field => 'payment_ts',
3574 for (qw/order_by limit offset/) {
3575 $query->{$_} = $filters->{$_} if defined $filters->{$_};
3578 if(defined $filters->{where}) {
3579 foreach (keys %{$filters->{where}}) {
3580 # don't allow the caller to expand the result set to other users
3581 $query->{where}->{$_} = $filters->{where}->{$_} unless $_ eq 'xact';
3585 my $payment_ids = $e->json_query($query);
3586 for my $pid (@$payment_ids) {
3587 my $pay = $e->retrieve_money_payment([
3592 mbt => ['summary', 'circulation', 'grocery'],
3593 circ => ['target_copy'],
3594 acp => ['call_number'],
3602 xact_type => $pay->xact->summary->xact_type,
3603 last_billing_type => $pay->xact->summary->last_billing_type,
3606 if($pay->xact->summary->xact_type eq 'circulation') {
3607 $resp->{barcode} = $pay->xact->circulation->target_copy->barcode;
3608 $resp->{title} = $U->record_to_mvr($pay->xact->circulation->target_copy->call_number->record)->title;
3611 $pay->xact($pay->xact->id); # de-flesh
3612 $conn->respond($resp);
3620 __PACKAGE__->register_method (
3621 method => 'negative_balance_users',
3622 api_name => 'open-ils.actor.users.negative_balance',
3625 Returns all users that have an overall negative balance
3626 @param auth Authentication token
3627 @param org_id The context org unit as an ID or list of IDs. This will be the home
3628 library of the user. If no org_unit is specified, no org unit filter is applied
3632 sub negative_balance_users {
3633 my($self, $conn, $auth, $org_id) = @_;
3635 my $e = new_editor(authtoken => $auth);
3636 return $e->die_event unless $e->checkauth;
3637 return $e->die_event unless $e->allowed('VIEW_USER', $org_id);
3641 mous => ['usr', 'balance_owed'],
3644 {column => 'last_billing_ts', transform => 'max', aggregate => 1},
3645 {column => 'last_payment_ts', transform => 'max', aggregate => 1},
3662 where => {'+mous' => {balance_owed => {'<' => 0}}}
3665 $query->{from}->{mous}->{au}->{filter}->{home_ou} = $org_id if $org_id;
3667 my $list = $e->json_query($query, {timeout => 600});
3669 for my $data (@$list) {
3671 usr => $e->retrieve_actor_user([$data->{usr}, {flesh => 1, flesh_fields => {au => ['card']}}]),
3672 balance_owed => $data->{balance_owed},
3673 last_billing_activity => max($data->{last_billing_ts}, $data->{last_payment_ts})
3680 __PACKAGE__->register_method(
3681 method => "request_password_reset",
3682 api_name => "open-ils.actor.patron.password_reset.request",
3684 desc => "Generates a UUID token usable with the open-ils.actor.patron.password_reset.commit " .
3685 "method for changing a user's password. The UUID token is distributed via A/T " .
3686 "templates (i.e. email to the user).",
3688 { desc => 'user_id_type', type => 'string' },
3689 { desc => 'user_id', type => 'string' },
3690 { desc => 'optional (based on library setting) matching email address for authorizing request', type => 'string' },
3692 return => {desc => '1 on success, Event on error'}
3695 sub request_password_reset {
3696 my($self, $conn, $user_id_type, $user_id, $email) = @_;
3698 # Check to see if password reset requests are already being throttled:
3699 # 0. Check cache to see if we're in throttle mode (avoid hitting database)
3701 my $e = new_editor(xact => 1);
3704 # Get the user, if any, depending on the input value
3705 if ($user_id_type eq 'username') {
3706 $user = $e->search_actor_user({usrname => $user_id})->[0];
3709 return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' );
3711 } elsif ($user_id_type eq 'barcode') {
3712 my $card = $e->search_actor_card([
3713 {barcode => $user_id},
3714 {flesh => 1, flesh_fields => {ac => ['usr']}}])->[0];
3717 return OpenILS::Event->new('ACTOR_USER_NOT_FOUND');
3722 # If the user doesn't have an email address, we can't help them
3723 if (!$user->email) {
3725 return OpenILS::Event->new('PATRON_NO_EMAIL_ADDRESS');
3728 my $email_must_match = $U->ou_ancestor_setting_value($user->home_ou, 'circ.password_reset_request_requires_matching_email');
3729 if ($email_must_match) {
3730 if ($user->email ne $email) {
3731 return OpenILS::Event->new('EMAIL_VERIFICATION_FAILED');
3735 _reset_password_request($conn, $e, $user);
3738 # Once we have the user, we can issue the password reset request
3739 # XXX Add a wrapper method that accepts barcode + email input
3740 sub _reset_password_request {
3741 my ($conn, $e, $user) = @_;
3743 # 1. Get throttle threshold and time-to-live from OU_settings
3744 my $aupr_throttle = $U->ou_ancestor_setting_value($user->home_ou, 'circ.password_reset_request_throttle') || 1000;
3745 my $aupr_ttl = $U->ou_ancestor_setting_value($user->home_ou, 'circ.password_reset_request_time_to_live') || 24*60*60;
3747 my $threshold_time = DateTime->now(time_zone => 'local')->subtract(seconds => $aupr_ttl)->iso8601();
3749 # 2. Get time of last request and number of active requests (num_active)
3750 my $active_requests = $e->json_query({
3756 transform => 'COUNT'
3759 column => 'request_time',
3765 has_been_reset => { '=' => 'f' },
3766 request_time => { '>' => $threshold_time }
3770 # Guard against no active requests
3771 if ($active_requests->[0]->{'request_time'}) {
3772 my $last_request = DateTime::Format::ISO8601->parse_datetime(clense_ISO8601($active_requests->[0]->{'request_time'}));
3773 my $now = DateTime::Format::ISO8601->new();
3775 # 3. if (num_active > throttle_threshold) and (now - last_request < 1 minute)
3776 if (($active_requests->[0]->{'usr'} > $aupr_throttle) &&
3777 ($last_request->add_duration('1 minute') > $now)) {
3778 $cache->put_cache('open-ils.actor.password.throttle', DateTime::Format::ISO8601->new(), 60);
3780 return OpenILS::Event->new('PATRON_TOO_MANY_ACTIVE_PASSWORD_RESET_REQUESTS');
3784 # TODO Check to see if the user is in a password-reset-restricted group
3786 # Otherwise, go ahead and try to get the user.
3788 # Check the number of active requests for this user
3789 $active_requests = $e->json_query({
3795 transform => 'COUNT'
3800 usr => { '=' => $user->id },
3801 has_been_reset => { '=' => 'f' },
3802 request_time => { '>' => $threshold_time }
3806 $logger->info("User " . $user->id . " has " . $active_requests->[0]->{'usr'} . " active password reset requests.");
3808 # if less than or equal to per-user threshold, proceed; otherwise, return event
3809 my $aupr_per_user_limit = $U->ou_ancestor_setting_value($user->home_ou, 'circ.password_reset_request_per_user_limit') || 3;
3810 if ($active_requests->[0]->{'usr'} > $aupr_per_user_limit) {
3812 return OpenILS::Event->new('PATRON_TOO_MANY_ACTIVE_PASSWORD_RESET_REQUESTS');
3815 # Create the aupr object and insert into the database
3816 my $reset_request = Fieldmapper::actor::usr_password_reset->new;
3817 my $uuid = create_uuid_as_string(UUID_V4);
3818 $reset_request->uuid($uuid);
3819 $reset_request->usr($user->id);
3821 my $aupr = $e->create_actor_usr_password_reset($reset_request) or return $e->die_event;
3824 # Create an event to notify user of the URL to reset their password
3826 # Can we stuff this in the user_data param for trigger autocreate?
3827 my $hostname = $U->ou_ancestor_setting_value($user->home_ou, 'lib.hostname') || 'localhost';
3829 my $ses = OpenSRF::AppSession->create('open-ils.trigger');
3830 $ses->request('open-ils.trigger.event.autocreate', 'password.reset_request', $aupr, $user->home_ou);
3833 # $U->create_trigger_event('password.reset_request', $aupr, $user->home_ou);
3838 __PACKAGE__->register_method(
3839 method => "commit_password_reset",
3840 api_name => "open-ils.actor.patron.password_reset.commit",
3842 desc => "Checks a UUID token generated by the open-ils.actor.patron.password_reset.request method for " .
3843 "validity, and if valid, uses it as authorization for changing the associated user's password " .
3844 "with the supplied password.",
3846 { desc => 'uuid', type => 'string' },
3847 { desc => 'password', type => 'string' },
3849 return => {desc => '1 on success, Event on error'}
3852 sub commit_password_reset {
3853 my($self, $conn, $uuid, $password) = @_;
3855 # Check to see if password reset requests are already being throttled:
3856 # 0. Check cache to see if we're in throttle mode (avoid hitting database)
3857 $cache ||= OpenSRF::Utils::Cache->new("global", 0);
3858 my $throttle = $cache->get_cache('open-ils.actor.password.throttle') || undef;
3860 return OpenILS::Event->new('PATRON_NOT_AN_ACTIVE_PASSWORD_RESET_REQUEST');
3863 my $e = new_editor(xact => 1);
3865 my $aupr = $e->search_actor_usr_password_reset({
3872 return OpenILS::Event->new('PATRON_NOT_AN_ACTIVE_PASSWORD_RESET_REQUEST');
3874 my $user_id = $aupr->[0]->usr;
3875 my $user = $e->retrieve_actor_user($user_id);
3877 # Ensure we're still within the TTL for the request
3878 my $aupr_ttl = $U->ou_ancestor_setting_value($user->home_ou, 'circ.password_reset_request_time_to_live') || 24*60*60;
3879 my $threshold = DateTime::Format::ISO8601->parse_datetime(clense_ISO8601($aupr->[0]->request_time))->add(seconds => $aupr_ttl);
3880 if ($threshold < DateTime->now(time_zone => 'local')) {
3882 $logger->info("Password reset request needed to be submitted before $threshold");
3883 return OpenILS::Event->new('PATRON_NOT_AN_ACTIVE_PASSWORD_RESET_REQUEST');
3886 # Check complexity of password against OU-defined regex
3887 my $pw_regex = $U->ou_ancestor_setting_value($user->home_ou, 'global.password_regex');
3891 # Calling JSON2perl on the $pw_regex causes failure, even before the fancy Unicode regex
3892 # ($pw_regex = OpenSRF::Utils::JSON->JSON2perl($pw_regex)) =~ s/\\u([0-9a-fA-F]{4})/\\x{$1}/gs;
3893 $is_strong = check_password_strength_custom($password, $pw_regex);
3895 $is_strong = check_password_strength_default($password);
3900 return OpenILS::Event->new('PATRON_PASSWORD_WAS_NOT_STRONG');
3903 # All is well; update the password
3904 $user->passwd($password);
3905 $e->update_actor_user($user);
3907 # And flag that this password reset request has been honoured
3908 $aupr->[0]->has_been_reset('t');
3909 $e->update_actor_usr_password_reset($aupr->[0]);
3915 sub check_password_strength_default {
3916 my $password = shift;
3917 # Use the default set of checks
3918 if ( (length($password) < 7) or
3919 ($password !~ m/.*\d+.*/) or
3920 ($password !~ m/.*[A-Za-z]+.*/)
3927 sub check_password_strength_custom {
3928 my ($password, $pw_regex) = @_;
3930 $pw_regex = qr/$pw_regex/;
3931 if ($password !~ /$pw_regex/) {
3939 __PACKAGE__->register_method(
3940 method => "event_def_opt_in_settings",
3941 api_name => "open-ils.actor.event_def.opt_in.settings",
3944 desc => 'Streams the set of "cust" objects that are used as opt-in settings for event definitions',
3946 { desc => 'Authentication token', type => 'string'},
3948 desc => 'Org Unit ID. (optional). If no org ID is present, the home_ou of the requesting user is used',
3953 desc => q/set of "cust" objects that are used as opt-in settings for event definitions at the specified org unit/,
3960 sub event_def_opt_in_settings {
3961 my($self, $conn, $auth, $org_id) = @_;
3962 my $e = new_editor(authtoken => $auth);
3963 return $e->event unless $e->checkauth;
3965 if(defined $org_id and $org_id != $e->requestor->home_ou) {
3966 return $e->event unless
3967 $e->allowed(['VIEW_USER_SETTING_TYPE', 'ADMIN_USER_SETTING_TYPE'], $org_id);
3969 $org_id = $e->requestor->home_ou;
3972 # find all config.user_setting_type's related to event_defs for the requested org unit
3973 my $types = $e->json_query({
3974 select => {cust => ['name']},
3975 from => {atevdef => 'cust'},
3978 owner => $U->get_org_ancestors($org_id), # context org plus parents
3985 $conn->respond($_) for
3986 @{$e->search_config_usr_setting_type({name => [map {$_->{name}} @$types]})};
3993 __PACKAGE__->register_method(
3994 method => "user_visible_circs",
3995 api_name => "open-ils.actor.history.circ.visible",
3998 desc => 'Returns the set of opt-in visible circulations accompanied by circulation chain summaries',
4000 { desc => 'Authentication token', type => 'string'},
4001 { desc => 'User ID. If no user id is present, the authenticated user is assumed', type => 'number' },
4002 { desc => 'Options hash. Supported fields are "limit" and "offset"', type => 'object' },
4005 desc => q/An object with 2 fields: circulation and summary.
4006 circulation is the "circ" object. summary is the related "accs" object/,
4012 __PACKAGE__->register_method(
4013 method => "user_visible_circs",
4014 api_name => "open-ils.actor.history.circ.visible.print",
4017 desc => 'Returns printable output for the set of opt-in visible circulations',
4019 { desc => 'Authentication token', type => 'string'},
4020 { desc => 'User ID. If no user id is present, the authenticated user is assumed', type => 'number' },
4021 { desc => 'Options hash. Supported fields are "limit" and "offset"', type => 'object' },
4024 desc => q/An action_trigger.event object or error event./,
4030 __PACKAGE__->register_method(
4031 method => "user_visible_circs",
4032 api_name => "open-ils.actor.history.circ.visible.email",
4035 desc => 'Emails the set of opt-in visible circulations to the requestor',
4037 { desc => 'Authentication token', type => 'string'},
4038 { desc => 'User ID. If no user id is present, the authenticated user is assumed', type => 'number' },
4039 { desc => 'Options hash. Supported fields are "limit" and "offset"', type => 'object' },
4042 desc => q/undef, or event on error/
4047 __PACKAGE__->register_method(
4048 method => "user_visible_circs",
4049 api_name => "open-ils.actor.history.hold.visible",
4052 desc => 'Returns the set of opt-in visible holds',
4054 { desc => 'Authentication token', type => 'string'},
4055 { desc => 'User ID. If no user id is present, the authenticated user is assumed', type => 'number' },
4056 { desc => 'Options hash. Supported fields are "limit" and "offset"', type => 'object' },
4059 desc => q/An object with 1 field: "hold"/,
4065 __PACKAGE__->register_method(
4066 method => "user_visible_circs",
4067 api_name => "open-ils.actor.history.hold.visible.print",
4070 desc => 'Returns printable output for the set of opt-in visible holds',
4072 { desc => 'Authentication token', type => 'string'},
4073 { desc => 'User ID. If no user id is present, the authenticated user is assumed', type => 'number' },
4074 { desc => 'Options hash. Supported fields are "limit" and "offset"', type => 'object' },
4077 desc => q/An action_trigger.event object or error event./,
4083 __PACKAGE__->register_method(
4084 method => "user_visible_circs",
4085 api_name => "open-ils.actor.history.hold.visible.email",
4088 desc => 'Emails the set of opt-in visible holds to the requestor',
4090 { desc => 'Authentication token', type => 'string'},
4091 { desc => 'User ID. If no user id is present, the authenticated user is assumed', type => 'number' },
4092 { desc => 'Options hash. Supported fields are "limit" and "offset"', type => 'object' },
4095 desc => q/undef, or event on error/
4100 sub user_visible_circs {
4101 my($self, $conn, $auth, $user_id, $options) = @_;
4103 my $is_hold = ($self->api_name =~ /hold/);
4104 my $for_print = ($self->api_name =~ /print/);
4105 my $for_email = ($self->api_name =~ /email/);
4106 my $e = new_editor(authtoken => $auth);
4107 return $e->event unless $e->checkauth;
4109 $user_id ||= $e->requestor->id;
4111 $options->{limit} ||= 50;
4112 $options->{offset} ||= 0;
4114 if($user_id != $e->requestor->id) {
4115 my $perm = ($is_hold) ? 'VIEW_HOLD' : 'VIEW_CIRCULATIONS';
4116 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
4117 return $e->event unless $e->allowed($perm, $user->home_ou);
4120 my $db_func = ($is_hold) ? 'action.usr_visible_holds' : 'action.usr_visible_circs';
4122 my $data = $e->json_query({
4123 from => [$db_func, $user_id],
4124 limit => $$options{limit},
4125 offset => $$options{offset}
4127 # TODO: I only want IDs. code below didn't get me there
4128 # {"select":{"au":[{"column":"id", "result_field":"id",
4129 # "transform":"action.usr_visible_circs"}]}, "where":{"id":10}, "from":"au"}
4134 return undef unless @$data;
4138 # collect the batch of objects
4142 my $hold_list = $e->search_action_hold_request({id => [map { $_->{id} } @$data]});
4143 return $U->fire_object_event(undef, 'ahr.format.history.print', $hold_list, $$hold_list[0]->request_lib);
4147 my $circ_list = $e->search_action_circulation({id => [map { $_->{id} } @$data]});
4148 return $U->fire_object_event(undef, 'circ.format.history.print', $circ_list, $$circ_list[0]->circ_lib);
4151 } elsif ($for_email) {
4153 $conn->respond_complete(1) if $for_email; # no sense in waiting
4161 my $hold = $e->retrieve_action_hold_request($id);
4162 $U->create_events_for_hook('ahr.format.history.email', $hold, $hold->request_lib, undef, undef, 1);
4163 # events will be fired from action_trigger_runner
4167 my $circ = $e->retrieve_action_circulation($id);
4168 $U->create_events_for_hook('circ.format.history.email', $circ, $circ->circ_lib, undef, undef, 1);
4169 # events will be fired from action_trigger_runner
4173 } else { # just give me the data please
4181 my $hold = $e->retrieve_action_hold_request($id);
4182 $conn->respond({hold => $hold});
4186 my $circ = $e->retrieve_action_circulation($id);
4189 summary => $U->create_circ_chain_summary($e, $id)
4198 __PACKAGE__->register_method(
4199 method => "user_saved_search_cud",
4200 api_name => "open-ils.actor.user.saved_search.cud",
4203 desc => 'Create/Update/Delete Access to user saved searches',
4205 { desc => 'Authentication token', type => 'string' },
4206 { desc => 'Saved Search Object', type => 'object', class => 'auss' }
4209 desc => q/The retrieved or updated saved search object, or id of a deleted object; Event on error/,
4215 __PACKAGE__->register_method(
4216 method => "user_saved_search_cud",
4217 api_name => "open-ils.actor.user.saved_search.retrieve",
4220 desc => 'Retrieve a saved search object',
4222 { desc => 'Authentication token', type => 'string' },
4223 { desc => 'Saved Search ID', type => 'number' }
4226 desc => q/The saved search object, Event on error/,
4232 sub user_saved_search_cud {
4233 my( $self, $client, $auth, $search ) = @_;
4234 my $e = new_editor( authtoken=>$auth );
4235 return $e->die_event unless $e->checkauth;
4237 my $o_search; # prior version of the object, if any
4238 my $res; # to be returned
4240 # branch on the operation type
4242 if( $self->api_name =~ /retrieve/ ) { # Retrieve
4244 # Get the old version, to check ownership
4245 $o_search = $e->retrieve_actor_usr_saved_search( $search )
4246 or return $e->die_event;
4248 # You can't read somebody else's search
4249 return OpenILS::Event->new('BAD_PARAMS')
4250 unless $o_search->owner == $e->requestor->id;
4256 $e->xact_begin; # start an editor transaction
4258 if( $search->isnew ) { # Create
4260 # You can't create a search for somebody else
4261 return OpenILS::Event->new('BAD_PARAMS')
4262 unless $search->owner == $e->requestor->id;
4264 $e->create_actor_usr_saved_search( $search )
4265 or return $e->die_event;
4269 } elsif( $search->ischanged ) { # Update
4271 # You can't change ownership of a search
4272 return OpenILS::Event->new('BAD_PARAMS')
4273 unless $search->owner == $e->requestor->id;
4275 # Get the old version, to check ownership
4276 $o_search = $e->retrieve_actor_usr_saved_search( $search->id )
4277 or return $e->die_event;
4279 # You can't update somebody else's search
4280 return OpenILS::Event->new('BAD_PARAMS')
4281 unless $o_search->owner == $e->requestor->id;
4284 $e->update_actor_usr_saved_search( $search )
4285 or return $e->die_event;
4289 } elsif( $search->isdeleted ) { # Delete
4291 # Get the old version, to check ownership
4292 $o_search = $e->retrieve_actor_usr_saved_search( $search->id )
4293 or return $e->die_event;
4295 # You can't delete somebody else's search
4296 return OpenILS::Event->new('BAD_PARAMS')
4297 unless $o_search->owner == $e->requestor->id;
4300 $e->delete_actor_usr_saved_search( $o_search )
4301 or return $e->die_event;
4312 __PACKAGE__->register_method(
4313 method => "get_barcodes",
4314 api_name => "open-ils.actor.get_barcodes"
4318 my( $self, $client, $auth, $org_id, $context, $barcode ) = @_;
4319 my $e = new_editor(authtoken => $auth);
4320 return $e->event unless $e->checkauth;
4321 return $e->event unless $e->allowed('STAFF_LOGIN', $org_id);
4323 my $db_result = $e->json_query(
4325 'evergreen.get_barcodes',
4326 $org_id, $context, $barcode,
4330 if($context =~ /actor/) {
4331 my $filter_result = ();
4333 foreach my $result (@$db_result) {
4334 if($result->{type} eq 'actor') {
4335 if($e->requestor->id != $result->{id}) {
4336 $patron = $e->retrieve_actor_user($result->{id});
4338 push(@$filter_result, $e->event);
4341 if($e->allowed('VIEW_USER', $patron->home_ou)) {
4342 push(@$filter_result, $result);
4345 push(@$filter_result, $e->event);
4349 push(@$filter_result, $result);
4353 push(@$filter_result, $result);
4356 return $filter_result;