package Koha::Patron::MessagePreference; # Copyright Koha-Suomi Oy 2016 # # 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, see . use Modern::Perl; use Koha::Database; use Koha::Exceptions; use Koha::Exceptions::Patron::MessagePreference; use Koha::Exceptions::Patron::MessagePreference::Transport; use Koha::Exceptions::Patron::Category; use Koha::Patron::Categories; use Koha::Patron::MessagePreference::Attributes; use Koha::Patron::MessagePreferences; use Koha::Patron::MessagePreference::Transport::Preferences; use Koha::Patron::MessagePreference::Transport::Types; use Koha::Patron::MessagePreference::Transports; use Koha::Patrons; use base qw(Koha::Object); =head1 NAME Koha::Patron::MessagePreference - Koha Patron Message Preference object class =head1 API =head2 Class Methods =cut =head3 new my $preference = Koha::Patron::MessagePreference->new({ borrowernumber => 123, #categorycode => 'ABC', message_attribute_id => 4, message_transport_types => ['email', 'sms'], # see documentation below wants_digest => 1, days_in_advance => 7, }); Takes either borrowernumber or categorycode, but not both. days_in_advance may not be available. See message_attributes table for takes_days configuration. wants_digest may not be available. See message_transports table for is_digest configuration. You can instantiate a new object without custom validation errors, but when storing, validation may throw exceptions. See C for more documentation. C is a parameter that is not actually a column in this Koha-object. Given this parameter, the message transport types will be added as related transport types for this object. For get and set, you can access them via subroutine C in this class. =cut sub new { my ( $class, $params ) = @_; my $types = $params->{'message_transport_types'}; delete $params->{'message_transport_types'}; my $self = $class->SUPER::new($params); $self->_set_message_transport_types($types); return $self; } =head3 new_from_default my $preference = Koha::Patron::MessagePreference->new_from_default({ borrowernumber => 123, categorycode => 'ABC', # if not given, patron's categorycode will be used message_attribute_id => 1, }); NOTE: This subroutine initializes and STORES the object (in order to set message transport types for the preference), so no need to call ->store when preferences are initialized via this method. Stores default messaging preference for C to patron for given C. Throws Koha::Exceptions::MissingParameter if any of following is missing: - borrowernumber - message_attribute_id Throws Koha::Exceptions::ObjectNotFound if default preferences are not found. =cut sub new_from_default { my ( $class, $params ) = @_; my @required = qw(borrowernumber message_attribute_id); foreach my $p (@required) { Koha::Exceptions::MissingParameter->throw( error => "Missing required parameter '$p'.", ) unless exists $params->{$p}; } unless ( $params->{'categorycode'} ) { my $patron = Koha::Patrons->find( $params->{borrowernumber} ); $params->{'categorycode'} = $patron->categorycode; } my $default = Koha::Patron::MessagePreferences->find( { categorycode => $params->{'categorycode'}, message_attribute_id => $params->{'message_attribute_id'}, } ); Koha::Exceptions::ObjectNotFound->throw( error => 'Default messaging preference for given categorycode and' . ' message_attribute_id cannot be found.', ) unless $default; $default = $default->unblessed; # Add a new messaging preference for patron my $self = $class->SUPER::new( { borrowernumber => $params->{'borrowernumber'}, message_attribute_id => $default->{'message_attribute_id'}, days_in_advance => $default->{'days_in_advance'}, wants_digest => $default->{'wants_digest'}, } )->store; # Set default messaging transport types my $default_transport_types = Koha::Patron::MessagePreference::Transport::Preferences->search( { borrower_message_preference_id => $default->{'borrower_message_preference_id'} } ); while ( my $transport = $default_transport_types->next ) { Koha::Patron::MessagePreference::Transport::Preference->new( { borrower_message_preference_id => $self->borrower_message_preference_id, message_transport_type => $transport->message_transport_type, } )->store; } return $self; } =head3 message_name $preference->message_name Gets message_name for this messaging preference. Setter not implemented. =cut sub message_name { my ($self) = @_; if ( $self->{'_message_name'} ) { return $self->{'_message_name'}; } $self->{'_message_name'} = Koha::Patron::MessagePreference::Attributes->find( { message_attribute_id => $self->message_attribute_id, } )->message_name; return $self->{'_message_name'}; } =head3 message_transport_types $preference->message_transport_types Returns a HASHREF of message transport types for this messaging preference, e.g. if ($preference->message_transport_types->{'email'}) { # email is one of the transport preferences } $preference->message_transport_types('email', 'sms'); Sets the given message transport types for this messaging preference =cut sub message_transport_types { my $self = shift; unless (@_) { if ( $self->{'_message_transport_types'} ) { return $self->{'_message_transport_types'}; } map { my $transport = Koha::Patron::MessagePreference::Transports->find( { message_attribute_id => $self->message_attribute_id, message_transport_type => $_->message_transport_type, is_digest => $self->wants_digest } ); unless ($transport) { my $logger = Koha::Logger->get; $logger->warn( $self->message_name . ' has no transport with ' . $_->message_transport_type . ' (digest: ' . ( $self->wants_digest ? 'yes' : 'no' ) . ').' ); } $self->{'_message_transport_types'}->{ $_->message_transport_type } = $transport ? $transport->letter_code : ' '; } Koha::Patron::MessagePreference::Transport::Preferences->search( { borrower_message_preference_id => $self->borrower_message_preference_id, } )->as_list; return $self->{'_message_transport_types'} || {}; } else { $self->_set_message_transport_types(@_); return $self; } } =head3 mtt_deliverable $preference->mtt_deliverable('sms'[, $borrowernumer]); Returns true if given message transport type can be used to deliver message to patron. By default, uses the borrowernumber bound to C<$preference>, but this may be overridden by providing optional C<$borrowernumber> parameter. =cut sub mtt_deliverable { my ( $self, $mtt, $borrowernumber ) = @_; $borrowernumber //= $self->borrowernumber; return 0 unless ($borrowernumber); my $patron = Koha::Patrons->find( $self->borrowernumber ); return ( ( $mtt eq 'email' and $patron->notice_email_address ) # No email address or ( $mtt eq 'sms' and $patron->smsalertnumber ) # No SMS number or ( $mtt eq 'itiva' and C4::Context->preference('TalkingTechItivaPhoneNotification') ) # Notice is handled by TalkingTech_itiva_outbound.pl or ( $mtt eq 'phone' and $patron->phone ) ) # No phone number to call ? 1 : 0; } =head3 set $preference->set({ message_transport_types => ['sms', 'phone'], wants_digest => 0, })->store; Sets preference object values and additionally message_transport_types if given. =cut sub set { my ( $self, $params ) = @_; my $mtt = $params->{'message_transport_types'}; delete $params->{'message_transport_types'}; $self->SUPER::set($params) if $params; if ($mtt) { $self->message_transport_types($mtt); } return $self; } =head3 store Makes a validation before actual Koha::Object->store so that proper exceptions can be thrown. See C for documentation about exceptions. =cut sub store { my $self = shift; $self->validate->SUPER::store(@_); # store message transport types if ( exists $self->{'_message_transport_types'} ) { Koha::Patron::MessagePreference::Transport::Preferences->search( { borrower_message_preference_id => $self->borrower_message_preference_id, } )->delete; foreach my $type ( keys %{ $self->{'_message_transport_types'} } ) { Koha::Patron::MessagePreference::Transport::Preference->new( { borrower_message_preference_id => $self->borrower_message_preference_id, message_transport_type => $type, } )->store; } } return $self; } =head3 validate Makes a basic validation for object. Returns Koha::Patron::MessagePreference object, or throws and exception. =cut sub validate { my ($self) = @_; if ( $self->borrowernumber && $self->categorycode ) { Koha::Exceptions::TooManyParameters->throw( error => 'Both borrowernumber and category given, only one accepted', ); } if ( !$self->borrowernumber && !$self->categorycode ) { Koha::Exceptions::MissingParameter->throw( error => 'borrowernumber or category required, none given', ); } if ( $self->borrowernumber ) { Koha::Exceptions::Patron::NotFound->throw( error => 'Patron not found.', ) unless Koha::Patrons->find( $self->borrowernumber ); } if ( $self->categorycode ) { Koha::Exceptions::Patron::Category::NotFound->throw( error => 'Category not found.', ) unless Koha::Patron::Categories->find( $self->categorycode ); } if ( !$self->in_storage ) { my $previous = Koha::Patron::MessagePreferences->search( { borrowernumber => $self->borrowernumber, categorycode => $self->categorycode, message_attribute_id => $self->message_attribute_id, } ); if ( $previous->count ) { Koha::Exceptions::DuplicateObject->throw( error => 'A preference for this borrower/category and' . ' message_attribute_id already exists', ); } } my $attr = Koha::Patron::MessagePreference::Attributes->find( $self->message_attribute_id ); unless ($attr) { Koha::Exceptions::Patron::MessagePreference::AttributeNotFound->throw( error => 'Message attribute with id ' . $self->message_attribute_id . ' not found', message_attribute_id => $self->message_attribute_id, ); } if ( defined $self->days_in_advance ) { if ( $attr && $attr->takes_days == 0 ) { Koha::Exceptions::Patron::MessagePreference::DaysInAdvanceNotAvailable->throw( error => 'days_in_advance cannot be defined for ' . $attr->message_name . '.', message_name => $attr->message_name, ); } elsif ( $self->days_in_advance < 0 || $self->days_in_advance > 30 ) { Koha::Exceptions::Patron::MessagePreference::DaysInAdvanceOutOfRange->throw( error => 'days_in_advance has to be a value between 0-30 for ' . $attr->message_name . '.', message_name => $attr->message_name, min => 0, max => 30, ); } } if ( defined $self->wants_digest ) { my $transports = Koha::Patron::MessagePreference::Transports->search( { message_attribute_id => $self->message_attribute_id, is_digest => $self->wants_digest ? 1 : 0, } ); unless ( $transports->count ) { if ( !$self->wants_digest ) { Koha::Exceptions::Patron::MessagePreference::DigestRequired->throw( error => 'Digest must be selected for ' . $attr->message_name . '.', message_name => $attr->message_name ); } else { Koha::Exceptions::Patron::MessagePreference::DigestNotAvailable->throw( error => 'Digest cannot be selected for ' . $attr->message_name . '.', message_name => $attr->message_name ); } } } return $self; } sub _set_message_transport_types { my $self = shift; return unless $_[0]; $self->{'_message_transport_types'} = undef; my $types = ref $_[0] eq "ARRAY" ? $_[0] : [@_]; return unless $types; $self->_validate_message_transport_types( { message_transport_types => $types } ); foreach my $type (@$types) { unless ( exists $self->{'_message_transport_types'}->{$type} ) { my $transport = Koha::Patron::MessagePreference::Transports->search( { message_attribute_id => $self->message_attribute_id, message_transport_type => $type } )->next; unless ($transport) { Koha::Exceptions::Patron::MessagePreference::NoTransportType->throw( error => 'No transport configured for ' . $self->message_name . " transport type $type.", message_name => $self->message_name, transport_type => $type, ); } if ( defined $self->borrowernumber ) { if ( !$self->mtt_deliverable($type) ) { Koha::Exceptions::Patron::MessagePreference::EmailAddressRequired->throw( error => 'Patron has not set email address, ' . 'cannot use email as message transport', message_name => $self->message_name, borrowernumber => $self->borrowernumber, ) if $type eq 'email'; Koha::Exceptions::Patron::MessagePreference::TalkingTechItivaPhoneNotificationRequired->throw( error => 'System preference TalkingTechItivaPhoneNotification disabled' . 'cannot use itiva as message transport', message_name => $self->message_name, borrowernumber => $self->borrowernumber, ) if $type eq 'itiva'; Koha::Exceptions::Patron::MessagePreference::PhoneNumberRequired->throw( error => 'Patron has not set phone number' . 'cannot use phone as message transport', message_name => $self->message_name, borrowernumber => $self->borrowernumber, ) if $type eq 'phone'; Koha::Exceptions::Patron::MessagePreference::SMSNumberRequired->throw( error => 'Patron has not set SMS number' . 'cannot use sms as message transport', message_name => $self->message_name, borrowernumber => $self->borrowernumber, ) if $type eq 'sms'; } } $self->{'_message_transport_types'}->{$type} = $transport->letter_code; } } return $self; } sub _validate_message_transport_types { my ( $self, $params ) = @_; if ( ref($params) eq 'HASH' && $params->{'message_transport_types'} ) { if ( ref( $params->{'message_transport_types'} ) ne 'ARRAY' ) { $params->{'message_transport_types'} = [ $params->{'message_transport_types'} ]; } my $types = $params->{'message_transport_types'}; foreach my $type ( @{$types} ) { unless ( Koha::Patron::MessagePreference::Transport::Types->find( { message_transport_type => $type } ) ) { Koha::Exceptions::Patron::MessagePreference::Transport::TypeNotFound->throw( error => "Message transport type '$type' does not exist", transport_type => $type, ); } } return $types; } } =head3 type =cut sub _type { return 'BorrowerMessagePreference'; } =head1 AUTHOR Lari Taskula =cut 1;