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_depth ) = @_;
1227 my $e = new_editor(authtoken=>$auth);
1228 return $e->event unless $e->checkauth;
1229 return $e->event unless $e->allowed('VIEW_USER');
1230 return $U->storagereq(
1231 "open-ils.storage.actor.user.crazy_search", $search_hash,
1232 $search_limit, $search_sort, $include_inactive, $e->requestor->ws_ou, $search_depth);
1236 __PACKAGE__->register_method(
1237 method => "update_passwd",
1238 api_name => "open-ils.actor.user.password.update",
1240 desc => "Update the operator's password",
1242 { desc => 'Authentication token', type => 'string' },
1243 { desc => 'New password', type => 'string' },
1244 { desc => 'Current password', type => 'string' }
1246 return => {desc => '1 on success, Event on error or incorrect current password'}
1250 __PACKAGE__->register_method(
1251 method => "update_passwd",
1252 api_name => "open-ils.actor.user.username.update",
1254 desc => "Update the operator's username",
1256 { desc => 'Authentication token', type => 'string' },
1257 { desc => 'New username', type => 'string' }
1259 return => {desc => '1 on success, Event on error'}
1263 __PACKAGE__->register_method(
1264 method => "update_passwd",
1265 api_name => "open-ils.actor.user.email.update",
1267 desc => "Update the operator's email address",
1269 { desc => 'Authentication token', type => 'string' },
1270 { desc => 'New email address', type => 'string' }
1272 return => {desc => '1 on success, Event on error'}
1277 my( $self, $conn, $auth, $new_val, $orig_pw ) = @_;
1278 my $e = new_editor(xact=>1, authtoken=>$auth);
1279 return $e->die_event unless $e->checkauth;
1281 my $db_user = $e->retrieve_actor_user($e->requestor->id)
1282 or return $e->die_event;
1283 my $api = $self->api_name;
1285 if( $api =~ /password/o ) {
1286 # make sure the original password matches the in-database password
1287 if (md5_hex($orig_pw) ne $db_user->passwd) {
1289 return new OpenILS::Event('INCORRECT_PASSWORD');
1291 $db_user->passwd($new_val);
1295 # if we don't clear the password, the user will be updated with
1296 # a hashed version of the hashed version of their password
1297 $db_user->clear_passwd;
1299 if( $api =~ /username/o ) {
1301 # make sure no one else has this username
1302 my $exist = $e->search_actor_user({usrname=>$new_val},{idlist=>1});
1305 return new OpenILS::Event('USERNAME_EXISTS');
1307 $db_user->usrname($new_val);
1309 } elsif( $api =~ /email/o ) {
1310 $db_user->email($new_val);
1314 $e->update_actor_user($db_user) or return $e->die_event;
1317 # update the cached user to pick up these changes
1318 $U->simplereq('open-ils.auth', 'open-ils.auth.session.reset_timeout', $auth, 1);
1324 __PACKAGE__->register_method(
1325 method => "check_user_perms",
1326 api_name => "open-ils.actor.user.perm.check",
1327 notes => <<" NOTES");
1328 Takes a login session, user id, an org id, and an array of perm type strings. For each
1329 perm type, if the user does *not* have the given permission it is added
1330 to a list which is returned from the method. If all permissions
1331 are allowed, an empty list is returned
1332 if the logged in user does not match 'user_id', then the logged in user must
1333 have VIEW_PERMISSION priveleges.
1336 sub check_user_perms {
1337 my( $self, $client, $login_session, $user_id, $org_id, $perm_types ) = @_;
1339 my( $staff, $evt ) = $apputils->checkses($login_session);
1340 return $evt if $evt;
1342 if($staff->id ne $user_id) {
1343 if( $evt = $apputils->check_perms(
1344 $staff->id, $org_id, 'VIEW_PERMISSION') ) {
1350 for my $perm (@$perm_types) {
1351 if($apputils->check_perms($user_id, $org_id, $perm)) {
1352 push @not_allowed, $perm;
1356 return \@not_allowed
1359 __PACKAGE__->register_method(
1360 method => "check_user_perms2",
1361 api_name => "open-ils.actor.user.perm.check.multi_org",
1363 Checks the permissions on a list of perms and orgs for a user
1364 @param authtoken The login session key
1365 @param user_id The id of the user to check
1366 @param orgs The array of org ids
1367 @param perms The array of permission names
1368 @return An array of [ orgId, permissionName ] arrays that FAILED the check
1369 if the logged in user does not match 'user_id', then the logged in user must
1370 have VIEW_PERMISSION priveleges.
1373 sub check_user_perms2 {
1374 my( $self, $client, $authtoken, $user_id, $orgs, $perms ) = @_;
1376 my( $staff, $target, $evt ) = $apputils->checkses_requestor(
1377 $authtoken, $user_id, 'VIEW_PERMISSION' );
1378 return $evt if $evt;
1381 for my $org (@$orgs) {
1382 for my $perm (@$perms) {
1383 if($apputils->check_perms($user_id, $org, $perm)) {
1384 push @not_allowed, [ $org, $perm ];
1389 return \@not_allowed
1393 __PACKAGE__->register_method(
1394 method => 'check_user_perms3',
1395 api_name => 'open-ils.actor.user.perm.highest_org',
1397 Returns the highest org unit id at which a user has a given permission
1398 If the requestor does not match the target user, the requestor must have
1399 'VIEW_PERMISSION' rights at the home org unit of the target user
1400 @param authtoken The login session key
1401 @param userid The id of the user in question
1402 @param perm The permission to check
1403 @return The org unit highest in the org tree within which the user has
1404 the requested permission
1407 sub check_user_perms3 {
1408 my($self, $client, $authtoken, $user_id, $perm) = @_;
1409 my $e = new_editor(authtoken=>$authtoken);
1410 return $e->event unless $e->checkauth;
1412 my $tree = $U->get_org_tree();
1414 unless($e->requestor->id == $user_id) {
1415 my $user = $e->retrieve_actor_user($user_id)
1416 or return $e->event;
1417 return $e->event unless $e->allowed('VIEW_PERMISSION', $user->home_ou);
1418 return $U->find_highest_perm_org($perm, $user_id, $user->home_ou, $tree );
1421 return $U->find_highest_perm_org($perm, $user_id, $e->requestor->ws_ou, $tree);
1424 __PACKAGE__->register_method(
1425 method => 'user_has_work_perm_at',
1426 api_name => 'open-ils.actor.user.has_work_perm_at',
1430 Returns a set of org unit IDs which represent the highest orgs in
1431 the org tree where the user has the requested permission. The
1432 purpose of this method is to return the smallest set of org units
1433 which represent the full expanse of the user's ability to perform
1434 the requested action. The user whose perms this method should
1435 check is implied by the authtoken. /,
1437 {desc => 'authtoken', type => 'string'},
1438 {desc => 'permission name', type => 'string'},
1439 {desc => q/user id, optional. If present, check perms for
1440 this user instead of the logged in user/, type => 'number'},
1442 return => {desc => 'An array of org IDs'}
1446 sub user_has_work_perm_at {
1447 my($self, $conn, $auth, $perm, $user_id) = @_;
1448 my $e = new_editor(authtoken=>$auth);
1449 return $e->event unless $e->checkauth;
1450 if(defined $user_id) {
1451 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
1452 return $e->event unless $e->allowed('VIEW_PERMISSION', $user->home_ou);
1454 return $U->user_has_work_perm_at($e, $perm, undef, $user_id);
1457 __PACKAGE__->register_method(
1458 method => 'user_has_work_perm_at_batch',
1459 api_name => 'open-ils.actor.user.has_work_perm_at.batch',
1463 sub user_has_work_perm_at_batch {
1464 my($self, $conn, $auth, $perms, $user_id) = @_;
1465 my $e = new_editor(authtoken=>$auth);
1466 return $e->event unless $e->checkauth;
1467 if(defined $user_id) {
1468 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
1469 return $e->event unless $e->allowed('VIEW_PERMISSION', $user->home_ou);
1472 $map->{$_} = $U->user_has_work_perm_at($e, $_) for @$perms;
1478 __PACKAGE__->register_method(
1479 method => 'check_user_perms4',
1480 api_name => 'open-ils.actor.user.perm.highest_org.batch',
1482 Returns the highest org unit id at which a user has a given permission
1483 If the requestor does not match the target user, the requestor must have
1484 'VIEW_PERMISSION' rights at the home org unit of the target user
1485 @param authtoken The login session key
1486 @param userid The id of the user in question
1487 @param perms An array of perm names to check
1488 @return An array of orgId's representing the org unit
1489 highest in the org tree within which the user has the requested permission
1490 The arrah of orgId's has matches the order of the perms array
1493 sub check_user_perms4 {
1494 my( $self, $client, $authtoken, $userid, $perms ) = @_;
1496 my( $staff, $target, $org, $evt );
1498 ( $staff, $target, $evt ) = $apputils->checkses_requestor(
1499 $authtoken, $userid, 'VIEW_PERMISSION' );
1500 return $evt if $evt;
1503 return [] unless ref($perms);
1504 my $tree = $U->get_org_tree();
1506 for my $p (@$perms) {
1507 push( @arr, $U->find_highest_perm_org( $p, $userid, $target->home_ou, $tree ) );
1513 __PACKAGE__->register_method(
1514 method => "user_fines_summary",
1515 api_name => "open-ils.actor.user.fines.summary",
1518 desc => 'Returns a short summary of the users total open fines, ' .
1519 'excluding voided fines Params are login_session, user_id' ,
1521 {desc => 'Authentication token', type => 'string'},
1522 {desc => 'User ID', type => 'string'} # number?
1525 desc => "a 'mous' object, event on error",
1530 sub user_fines_summary {
1531 my( $self, $client, $auth, $user_id ) = @_;
1533 my $e = new_editor(authtoken=>$auth);
1534 return $e->event unless $e->checkauth;
1536 if( $user_id ne $e->requestor->id ) {
1537 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
1538 return $e->event unless
1539 $e->allowed('VIEW_USER_FINES_SUMMARY', $user->home_ou);
1542 return $e->search_money_open_user_summary({usr => $user_id})->[0];
1546 __PACKAGE__->register_method(
1547 method => "user_opac_vitals",
1548 api_name => "open-ils.actor.user.opac.vital_stats",
1552 desc => 'Returns a short summary of the users vital stats, including ' .
1553 'identification information, accumulated balance, number of holds, ' .
1554 'and current open circulation stats' ,
1556 {desc => 'Authentication token', type => 'string'},
1557 {desc => 'Optional User ID, for use in the staff client', type => 'number'} # number?
1560 desc => "An object with four properties: user, fines, checkouts and holds."
1565 sub user_opac_vitals {
1566 my( $self, $client, $auth, $user_id ) = @_;
1568 my $e = new_editor(authtoken=>$auth);
1569 return $e->event unless $e->checkauth;
1571 $user_id ||= $e->requestor->id;
1573 my $user = $e->retrieve_actor_user( $user_id );
1576 ->method_lookup('open-ils.actor.user.fines.summary')
1577 ->run($auth => $user_id);
1578 return $fines if (defined($U->event_code($fines)));
1581 $fines = new Fieldmapper::money::open_user_summary ();
1582 $fines->balance_owed(0.00);
1583 $fines->total_owed(0.00);
1584 $fines->total_paid(0.00);
1585 $fines->usr($user_id);
1589 ->method_lookup('open-ils.actor.user.hold_requests.count')
1590 ->run($auth => $user_id);
1591 return $holds if (defined($U->event_code($holds)));
1594 ->method_lookup('open-ils.actor.user.checked_out.count')
1595 ->run($auth => $user_id);
1596 return $out if (defined($U->event_code($out)));
1598 $out->{"total_out"} = reduce { $a + $out->{$b} } 0, qw/out overdue long_overdue/;
1602 first_given_name => $user->first_given_name,
1603 second_given_name => $user->second_given_name,
1604 family_name => $user->family_name,
1605 alias => $user->alias,
1606 usrname => $user->usrname
1608 fines => $fines->to_bare_hash,
1615 ##### a small consolidation of related method registrations
1616 my $common_params = [
1617 { desc => 'Authentication token', type => 'string' },
1618 { desc => 'User ID', type => 'string' },
1619 { desc => 'Transactions type (optional, defaults to all)', type => 'string' },
1620 { desc => 'Options hash. May contain limit and offset for paged results.', type => 'object' },
1623 'open-ils.actor.user.transactions' => '',
1624 'open-ils.actor.user.transactions.fleshed' => '',
1625 'open-ils.actor.user.transactions.have_charge' => ' that have an initial charge',
1626 'open-ils.actor.user.transactions.have_charge.fleshed' => ' that have an initial charge',
1627 'open-ils.actor.user.transactions.have_balance' => ' that have an outstanding balance',
1628 'open-ils.actor.user.transactions.have_balance.fleshed' => ' that have an outstanding balance',
1631 foreach (keys %methods) {
1633 method => "user_transactions",
1636 desc => 'For a given user, retrieve a list of '
1637 . (/\.fleshed/ ? 'fleshed ' : '')
1638 . 'transactions' . $methods{$_}
1639 . ' optionally limited to transactions of a given type.',
1640 params => $common_params,
1642 desc => "List of objects, or event on error. Each object is a hash containing: transaction, circ, record. "
1643 . 'These represent the relevant (mbts) transaction, attached circulation and title pointed to in the circ, respectively.',
1647 $args{authoritative} = 1;
1648 __PACKAGE__->register_method(%args);
1651 # Now for the counts
1653 'open-ils.actor.user.transactions.count' => '',
1654 'open-ils.actor.user.transactions.have_charge.count' => ' that have an initial charge',
1655 'open-ils.actor.user.transactions.have_balance.count' => ' that have an outstanding balance',
1658 foreach (keys %methods) {
1660 method => "user_transactions",
1663 desc => 'For a given user, retrieve a count of open '
1664 . 'transactions' . $methods{$_}
1665 . ' optionally limited to transactions of a given type.',
1666 params => $common_params,
1667 return => { desc => "Integer count of transactions, or event on error" }
1670 /\.have_balance/ and $args{authoritative} = 1; # FIXME: I don't know why have_charge isn't authoritative
1671 __PACKAGE__->register_method(%args);
1674 __PACKAGE__->register_method(
1675 method => "user_transactions",
1676 api_name => "open-ils.actor.user.transactions.have_balance.total",
1679 desc => 'For a given user, retrieve the total balance owed for open transactions,'
1680 . ' optionally limited to transactions of a given type.',
1681 params => $common_params,
1682 return => { desc => "Decimal balance value, or event on error" }
1687 sub user_transactions {
1688 my( $self, $client, $auth, $user_id, $type, $options ) = @_;
1691 my $e = new_editor(authtoken => $auth);
1692 return $e->event unless $e->checkauth;
1694 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
1696 return $e->event unless
1697 $e->requestor->id == $user_id or
1698 $e->allowed('VIEW_USER_TRANSACTIONS', $user->home_ou);
1700 my $api = $self->api_name();
1702 my $filter = ($api =~ /have_balance/o) ?
1703 { 'balance_owed' => { '<>' => 0 } }:
1704 { 'total_owed' => { '>' => 0 } };
1706 my $method = 'open-ils.actor.user.transactions.history.still_open';
1707 $method = "$method.authoritative" if $api =~ /authoritative/;
1708 my ($trans) = $self->method_lookup($method)->run($auth, $user_id, $type, $filter, $options);
1710 if($api =~ /total/o) {
1712 $total += $_->balance_owed for @$trans;
1716 ($api =~ /count/o ) and return scalar @$trans;
1717 ($api !~ /fleshed/o) and return $trans;
1720 for my $t (@$trans) {
1722 if( $t->xact_type ne 'circulation' ) {
1723 push @resp, {transaction => $t};
1727 my $circ_data = flesh_circ($e, $t->id);
1728 push @resp, {transaction => $t, %$circ_data};
1735 __PACKAGE__->register_method(
1736 method => "user_transaction_retrieve",
1737 api_name => "open-ils.actor.user.transaction.fleshed.retrieve",
1740 notes => "Returns a fleshed transaction record"
1743 __PACKAGE__->register_method(
1744 method => "user_transaction_retrieve",
1745 api_name => "open-ils.actor.user.transaction.retrieve",
1748 notes => "Returns a transaction record"
1751 sub user_transaction_retrieve {
1752 my($self, $client, $auth, $bill_id) = @_;
1754 my $e = new_editor(authtoken => $auth);
1755 return $e->event unless $e->checkauth;
1757 my $trans = $e->retrieve_money_billable_transaction_summary(
1758 [$bill_id, {flesh => 1, flesh_fields => {mbts => ['usr']}}]) or return $e->event;
1760 return $e->event unless $e->allowed('VIEW_USER_TRANSACTIONS', $trans->usr->home_ou);
1762 $trans->usr($trans->usr->id); # de-flesh for backwards compat
1764 return $trans unless $self->api_name =~ /flesh/;
1765 return {transaction => $trans} if $trans->xact_type ne 'circulation';
1767 my $circ_data = flesh_circ($e, $trans->id, 1);
1769 return {transaction => $trans, %$circ_data};
1774 my $circ_id = shift;
1775 my $flesh_copy = shift;
1777 my $circ = $e->retrieve_action_circulation([
1781 circ => ['target_copy'],
1782 acp => ['call_number'],
1789 my $copy = $circ->target_copy;
1791 if($circ->target_copy->call_number->id == OILS_PRECAT_CALL_NUMBER) {
1792 $mods = new Fieldmapper::metabib::virtual_record;
1793 $mods->doc_id(OILS_PRECAT_RECORD);
1794 $mods->title($copy->dummy_title);
1795 $mods->author($copy->dummy_author);
1798 $mods = $U->record_to_mvr($circ->target_copy->call_number->record);
1802 $circ->target_copy($circ->target_copy->id);
1803 $copy->call_number($copy->call_number->id);
1805 return {circ => $circ, record => $mods, copy => ($flesh_copy) ? $copy : undef };
1809 __PACKAGE__->register_method(
1810 method => "hold_request_count",
1811 api_name => "open-ils.actor.user.hold_requests.count",
1814 notes => 'Returns hold ready/total counts'
1817 sub hold_request_count {
1818 my( $self, $client, $authtoken, $user_id ) = @_;
1819 my $e = new_editor(authtoken => $authtoken);
1820 return $e->event unless $e->checkauth;
1822 $user_id = $e->requestor->id unless defined $user_id;
1824 if($e->requestor->id ne $user_id) {
1825 my $user = $e->retrieve_actor_user($user_id);
1826 return $e->event unless $e->allowed('VIEW_HOLD', $user->home_ou);
1829 my $holds = $e->json_query({
1830 select => {ahr => ['shelf_time']},
1834 fulfillment_time => {"=" => undef },
1835 cancel_time => undef,
1840 total => scalar(@$holds),
1841 ready => scalar(grep { $_->{shelf_time} } @$holds)
1845 __PACKAGE__->register_method(
1846 method => "checked_out",
1847 api_name => "open-ils.actor.user.checked_out",
1851 desc => "For a given user, returns a structure of circulations objects sorted by out, overdue, lost, claims_returned, long_overdue. "
1852 . "A list of IDs are returned of each type. Circs marked lost, long_overdue, and claims_returned will not be 'finished' "
1853 . "(i.e., outstanding balance or some other pending action on the circ). "
1854 . "The .count method also includes a 'total' field which sums all open circs.",
1856 { desc => 'Authentication Token', type => 'string'},
1857 { desc => 'User ID', type => 'string'},
1860 desc => 'Returns event on error, or an object with ID lists, like: '
1861 . '{"out":[12552,451232], "claims_returned":[], "long_overdue":[23421] "overdue":[], "lost":[]}'
1866 __PACKAGE__->register_method(
1867 method => "checked_out",
1868 api_name => "open-ils.actor.user.checked_out.count",
1871 signature => q/@see open-ils.actor.user.checked_out/
1875 my( $self, $conn, $auth, $userid ) = @_;
1877 my $e = new_editor(authtoken=>$auth);
1878 return $e->event unless $e->checkauth;
1880 if( $userid ne $e->requestor->id ) {
1881 my $user = $e->retrieve_actor_user($userid) or return $e->event;
1882 unless($e->allowed('VIEW_CIRCULATIONS', $user->home_ou)) {
1884 # see if there is a friend link allowing circ.view perms
1885 my $allowed = OpenILS::Application::Actor::Friends->friend_perm_allowed(
1886 $e, $userid, $e->requestor->id, 'circ.view');
1887 return $e->event unless $allowed;
1891 my $count = $self->api_name =~ /count/;
1892 return _checked_out( $count, $e, $userid );
1896 my( $iscount, $e, $userid ) = @_;
1902 claims_returned => [],
1905 my $meth = 'retrieve_action_open_circ_';
1913 claims_returned => 0,
1920 my $data = $e->$meth($userid);
1924 $result{$_} += $data->$_() for (keys %result);
1925 $result{total} += $data->$_() for (keys %result);
1927 for my $k (keys %result) {
1928 $result{$k} = [ grep { $_ > 0 } split( ',', $data->$k()) ];
1938 __PACKAGE__->register_method(
1939 method => "checked_in_with_fines",
1940 api_name => "open-ils.actor.user.checked_in_with_fines",
1943 signature => q/@see open-ils.actor.user.checked_out/
1946 sub checked_in_with_fines {
1947 my( $self, $conn, $auth, $userid ) = @_;
1949 my $e = new_editor(authtoken=>$auth);
1950 return $e->event unless $e->checkauth;
1952 if( $userid ne $e->requestor->id ) {
1953 return $e->event unless $e->allowed('VIEW_CIRCULATIONS');
1956 # money is owed on these items and they are checked in
1957 my $open = $e->search_action_circulation(
1960 xact_finish => undef,
1961 checkin_time => { "!=" => undef },
1966 my( @lost, @cr, @lo );
1967 for my $c (@$open) {
1968 push( @lost, $c->id ) if $c->stop_fines eq 'LOST';
1969 push( @cr, $c->id ) if $c->stop_fines eq 'CLAIMSRETURNED';
1970 push( @lo, $c->id ) if $c->stop_fines eq 'LONGOVERDUE';
1975 claims_returned => \@cr,
1976 long_overdue => \@lo
1982 my ($api, $desc, $auth) = @_;
1983 $desc = $desc ? (" " . $desc) : '';
1984 my $ids = ($api =~ /ids$/) ? 1 : 0;
1987 method => "user_transaction_history",
1988 api_name => "open-ils.actor.user.transactions.$api",
1990 desc => "For a given User ID, returns a list of billable transaction" .
1991 ($ids ? " id" : '') .
1992 "s$desc, optionally filtered by type and/or fields in money.billable_xact_summary. " .
1993 "The VIEW_USER_TRANSACTIONS permission is required to view another user's transactions",
1995 {desc => 'Authentication token', type => 'string'},
1996 {desc => 'User ID', type => 'number'},
1997 {desc => 'Transaction type (optional)', type => 'number'},
1998 {desc => 'Hash of Billable Transaction Summary filters (optional)', type => 'object'}
2001 desc => 'List of transaction' . ($ids ? " id" : '') . 's, Event on error'
2005 $auth and push @sig, (authoritative => 1);
2009 my %auth_hist_methods = (
2011 'history.have_charge' => 'that have an initial charge',
2012 'history.still_open' => 'that are not finished',
2013 'history.have_balance' => 'that have a balance',
2014 'history.have_bill' => 'that have billings',
2015 'history.have_bill_or_payment' => 'that have non-zero-sum billings or at least 1 payment',
2016 'history.have_payment' => 'that have at least 1 payment',
2019 foreach (keys %auth_hist_methods) {
2020 __PACKAGE__->register_method(_sigmaker($_, $auth_hist_methods{$_}, 1));
2021 __PACKAGE__->register_method(_sigmaker("$_.ids", $auth_hist_methods{$_}, 1));
2022 __PACKAGE__->register_method(_sigmaker("$_.fleshed", $auth_hist_methods{$_}, 1));
2025 sub user_transaction_history {
2026 my( $self, $conn, $auth, $userid, $type, $filter, $options ) = @_;
2030 my $e = new_editor(authtoken=>$auth);
2031 return $e->die_event unless $e->checkauth;
2033 if ($e->requestor->id ne $userid) {
2034 return $e->die_event unless $e->allowed('VIEW_USER_TRANSACTIONS');
2037 my $api = $self->api_name;
2038 my @xact_finish = (xact_finish => undef ) if ($api =~ /history\.still_open$/); # What about history.still_open.ids?
2040 if(defined($type)) {
2041 $filter->{'xact_type'} = $type;
2044 if($api =~ /have_bill_or_payment/o) {
2046 # transactions that have a non-zero sum across all billings or at least 1 payment
2047 $filter->{'-or'} = {
2048 'balance_owed' => { '<>' => 0 },
2049 'last_payment_ts' => { '<>' => undef }
2052 } elsif($api =~ /have_payment/) {
2054 $filter->{last_payment_ts} ||= {'<>' => undef};
2056 } elsif( $api =~ /have_balance/o) {
2058 # transactions that have a non-zero overall balance
2059 $filter->{'balance_owed'} = { '<>' => 0 };
2061 } elsif( $api =~ /have_charge/o) {
2063 # transactions that have at least 1 billing, regardless of whether it was voided
2064 $filter->{'last_billing_ts'} = { '<>' => undef };
2066 } elsif( $api =~ /have_bill/o) { # needs to be an elsif, or we double-match have_bill_or_payment!
2068 # transactions that have non-zero sum across all billings. This will exclude
2069 # xacts where all billings have been voided
2070 $filter->{'total_owed'} = { '<>' => 0 };
2073 my $options_clause = { order_by => { mbt => 'xact_start DESC' } };
2074 $options_clause->{'limit'} = $options->{'limit'} if $options->{'limit'};
2075 $options_clause->{'offset'} = $options->{'offset'} if $options->{'offset'};
2077 my $mbts = $e->search_money_billable_transaction_summary(
2078 [ { usr => $userid, @xact_finish, %$filter },
2083 return [map {$_->id} @$mbts] if $api =~ /\.ids/;
2084 return $mbts unless $api =~ /fleshed/;
2087 for my $t (@$mbts) {
2089 if( $t->xact_type ne 'circulation' ) {
2090 push @resp, {transaction => $t};
2094 my $circ_data = flesh_circ($e, $t->id);
2095 push @resp, {transaction => $t, %$circ_data};
2103 __PACKAGE__->register_method(
2104 method => "user_perms",
2105 api_name => "open-ils.actor.permissions.user_perms.retrieve",
2107 notes => "Returns a list of permissions"
2111 my( $self, $client, $authtoken, $user ) = @_;
2113 my( $staff, $evt ) = $apputils->checkses($authtoken);
2114 return $evt if $evt;
2116 $user ||= $staff->id;
2118 if( $user != $staff->id and $evt = $apputils->check_perms( $staff->id, $staff->home_ou, 'VIEW_PERMISSION') ) {
2122 return $apputils->simple_scalar_request(
2124 "open-ils.storage.permission.user_perms.atomic",
2128 __PACKAGE__->register_method(
2129 method => "retrieve_perms",
2130 api_name => "open-ils.actor.permissions.retrieve",
2131 notes => "Returns a list of permissions"
2133 sub retrieve_perms {
2134 my( $self, $client ) = @_;
2135 return $apputils->simple_scalar_request(
2137 "open-ils.cstore.direct.permission.perm_list.search.atomic",
2138 { id => { '!=' => undef } }
2142 __PACKAGE__->register_method(
2143 method => "retrieve_groups",
2144 api_name => "open-ils.actor.groups.retrieve",
2145 notes => "Returns a list of user groups"
2147 sub retrieve_groups {
2148 my( $self, $client ) = @_;
2149 return new_editor()->retrieve_all_permission_grp_tree();
2152 __PACKAGE__->register_method(
2153 method => "retrieve_org_address",
2154 api_name => "open-ils.actor.org_unit.address.retrieve",
2155 notes => <<' NOTES');
2156 Returns an org_unit address by ID
2157 @param An org_address ID
2159 sub retrieve_org_address {
2160 my( $self, $client, $id ) = @_;
2161 return $apputils->simple_scalar_request(
2163 "open-ils.cstore.direct.actor.org_address.retrieve",
2168 __PACKAGE__->register_method(
2169 method => "retrieve_groups_tree",
2170 api_name => "open-ils.actor.groups.tree.retrieve",
2171 notes => "Returns a list of user groups"
2174 sub retrieve_groups_tree {
2175 my( $self, $client ) = @_;
2176 return new_editor()->search_permission_grp_tree(
2181 flesh_fields => { pgt => ["children"] },
2182 order_by => { pgt => 'name'}
2189 __PACKAGE__->register_method(
2190 method => "add_user_to_groups",
2191 api_name => "open-ils.actor.user.set_groups",
2192 notes => "Adds a user to one or more permission groups"
2195 sub add_user_to_groups {
2196 my( $self, $client, $authtoken, $userid, $groups ) = @_;
2198 my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2199 $authtoken, $userid, 'CREATE_USER_GROUP_LINK' );
2200 return $evt if $evt;
2202 ( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2203 $authtoken, $userid, 'REMOVE_USER_GROUP_LINK' );
2204 return $evt if $evt;
2206 $apputils->simplereq(
2208 'open-ils.storage.direct.permission.usr_grp_map.mass_delete', { usr => $userid } );
2210 for my $group (@$groups) {
2211 my $link = Fieldmapper::permission::usr_grp_map->new;
2213 $link->usr($userid);
2215 my $id = $apputils->simplereq(
2217 'open-ils.storage.direct.permission.usr_grp_map.create', $link );
2223 __PACKAGE__->register_method(
2224 method => "get_user_perm_groups",
2225 api_name => "open-ils.actor.user.get_groups",
2226 notes => "Retrieve a user's permission groups."
2230 sub get_user_perm_groups {
2231 my( $self, $client, $authtoken, $userid ) = @_;
2233 my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2234 $authtoken, $userid, 'VIEW_PERM_GROUPS' );
2235 return $evt if $evt;
2237 return $apputils->simplereq(
2239 'open-ils.cstore.direct.permission.usr_grp_map.search.atomic', { usr => $userid } );
2243 __PACKAGE__->register_method(
2244 method => "get_user_work_ous",
2245 api_name => "open-ils.actor.user.get_work_ous",
2246 notes => "Retrieve a user's work org units."
2249 __PACKAGE__->register_method(
2250 method => "get_user_work_ous",
2251 api_name => "open-ils.actor.user.get_work_ous.ids",
2252 notes => "Retrieve a user's work org units."
2255 sub get_user_work_ous {
2256 my( $self, $client, $auth, $userid ) = @_;
2257 my $e = new_editor(authtoken=>$auth);
2258 return $e->event unless $e->checkauth;
2259 $userid ||= $e->requestor->id;
2261 if($e->requestor->id != $userid) {
2262 my $user = $e->retrieve_actor_user($userid)
2263 or return $e->event;
2264 return $e->event unless $e->allowed('ASSIGN_WORK_ORG_UNIT', $user->home_ou);
2267 return $e->search_permission_usr_work_ou_map({usr => $userid})
2268 unless $self->api_name =~ /.ids$/;
2270 # client just wants a list of org IDs
2271 return $U->get_user_work_ou_ids($e, $userid);
2276 __PACKAGE__->register_method(
2277 method => 'register_workstation',
2278 api_name => 'open-ils.actor.workstation.register.override',
2279 signature => q/@see open-ils.actor.workstation.register/
2282 __PACKAGE__->register_method(
2283 method => 'register_workstation',
2284 api_name => 'open-ils.actor.workstation.register',
2286 Registers a new workstion in the system
2287 @param authtoken The login session key
2288 @param name The name of the workstation id
2289 @param owner The org unit that owns this workstation
2290 @return The workstation id on success, WORKSTATION_NAME_EXISTS
2291 if the name is already in use.
2295 sub register_workstation {
2296 my( $self, $conn, $authtoken, $name, $owner ) = @_;
2298 my $e = new_editor(authtoken=>$authtoken, xact=>1);
2299 return $e->die_event unless $e->checkauth;
2300 return $e->die_event unless $e->allowed('REGISTER_WORKSTATION', $owner);
2301 my $existing = $e->search_actor_workstation({name => $name})->[0];
2305 if( $self->api_name =~ /override/o ) {
2306 # workstation with the given name exists.
2308 if($owner ne $existing->owning_lib) {
2309 # if necessary, update the owning_lib of the workstation
2311 $logger->info("changing owning lib of workstation ".$existing->id.
2312 " from ".$existing->owning_lib." to $owner");
2313 return $e->die_event unless
2314 $e->allowed('UPDATE_WORKSTATION', $existing->owning_lib);
2316 return $e->die_event unless $e->allowed('UPDATE_WORKSTATION', $owner);
2318 $existing->owning_lib($owner);
2319 return $e->die_event unless $e->update_actor_workstation($existing);
2325 "attempt to register an existing workstation. returning existing ID");
2328 return $existing->id;
2331 return OpenILS::Event->new('WORKSTATION_NAME_EXISTS')
2335 my $ws = Fieldmapper::actor::workstation->new;
2336 $ws->owning_lib($owner);
2338 $e->create_actor_workstation($ws) or return $e->die_event;
2340 return $ws->id; # note: editor sets the id on the new object for us
2343 __PACKAGE__->register_method(
2344 method => 'workstation_list',
2345 api_name => 'open-ils.actor.workstation.list',
2347 Returns a list of workstations registered at the given location
2348 @param authtoken The login session key
2349 @param ids A list of org_unit.id's for the workstation owners
2353 sub workstation_list {
2354 my( $self, $conn, $authtoken, @orgs ) = @_;
2356 my $e = new_editor(authtoken=>$authtoken);
2357 return $e->event unless $e->checkauth;
2362 unless $e->allowed('REGISTER_WORKSTATION', $o);
2363 $results{$o} = $e->search_actor_workstation({owning_lib=>$o});
2369 __PACKAGE__->register_method(
2370 method => 'fetch_patron_note',
2371 api_name => 'open-ils.actor.note.retrieve.all',
2374 Returns a list of notes for a given user
2375 Requestor must have VIEW_USER permission if pub==false and
2376 @param authtoken The login session key
2377 @param args Hash of params including
2378 patronid : the patron's id
2379 pub : true if retrieving only public notes
2383 sub fetch_patron_note {
2384 my( $self, $conn, $authtoken, $args ) = @_;
2385 my $patronid = $$args{patronid};
2387 my($reqr, $evt) = $U->checkses($authtoken);
2388 return $evt if $evt;
2391 ($patron, $evt) = $U->fetch_user($patronid);
2392 return $evt if $evt;
2395 if( $patronid ne $reqr->id ) {
2396 $evt = $U->check_perms($reqr->id, $patron->home_ou, 'VIEW_USER');
2397 return $evt if $evt;
2399 return $U->cstorereq(
2400 'open-ils.cstore.direct.actor.usr_note.search.atomic',
2401 { usr => $patronid, pub => 't' } );
2404 $evt = $U->check_perms($reqr->id, $patron->home_ou, 'VIEW_USER');
2405 return $evt if $evt;
2407 return $U->cstorereq(
2408 'open-ils.cstore.direct.actor.usr_note.search.atomic', { usr => $patronid } );
2411 __PACKAGE__->register_method(
2412 method => 'create_user_note',
2413 api_name => 'open-ils.actor.note.create',
2415 Creates a new note for the given user
2416 @param authtoken The login session key
2417 @param note The note object
2420 sub create_user_note {
2421 my( $self, $conn, $authtoken, $note ) = @_;
2422 my $e = new_editor(xact=>1, authtoken=>$authtoken);
2423 return $e->die_event unless $e->checkauth;
2425 my $user = $e->retrieve_actor_user($note->usr)
2426 or return $e->die_event;
2428 return $e->die_event unless
2429 $e->allowed('UPDATE_USER',$user->home_ou);
2431 $note->creator($e->requestor->id);
2432 $e->create_actor_usr_note($note) or return $e->die_event;
2438 __PACKAGE__->register_method(
2439 method => 'delete_user_note',
2440 api_name => 'open-ils.actor.note.delete',
2442 Deletes a note for the given user
2443 @param authtoken The login session key
2444 @param noteid The note id
2447 sub delete_user_note {
2448 my( $self, $conn, $authtoken, $noteid ) = @_;
2450 my $e = new_editor(xact=>1, authtoken=>$authtoken);
2451 return $e->die_event unless $e->checkauth;
2452 my $note = $e->retrieve_actor_usr_note($noteid)
2453 or return $e->die_event;
2454 my $user = $e->retrieve_actor_user($note->usr)
2455 or return $e->die_event;
2456 return $e->die_event unless
2457 $e->allowed('UPDATE_USER', $user->home_ou);
2459 $e->delete_actor_usr_note($note) or return $e->die_event;
2465 __PACKAGE__->register_method(
2466 method => 'update_user_note',
2467 api_name => 'open-ils.actor.note.update',
2469 @param authtoken The login session key
2470 @param note The note
2474 sub update_user_note {
2475 my( $self, $conn, $auth, $note ) = @_;
2476 my $e = new_editor(authtoken=>$auth, xact=>1);
2477 return $e->die_event unless $e->checkauth;
2478 my $patron = $e->retrieve_actor_user($note->usr)
2479 or return $e->die_event;
2480 return $e->die_event unless
2481 $e->allowed('UPDATE_USER', $patron->home_ou);
2482 $e->update_actor_user_note($note)
2483 or return $e->die_event;
2490 __PACKAGE__->register_method(
2491 method => 'create_closed_date',
2492 api_name => 'open-ils.actor.org_unit.closed_date.create',
2494 Creates a new closing entry for the given org_unit
2495 @param authtoken The login session key
2496 @param note The closed_date object
2499 sub create_closed_date {
2500 my( $self, $conn, $authtoken, $cd ) = @_;
2502 my( $user, $evt ) = $U->checkses($authtoken);
2503 return $evt if $evt;
2505 $evt = $U->check_perms($user->id, $cd->org_unit, 'CREATE_CLOSEING');
2506 return $evt if $evt;
2508 $logger->activity("user ".$user->id." creating library closing for ".$cd->org_unit);
2510 my $id = $U->storagereq(
2511 'open-ils.storage.direct.actor.org_unit.closed_date.create', $cd );
2512 return $U->DB_UPDATE_FAILED($cd) unless $id;
2517 __PACKAGE__->register_method(
2518 method => 'delete_closed_date',
2519 api_name => 'open-ils.actor.org_unit.closed_date.delete',
2521 Deletes a closing entry for the given org_unit
2522 @param authtoken The login session key
2523 @param noteid The close_date id
2526 sub delete_closed_date {
2527 my( $self, $conn, $authtoken, $cd ) = @_;
2529 my( $user, $evt ) = $U->checkses($authtoken);
2530 return $evt if $evt;
2533 ($cd_obj, $evt) = fetch_closed_date($cd);
2534 return $evt if $evt;
2536 $evt = $U->check_perms($user->id, $cd->org_unit, 'DELETE_CLOSEING');
2537 return $evt if $evt;
2539 $logger->activity("user ".$user->id." deleting library closing for ".$cd->org_unit);
2541 my $stat = $U->storagereq(
2542 'open-ils.storage.direct.actor.org_unit.closed_date.delete', $cd );
2543 return $U->DB_UPDATE_FAILED($cd) unless $stat;
2548 __PACKAGE__->register_method(
2549 method => 'usrname_exists',
2550 api_name => 'open-ils.actor.username.exists',
2552 desc => 'Check if a username is already taken (by an undeleted patron)',
2554 {desc => 'Authentication token', type => 'string'},
2555 {desc => 'Username', type => 'string'}
2558 desc => 'id of existing user if username exists, undef otherwise. Event on error'
2563 sub usrname_exists {
2564 my( $self, $conn, $auth, $usrname ) = @_;
2565 my $e = new_editor(authtoken=>$auth);
2566 return $e->event unless $e->checkauth;
2567 my $a = $e->search_actor_user({usrname => $usrname, deleted=>'f'}, {idlist=>1});
2568 return $$a[0] if $a and @$a;
2572 __PACKAGE__->register_method(
2573 method => 'barcode_exists',
2574 api_name => 'open-ils.actor.barcode.exists',
2576 signature => 'Returns 1 if the requested barcode exists, returns 0 otherwise'
2579 sub barcode_exists {
2580 my( $self, $conn, $auth, $barcode ) = @_;
2581 my $e = new_editor(authtoken=>$auth);
2582 return $e->event unless $e->checkauth;
2583 my $card = $e->search_actor_card({barcode => $barcode});
2589 #return undef unless @$card;
2590 #return $card->[0]->usr;
2594 __PACKAGE__->register_method(
2595 method => 'retrieve_net_levels',
2596 api_name => 'open-ils.actor.net_access_level.retrieve.all',
2599 sub retrieve_net_levels {
2600 my( $self, $conn, $auth ) = @_;
2601 my $e = new_editor(authtoken=>$auth);
2602 return $e->event unless $e->checkauth;
2603 return $e->retrieve_all_config_net_access_level();
2606 # Retain the old typo API name just in case
2607 __PACKAGE__->register_method(
2608 method => 'fetch_org_by_shortname',
2609 api_name => 'open-ils.actor.org_unit.retrieve_by_shorname',
2611 __PACKAGE__->register_method(
2612 method => 'fetch_org_by_shortname',
2613 api_name => 'open-ils.actor.org_unit.retrieve_by_shortname',
2615 sub fetch_org_by_shortname {
2616 my( $self, $conn, $sname ) = @_;
2617 my $e = new_editor();
2618 my $org = $e->search_actor_org_unit({ shortname => uc($sname)})->[0];
2619 return $e->event unless $org;
2624 __PACKAGE__->register_method(
2625 method => 'session_home_lib',
2626 api_name => 'open-ils.actor.session.home_lib',
2629 sub session_home_lib {
2630 my( $self, $conn, $auth ) = @_;
2631 my $e = new_editor(authtoken=>$auth);
2632 return undef unless $e->checkauth;
2633 my $org = $e->retrieve_actor_org_unit($e->requestor->home_ou);
2634 return $org->shortname;
2637 __PACKAGE__->register_method(
2638 method => 'session_safe_token',
2639 api_name => 'open-ils.actor.session.safe_token',
2641 Returns a hashed session ID that is safe for export to the world.
2642 This safe token will expire after 1 hour of non-use.
2643 @param auth Active authentication token
2647 sub session_safe_token {
2648 my( $self, $conn, $auth ) = @_;
2649 my $e = new_editor(authtoken=>$auth);
2650 return undef unless $e->checkauth;
2652 my $safe_token = md5_hex($auth);
2654 $cache ||= OpenSRF::Utils::Cache->new("global", 0);
2656 # Add more like the following if needed...
2658 "safe-token-home_lib-shortname-$safe_token",
2659 $e->retrieve_actor_org_unit(
2660 $e->requestor->home_ou
2669 __PACKAGE__->register_method(
2670 method => 'safe_token_home_lib',
2671 api_name => 'open-ils.actor.safe_token.home_lib.shortname',
2673 Returns the home library shortname from the session
2674 asscociated with a safe token from generated by
2675 open-ils.actor.session.safe_token.
2676 @param safe_token Active safe token
2680 sub safe_token_home_lib {
2681 my( $self, $conn, $safe_token ) = @_;
2683 $cache ||= OpenSRF::Utils::Cache->new("global", 0);
2684 return $cache->get_cache( 'safe-token-home_lib-shortname-'. $safe_token );
2689 __PACKAGE__->register_method(
2690 method => 'slim_tree',
2691 api_name => "open-ils.actor.org_tree.slim_hash.retrieve",
2694 my $tree = new_editor()->search_actor_org_unit(
2696 {"parent_ou" => undef },
2699 flesh_fields => { aou => ['children'] },
2700 order_by => { aou => 'name'},
2701 select => { aou => ["id","shortname", "name"]},
2706 return trim_tree($tree);
2712 return undef unless $tree;
2714 code => $tree->shortname,
2715 name => $tree->name,
2717 if( $tree->children and @{$tree->children} ) {
2718 $htree->{children} = [];
2719 for my $c (@{$tree->children}) {
2720 push( @{$htree->{children}}, trim_tree($c) );
2728 __PACKAGE__->register_method(
2729 method => "update_penalties",
2730 api_name => "open-ils.actor.user.penalties.update"
2733 sub update_penalties {
2734 my($self, $conn, $auth, $user_id) = @_;
2735 my $e = new_editor(authtoken=>$auth, xact => 1);
2736 return $e->die_event unless $e->checkauth;
2737 my $user = $e->retrieve_actor_user($user_id) or return $e->die_event;
2738 return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
2739 my $evt = OpenILS::Utils::Penalty->calculate_penalties($e, $user_id, $e->requestor->ws_ou);
2740 return $evt if $evt;
2746 __PACKAGE__->register_method(
2747 method => "apply_penalty",
2748 api_name => "open-ils.actor.user.penalty.apply"
2752 my($self, $conn, $auth, $penalty) = @_;
2754 my $e = new_editor(authtoken=>$auth, xact => 1);
2755 return $e->die_event unless $e->checkauth;
2757 my $user = $e->retrieve_actor_user($penalty->usr) or return $e->die_event;
2758 return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
2760 my $ptype = $e->retrieve_config_standing_penalty($penalty->standing_penalty) or return $e->die_event;
2763 (defined $ptype->org_depth) ?
2764 $U->org_unit_ancestor_at_depth($penalty->org_unit, $ptype->org_depth) :
2767 $penalty->org_unit($ctx_org);
2768 $penalty->staff($e->requestor->id);
2769 $e->create_actor_user_standing_penalty($penalty) or return $e->die_event;
2772 return $penalty->id;
2775 __PACKAGE__->register_method(
2776 method => "remove_penalty",
2777 api_name => "open-ils.actor.user.penalty.remove"
2780 sub remove_penalty {
2781 my($self, $conn, $auth, $penalty) = @_;
2782 my $e = new_editor(authtoken=>$auth, xact => 1);
2783 return $e->die_event unless $e->checkauth;
2784 my $user = $e->retrieve_actor_user($penalty->usr) or return $e->die_event;
2785 return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
2787 $e->delete_actor_user_standing_penalty($penalty) or return $e->die_event;
2792 __PACKAGE__->register_method(
2793 method => "update_penalty_note",
2794 api_name => "open-ils.actor.user.penalty.note.update"
2797 sub update_penalty_note {
2798 my($self, $conn, $auth, $penalty_ids, $note) = @_;
2799 my $e = new_editor(authtoken=>$auth, xact => 1);
2800 return $e->die_event unless $e->checkauth;
2801 for my $penalty_id (@$penalty_ids) {
2802 my $penalty = $e->search_actor_user_standing_penalty( { id => $penalty_id } )->[0];
2803 if (! $penalty ) { return $e->die_event; }
2804 my $user = $e->retrieve_actor_user($penalty->usr) or return $e->die_event;
2805 return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
2807 $penalty->note( $note ); $penalty->ischanged( 1 );
2809 $e->update_actor_user_standing_penalty($penalty) or return $e->die_event;
2815 __PACKAGE__->register_method(
2816 method => "ranged_penalty_thresholds",
2817 api_name => "open-ils.actor.grp_penalty_threshold.ranged.retrieve",
2821 sub ranged_penalty_thresholds {
2822 my($self, $conn, $auth, $context_org) = @_;
2823 my $e = new_editor(authtoken=>$auth);
2824 return $e->event unless $e->checkauth;
2825 return $e->event unless $e->allowed('VIEW_GROUP_PENALTY_THRESHOLD', $context_org);
2826 my $list = $e->search_permission_grp_penalty_threshold([
2827 {org_unit => $U->get_org_ancestors($context_org)},
2828 {order_by => {pgpt => 'id'}}
2830 $conn->respond($_) for @$list;
2836 __PACKAGE__->register_method(
2837 method => "user_retrieve_fleshed_by_id",
2839 api_name => "open-ils.actor.user.fleshed.retrieve",
2842 sub user_retrieve_fleshed_by_id {
2843 my( $self, $client, $auth, $user_id, $fields ) = @_;
2844 my $e = new_editor(authtoken => $auth);
2845 return $e->event unless $e->checkauth;
2847 if( $e->requestor->id != $user_id ) {
2848 return $e->event unless $e->allowed('VIEW_USER');
2854 "standing_penalties",
2858 "stat_cat_entries" ];
2859 return new_flesh_user($user_id, $fields, $e);
2863 sub new_flesh_user {
2866 my $fields = shift || [];
2869 my $fetch_penalties = 0;
2870 if(grep {$_ eq 'standing_penalties'} @$fields) {
2871 $fields = [grep {$_ ne 'standing_penalties'} @$fields];
2872 $fetch_penalties = 1;
2875 my $user = $e->retrieve_actor_user(
2880 "flesh_fields" => { "au" => $fields }
2883 ) or return $e->die_event;
2886 if( grep { $_ eq 'addresses' } @$fields ) {
2888 $user->addresses([]) unless @{$user->addresses};
2889 # don't expose "replaced" addresses by default
2890 $user->addresses([grep {$_->id >= 0} @{$user->addresses}]);
2892 if( ref $user->billing_address ) {
2893 unless( grep { $user->billing_address->id == $_->id } @{$user->addresses} ) {
2894 push( @{$user->addresses}, $user->billing_address );
2898 if( ref $user->mailing_address ) {
2899 unless( grep { $user->mailing_address->id == $_->id } @{$user->addresses} ) {
2900 push( @{$user->addresses}, $user->mailing_address );
2905 if($fetch_penalties) {
2906 # grab the user penalties ranged for this location
2907 $user->standing_penalties(
2908 $e->search_actor_user_standing_penalty([
2911 {stop_date => undef},
2912 {stop_date => {'>' => 'now'}}
2914 org_unit => $U->get_org_ancestors($e->requestor->ws_ou)
2917 flesh_fields => {ausp => ['standing_penalty']}
2924 $user->clear_passwd();
2931 __PACKAGE__->register_method(
2932 method => "user_retrieve_parts",
2933 api_name => "open-ils.actor.user.retrieve.parts",
2936 sub user_retrieve_parts {
2937 my( $self, $client, $auth, $user_id, $fields ) = @_;
2938 my $e = new_editor(authtoken => $auth);
2939 return $e->event unless $e->checkauth;
2940 $user_id ||= $e->requestor->id;
2941 if( $e->requestor->id != $user_id ) {
2942 return $e->event unless $e->allowed('VIEW_USER');
2945 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
2946 push(@resp, $user->$_()) for(@$fields);
2952 __PACKAGE__->register_method(
2953 method => 'user_opt_in_enabled',
2954 api_name => 'open-ils.actor.user.org_unit_opt_in.enabled',
2955 signature => '@return 1 if user opt-in is globally enabled, 0 otherwise.'
2958 sub user_opt_in_enabled {
2959 my($self, $conn) = @_;
2960 my $sc = OpenSRF::Utils::SettingsClient->new;
2961 return 1 if lc($sc->config_value(share => user => 'opt_in')) eq 'true';
2966 __PACKAGE__->register_method(
2967 method => 'user_opt_in_at_org',
2968 api_name => 'open-ils.actor.user.org_unit_opt_in.check',
2970 @param $auth The auth token
2971 @param user_id The ID of the user to test
2972 @return 1 if the user has opted in at the specified org,
2973 event on error, and 0 otherwise. /
2975 sub user_opt_in_at_org {
2976 my($self, $conn, $auth, $user_id) = @_;
2978 # see if we even need to enforce the opt-in value
2979 return 1 unless user_opt_in_enabled($self);
2981 my $e = new_editor(authtoken => $auth);
2982 return $e->event unless $e->checkauth;
2983 my $org_id = $e->requestor->ws_ou;
2985 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
2986 return $e->event unless $e->allowed('VIEW_USER', $user->home_ou);
2988 # user is automatically opted-in at the home org
2989 return 1 if $user->home_ou eq $org_id;
2991 my $vals = $e->search_actor_usr_org_unit_opt_in(
2992 {org_unit=>$org_id, usr=>$user_id},{idlist=>1});
2998 __PACKAGE__->register_method(
2999 method => 'create_user_opt_in_at_org',
3000 api_name => 'open-ils.actor.user.org_unit_opt_in.create',
3002 @param $auth The auth token
3003 @param user_id The ID of the user to test
3004 @return The ID of the newly created object, event on error./
3007 sub create_user_opt_in_at_org {
3008 my($self, $conn, $auth, $user_id) = @_;
3010 my $e = new_editor(authtoken => $auth, xact=>1);
3011 return $e->die_event unless $e->checkauth;
3012 my $org_id = $e->requestor->ws_ou;
3014 my $user = $e->retrieve_actor_user($user_id) or return $e->die_event;
3015 return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
3017 my $opt_in = Fieldmapper::actor::usr_org_unit_opt_in->new;
3019 $opt_in->org_unit($org_id);
3020 $opt_in->usr($user_id);
3021 $opt_in->staff($e->requestor->id);
3022 $opt_in->opt_in_ts('now');
3023 $opt_in->opt_in_ws($e->requestor->wsid);
3025 $opt_in = $e->create_actor_usr_org_unit_opt_in($opt_in)
3026 or return $e->die_event;
3034 __PACKAGE__->register_method (
3035 method => 'retrieve_org_hours',
3036 api_name => 'open-ils.actor.org_unit.hours_of_operation.retrieve',
3038 Returns the hours of operation for a specified org unit
3039 @param authtoken The login session key
3040 @param org_id The org_unit ID
3044 sub retrieve_org_hours {
3045 my($self, $conn, $auth, $org_id) = @_;
3046 my $e = new_editor(authtoken => $auth);
3047 return $e->die_event unless $e->checkauth;
3048 $org_id ||= $e->requestor->ws_ou;
3049 return $e->retrieve_actor_org_unit_hours_of_operation($org_id);
3053 __PACKAGE__->register_method (
3054 method => 'verify_user_password',
3055 api_name => 'open-ils.actor.verify_user_password',
3057 Given a barcode or username and the MD5 encoded password,
3058 returns 1 if the password is correct. Returns 0 otherwise.
3062 sub verify_user_password {
3063 my($self, $conn, $auth, $barcode, $username, $password) = @_;
3064 my $e = new_editor(authtoken => $auth);
3065 return $e->die_event unless $e->checkauth;
3067 my $user_by_barcode;
3068 my $user_by_username;
3070 my $card = $e->search_actor_card([
3071 {barcode => $barcode},
3072 {flesh => 1, flesh_fields => {ac => ['usr']}}])->[0] or return 0;
3073 $user_by_barcode = $card->usr;
3074 $user = $user_by_barcode;
3077 $user_by_username = $e->search_actor_user({usrname => $username})->[0] or return 0;
3078 $user = $user_by_username;
3080 return 0 if (!$user);
3081 return 0 if ($user_by_username && $user_by_barcode && $user_by_username->id != $user_by_barcode->id);
3082 return $e->event unless $e->allowed('VIEW_USER', $user->home_ou);
3083 return 1 if $user->passwd eq $password;
3087 __PACKAGE__->register_method (
3088 method => 'retrieve_usr_id_via_barcode_or_usrname',
3089 api_name => "open-ils.actor.user.retrieve_id_by_barcode_or_username",
3091 Given a barcode or username returns the id for the user or
3096 sub retrieve_usr_id_via_barcode_or_usrname {
3097 my($self, $conn, $auth, $barcode, $username) = @_;
3098 my $e = new_editor(authtoken => $auth);
3099 return $e->die_event unless $e->checkauth;
3100 my $id_as_barcode= OpenSRF::Utils::SettingsClient->new->config_value(apps => 'open-ils.actor' => app_settings => 'id_as_barcode');
3102 my $user_by_barcode;
3103 my $user_by_username;
3104 $logger->info("$id_as_barcode is the ID as BARCODE");
3106 my $card = $e->search_actor_card([
3107 {barcode => $barcode},
3108 {flesh => 1, flesh_fields => {ac => ['usr']}}])->[0];
3109 if ($id_as_barcode =~ /^t/i) {
3111 $user = $e->retrieve_actor_user($barcode);
3112 return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' ) if(!$user);
3114 $user_by_barcode = $card->usr;
3115 $user = $user_by_barcode;
3118 return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' ) if(!$card);
3119 $user_by_barcode = $card->usr;
3120 $user = $user_by_barcode;
3125 $user_by_username = $e->search_actor_user({usrname => $username})->[0] or return OpenILS::Event->new( 'ACTOR_USR_NOT_FOUND' );
3127 $user = $user_by_username;
3129 return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' ) if (!$user);
3130 return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' ) if ($user_by_username && $user_by_barcode && $user_by_username->id != $user_by_barcode->id);
3131 return $e->event unless $e->allowed('VIEW_USER', $user->home_ou);
3136 __PACKAGE__->register_method (
3137 method => 'merge_users',
3138 api_name => 'open-ils.actor.user.merge',
3141 Given a list of source users and destination user, transfer all data from the source
3142 to the dest user and delete the source user. All user related data is
3143 transferred, including circulations, holds, bookbags, etc.
3149 my($self, $conn, $auth, $master_id, $user_ids, $options) = @_;
3150 my $e = new_editor(xact => 1, authtoken => $auth);
3151 return $e->die_event unless $e->checkauth;
3153 # disallow the merge if any subordinate accounts are in collections
3154 my $colls = $e->search_money_collections_tracker({usr => $user_ids}, {idlist => 1});
3155 return OpenILS::Event->new('MERGED_USER_IN_COLLECTIONS', payload => $user_ids) if @$colls;
3157 my $master_user = $e->retrieve_actor_user($master_id) or return $e->die_event;
3158 my $del_addrs = ($U->ou_ancestor_setting_value(
3159 $master_user->home_ou, 'circ.user_merge.delete_addresses', $e)) ? 't' : 'f';
3160 my $del_cards = ($U->ou_ancestor_setting_value(
3161 $master_user->home_ou, 'circ.user_merge.delete_cards', $e)) ? 't' : 'f';
3162 my $deactivate_cards = ($U->ou_ancestor_setting_value(
3163 $master_user->home_ou, 'circ.user_merge.deactivate_cards', $e)) ? 't' : 'f';
3165 for my $src_id (@$user_ids) {
3166 my $src_user = $e->retrieve_actor_user($src_id) or return $e->die_event;
3168 return $e->die_event unless $e->allowed('MERGE_USERS', $src_user->home_ou);
3169 if($src_user->home_ou ne $master_user->home_ou) {
3170 return $e->die_event unless $e->allowed('MERGE_USERS', $master_user->home_ou);
3173 return $e->die_event unless
3174 $e->json_query({from => [
3189 __PACKAGE__->register_method (
3190 method => 'approve_user_address',
3191 api_name => 'open-ils.actor.user.pending_address.approve',
3198 sub approve_user_address {
3199 my($self, $conn, $auth, $addr) = @_;
3200 my $e = new_editor(xact => 1, authtoken => $auth);
3201 return $e->die_event unless $e->checkauth;
3203 # if the caller passes an address object, assume they want to
3204 # update it first before approving it
3205 $e->update_actor_user_address($addr) or return $e->die_event;
3207 $addr = $e->retrieve_actor_user_address($addr) or return $e->die_event;
3209 my $user = $e->retrieve_actor_user($addr->usr);
3210 return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
3211 my $result = $e->json_query({from => ['actor.approve_pending_address', $addr->id]})->[0]
3212 or return $e->die_event;
3214 return [values %$result]->[0];
3218 __PACKAGE__->register_method (
3219 method => 'retrieve_friends',
3220 api_name => 'open-ils.actor.friends.retrieve',
3223 returns { confirmed: [], pending_out: [], pending_in: []}
3224 pending_out are users I'm requesting friendship with
3225 pending_in are users requesting friendship with me
3230 sub retrieve_friends {
3231 my($self, $conn, $auth, $user_id, $options) = @_;
3232 my $e = new_editor(authtoken => $auth);
3233 return $e->event unless $e->checkauth;
3234 $user_id ||= $e->requestor->id;
3236 if($user_id != $e->requestor->id) {
3237 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
3238 return $e->event unless $e->allowed('VIEW_USER', $user->home_ou);
3241 return OpenILS::Application::Actor::Friends->retrieve_friends(
3242 $e, $user_id, $options);
3247 __PACKAGE__->register_method (
3248 method => 'apply_friend_perms',
3249 api_name => 'open-ils.actor.friends.perms.apply',
3255 sub apply_friend_perms {
3256 my($self, $conn, $auth, $user_id, $delegate_id, @perms) = @_;
3257 my $e = new_editor(authtoken => $auth, xact => 1);
3258 return $e->die_event unless $e->checkauth;
3260 if($user_id != $e->requestor->id) {
3261 my $user = $e->retrieve_actor_user($user_id) or return $e->die_event;
3262 return $e->die_event unless $e->allowed('VIEW_USER', $user->home_ou);
3265 for my $perm (@perms) {
3267 OpenILS::Application::Actor::Friends->apply_friend_perm(
3268 $e, $user_id, $delegate_id, $perm);
3269 return $evt if $evt;
3277 __PACKAGE__->register_method (
3278 method => 'update_user_pending_address',
3279 api_name => 'open-ils.actor.user.address.pending.cud'
3282 sub update_user_pending_address {
3283 my($self, $conn, $auth, $addr) = @_;
3284 my $e = new_editor(authtoken => $auth, xact => 1);
3285 return $e->die_event unless $e->checkauth;
3287 if($addr->usr != $e->requestor->id) {
3288 my $user = $e->retrieve_actor_user($addr->usr) or return $e->die_event;
3289 return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
3293 $e->create_actor_user_address($addr) or return $e->die_event;
3294 } elsif($addr->isdeleted) {
3295 $e->delete_actor_user_address($addr) or return $e->die_event;
3297 $e->update_actor_user_address($addr) or return $e->die_event;
3305 __PACKAGE__->register_method (
3306 method => 'user_events',
3307 api_name => 'open-ils.actor.user.events.circ',
3310 __PACKAGE__->register_method (
3311 method => 'user_events',
3312 api_name => 'open-ils.actor.user.events.ahr',
3317 my($self, $conn, $auth, $user_id, $filters) = @_;
3318 my $e = new_editor(authtoken => $auth);
3319 return $e->event unless $e->checkauth;
3321 (my $obj_type = $self->api_name) =~ s/.*\.([a-z]+)$/$1/;
3322 my $user_field = 'usr';
3325 $filters->{target} = {
3326 select => { $obj_type => ['id'] },
3328 where => {usr => $user_id}
3331 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
3332 if($e->requestor->id != $user_id) {
3333 return $e->event unless $e->allowed('VIEW_USER', $user->home_ou);
3336 my $ses = OpenSRF::AppSession->create('open-ils.trigger');
3337 my $req = $ses->request('open-ils.trigger.events_by_target',
3338 $obj_type, $filters, {atevdef => ['reactor', 'validator']}, 2);
3340 while(my $resp = $req->recv) {
3341 my $val = $resp->content;
3342 my $tgt = $val->target;
3344 if($obj_type eq 'circ') {
3345 $tgt->target_copy($e->retrieve_asset_copy($tgt->target_copy));
3347 } elsif($obj_type eq 'ahr') {
3348 $tgt->current_copy($e->retrieve_asset_copy($tgt->current_copy))
3349 if $tgt->current_copy;
3352 $conn->respond($val) if $val;
3358 __PACKAGE__->register_method (
3359 method => 'copy_events',
3360 api_name => 'open-ils.actor.copy.events.circ',
3363 __PACKAGE__->register_method (
3364 method => 'copy_events',
3365 api_name => 'open-ils.actor.copy.events.ahr',
3370 my($self, $conn, $auth, $copy_id, $filters) = @_;
3371 my $e = new_editor(authtoken => $auth);
3372 return $e->event unless $e->checkauth;
3374 (my $obj_type = $self->api_name) =~ s/.*\.([a-z]+)$/$1/;
3376 my $copy = $e->retrieve_asset_copy($copy_id) or return $e->event;
3378 my $copy_field = 'target_copy';
3379 $copy_field = 'current_copy' if $obj_type eq 'ahr';
3382 $filters->{target} = {
3383 select => { $obj_type => ['id'] },
3385 where => {$copy_field => $copy_id}
3389 my $ses = OpenSRF::AppSession->create('open-ils.trigger');
3390 my $req = $ses->request('open-ils.trigger.events_by_target',
3391 $obj_type, $filters, {atevdef => ['reactor', 'validator']}, 2);
3393 while(my $resp = $req->recv) {
3394 my $val = $resp->content;
3395 my $tgt = $val->target;
3397 my $user = $e->retrieve_actor_user($tgt->usr);
3398 if($e->requestor->id != $user->id) {
3399 return $e->event unless $e->allowed('VIEW_USER', $user->home_ou);
3402 $tgt->$copy_field($copy);
3405 $conn->respond($val) if $val;
3414 __PACKAGE__->register_method (
3415 method => 'update_events',
3416 api_name => 'open-ils.actor.user.event.cancel.batch',
3419 __PACKAGE__->register_method (
3420 method => 'update_events',
3421 api_name => 'open-ils.actor.user.event.reset.batch',
3426 my($self, $conn, $auth, $event_ids) = @_;
3427 my $e = new_editor(xact => 1, authtoken => $auth);
3428 return $e->die_event unless $e->checkauth;
3431 for my $id (@$event_ids) {
3433 # do a little dance to determine what user we are ultimately affecting
3434 my $event = $e->retrieve_action_trigger_event([
3437 flesh_fields => {atev => ['event_def'], atevdef => ['hook']}
3439 ]) or return $e->die_event;
3442 if($event->event_def->hook->core_type eq 'circ') {
3443 $user_id = $e->retrieve_action_circulation($event->target)->usr;
3444 } elsif($event->event_def->hook->core_type eq 'ahr') {
3445 $user_id = $e->retrieve_action_hold_request($event->target)->usr;
3450 my $user = $e->retrieve_actor_user($user_id);
3451 return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
3453 if($self->api_name =~ /cancel/) {
3454 $event->state('invalid');
3455 } elsif($self->api_name =~ /reset/) {
3456 $event->clear_start_time;
3457 $event->clear_update_time;
3458 $event->state('pending');
3461 $e->update_action_trigger_event($event) or return $e->die_event;
3462 $conn->respond({maximum => scalar(@$event_ids), progress => $x++});
3466 return {complete => 1};
3470 __PACKAGE__->register_method (
3471 method => 'really_delete_user',
3472 api_name => 'open-ils.actor.user.delete',
3474 It anonymizes all personally identifiable information in actor.usr. By calling actor.usr_purge_data()
3475 it also purges related data from other tables, sometimes by transferring it to a designated destination user.
3476 The usrname field (along with first_given_name and family_name) is updated to id '-PURGED-' now().
3477 dest_usr_id is only required when deleting a user that performs staff functions.
3481 sub really_delete_user {
3482 my($self, $conn, $auth, $user_id, $dest_user_id) = @_;
3483 my $e = new_editor(authtoken => $auth, xact => 1);
3484 return $e->die_event unless $e->checkauth;
3485 my $user = $e->retrieve_actor_user($user_id) or return $e->die_event;
3486 return $e->die_event unless $e->allowed('DELETE_USER', $user->home_ou);
3487 my $stat = $e->json_query(
3488 {from => ['actor.usr_delete', $user_id, $dest_user_id]})->[0]
3489 or return $e->die_event;
3496 __PACKAGE__->register_method (
3497 method => 'user_payments',
3498 api_name => 'open-ils.actor.user.payments.retrieve',
3501 Returns all payments for a given user. Default order is newest payments first.
3502 @param auth Authentication token
3503 @param user_id The user ID
3504 @param filters An optional hash of filters, including limit, offset, and order_by definitions
3509 my($self, $conn, $auth, $user_id, $filters) = @_;
3512 my $e = new_editor(authtoken => $auth);
3513 return $e->die_event unless $e->checkauth;
3515 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
3516 return $e->event unless
3517 $e->requestor->id == $user_id or
3518 $e->allowed('VIEW_USER_TRANSACTIONS', $user->home_ou);
3520 # Find all payments for all transactions for user $user_id
3522 select => {mp => ['id']},
3527 select => {mbt => ['id']},
3529 where => {usr => $user_id}
3533 order_by => [{ # by default, order newest payments first
3535 field => 'payment_ts',
3540 for (qw/order_by limit offset/) {
3541 $query->{$_} = $filters->{$_} if defined $filters->{$_};
3544 if(defined $filters->{where}) {
3545 foreach (keys %{$filters->{where}}) {
3546 # don't allow the caller to expand the result set to other users
3547 $query->{where}->{$_} = $filters->{where}->{$_} unless $_ eq 'xact';
3551 my $payment_ids = $e->json_query($query);
3552 for my $pid (@$payment_ids) {
3553 my $pay = $e->retrieve_money_payment([
3558 mbt => ['summary', 'circulation', 'grocery'],
3559 circ => ['target_copy'],
3560 acp => ['call_number'],
3568 xact_type => $pay->xact->summary->xact_type,
3569 last_billing_type => $pay->xact->summary->last_billing_type,
3572 if($pay->xact->summary->xact_type eq 'circulation') {
3573 $resp->{barcode} = $pay->xact->circulation->target_copy->barcode;
3574 $resp->{title} = $U->record_to_mvr($pay->xact->circulation->target_copy->call_number->record)->title;
3577 $pay->xact($pay->xact->id); # de-flesh
3578 $conn->respond($resp);
3586 __PACKAGE__->register_method (
3587 method => 'negative_balance_users',
3588 api_name => 'open-ils.actor.users.negative_balance',
3591 Returns all users that have an overall negative balance
3592 @param auth Authentication token
3593 @param org_id The context org unit as an ID or list of IDs. This will be the home
3594 library of the user. If no org_unit is specified, no org unit filter is applied
3598 sub negative_balance_users {
3599 my($self, $conn, $auth, $org_id) = @_;
3601 my $e = new_editor(authtoken => $auth);
3602 return $e->die_event unless $e->checkauth;
3603 return $e->die_event unless $e->allowed('VIEW_USER', $org_id);
3607 mous => ['usr', 'balance_owed'],
3610 {column => 'last_billing_ts', transform => 'max', aggregate => 1},
3611 {column => 'last_payment_ts', transform => 'max', aggregate => 1},
3628 where => {'+mous' => {balance_owed => {'<' => 0}}}
3631 $query->{from}->{mous}->{au}->{filter}->{home_ou} = $org_id if $org_id;
3633 my $list = $e->json_query($query, {timeout => 600});
3635 for my $data (@$list) {
3637 usr => $e->retrieve_actor_user([$data->{usr}, {flesh => 1, flesh_fields => {au => ['card']}}]),
3638 balance_owed => $data->{balance_owed},
3639 last_billing_activity => max($data->{last_billing_ts}, $data->{last_payment_ts})
3646 __PACKAGE__->register_method(
3647 method => "request_password_reset",
3648 api_name => "open-ils.actor.patron.password_reset.request",
3650 desc => "Generates a UUID token usable with the open-ils.actor.patron.password_reset.commit " .
3651 "method for changing a user's password. The UUID token is distributed via A/T " .
3652 "templates (i.e. email to the user).",
3654 { desc => 'user_id_type', type => 'string' },
3655 { desc => 'user_id', type => 'string' },
3656 { desc => 'optional (based on library setting) matching email address for authorizing request', type => 'string' },
3658 return => {desc => '1 on success, Event on error'}
3661 sub request_password_reset {
3662 my($self, $conn, $user_id_type, $user_id, $email) = @_;
3664 # Check to see if password reset requests are already being throttled:
3665 # 0. Check cache to see if we're in throttle mode (avoid hitting database)
3667 my $e = new_editor(xact => 1);
3670 # Get the user, if any, depending on the input value
3671 if ($user_id_type eq 'username') {
3672 $user = $e->search_actor_user({usrname => $user_id})->[0];
3675 return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' );
3677 } elsif ($user_id_type eq 'barcode') {
3678 my $card = $e->search_actor_card([
3679 {barcode => $user_id},
3680 {flesh => 1, flesh_fields => {ac => ['usr']}}])->[0];
3683 return OpenILS::Event->new('ACTOR_USER_NOT_FOUND');
3688 # If the user doesn't have an email address, we can't help them
3689 if (!$user->email) {
3691 return OpenILS::Event->new('PATRON_NO_EMAIL_ADDRESS');
3694 my $email_must_match = $U->ou_ancestor_setting_value($user->home_ou, 'circ.password_reset_request_requires_matching_email');
3695 if ($email_must_match) {
3696 if ($user->email ne $email) {
3697 return OpenILS::Event->new('EMAIL_VERIFICATION_FAILED');
3701 _reset_password_request($conn, $e, $user);
3704 # Once we have the user, we can issue the password reset request
3705 # XXX Add a wrapper method that accepts barcode + email input
3706 sub _reset_password_request {
3707 my ($conn, $e, $user) = @_;
3709 # 1. Get throttle threshold and time-to-live from OU_settings
3710 my $aupr_throttle = $U->ou_ancestor_setting_value($user->home_ou, 'circ.password_reset_request_throttle') || 1000;
3711 my $aupr_ttl = $U->ou_ancestor_setting_value($user->home_ou, 'circ.password_reset_request_time_to_live') || 24*60*60;
3713 my $threshold_time = DateTime->now(time_zone => 'local')->subtract(seconds => $aupr_ttl)->iso8601();
3715 # 2. Get time of last request and number of active requests (num_active)
3716 my $active_requests = $e->json_query({
3722 transform => 'COUNT'
3725 column => 'request_time',
3731 has_been_reset => { '=' => 'f' },
3732 request_time => { '>' => $threshold_time }
3736 # Guard against no active requests
3737 if ($active_requests->[0]->{'request_time'}) {
3738 my $last_request = DateTime::Format::ISO8601->parse_datetime(clense_ISO8601($active_requests->[0]->{'request_time'}));
3739 my $now = DateTime::Format::ISO8601->new();
3741 # 3. if (num_active > throttle_threshold) and (now - last_request < 1 minute)
3742 if (($active_requests->[0]->{'usr'} > $aupr_throttle) &&
3743 ($last_request->add_duration('1 minute') > $now)) {
3744 $cache->put_cache('open-ils.actor.password.throttle', DateTime::Format::ISO8601->new(), 60);
3746 return OpenILS::Event->new('PATRON_TOO_MANY_ACTIVE_PASSWORD_RESET_REQUESTS');
3750 # TODO Check to see if the user is in a password-reset-restricted group
3752 # Otherwise, go ahead and try to get the user.
3754 # Check the number of active requests for this user
3755 $active_requests = $e->json_query({
3761 transform => 'COUNT'
3766 usr => { '=' => $user->id },
3767 has_been_reset => { '=' => 'f' },
3768 request_time => { '>' => $threshold_time }
3772 $logger->info("User " . $user->id . " has " . $active_requests->[0]->{'usr'} . " active password reset requests.");
3774 # if less than or equal to per-user threshold, proceed; otherwise, return event
3775 my $aupr_per_user_limit = $U->ou_ancestor_setting_value($user->home_ou, 'circ.password_reset_request_per_user_limit') || 3;
3776 if ($active_requests->[0]->{'usr'} > $aupr_per_user_limit) {
3778 return OpenILS::Event->new('PATRON_TOO_MANY_ACTIVE_PASSWORD_RESET_REQUESTS');
3781 # Create the aupr object and insert into the database
3782 my $reset_request = Fieldmapper::actor::usr_password_reset->new;
3783 my $uuid = create_uuid_as_string(UUID_V4);
3784 $reset_request->uuid($uuid);
3785 $reset_request->usr($user->id);
3787 my $aupr = $e->create_actor_usr_password_reset($reset_request) or return $e->die_event;
3790 # Create an event to notify user of the URL to reset their password
3792 # Can we stuff this in the user_data param for trigger autocreate?
3793 my $hostname = $U->ou_ancestor_setting_value($user->home_ou, 'lib.hostname') || 'localhost';
3795 my $ses = OpenSRF::AppSession->create('open-ils.trigger');
3796 $ses->request('open-ils.trigger.event.autocreate', 'password.reset_request', $aupr, $user->home_ou);
3799 # $U->create_trigger_event('password.reset_request', $aupr, $user->home_ou);
3804 __PACKAGE__->register_method(
3805 method => "commit_password_reset",
3806 api_name => "open-ils.actor.patron.password_reset.commit",
3808 desc => "Checks a UUID token generated by the open-ils.actor.patron.password_reset.request method for " .
3809 "validity, and if valid, uses it as authorization for changing the associated user's password " .
3810 "with the supplied password.",
3812 { desc => 'uuid', type => 'string' },
3813 { desc => 'password', type => 'string' },
3815 return => {desc => '1 on success, Event on error'}
3818 sub commit_password_reset {
3819 my($self, $conn, $uuid, $password) = @_;
3821 # Check to see if password reset requests are already being throttled:
3822 # 0. Check cache to see if we're in throttle mode (avoid hitting database)
3823 $cache ||= OpenSRF::Utils::Cache->new("global", 0);
3824 my $throttle = $cache->get_cache('open-ils.actor.password.throttle') || undef;
3826 return OpenILS::Event->new('PATRON_NOT_AN_ACTIVE_PASSWORD_RESET_REQUEST');
3829 my $e = new_editor(xact => 1);
3831 my $aupr = $e->search_actor_usr_password_reset({
3838 return OpenILS::Event->new('PATRON_NOT_AN_ACTIVE_PASSWORD_RESET_REQUEST');
3840 my $user_id = $aupr->[0]->usr;
3841 my $user = $e->retrieve_actor_user($user_id);
3843 # Ensure we're still within the TTL for the request
3844 my $aupr_ttl = $U->ou_ancestor_setting_value($user->home_ou, 'circ.password_reset_request_time_to_live') || 24*60*60;
3845 my $threshold = DateTime::Format::ISO8601->parse_datetime(clense_ISO8601($aupr->[0]->request_time))->add(seconds => $aupr_ttl);
3846 if ($threshold < DateTime->now(time_zone => 'local')) {
3848 $logger->info("Password reset request needed to be submitted before $threshold");
3849 return OpenILS::Event->new('PATRON_NOT_AN_ACTIVE_PASSWORD_RESET_REQUEST');
3852 # Check complexity of password against OU-defined regex
3853 my $pw_regex = $U->ou_ancestor_setting_value($user->home_ou, 'global.password_regex');
3857 # Calling JSON2perl on the $pw_regex causes failure, even before the fancy Unicode regex
3858 # ($pw_regex = OpenSRF::Utils::JSON->JSON2perl($pw_regex)) =~ s/\\u([0-9a-fA-F]{4})/\\x{$1}/gs;
3859 $is_strong = check_password_strength_custom($password, $pw_regex);
3861 $is_strong = check_password_strength_default($password);
3866 return OpenILS::Event->new('PATRON_PASSWORD_WAS_NOT_STRONG');
3869 # All is well; update the password
3870 $user->passwd($password);
3871 $e->update_actor_user($user);
3873 # And flag that this password reset request has been honoured
3874 $aupr->[0]->has_been_reset('t');
3875 $e->update_actor_usr_password_reset($aupr->[0]);
3881 sub check_password_strength_default {
3882 my $password = shift;
3883 # Use the default set of checks
3884 if ( (length($password) < 7) or
3885 ($password !~ m/.*\d+.*/) or
3886 ($password !~ m/.*[A-Za-z]+.*/)
3893 sub check_password_strength_custom {
3894 my ($password, $pw_regex) = @_;
3896 $pw_regex = qr/$pw_regex/;
3897 if ($password !~ /$pw_regex/) {
3905 __PACKAGE__->register_method(
3906 method => "event_def_opt_in_settings",
3907 api_name => "open-ils.actor.event_def.opt_in.settings",
3910 desc => 'Streams the set of "cust" objects that are used as opt-in settings for event definitions',
3912 { desc => 'Authentication token', type => 'string'},
3914 desc => 'Org Unit ID. (optional). If no org ID is present, the home_ou of the requesting user is used',
3919 desc => q/set of "cust" objects that are used as opt-in settings for event definitions at the specified org unit/,
3926 sub event_def_opt_in_settings {
3927 my($self, $conn, $auth, $org_id) = @_;
3928 my $e = new_editor(authtoken => $auth);
3929 return $e->event unless $e->checkauth;
3931 if(defined $org_id and $org_id != $e->requestor->home_ou) {
3932 return $e->event unless
3933 $e->allowed(['VIEW_USER_SETTING_TYPE', 'ADMIN_USER_SETTING_TYPE'], $org_id);
3935 $org_id = $e->requestor->home_ou;
3938 # find all config.user_setting_type's related to event_defs for the requested org unit
3939 my $types = $e->json_query({
3940 select => {cust => ['name']},
3941 from => {atevdef => 'cust'},
3944 owner => $U->get_org_ancestors($org_id), # context org plus parents
3951 $conn->respond($_) for
3952 @{$e->search_config_usr_setting_type({name => [map {$_->{name}} @$types]})};
3959 __PACKAGE__->register_method(
3960 method => "user_visible_circs",
3961 api_name => "open-ils.actor.history.circ.visible",
3964 desc => 'Returns the set of opt-in visible circulations accompanied by circulation chain summaries',
3966 { desc => 'Authentication token', type => 'string'},
3967 { desc => 'User ID. If no user id is present, the authenticated user is assumed', type => 'number' },
3968 { desc => 'Options hash. Supported fields are "limit" and "offset"', type => 'object' },
3971 desc => q/An object with 2 fields: circulation and summary.
3972 circulation is the "circ" object. summary is the related "accs" object/,
3978 __PACKAGE__->register_method(
3979 method => "user_visible_circs",
3980 api_name => "open-ils.actor.history.circ.visible.print",
3983 desc => 'Returns printable output for the set of opt-in visible circulations',
3985 { desc => 'Authentication token', type => 'string'},
3986 { desc => 'User ID. If no user id is present, the authenticated user is assumed', type => 'number' },
3987 { desc => 'Options hash. Supported fields are "limit" and "offset"', type => 'object' },
3990 desc => q/An action_trigger.event object or error event./,
3996 __PACKAGE__->register_method(
3997 method => "user_visible_circs",
3998 api_name => "open-ils.actor.history.circ.visible.email",
4001 desc => 'Emails the set of opt-in visible circulations to the requestor',
4003 { desc => 'Authentication token', type => 'string'},
4004 { desc => 'User ID. If no user id is present, the authenticated user is assumed', type => 'number' },
4005 { desc => 'Options hash. Supported fields are "limit" and "offset"', type => 'object' },
4008 desc => q/undef, or event on error/
4013 __PACKAGE__->register_method(
4014 method => "user_visible_circs",
4015 api_name => "open-ils.actor.history.hold.visible",
4018 desc => 'Returns the set of opt-in visible holds',
4020 { desc => 'Authentication token', type => 'string'},
4021 { desc => 'User ID. If no user id is present, the authenticated user is assumed', type => 'number' },
4022 { desc => 'Options hash. Supported fields are "limit" and "offset"', type => 'object' },
4025 desc => q/An object with 1 field: "hold"/,
4031 __PACKAGE__->register_method(
4032 method => "user_visible_circs",
4033 api_name => "open-ils.actor.history.hold.visible.print",
4036 desc => 'Returns printable output for the set of opt-in visible holds',
4038 { desc => 'Authentication token', type => 'string'},
4039 { desc => 'User ID. If no user id is present, the authenticated user is assumed', type => 'number' },
4040 { desc => 'Options hash. Supported fields are "limit" and "offset"', type => 'object' },
4043 desc => q/An action_trigger.event object or error event./,
4049 __PACKAGE__->register_method(
4050 method => "user_visible_circs",
4051 api_name => "open-ils.actor.history.hold.visible.email",
4054 desc => 'Emails the set of opt-in visible holds to the requestor',
4056 { desc => 'Authentication token', type => 'string'},
4057 { desc => 'User ID. If no user id is present, the authenticated user is assumed', type => 'number' },
4058 { desc => 'Options hash. Supported fields are "limit" and "offset"', type => 'object' },
4061 desc => q/undef, or event on error/
4066 sub user_visible_circs {
4067 my($self, $conn, $auth, $user_id, $options) = @_;
4069 my $is_hold = ($self->api_name =~ /hold/);
4070 my $for_print = ($self->api_name =~ /print/);
4071 my $for_email = ($self->api_name =~ /email/);
4072 my $e = new_editor(authtoken => $auth);
4073 return $e->event unless $e->checkauth;
4075 $user_id ||= $e->requestor->id;
4077 $options->{limit} ||= 50;
4078 $options->{offset} ||= 0;
4080 if($user_id != $e->requestor->id) {
4081 my $perm = ($is_hold) ? 'VIEW_HOLD' : 'VIEW_CIRCULATIONS';
4082 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
4083 return $e->event unless $e->allowed($perm, $user->home_ou);
4086 my $db_func = ($is_hold) ? 'action.usr_visible_holds' : 'action.usr_visible_circs';
4088 my $data = $e->json_query({
4089 from => [$db_func, $user_id],
4090 limit => $$options{limit},
4091 offset => $$options{offset}
4093 # TODO: I only want IDs. code below didn't get me there
4094 # {"select":{"au":[{"column":"id", "result_field":"id",
4095 # "transform":"action.usr_visible_circs"}]}, "where":{"id":10}, "from":"au"}
4100 return undef unless @$data;
4104 # collect the batch of objects
4108 my $hold_list = $e->search_action_hold_request({id => [map { $_->{id} } @$data]});
4109 return $U->fire_object_event(undef, 'ahr.format.history.print', $hold_list, $$hold_list[0]->request_lib);
4113 my $circ_list = $e->search_action_circulation({id => [map { $_->{id} } @$data]});
4114 return $U->fire_object_event(undef, 'circ.format.history.print', $circ_list, $$circ_list[0]->circ_lib);
4117 } elsif ($for_email) {
4119 $conn->respond_complete(1) if $for_email; # no sense in waiting
4127 my $hold = $e->retrieve_action_hold_request($id);
4128 $U->create_events_for_hook('ahr.format.history.email', $hold, $hold->request_lib, undef, undef, 1);
4129 # events will be fired from action_trigger_runner
4133 my $circ = $e->retrieve_action_circulation($id);
4134 $U->create_events_for_hook('circ.format.history.email', $circ, $circ->circ_lib, undef, undef, 1);
4135 # events will be fired from action_trigger_runner
4139 } else { # just give me the data please
4147 my $hold = $e->retrieve_action_hold_request($id);
4148 $conn->respond({hold => $hold});
4152 my $circ = $e->retrieve_action_circulation($id);
4155 summary => $U->create_circ_chain_summary($e, $id)
4164 __PACKAGE__->register_method(
4165 method => "user_saved_search_cud",
4166 api_name => "open-ils.actor.user.saved_search.cud",
4169 desc => 'Create/Update/Delete Access to user saved searches',
4171 { desc => 'Authentication token', type => 'string' },
4172 { desc => 'Saved Search Object', type => 'object', class => 'auss' }
4175 desc => q/The retrieved or updated saved search object, or id of a deleted object; Event on error/,
4181 __PACKAGE__->register_method(
4182 method => "user_saved_search_cud",
4183 api_name => "open-ils.actor.user.saved_search.retrieve",
4186 desc => 'Retrieve a saved search object',
4188 { desc => 'Authentication token', type => 'string' },
4189 { desc => 'Saved Search ID', type => 'number' }
4192 desc => q/The saved search object, Event on error/,
4198 sub user_saved_search_cud {
4199 my( $self, $client, $auth, $search ) = @_;
4200 my $e = new_editor( authtoken=>$auth );
4201 return $e->die_event unless $e->checkauth;
4203 my $o_search; # prior version of the object, if any
4204 my $res; # to be returned
4206 # branch on the operation type
4208 if( $self->api_name =~ /retrieve/ ) { # Retrieve
4210 # Get the old version, to check ownership
4211 $o_search = $e->retrieve_actor_usr_saved_search( $search )
4212 or return $e->die_event;
4214 # You can't read somebody else's search
4215 return OpenILS::Event->new('BAD_PARAMS')
4216 unless $o_search->owner == $e->requestor->id;
4222 $e->xact_begin; # start an editor transaction
4224 if( $search->isnew ) { # Create
4226 # You can't create a search for somebody else
4227 return OpenILS::Event->new('BAD_PARAMS')
4228 unless $search->owner == $e->requestor->id;
4230 $e->create_actor_usr_saved_search( $search )
4231 or return $e->die_event;
4235 } elsif( $search->ischanged ) { # Update
4237 # You can't change ownership of a search
4238 return OpenILS::Event->new('BAD_PARAMS')
4239 unless $search->owner == $e->requestor->id;
4241 # Get the old version, to check ownership
4242 $o_search = $e->retrieve_actor_usr_saved_search( $search->id )
4243 or return $e->die_event;
4245 # You can't update somebody else's search
4246 return OpenILS::Event->new('BAD_PARAMS')
4247 unless $o_search->owner == $e->requestor->id;
4250 $e->update_actor_usr_saved_search( $search )
4251 or return $e->die_event;
4255 } elsif( $search->isdeleted ) { # Delete
4257 # Get the old version, to check ownership
4258 $o_search = $e->retrieve_actor_usr_saved_search( $search->id )
4259 or return $e->die_event;
4261 # You can't delete somebody else's search
4262 return OpenILS::Event->new('BAD_PARAMS')
4263 unless $o_search->owner == $e->requestor->id;
4266 $e->delete_actor_usr_saved_search( $o_search )
4267 or return $e->die_event;
4278 __PACKAGE__->register_method(
4279 method => "get_barcodes",
4280 api_name => "open-ils.actor.get_barcodes"
4284 my( $self, $client, $auth, $org_id, $context, $barcode ) = @_;
4285 my $e = new_editor(authtoken => $auth);
4286 return $e->event unless $e->checkauth;
4287 return $e->event unless $e->allowed('STAFF_LOGIN', $org_id);
4289 my $db_result = $e->json_query(
4291 'evergreen.get_barcodes',
4292 $org_id, $context, $barcode,
4296 if($context =~ /actor/) {
4297 my $filter_result = ();
4299 foreach my $result (@$db_result) {
4300 if($result->{type} eq 'actor') {
4301 if($e->requestor->id != $result->{id}) {
4302 $patron = $e->retrieve_actor_user($result->{id});
4304 push(@$filter_result, $e->event);
4307 if($e->allowed('VIEW_USER', $patron->home_ou)) {
4308 push(@$filter_result, $result);
4311 push(@$filter_result, $e->event);
4315 push(@$filter_result, $result);
4319 push(@$filter_result, $result);
4322 return $filter_result;