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/;
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)));
1597 first_given_name => $user->first_given_name,
1598 second_given_name => $user->second_given_name,
1599 family_name => $user->family_name,
1600 alias => $user->alias,
1601 usrname => $user->usrname
1603 fines => $fines->to_bare_hash,
1610 ##### a small consolidation of related method registrations
1611 my $common_params = [
1612 { desc => 'Authentication token', type => 'string' },
1613 { desc => 'User ID', type => 'string' },
1614 { desc => 'Transactions type (optional, defaults to all)', type => 'string' },
1615 { desc => 'Options hash. May contain limit and offset for paged results.', type => 'object' },
1618 'open-ils.actor.user.transactions' => '',
1619 'open-ils.actor.user.transactions.fleshed' => '',
1620 'open-ils.actor.user.transactions.have_charge' => ' that have an initial charge',
1621 'open-ils.actor.user.transactions.have_charge.fleshed' => ' that have an initial charge',
1622 'open-ils.actor.user.transactions.have_balance' => ' that have an outstanding balance',
1623 'open-ils.actor.user.transactions.have_balance.fleshed' => ' that have an outstanding balance',
1626 foreach (keys %methods) {
1628 method => "user_transactions",
1631 desc => 'For a given user, retrieve a list of '
1632 . (/\.fleshed/ ? 'fleshed ' : '')
1633 . 'transactions' . $methods{$_}
1634 . ' optionally limited to transactions of a given type.',
1635 params => $common_params,
1637 desc => "List of objects, or event on error. Each object is a hash containing: transaction, circ, record. "
1638 . 'These represent the relevant (mbts) transaction, attached circulation and title pointed to in the circ, respectively.',
1642 $args{authoritative} = 1;
1643 __PACKAGE__->register_method(%args);
1646 # Now for the counts
1648 'open-ils.actor.user.transactions.count' => '',
1649 'open-ils.actor.user.transactions.have_charge.count' => ' that have an initial charge',
1650 'open-ils.actor.user.transactions.have_balance.count' => ' that have an outstanding balance',
1653 foreach (keys %methods) {
1655 method => "user_transactions",
1658 desc => 'For a given user, retrieve a count of open '
1659 . 'transactions' . $methods{$_}
1660 . ' optionally limited to transactions of a given type.',
1661 params => $common_params,
1662 return => { desc => "Integer count of transactions, or event on error" }
1665 /\.have_balance/ and $args{authoritative} = 1; # FIXME: I don't know why have_charge isn't authoritative
1666 __PACKAGE__->register_method(%args);
1669 __PACKAGE__->register_method(
1670 method => "user_transactions",
1671 api_name => "open-ils.actor.user.transactions.have_balance.total",
1674 desc => 'For a given user, retrieve the total balance owed for open transactions,'
1675 . ' optionally limited to transactions of a given type.',
1676 params => $common_params,
1677 return => { desc => "Decimal balance value, or event on error" }
1682 sub user_transactions {
1683 my( $self, $client, $auth, $user_id, $type, $options ) = @_;
1686 my $e = new_editor(authtoken => $auth);
1687 return $e->event unless $e->checkauth;
1689 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
1691 return $e->event unless
1692 $e->requestor->id == $user_id or
1693 $e->allowed('VIEW_USER_TRANSACTIONS', $user->home_ou);
1695 my $api = $self->api_name();
1697 my $filter = ($api =~ /have_balance/o) ?
1698 { 'balance_owed' => { '<>' => 0 } }:
1699 { 'total_owed' => { '>' => 0 } };
1701 my $method = 'open-ils.actor.user.transactions.history.still_open';
1702 $method = "$method.authoritative" if $api => /authoritative/;
1703 my ($trans) = $self->method_lookup($method)->run($auth, $user_id, $type, $filter, $options);
1705 if($api =~ /total/o) {
1707 $total += $_->balance_owed for @$trans;
1711 ($api =~ /count/o ) and return scalar @$trans;
1712 ($api !~ /fleshed/o) and return $trans;
1715 for my $t (@$trans) {
1717 if( $t->xact_type ne 'circulation' ) {
1718 push @resp, {transaction => $t};
1722 my $circ_data = flesh_circ($e, $t->id);
1723 push @resp, {transaction => $t, %$circ_data};
1730 __PACKAGE__->register_method(
1731 method => "user_transaction_retrieve",
1732 api_name => "open-ils.actor.user.transaction.fleshed.retrieve",
1735 notes => "Returns a fleshed transaction record"
1738 __PACKAGE__->register_method(
1739 method => "user_transaction_retrieve",
1740 api_name => "open-ils.actor.user.transaction.retrieve",
1743 notes => "Returns a transaction record"
1746 sub user_transaction_retrieve {
1747 my($self, $client, $auth, $bill_id) = @_;
1749 my $e = new_editor(authtoken => $auth);
1750 return $e->event unless $e->checkauth;
1752 my $trans = $e->retrieve_money_billable_transaction_summary(
1753 [$bill_id, {flesh => 1, flesh_fields => {mbts => ['usr']}}]) or return $e->event;
1755 return $e->event unless $e->allowed('VIEW_USER_TRANSACTIONS', $trans->usr->home_ou);
1757 $trans->usr($trans->usr->id); # de-flesh for backwards compat
1759 return $trans unless $self->api_name =~ /flesh/;
1760 return {transaction => $trans} if $trans->xact_type ne 'circulation';
1762 my $circ_data = flesh_circ($e, $trans->id, 1);
1764 return {transaction => $trans, %$circ_data};
1769 my $circ_id = shift;
1770 my $flesh_copy = shift;
1772 my $circ = $e->retrieve_action_circulation([
1776 circ => ['target_copy'],
1777 acp => ['call_number'],
1784 my $copy = $circ->target_copy;
1786 if($circ->target_copy->call_number->id == OILS_PRECAT_CALL_NUMBER) {
1787 $mods = new Fieldmapper::metabib::virtual_record;
1788 $mods->doc_id(OILS_PRECAT_RECORD);
1789 $mods->title($copy->dummy_title);
1790 $mods->author($copy->dummy_author);
1793 my $u = OpenILS::Utils::ModsParser->new();
1794 $u->start_mods_batch($circ->target_copy->call_number->record->marc);
1795 $mods = $u->finish_mods_batch();
1799 $circ->target_copy($circ->target_copy->id);
1800 $copy->call_number($copy->call_number->id);
1802 return {circ => $circ, record => $mods, copy => ($flesh_copy) ? $copy : undef };
1806 __PACKAGE__->register_method(
1807 method => "hold_request_count",
1808 api_name => "open-ils.actor.user.hold_requests.count",
1811 notes => 'Returns hold ready/total counts'
1814 sub hold_request_count {
1815 my( $self, $client, $login_session, $userid ) = @_;
1817 my( $user_obj, $target, $evt ) = $apputils->checkses_requestor(
1818 $login_session, $userid, 'VIEW_HOLD' );
1819 return $evt if $evt;
1822 my $holds = $apputils->simple_scalar_request(
1824 "open-ils.cstore.direct.action.hold_request.search.atomic",
1827 fulfillment_time => {"=" => undef },
1828 cancel_time => undef,
1833 for my $h (@$holds) {
1834 next unless $h->capture_time and $h->current_copy;
1836 my $copy = $apputils->simple_scalar_request(
1838 "open-ils.cstore.direct.asset.copy.retrieve",
1842 if ($copy and $copy->status == 8) {
1847 return { total => scalar(@$holds), ready => scalar(@ready) };
1850 __PACKAGE__->register_method(
1851 method => "checked_out",
1852 api_name => "open-ils.actor.user.checked_out",
1856 desc => "For a given user, returns a structure of circulations objects sorted by out, overdue, lost, claims_returned, long_overdue. "
1857 . "A list of IDs are returned of each type. Circs marked lost, long_overdue, and claims_returned will not be 'finished' "
1858 . "(i.e., outstanding balance or some other pending action on the circ). "
1859 . "The .count method also includes a 'total' field which sums all open circs.",
1861 { desc => 'Authentication Token', type => 'string'},
1862 { desc => 'User ID', type => 'string'},
1865 desc => 'Returns event on error, or an object with ID lists, like: '
1866 . '{"out":[12552,451232], "claims_returned":[], "long_overdue":[23421] "overdue":[], "lost":[]}'
1871 __PACKAGE__->register_method(
1872 method => "checked_out",
1873 api_name => "open-ils.actor.user.checked_out.count",
1876 signature => q/@see open-ils.actor.user.checked_out/
1880 my( $self, $conn, $auth, $userid ) = @_;
1882 my $e = new_editor(authtoken=>$auth);
1883 return $e->event unless $e->checkauth;
1885 if( $userid ne $e->requestor->id ) {
1886 my $user = $e->retrieve_actor_user($userid) or return $e->event;
1887 unless($e->allowed('VIEW_CIRCULATIONS', $user->home_ou)) {
1889 # see if there is a friend link allowing circ.view perms
1890 my $allowed = OpenILS::Application::Actor::Friends->friend_perm_allowed(
1891 $e, $userid, $e->requestor->id, 'circ.view');
1892 return $e->event unless $allowed;
1896 my $count = $self->api_name =~ /count/;
1897 return _checked_out( $count, $e, $userid );
1901 my( $iscount, $e, $userid ) = @_;
1907 claims_returned => [],
1910 my $meth = 'retrieve_action_open_circ_';
1918 claims_returned => 0,
1925 my $data = $e->$meth($userid);
1929 $result{$_} += $data->$_() for (keys %result);
1930 $result{total} += $data->$_() for (keys %result);
1932 for my $k (keys %result) {
1933 $result{$k} = [ grep { $_ > 0 } split( ',', $data->$k()) ];
1943 __PACKAGE__->register_method(
1944 method => "checked_in_with_fines",
1945 api_name => "open-ils.actor.user.checked_in_with_fines",
1948 signature => q/@see open-ils.actor.user.checked_out/
1951 sub checked_in_with_fines {
1952 my( $self, $conn, $auth, $userid ) = @_;
1954 my $e = new_editor(authtoken=>$auth);
1955 return $e->event unless $e->checkauth;
1957 if( $userid ne $e->requestor->id ) {
1958 return $e->event unless $e->allowed('VIEW_CIRCULATIONS');
1961 # money is owed on these items and they are checked in
1962 my $open = $e->search_action_circulation(
1965 xact_finish => undef,
1966 checkin_time => { "!=" => undef },
1971 my( @lost, @cr, @lo );
1972 for my $c (@$open) {
1973 push( @lost, $c->id ) if $c->stop_fines eq 'LOST';
1974 push( @cr, $c->id ) if $c->stop_fines eq 'CLAIMSRETURNED';
1975 push( @lo, $c->id ) if $c->stop_fines eq 'LONGOVERDUE';
1980 claims_returned => \@cr,
1981 long_overdue => \@lo
1987 my ($api, $desc, $auth) = @_;
1988 $desc = $desc ? (" " . $desc) : '';
1989 my $ids = ($api =~ /ids$/) ? 1 : 0;
1992 method => "user_transaction_history",
1993 api_name => "open-ils.actor.user.transactions.$api",
1995 desc => "For a given User ID, returns a list of billable transaction" .
1996 ($ids ? " id" : '') .
1997 "s$desc, optionally filtered by type and/or fields in money.billable_xact_summary. " .
1998 "The VIEW_USER_TRANSACTIONS permission is required to view another user's transactions",
2000 {desc => 'Authentication token', type => 'string'},
2001 {desc => 'User ID', type => 'number'},
2002 {desc => 'Transaction type (optional)', type => 'number'},
2003 {desc => 'Hash of Billable Transaction Summary filters (optional)', type => 'object'}
2006 desc => 'List of transaction' . ($ids ? " id" : '') . 's, Event on error'
2010 $auth and push @sig, (authoritative => 1);
2014 my %auth_hist_methods = (
2016 'history.have_charge' => 'that have an initial charge',
2017 'history.still_open' => 'that are not finished',
2018 'history.have_balance' => 'that have a balance',
2019 'history.have_bill' => 'that have billings',
2020 'history.have_bill_or_payment' => 'that have non-zero-sum billings or at least 1 payment',
2021 'history.have_payment' => 'that have at least 1 payment',
2024 foreach (keys %auth_hist_methods) {
2025 __PACKAGE__->register_method(_sigmaker($_, $auth_hist_methods{$_}, 1));
2026 __PACKAGE__->register_method(_sigmaker("$_.ids", $auth_hist_methods{$_}, 1));
2027 __PACKAGE__->register_method(_sigmaker("$_.fleshed", $auth_hist_methods{$_}, 1));
2030 sub user_transaction_history {
2031 my( $self, $conn, $auth, $userid, $type, $filter, $options ) = @_;
2035 my $e = new_editor(authtoken=>$auth);
2036 return $e->die_event unless $e->checkauth;
2038 if ($e->requestor->id ne $userid) {
2039 return $e->die_event unless $e->allowed('VIEW_USER_TRANSACTIONS');
2042 my $api = $self->api_name;
2043 my @xact_finish = (xact_finish => undef ) if ($api =~ /history\.still_open$/); # What about history.still_open.ids?
2045 if(defined($type)) {
2046 $filter->{'xact_type'} = $type;
2049 if($api =~ /have_bill_or_payment/o) {
2051 # transactions that have a non-zero sum across all billings or at least 1 payment
2052 $filter->{'-or'} = {
2053 'balance_owed' => { '<>' => 0 },
2054 'last_payment_ts' => { '<>' => undef }
2057 } elsif($api =~ /have_payment/) {
2059 $filter->{last_payment_ts} ||= {'<>' => undef};
2061 } elsif( $api =~ /have_balance/o) {
2063 # transactions that have a non-zero overall balance
2064 $filter->{'balance_owed'} = { '<>' => 0 };
2066 } elsif( $api =~ /have_charge/o) {
2068 # transactions that have at least 1 billing, regardless of whether it was voided
2069 $filter->{'last_billing_ts'} = { '<>' => undef };
2071 } elsif( $api =~ /have_bill/o) { # needs to be an elsif, or we double-match have_bill_or_payment!
2073 # transactions that have non-zero sum across all billings. This will exclude
2074 # xacts where all billings have been voided
2075 $filter->{'total_owed'} = { '<>' => 0 };
2078 my $options_clause = { order_by => { mbt => 'xact_start DESC' } };
2079 $options_clause->{'limit'} = $options->{'limit'} if $options->{'limit'};
2080 $options_clause->{'offset'} = $options->{'offset'} if $options->{'offset'};
2082 my $mbts = $e->search_money_billable_transaction_summary(
2083 [ { usr => $userid, @xact_finish, %$filter },
2088 return [map {$_->id} @$mbts] if $api =~ /\.ids/;
2089 return $mbts unless $api =~ /fleshed/;
2092 for my $t (@$mbts) {
2094 if( $t->xact_type ne 'circulation' ) {
2095 push @resp, {transaction => $t};
2099 my $circ_data = flesh_circ($e, $t->id);
2100 push @resp, {transaction => $t, %$circ_data};
2108 __PACKAGE__->register_method(
2109 method => "user_perms",
2110 api_name => "open-ils.actor.permissions.user_perms.retrieve",
2112 notes => "Returns a list of permissions"
2116 my( $self, $client, $authtoken, $user ) = @_;
2118 my( $staff, $evt ) = $apputils->checkses($authtoken);
2119 return $evt if $evt;
2121 $user ||= $staff->id;
2123 if( $user != $staff->id and $evt = $apputils->check_perms( $staff->id, $staff->home_ou, 'VIEW_PERMISSION') ) {
2127 return $apputils->simple_scalar_request(
2129 "open-ils.storage.permission.user_perms.atomic",
2133 __PACKAGE__->register_method(
2134 method => "retrieve_perms",
2135 api_name => "open-ils.actor.permissions.retrieve",
2136 notes => "Returns a list of permissions"
2138 sub retrieve_perms {
2139 my( $self, $client ) = @_;
2140 return $apputils->simple_scalar_request(
2142 "open-ils.cstore.direct.permission.perm_list.search.atomic",
2143 { id => { '!=' => undef } }
2147 __PACKAGE__->register_method(
2148 method => "retrieve_groups",
2149 api_name => "open-ils.actor.groups.retrieve",
2150 notes => "Returns a list of user groups"
2152 sub retrieve_groups {
2153 my( $self, $client ) = @_;
2154 return new_editor()->retrieve_all_permission_grp_tree();
2157 __PACKAGE__->register_method(
2158 method => "retrieve_org_address",
2159 api_name => "open-ils.actor.org_unit.address.retrieve",
2160 notes => <<' NOTES');
2161 Returns an org_unit address by ID
2162 @param An org_address ID
2164 sub retrieve_org_address {
2165 my( $self, $client, $id ) = @_;
2166 return $apputils->simple_scalar_request(
2168 "open-ils.cstore.direct.actor.org_address.retrieve",
2173 __PACKAGE__->register_method(
2174 method => "retrieve_groups_tree",
2175 api_name => "open-ils.actor.groups.tree.retrieve",
2176 notes => "Returns a list of user groups"
2179 sub retrieve_groups_tree {
2180 my( $self, $client ) = @_;
2181 return new_editor()->search_permission_grp_tree(
2186 flesh_fields => { pgt => ["children"] },
2187 order_by => { pgt => 'name'}
2194 __PACKAGE__->register_method(
2195 method => "add_user_to_groups",
2196 api_name => "open-ils.actor.user.set_groups",
2197 notes => "Adds a user to one or more permission groups"
2200 sub add_user_to_groups {
2201 my( $self, $client, $authtoken, $userid, $groups ) = @_;
2203 my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2204 $authtoken, $userid, 'CREATE_USER_GROUP_LINK' );
2205 return $evt if $evt;
2207 ( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2208 $authtoken, $userid, 'REMOVE_USER_GROUP_LINK' );
2209 return $evt if $evt;
2211 $apputils->simplereq(
2213 'open-ils.storage.direct.permission.usr_grp_map.mass_delete', { usr => $userid } );
2215 for my $group (@$groups) {
2216 my $link = Fieldmapper::permission::usr_grp_map->new;
2218 $link->usr($userid);
2220 my $id = $apputils->simplereq(
2222 'open-ils.storage.direct.permission.usr_grp_map.create', $link );
2228 __PACKAGE__->register_method(
2229 method => "get_user_perm_groups",
2230 api_name => "open-ils.actor.user.get_groups",
2231 notes => "Retrieve a user's permission groups."
2235 sub get_user_perm_groups {
2236 my( $self, $client, $authtoken, $userid ) = @_;
2238 my( $requestor, $target, $evt ) = $apputils->checkses_requestor(
2239 $authtoken, $userid, 'VIEW_PERM_GROUPS' );
2240 return $evt if $evt;
2242 return $apputils->simplereq(
2244 'open-ils.cstore.direct.permission.usr_grp_map.search.atomic', { usr => $userid } );
2248 __PACKAGE__->register_method(
2249 method => "get_user_work_ous",
2250 api_name => "open-ils.actor.user.get_work_ous",
2251 notes => "Retrieve a user's work org units."
2254 __PACKAGE__->register_method(
2255 method => "get_user_work_ous",
2256 api_name => "open-ils.actor.user.get_work_ous.ids",
2257 notes => "Retrieve a user's work org units."
2260 sub get_user_work_ous {
2261 my( $self, $client, $auth, $userid ) = @_;
2262 my $e = new_editor(authtoken=>$auth);
2263 return $e->event unless $e->checkauth;
2264 $userid ||= $e->requestor->id;
2266 if($e->requestor->id != $userid) {
2267 my $user = $e->retrieve_actor_user($userid)
2268 or return $e->event;
2269 return $e->event unless $e->allowed('ASSIGN_WORK_ORG_UNIT', $user->home_ou);
2272 return $e->search_permission_usr_work_ou_map({usr => $userid})
2273 unless $self->api_name =~ /.ids$/;
2275 # client just wants a list of org IDs
2276 return $U->get_user_work_ou_ids($e, $userid);
2281 __PACKAGE__->register_method(
2282 method => 'register_workstation',
2283 api_name => 'open-ils.actor.workstation.register.override',
2284 signature => q/@see open-ils.actor.workstation.register/
2287 __PACKAGE__->register_method(
2288 method => 'register_workstation',
2289 api_name => 'open-ils.actor.workstation.register',
2291 Registers a new workstion in the system
2292 @param authtoken The login session key
2293 @param name The name of the workstation id
2294 @param owner The org unit that owns this workstation
2295 @return The workstation id on success, WORKSTATION_NAME_EXISTS
2296 if the name is already in use.
2300 sub register_workstation {
2301 my( $self, $conn, $authtoken, $name, $owner ) = @_;
2303 my $e = new_editor(authtoken=>$authtoken, xact=>1);
2304 return $e->die_event unless $e->checkauth;
2305 return $e->die_event unless $e->allowed('REGISTER_WORKSTATION', $owner);
2306 my $existing = $e->search_actor_workstation({name => $name})->[0];
2310 if( $self->api_name =~ /override/o ) {
2311 # workstation with the given name exists.
2313 if($owner ne $existing->owning_lib) {
2314 # if necessary, update the owning_lib of the workstation
2316 $logger->info("changing owning lib of workstation ".$existing->id.
2317 " from ".$existing->owning_lib." to $owner");
2318 return $e->die_event unless
2319 $e->allowed('UPDATE_WORKSTATION', $existing->owning_lib);
2321 return $e->die_event unless $e->allowed('UPDATE_WORKSTATION', $owner);
2323 $existing->owning_lib($owner);
2324 return $e->die_event unless $e->update_actor_workstation($existing);
2330 "attempt to register an existing workstation. returning existing ID");
2333 return $existing->id;
2336 return OpenILS::Event->new('WORKSTATION_NAME_EXISTS')
2340 my $ws = Fieldmapper::actor::workstation->new;
2341 $ws->owning_lib($owner);
2343 $e->create_actor_workstation($ws) or return $e->die_event;
2345 return $ws->id; # note: editor sets the id on the new object for us
2348 __PACKAGE__->register_method(
2349 method => 'workstation_list',
2350 api_name => 'open-ils.actor.workstation.list',
2352 Returns a list of workstations registered at the given location
2353 @param authtoken The login session key
2354 @param ids A list of org_unit.id's for the workstation owners
2358 sub workstation_list {
2359 my( $self, $conn, $authtoken, @orgs ) = @_;
2361 my $e = new_editor(authtoken=>$authtoken);
2362 return $e->event unless $e->checkauth;
2367 unless $e->allowed('REGISTER_WORKSTATION', $o);
2368 $results{$o} = $e->search_actor_workstation({owning_lib=>$o});
2374 __PACKAGE__->register_method(
2375 method => 'fetch_patron_note',
2376 api_name => 'open-ils.actor.note.retrieve.all',
2379 Returns a list of notes for a given user
2380 Requestor must have VIEW_USER permission if pub==false and
2381 @param authtoken The login session key
2382 @param args Hash of params including
2383 patronid : the patron's id
2384 pub : true if retrieving only public notes
2388 sub fetch_patron_note {
2389 my( $self, $conn, $authtoken, $args ) = @_;
2390 my $patronid = $$args{patronid};
2392 my($reqr, $evt) = $U->checkses($authtoken);
2393 return $evt if $evt;
2396 ($patron, $evt) = $U->fetch_user($patronid);
2397 return $evt if $evt;
2400 if( $patronid ne $reqr->id ) {
2401 $evt = $U->check_perms($reqr->id, $patron->home_ou, 'VIEW_USER');
2402 return $evt if $evt;
2404 return $U->cstorereq(
2405 'open-ils.cstore.direct.actor.usr_note.search.atomic',
2406 { usr => $patronid, pub => 't' } );
2409 $evt = $U->check_perms($reqr->id, $patron->home_ou, 'VIEW_USER');
2410 return $evt if $evt;
2412 return $U->cstorereq(
2413 'open-ils.cstore.direct.actor.usr_note.search.atomic', { usr => $patronid } );
2416 __PACKAGE__->register_method(
2417 method => 'create_user_note',
2418 api_name => 'open-ils.actor.note.create',
2420 Creates a new note for the given user
2421 @param authtoken The login session key
2422 @param note The note object
2425 sub create_user_note {
2426 my( $self, $conn, $authtoken, $note ) = @_;
2427 my $e = new_editor(xact=>1, authtoken=>$authtoken);
2428 return $e->die_event unless $e->checkauth;
2430 my $user = $e->retrieve_actor_user($note->usr)
2431 or return $e->die_event;
2433 return $e->die_event unless
2434 $e->allowed('UPDATE_USER',$user->home_ou);
2436 $note->creator($e->requestor->id);
2437 $e->create_actor_usr_note($note) or return $e->die_event;
2443 __PACKAGE__->register_method(
2444 method => 'delete_user_note',
2445 api_name => 'open-ils.actor.note.delete',
2447 Deletes a note for the given user
2448 @param authtoken The login session key
2449 @param noteid The note id
2452 sub delete_user_note {
2453 my( $self, $conn, $authtoken, $noteid ) = @_;
2455 my $e = new_editor(xact=>1, authtoken=>$authtoken);
2456 return $e->die_event unless $e->checkauth;
2457 my $note = $e->retrieve_actor_usr_note($noteid)
2458 or return $e->die_event;
2459 my $user = $e->retrieve_actor_user($note->usr)
2460 or return $e->die_event;
2461 return $e->die_event unless
2462 $e->allowed('UPDATE_USER', $user->home_ou);
2464 $e->delete_actor_usr_note($note) or return $e->die_event;
2470 __PACKAGE__->register_method(
2471 method => 'update_user_note',
2472 api_name => 'open-ils.actor.note.update',
2474 @param authtoken The login session key
2475 @param note The note
2479 sub update_user_note {
2480 my( $self, $conn, $auth, $note ) = @_;
2481 my $e = new_editor(authtoken=>$auth, xact=>1);
2482 return $e->die_event unless $e->checkauth;
2483 my $patron = $e->retrieve_actor_user($note->usr)
2484 or return $e->die_event;
2485 return $e->die_event unless
2486 $e->allowed('UPDATE_USER', $patron->home_ou);
2487 $e->update_actor_user_note($note)
2488 or return $e->die_event;
2495 __PACKAGE__->register_method(
2496 method => 'create_closed_date',
2497 api_name => 'open-ils.actor.org_unit.closed_date.create',
2499 Creates a new closing entry for the given org_unit
2500 @param authtoken The login session key
2501 @param note The closed_date object
2504 sub create_closed_date {
2505 my( $self, $conn, $authtoken, $cd ) = @_;
2507 my( $user, $evt ) = $U->checkses($authtoken);
2508 return $evt if $evt;
2510 $evt = $U->check_perms($user->id, $cd->org_unit, 'CREATE_CLOSEING');
2511 return $evt if $evt;
2513 $logger->activity("user ".$user->id." creating library closing for ".$cd->org_unit);
2515 my $id = $U->storagereq(
2516 'open-ils.storage.direct.actor.org_unit.closed_date.create', $cd );
2517 return $U->DB_UPDATE_FAILED($cd) unless $id;
2522 __PACKAGE__->register_method(
2523 method => 'delete_closed_date',
2524 api_name => 'open-ils.actor.org_unit.closed_date.delete',
2526 Deletes a closing entry for the given org_unit
2527 @param authtoken The login session key
2528 @param noteid The close_date id
2531 sub delete_closed_date {
2532 my( $self, $conn, $authtoken, $cd ) = @_;
2534 my( $user, $evt ) = $U->checkses($authtoken);
2535 return $evt if $evt;
2538 ($cd_obj, $evt) = fetch_closed_date($cd);
2539 return $evt if $evt;
2541 $evt = $U->check_perms($user->id, $cd->org_unit, 'DELETE_CLOSEING');
2542 return $evt if $evt;
2544 $logger->activity("user ".$user->id." deleting library closing for ".$cd->org_unit);
2546 my $stat = $U->storagereq(
2547 'open-ils.storage.direct.actor.org_unit.closed_date.delete', $cd );
2548 return $U->DB_UPDATE_FAILED($cd) unless $stat;
2553 __PACKAGE__->register_method(
2554 method => 'usrname_exists',
2555 api_name => 'open-ils.actor.username.exists',
2557 desc => 'Check if a username is already taken (by an undeleted patron)',
2559 {desc => 'Authentication token', type => 'string'},
2560 {desc => 'Username', type => 'string'}
2563 desc => 'id of existing user if username exists, undef otherwise. Event on error'
2568 sub usrname_exists {
2569 my( $self, $conn, $auth, $usrname ) = @_;
2570 my $e = new_editor(authtoken=>$auth);
2571 return $e->event unless $e->checkauth;
2572 my $a = $e->search_actor_user({usrname => $usrname, deleted=>'f'}, {idlist=>1});
2573 return $$a[0] if $a and @$a;
2577 __PACKAGE__->register_method(
2578 method => 'barcode_exists',
2579 api_name => 'open-ils.actor.barcode.exists',
2581 signature => 'Returns 1 if the requested barcode exists, returns 0 otherwise'
2584 sub barcode_exists {
2585 my( $self, $conn, $auth, $barcode ) = @_;
2586 my $e = new_editor(authtoken=>$auth);
2587 return $e->event unless $e->checkauth;
2588 my $card = $e->search_actor_card({barcode => $barcode});
2594 #return undef unless @$card;
2595 #return $card->[0]->usr;
2599 __PACKAGE__->register_method(
2600 method => 'retrieve_net_levels',
2601 api_name => 'open-ils.actor.net_access_level.retrieve.all',
2604 sub retrieve_net_levels {
2605 my( $self, $conn, $auth ) = @_;
2606 my $e = new_editor(authtoken=>$auth);
2607 return $e->event unless $e->checkauth;
2608 return $e->retrieve_all_config_net_access_level();
2611 # Retain the old typo API name just in case
2612 __PACKAGE__->register_method(
2613 method => 'fetch_org_by_shortname',
2614 api_name => 'open-ils.actor.org_unit.retrieve_by_shorname',
2616 __PACKAGE__->register_method(
2617 method => 'fetch_org_by_shortname',
2618 api_name => 'open-ils.actor.org_unit.retrieve_by_shortname',
2620 sub fetch_org_by_shortname {
2621 my( $self, $conn, $sname ) = @_;
2622 my $e = new_editor();
2623 my $org = $e->search_actor_org_unit({ shortname => uc($sname)})->[0];
2624 return $e->event unless $org;
2629 __PACKAGE__->register_method(
2630 method => 'session_home_lib',
2631 api_name => 'open-ils.actor.session.home_lib',
2634 sub session_home_lib {
2635 my( $self, $conn, $auth ) = @_;
2636 my $e = new_editor(authtoken=>$auth);
2637 return undef unless $e->checkauth;
2638 my $org = $e->retrieve_actor_org_unit($e->requestor->home_ou);
2639 return $org->shortname;
2642 __PACKAGE__->register_method(
2643 method => 'session_safe_token',
2644 api_name => 'open-ils.actor.session.safe_token',
2646 Returns a hashed session ID that is safe for export to the world.
2647 This safe token will expire after 1 hour of non-use.
2648 @param auth Active authentication token
2652 sub session_safe_token {
2653 my( $self, $conn, $auth ) = @_;
2654 my $e = new_editor(authtoken=>$auth);
2655 return undef unless $e->checkauth;
2657 my $safe_token = md5_hex($auth);
2659 $cache ||= OpenSRF::Utils::Cache->new("global", 0);
2661 # Add more like the following if needed...
2663 "safe-token-home_lib-shortname-$safe_token",
2664 $e->retrieve_actor_org_unit(
2665 $e->requestor->home_ou
2674 __PACKAGE__->register_method(
2675 method => 'safe_token_home_lib',
2676 api_name => 'open-ils.actor.safe_token.home_lib.shortname',
2678 Returns the home library shortname from the session
2679 asscociated with a safe token from generated by
2680 open-ils.actor.session.safe_token.
2681 @param safe_token Active safe token
2685 sub safe_token_home_lib {
2686 my( $self, $conn, $safe_token ) = @_;
2688 $cache ||= OpenSRF::Utils::Cache->new("global", 0);
2689 return $cache->get_cache( 'safe-token-home_lib-shortname-'. $safe_token );
2694 __PACKAGE__->register_method(
2695 method => 'slim_tree',
2696 api_name => "open-ils.actor.org_tree.slim_hash.retrieve",
2699 my $tree = new_editor()->search_actor_org_unit(
2701 {"parent_ou" => undef },
2704 flesh_fields => { aou => ['children'] },
2705 order_by => { aou => 'name'},
2706 select => { aou => ["id","shortname", "name"]},
2711 return trim_tree($tree);
2717 return undef unless $tree;
2719 code => $tree->shortname,
2720 name => $tree->name,
2722 if( $tree->children and @{$tree->children} ) {
2723 $htree->{children} = [];
2724 for my $c (@{$tree->children}) {
2725 push( @{$htree->{children}}, trim_tree($c) );
2733 __PACKAGE__->register_method(
2734 method => "update_penalties",
2735 api_name => "open-ils.actor.user.penalties.update"
2738 sub update_penalties {
2739 my($self, $conn, $auth, $user_id) = @_;
2740 my $e = new_editor(authtoken=>$auth, xact => 1);
2741 return $e->die_event unless $e->checkauth;
2742 my $user = $e->retrieve_actor_user($user_id) or return $e->die_event;
2743 return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
2744 my $evt = OpenILS::Utils::Penalty->calculate_penalties($e, $user_id, $e->requestor->ws_ou);
2745 return $evt if $evt;
2751 __PACKAGE__->register_method(
2752 method => "apply_penalty",
2753 api_name => "open-ils.actor.user.penalty.apply"
2757 my($self, $conn, $auth, $penalty) = @_;
2759 my $e = new_editor(authtoken=>$auth, xact => 1);
2760 return $e->die_event unless $e->checkauth;
2762 my $user = $e->retrieve_actor_user($penalty->usr) or return $e->die_event;
2763 return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
2765 my $ptype = $e->retrieve_config_standing_penalty($penalty->standing_penalty) or return $e->die_event;
2768 (defined $ptype->org_depth) ?
2769 $U->org_unit_ancestor_at_depth($penalty->org_unit, $ptype->org_depth) :
2772 $penalty->org_unit($ctx_org);
2773 $penalty->staff($e->requestor->id);
2774 $e->create_actor_user_standing_penalty($penalty) or return $e->die_event;
2777 return $penalty->id;
2780 __PACKAGE__->register_method(
2781 method => "remove_penalty",
2782 api_name => "open-ils.actor.user.penalty.remove"
2785 sub remove_penalty {
2786 my($self, $conn, $auth, $penalty) = @_;
2787 my $e = new_editor(authtoken=>$auth, xact => 1);
2788 return $e->die_event unless $e->checkauth;
2789 my $user = $e->retrieve_actor_user($penalty->usr) or return $e->die_event;
2790 return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
2792 $e->delete_actor_user_standing_penalty($penalty) or return $e->die_event;
2797 __PACKAGE__->register_method(
2798 method => "update_penalty_note",
2799 api_name => "open-ils.actor.user.penalty.note.update"
2802 sub update_penalty_note {
2803 my($self, $conn, $auth, $penalty_ids, $note) = @_;
2804 my $e = new_editor(authtoken=>$auth, xact => 1);
2805 return $e->die_event unless $e->checkauth;
2806 for my $penalty_id (@$penalty_ids) {
2807 my $penalty = $e->search_actor_user_standing_penalty( { id => $penalty_id } )->[0];
2808 if (! $penalty ) { return $e->die_event; }
2809 my $user = $e->retrieve_actor_user($penalty->usr) or return $e->die_event;
2810 return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
2812 $penalty->note( $note ); $penalty->ischanged( 1 );
2814 $e->update_actor_user_standing_penalty($penalty) or return $e->die_event;
2820 __PACKAGE__->register_method(
2821 method => "ranged_penalty_thresholds",
2822 api_name => "open-ils.actor.grp_penalty_threshold.ranged.retrieve",
2826 sub ranged_penalty_thresholds {
2827 my($self, $conn, $auth, $context_org) = @_;
2828 my $e = new_editor(authtoken=>$auth);
2829 return $e->event unless $e->checkauth;
2830 return $e->event unless $e->allowed('VIEW_GROUP_PENALTY_THRESHOLD', $context_org);
2831 my $list = $e->search_permission_grp_penalty_threshold([
2832 {org_unit => $U->get_org_ancestors($context_org)},
2833 {order_by => {pgpt => 'id'}}
2835 $conn->respond($_) for @$list;
2841 __PACKAGE__->register_method(
2842 method => "user_retrieve_fleshed_by_id",
2844 api_name => "open-ils.actor.user.fleshed.retrieve",
2847 sub user_retrieve_fleshed_by_id {
2848 my( $self, $client, $auth, $user_id, $fields ) = @_;
2849 my $e = new_editor(authtoken => $auth);
2850 return $e->event unless $e->checkauth;
2852 if( $e->requestor->id != $user_id ) {
2853 return $e->event unless $e->allowed('VIEW_USER');
2859 "standing_penalties",
2863 "stat_cat_entries" ];
2864 return new_flesh_user($user_id, $fields, $e);
2868 sub new_flesh_user {
2871 my $fields = shift || [];
2874 my $fetch_penalties = 0;
2875 if(grep {$_ eq 'standing_penalties'} @$fields) {
2876 $fields = [grep {$_ ne 'standing_penalties'} @$fields];
2877 $fetch_penalties = 1;
2880 my $user = $e->retrieve_actor_user(
2885 "flesh_fields" => { "au" => $fields }
2888 ) or return $e->die_event;
2891 if( grep { $_ eq 'addresses' } @$fields ) {
2893 $user->addresses([]) unless @{$user->addresses};
2894 # don't expose "replaced" addresses by default
2895 $user->addresses([grep {$_->id >= 0} @{$user->addresses}]);
2897 if( ref $user->billing_address ) {
2898 unless( grep { $user->billing_address->id == $_->id } @{$user->addresses} ) {
2899 push( @{$user->addresses}, $user->billing_address );
2903 if( ref $user->mailing_address ) {
2904 unless( grep { $user->mailing_address->id == $_->id } @{$user->addresses} ) {
2905 push( @{$user->addresses}, $user->mailing_address );
2910 if($fetch_penalties) {
2911 # grab the user penalties ranged for this location
2912 $user->standing_penalties(
2913 $e->search_actor_user_standing_penalty([
2916 {stop_date => undef},
2917 {stop_date => {'>' => 'now'}}
2919 org_unit => $U->get_org_ancestors($e->requestor->ws_ou)
2922 flesh_fields => {ausp => ['standing_penalty']}
2929 $user->clear_passwd();
2936 __PACKAGE__->register_method(
2937 method => "user_retrieve_parts",
2938 api_name => "open-ils.actor.user.retrieve.parts",
2941 sub user_retrieve_parts {
2942 my( $self, $client, $auth, $user_id, $fields ) = @_;
2943 my $e = new_editor(authtoken => $auth);
2944 return $e->event unless $e->checkauth;
2945 $user_id ||= $e->requestor->id;
2946 if( $e->requestor->id != $user_id ) {
2947 return $e->event unless $e->allowed('VIEW_USER');
2950 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
2951 push(@resp, $user->$_()) for(@$fields);
2957 __PACKAGE__->register_method(
2958 method => 'user_opt_in_enabled',
2959 api_name => 'open-ils.actor.user.org_unit_opt_in.enabled',
2960 signature => '@return 1 if user opt-in is globally enabled, 0 otherwise.'
2963 sub user_opt_in_enabled {
2964 my($self, $conn) = @_;
2965 my $sc = OpenSRF::Utils::SettingsClient->new;
2966 return 1 if lc($sc->config_value(share => user => 'opt_in')) eq 'true';
2971 __PACKAGE__->register_method(
2972 method => 'user_opt_in_at_org',
2973 api_name => 'open-ils.actor.user.org_unit_opt_in.check',
2975 @param $auth The auth token
2976 @param user_id The ID of the user to test
2977 @return 1 if the user has opted in at the specified org,
2978 event on error, and 0 otherwise. /
2980 sub user_opt_in_at_org {
2981 my($self, $conn, $auth, $user_id) = @_;
2983 # see if we even need to enforce the opt-in value
2984 return 1 unless user_opt_in_enabled($self);
2986 my $e = new_editor(authtoken => $auth);
2987 return $e->event unless $e->checkauth;
2988 my $org_id = $e->requestor->ws_ou;
2990 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
2991 return $e->event unless $e->allowed('VIEW_USER', $user->home_ou);
2993 # user is automatically opted-in at the home org
2994 return 1 if $user->home_ou eq $org_id;
2996 my $vals = $e->search_actor_usr_org_unit_opt_in(
2997 {org_unit=>$org_id, usr=>$user_id},{idlist=>1});
3003 __PACKAGE__->register_method(
3004 method => 'create_user_opt_in_at_org',
3005 api_name => 'open-ils.actor.user.org_unit_opt_in.create',
3007 @param $auth The auth token
3008 @param user_id The ID of the user to test
3009 @return The ID of the newly created object, event on error./
3012 sub create_user_opt_in_at_org {
3013 my($self, $conn, $auth, $user_id) = @_;
3015 my $e = new_editor(authtoken => $auth, xact=>1);
3016 return $e->die_event unless $e->checkauth;
3017 my $org_id = $e->requestor->ws_ou;
3019 my $user = $e->retrieve_actor_user($user_id) or return $e->die_event;
3020 return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
3022 my $opt_in = Fieldmapper::actor::usr_org_unit_opt_in->new;
3024 $opt_in->org_unit($org_id);
3025 $opt_in->usr($user_id);
3026 $opt_in->staff($e->requestor->id);
3027 $opt_in->opt_in_ts('now');
3028 $opt_in->opt_in_ws($e->requestor->wsid);
3030 $opt_in = $e->create_actor_usr_org_unit_opt_in($opt_in)
3031 or return $e->die_event;
3039 __PACKAGE__->register_method (
3040 method => 'retrieve_org_hours',
3041 api_name => 'open-ils.actor.org_unit.hours_of_operation.retrieve',
3043 Returns the hours of operation for a specified org unit
3044 @param authtoken The login session key
3045 @param org_id The org_unit ID
3049 sub retrieve_org_hours {
3050 my($self, $conn, $auth, $org_id) = @_;
3051 my $e = new_editor(authtoken => $auth);
3052 return $e->die_event unless $e->checkauth;
3053 $org_id ||= $e->requestor->ws_ou;
3054 return $e->retrieve_actor_org_unit_hours_of_operation($org_id);
3058 __PACKAGE__->register_method (
3059 method => 'verify_user_password',
3060 api_name => 'open-ils.actor.verify_user_password',
3062 Given a barcode or username and the MD5 encoded password,
3063 returns 1 if the password is correct. Returns 0 otherwise.
3067 sub verify_user_password {
3068 my($self, $conn, $auth, $barcode, $username, $password) = @_;
3069 my $e = new_editor(authtoken => $auth);
3070 return $e->die_event unless $e->checkauth;
3072 my $user_by_barcode;
3073 my $user_by_username;
3075 my $card = $e->search_actor_card([
3076 {barcode => $barcode},
3077 {flesh => 1, flesh_fields => {ac => ['usr']}}])->[0] or return 0;
3078 $user_by_barcode = $card->usr;
3079 $user = $user_by_barcode;
3082 $user_by_username = $e->search_actor_user({usrname => $username})->[0] or return 0;
3083 $user = $user_by_username;
3085 return 0 if (!$user);
3086 return 0 if ($user_by_username && $user_by_barcode && $user_by_username->id != $user_by_barcode->id);
3087 return $e->event unless $e->allowed('VIEW_USER', $user->home_ou);
3088 return 1 if $user->passwd eq $password;
3092 __PACKAGE__->register_method (
3093 method => 'retrieve_usr_id_via_barcode_or_usrname',
3094 api_name => "open-ils.actor.user.retrieve_id_by_barcode_or_username",
3096 Given a barcode or username returns the id for the user or
3101 sub retrieve_usr_id_via_barcode_or_usrname {
3102 my($self, $conn, $auth, $barcode, $username) = @_;
3103 my $e = new_editor(authtoken => $auth);
3104 return $e->die_event unless $e->checkauth;
3105 my $id_as_barcode= OpenSRF::Utils::SettingsClient->new->config_value(apps => 'open-ils.actor' => app_settings => 'id_as_barcode');
3107 my $user_by_barcode;
3108 my $user_by_username;
3109 $logger->info("$id_as_barcode is the ID as BARCODE");
3111 my $card = $e->search_actor_card([
3112 {barcode => $barcode},
3113 {flesh => 1, flesh_fields => {ac => ['usr']}}])->[0];
3114 if ($id_as_barcode =~ /^t/i) {
3116 $user = $e->retrieve_actor_user($barcode);
3117 return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' ) if(!$user);
3119 $user_by_barcode = $card->usr;
3120 $user = $user_by_barcode;
3123 return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' ) if(!$card);
3124 $user_by_barcode = $card->usr;
3125 $user = $user_by_barcode;
3130 $user_by_username = $e->search_actor_user({usrname => $username})->[0] or return OpenILS::Event->new( 'ACTOR_USR_NOT_FOUND' );
3132 $user = $user_by_username;
3134 return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' ) if (!$user);
3135 return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' ) if ($user_by_username && $user_by_barcode && $user_by_username->id != $user_by_barcode->id);
3136 return $e->event unless $e->allowed('VIEW_USER', $user->home_ou);
3141 __PACKAGE__->register_method (
3142 method => 'merge_users',
3143 api_name => 'open-ils.actor.user.merge',
3146 Given a list of source users and destination user, transfer all data from the source
3147 to the dest user and delete the source user. All user related data is
3148 transferred, including circulations, holds, bookbags, etc.
3154 my($self, $conn, $auth, $master_id, $user_ids, $options) = @_;
3155 my $e = new_editor(xact => 1, authtoken => $auth);
3156 return $e->die_event unless $e->checkauth;
3158 # disallow the merge if any subordinate accounts are in collections
3159 my $colls = $e->search_money_collections_tracker({usr => $user_ids}, {idlist => 1});
3160 return OpenILS::Event->new('MERGED_USER_IN_COLLECTIONS', payload => $user_ids) if @$colls;
3162 my $master_user = $e->retrieve_actor_user($master_id) or return $e->die_event;
3163 my $del_addrs = ($U->ou_ancestor_setting_value(
3164 $master_user->home_ou, 'circ.user_merge.delete_addresses', $e)) ? 't' : 'f';
3165 my $del_cards = ($U->ou_ancestor_setting_value(
3166 $master_user->home_ou, 'circ.user_merge.delete_cards', $e)) ? 't' : 'f';
3167 my $deactivate_cards = ($U->ou_ancestor_setting_value(
3168 $master_user->home_ou, 'circ.user_merge.deactivate_cards', $e)) ? 't' : 'f';
3170 for my $src_id (@$user_ids) {
3171 my $src_user = $e->retrieve_actor_user($src_id) or return $e->die_event;
3173 return $e->die_event unless $e->allowed('MERGE_USERS', $src_user->home_ou);
3174 if($src_user->home_ou ne $master_user->home_ou) {
3175 return $e->die_event unless $e->allowed('MERGE_USERS', $master_user->home_ou);
3178 return $e->die_event unless
3179 $e->json_query({from => [
3194 __PACKAGE__->register_method (
3195 method => 'approve_user_address',
3196 api_name => 'open-ils.actor.user.pending_address.approve',
3203 sub approve_user_address {
3204 my($self, $conn, $auth, $addr) = @_;
3205 my $e = new_editor(xact => 1, authtoken => $auth);
3206 return $e->die_event unless $e->checkauth;
3208 # if the caller passes an address object, assume they want to
3209 # update it first before approving it
3210 $e->update_actor_user_address($addr) or return $e->die_event;
3212 $addr = $e->retrieve_actor_user_address($addr) or return $e->die_event;
3214 my $user = $e->retrieve_actor_user($addr->usr);
3215 return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
3216 my $result = $e->json_query({from => ['actor.approve_pending_address', $addr->id]})->[0]
3217 or return $e->die_event;
3219 return [values %$result]->[0];
3223 __PACKAGE__->register_method (
3224 method => 'retrieve_friends',
3225 api_name => 'open-ils.actor.friends.retrieve',
3228 returns { confirmed: [], pending_out: [], pending_in: []}
3229 pending_out are users I'm requesting friendship with
3230 pending_in are users requesting friendship with me
3235 sub retrieve_friends {
3236 my($self, $conn, $auth, $user_id, $options) = @_;
3237 my $e = new_editor(authtoken => $auth);
3238 return $e->event unless $e->checkauth;
3239 $user_id ||= $e->requestor->id;
3241 if($user_id != $e->requestor->id) {
3242 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
3243 return $e->event unless $e->allowed('VIEW_USER', $user->home_ou);
3246 return OpenILS::Application::Actor::Friends->retrieve_friends(
3247 $e, $user_id, $options);
3252 __PACKAGE__->register_method (
3253 method => 'apply_friend_perms',
3254 api_name => 'open-ils.actor.friends.perms.apply',
3260 sub apply_friend_perms {
3261 my($self, $conn, $auth, $user_id, $delegate_id, @perms) = @_;
3262 my $e = new_editor(authtoken => $auth, xact => 1);
3263 return $e->die_event unless $e->checkauth;
3265 if($user_id != $e->requestor->id) {
3266 my $user = $e->retrieve_actor_user($user_id) or return $e->die_event;
3267 return $e->die_event unless $e->allowed('VIEW_USER', $user->home_ou);
3270 for my $perm (@perms) {
3272 OpenILS::Application::Actor::Friends->apply_friend_perm(
3273 $e, $user_id, $delegate_id, $perm);
3274 return $evt if $evt;
3282 __PACKAGE__->register_method (
3283 method => 'update_user_pending_address',
3284 api_name => 'open-ils.actor.user.address.pending.cud'
3287 sub update_user_pending_address {
3288 my($self, $conn, $auth, $addr) = @_;
3289 my $e = new_editor(authtoken => $auth, xact => 1);
3290 return $e->die_event unless $e->checkauth;
3292 if($addr->usr != $e->requestor->id) {
3293 my $user = $e->retrieve_actor_user($addr->usr) or return $e->die_event;
3294 return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
3298 $e->create_actor_user_address($addr) or return $e->die_event;
3299 } elsif($addr->isdeleted) {
3300 $e->delete_actor_user_address($addr) or return $e->die_event;
3302 $e->update_actor_user_address($addr) or return $e->die_event;
3310 __PACKAGE__->register_method (
3311 method => 'user_events',
3312 api_name => 'open-ils.actor.user.events.circ',
3315 __PACKAGE__->register_method (
3316 method => 'user_events',
3317 api_name => 'open-ils.actor.user.events.ahr',
3322 my($self, $conn, $auth, $user_id, $filters) = @_;
3323 my $e = new_editor(authtoken => $auth);
3324 return $e->event unless $e->checkauth;
3326 (my $obj_type = $self->api_name) =~ s/.*\.([a-z]+)$/$1/;
3327 my $user_field = 'usr';
3330 $filters->{target} = {
3331 select => { $obj_type => ['id'] },
3333 where => {usr => $user_id}
3336 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
3337 if($e->requestor->id != $user_id) {
3338 return $e->event unless $e->allowed('VIEW_USER', $user->home_ou);
3341 my $ses = OpenSRF::AppSession->create('open-ils.trigger');
3342 my $req = $ses->request('open-ils.trigger.events_by_target',
3343 $obj_type, $filters, {atevdef => ['reactor', 'validator']}, 2);
3345 while(my $resp = $req->recv) {
3346 my $val = $resp->content;
3347 my $tgt = $val->target;
3349 if($obj_type eq 'circ') {
3350 $tgt->target_copy($e->retrieve_asset_copy($tgt->target_copy));
3352 } elsif($obj_type eq 'ahr') {
3353 $tgt->current_copy($e->retrieve_asset_copy($tgt->current_copy))
3354 if $tgt->current_copy;
3357 $conn->respond($val) if $val;
3363 __PACKAGE__->register_method (
3364 method => 'copy_events',
3365 api_name => 'open-ils.actor.copy.events.circ',
3368 __PACKAGE__->register_method (
3369 method => 'copy_events',
3370 api_name => 'open-ils.actor.copy.events.ahr',
3375 my($self, $conn, $auth, $copy_id, $filters) = @_;
3376 my $e = new_editor(authtoken => $auth);
3377 return $e->event unless $e->checkauth;
3379 (my $obj_type = $self->api_name) =~ s/.*\.([a-z]+)$/$1/;
3381 my $copy = $e->retrieve_asset_copy($copy_id) or return $e->event;
3383 my $copy_field = 'target_copy';
3384 $copy_field = 'current_copy' if $obj_type eq 'ahr';
3387 $filters->{target} = {
3388 select => { $obj_type => ['id'] },
3390 where => {$copy_field => $copy_id}
3394 my $ses = OpenSRF::AppSession->create('open-ils.trigger');
3395 my $req = $ses->request('open-ils.trigger.events_by_target',
3396 $obj_type, $filters, {atevdef => ['reactor', 'validator']}, 2);
3398 while(my $resp = $req->recv) {
3399 my $val = $resp->content;
3400 my $tgt = $val->target;
3402 my $user = $e->retrieve_actor_user($tgt->usr);
3403 if($e->requestor->id != $user->id) {
3404 return $e->event unless $e->allowed('VIEW_USER', $user->home_ou);
3407 $tgt->$copy_field($copy);
3410 $conn->respond($val) if $val;
3419 __PACKAGE__->register_method (
3420 method => 'update_events',
3421 api_name => 'open-ils.actor.user.event.cancel.batch',
3424 __PACKAGE__->register_method (
3425 method => 'update_events',
3426 api_name => 'open-ils.actor.user.event.reset.batch',
3431 my($self, $conn, $auth, $event_ids) = @_;
3432 my $e = new_editor(xact => 1, authtoken => $auth);
3433 return $e->die_event unless $e->checkauth;
3436 for my $id (@$event_ids) {
3438 # do a little dance to determine what user we are ultimately affecting
3439 my $event = $e->retrieve_action_trigger_event([
3442 flesh_fields => {atev => ['event_def'], atevdef => ['hook']}
3444 ]) or return $e->die_event;
3447 if($event->event_def->hook->core_type eq 'circ') {
3448 $user_id = $e->retrieve_action_circulation($event->target)->usr;
3449 } elsif($event->event_def->hook->core_type eq 'ahr') {
3450 $user_id = $e->retrieve_action_hold_request($event->target)->usr;
3455 my $user = $e->retrieve_actor_user($user_id);
3456 return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
3458 if($self->api_name =~ /cancel/) {
3459 $event->state('invalid');
3460 } elsif($self->api_name =~ /reset/) {
3461 $event->clear_start_time;
3462 $event->clear_update_time;
3463 $event->state('pending');
3466 $e->update_action_trigger_event($event) or return $e->die_event;
3467 $conn->respond({maximum => scalar(@$event_ids), progress => $x++});
3471 return {complete => 1};
3475 __PACKAGE__->register_method (
3476 method => 'really_delete_user',
3477 api_name => 'open-ils.actor.user.delete',
3479 It anonymizes all personally identifiable information in actor.usr. By calling actor.usr_purge_data()
3480 it also purges related data from other tables, sometimes by transferring it to a designated destination user.
3481 The usrname field (along with first_given_name and family_name) is updated to id '-PURGED-' now().
3482 dest_usr_id is only required when deleting a user that performs staff functions.
3486 sub really_delete_user {
3487 my($self, $conn, $auth, $user_id, $dest_user_id) = @_;
3488 my $e = new_editor(authtoken => $auth, xact => 1);
3489 return $e->die_event unless $e->checkauth;
3490 my $user = $e->retrieve_actor_user($user_id) or return $e->die_event;
3491 return $e->die_event unless $e->allowed('DELETE_USER', $user->home_ou);
3492 my $stat = $e->json_query(
3493 {from => ['actor.usr_delete', $user_id, $dest_user_id]})->[0]
3494 or return $e->die_event;
3501 __PACKAGE__->register_method (
3502 method => 'user_payments',
3503 api_name => 'open-ils.actor.user.payments.retrieve',
3506 Returns all payments for a given user. Default order is newest payments first.
3507 @param auth Authentication token
3508 @param user_id The user ID
3509 @param filters An optional hash of filters, including limit, offset, and order_by definitions
3514 my($self, $conn, $auth, $user_id, $filters) = @_;
3517 my $e = new_editor(authtoken => $auth);
3518 return $e->die_event unless $e->checkauth;
3520 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
3521 return $e->event unless
3522 $e->requestor->id == $user_id or
3523 $e->allowed('VIEW_USER_TRANSACTIONS', $user->home_ou);
3525 # Find all payments for all transactions for user $user_id
3527 select => {mp => ['id']},
3532 select => {mbt => ['id']},
3534 where => {usr => $user_id}
3538 order_by => [{ # by default, order newest payments first
3540 field => 'payment_ts',
3545 for (qw/order_by limit offset/) {
3546 $query->{$_} = $filters->{$_} if defined $filters->{$_};
3549 if(defined $filters->{where}) {
3550 foreach (keys %{$filters->{where}}) {
3551 # don't allow the caller to expand the result set to other users
3552 $query->{where}->{$_} = $filters->{where}->{$_} unless $_ eq 'xact';
3556 my $payment_ids = $e->json_query($query);
3557 for my $pid (@$payment_ids) {
3558 my $pay = $e->retrieve_money_payment([
3563 mbt => ['summary', 'circulation', 'grocery'],
3564 circ => ['target_copy'],
3565 acp => ['call_number'],
3573 xact_type => $pay->xact->summary->xact_type,
3574 last_billing_type => $pay->xact->summary->last_billing_type,
3577 if($pay->xact->summary->xact_type eq 'circulation') {
3578 $resp->{barcode} = $pay->xact->circulation->target_copy->barcode;
3579 $resp->{title} = $U->record_to_mvr($pay->xact->circulation->target_copy->call_number->record)->title;
3582 $pay->xact($pay->xact->id); # de-flesh
3583 $conn->respond($resp);
3591 __PACKAGE__->register_method (
3592 method => 'negative_balance_users',
3593 api_name => 'open-ils.actor.users.negative_balance',
3596 Returns all users that have an overall negative balance
3597 @param auth Authentication token
3598 @param org_id The context org unit as an ID or list of IDs. This will be the home
3599 library of the user. If no org_unit is specified, no org unit filter is applied
3603 sub negative_balance_users {
3604 my($self, $conn, $auth, $org_id) = @_;
3606 my $e = new_editor(authtoken => $auth);
3607 return $e->die_event unless $e->checkauth;
3608 return $e->die_event unless $e->allowed('VIEW_USER', $org_id);
3612 mous => ['usr', 'balance_owed'],
3615 {column => 'last_billing_ts', transform => 'max', aggregate => 1},
3616 {column => 'last_payment_ts', transform => 'max', aggregate => 1},
3633 where => {'+mous' => {balance_owed => {'<' => 0}}}
3636 $query->{from}->{mous}->{au}->{filter}->{home_ou} = $org_id if $org_id;
3638 my $list = $e->json_query($query, {timeout => 600});
3640 for my $data (@$list) {
3642 usr => $e->retrieve_actor_user([$data->{usr}, {flesh => 1, flesh_fields => {au => ['card']}}]),
3643 balance_owed => $data->{balance_owed},
3644 last_billing_activity => max($data->{last_billing_ts}, $data->{last_payment_ts})
3651 __PACKAGE__->register_method(
3652 method => "request_password_reset",
3653 api_name => "open-ils.actor.patron.password_reset.request",
3655 desc => "Generates a UUID token usable with the open-ils.actor.patron.password_reset.commit " .
3656 "method for changing a user's password. The UUID token is distributed via A/T " .
3657 "templates (i.e. email to the user).",
3659 { desc => 'user_id_type', type => 'string' },
3660 { desc => 'user_id', type => 'string' },
3661 { desc => 'optional (based on library setting) matching email address for authorizing request', type => 'string' },
3663 return => {desc => '1 on success, Event on error'}
3666 sub request_password_reset {
3667 my($self, $conn, $user_id_type, $user_id, $email) = @_;
3669 # Check to see if password reset requests are already being throttled:
3670 # 0. Check cache to see if we're in throttle mode (avoid hitting database)
3672 my $e = new_editor(xact => 1);
3675 # Get the user, if any, depending on the input value
3676 if ($user_id_type eq 'username') {
3677 $user = $e->search_actor_user({usrname => $user_id})->[0];
3680 return OpenILS::Event->new( 'ACTOR_USER_NOT_FOUND' );
3682 } elsif ($user_id_type eq 'barcode') {
3683 my $card = $e->search_actor_card([
3684 {barcode => $user_id},
3685 {flesh => 1, flesh_fields => {ac => ['usr']}}])->[0];
3688 return OpenILS::Event->new('ACTOR_USER_NOT_FOUND');
3693 # If the user doesn't have an email address, we can't help them
3694 if (!$user->email) {
3696 return OpenILS::Event->new('PATRON_NO_EMAIL_ADDRESS');
3699 my $email_must_match = $U->ou_ancestor_setting_value($user->home_ou, 'circ.password_reset_request_requires_matching_email');
3700 if ($email_must_match) {
3701 if ($user->email ne $email) {
3702 return OpenILS::Event->new('EMAIL_VERIFICATION_FAILED');
3706 _reset_password_request($conn, $e, $user);
3709 # Once we have the user, we can issue the password reset request
3710 # XXX Add a wrapper method that accepts barcode + email input
3711 sub _reset_password_request {
3712 my ($conn, $e, $user) = @_;
3714 # 1. Get throttle threshold and time-to-live from OU_settings
3715 my $aupr_throttle = $U->ou_ancestor_setting_value($user->home_ou, 'circ.password_reset_request_throttle') || 1000;
3716 my $aupr_ttl = $U->ou_ancestor_setting_value($user->home_ou, 'circ.password_reset_request_time_to_live') || 24*60*60;
3718 my $threshold_time = DateTime->now(time_zone => 'local')->subtract(seconds => $aupr_ttl)->iso8601();
3720 # 2. Get time of last request and number of active requests (num_active)
3721 my $active_requests = $e->json_query({
3727 transform => 'COUNT'
3730 column => 'request_time',
3736 has_been_reset => { '=' => 'f' },
3737 request_time => { '>' => $threshold_time }
3741 # Guard against no active requests
3742 if ($active_requests->[0]->{'request_time'}) {
3743 my $last_request = DateTime::Format::ISO8601->parse_datetime(clense_ISO8601($active_requests->[0]->{'request_time'}));
3744 my $now = DateTime::Format::ISO8601->new();
3746 # 3. if (num_active > throttle_threshold) and (now - last_request < 1 minute)
3747 if (($active_requests->[0]->{'usr'} > $aupr_throttle) &&
3748 ($last_request->add_duration('1 minute') > $now)) {
3749 $cache->put_cache('open-ils.actor.password.throttle', DateTime::Format::ISO8601->new(), 60);
3751 return OpenILS::Event->new('PATRON_TOO_MANY_ACTIVE_PASSWORD_RESET_REQUESTS');
3755 # TODO Check to see if the user is in a password-reset-restricted group
3757 # Otherwise, go ahead and try to get the user.
3759 # Check the number of active requests for this user
3760 $active_requests = $e->json_query({
3766 transform => 'COUNT'
3771 usr => { '=' => $user->id },
3772 has_been_reset => { '=' => 'f' },
3773 request_time => { '>' => $threshold_time }
3777 $logger->info("User " . $user->id . " has " . $active_requests->[0]->{'usr'} . " active password reset requests.");
3779 # if less than or equal to per-user threshold, proceed; otherwise, return event
3780 my $aupr_per_user_limit = $U->ou_ancestor_setting_value($user->home_ou, 'circ.password_reset_request_per_user_limit') || 3;
3781 if ($active_requests->[0]->{'usr'} > $aupr_per_user_limit) {
3783 return OpenILS::Event->new('PATRON_TOO_MANY_ACTIVE_PASSWORD_RESET_REQUESTS');
3786 # Create the aupr object and insert into the database
3787 my $reset_request = Fieldmapper::actor::usr_password_reset->new;
3788 my $uuid = create_uuid_as_string(UUID_V4);
3789 $reset_request->uuid($uuid);
3790 $reset_request->usr($user->id);
3792 my $aupr = $e->create_actor_usr_password_reset($reset_request) or return $e->die_event;
3795 # Create an event to notify user of the URL to reset their password
3797 # Can we stuff this in the user_data param for trigger autocreate?
3798 my $hostname = $U->ou_ancestor_setting_value($user->home_ou, 'lib.hostname') || 'localhost';
3800 my $ses = OpenSRF::AppSession->create('open-ils.trigger');
3801 $ses->request('open-ils.trigger.event.autocreate', 'password.reset_request', $aupr, $user->home_ou);
3804 # $U->create_trigger_event('password.reset_request', $aupr, $user->home_ou);
3809 __PACKAGE__->register_method(
3810 method => "commit_password_reset",
3811 api_name => "open-ils.actor.patron.password_reset.commit",
3813 desc => "Checks a UUID token generated by the open-ils.actor.patron.password_reset.request method for " .
3814 "validity, and if valid, uses it as authorization for changing the associated user's password " .
3815 "with the supplied password.",
3817 { desc => 'uuid', type => 'string' },
3818 { desc => 'password', type => 'string' },
3820 return => {desc => '1 on success, Event on error'}
3823 sub commit_password_reset {
3824 my($self, $conn, $uuid, $password) = @_;
3826 # Check to see if password reset requests are already being throttled:
3827 # 0. Check cache to see if we're in throttle mode (avoid hitting database)
3828 $cache ||= OpenSRF::Utils::Cache->new("global", 0);
3829 my $throttle = $cache->get_cache('open-ils.actor.password.throttle') || undef;
3831 return OpenILS::Event->new('PATRON_NOT_AN_ACTIVE_PASSWORD_RESET_REQUEST');
3834 my $e = new_editor(xact => 1);
3836 my $aupr = $e->search_actor_usr_password_reset({
3843 return OpenILS::Event->new('PATRON_NOT_AN_ACTIVE_PASSWORD_RESET_REQUEST');
3845 my $user_id = $aupr->[0]->usr;
3846 my $user = $e->retrieve_actor_user($user_id);
3848 # Ensure we're still within the TTL for the request
3849 my $aupr_ttl = $U->ou_ancestor_setting_value($user->home_ou, 'circ.password_reset_request_time_to_live') || 24*60*60;
3850 my $threshold = DateTime::Format::ISO8601->parse_datetime(clense_ISO8601($aupr->[0]->request_time))->add(seconds => $aupr_ttl);
3851 if ($threshold < DateTime->now(time_zone => 'local')) {
3853 $logger->info("Password reset request needed to be submitted before $threshold");
3854 return OpenILS::Event->new('PATRON_NOT_AN_ACTIVE_PASSWORD_RESET_REQUEST');
3857 # Check complexity of password against OU-defined regex
3858 my $pw_regex = $U->ou_ancestor_setting_value($user->home_ou, 'global.password_regex');
3862 # Calling JSON2perl on the $pw_regex causes failure, even before the fancy Unicode regex
3863 # ($pw_regex = OpenSRF::Utils::JSON->JSON2perl($pw_regex)) =~ s/\\u([0-9a-fA-F]{4})/\\x{$1}/gs;
3864 $is_strong = check_password_strength_custom($password, $pw_regex);
3866 $is_strong = check_password_strength_default($password);
3871 return OpenILS::Event->new('PATRON_PASSWORD_WAS_NOT_STRONG');
3874 # All is well; update the password
3875 $user->passwd($password);
3876 $e->update_actor_user($user);
3878 # And flag that this password reset request has been honoured
3879 $aupr->[0]->has_been_reset('t');
3880 $e->update_actor_usr_password_reset($aupr->[0]);
3886 sub check_password_strength_default {
3887 my $password = shift;
3888 # Use the default set of checks
3889 if ( (length($password) < 7) or
3890 ($password !~ m/.*\d+.*/) or
3891 ($password !~ m/.*[A-Za-z]+.*/)
3898 sub check_password_strength_custom {
3899 my ($password, $pw_regex) = @_;
3901 $pw_regex = qr/$pw_regex/;
3902 if ($password !~ /$pw_regex/) {
3910 __PACKAGE__->register_method(
3911 method => "event_def_opt_in_settings",
3912 api_name => "open-ils.actor.event_def.opt_in.settings",
3915 desc => 'Streams the set of "cust" objects that are used as opt-in settings for event definitions',
3917 { desc => 'Authentication token', type => 'string'},
3919 desc => 'Org Unit ID. (optional). If no org ID is present, the home_ou of the requesting user is used',
3924 desc => q/set of "cust" objects that are used as opt-in settings for event definitions at the specified org unit/,
3931 sub event_def_opt_in_settings {
3932 my($self, $conn, $auth, $org_id) = @_;
3933 my $e = new_editor(authtoken => $auth);
3934 return $e->event unless $e->checkauth;
3936 if(defined $org_id and $org_id != $e->requestor->home_ou) {
3937 return $e->event unless
3938 $e->allowed(['VIEW_USER_SETTING_TYPE', 'ADMIN_USER_SETTING_TYPE'], $org_id);
3940 $org_id = $e->requestor->home_ou;
3943 # find all config.user_setting_type's related to event_defs for the requested org unit
3944 my $types = $e->json_query({
3945 select => {cust => ['name']},
3946 from => {atevdef => 'cust'},
3949 owner => $U->get_org_ancestors($org_id), # context org plus parents
3956 $conn->respond($_) for
3957 @{$e->search_config_usr_setting_type({name => [map {$_->{name}} @$types]})};
3964 __PACKAGE__->register_method(
3965 method => "user_visible_circs",
3966 api_name => "open-ils.actor.history.circ.visible",
3969 desc => 'Returns the set of opt-in visible circulations accompanied by circulation chain summaries',
3971 { desc => 'Authentication token', type => 'string'},
3972 { desc => 'User ID. If no user id is present, the authenticated user is assumed', type => 'number' },
3973 { desc => 'Options hash. Supported fields are "limit" and "offset"', type => 'object' },
3976 desc => q/An object with 2 fields: circulation and summary.
3977 circulation is the "circ" object. summary is the related "accs" object/,
3983 __PACKAGE__->register_method(
3984 method => "user_visible_circs",
3985 api_name => "open-ils.actor.history.circ.visible.print",
3988 desc => 'Returns printable output for the set of opt-in visible circulations',
3990 { desc => 'Authentication token', type => 'string'},
3991 { desc => 'User ID. If no user id is present, the authenticated user is assumed', type => 'number' },
3992 { desc => 'Options hash. Supported fields are "limit" and "offset"', type => 'object' },
3995 desc => q/An action_trigger.event object or error event./,
4001 __PACKAGE__->register_method(
4002 method => "user_visible_circs",
4003 api_name => "open-ils.actor.history.circ.visible.email",
4006 desc => 'Emails the set of opt-in visible circulations to the requestor',
4008 { desc => 'Authentication token', type => 'string'},
4009 { desc => 'User ID. If no user id is present, the authenticated user is assumed', type => 'number' },
4010 { desc => 'Options hash. Supported fields are "limit" and "offset"', type => 'object' },
4013 desc => q/undef, or event on error/
4018 __PACKAGE__->register_method(
4019 method => "user_visible_circs",
4020 api_name => "open-ils.actor.history.hold.visible",
4023 desc => 'Returns the set of opt-in visible holds',
4025 { desc => 'Authentication token', type => 'string'},
4026 { desc => 'User ID. If no user id is present, the authenticated user is assumed', type => 'number' },
4027 { desc => 'Options hash. Supported fields are "limit" and "offset"', type => 'object' },
4030 desc => q/An object with 1 field: "hold"/,
4036 __PACKAGE__->register_method(
4037 method => "user_visible_circs",
4038 api_name => "open-ils.actor.history.hold.visible.print",
4041 desc => 'Returns printable output for the set of opt-in visible holds',
4043 { desc => 'Authentication token', type => 'string'},
4044 { desc => 'User ID. If no user id is present, the authenticated user is assumed', type => 'number' },
4045 { desc => 'Options hash. Supported fields are "limit" and "offset"', type => 'object' },
4048 desc => q/An action_trigger.event object or error event./,
4054 __PACKAGE__->register_method(
4055 method => "user_visible_circs",
4056 api_name => "open-ils.actor.history.hold.visible.email",
4059 desc => 'Emails the set of opt-in visible holds to the requestor',
4061 { desc => 'Authentication token', type => 'string'},
4062 { desc => 'User ID. If no user id is present, the authenticated user is assumed', type => 'number' },
4063 { desc => 'Options hash. Supported fields are "limit" and "offset"', type => 'object' },
4066 desc => q/undef, or event on error/
4071 sub user_visible_circs {
4072 my($self, $conn, $auth, $user_id, $options) = @_;
4074 my $is_hold = ($self->api_name =~ /hold/);
4075 my $for_print = ($self->api_name =~ /print/);
4076 my $for_email = ($self->api_name =~ /email/);
4077 my $e = new_editor(authtoken => $auth);
4078 return $e->event unless $e->checkauth;
4080 $user_id ||= $e->requestor->id;
4082 $options->{limit} ||= 50;
4083 $options->{offset} ||= 0;
4085 if($user_id != $e->requestor->id) {
4086 my $perm = ($is_hold) ? 'VIEW_HOLD' : 'VIEW_CIRCULATIONS';
4087 my $user = $e->retrieve_actor_user($user_id) or return $e->event;
4088 return $e->event unless $e->allowed($perm, $user->home_ou);
4091 my $db_func = ($is_hold) ? 'action.usr_visible_holds' : 'action.usr_visible_circs';
4093 my $data = $e->json_query({
4094 from => [$db_func, $user_id],
4095 limit => $$options{limit},
4096 offset => $$options{offset}
4098 # TODO: I only want IDs. code below didn't get me there
4099 # {"select":{"au":[{"column":"id", "result_field":"id",
4100 # "transform":"action.usr_visible_circs"}]}, "where":{"id":10}, "from":"au"}
4105 return undef unless @$data;
4109 # collect the batch of objects
4113 my $hold_list = $e->search_action_hold_request({id => [map { $_->{id} } @$data]});
4114 return $U->fire_object_event(undef, 'ahr.format.history.print', $hold_list, $$hold_list[0]->request_lib);
4118 my $circ_list = $e->search_action_circulation({id => [map { $_->{id} } @$data]});
4119 return $U->fire_object_event(undef, 'circ.format.history.print', $circ_list, $$circ_list[0]->circ_lib);
4122 } elsif ($for_email) {
4124 $conn->respond_complete(1) if $for_email; # no sense in waiting
4132 my $hold = $e->retrieve_action_hold_request($id);
4133 $U->create_events_for_hook('ahr.format.history.email', $hold, $hold->request_lib, undef, undef, 1);
4134 # events will be fired from action_trigger_runner
4138 my $circ = $e->retrieve_action_circulation($id);
4139 $U->create_events_for_hook('circ.format.history.email', $circ, $circ->circ_lib, undef, undef, 1);
4140 # events will be fired from action_trigger_runner
4144 } else { # just give me the data please
4152 my $hold = $e->retrieve_action_hold_request($id);
4153 $conn->respond({hold => $hold});
4157 my $circ = $e->retrieve_action_circulation($id);
4160 summary => $U->create_circ_chain_summary($e, $id)
4169 __PACKAGE__->register_method(
4170 method => "user_saved_search_cud",
4171 api_name => "open-ils.actor.user.saved_search.cud",
4174 desc => 'Create/Update/Delete Access to user saved searches',
4176 { desc => 'Authentication token', type => 'string' },
4177 { desc => 'Saved Search Object', type => 'object', class => 'auss' }
4180 desc => q/The retrieved or updated saved search object, or id of a deleted object; Event on error/,
4186 __PACKAGE__->register_method(
4187 method => "user_saved_search_cud",
4188 api_name => "open-ils.actor.user.saved_search.retrieve",
4191 desc => 'Retrieve a saved search object',
4193 { desc => 'Authentication token', type => 'string' },
4194 { desc => 'Saved Search ID', type => 'number' }
4197 desc => q/The saved search object, Event on error/,
4203 sub user_saved_search_cud {
4204 my( $self, $client, $auth, $search ) = @_;
4205 my $e = new_editor( authtoken=>$auth );
4206 return $e->die_event unless $e->checkauth;
4208 my $o_search; # prior version of the object, if any
4209 my $res; # to be returned
4211 # branch on the operation type
4213 if( $self->api_name =~ /retrieve/ ) { # Retrieve
4215 # Get the old version, to check ownership
4216 $o_search = $e->retrieve_actor_usr_saved_search( $search )
4217 or return $e->die_event;
4219 # You can't read somebody else's search
4220 return OpenILS::Event->new('BAD_PARAMS')
4221 unless $o_search->owner == $e->requestor->id;
4227 $e->xact_begin; # start an editor transaction
4229 if( $search->isnew ) { # Create
4231 # You can't create a search for somebody else
4232 return OpenILS::Event->new('BAD_PARAMS')
4233 unless $search->owner == $e->requestor->id;
4235 $e->create_actor_usr_saved_search( $search )
4236 or return $e->die_event;
4240 } elsif( $search->ischanged ) { # Update
4242 # You can't change ownership of a search
4243 return OpenILS::Event->new('BAD_PARAMS')
4244 unless $search->owner == $e->requestor->id;
4246 # Get the old version, to check ownership
4247 $o_search = $e->retrieve_actor_usr_saved_search( $search->id )
4248 or return $e->die_event;
4250 # You can't update somebody else's search
4251 return OpenILS::Event->new('BAD_PARAMS')
4252 unless $o_search->owner == $e->requestor->id;
4255 $e->update_actor_usr_saved_search( $search )
4256 or return $e->die_event;
4260 } elsif( $search->isdeleted ) { # Delete
4262 # Get the old version, to check ownership
4263 $o_search = $e->retrieve_actor_usr_saved_search( $search->id )
4264 or return $e->die_event;
4266 # You can't delete somebody else's search
4267 return OpenILS::Event->new('BAD_PARAMS')
4268 unless $o_search->owner == $e->requestor->id;
4271 $e->delete_actor_usr_saved_search( $o_search )
4272 or return $e->die_event;