@@ -, +, @@ GET /messaging_preferences?borrowernumber=123 - allowed if getting own preferences - otherwise borrowers flag required GET /messaging_preferences?categorycode=K - borrowers flag required PUT /messaging_preferences?borrowernumber=123 - allowed if modifying own preferences - otherwise borrowers flag required PUT /messaging_preferences?categorycode=K - borrowers flag required "Advanced_Notice": { "days_in_advance": { "configurable": true, "value": 20 }, "digest": { "configurable": true, "value": true }, "transport_types": { "email": true, "sms": false } }, "Another_Message_Name": { "days_in_advance": { "configurable": false, "value": null }, "digest": { ... }, ... --- Koha/Patron/Message/Preference.pm | 51 +++ Koha/Patron/Message/Preferences.pm | 29 ++ Koha/REST/V1/Patron/Message/Preference.pm | 164 ++++++++ api/v1/swagger/definitions.json | 3 + .../swagger/definitions/messagingpreference.json | 20 + .../definitions/messagingpreference/transport.json | 52 +++ api/v1/swagger/paths.json | 3 + api/v1/swagger/paths/messagingpreferences.json | 155 ++++++++ t/db_dependent/api/v1/messagingpreferences.t | 417 +++++++++++++++++++++ 9 files changed, 894 insertions(+) create mode 100644 Koha/REST/V1/Patron/Message/Preference.pm create mode 100644 api/v1/swagger/definitions/messagingpreference.json create mode 100644 api/v1/swagger/definitions/messagingpreference/transport.json create mode 100644 api/v1/swagger/paths/messagingpreferences.json create mode 100644 t/db_dependent/api/v1/messagingpreferences.t --- a/Koha/Patron/Message/Preference.pm +++ a/Koha/Patron/Message/Preference.pm @@ -281,6 +281,57 @@ sub store { return $self; } +sub TO_JSON { + my ($self, $params) = @_; + + my $json = {}; + my $pref; + $params->{'options'} ||= Koha::Patron::Message::Preferences->get_options; + my $transports = $self->message_transport_types; + + foreach my $option (@{$params->{'options'}}) { + foreach my $param (keys %$option) { + if ($param eq 'message_attribute_id' && + $option->{$param} == $self->message_attribute_id) { + $pref = $option; + last; + } + } + } + + foreach my $conf (keys %$pref) { + if ($conf =~ /^transport_/) { + my $mtt = $conf =~ s/^transport_//r; + if ($mtt eq 'sms' && !C4::Context->preference('SMSSendDriver')) { + next; + } + if ($mtt eq 'phone' && + !C4::Context->preference('TalkingTechItivaPhoneNotification')) { + next; + } + $json->{'transport_types'}->{$mtt} = $transports->{$mtt} ? + Mojo::JSON->true : Mojo::JSON->false; + } + } + + $json->{'days_in_advance'} = { + configurable => $pref->{'takes_days'} ? + Mojo::JSON->true : Mojo::JSON->false, + value => defined $self->days_in_advance ? + 0+$self->days_in_advance : undef + }; + $json->{'digest'} = { + configurable => $self->wants_digest + ? $pref->{'has_digest_off'} # can digest be unchecked? + ? Mojo::JSON->true : Mojo::JSON->false + : $pref->{'has_digest'} # can digest be checked? + ? Mojo::JSON->true : Mojo::JSON->false, + value => $self->wants_digest ? Mojo::JSON->true : Mojo::JSON->false + }; + + return $json; +} + =head3 validate Makes a basic validation for object. --- a/Koha/Patron/Message/Preferences.pm +++ a/Koha/Patron/Message/Preferences.pm @@ -125,6 +125,35 @@ sub search_with_message_name { return $self->SUPER::search($params, $attributes); } +sub TO_JSON { + my ($self) = @_; + + my $preferences = {}; + my $options = $self->get_options; + foreach my $preference ($self->as_list) { + $preferences->{$preference->message_name} = $preference->TO_JSON({ + options => $options + }); + } + + # If some preferences are not stored even though they are valid options, + # then add those options to the returned HASHref as well + foreach my $option (@$options) { + unless ($preferences->{$option->{'message_name'}}) { + my $message_attribute_id = Koha::Patron::Message::Attributes->find({ + message_name => $option->{'message_name'} + })->message_attribute_id; + $preferences->{$option->{'message_name'}} = + Koha::Patron::Message::Preference->new({ + borrowernumber => -1, + message_attribute_id => $message_attribute_id, + })->TO_JSON({ options => $options }); + } + } + + return $preferences; +} + =head3 type =cut --- a/Koha/REST/V1/Patron/Message/Preference.pm +++ a/Koha/REST/V1/Patron/Message/Preference.pm @@ -0,0 +1,164 @@ +package Koha::REST::V1::Patron::Message::Preference; + +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software +# Foundation; either version 3 of the License, or (at your option) any later +# version. +# +# Koha is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with Koha; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +use Modern::Perl; + +use Mojo::Base 'Mojolicious::Controller'; + +use Koha::Patron::Message::Attributes; +use Koha::Patron::Message::Preferences; +use Koha::Patron::Message::Transport::Types; +use Koha::Patron::Message::Transports; + +use Try::Tiny; + +sub list { + my $c = shift->openapi->valid_input or return; + + my $enabled = _check_system_preferences($c); + return $enabled if $enabled; + + my $borrowernumber = $c->validation->param('borrowernumber'); + my $categorycode = $c->validation->param('categorycode'); + + my $found = $borrowernumber + ? Koha::Patrons->find($borrowernumber) + : Koha::Patron::Categories->find($categorycode); + + return try { + die unless $found; + my $preferences = Koha::Patron::Message::Preferences->search({ + borrowernumber => $borrowernumber, + categorycode => $categorycode, + }); + + return $c->render(status => 200, openapi => $preferences); + } + catch { + unless ($found) { + return $c->render( status => 400, openapi => { + error => "Patron or category not found" } ); + } + Koha::Exceptions::rethrow_exception($_); + }; +} + +sub edit { + my $c = shift->openapi->valid_input or return; + + my $enabled = _check_system_preferences($c); + return $enabled if $enabled; + + my $borrowernumber = $c->validation->param('borrowernumber'); + my $categorycode = $c->validation->param('categorycode'); + my $body = $c->validation->param('body'); + + my $found = $borrowernumber + ? Koha::Patrons->find($borrowernumber) + : Koha::Patron::Categories->find($categorycode); + + return try { + die unless $found; + foreach my $in (keys %{$body}) { + my $preference = + Koha::Patron::Message::Preferences->find_with_message_name({ + borrowernumber => $borrowernumber, + categorycode => $categorycode, + message_name => $in + }); + + # Format wants_digest and days_in_advance values + my $wants_digest = $body->{$in}->{'digest'} ? + $body->{$in}->{'digest'}->{'value'} ? 1 : 0 : 0; + my $days_in_advance = $body->{$in}->{'days_in_advance'} ? + defined $body->{$in}->{'days_in_advance'}->{'value'} ? + $body->{$in}->{'days_in_advance'}->{'value'} : undef : undef; + + # HASHref for updated preference + my @transport_types; + foreach my $mtt (keys %{$body->{$in}->{'transport_types'}}) { + if ($body->{$in}->{'transport_types'}->{$mtt}) { + push @transport_types, $mtt; + } + } + my $edited_preference = { + wants_digest => $wants_digest, + days_in_advance => $days_in_advance, + message_transport_types => \@transport_types + }; + + # Unless a preference for this message name exists, create it + unless ($preference) { + my $attr = Koha::Patron::Message::Attributes->find({ + message_name => $in + }); + unless ($attr) { + Koha::Exceptions::BadParameter->throw( + error => "Message type $in not found." + ); + } + $edited_preference->{'message_attribute_id'} = + $attr->message_attribute_id; + if ($borrowernumber) { + $edited_preference->{'borrowernumber'}=$found->borrowernumber; + } else { + $edited_preference->{'categorycode'}=$found->categorycode; + } + Koha::Patron::Message::Preference->new($edited_preference)->store; + } + # Otherwise, modify the already-existing one + else { + $preference->set($edited_preference)->store; + } + } + + # Finally, return the preferences + my $preferences = Koha::Patron::Message::Preferences->search({ + borrowernumber => $borrowernumber, + categorycode => $categorycode, + }); + + return $c->render( status => 200, openapi => $preferences); + } + catch { + unless ($found) { + return $c->render( status => 400, openapi => { + error => "Patron or category not found" } ); + } + if ($_->isa('Koha::Exceptions::BadParameter')) { + return $c->render( status => 400, openapi => { error => $_->error }); + } + Koha::Exceptions::rethrow_exception($_); + }; +} + +sub _check_system_preferences { + my $c = shift; + if ( ! C4::Context->preference('EnhancedMessagingPreferences') ) { + return $c->render( status => 403, openapi => { + error => "Enhanced messaging preferences are not enabled"}); + } + if (($c->stash('is_owner_access') || $c->stash('is_guarantor_access')) + && ! C4::Context->preference('EnhancedMessagingPreferencesOPAC')) { + return $c->render( status => 403, openapi => { + error => "Patrons does not have access to enhanced messaging" + ." preferences" }); + } + return; +} + +1; --- a/api/v1/swagger/definitions.json +++ a/api/v1/swagger/definitions.json @@ -11,6 +11,9 @@ "holds": { "$ref": "definitions/holds.json" }, + "messagingpreference": { + "$ref": "definitions/messagingpreference.json" + }, "patron": { "$ref": "definitions/patron.json" } --- a/api/v1/swagger/definitions/messagingpreference.json +++ a/api/v1/swagger/definitions/messagingpreference.json @@ -0,0 +1,20 @@ +{ + "type": "object", + "properties": { + "Item_Due": { + "$ref": "messagingpreference/transport.json" + }, + "Advance_Notice": { + "$ref": "messagingpreference/transport.json" + }, + "Hold_Filled": { + "$ref": "messagingpreference/transport.json" + }, + "Item_Check_in": { + "$ref": "messagingpreference/transport.json" + }, + "Item_Checkout": { + "$ref": "messagingpreference/transport.json" + } + } +} --- a/api/v1/swagger/definitions/messagingpreference/transport.json +++ a/api/v1/swagger/definitions/messagingpreference/transport.json @@ -0,0 +1,52 @@ +{ + "type": "object", + "properties": { + "transport_types": { + "type": "object", + "properties": { + "email": { + "type": "boolean" + }, + "phone": { + "type": "boolean" + }, + "print": { + "type": "boolean" + }, + "sms": { + "type": "boolean" + } + } + }, + "days_in_advance": { + "type": "object", + "properties": { + "configurable": { + "description": "Can this message have a value for days in advance", + "type": "boolean", + "readOnly": true + }, + "value": { + "type": ["integer", "null"], + "minimum": 0, + "maximum": 30 + } + } + }, + "digest": { + "type": "object", + "properties": { + "configurable": { + "description": "Can this message be requested as digest", + "type": "boolean", + "readOnly": true + }, + "value": { + "description": "Is digest enabled", + "type": "boolean" + } + } + } + }, + "additionalProperties": false +} --- a/api/v1/swagger/paths.json +++ a/api/v1/swagger/paths.json @@ -11,6 +11,9 @@ "/holds/{reserve_id}": { "$ref": "paths/holds.json#/~1holds~1{reserve_id}" }, + "/messaging_preferences": { + "$ref": "paths/messagingpreferences.json#/~1messaging_preferences" + }, "/patrons": { "$ref": "paths/patrons.json#/~1patrons" }, --- a/api/v1/swagger/paths/messagingpreferences.json +++ a/api/v1/swagger/paths/messagingpreferences.json @@ -0,0 +1,155 @@ +{ + "/messaging_preferences": { + "get": { + "x-mojo-to": "Patron::Message::Preference#list", + "operationId": "listMessagingPreferences", + "x-koha-authorization": { + "permissions": { + "borrowers": "*" + }, + "allow-owner": true, + "allow-guarantor": true + }, + "tags": ["notices"], + "parameters": [{ + "name": "borrowernumber", + "in": "query", + "description": "Patron's borrowernumber", + "required": false, + "type": "integer" + }, { + "name": "categorycode", + "in": "query", + "description": "Categorycode to find default messaging preferences for a category", + "required": false, + "type": "string" + }], + "produces": [ + "application/json" + ], + "responses": { + "200": { + "description": "A messaging preference object", + "schema": { + "$ref" : "../definitions.json#/messagingpreference" + } + }, + "400": { + "description": "Bad parameter given", + "schema": { + "$ref": "../definitions.json#/error" + } + }, + "401": { + "description": "Authentication required", + "schema": { + "$ref": "../definitions.json#/error" + } + }, + "403": { + "description": "Access forbidden", + "schema": { + "$ref": "../definitions.json#/error" + } + }, + "404": { + "description": "Preferences not found", + "schema": { + "$ref": "../definitions.json#/error" + } + }, + "500": { + "description": "Internal error", + "schema": { + "$ref": "../definitions.json#/error" + } + }, + "503": { + "description": "Under maintenance", + "schema": { + "$ref": "../definitions.json#/error" + } + } + } + }, + "put": { + "x-mojo-to": "Patron::Message::Preference#edit", + "operationId": "editMessagingPreferences", + "description": "Modifies patron's messaging preferences. Please note that not all parameters defined in body object's documentation can actually be used for each message option due to dynamic configuration. Make a GET request first to see whether an option is configurable or if message transport type is available.", + "x-koha-authorization": { + "permissions": { + "borrowers": "*" + }, + "allow-owner": true, + "allow-guarantor": true + }, + "tags": ["notices"], + "parameters": [ + { + "name": "borrowernumber", + "in": "query", + "description": "Patron's borrowernumber", + "required": false, + "type": "integer" + }, { + "name": "categorycode", + "in": "query", + "description": "Categorycode to find default messaging preferences for a category", + "required": false, + "type": "string" + }, { + "name": "body", + "in": "body", + "description": "A JSON object containing information on messaging preferences", + "required": true, + "schema": { "$ref" : "../definitions.json#/messagingpreference" } + } + ], + "produces": [ + "application/json" + ], + "responses": { + "200": { + "description": "A messaging preferences object", + "schema": { + "$ref" : "../definitions.json#/messagingpreference" + } + }, + "400": { + "description": "Missing or wrong parameters", + "schema": { "$ref": "../definitions.json#/error" } + }, + "401": { + "description": "Authentication required", + "schema": { + "$ref": "../definitions.json#/error" + } + }, + "403": { + "description": "Access forbidden", + "schema": { + "$ref": "../definitions.json#/error" + } + }, + "404": { + "description": "Patron or category not found", + "schema": { + "$ref": "../definitions.json#/error" + } + }, + "500": { + "description": "Internal error", + "schema": { + "$ref": "../definitions.json#/error" + } + }, + "503": { + "description": "Under maintenance", + "schema": { + "$ref": "../definitions.json#/error" + } + } + } + } + } +} --- a/t/db_dependent/api/v1/messagingpreferences.t +++ a/t/db_dependent/api/v1/messagingpreferences.t @@ -0,0 +1,417 @@ +#!/usr/bin/env perl + +# Copyright Koha-Suomi Oy 2017 +# +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software +# Foundation; either version 3 of the License, or (at your option) any later +# version. +# +# Koha is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with Koha; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +use Modern::Perl; + + +use Test::More tests => 2; +use Test::Mojo; +use t::lib::Mocks; +use t::lib::TestBuilder; + +use C4::Auth; +use C4::Context; + +use Koha::Database; +use Koha::Notice::Templates; +use Koha::Patron::Message::Preferences; + +my $schema = Koha::Database->new->schema; +my $builder = t::lib::TestBuilder->new; + +# FIXME: sessionStorage defaults to mysql, but it seems to break transaction handling +# this affects the other REST api tests +t::lib::Mocks::mock_preference( 'SessionStorage', 'tmp' ); + +my $remote_address = '127.0.0.1'; +my $t = Test::Mojo->new('Koha::REST::V1'); + +subtest 'list() tests' => sub { + plan tests => 34; + + $schema->storage->txn_begin; + + my $path = '/api/v1/messaging_preferences'; + + my ($patron, $session) = create_user_and_session(); + my $borrowernumber = $patron->borrowernumber; + my $categorycode = $patron->categorycode; + my ($another_patron, undef) = create_user_and_session(); + my ($librarian, $librarian_session) = create_user_and_session({ + flags => 16 + }); + my ($preference, $mtt1, $mtt2) = build_a_test_complete_preference({ + patron => $patron + }); + my ($preference2, $mtt1_2, $mtt2_2) = build_a_test_category_preference({ + patron => $patron + }); + + t::lib::Mocks::mock_preference('EnhancedMessagingPreferences', 1); + my $tx = $t->ua->build_tx(GET => $path); + $tx->req->env({REMOTE_ADDR => '127.0.0.1'}); + $t->request_ok($tx) + ->status_is(401); + + $tx = $t->ua->build_tx(GET => "$path?borrowernumber=" + .$another_patron->borrowernumber); + $tx->req->cookies({name => 'CGISESSID', value => $session->id}); + $tx->req->env({REMOTE_ADDR => '127.0.0.1'}); + $t->request_ok($tx) + ->status_is(403); + + $tx = $t->ua->build_tx(GET => "$path?categorycode=$categorycode"); + $tx->req->cookies({name => 'CGISESSID', value => $session->id}); + $tx->req->env({REMOTE_ADDR => '127.0.0.1'}); + $t->request_ok($tx) + ->status_is(403); + + $tx = $t->ua->build_tx(GET => $path); + $tx->req->cookies({name => 'CGISESSID', value => $librarian_session->id}); + $tx->req->env({REMOTE_ADDR => '127.0.0.1'}); + $t->request_ok($tx) + ->status_is(400) + ->json_is('/error', 'Patron or category not found'); + + t::lib::Mocks::mock_preference('EnhancedMessagingPreferences', 0); + $tx = $t->ua->build_tx(GET => "$path?borrowernumber=$borrowernumber"); + $tx->req->cookies({name => 'CGISESSID', value => $librarian_session->id}); + $tx->req->env({REMOTE_ADDR => '127.0.0.1'}); + $t->request_ok($tx) + ->status_is(403) + ->json_is('/error' => 'Enhanced messaging preferences are not enabled'); + t::lib::Mocks::mock_preference('EnhancedMessagingPreferences', 1); + + $tx = $t->ua->build_tx(GET => "$path?borrowernumber=$borrowernumber"); + $tx->req->cookies({name => 'CGISESSID', value => $librarian_session->id}); + $tx->req->env({REMOTE_ADDR => '127.0.0.1'}); + $t->request_ok($tx) + ->status_is(200) + ->json_has('/'.$preference->message_name); + + $tx = $t->ua->build_tx(GET => "$path?borrowernumber=$borrowernumber"); + $tx->req->cookies({name => 'CGISESSID', value => $session->id}); + $tx->req->env({REMOTE_ADDR => '127.0.0.1'}); + $t->request_ok($tx) + ->status_is(200) + ->json_has('/'.$preference->message_name) + ->json_has('/'.$preference2->message_name) + ->json_is('/'.$preference->message_name.'/transport_types/' + .$mtt1->message_transport_type => Mojo::JSON->true) + ->json_is('/'.$preference->message_name.'/transport_types/' + .$mtt2->message_transport_type => Mojo::JSON->true) + ->json_is('/'.$preference->message_name.'/days_in_advance/configurable' + => Mojo::JSON->false) + ->json_is('/'.$preference->message_name.'/days_in_advance/value' + => undef) + ->json_is('/'.$preference->message_name.'/digest/configurable' + => Mojo::JSON->false) + ->json_is('/'.$preference->message_name.'/digest/value' + => Mojo::JSON->false); + + $tx = $t->ua->build_tx(GET => "$path?categorycode=$categorycode"); + $tx->req->cookies({name => 'CGISESSID', value => $librarian_session->id}); + $tx->req->env({REMOTE_ADDR => '127.0.0.1'}); + $t->request_ok($tx) + ->status_is(200) + ->json_has('/'.$preference->message_name) + ->json_has('/'.$preference2->message_name) + ->json_is('/'.$preference2->message_name.'/transport_types/' + .$mtt1_2->message_transport_type => Mojo::JSON->true) + ->json_is('/'.$preference2->message_name.'/transport_types/' + .$mtt2_2->message_transport_type => Mojo::JSON->true) + ->json_is('/'.$preference2->message_name.'/days_in_advance/configurable' + => Mojo::JSON->false) + ->json_is('/'.$preference2->message_name.'/days_in_advance/value' + => undef) + ->json_is('/'.$preference2->message_name.'/digest/value' + => Mojo::JSON->false); + + $schema->storage->txn_rollback; +}; + +subtest 'edit() tests' => sub { + plan tests => 28; + + $schema->storage->txn_begin; + + my $path = '/api/v1/messaging_preferences'; + + my ($patron, $session) = create_user_and_session(); + my ($librarian, $librarian_session) = create_user_and_session({ + flags => 16 + }); + my ($another_patron, undef) = create_user_and_session(); + my $borrowernumber = $patron->borrowernumber; + my $categorycode = $patron->categorycode; + my ($preference, $mtt1, $mtt2) = build_a_test_complete_preference({ + patron => $patron + }); + my ($preference2, $mtt1_2, $mtt2_2) = build_a_test_category_preference({ + patron => $patron + }); + + my $edited_preference = { + $preference->message_name => { + digest => { + value => Mojo::JSON->true + }, + days_in_advance => { + value => 15 + }, + transport_types => { + $mtt1->message_transport_type => Mojo::JSON->true, + $mtt2->message_transport_type => Mojo::JSON->false + } + } + }; + + t::lib::Mocks::mock_preference('EnhancedMessagingPreferences', 1); + my $tx = $t->ua->build_tx(PUT => $path => json => $edited_preference); + $tx->req->env({REMOTE_ADDR => '127.0.0.1'}); + $t->request_ok($tx) + ->status_is(401); + + $tx = $t->ua->build_tx(PUT => "$path?borrowernumber=" + .$another_patron->borrowernumber + => json => $edited_preference); + $tx->req->cookies({name => 'CGISESSID', value => $session->id}); + $tx->req->env({REMOTE_ADDR => '127.0.0.1'}); + $t->request_ok($tx) + ->status_is(403); + + $tx = $t->ua->build_tx(PUT => "$path?categorycode=$categorycode" + => json => $edited_preference); + $tx->req->cookies({name => 'CGISESSID', value => $session->id}); + $tx->req->env({REMOTE_ADDR => '127.0.0.1'}); + $t->request_ok($tx) + ->status_is(403); + + $tx = $t->ua->build_tx(PUT => $path => json => $edited_preference); + $tx->req->cookies({name => 'CGISESSID', value => $librarian_session->id}); + $tx->req->env({REMOTE_ADDR => '127.0.0.1'}); + $t->request_ok($tx) + ->status_is(400) + ->json_is('/error', 'Patron or category not found'); + + t::lib::Mocks::mock_preference('EnhancedMessagingPreferences', 0); + $tx = $t->ua->build_tx(PUT => "$path?borrowernumber=$borrowernumber" => + json => $edited_preference); + $tx->req->cookies({name => 'CGISESSID', value => $librarian_session->id}); + $tx->req->env({REMOTE_ADDR => '127.0.0.1'}); + $t->request_ok($tx) + ->status_is(403) + ->json_is('/error' => 'Enhanced messaging preferences are not enabled'); + t::lib::Mocks::mock_preference('EnhancedMessagingPreferences', 1); + + $tx = $t->ua->build_tx(PUT => "$path?borrowernumber=$borrowernumber" => + json => $edited_preference); + $tx->req->cookies({name => 'CGISESSID', value => $session->id}); + $tx->req->env({REMOTE_ADDR => '127.0.0.1'}); + $t->request_ok($tx) + ->status_is(400) + ->json_like('/error' => qr/^days_in_advance cannot/); + + $edited_preference->{$preference->message_name}-> + {days_in_advance}->{value} = undef; + $tx = $t->ua->build_tx(PUT => "$path?borrowernumber=$borrowernumber" => + json => $edited_preference); + $tx->req->cookies({name => 'CGISESSID', value => $session->id}); + $tx->req->env({REMOTE_ADDR => '127.0.0.1'}); + $t->request_ok($tx) + ->status_is(400) + ->json_like('/error' => qr/igest not available/); + + $edited_preference->{$preference->message_name}->{digest}->{value} + = Mojo::JSON->false; + $tx = $t->ua->build_tx(PUT => "$path?borrowernumber=$borrowernumber" => + json => $edited_preference); + $tx->req->cookies({name => 'CGISESSID', value => $session->id}); + $tx->req->env({REMOTE_ADDR => '127.0.0.1'}); + $t->request_ok($tx) + ->status_is(200) + ->json_is('/'.$preference->message_name.'/transport_types/'. + $mtt2->message_transport_type => Mojo::JSON->false) + ->json_is('/'.$preference->message_name.'/transport_types/'. + $mtt1->message_transport_type => Mojo::JSON->true); + + Koha::Patron::Message::Transports->search({ + message_attribute_id => $preference->message_attribute_id, + })->update({ is_digest => 1 }); + $edited_preference->{$preference->message_name}->{digest}->{value} + = Mojo::JSON->true; + $tx = $t->ua->build_tx(PUT => "$path?borrowernumber=$borrowernumber" => + json => $edited_preference); + $tx->req->cookies({name => 'CGISESSID', value => $session->id}); + $tx->req->env({REMOTE_ADDR => '127.0.0.1'}); + $t->request_ok($tx) + ->status_is(200) + ->json_is('/'.$preference->message_name.'/digest/value' => Mojo::JSON->true); + + # Test librarian access + $tx = $t->ua->build_tx(PUT => "$path?borrowernumber=$borrowernumber" => + json => $edited_preference); + $tx->req->cookies({name => 'CGISESSID', value => $librarian_session->id}); + $tx->req->env({REMOTE_ADDR => '127.0.0.1'}); + $t->request_ok($tx) + ->status_is(200) + ->json_is('/'.$preference->message_name.'/digest/value' => Mojo::JSON->true); + + $schema->storage->txn_rollback; +}; + +sub create_user_and_session { + my ($params) = @_; + + my $categorycode = $builder->build({ source => 'Category' })->{categorycode}; + my $branchcode = $builder->build({ source => 'Branch' })->{ branchcode }; + + my $borrower = $builder->build({ + source => 'Borrower', + value => { + branchcode => $branchcode, + categorycode => $categorycode, + flags => $params->{'flags'} || 0, + } + }); + + my $session = C4::Auth::get_session(''); + $session->param('number', $borrower->{ borrowernumber }); + $session->param('id', $borrower->{ userid }); + $session->param('ip', '127.0.0.1'); + $session->param('lasttime', time()); + $session->flush; + my $patron = Koha::Patrons->find($borrower->{borrowernumber}); + + return ($patron, $session); +} + + +sub build_a_test_attribute { + my ($params) = @_; + + $params->{takes_days} = $params->{takes_days} && $params->{takes_days} > 0 + ? 1 : 0; + + my $attribute = $builder->build({ + source => 'MessageAttribute', + value => $params, + }); + + return Koha::Patron::Message::Attributes->find( + $attribute->{message_attribute_id} + ); +} + +sub build_a_test_category { + my $categorycode = $builder->build({ + source => 'Category' })->{categorycode}; + + return Koha::Patron::Categories->find($categorycode); +} + +sub build_a_test_letter { + my ($params) = @_; + + my $mtt = $params->{mtt} ? $params->{mtt} : 'email'; + my $branchcode = $builder->build({ + source => 'Branch' })->{branchcode}; + my $letter = $builder->build({ + source => 'Letter', + value => { + branchcode => '', + is_html => 0, + message_transport_type => $mtt + } + }); + + return Koha::Notice::Templates->find({ + module => $letter->{module}, + code => $letter->{code}, + branchcode => $letter->{branchcode}, + }); +} + +sub build_a_test_transport_type { + my $mtt = $builder->build({ + source => 'MessageTransportType' }); + + return Koha::Patron::Message::Transport::Types->find( + $mtt->{message_transport_type} + ); +} + +sub build_a_test_category_preference { + my ($params) = @_; + + my $patron = $params->{patron}; + my $attr = $params->{attr} + ? $params->{attr} + : build_a_test_attribute($params->{days_in_advance}); + + my $letter = $params->{letter} ? $params->{letter} : build_a_test_letter(); + my $mtt1 = $params->{mtt1} ? $params->{mtt1} : build_a_test_transport_type(); + my $mtt2 = $params->{mtt2} ? $params->{mtt2} : build_a_test_transport_type(); + + Koha::Patron::Message::Transport->new({ + message_attribute_id => $attr->message_attribute_id, + message_transport_type => $mtt1->message_transport_type, + is_digest => $params->{digest} ? 1 : 0, + letter_module => $letter->module, + letter_code => $letter->code, + })->store; + + Koha::Patron::Message::Transport->new({ + message_attribute_id => $attr->message_attribute_id, + message_transport_type => $mtt2->message_transport_type, + is_digest => $params->{digest} ? 1 : 0, + letter_module => $letter->module, + letter_code => $letter->code, + })->store; + + my $default = Koha::Patron::Message::Preference->new({ + categorycode => $patron->categorycode, + message_attribute_id => $attr->message_attribute_id, + wants_digest => $params->{digest} ? 1 : 0, + days_in_advance => $params->{days_in_advance} + ? $params->{days_in_advance} : undef, + })->store; + + Koha::Patron::Message::Transport::Preference->new({ + borrower_message_preference_id => $default->borrower_message_preference_id, + message_transport_type => $mtt1->message_transport_type, + })->store; + Koha::Patron::Message::Transport::Preference->new({ + borrower_message_preference_id => $default->borrower_message_preference_id, + message_transport_type => $mtt2->message_transport_type, + })->store; + + return ($default, $mtt1, $mtt2); +} + +sub build_a_test_complete_preference { + my ($params) = @_; + + my ($default, $mtt1, $mtt2) = build_a_test_category_preference($params); + my $patron = $params->{patron}; + $patron->set_default_messaging_preferences; + return (Koha::Patron::Message::Preferences->search({ + borrowernumber => $patron->borrowernumber + })->next, $mtt1, $mtt2); +} --