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;
1321 __PACKAGE__->register_method(
1322 method => "check_user_perms",
1323 api_name => "open-ils.actor.user.perm.check",
1324 notes => <<" NOTES");
1325 Takes a login session, user id, an org id, and an array of perm type strings. For each
1326 perm type, if the user does *not* have the given permission it is added
1327 to a list which is returned from the method. If all permissions
1328 are allowed, an empty list is returned
1329 if the logged in user does not match 'user_id', then the logged in user must
1330 have VIEW_PERMISSION priveleges.
1333 sub check_user_perms {
1334 my( $self, $client, $login_session, $user_id, $org_id, $perm_types ) = @_;
1336 my( $staff, $evt ) = $apputils->checkses($login_session);
1337 return $evt if $evt;
1339 if($staff->id ne $user_id) {
1340 if( $evt = $apputils->check_perms(
1341 $staff->id, $org_id, 'VIEW_PERMISSION') ) {
1347 for my $perm (@$perm_types) {
1348 if($apputils->check_perms($user_id, $org_id, $perm)) {
1349 push @not_allowed, $perm;
1353 return \@not_allowed
1356 __PACKAGE__->register_method(
1357 method => "check_user_perms2",
1358 api_name => "open-ils.actor.user.perm.check.multi_org",
1360 Checks the permissions on a list of perms and orgs for a user
1361 @param authtoken The login session key
1362 @param user_id The id of the user to check
1363 @param orgs The array of org ids
1364 @param perms The array of permission names
1365 @return An array of [ orgId, permissionName ] arrays that FAILED the check
1366 if the logged in user does not match 'user_id', then the logged in user must
1367 have VIEW_PERMISSION priveleges.
1370 sub check_user_perms2 {
1371 my( $self, $client, $authtoken, $user_id, $orgs, $perms ) = @_;
1373 my( $staff, $target, $evt ) = $apputils->checkses_requestor(
1374 $authtoken, $user_id, 'VIEW_PERMISSION' );
1375 return $evt if $evt;
1378 for my $org (@$orgs) {
1379 for my $perm (@$perms) {
1380 if($apputils->check_perms($user_id, $org, $perm)) {
1381 push @not_allowed, [ $org, $perm ];
1386 return \@not_allowed
1390 __PACKAGE__->register_method(
1391 method => 'check_user_perms3',
1392 api_name => 'open-ils.actor.user.perm.highest_org',
1394 Returns the highest org unit id at which a user has a given permission
1395 If the requestor does not match the target user, the requestor must have
1396 'VIEW_PERMISSION' rights at the home org unit of the target user
1397 @param authtoken The login session key
1398 @param userid The id of the user in question
1399 @param perm The permission to check
1400 @return The org unit highest in the org tree within which the user has
1401 the requested permission
1404 sub check_user_perms3 {
1405 my($self, $client, $authtoken, $user_id, $perm) = @_;
1406 my $e = new_editor(authtoken=>$authtoken);
1407 return $e->event unless $e->checkauth;
1409 my $tree = $U->get_org_tree();
1411 unless($e->requestor->id == $user_id) {
1412 my $user = $e->retrieve_actor_user($user_id)
1413 or return $e->event;
1414 return $e->event unless $e->allowed('VIEW_PERMISSION', $user->home_ou);
1415 return $U->find_highest_perm_org($perm, $user_id, $user->home_ou, $tree );
1418 return $U->find_highest_perm_org($perm, $user_id, $e->requestor->ws_ou, $tree);
1421 __PACKAGE__->register_method(
1422 method => 'user_has_work_perm_at',
1423 api_name => 'open-ils.actor.user.has_work_perm_at',
1427 Returns a set of org unit IDs which represent the highest orgs in
1428 the org tree where the user has the requested permission. The
1429 purpose of this method is to return the smallest set of org units
1430 which represent the full expanse of the user's ability to perform
1431 the requested action. The user whose perms this method should
1432 check is implied by the authtoken. /,
1434 {desc => 'authtoken', type => 'string'},
1435 {desc => 'permission name', type => 'string'},
1436 {desc => q/user id, optional. If present, check perms for
1437 this user instead of the logged in user/, type => 'number'},
1439 return => {desc => 'An array of org IDs'}
1443 sub user_has_work_perm_at {
1444 my($self, $conn, $auth, $perm, $user_id) = @_;
1445 my $e = new_editor(authtoken=>$auth);
1446 return $e->event unless $e->checkauth;
1447 if(defined $user_id) {
1448 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
1449 return $e->event unless $e->allowed('VIEW_PERMISSION', $user->home_ou);
1451 return $U->user_has_work_perm_at($e, $perm, undef, $user_id);
1454 __PACKAGE__->register_method(
1455 method => 'user_has_work_perm_at_batch',
1456 api_name => 'open-ils.actor.user.has_work_perm_at.batch',
1460 sub user_has_work_perm_at_batch {
1461 my($self, $conn, $auth, $perms, $user_id) = @_;
1462 my $e = new_editor(authtoken=>$auth);
1463 return $e->event unless $e->checkauth;
1464 if(defined $user_id) {
1465 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
1466 return $e->event unless $e->allowed('VIEW_PERMISSION', $user->home_ou);
1469 $map->{$_} = $U->user_has_work_perm_at($e, $_) for @$perms;
1475 __PACKAGE__->register_method(
1476 method => 'check_user_perms4',
1477 api_name => 'open-ils.actor.user.perm.highest_org.batch',
1479 Returns the highest org unit id at which a user has a given permission
1480 If the requestor does not match the target user, the requestor must have
1481 'VIEW_PERMISSION' rights at the home org unit of the target user
1482 @param authtoken The login session key
1483 @param userid The id of the user in question
1484 @param perms An array of perm names to check
1485 @return An array of orgId's representing the org unit
1486 highest in the org tree within which the user has the requested permission
1487 The arrah of orgId's has matches the order of the perms array
1490 sub check_user_perms4 {
1491 my( $self, $client, $authtoken, $userid, $perms ) = @_;
1493 my( $staff, $target, $org, $evt );
1495 ( $staff, $target, $evt ) = $apputils->checkses_requestor(
1496 $authtoken, $userid, 'VIEW_PERMISSION' );
1497 return $evt if $evt;
1500 return [] unless ref($perms);
1501 my $tree = $U->get_org_tree();
1503 for my $p (@$perms) {
1504 push( @arr, $U->find_highest_perm_org( $p, $userid, $target->home_ou, $tree ) );
1510 __PACKAGE__->register_method(
1511 method => "user_fines_summary",
1512 api_name => "open-ils.actor.user.fines.summary",
1515 desc => 'Returns a short summary of the users total open fines, ' .
1516 'excluding voided fines Params are login_session, user_id' ,
1518 {desc => 'Authentication token', type => 'string'},
1519 {desc => 'User ID', type => 'string'} # number?
1522 desc => "a 'mous' object, event on error",
1527 sub user_fines_summary {
1528 my( $self, $client, $auth, $user_id ) = @_;
1530 my $e = new_editor(authtoken=>$auth);
1531 return $e->event unless $e->checkauth;
1533 if( $user_id ne $e->requestor->id ) {
1534 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
1535 return $e->event unless
1536 $e->allowed('VIEW_USER_FINES_SUMMARY', $user->home_ou);
1539 return $e->search_money_open_user_summary({usr => $user_id})->[0];
1543 __PACKAGE__->register_method(
1544 method => "user_opac_vitals",
1545 api_name => "open-ils.actor.user.opac.vital_stats",
1549 desc => 'Returns a short summary of the users vital stats, including ' .
1550 'identification information, accumulated balance, number of holds, ' .
1551 'and current open circulation stats' ,
1553 {desc => 'Authentication token', type => 'string'},
1554 {desc => 'Optional User ID, for use in the staff client', type => 'number'} # number?
1557 desc => "An object with four properties: user, fines, checkouts and holds."
1562 sub user_opac_vitals {
1563 my( $self, $client, $auth, $user_id ) = @_;
1565 my $e = new_editor(authtoken=>$auth);
1566 return $e->event unless $e->checkauth;
1568 $user_id ||= $e->requestor->id;
1570 my $user = $e->retrieve_actor_user( $user_id );
1573 ->method_lookup('open-ils.actor.user.fines.summary')
1574 ->run($auth => $user_id);
1575 return $fines if (defined($U->event_code($fines)));
1578 $fines = new Fieldmapper::money::open_user_summary ();
1579 $fines->balance_owed(0.00);
1580 $fines->total_owed(0.00);
1581 $fines->total_paid(0.00);
1582 $fines->usr($user_id);
1586 ->method_lookup('open-ils.actor.user.hold_requests.count')
1587 ->run($auth => $user_id);
1588 return $holds if (defined($U->event_code($holds)));
1591 ->method_lookup('open-ils.actor.user.checked_out.count')
1592 ->run($auth => $user_id);
1593 return $out if (defined($U->event_code($out)));
1595 $out->{"total_out"} = reduce { $a + $out->{$b} } 0, qw/out overdue long_overdue/;
1599 first_given_name => $user->first_given_name,
1600 second_given_name => $user->second_given_name,
1601 family_name => $user->family_name,
1602 alias => $user->alias,
1603 usrname => $user->usrname
1605 fines => $fines->to_bare_hash,
1612 ##### a small consolidation of related method registrations
1613 my $common_params = [
1614 { desc => 'Authentication token', type => 'string' },
1615 { desc => 'User ID', type => 'string' },
1616 { desc => 'Transactions type (optional, defaults to all)', type => 'string' },
1617 { desc => 'Options hash. May contain limit and offset for paged results.', type => 'object' },
1620 'open-ils.actor.user.transactions' => '',
1621 'open-ils.actor.user.transactions.fleshed' => '',
1622 'open-ils.actor.user.transactions.have_charge' => ' that have an initial charge',
1623 'open-ils.actor.user.transactions.have_charge.fleshed' => ' that have an initial charge',
1624 'open-ils.actor.user.transactions.have_balance' => ' that have an outstanding balance',
1625 'open-ils.actor.user.transactions.have_balance.fleshed' => ' that have an outstanding balance',
1628 foreach (keys %methods) {
1630 method => "user_transactions",
1633 desc => 'For a given user, retrieve a list of '
1634 . (/\.fleshed/ ? 'fleshed ' : '')
1635 . 'transactions' . $methods{$_}
1636 . ' optionally limited to transactions of a given type.',
1637 params => $common_params,
1639 desc => "List of objects, or event on error. Each object is a hash containing: transaction, circ, record. "
1640 . 'These represent the relevant (mbts) transaction, attached circulation and title pointed to in the circ, respectively.',
1644 /\.have_balance/ and $args{authoritative} = 1; # FIXME: I don't know why have_charge isn't authoritative
1645 __PACKAGE__->register_method(%args);
1648 # Now for the counts
1650 'open-ils.actor.user.transactions.count' => '',
1651 'open-ils.actor.user.transactions.have_charge.count' => ' that have an initial charge',
1652 'open-ils.actor.user.transactions.have_balance.count' => ' that have an outstanding balance',
1655 foreach (keys %methods) {
1657 method => "user_transactions",
1660 desc => 'For a given user, retrieve a count of open '
1661 . 'transactions' . $methods{$_}
1662 . ' optionally limited to transactions of a given type.',
1663 params => $common_params,
1664 return => { desc => "Integer count of transactions, or event on error" }
1667 /\.have_balance/ and $args{authoritative} = 1; # FIXME: I don't know why have_charge isn't authoritative
1668 __PACKAGE__->register_method(%args);
1671 __PACKAGE__->register_method(
1672 method => "user_transactions",
1673 api_name => "open-ils.actor.user.transactions.have_balance.total",
1676 desc => 'For a given user, retrieve the total balance owed for open transactions,'
1677 . ' optionally limited to transactions of a given type.',
1678 params => $common_params,
1679 return => { desc => "Decimal balance value, or event on error" }
1684 sub user_transactions {
1685 my( $self, $client, $login_session, $user_id, $type, $options ) = @_;
1688 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1689 $login_session, $user_id, 'VIEW_USER_TRANSACTIONS' );
1690 return $evt if $evt;
1692 my $api = $self->api_name();
1694 my $filter = ($api =~ /have_balance/o) ?
1695 { 'balance_owed' => { '<>' => 0 } }:
1696 { 'total_owed' => { '>' => 0 } };
1698 my ($trans) = $self->method_lookup(
1699 'open-ils.actor.user.transactions.history.still_open')
1700 ->run( $login_session, $user_id, $type, $filter, $options );
1702 if($api =~ /total/o) {
1704 for my $t (@$trans) {
1705 $total += $t->balance_owed;
1708 $logger->debug("Total balance owed by user $user_id: $total");
1712 ($api =~ /count/o ) and return scalar @$trans;
1713 ($api !~ /fleshed/o) and return $trans;
1716 for my $t (@$trans) {
1718 if( $t->xact_type ne 'circulation' ) {
1719 push @resp, {transaction => $t};
1723 my $circ = $apputils->simple_scalar_request(
1725 "open-ils.cstore.direct.action.circulation.retrieve",
1730 my $title = $apputils->simple_scalar_request(
1732 "open-ils.storage.fleshed.biblio.record_entry.retrieve_by_copy",
1733 $circ->target_copy );
1737 my $u = OpenILS::Utils::ModsParser->new();
1738 $u->start_mods_batch($title->marc());
1739 my $mods = $u->finish_mods_batch();
1740 $mods->doc_id($title->id) if $mods;
1742 push @resp, {transaction => $t, circ => $circ, record => $mods };
1750 __PACKAGE__->register_method(
1751 method => "user_transaction_retrieve",
1752 api_name => "open-ils.actor.user.transaction.fleshed.retrieve",
1755 notes => "Returns a fleshed transaction record"
1758 __PACKAGE__->register_method(
1759 method => "user_transaction_retrieve",
1760 api_name => "open-ils.actor.user.transaction.retrieve",
1763 notes => "Returns a transaction record"
1766 sub user_transaction_retrieve {
1767 my($self, $client, $auth, $bill_id) = @_;
1769 my $e = new_editor(authtoken => $auth);
1770 return $e->event unless $e->checkauth;
1772 my $trans = $e->retrieve_money_billable_transaction_summary(
1773 [$bill_id, {flesh => 1, flesh_fields => {mbts => ['usr']}}]) or return $e->event;
1775 return $e->event unless $e->allowed('VIEW_USER_TRANSACTIONS', $trans->usr->home_ou);
1777 $trans->usr($trans->usr->id); # de-flesh for backwards compat
1779 return $trans unless $self->api_name =~ /flesh/;
1780 return {transaction => $trans} if $trans->xact_type ne 'circulation';
1782 my $circ = $e->retrieve_action_circulation([
1786 circ => ['target_copy'],
1787 acp => ['call_number'],
1794 my $copy = $circ->target_copy;
1796 if($circ->target_copy->call_number->id == OILS_PRECAT_CALL_NUMBER) {
1797 $mods = new Fieldmapper::metabib::virtual_record;
1798 $mods->doc_id(OILS_PRECAT_RECORD);
1799 $mods->title($copy->dummy_title);
1800 $mods->author($copy->dummy_author);
1803 my $u = OpenILS::Utils::ModsParser->new();
1804 $u->start_mods_batch($circ->target_copy->call_number->record->marc);
1805 $mods = $u->finish_mods_batch();
1809 $circ->target_copy($circ->target_copy->id);
1810 $copy->call_number($copy->call_number->id);
1812 return {transaction => $trans, circ => $circ, record => $mods, copy => $copy };
1816 __PACKAGE__->register_method(
1817 method => "hold_request_count",
1818 api_name => "open-ils.actor.user.hold_requests.count",
1821 notes => 'Returns hold ready/total counts'
1824 sub hold_request_count {
1825 my( $self, $client, $login_session, $userid ) = @_;
1827 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1828 $login_session, $userid, 'VIEW_HOLD' );
1829 return $evt if $evt;
1832 my $holds = $apputils->simple_scalar_request(
1834 "open-ils.cstore.direct.action.hold_request.search.atomic",
1837 fulfillment_time => {"=" => undef },
1838 cancel_time => undef,
1843 for my $h (@$holds) {
1844 next unless $h->capture_time and $h->current_copy;
1846 my $copy = $apputils->simple_scalar_request(
1848 "open-ils.cstore.direct.asset.copy.retrieve",
1852 if ($copy and $copy->status == 8) {
1857 return { total => scalar(@$holds), ready => scalar(@ready) };
1860 __PACKAGE__->register_method(
1861 method => "checked_out",
1862 api_name => "open-ils.actor.user.checked_out",
1866 desc => "For a given user, returns a structure of circulations objects sorted by out, overdue, lost, claims_returned, long_overdue. "
1867 . "A list of IDs are returned of each type. Circs marked lost, long_overdue, and claims_returned will not be 'finished' "
1868 . "(i.e., outstanding balance or some other pending action on the circ). "
1869 . "The .count method also includes a 'total' field which sums all open circs.",
1871 { desc => 'Authentication Token', type => 'string'},
1872 { desc => 'User ID', type => 'string'},
1875 desc => 'Returns event on error, or an object with ID lists, like: '
1876 . '{"out":[12552,451232], "claims_returned":[], "long_overdue":[23421] "overdue":[], "lost":[]}'
1881 __PACKAGE__->register_method(
1882 method => "checked_out",
1883 api_name => "open-ils.actor.user.checked_out.count",
1886 signature => q/@see open-ils.actor.user.checked_out/
1890 my( $self, $conn, $auth, $userid ) = @_;
1892 my $e = new_editor(authtoken=>$auth);
1893 return $e->event unless $e->checkauth;
1895 if( $userid ne $e->requestor->id ) {
1896 my $user = $e->retrieve_actor_user($userid) or return $e->event;
1897 unless($e->allowed('VIEW_CIRCULATIONS', $user->home_ou)) {
1899 # see if there is a friend link allowing circ.view perms
1900 my $allowed = OpenILS::Application::Actor::Friends->friend_perm_allowed(
1901 $e, $userid, $e->requestor->id, 'circ.view');
1902 return $e->event unless $allowed;
1906 my $count = $self->api_name =~ /count/;
1907 return _checked_out( $count, $e, $userid );
1911 my( $iscount, $e, $userid ) = @_;
1917 claims_returned => [],
1920 my $meth = 'retrieve_action_open_circ_';
1928 claims_returned => 0,
1935 my $data = $e->$meth($userid);
1939 $result{$_} += $data->$_() for (keys %result);
1940 $result{total} += $data->$_() for (keys %result);
1942 for my $k (keys %result) {
1943 $result{$k} = [ grep { $_ > 0 } split( ',', $data->$k()) ];
1953 __PACKAGE__->register_method(
1954 method => "checked_in_with_fines",
1955 api_name => "open-ils.actor.user.checked_in_with_fines",
1958 signature => q/@see open-ils.actor.user.checked_out/
1961 sub checked_in_with_fines {
1962 my( $self, $conn, $auth, $userid ) = @_;
1964 my $e = new_editor(authtoken=>$auth);
1965 return $e->event unless $e->checkauth;
1967 if( $userid ne $e->requestor->id ) {
1968 return $e->event unless $e->allowed('VIEW_CIRCULATIONS');
1971 # money is owed on these items and they are checked in
1972 my $open = $e->search_action_circulation(
1975 xact_finish => undef,
1976 checkin_time => { "!=" => undef },
1981 my( @lost, @cr, @lo );
1982 for my $c (@$open) {
1983 push( @lost, $c->id ) if $c->stop_fines eq 'LOST';
1984 push( @cr, $c->id ) if $c->stop_fines eq 'CLAIMSRETURNED';
1985 push( @lo, $c->id ) if $c->stop_fines eq 'LONGOVERDUE';
1990 claims_returned => \@cr,
1991 long_overdue => \@lo
1997 my ($api, $desc, $auth) = @_;
1998 $desc = $desc ? (" " . $desc) : '';
1999 my $ids = ($api =~ /ids$/) ? 1 : 0;
2002 method => "user_transaction_history",
2003 api_name => "open-ils.actor.user.transactions.$api",
2005 desc => "For a given User ID, returns a list of billable transaction" .
2006 ($ids ? " id" : '') .
2007 "s$desc, optionally filtered by type and/or fields in money.billable_xact_summary. " .
2008 "The VIEW_USER_TRANSACTIONS permission is required to view another user's transactions",
2010 {desc => 'Authentication token', type => 'string'},
2011 {desc => 'User ID', type => 'number'},
2012 {desc => 'Transaction type (optional)', type => 'number'},
2013 {desc => 'Hash of Billable Transaction Summary filters (optional)', type => 'object'}
2016 desc => 'List of transaction' . ($ids ? " id" : '') . 's, Event on error'
2020 $auth and push @sig, (authoritative => 1);
2024 my %hist_methods = (
2026 'history.have_charge' => 'that have an initial charge',
2027 'history.still_open' => 'that are not finished',
2029 my %auth_hist_methods = (
2030 'history.have_balance' => 'that have a balance',
2031 'history.have_bill' => 'that have billings',
2032 'history.have_bill_or_payment' => 'that have non-zero-sum billings or at least 1 payment',
2034 foreach (keys %hist_methods) {
2035 __PACKAGE__->register_method(_sigmaker($_, $hist_methods{$_}));
2036 __PACKAGE__->register_method(_sigmaker("$_.ids", $hist_methods{$_}));
2038 foreach (keys %auth_hist_methods) {
2039 __PACKAGE__->register_method(_sigmaker($_, $auth_hist_methods{$_}, 1));
2040 __PACKAGE__->register_method(_sigmaker("$_.ids", $auth_hist_methods{$_}, 1));
2043 sub user_transaction_history {
2044 my( $self, $conn, $auth, $userid, $type, $filter, $options ) = @_;
2048 my $e = new_editor(authtoken=>$auth);
2049 return $e->die_event unless $e->checkauth;
2051 if ($e->requestor->id ne $userid) {
2052 return $e->die_event unless $e->allowed('VIEW_USER_TRANSACTIONS');
2055 my $api = $self->api_name;
2056 my @xact_finish = (xact_finish => undef ) if ($api =~ /history\.still_open$/); # What about history.still_open.ids?
2058 if(defined($type)) {
2059 $filter->{'xact_type'} = $type;
2062 if($api =~ /have_bill_or_payment/o) {
2064 # transactions that have a non-zero sum across all billings or at least 1 payment
2065 $filter->{'-or'} = {
2066 'balance_owed' => { '<>' => 0 },
2067 'last_payment_ts' => { '<>' => undef }
2070 } elsif( $api =~ /have_balance/o) {
2072 # transactions that have a non-zero overall balance
2073 $filter->{'balance_owed'} = { '<>' => 0 };
2075 } elsif( $api =~ /have_charge/o) {
2077 # transactions that have at least 1 billing, regardless of whether it was voided
2078 $filter->{'last_billing_ts'} = { '<>' => undef };
2080 } elsif( $api =~ /have_bill/o) { # needs to be an elsif, or we double-match have_bill_or_payment!
2082 # transactions that have non-zero sum across all billings. This will exclude
2083 # xacts where all billings have been voided
2084 $filter->{'total_owed'} = { '<>' => 0 };
2087 my $options_clause = { order_by => { mbt => 'xact_start DESC' } };
2088 $options_clause->{'limit'} = $options->{'limit'} if $options->{'limit'};
2089 $options_clause->{'offset'} = $options->{'offset'} if $options->{'offset'};
2091 my $mbts = $e->search_money_billable_transaction_summary(
2093 { usr => $userid, @xact_finish, %$filter },
2098 if ($api =~ /\.ids/) {
2099 return [map {$_->id} @$mbts];
2107 __PACKAGE__->register_method(
2108 method => "user_perms",
2109 api_name => "open-ils.actor.permissions.user_perms.retrieve",
2111 notes => "Returns a list of permissions"
2115 my( $self, $client, $authtoken, $user ) = @_;
2117 my( $staff, $evt ) = $apputils->checkses($authtoken);
2118 return $evt if $evt;
2120 $user ||= $staff->id;
2122 if( $user != $staff->id and $evt = $apputils->check_perms( $staff->id, $staff->home_ou, 'VIEW_PERMISSION') ) {
2126 return $apputils->simple_scalar_request(
2128 "open-ils.storage.permission.user_perms.atomic",
2132 __PACKAGE__->register_method(
2133 method => "retrieve_perms",
2134 api_name => "open-ils.actor.permissions.retrieve",
2135 notes => "Returns a list of permissions"
2137 sub retrieve_perms {
2138 my( $self, $client ) = @_;
2139 return $apputils->simple_scalar_request(
2141 "open-ils.cstore.direct.permission.perm_list.search.atomic",
2142 { id => { '!=' => undef } }
2146 __PACKAGE__->register_method(
2147 method => "retrieve_groups",
2148 api_name => "open-ils.actor.groups.retrieve",
2149 notes => "Returns a list of user groups"
2151 sub retrieve_groups {
2152 my( $self, $client ) = @_;
2153 return new_editor()->retrieve_all_permission_grp_tree();
2156 __PACKAGE__->register_method(
2157 method => "retrieve_org_address",
2158 api_name => "open-ils.actor.org_unit.address.retrieve",
2159 notes => <<' NOTES');
2160 Returns an org_unit address by ID
2161 @param An org_address ID
2163 sub retrieve_org_address {
2164 my( $self, $client, $id ) = @_;
2165 return $apputils->simple_scalar_request(
2167 "open-ils.cstore.direct.actor.org_address.retrieve",
2172 __PACKAGE__->register_method(
2173 method => "retrieve_groups_tree",
2174 api_name => "open-ils.actor.groups.tree.retrieve",
2175 notes => "Returns a list of user groups"
2178 sub retrieve_groups_tree {
2179 my( $self, $client ) = @_;
2180 return new_editor()->search_permission_grp_tree(
2185 flesh_fields => { pgt => ["children"] },
2186 order_by => { pgt => 'name'}
2193 __PACKAGE__->register_method(
2194 method => "add_user_to_groups",
2195 api_name => "open-ils.actor.user.set_groups",
2196 notes => "Adds a user to one or more permission groups"
2199 sub add_user_to_groups {
2200 my( $self, $client, $authtoken, $userid, $groups ) = @_;
2202 my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2203 $authtoken, $userid, 'CREATE_USER_GROUP_LINK' );
2204 return $evt if $evt;
2206 ( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2207 $authtoken, $userid, 'REMOVE_USER_GROUP_LINK' );
2208 return $evt if $evt;
2210 $apputils->simplereq(
2212 'open-ils.storage.direct.permission.usr_grp_map.mass_delete', { usr => $userid } );
2214 for my $group (@$groups) {
2215 my $link = Fieldmapper::permission::usr_grp_map->new;
2217 $link->usr($userid);
2219 my $id = $apputils->simplereq(
2221 'open-ils.storage.direct.permission.usr_grp_map.create', $link );
2227 __PACKAGE__->register_method(
2228 method => "get_user_perm_groups",
2229 api_name => "open-ils.actor.user.get_groups",
2230 notes => "Retrieve a user's permission groups."
2234 sub get_user_perm_groups {
2235 my( $self, $client, $authtoken, $userid ) = @_;
2237 my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2238 $authtoken, $userid, 'VIEW_PERM_GROUPS' );
2239 return $evt if $evt;
2241 return $apputils->simplereq(
2243 'open-ils.cstore.direct.permission.usr_grp_map.search.atomic', { usr => $userid } );
2247 __PACKAGE__->register_method(
2248 method => "get_user_work_ous",
2249 api_name => "open-ils.actor.user.get_work_ous",
2250 notes => "Retrieve a user's work org units."
2253 __PACKAGE__->register_method(
2254 method => "get_user_work_ous",
2255 api_name => "open-ils.actor.user.get_work_ous.ids",
2256 notes => "Retrieve a user's work org units."
2259 sub get_user_work_ous {
2260 my( $self, $client, $auth, $userid ) = @_;
2261 my $e = new_editor(authtoken=>$auth);
2262 return $e->event unless $e->checkauth;
2263 $userid ||= $e->requestor->id;
2265 if($e->requestor->id != $userid) {
2266 my $user = $e->retrieve_actor_user($userid)
2267 or return $e->event;
2268 return $e->event unless $e->allowed('ASSIGN_WORK_ORG_UNIT', $user->home_ou);
2271 return $e->search_permission_usr_work_ou_map({usr => $userid})
2272 unless $self->api_name =~ /.ids$/;
2274 # client just wants a list of org IDs
2275 return $U->get_user_work_ou_ids($e, $userid);
2280 __PACKAGE__->register_method(
2281 method => 'register_workstation',
2282 api_name => 'open-ils.actor.workstation.register.override',
2283 signature => q/@see open-ils.actor.workstation.register/
2286 __PACKAGE__->register_method(
2287 method => 'register_workstation',
2288 api_name => 'open-ils.actor.workstation.register',
2290 Registers a new workstion in the system
2291 @param authtoken The login session key
2292 @param name The name of the workstation id
2293 @param owner The org unit that owns this workstation
2294 @return The workstation id on success, WORKSTATION_NAME_EXISTS
2295 if the name is already in use.
2299 sub register_workstation {
2300 my( $self, $conn, $authtoken, $name, $owner ) = @_;
2302 my $e = new_editor(authtoken=>$authtoken, xact=>1);
2303 return $e->die_event unless $e->checkauth;
2304 return $e->die_event unless $e->allowed('REGISTER_WORKSTATION', $owner);
2305 my $existing = $e->search_actor_workstation({name => $name})->[0];
2309 if( $self->api_name =~ /override/o ) {
2310 # workstation with the given name exists.
2312 if($owner ne $existing->owning_lib) {
2313 # if necessary, update the owning_lib of the workstation
2315 $logger->info("changing owning lib of workstation ".$existing->id.
2316 " from ".$existing->owning_lib." to $owner");
2317 return $e->die_event unless
2318 $e->allowed('UPDATE_WORKSTATION', $existing->owning_lib);
2320 return $e->die_event unless $e->allowed('UPDATE_WORKSTATION', $owner);
2322 $existing->owning_lib($owner);
2323 return $e->die_event unless $e->update_actor_workstation($existing);
2329 "attempt to register an existing workstation. returning existing ID");
2332 return $existing->id;
2335 return OpenILS::Event->new('WORKSTATION_NAME_EXISTS')
2339 my $ws = Fieldmapper::actor::workstation->new;
2340 $ws->owning_lib($owner);
2342 $e->create_actor_workstation($ws) or return $e->die_event;
2344 return $ws->id; # note: editor sets the id on the new object for us
2347 __PACKAGE__->register_method(
2348 method => 'workstation_list',
2349 api_name => 'open-ils.actor.workstation.list',
2351 Returns a list of workstations registered at the given location
2352 @param authtoken The login session key
2353 @param ids A list of org_unit.id's for the workstation owners
2357 sub workstation_list {
2358 my( $self, $conn, $authtoken, @orgs ) = @_;
2360 my $e = new_editor(authtoken=>$authtoken);
2361 return $e->event unless $e->checkauth;
2366 unless $e->allowed('REGISTER_WORKSTATION', $o);
2367 $results{$o} = $e->search_actor_workstation({owning_lib=>$o});
2373 __PACKAGE__->register_method(
2374 method => 'fetch_patron_note',
2375 api_name => 'open-ils.actor.note.retrieve.all',
2378 Returns a list of notes for a given user
2379 Requestor must have VIEW_USER permission if pub==false and
2380 @param authtoken The login session key
2381 @param args Hash of params including
2382 patronid : the patron's id
2383 pub : true if retrieving only public notes
2387 sub fetch_patron_note {
2388 my( $self, $conn, $authtoken, $args ) = @_;
2389 my $patronid = $$args{patronid};
2391 my($reqr, $evt) = $U->checkses($authtoken);
2392 return $evt if $evt;
2395 ($patron, $evt) = $U->fetch_user($patronid);
2396 return $evt if $evt;
2399 if( $patronid ne $reqr->id ) {
2400 $evt = $U->check_perms($reqr->id, $patron->home_ou, 'VIEW_USER');
2401 return $evt if $evt;
2403 return $U->cstorereq(
2404 'open-ils.cstore.direct.actor.usr_note.search.atomic',
2405 { usr => $patronid, pub => 't' } );
2408 $evt = $U->check_perms($reqr->id, $patron->home_ou, 'VIEW_USER');
2409 return $evt if $evt;
2411 return $U->cstorereq(
2412 'open-ils.cstore.direct.actor.usr_note.search.atomic', { usr => $patronid } );
2415 __PACKAGE__->register_method(
2416 method => 'create_user_note',
2417 api_name => 'open-ils.actor.note.create',
2419 Creates a new note for the given user
2420 @param authtoken The login session key
2421 @param note The note object
2424 sub create_user_note {
2425 my( $self, $conn, $authtoken, $note ) = @_;
2426 my $e = new_editor(xact=>1, authtoken=>$authtoken);
2427 return $e->die_event unless $e->checkauth;
2429 my $user = $e->retrieve_actor_user($note->usr)
2430 or return $e->die_event;
2432 return $e->die_event unless
2433 $e->allowed('UPDATE_USER',$user->home_ou);
2435 $note->creator($e->requestor->id);
2436 $e->create_actor_usr_note($note) or return $e->die_event;
2442 __PACKAGE__->register_method(
2443 method => 'delete_user_note',
2444 api_name => 'open-ils.actor.note.delete',
2446 Deletes a note for the given user
2447 @param authtoken The login session key
2448 @param noteid The note id
2451 sub delete_user_note {
2452 my( $self, $conn, $authtoken, $noteid ) = @_;
2454 my $e = new_editor(xact=>1, authtoken=>$authtoken);
2455 return $e->die_event unless $e->checkauth;
2456 my $note = $e->retrieve_actor_usr_note($noteid)
2457 or return $e->die_event;
2458 my $user = $e->retrieve_actor_user($note->usr)
2459 or return $e->die_event;
2460 return $e->die_event unless
2461 $e->allowed('UPDATE_USER', $user->home_ou);
2463 $e->delete_actor_usr_note($note) or return $e->die_event;
2469 __PACKAGE__->register_method(
2470 method => 'update_user_note',
2471 api_name => 'open-ils.actor.note.update',
2473 @param authtoken The login session key
2474 @param note The note
2478 sub update_user_note {
2479 my( $self, $conn, $auth, $note ) = @_;
2480 my $e = new_editor(authtoken=>$auth, xact=>1);
2481 return $e->die_event unless $e->checkauth;
2482 my $patron = $e->retrieve_actor_user($note->usr)
2483 or return $e->die_event;
2484 return $e->die_event unless
2485 $e->allowed('UPDATE_USER', $patron->home_ou);
2486 $e->update_actor_user_note($note)
2487 or return $e->die_event;
2494 __PACKAGE__->register_method(
2495 method => 'create_closed_date',
2496 api_name => 'open-ils.actor.org_unit.closed_date.create',
2498 Creates a new closing entry for the given org_unit
2499 @param authtoken The login session key
2500 @param note The closed_date object
2503 sub create_closed_date {
2504 my( $self, $conn, $authtoken, $cd ) = @_;
2506 my( $user, $evt ) = $U->checkses($authtoken);
2507 return $evt if $evt;
2509 $evt = $U->check_perms($user->id, $cd->org_unit, 'CREATE_CLOSEING');
2510 return $evt if $evt;
2512 $logger->activity("user ".$user->id." creating library closing for ".$cd->org_unit);
2514 my $id = $U->storagereq(
2515 'open-ils.storage.direct.actor.org_unit.closed_date.create', $cd );
2516 return $U->DB_UPDATE_FAILED($cd) unless $id;
2521 __PACKAGE__->register_method(
2522 method => 'delete_closed_date',
2523 api_name => 'open-ils.actor.org_unit.closed_date.delete',
2525 Deletes a closing entry for the given org_unit
2526 @param authtoken The login session key
2527 @param noteid The close_date id
2530 sub delete_closed_date {
2531 my( $self, $conn, $authtoken, $cd ) = @_;
2533 my( $user, $evt ) = $U->checkses($authtoken);
2534 return $evt if $evt;
2537 ($cd_obj, $evt) = fetch_closed_date($cd);
2538 return $evt if $evt;
2540 $evt = $U->check_perms($user->id, $cd->org_unit, 'DELETE_CLOSEING');
2541 return $evt if $evt;
2543 $logger->activity("user ".$user->id." deleting library closing for ".$cd->org_unit);
2545 my $stat = $U->storagereq(
2546 'open-ils.storage.direct.actor.org_unit.closed_date.delete', $cd );
2547 return $U->DB_UPDATE_FAILED($cd) unless $stat;
2552 __PACKAGE__->register_method(
2553 method => 'usrname_exists',
2554 api_name => 'open-ils.actor.username.exists',
2556 desc => 'Check if a username is already taken (by an undeleted patron)',
2558 {desc => 'Authentication token', type => 'string'},
2559 {desc => 'Username', type => 'string'}
2562 desc => 'id of existing user if username exists, undef otherwise. Event on error'
2567 sub usrname_exists {
2568 my( $self, $conn, $auth, $usrname ) = @_;
2569 my $e = new_editor(authtoken=>$auth);
2570 return $e->event unless $e->checkauth;
2571 my $a = $e->search_actor_user({usrname => $usrname, deleted=>'f'}, {idlist=>1});
2572 return $$a[0] if $a and @$a;
2576 __PACKAGE__->register_method(
2577 method => 'barcode_exists',
2578 api_name => 'open-ils.actor.barcode.exists',
2580 signature => 'Returns 1 if the requested barcode exists, returns 0 otherwise'
2583 sub barcode_exists {
2584 my( $self, $conn, $auth, $barcode ) = @_;
2585 my $e = new_editor(authtoken=>$auth);
2586 return $e->event unless $e->checkauth;
2587 my $card = $e->search_actor_card({barcode => $barcode});
2593 #return undef unless @$card;
2594 #return $card->[0]->usr;
2598 __PACKAGE__->register_method(
2599 method => 'retrieve_net_levels',
2600 api_name => 'open-ils.actor.net_access_level.retrieve.all',
2603 sub retrieve_net_levels {
2604 my( $self, $conn, $auth ) = @_;
2605 my $e = new_editor(authtoken=>$auth);
2606 return $e->event unless $e->checkauth;
2607 return $e->retrieve_all_config_net_access_level();
2610 # Retain the old typo API name just in case
2611 __PACKAGE__->register_method(
2612 method => 'fetch_org_by_shortname',
2613 api_name => 'open-ils.actor.org_unit.retrieve_by_shorname',
2615 __PACKAGE__->register_method(
2616 method => 'fetch_org_by_shortname',
2617 api_name => 'open-ils.actor.org_unit.retrieve_by_shortname',
2619 sub fetch_org_by_shortname {
2620 my( $self, $conn, $sname ) = @_;
2621 my $e = new_editor();
2622 my $org = $e->search_actor_org_unit({ shortname => uc($sname)})->[0];
2623 return $e->event unless $org;
2628 __PACKAGE__->register_method(
2629 method => 'session_home_lib',
2630 api_name => 'open-ils.actor.session.home_lib',
2633 sub session_home_lib {
2634 my( $self, $conn, $auth ) = @_;
2635 my $e = new_editor(authtoken=>$auth);
2636 return undef unless $e->checkauth;
2637 my $org = $e->retrieve_actor_org_unit($e->requestor->home_ou);
2638 return $org->shortname;
2641 __PACKAGE__->register_method(
2642 method => 'session_safe_token',
2643 api_name => 'open-ils.actor.session.safe_token',
2645 Returns a hashed session ID that is safe for export to the world.
2646 This safe token will expire after 1 hour of non-use.
2647 @param auth Active authentication token
2651 sub session_safe_token {
2652 my( $self, $conn, $auth ) = @_;
2653 my $e = new_editor(authtoken=>$auth);
2654 return undef unless $e->checkauth;
2656 my $safe_token = md5_hex($auth);
2658 $cache ||= OpenSRF::Utils::Cache->new("global", 0);
2660 # Add more like the following if needed...
2662 "safe-token-home_lib-shortname-$safe_token",
2663 $e->retrieve_actor_org_unit(
2664 $e->requestor->home_ou
2673 __PACKAGE__->register_method(
2674 method => 'safe_token_home_lib',
2675 api_name => 'open-ils.actor.safe_token.home_lib.shortname',
2677 Returns the home library shortname from the session
2678 asscociated with a safe token from generated by
2679 open-ils.actor.session.safe_token.
2680 @param safe_token Active safe token
2684 sub safe_token_home_lib {
2685 my( $self, $conn, $safe_token ) = @_;
2687 $cache ||= OpenSRF::Utils::Cache->new("global", 0);
2688 return $cache->get_cache( 'safe-token-home_lib-shortname-'. $safe_token );
2693 __PACKAGE__->register_method(
2694 method => 'slim_tree',
2695 api_name => "open-ils.actor.org_tree.slim_hash.retrieve",
2698 my $tree = new_editor()->search_actor_org_unit(
2700 {"parent_ou" => undef },
2703 flesh_fields => { aou => ['children'] },
2704 order_by => { aou => 'name'},
2705 select => { aou => ["id","shortname", "name"]},
2710 return trim_tree($tree);
2716 return undef unless $tree;
2718 code => $tree->shortname,
2719 name => $tree->name,
2721 if( $tree->children and @{$tree->children} ) {
2722 $htree->{children} = [];
2723 for my $c (@{$tree->children}) {
2724 push( @{$htree->{children}}, trim_tree($c) );
2732 __PACKAGE__->register_method(
2733 method => "update_penalties",
2734 api_name => "open-ils.actor.user.penalties.update"
2737 sub update_penalties {
2738 my($self, $conn, $auth, $user_id) = @_;
2739 my $e = new_editor(authtoken=>$auth, xact => 1);
2740 return $e->die_event unless $e->checkauth;
2741 my $user = $e->retrieve_actor_user($user_id) or return $e->die_event;
2742 return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
2743 my $evt = OpenILS::Utils::Penalty->calculate_penalties($e, $user_id, $e->requestor->ws_ou);
2744 return $evt if $evt;
2750 __PACKAGE__->register_method(
2751 method => "apply_penalty",
2752 api_name => "open-ils.actor.user.penalty.apply"
2756 my($self, $conn, $auth, $penalty) = @_;
2758 my $e = new_editor(authtoken=>$auth, xact => 1);
2759 return $e->die_event unless $e->checkauth;
2761 my $user = $e->retrieve_actor_user($penalty->usr) or return $e->die_event;
2762 return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
2764 my $ptype = $e->retrieve_config_standing_penalty($penalty->standing_penalty) or return $e->die_event;
2767 (defined $ptype->org_depth) ?
2768 $U->org_unit_ancestor_at_depth($penalty->org_unit, $ptype->org_depth) :
2771 $penalty->org_unit($ctx_org);
2772 $penalty->staff($e->requestor->id);
2773 $e->create_actor_user_standing_penalty($penalty) or return $e->die_event;
2776 return $penalty->id;
2779 __PACKAGE__->register_method(
2780 method => "remove_penalty",
2781 api_name => "open-ils.actor.user.penalty.remove"
2784 sub remove_penalty {
2785 my($self, $conn, $auth, $penalty) = @_;
2786 my $e = new_editor(authtoken=>$auth, xact => 1);
2787 return $e->die_event unless $e->checkauth;
2788 my $user = $e->retrieve_actor_user($penalty->usr) or return $e->die_event;
2789 return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
2791 $e->delete_actor_user_standing_penalty($penalty) or return $e->die_event;
2796 __PACKAGE__->register_method(
2797 method => "update_penalty_note",
2798 api_name => "open-ils.actor.user.penalty.note.update"
2801 sub update_penalty_note {
2802 my($self, $conn, $auth, $penalty_ids, $note) = @_;
2803 my $e = new_editor(authtoken=>$auth, xact => 1);
2804 return $e->die_event unless $e->checkauth;
2805 for my $penalty_id (@$penalty_ids) {
2806 my $penalty = $e->search_actor_user_standing_penalty( { id => $penalty_id } )->[0];
2807 if (! $penalty ) { return $e->die_event; }
2808 my $user = $e->retrieve_actor_user($penalty->usr) or return $e->die_event;
2809 return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
2811 $penalty->note( $note ); $penalty->ischanged( 1 );
2813 $e->update_actor_user_standing_penalty($penalty) or return $e->die_event;
2819 __PACKAGE__->register_method(
2820 method => "ranged_penalty_thresholds",
2821 api_name => "open-ils.actor.grp_penalty_threshold.ranged.retrieve",
2825 sub ranged_penalty_thresholds {
2826 my($self, $conn, $auth, $context_org) = @_;
2827 my $e = new_editor(authtoken=>$auth);
2828 return $e->event unless $e->checkauth;
2829 return $e->event unless $e->allowed('VIEW_GROUP_PENALTY_THRESHOLD', $context_org);
2830 my $list = $e->search_permission_grp_penalty_threshold([
2831 {org_unit => $U->get_org_ancestors($context_org)},
2832 {order_by => {pgpt => 'id'}}
2834 $conn->respond($_) for @$list;
2840 __PACKAGE__->register_method(
2841 method => "user_retrieve_fleshed_by_id",
2843 api_name => "open-ils.actor.user.fleshed.retrieve",
2846 sub user_retrieve_fleshed_by_id {
2847 my( $self, $client, $auth, $user_id, $fields ) = @_;
2848 my $e = new_editor(authtoken => $auth);
2849 return $e->event unless $e->checkauth;
2851 if( $e->requestor->id != $user_id ) {
2852 return $e->event unless $e->allowed('VIEW_USER');
2858 "standing_penalties",
2862 "stat_cat_entries" ];
2863 return new_flesh_user($user_id, $fields, $e);
2867 sub new_flesh_user {
2870 my $fields = shift || [];
2873 my $fetch_penalties = 0;
2874 if(grep {$_ eq 'standing_penalties'} @$fields) {
2875 $fields = [grep {$_ ne 'standing_penalties'} @$fields];
2876 $fetch_penalties = 1;
2879 my $user = $e->retrieve_actor_user(
2884 "flesh_fields" => { "au" => $fields }
2887 ) or return $e->die_event;
2890 if( grep { $_ eq 'addresses' } @$fields ) {
2892 $user->addresses([]) unless @{$user->addresses};
2893 # don't expose "replaced" addresses by default
2894 $user->addresses([grep {$_->id >= 0} @{$user->addresses}]);
2896 if( ref $user->billing_address ) {
2897 unless( grep { $user->billing_address->id == $_->id } @{$user->addresses} ) {
2898 push( @{$user->addresses}, $user->billing_address );
2902 if( ref $user->mailing_address ) {
2903 unless( grep { $user->mailing_address->id == $_->id } @{$user->addresses} ) {
2904 push( @{$user->addresses}, $user->mailing_address );
2909 if($fetch_penalties) {
2910 # grab the user penalties ranged for this location
2911 $user->standing_penalties(
2912 $e->search_actor_user_standing_penalty([
2915 {stop_date => undef},
2916 {stop_date => {'>' => 'now'}}
2918 org_unit => $U->get_org_ancestors($e->requestor->ws_ou)
2921 flesh_fields => {ausp => ['standing_penalty']}
2928 $user->clear_passwd();
2935 __PACKAGE__->register_method(
2936 method => "user_retrieve_parts",
2937 api_name => "open-ils.actor.user.retrieve.parts",
2940 sub user_retrieve_parts {
2941 my( $self, $client, $auth, $user_id, $fields ) = @_;
2942 my $e = new_editor(authtoken => $auth);
2943 return $e->event unless $e->checkauth;
2944 $user_id ||= $e->requestor->id;
2945 if( $e->requestor->id != $user_id ) {
2946 return $e->event unless $e->allowed('VIEW_USER');
2949 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
2950 push(@resp, $user->$_()) for(@$fields);
2956 __PACKAGE__->register_method(
2957 method => 'user_opt_in_enabled',
2958 api_name => 'open-ils.actor.user.org_unit_opt_in.enabled',
2959 signature => '@return 1 if user opt-in is globally enabled, 0 otherwise.'
2962 sub user_opt_in_enabled {
2963 my($self, $conn) = @_;
2964 my $sc = OpenSRF::Utils::SettingsClient->new;
2965 return 1 if lc($sc->config_value(share => user => 'opt_in')) eq 'true';
2970 __PACKAGE__->register_method(
2971 method => 'user_opt_in_at_org',
2972 api_name => 'open-ils.actor.user.org_unit_opt_in.check',
2974 @param $auth The auth token
2975 @param user_id The ID of the user to test
2976 @return 1 if the user has opted in at the specified org,
2977 event on error, and 0 otherwise. /
2979 sub user_opt_in_at_org {
2980 my($self, $conn, $auth, $user_id) = @_;
2982 # see if we even need to enforce the opt-in value
2983 return 1 unless user_opt_in_enabled($self);
2985 my $e = new_editor(authtoken => $auth);
2986 return $e->event unless $e->checkauth;
2987 my $org_id = $e->requestor->ws_ou;
2989 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
2990 return $e->event unless $e->allowed('VIEW_USER', $user->home_ou);
2992 # user is automatically opted-in at the home org
2993 return 1 if $user->home_ou eq $org_id;
2995 my $vals = $e->search_actor_usr_org_unit_opt_in(
2996 {org_unit=>$org_id, usr=>$user_id},{idlist=>1});
3002 __PACKAGE__->register_method(
3003 method => 'create_user_opt_in_at_org',
3004 api_name => 'open-ils.actor.user.org_unit_opt_in.create',
3006 @param $auth The auth token
3007 @param user_id The ID of the user to test
3008 @return The ID of the newly created object, event on error./
3011 sub create_user_opt_in_at_org {
3012 my($self, $conn, $auth, $user_id) = @_;
3014 my $e = new_editor(authtoken => $auth, xact=>1);
3015 return $e->die_event unless $e->checkauth;
3016 my $org_id = $e->requestor->ws_ou;
3018 my $user = $e->retrieve_actor_user($user_id) or return $e->die_event;
3019 return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
3021 my $opt_in = Fieldmapper::actor::usr_org_unit_opt_in->new;
3023 $opt_in->org_unit($org_id);
3024 $opt_in->usr($user_id);
3025 $opt_in->staff($e->requestor->id);
3026 $opt_in->opt_in_ts('now');
3027 $opt_in->opt_in_ws($e->requestor->wsid);
3029 $opt_in = $e->create_actor_usr_org_unit_opt_in($opt_in)
3030 or return $e->die_event;
3038 __PACKAGE__->register_method (
3039 method => 'retrieve_org_hours',
3040 api_name => 'open-ils.actor.org_unit.hours_of_operation.retrieve',
3042 Returns the hours of operation for a specified org unit
3043 @param authtoken The login session key
3044 @param org_id The org_unit ID
3048 sub retrieve_org_hours {
3049 my($self, $conn, $auth, $org_id) = @_;
3050 my $e = new_editor(authtoken => $auth);
3051 return $e->die_event unless $e->checkauth;
3052 $org_id ||= $e->requestor->ws_ou;
3053 return $e->retrieve_actor_org_unit_hours_of_operation($org_id);
3057 __PACKAGE__->register_method (
3058 method => 'verify_user_password',
3059 api_name => 'open-ils.actor.verify_user_password',
3061 Given a barcode or username and the MD5 encoded password,
3062 returns 1 if the password is correct. Returns 0 otherwise.
3066 sub verify_user_password {
3067 my($self, $conn, $auth, $barcode, $username, $password) = @_;
3068 my $e = new_editor(authtoken => $auth);
3069 return $e->die_event unless $e->checkauth;
3071 my $user_by_barcode;
3072 my $user_by_username;
3074 my $card = $e->search_actor_card([
3075 {barcode => $barcode},
3076 {flesh => 1, flesh_fields => {ac => ['usr']}}])->[0] or return 0;
3077 $user_by_barcode = $card->usr;
3078 $user = $user_by_barcode;
3081 $user_by_username = $e->search_actor_user({usrname => $username})->[0] or return 0;
3082 $user = $user_by_username;
3084 return 0 if (!$user);
3085 return 0 if ($user_by_username && $user_by_barcode && $user_by_username->id != $user_by_barcode->id);
3086 return $e->event unless $e->allowed('VIEW_USER', $user->home_ou);
3087 return 1 if $user->passwd eq $password;
3091 __PACKAGE__->register_method (
3092 method => 'retrieve_usr_id_via_barcode_or_usrname',
3093 api_name => "open-ils.actor.user.retrieve_id_by_barcode_or_username",
3095 Given a barcode or username returns the id for the user or
3100 sub retrieve_usr_id_via_barcode_or_usrname {
3101 my($self, $conn, $auth, $barcode, $username) = @_;
3102 my $e = new_editor(authtoken => $auth);
3103 return $e->die_event unless $e->checkauth;
3104 my $id_as_barcode= OpenSRF::Utils::SettingsClient->new->config_value(apps => 'open-ils.actor' => app_settings => 'id_as_barcode');
3106 my $user_by_barcode;
3107 my $user_by_username;
3108 $logger->info("$id_as_barcode is the ID as BARCODE");
3110 my $card = $e->search_actor_card([
3111 {barcode => $barcode},
3112 {flesh => 1, flesh_fields => {ac => ['usr']}}])->[0];
3113 if ($id_as_barcode =~ /^t/i) {
3115 $user = $e->retrieve_actor_user($barcode);
3116 return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' ) if(!$user);
3118 $user_by_barcode = $card->usr;
3119 $user = $user_by_barcode;
3122 return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' ) if(!$card);
3123 $user_by_barcode = $card->usr;
3124 $user = $user_by_barcode;
3129 $user_by_username = $e->search_actor_user({usrname => $username})->[0] or return OpenILS::Event->new( 'ACTOR_USR_NOT_FOUND' );
3131 $user = $user_by_username;
3133 return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' ) if (!$user);
3134 return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' ) if ($user_by_username && $user_by_barcode && $user_by_username->id != $user_by_barcode->id);
3135 return $e->event unless $e->allowed('VIEW_USER', $user->home_ou);
3140 __PACKAGE__->register_method (
3141 method => 'merge_users',
3142 api_name => 'open-ils.actor.user.merge',
3145 Given a list of source users and destination user, transfer all data from the source
3146 to the dest user and delete the source user. All user related data is
3147 transferred, including circulations, holds, bookbags, etc.
3153 my($self, $conn, $auth, $master_id, $user_ids, $options) = @_;
3154 my $e = new_editor(xact => 1, authtoken => $auth);
3155 return $e->die_event unless $e->checkauth;
3157 # disallow the merge if any subordinate accounts are in collections
3158 my $colls = $e->search_money_collections_tracker({usr => $user_ids}, {idlist => 1});
3159 return OpenILS::Event->new('MERGED_USER_IN_COLLECTIONS', payload => $user_ids) if @$colls;
3161 my $master_user = $e->retrieve_actor_user($master_id) or return $e->die_event;
3162 my $del_addrs = ($U->ou_ancestor_setting_value(
3163 $master_user->home_ou, 'circ.user_merge.delete_addresses', $e)) ? 't' : 'f';
3164 my $del_cards = ($U->ou_ancestor_setting_value(
3165 $master_user->home_ou, 'circ.user_merge.delete_cards', $e)) ? 't' : 'f';
3166 my $deactivate_cards = ($U->ou_ancestor_setting_value(
3167 $master_user->home_ou, 'circ.user_merge.deactivate_cards', $e)) ? 't' : 'f';
3169 for my $src_id (@$user_ids) {
3170 my $src_user = $e->retrieve_actor_user($src_id) or return $e->die_event;
3172 return $e->die_event unless $e->allowed('MERGE_USERS', $src_user->home_ou);
3173 if($src_user->home_ou ne $master_user->home_ou) {
3174 return $e->die_event unless $e->allowed('MERGE_USERS', $master_user->home_ou);
3177 return $e->die_event unless
3178 $e->json_query({from => [
3193 __PACKAGE__->register_method (
3194 method => 'approve_user_address',
3195 api_name => 'open-ils.actor.user.pending_address.approve',
3202 sub approve_user_address {
3203 my($self, $conn, $auth, $addr) = @_;
3204 my $e = new_editor(xact => 1, authtoken => $auth);
3205 return $e->die_event unless $e->checkauth;
3207 # if the caller passes an address object, assume they want to
3208 # update it first before approving it
3209 $e->update_actor_user_address($addr) or return $e->die_event;
3211 $addr = $e->retrieve_actor_user_address($addr) or return $e->die_event;
3213 my $user = $e->retrieve_actor_user($addr->usr);
3214 return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
3215 my $result = $e->json_query({from => ['actor.approve_pending_address', $addr->id]})->[0]
3216 or return $e->die_event;
3218 return [values %$result]->[0];
3222 __PACKAGE__->register_method (
3223 method => 'retrieve_friends',
3224 api_name => 'open-ils.actor.friends.retrieve',
3227 returns { confirmed: [], pending_out: [], pending_in: []}
3228 pending_out are users I'm requesting friendship with
3229 pending_in are users requesting friendship with me
3234 sub retrieve_friends {
3235 my($self, $conn, $auth, $user_id, $options) = @_;
3236 my $e = new_editor(authtoken => $auth);
3237 return $e->event unless $e->checkauth;
3238 $user_id ||= $e->requestor->id;
3240 if($user_id != $e->requestor->id) {
3241 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
3242 return $e->event unless $e->allowed('VIEW_USER', $user->home_ou);
3245 return OpenILS::Application::Actor::Friends->retrieve_friends(
3246 $e, $user_id, $options);
3251 __PACKAGE__->register_method (
3252 method => 'apply_friend_perms',
3253 api_name => 'open-ils.actor.friends.perms.apply',
3259 sub apply_friend_perms {
3260 my($self, $conn, $auth, $user_id, $delegate_id, @perms) = @_;
3261 my $e = new_editor(authtoken => $auth, xact => 1);
3262 return $e->die_event unless $e->checkauth;
3264 if($user_id != $e->requestor->id) {
3265 my $user = $e->retrieve_actor_user($user_id) or return $e->die_event;
3266 return $e->die_event unless $e->allowed('VIEW_USER', $user->home_ou);
3269 for my $perm (@perms) {
3271 OpenILS::Application::Actor::Friends->apply_friend_perm(
3272 $e, $user_id, $delegate_id, $perm);
3273 return $evt if $evt;
3281 __PACKAGE__->register_method (
3282 method => 'update_user_pending_address',
3283 api_name => 'open-ils.actor.user.address.pending.cud'
3286 sub update_user_pending_address {
3287 my($self, $conn, $auth, $addr) = @_;
3288 my $e = new_editor(authtoken => $auth, xact => 1);
3289 return $e->die_event unless $e->checkauth;
3291 if($addr->usr != $e->requestor->id) {
3292 my $user = $e->retrieve_actor_user($addr->usr) or return $e->die_event;
3293 return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
3297 $e->create_actor_user_address($addr) or return $e->die_event;
3298 } elsif($addr->isdeleted) {
3299 $e->delete_actor_user_address($addr) or return $e->die_event;
3301 $e->update_actor_user_address($addr) or return $e->die_event;
3309 __PACKAGE__->register_method (
3310 method => 'user_events',
3311 api_name => 'open-ils.actor.user.events.circ',
3314 __PACKAGE__->register_method (
3315 method => 'user_events',
3316 api_name => 'open-ils.actor.user.events.ahr',
3321 my($self, $conn, $auth, $user_id, $filters) = @_;
3322 my $e = new_editor(authtoken => $auth);
3323 return $e->event unless $e->checkauth;
3325 (my $obj_type = $self->api_name) =~ s/.*\.([a-z]+)$/$1/;
3326 my $user_field = 'usr';
3329 $filters->{target} = {
3330 select => { $obj_type => ['id'] },
3332 where => {usr => $user_id}
3335 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
3336 if($e->requestor->id != $user_id) {
3337 return $e->event unless $e->allowed('VIEW_USER', $user->home_ou);
3340 my $ses = OpenSRF::AppSession->create('open-ils.trigger');
3341 my $req = $ses->request('open-ils.trigger.events_by_target',
3342 $obj_type, $filters, {atevdef => ['reactor', 'validator']}, 2);
3344 while(my $resp = $req->recv) {
3345 my $val = $resp->content;
3346 my $tgt = $val->target;
3348 if($obj_type eq 'circ') {
3349 $tgt->target_copy($e->retrieve_asset_copy($tgt->target_copy));
3351 } elsif($obj_type eq 'ahr') {
3352 $tgt->current_copy($e->retrieve_asset_copy($tgt->current_copy))
3353 if $tgt->current_copy;
3356 $conn->respond($val) if $val;
3362 __PACKAGE__->register_method (
3363 method => 'copy_events',
3364 api_name => 'open-ils.actor.copy.events.circ',
3367 __PACKAGE__->register_method (
3368 method => 'copy_events',
3369 api_name => 'open-ils.actor.copy.events.ahr',
3374 my($self, $conn, $auth, $copy_id, $filters) = @_;
3375 my $e = new_editor(authtoken => $auth);
3376 return $e->event unless $e->checkauth;
3378 (my $obj_type = $self->api_name) =~ s/.*\.([a-z]+)$/$1/;
3380 my $copy = $e->retrieve_asset_copy($copy_id) or return $e->event;
3382 my $copy_field = 'target_copy';
3383 $copy_field = 'current_copy' if $obj_type eq 'ahr';
3386 $filters->{target} = {
3387 select => { $obj_type => ['id'] },
3389 where => {$copy_field => $copy_id}
3393 my $ses = OpenSRF::AppSession->create('open-ils.trigger');
3394 my $req = $ses->request('open-ils.trigger.events_by_target',
3395 $obj_type, $filters, {atevdef => ['reactor', 'validator']}, 2);
3397 while(my $resp = $req->recv) {
3398 my $val = $resp->content;
3399 my $tgt = $val->target;
3401 my $user = $e->retrieve_actor_user($tgt->usr);
3402 if($e->requestor->id != $user->id) {
3403 return $e->event unless $e->allowed('VIEW_USER', $user->home_ou);
3406 $tgt->$copy_field($copy);
3409 $conn->respond($val) if $val;
3418 __PACKAGE__->register_method (
3419 method => 'update_events',
3420 api_name => 'open-ils.actor.user.event.cancel.batch',
3423 __PACKAGE__->register_method (
3424 method => 'update_events',
3425 api_name => 'open-ils.actor.user.event.reset.batch',
3430 my($self, $conn, $auth, $event_ids) = @_;
3431 my $e = new_editor(xact => 1, authtoken => $auth);
3432 return $e->die_event unless $e->checkauth;
3435 for my $id (@$event_ids) {
3437 # do a little dance to determine what user we are ultimately affecting
3438 my $event = $e->retrieve_action_trigger_event([
3441 flesh_fields => {atev => ['event_def'], atevdef => ['hook']}
3443 ]) or return $e->die_event;
3446 if($event->event_def->hook->core_type eq 'circ') {
3447 $user_id = $e->retrieve_action_circulation($event->target)->usr;
3448 } elsif($event->event_def->hook->core_type eq 'ahr') {
3449 $user_id = $e->retrieve_action_hold_request($event->target)->usr;
3454 my $user = $e->retrieve_actor_user($user_id);
3455 return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
3457 if($self->api_name =~ /cancel/) {
3458 $event->state('invalid');
3459 } elsif($self->api_name =~ /reset/) {
3460 $event->clear_start_time;
3461 $event->clear_update_time;
3462 $event->state('pending');
3465 $e->update_action_trigger_event($event) or return $e->die_event;
3466 $conn->respond({maximum => scalar(@$event_ids), progress => $x++});
3470 return {complete => 1};
3474 __PACKAGE__->register_method (
3475 method => 'really_delete_user',
3476 api_name => 'open-ils.actor.user.delete',
3478 It anonymizes all personally identifiable information in actor.usr. By calling actor.usr_purge_data()
3479 it also purges related data from other tables, sometimes by transferring it to a designated destination user.
3480 The usrname field (along with first_given_name and family_name) is updated to id '-PURGED-' now().
3481 dest_usr_id is only required when deleting a user that performs staff functions.
3485 sub really_delete_user {
3486 my($self, $conn, $auth, $user_id, $dest_user_id) = @_;
3487 my $e = new_editor(authtoken => $auth, xact => 1);
3488 return $e->die_event unless $e->checkauth;
3489 my $user = $e->retrieve_actor_user($user_id) or return $e->die_event;
3490 return $e->die_event unless $e->allowed('DELETE_USER', $user->home_ou);
3491 my $stat = $e->json_query(
3492 {from => ['actor.usr_delete', $user_id, $dest_user_id]})->[0]
3493 or return $e->die_event;
3500 __PACKAGE__->register_method (
3501 method => 'user_payments',
3502 api_name => 'open-ils.actor.user.payments.retrieve',
3505 Returns all payments for a given user. Default order is newest payments first.
3506 @param auth Authentication token
3507 @param user_id The user ID
3508 @param filters An optional hash of filters, including limit, offset, and order_by definitions
3513 my($self, $conn, $auth, $user_id, $filters) = @_;
3516 my $e = new_editor(authtoken => $auth);
3517 return $e->die_event unless $e->checkauth;
3519 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
3520 return $e->event unless $e->allowed('VIEW_USER_TRANSACTIONS', $user->home_ou);
3522 # Find all payments for all transactions for user $user_id
3524 select => {mp => ['id']},
3529 select => {mbt => ['id']},
3531 where => {usr => $user_id}
3535 order_by => [{ # by default, order newest payments first
3537 field => 'payment_ts',
3542 for (qw/order_by limit offset/) {
3543 $query->{$_} = $filters->{$_} if defined $filters->{$_};
3546 if(defined $filters->{where}) {
3547 foreach (keys %{$filters->{where}}) {
3548 # don't allow the caller to expand the result set to other users
3549 $query->{where}->{$_} = $filters->{where}->{$_} unless $_ eq 'xact';
3553 my $payment_ids = $e->json_query($query);
3554 for my $pid (@$payment_ids) {
3555 my $pay = $e->retrieve_money_payment([
3560 mbt => ['summary', 'circulation', 'grocery'],
3561 circ => ['target_copy'],
3562 acp => ['call_number'],
3570 xact_type => $pay->xact->summary->xact_type,
3571 last_billing_type => $pay->xact->summary->last_billing_type,
3574 if($pay->xact->summary->xact_type eq 'circulation') {
3575 $resp->{barcode} = $pay->xact->circulation->target_copy->barcode;
3576 $resp->{title} = $U->record_to_mvr($pay->xact->circulation->target_copy->call_number->record)->title;
3579 $pay->xact($pay->xact->id); # de-flesh
3580 $conn->respond($resp);
3588 __PACKAGE__->register_method (
3589 method => 'negative_balance_users',
3590 api_name => 'open-ils.actor.users.negative_balance',
3593 Returns all users that have an overall negative balance
3594 @param auth Authentication token
3595 @param org_id The context org unit as an ID or list of IDs. This will be the home
3596 library of the user. If no org_unit is specified, no org unit filter is applied
3600 sub negative_balance_users {
3601 my($self, $conn, $auth, $org_id) = @_;
3603 my $e = new_editor(authtoken => $auth);
3604 return $e->die_event unless $e->checkauth;
3605 return $e->die_event unless $e->allowed('VIEW_USER', $org_id);
3609 mous => ['usr', 'balance_owed'],
3612 {column => 'last_billing_ts', transform => 'max', aggregate => 1},
3613 {column => 'last_payment_ts', transform => 'max', aggregate => 1},
3630 where => {'+mous' => {balance_owed => {'<' => 0}}}
3633 $query->{from}->{mous}->{au}->{filter}->{home_ou} = $org_id if $org_id;
3635 my $list = $e->json_query($query, {timeout => 600});
3637 for my $data (@$list) {
3639 usr => $e->retrieve_actor_user([$data->{usr}, {flesh => 1, flesh_fields => {au => ['card']}}]),
3640 balance_owed => $data->{balance_owed},
3641 last_billing_activity => max($data->{last_billing_ts}, $data->{last_payment_ts})
3648 __PACKAGE__->register_method(
3649 method => "request_password_reset",
3650 api_name => "open-ils.actor.patron.password_reset.request",
3652 desc => "Generates a UUID token usable with the open-ils.actor.patron.password_reset.commit " .
3653 "method for changing a user's password. The UUID token is distributed via A/T " .
3654 "templates (i.e. email to the user).",
3656 { desc => 'user_id_type', type => 'string' },
3657 { desc => 'user_id', type => 'string' },
3658 { desc => 'optional (based on library setting) matching email address for authorizing request', type => 'string' },
3660 return => {desc => '1 on success, Event on error'}
3663 sub request_password_reset {
3664 my($self, $conn, $user_id_type, $user_id, $email) = @_;
3666 # Check to see if password reset requests are already being throttled:
3667 # 0. Check cache to see if we're in throttle mode (avoid hitting database)
3669 my $e = new_editor(xact => 1);
3672 # Get the user, if any, depending on the input value
3673 if ($user_id_type eq 'username') {
3674 $user = $e->search_actor_user({usrname => $user_id})->[0];
3677 return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' );
3679 } elsif ($user_id_type eq 'barcode') {
3680 my $card = $e->search_actor_card([
3681 {barcode => $user_id},
3682 {flesh => 1, flesh_fields => {ac => ['usr']}}])->[0];
3685 return OpenILS::Event->new('ACTOR_USER_NOT_FOUND');
3690 # If the user doesn't have an email address, we can't help them
3691 if (!$user->email) {
3693 return OpenILS::Event->new('PATRON_NO_EMAIL_ADDRESS');
3696 my $email_must_match = $U->ou_ancestor_setting_value($user->home_ou, 'circ.password_reset_request_requires_matching_email');
3697 if ($email_must_match) {
3698 if ($user->email ne $email) {
3699 return OpenILS::Event->new('EMAIL_VERIFICATION_FAILED');
3703 _reset_password_request($conn, $e, $user);
3706 # Once we have the user, we can issue the password reset request
3707 # XXX Add a wrapper method that accepts barcode + email input
3708 sub _reset_password_request {
3709 my ($conn, $e, $user) = @_;
3711 # 1. Get throttle threshold and time-to-live from OU_settings
3712 my $aupr_throttle = $U->ou_ancestor_setting_value($user->home_ou, 'circ.password_reset_request_throttle') || 1000;
3713 my $aupr_ttl = $U->ou_ancestor_setting_value($user->home_ou, 'circ.password_reset_request_time_to_live') || 24*60*60;
3715 my $threshold_time = DateTime->now(time_zone => 'local')->subtract(seconds => $aupr_ttl)->iso8601();
3717 # 2. Get time of last request and number of active requests (num_active)
3718 my $active_requests = $e->json_query({
3724 transform => 'COUNT'
3727 column => 'request_time',
3733 has_been_reset => { '=' => 'f' },
3734 request_time => { '>' => $threshold_time }
3738 # Guard against no active requests
3739 if ($active_requests->[0]->{'request_time'}) {
3740 my $last_request = DateTime::Format::ISO8601->parse_datetime(clense_ISO8601($active_requests->[0]->{'request_time'}));
3741 my $now = DateTime::Format::ISO8601->new();
3743 # 3. if (num_active > throttle_threshold) and (now - last_request < 1 minute)
3744 if (($active_requests->[0]->{'usr'} > $aupr_throttle) &&
3745 ($last_request->add_duration('1 minute') > $now)) {
3746 $cache->put_cache('open-ils.actor.password.throttle', DateTime::Format::ISO8601->new(), 60);
3748 return OpenILS::Event->new('PATRON_TOO_MANY_ACTIVE_PASSWORD_RESET_REQUESTS');
3752 # TODO Check to see if the user is in a password-reset-restricted group
3754 # Otherwise, go ahead and try to get the user.
3756 # Check the number of active requests for this user
3757 $active_requests = $e->json_query({
3763 transform => 'COUNT'
3768 usr => { '=' => $user->id },
3769 has_been_reset => { '=' => 'f' },
3770 request_time => { '>' => $threshold_time }
3774 $logger->info("User " . $user->id . " has " . $active_requests->[0]->{'usr'} . " active password reset requests.");
3776 # if less than or equal to per-user threshold, proceed; otherwise, return event
3777 my $aupr_per_user_limit = $U->ou_ancestor_setting_value($user->home_ou, 'circ.password_reset_request_per_user_limit') || 3;
3778 if ($active_requests->[0]->{'usr'} > $aupr_per_user_limit) {
3780 return OpenILS::Event->new('PATRON_TOO_MANY_ACTIVE_PASSWORD_RESET_REQUESTS');
3783 # Create the aupr object and insert into the database
3784 my $reset_request = Fieldmapper::actor::usr_password_reset->new;
3785 my $uuid = create_uuid_as_string(UUID_V4);
3786 $reset_request->uuid($uuid);
3787 $reset_request->usr($user->id);
3789 my $aupr = $e->create_actor_usr_password_reset($reset_request) or return $e->die_event;
3792 # Create an event to notify user of the URL to reset their password
3794 # Can we stuff this in the user_data param for trigger autocreate?
3795 my $hostname = $U->ou_ancestor_setting_value($user->home_ou, 'lib.hostname') || 'localhost';
3797 my $ses = OpenSRF::AppSession->create('open-ils.trigger');
3798 $ses->request('open-ils.trigger.event.autocreate', 'password.reset_request', $aupr, $user->home_ou);
3801 # $U->create_trigger_event('password.reset_request', $aupr, $user->home_ou);
3806 __PACKAGE__->register_method(
3807 method => "commit_password_reset",
3808 api_name => "open-ils.actor.patron.password_reset.commit",
3810 desc => "Checks a UUID token generated by the open-ils.actor.patron.password_reset.request method for " .
3811 "validity, and if valid, uses it as authorization for changing the associated user's password " .
3812 "with the supplied password.",
3814 { desc => 'uuid', type => 'string' },
3815 { desc => 'password', type => 'string' },
3817 return => {desc => '1 on success, Event on error'}
3820 sub commit_password_reset {
3821 my($self, $conn, $uuid, $password) = @_;
3823 # Check to see if password reset requests are already being throttled:
3824 # 0. Check cache to see if we're in throttle mode (avoid hitting database)
3825 $cache ||= OpenSRF::Utils::Cache->new("global", 0);
3826 my $throttle = $cache->get_cache('open-ils.actor.password.throttle') || undef;
3828 return OpenILS::Event->new('PATRON_NOT_AN_ACTIVE_PASSWORD_RESET_REQUEST');
3831 my $e = new_editor(xact => 1);
3833 my $aupr = $e->search_actor_usr_password_reset({
3840 return OpenILS::Event->new('PATRON_NOT_AN_ACTIVE_PASSWORD_RESET_REQUEST');
3842 my $user_id = $aupr->[0]->usr;
3843 my $user = $e->retrieve_actor_user($user_id);
3845 # Ensure we're still within the TTL for the request
3846 my $aupr_ttl = $U->ou_ancestor_setting_value($user->home_ou, 'circ.password_reset_request_time_to_live') || 24*60*60;
3847 my $threshold = DateTime::Format::ISO8601->parse_datetime(clense_ISO8601($aupr->[0]->request_time))->add(seconds => $aupr_ttl);
3848 if ($threshold < DateTime->now(time_zone => 'local')) {
3850 $logger->info("Password reset request needed to be submitted before $threshold");
3851 return OpenILS::Event->new('PATRON_NOT_AN_ACTIVE_PASSWORD_RESET_REQUEST');
3854 # Check complexity of password against OU-defined regex
3855 my $pw_regex = $U->ou_ancestor_setting_value($user->home_ou, 'global.password_regex');
3859 # Calling JSON2perl on the $pw_regex causes failure, even before the fancy Unicode regex
3860 # ($pw_regex = OpenSRF::Utils::JSON->JSON2perl($pw_regex)) =~ s/\\u([0-9a-fA-F]{4})/\\x{$1}/gs;
3861 $is_strong = check_password_strength_custom($password, $pw_regex);
3863 $is_strong = check_password_strength_default($password);
3868 return OpenILS::Event->new('PATRON_PASSWORD_WAS_NOT_STRONG');
3871 # All is well; update the password
3872 $user->passwd($password);
3873 $e->update_actor_user($user);
3875 # And flag that this password reset request has been honoured
3876 $aupr->[0]->has_been_reset('t');
3877 $e->update_actor_usr_password_reset($aupr->[0]);
3883 sub check_password_strength_default {
3884 my $password = shift;
3885 # Use the default set of checks
3886 if ( (length($password) < 7) or
3887 ($password !~ m/.*\d+.*/) or
3888 ($password !~ m/.*[A-Za-z]+.*/)
3895 sub check_password_strength_custom {
3896 my ($password, $pw_regex) = @_;
3898 $pw_regex = qr/$pw_regex/;
3899 if ($password !~ /$pw_regex/) {
3907 __PACKAGE__->register_method(
3908 method => "event_def_opt_in_settings",
3909 api_name => "open-ils.actor.event_def.opt_in.settings",
3912 desc => 'Streams the set of "cust" objects that are used as opt-in settings for event definitions',
3914 { desc => 'Authentication token', type => 'string'},
3916 desc => 'Org Unit ID. (optional). If no org ID is present, the home_ou of the requesting user is used',
3921 desc => q/set of "cust" objects that are used as opt-in settings for event definitions at the specified org unit/,
3928 sub event_def_opt_in_settings {
3929 my($self, $conn, $auth, $org_id) = @_;
3930 my $e = new_editor(authtoken => $auth);
3931 return $e->event unless $e->checkauth;
3933 if(defined $org_id and $org_id != $e->requestor->home_ou) {
3934 return $e->event unless
3935 $e->allowed(['VIEW_USER_SETTING_TYPE', 'ADMIN_USER_SETTING_TYPE'], $org_id);
3937 $org_id = $e->requestor->home_ou;
3940 # find all config.user_setting_type's related to event_defs for the requested org unit
3941 my $types = $e->json_query({
3942 select => {cust => ['name']},
3943 from => {atevdef => 'cust'},
3946 owner => $U->get_org_ancestors($org_id), # context org plus parents
3953 $conn->respond($_) for
3954 @{$e->search_config_usr_setting_type({name => [map {$_->{name}} @$types]})};
3961 __PACKAGE__->register_method(
3962 method => "user_visible_circs",
3963 api_name => "open-ils.actor.history.circ.visible",
3966 desc => 'Returns the set of opt-in visible circulations accompanied by circulation chain summaries',
3968 { desc => 'Authentication token', type => 'string'},
3969 { desc => 'User ID. If no user id is present, the authenticated user is assumed', type => 'number' },
3970 { desc => 'Options hash. Supported fields are "limit" and "offset"', type => 'object' },
3973 desc => q/An object with 2 fields: circulation and summary.
3974 circulation is the "circ" object. summary is the related "accs" object/,
3980 __PACKAGE__->register_method(
3981 method => "user_visible_circs",
3982 api_name => "open-ils.actor.history.circ.visible.print",
3985 desc => 'Returns printable output for the set of opt-in visible circulations',
3987 { desc => 'Authentication token', type => 'string'},
3988 { desc => 'User ID. If no user id is present, the authenticated user is assumed', type => 'number' },
3989 { desc => 'Options hash. Supported fields are "limit" and "offset"', type => 'object' },
3992 desc => q/An action_trigger.event object or error event./,
3998 __PACKAGE__->register_method(
3999 method => "user_visible_circs",
4000 api_name => "open-ils.actor.history.circ.visible.email",
4003 desc => 'Emails the set of opt-in visible circulations to the requestor',
4005 { desc => 'Authentication token', type => 'string'},
4006 { desc => 'User ID. If no user id is present, the authenticated user is assumed', type => 'number' },
4007 { desc => 'Options hash. Supported fields are "limit" and "offset"', type => 'object' },
4010 desc => q/undef, or event on error/
4015 __PACKAGE__->register_method(
4016 method => "user_visible_circs",
4017 api_name => "open-ils.actor.history.hold.visible",
4020 desc => 'Returns the set of opt-in visible holds',
4022 { desc => 'Authentication token', type => 'string'},
4023 { desc => 'User ID. If no user id is present, the authenticated user is assumed', type => 'number' },
4024 { desc => 'Options hash. Supported fields are "limit" and "offset"', type => 'object' },
4027 desc => q/An object with 1 field: "hold"/,
4033 __PACKAGE__->register_method(
4034 method => "user_visible_circs",
4035 api_name => "open-ils.actor.history.hold.visible.print",
4038 desc => 'Returns printable output for the set of opt-in visible holds',
4040 { desc => 'Authentication token', type => 'string'},
4041 { desc => 'User ID. If no user id is present, the authenticated user is assumed', type => 'number' },
4042 { desc => 'Options hash. Supported fields are "limit" and "offset"', type => 'object' },
4045 desc => q/An action_trigger.event object or error event./,
4051 __PACKAGE__->register_method(
4052 method => "user_visible_circs",
4053 api_name => "open-ils.actor.history.hold.visible.email",
4056 desc => 'Emails the set of opt-in visible holds to the requestor',
4058 { desc => 'Authentication token', type => 'string'},
4059 { desc => 'User ID. If no user id is present, the authenticated user is assumed', type => 'number' },
4060 { desc => 'Options hash. Supported fields are "limit" and "offset"', type => 'object' },
4063 desc => q/undef, or event on error/
4068 sub user_visible_circs {
4069 my($self, $conn, $auth, $user_id, $options) = @_;
4071 my $is_hold = ($self->api_name =~ /hold/);
4072 my $for_print = ($self->api_name =~ /print/);
4073 my $for_email = ($self->api_name =~ /email/);
4074 my $e = new_editor(authtoken => $auth);
4075 return $e->event unless $e->checkauth;
4077 $user_id ||= $e->requestor->id;
4079 $options->{limit} ||= 50;
4080 $options->{offset} ||= 0;
4082 if($user_id != $e->requestor->id) {
4083 my $perm = ($is_hold) ? 'VIEW_HOLD' : 'VIEW_CIRCULATIONS';
4084 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
4085 return $e->event unless $e->allowed($perm, $user->home_ou);
4088 my $db_func = ($is_hold) ? 'action.usr_visible_holds' : 'action.usr_visible_circs';
4090 my $data = $e->json_query({
4091 from => [$db_func, $user_id],
4092 limit => $$options{limit},
4093 offset => $$options{offset}
4095 # TODO: I only want IDs. code below didn't get me there
4096 # {"select":{"au":[{"column":"id", "result_field":"id",
4097 # "transform":"action.usr_visible_circs"}]}, "where":{"id":10}, "from":"au"}
4102 return undef unless @$data;
4106 # collect the batch of objects
4110 my $hold_list = $e->search_action_hold_request({id => [map { $_->{id} } @$data]});
4111 return $U->fire_object_event(undef, 'ahr.format.history.print', $hold_list, $$hold_list[0]->request_lib);
4115 my $circ_list = $e->search_action_circulation({id => [map { $_->{id} } @$data]});
4116 return $U->fire_object_event(undef, 'circ.format.history.print', $circ_list, $$circ_list[0]->circ_lib);
4119 } elsif ($for_email) {
4121 $conn->respond_complete(1) if $for_email; # no sense in waiting
4129 my $hold = $e->retrieve_action_hold_request($id);
4130 $U->create_events_for_hook('ahr.format.history.email', $hold, $hold->request_lib, undef, undef, 1);
4131 # events will be fired from action_trigger_runner
4135 my $circ = $e->retrieve_action_circulation($id);
4136 $U->create_events_for_hook('circ.format.history.email', $circ, $circ->circ_lib, undef, undef, 1);
4137 # events will be fired from action_trigger_runner
4141 } else { # just give me the data please
4149 my $hold = $e->retrieve_action_hold_request($id);
4150 $conn->respond({hold => $hold});
4154 my $circ = $e->retrieve_action_circulation($id);
4157 summary => $U->create_circ_chain_summary($e, $id)
4166 __PACKAGE__->register_method(
4167 method => "user_saved_search_cud",
4168 api_name => "open-ils.actor.user.saved_search.cud",
4171 desc => 'Create/Update/Delete Access to user saved searches',
4173 { desc => 'Authentication token', type => 'string' },
4174 { desc => 'Saved Search Object', type => 'object', class => 'auss' }
4177 desc => q/The retrieved or updated saved search object, or id of a deleted object; Event on error/,
4183 __PACKAGE__->register_method(
4184 method => "user_saved_search_cud",
4185 api_name => "open-ils.actor.user.saved_search.retrieve",
4188 desc => 'Retrieve a saved search object',
4190 { desc => 'Authentication token', type => 'string' },
4191 { desc => 'Saved Search ID', type => 'number' }
4194 desc => q/The saved search object, Event on error/,
4200 sub user_saved_search_cud {
4201 my( $self, $client, $auth, $search ) = @_;
4202 my $e = new_editor( authtoken=>$auth );
4203 return $e->die_event unless $e->checkauth;
4205 my $o_search; # prior version of the object, if any
4206 my $res; # to be returned
4208 # branch on the operation type
4210 if( $self->api_name =~ /retrieve/ ) { # Retrieve
4212 # Get the old version, to check ownership
4213 $o_search = $e->retrieve_actor_usr_saved_search( $search )
4214 or return $e->die_event;
4216 # You can't read somebody else's search
4217 return OpenILS::Event->new('BAD_PARAMS')
4218 unless $o_search->owner == $e->requestor->id;
4224 $e->xact_begin; # start an editor transaction
4226 if( $search->isnew ) { # Create
4228 # You can't create a search for somebody else
4229 return OpenILS::Event->new('BAD_PARAMS')
4230 unless $search->owner == $e->requestor->id;
4232 $e->create_actor_usr_saved_search( $search )
4233 or return $e->die_event;
4237 } elsif( $search->ischanged ) { # Update
4239 # You can't change ownership of a search
4240 return OpenILS::Event->new('BAD_PARAMS')
4241 unless $search->owner == $e->requestor->id;
4243 # Get the old version, to check ownership
4244 $o_search = $e->retrieve_actor_usr_saved_search( $search->id )
4245 or return $e->die_event;
4247 # You can't update somebody else's search
4248 return OpenILS::Event->new('BAD_PARAMS')
4249 unless $o_search->owner == $e->requestor->id;
4252 $e->update_actor_usr_saved_search( $search )
4253 or return $e->die_event;
4257 } elsif( $search->isdeleted ) { # Delete
4259 # Get the old version, to check ownership
4260 $o_search = $e->retrieve_actor_usr_saved_search( $search->id )
4261 or return $e->die_event;
4263 # You can't delete somebody else's search
4264 return OpenILS::Event->new('BAD_PARAMS')
4265 unless $o_search->owner == $e->requestor->id;
4268 $e->delete_actor_usr_saved_search( $o_search )
4269 or return $e->die_event;