</actions>
</permacrud>
</class>
+ <class id="aupw" controller="open-ils.cstore open-ils.pcrud" oils_obj:fieldmapper="actor::usr_privacy_waiver" oils_persist:tablename="actor.usr_privacy_waiver" reporter:label="Privacy Waiver">
+ <fields oils_persist:primary="id" oils_persist:sequence="actor.usr_privacy_waiver_id_seq">
+ <field reporter:label="ID" name="id" reporter:datatype="id" />
+ <field reporter:label="User" name="usr" reporter:datatype="link" />
+ <field reporter:label="Name" name="name" reporter:datatype="text"/>
+ <field reporter:label="Place Holds?" name="place_holds" reporter:datatype="bool" />
+ <field reporter:label="Pick Up Holds?" name="pickup_holds" reporter:datatype="bool" />
+ <field reporter:label="View Borrowing History?" name="view_history" reporter:datatype="bool" />
+ <field reporter:label="Check Out Items?" name="checkout_items" reporter:datatype="bool" />
+ </fields>
+ <links>
+ <link field="usr" reltype="has_a" key="id" map="" class="au"/>
+ </links>
+ <permacrud xmlns="http://open-ils.org/spec/opensrf/IDL/permacrud/v1">
+ <actions>
+ <create permission="UPDATE_USER">
+ <context link="usr" field="home_ou"/>
+ </create>
+ <retrieve permission="VIEW_USER">
+ <context link="usr" field="home_ou"/>
+ </retrieve>
+ <update permission="UPDATE_USER">
+ <context link="usr" field="home_ou"/>
+ </update>
+ <delete permission="UPDATE_USER">
+ <context link="usr" field="home_ou"/>
+ </delete>
+ </actions>
+ </permacrud>
+ </class>
<class id="aupr" controller="open-ils.cstore" oils_obj:fieldmapper="actor::usr_password_reset" oils_persist:tablename="actor.usr_password_reset" reporter:label="User password reset requests">
<fields oils_persist:primary="id" oils_persist:sequence="actor.usr_password_reset_id_seq">
<field reporter:label="Request ID" name="id" reporter:datatype="id"/>
<field reporter:label="Standing Penalties" name="standing_penalties" oils_persist:virtual="true" reporter:datatype="link"/>
<field reporter:label="Statistical Category Entries" name="stat_cat_entries" oils_persist:virtual="true" reporter:datatype="link"/>
<field reporter:label="Survey Responses" name="survey_responses" oils_persist:virtual="true" reporter:datatype="link"/>
+ <field reporter:label="Privacy Waiver Entries" name = "waiver_entries" oils_persist:virtual="true" reporter:datatype="link"/>
<field reporter:label="Workstation Org Unit" name="ws_ou" oils_persist:virtual="true" reporter:datatype="link"/>
<field reporter:label="Workstation ID" name="wsid" oils_persist:virtual="true" reporter:datatype="link"/>
<field reporter:label="Active" name="active" reporter:datatype="bool"/>
<link field="card" reltype="has_a" key="id" map="" class="ac"/>
<link field="ident_type2" reltype="has_a" key="id" map="" class="cit"/>
<link field="stat_cat_entries" reltype="has_many" key="target_usr" map="" class="actscecm"/>
+ <link field="waiver_entries" reltype="has_many" key="usr" map="" class="aupw"/>
<link field="groups" reltype="has_many" key="usr" map="grp" class="pugm"/>
<link field="usrgroup" reltype="has_many" key="usrgroup" map="" class="au"/>
<link field="checkouts" reltype="has_many" key="usr" map="" class="circ"/>
__PACKAGE__->register_method(
+ method => "update_privacy_waiver",
+ api_name => "open-ils.actor.patron.privacy_waiver.update",
+ signature => {
+ desc => "Replaces any existing privacy waiver entries for the patron with the supplied values.",
+ params => [
+ {desc => 'Authentication token', type => 'string'},
+ {desc => 'User ID', type => 'number'},
+ {desc => 'Arrayref of privacy waiver entries', type => 'object'}
+ ],
+ return => {desc => '1 on success, Event on error'}
+ }
+);
+sub update_privacy_waiver {
+ my($self, $conn, $auth, $user_id, $waiver) = @_;
+ my $e = new_editor(xact => 1, authtoken => $auth);
+ return $e->die_event unless $e->checkauth;
+
+ $user_id = $e->requestor->id unless defined $user_id;
+
+ unless($e->requestor->id == $user_id) {
+ my $user = $e->retrieve_actor_user($user_id) or return $e->die_event;
+ return $e->die_event unless $e->allowed('UPDATE_USER', $user->home_ou);
+ }
+
+ foreach my $w (@$waiver) {
+ $w->{usr} = $user_id unless $w->{usr};
+ if ($w->{id} && $w->{id} ne 'new') {
+ my $existing_rows = $e->search_actor_usr_privacy_waiver({usr => $user_id, id => $w->{id}});
+ if ($existing_rows) {
+ my $existing = $existing_rows->[0];
+ # delete existing if name is empty
+ if (!$w->{name} or $w->{name} =~ /^\s*$/) {
+ $e->delete_actor_usr_privacy_waiver($existing) or return $e->die_event;
+
+ # delete existing if none of the boxes were checked
+ } elsif (!$w->{place_holds} && !$w->{pickup_holds} && !$w->{checkout_items} && !$w->{view_history}) {
+ $e->delete_actor_usr_privacy_waiver($existing) or return $e->die_event;
+
+ # otherwise, update existing waiver entry
+ } else {
+ $existing->name($w->{name});
+ $existing->place_holds($w->{place_holds});
+ $existing->pickup_holds($w->{pickup_holds});
+ $existing->checkout_items($w->{checkout_items});
+ $existing->view_history($w->{view_history});
+ $e->update_actor_usr_privacy_waiver($existing) or return $e->die_event;
+ }
+ } else {
+ $logger->warn("No privacy waiver entry found for user $user_id with ID " . $w->{id});
+ }
+
+ } else {
+ # ignore new entries with empty name or with no boxes checked
+ next if (!$w->{name} or $w->{name} =~ /^\s*$/);
+ next if (!$w->{place_holds} && !$w->{pickup_holds} && !$w->{checkout_items} && !$w->{view_history});
+ my $new = Fieldmapper::actor::usr_privacy_waiver->new;
+ $new->usr($w->{usr});
+ $new->name($w->{name});
+ $new->place_holds($w->{place_holds});
+ $new->pickup_holds($w->{pickup_holds});
+ $new->checkout_items($w->{checkout_items});
+ $new->view_history($w->{view_history});
+ $e->create_actor_usr_privacy_waiver($new) or return $e->die_event;
+ }
+ }
+
+ $e->commit;
+ return 1;
+}
+
+
+__PACKAGE__->register_method(
method => "set_ou_settings",
api_name => "open-ils.actor.org_unit.settings.update",
signature => {
( $new_patron, $evt ) = _add_update_cards($e, $patron, $new_patron);
return $evt if $evt;
+ ( $new_patron, $evt ) = _add_update_waiver_entries($e, $patron, $new_patron);
+ return $evt if $evt;
+
( $new_patron, $evt ) = _add_survey_responses($e, $patron, $new_patron);
return $evt if $evt;
"billing_address",
"mailing_address",
"stat_cat_entries",
+ "waiver_entries",
"settings",
"usr_activity"
];
$new_patron->clear_ischanged();
$new_patron->clear_isdeleted();
$new_patron->clear_stat_cat_entries();
+ $new_patron->clear_waiver_entries();
$new_patron->clear_permissions();
$new_patron->clear_standing_penalties();
}
+sub _add_update_waiver_entries {
+ my $e = shift;
+ my $patron = shift;
+ my $new_patron = shift;
+ my $evt;
+
+ my $waiver_entries = $patron->waiver_entries();
+ for my $waiver (@$waiver_entries) {
+ next unless ref $waiver;
+ $waiver->usr($new_patron->id());
+ if ($waiver->isnew()) {
+ next if (!$waiver->name() or $waiver->name() =~ /^\s*$/);
+ next if (!$waiver->place_holds() && !$waiver->pickup_holds() && !$waiver->checkout_items() && !$waiver->view_history());
+ $logger->info("Adding new patron waiver entry");
+ $waiver->clear_id();
+ $e->create_actor_usr_privacy_waiver($waiver) or return (undef, $e->die_event);
+ } elsif ($waiver->ischanged()) {
+ $logger->info("Updating patron waiver entry " . $waiver->id);
+ $e->update_actor_usr_privacy_waiver($waiver) or return (undef, $e->die_event);
+ } elsif ($waiver->isdeleted()) {
+ $logger->info("Deleting patron waiver entry " . $waiver->id);
+ $e->delete_actor_usr_privacy_waiver($waiver) or return (undef, $e->die_event);
+ }
+ }
+ return ($new_patron, undef);
+}
# returns event on error. returns undef otherwise
"billing_address",
"mailing_address",
"stat_cat_entries",
+ "waiver_entries",
"usr_activity" ];
return new_flesh_user($user_id, $fields, $e);
}
{
flesh => 1,
flesh_fields => {
- au => [qw/card home_ou addresses ident_type billing_address/, @extra_flesh]
+ au => [qw/card home_ou addresses ident_type billing_address waiver_entries/, @extra_flesh]
# ...
}
}
}
}
+ my $use_privacy_waiver = $self->ctx->{get_org_setting}->(
+ $e->requestor->home_ou, 'circ.privacy_waiver');
+
return Apache2::Const::OK
unless $self->cgi->request_method eq 'POST';
'open-ils.actor.patron.settings.update',
$self->editor->authtoken, undef, \%settings);
- # re-fetch user prefs
$self->ctx->{updated_user_settings} = \%settings;
+
+ if ($use_privacy_waiver) {
+ my %waiver;
+ my $saved_entries = ();
+ my @waiver_types = qw/place_holds pickup_holds checkout_items view_history/;
+
+ # initialize our waiver hash with waiver IDs from hidden input
+ # (this ensures that we capture entries with no checked boxes)
+ foreach my $waiver_row_id ($self->cgi->param("waiver_id")) {
+ $waiver{$waiver_row_id} = {};
+ }
+
+ # process our waiver checkboxes into a hash, keyed by waiver ID
+ # (a new entry, if any, has id = 'new')
+ foreach my $waiver_type (@waiver_types) {
+ if ($self->cgi->param("waiver_$waiver_type")) {
+ foreach my $waiver_id ($self->cgi->param("waiver_$waiver_type")) {
+ # ensure this waiver exists in our hash
+ $waiver{$waiver_id} = {} if !$waiver{$waiver_id};
+ $waiver{$waiver_id}->{$waiver_type} = 1;
+ }
+ }
+ }
+
+ foreach my $k (keys %waiver) {
+ my $w = $waiver{$k};
+ # get name from textbox
+ $w->{name} = $self->cgi->param("waiver_name_$k");
+ $w->{id} = $k;
+ foreach (@waiver_types) {
+ $w->{$_} = 0 unless ($w->{$_});
+ }
+ push @$saved_entries, $w;
+ }
+
+ # update patron privacy waiver entries
+ $U->simplereq(
+ 'open-ils.actor',
+ 'open-ils.actor.patron.privacy_waiver.update',
+ $self->editor->authtoken, undef, $saved_entries);
+
+ $self->ctx->{updated_waiver_entries} = $saved_entries;
+ }
+
+ # re-fetch user prefs
return $self->_load_user_with_prefs || Apache2::Const::OK;
}
END;
$FUNC$ LANGUAGE PLPGSQL;
+CREATE TABLE actor.usr_privacy_waiver (
+ id BIGSERIAL PRIMARY KEY,
+ usr BIGINT NOT NULL REFERENCES actor.usr(id) DEFERRABLE INITIALLY DEFERRED,
+ name TEXT NOT NULL,
+ place_holds BOOL DEFAULT FALSE,
+ pickup_holds BOOL DEFAULT FALSE,
+ view_history BOOL DEFAULT FALSE,
+ checkout_items BOOL DEFAULT FALSE
+);
+CREATE INDEX actor_usr_privacy_waiver_usr_idx ON actor.usr_privacy_waiver (usr);
COMMIT;
+INSERT INTO config.org_unit_setting_type
+ (name, label, description, grp, datatype)
+ VALUES (
+ 'circ.privacy_waiver',
+ oils_i18n_gettext('circ.privacy_waiver',
+ 'Allow others to use patron account (privacy waiver)',
+ 'coust', 'label'),
+ oils_i18n_gettext('circ.privacy_waiver',
+ 'Add a note to a user account indicating that specified people are allowed to ' ||
+ 'place holds, pick up holds, check out items, or view borrowing history for that user account',
+ 'coust', 'description'),
+ 'circ',
+ 'bool'
+ );
+
--- /dev/null
+BEGIN;
+
+SELECT evergreen.upgrade_deps_block_check('XXXX', :eg_version);
+
+INSERT INTO config.org_unit_setting_type
+ (name, label, description, grp, datatype)
+ VALUES (
+ 'circ.privacy_waiver',
+ oils_i18n_gettext('circ.privacy_waiver',
+ 'Allow others to use patron account (privacy waiver)',
+ 'coust', 'label'),
+ oils_i18n_gettext('circ.privacy_waiver',
+ 'Add a note to a user account indicating that specified people are allowed to ' ||
+ 'place holds, pick up holds, check out items, or view borrowing history for that user account',
+ 'coust', 'description'),
+ 'circ',
+ 'bool'
+ );
+
+COMMIT;
+
--- /dev/null
+BEGIN;
+
+INSERT INTO config.upgrade_log (version) VALUES ('XXXX');
+
+CREATE TABLE actor.usr_privacy_waiver (
+ id BIGSERIAL PRIMARY KEY,
+ usr BIGINT NOT NULL REFERENCES actor.usr(id) DEFERRABLE INITIALLY DEFERRED,
+ name TEXT NOT NULL,
+ place_holds BOOL DEFAULT FALSE,
+ pickup_holds BOOL DEFAULT FALSE,
+ view_history BOOL DEFAULT FALSE,
+ checkout_items BOOL DEFAULT FALSE
+);
+CREATE INDEX actor_usr_privacy_waiver_usr_idx ON actor.usr_privacy_waiver (usr);
+
+COMMIT;
+
<input type='hidden' name='history_delete_confirmed' value='1'/>
<input type="submit" value="[% l('Confirm') %]" class="opac-button"/>
</td></tr>
- [% ELSIF ctx.updated_user_settings %]
+ [% ELSIF ctx.updated_user_settings OR ctx.updated_waiver_entries %]
<tr><td colspan='2'>
<div class='renew-summary'>
[% l('Account Successfully Updated') %]
</tr>
[% END %]
+ [%- IF ctx.get_org_setting(ctx.user.home_ou.id, 'circ.privacy_waiver'); %]
+ <tr>
+ <td>[% l('Allow others to use my account') %]</td>
+ <td>
+ [% FOR waiver IN ctx.user.waiver_entries %]
+ <div id="waiver_[% waiver.id %]">
+ <input type="hidden" name="waiver_id" value="[% waiver.id %]"/>
+ [% l('Name:') %] <input type="textbox" name="waiver_name_[% waiver.id %]" value="[% waiver.name | html %]"/><br/>
+ <label>
+ <input type="checkbox" name="waiver_place_holds"
+ value="[% waiver.id %]" [% waiver.place_holds == 't' ? 'checked="checked"' : '' %]/>
+ [% l('Place Holds') %]
+ </label>
+ <label>
+ <input type="checkbox" name="waiver_pickup_holds"
+ value="[% waiver.id %]" [% waiver.pickup_holds == 't' ? 'checked="checked"' : '' %]/>
+ [% l('Pick Up Holds') %]
+ </label>
+ <label>
+ <input type="checkbox" name="waiver_checkout_items"
+ value="[% waiver.id %]" [% waiver.checkout_items == 't' ? 'checked="checked"' : '' %]/>
+ [% l('Check Out Items') %]
+ </label>
+ <label>
+ <input type="checkbox" name="waiver_view_history"
+ value="[% waiver.id %]" [% waiver.view_history == 't' ? 'checked="checked"' : '' %]/>
+ [% l('View Borrowing History') %]
+ </label>
+ </div>
+ [% END %]
+ <br/>
+ <div>
+ [% l('Name:') %] <input type="textbox" name="waiver_name_new"/><br/>
+ <label><input type="checkbox" name="waiver_place_holds" value="new"/>[% l('Place Holds') %]</label>
+ <label><input type="checkbox" name="waiver_pickup_holds" value="new"/>[% l('Pick Up Holds') %]</label>
+ <label><input type="checkbox" name="waiver_checkout_items" value="new"/>[% l('Check Out Items') %]</label>
+ <label><input type="checkbox" name="waiver_view_history" value="new"/>[% l('View Borrowing History') %]</label>
+ </div>
+ </td>
+ </tr>
+ [% END %]
+
<!--
<tr>
<td><label for='prefs_def_font'>[% l("Default Font Size") %]</label></td>
</div>
</div>
+<div class="row reg-field-row" ng-if="org_settings['circ.privacy_waiver']">
+ <div class="col-md-3 reg-field-label">
+ <label>[% l('Allow others to use my account') %]</label>
+ </div>
+ <div class="col-md-3 reg-field-input">
+ <div class="row" ng-repeat="waiver_entry in patron.waiver_entries" ng-hide="waiver_entry.isdeleted">
+ <div class="row flex-row">
+ <div class="flex-cell">
+ <input ng-change="field_modified()"
+ type='text' ng-model="waiver_entry.name"/>
+ </div>
+ <div class="flex-cell">
+ <button type="button"
+ ng-click="field_modified();delete_waiver_entry(waiver_entry)"
+ class="btn btn-danger">[% l('X') %]</button>
+ </div>
+ </div>
+ <div class="row flex-row reg-field-input">
+ <div class="flex-cell">
+ <label><input ng-change="field_modified()"
+ type='checkbox' ng-model="waiver_entry.place_holds"/>
+ [% l('Place Holds?') %]</label>
+ </div>
+ <div class="flex-cell">
+ <label><input ng-change="field_modified()"
+ type='checkbox' ng-model="waiver_entry.pickup_holds"/>
+ [% l('Pick Up Holds?') %]</label>
+ </div>
+ <div class="flex-cell">
+ <label><input ng-change="field_modified()"
+ type='checkbox' ng-model="waiver_entry.view_history"/>
+ [% l('View Borrowing History?') %]</label>
+ </div>
+ <div class="flex-cell">
+ <label><input ng-change="field_modified()"
+ type='checkbox' ng-model="waiver_entry.checkout_items"/>
+ [% l('Check Out Items?') %]</label>
+ </div>
+ </div> <!-- end checkboxes -->
+ </div> <!-- end ng-repeat waiver_entry -->
+ <div class="row">
+ <div class="col-md-3 reg-field-input">
+ <button type="button" ng-click="new_waiver_entry()"
+ class="btn btn-success">[% l('Add Person') %]</button>
+ </div>
+ </div>
+ </div> <!-- end waiver entries input -->
+</div> <!-- end waiver entries row -->
+
</div> <!-- end offline test -->
<!-- addresses -->
<div class="col-md-5">[% l('Name Keywords') %]</div>
<div class="col-md-7">{{patron().name_keywords()}}</div>
</div>
+ <div class="row patron-summary-divider" ng-if="patron().waiver_entries().length > 0">
+ [% l('Allow others to use my account') %]
+ </div>
+ <div class="row" ng-repeat="waiver in patron().waiver_entries()">
+ <div class="col-md-5">{{waiver.name()}}</div>
+ <div class="col-md-7">
+ <ul>
+ <li ng-show="waiver.place_holds() == 't'">[% l('Place holds') %]</li>
+ <li ng-show="waiver.pickup_holds() == 't'">[% l('Pick up holds') %]</li>
+ <li ng-show="waiver.view_history() == 't'">[% l('View borrowing history') %]</li>
+ <li ng-show="waiver.checkout_items() == 't'">[% l('Check out items') %]</li>
+ </ul>
+ </div>
+ </div>
</div>
<div class="row" ng-repeat="addr in patron().addresses()">
'ui.patron.registration.require_address',
'circ.holds.behind_desk_pickup_supported',
'circ.patron_edit.clone.copy_address',
+ 'circ.privacy_waiver',
'ui.patron.edit.au.prefix.require',
'ui.patron.edit.au.prefix.show',
'ui.patron.edit.au.prefix.suggest',
addr.pending = addr.pending === 't';
}
+ service.ingest_waiver_entry = function(patron, waiver_entry) {
+ waiver_entry.place_holds = waiver_entry.place_holds == 't';
+ waiver_entry.pickup_holds = waiver_entry.pickup_holds == 't';
+ waiver_entry.view_history = waiver_entry.view_history == 't';
+ waiver_entry.checkout_items = waiver_entry.checkout_items == 't';
+ }
+
/*
* Existing patron objects reqire some data munging before insertion
* into the scope.
}
});
+ angular.forEach(patron.waiver_entries,
+ function(waiver_entry) { service.ingest_waiver_entry(patron, waiver_entry) });
+
service.get_linked_addr_users(patron.addresses);
// Remove stat cat entries that link to out-of-scope stat
cards : [card],
home_ou : egCore.org.get(egCore.auth.user().ws_ou()),
stat_cat_entries : [],
+ waiver_entries : [],
groups : [],
addresses : [addr]
};
patron.stat_cat_entries().push(newmap);
});
+ var waiver_hashes = patron.waiver_entries();
+ patron.waiver_entries([]);
+ angular.forEach(waiver_hashes, function(waiver_hash) {
+ if (!waiver_hash.isnew && !waiver_hash.isdeleted)
+ waiver_hash.ischanged = true;
+ var waiver_entry = egCore.idl.fromHash('aupw', waiver_hash);
+ patron.waiver_entries().push(waiver_entry);
+ });
+
if (!patron.isnew()) patron.ischanged(true);
return egCore.net.request(
});
}
+ $scope.new_waiver_entry = function() {
+ var waiver = egCore.idl.toHash(new egCore.idl.aupw());
+ patronRegSvc.ingest_waiver_entry($scope.patron, waiver);
+ waiver.id = patronRegSvc.virt_id--;
+ waiver.isnew = true;
+ $scope.patron.waiver_entries.push(waiver);
+ }
+
+ deleted_waiver_entries = [];
+ $scope.delete_waiver_entry = function(waiver_entry) {
+ if (waiver_entry.id > 0) {
+ waiver_entry.isdeleted = true;
+ deleted_waiver_entries.push(waiver_entry);
+ }
+ var index = $scope.patron.waiver_entries.indexOf(waiver_entry);
+ $scope.patron.waiver_entries.splice(index, 1);
+ }
+
$scope.replace_card = function() {
$scope.patron.card.active = false;
$scope.patron.card.ischanged = true;
$scope.patron.addresses =
$scope.patron.addresses.concat(deleted_addresses);
+ // ditto for waiver entries
+ $scope.patron.waiver_entries =
+ $scope.patron.waiver_entries.concat(deleted_waiver_entries);
+
compress_hold_notify();
var updated_user;
'billing_address',
'mailing_address',
'stat_cat_entries',
+ 'waiver_entries',
'usr_activity',
'notes'
]
--- /dev/null
+Allow Others to Use My Account (Privacy Waiver)
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+Patrons who wish to authorize other people to use their account may
+now do so via My Account. In the Search and History Preferences tab
+under Account Preferences, a new section labeled "Allow others to use
+my account" allows patrons to enter a name and indicate that the
+specified person is allowed to place holds, pickup holds, view
+borrowing history, or check out items on their account. This
+information is displayed to circulation staff in the patron account
+summary in the web client. (Staff may also add, edit, and remove
+entries via the patron editor.)
+
+A new library setting, "Allow others to use patron account (privacy
+waiver)," is used to enable or disable this feature.
+