Koha/C4/Message.pm
Martin Renvoize 488e67656f
Bug 18398: (follow-up) Update POD & Unit tests
Signed-off-by: Martin Renvoize <martin.renvoize@ptfs-europe.com>
Signed-off-by: Nick Clemens <nick@bywatersolutions.com>
Signed-off-by: Tomas Cohen Arazi <tomascohen@theke.io>
2023-03-31 13:13:35 +02:00

416 lines
10 KiB
Perl

package C4::Message;
# Copyright Liblime 2009
# Copyright Catalyst IT 2012
#
# 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 <http://www.gnu.org/licenses>.
use strict;
use warnings;
use C4::Context;
use C4::Letters qw( GetPreparedLetter EnqueueLetter );
use YAML::XS qw( Dump );
use Encode;
use Carp qw( carp );
=head1 NAME
C4::Message - object for messages in the message_queue table
=head1 SYNOPSIS
How to add a new message to the queue:
use C4::Message;
use C4::Items;
my $patron = Koha::Patron->find({ borrowernumber => 1 });
my $item = Koha::Items->find($itemnumber)->unblessed;
my $letter = C4::Letters::GetPreparedLetter (
module => 'circulation',
letter_code => 'CHECKOUT',
branchcode => $branch,
tables => {
'biblio', $item->{biblionumber},
'biblioitems', $item->{biblionumber},
},
);
C4::Message->enqueue($letter, $patron, 'email');
How to update a borrower's last checkout message:
use C4::Message;
my $borrower = { borrowernumber => 1 };
my $message = C4::Message->find_last_message($borrower, 'CHECKOUT', 'email');
$message->append("you also checked out some other book....");
$message->update;
=head1 DESCRIPTION
This module presents an OO interface to the message_queue. Previously,
you could only add messages to the message_queue via
C<C4::Letters::EnqueueMessage()>. With this module, you can also get
previously inserted messages, manipulate them, and save them back to the
database.
=cut
our $AUTOLOAD;
=head2 Class Methods
=head3 C4::Message->new(\%attributes)
This method creates an in-memory version of a message object.
=cut
# C4::Message->new(\%attributes) -- constructor
sub new {
my ($class, $opts) = @_;
$opts ||= {};
bless {%$opts} => $class;
}
=head3 C4::Message->find($id)
This method searches the message_queue table for a row with the given
C<message_id> and it'll return a C4::Message object if it finds one.
=cut
# C4::Message->find($id) -- find a message by its message_id
sub find {
my ($class, $id) = @_;
my $dbh = C4::Context->dbh;
my $msgs = $dbh->selectall_arrayref(
qq{SELECT * FROM message_queue WHERE message_id = ?},
{ Slice => {} },
$id,
);
if (@$msgs) {
return $class->new($msgs->[0]);
} else {
return;
}
}
=head3 C4::Message->find_last_message($borrower, $letter_code, $transport)
This method is used to get the borrower's most recent, pending, check-in or
checkout message. (This makes it possible to add more information to the
message before it gets sent out.)
=cut
# C4::Message->find_last_message($borrower, $letter_code, $transport)
# -- get the borrower's most recent pending checkin or checkout notification
sub find_last_message {
my ($class, $borrower, $letter_code, $transport) = @_;
# $type is the message_transport_type
$transport ||= 'email';
my $dbh = C4::Context->dbh;
my $msgs = $dbh->selectall_arrayref(
qq{
SELECT *
FROM message_queue
WHERE status = 'pending'
AND borrowernumber = ?
AND letter_code = ?
AND message_transport_type = ?
},
{ Slice => {} },
$borrower->{borrowernumber},
$letter_code,
$transport,
);
if (@$msgs) {
return $class->new($msgs->[0]);
} else {
return;
}
}
=head3 C4::Message->enqueue($letter, $patron, $transport)
This is a front-end for C<C4::Letters::EnqueueLetter()> that adds metadata to
the message.
=cut
# C4::Message->enqueue($letter, $borrower, $transport)
sub enqueue {
my ( $class, $letter, $patron, $transport ) = @_;
my $metadata = _metadata($letter);
my $to_address = _to_address( $patron, $transport );
# Same as render_metadata
my $format ||= sub { $_[0] || "" };
my $body = join( '', map { $format->($_) } @{ $metadata->{body} } );
$letter->{content} = $metadata->{header} . $body . $metadata->{footer};
$letter->{metadata} = Encode::decode_utf8( Dump($metadata) );
C4::Letters::EnqueueLetter(
{
letter => $letter,
borrowernumber => $patron->id,
message_transport_type => $transport,
to_address => $to_address,
}
);
}
# based on message $transport, pick an appropriate address to send to
sub _to_address {
my ( $patron, $transport ) = @_;
my $address;
if ( $transport eq 'email' ) {
$address = $patron->notice_email_address;
}
elsif ( $transport eq 'sms' ) {
$address = $patron->smsalertnumber;
}
else {
warn "'$transport' is an unknown message transport.";
}
if ( not defined $address ) {
warn "An appropriate $transport address "
. "for borrower "
. $patron->userid
. "could not be found.";
}
return $address;
}
# _metadata($letter) -- return the letter split into head/body/footer
sub _metadata {
my ($letter) = @_;
if ($letter->{content} =~ /----/) {
my ($header, $body, $footer) = split(/----\r?\n?/, $letter->{content});
return {
header => $header,
body => [$body],
footer => $footer,
};
} else {
return {
header => '',
body => [$letter->{content}],
footer => '',
};
}
}
=head2 Instance Methods
=head3 $message->update()
This saves the $message object back to the database. It needs to have
already been created via C<enqueue> for this to work.
=cut
# $object->update -- save object to database
sub update {
my ($self) = @_;
my $dbh = C4::Context->dbh;
$dbh->do(
qq{
UPDATE message_queue
SET
borrowernumber = ?,
subject = ?,
content = ?,
metadata = ?,
letter_code = ?,
message_transport_type = ?,
status = ?,
time_queued = ?,
to_address = ?,
from_address = ?,
content_type = ?
WHERE message_id = ?
},
{},
$self->borrowernumber,
$self->subject,
$self->content,
$self->{metadata}, # we want the raw YAML here
$self->letter_code,
$self->message_transport_type,
$self->status,
$self->time_queued,
$self->to_address,
$self->from_address,
$self->content_type,
$self->message_id
);
}
=head3 $message->metadata(\%new_metadata)
This method automatically serializes and deserializes the metadata
attribute. (It is stored in YAML format.)
=cut
# $object->metadata -- this is a YAML serialized column that contains a
# structured representation of $object->content
sub metadata {
my ($self, $data) = @_;
if ($data) {
$data->{header} ||= '';
$data->{body} ||= [];
$data->{footer} ||= '';
$self->{metadata} = Encode::decode_utf8(Dump($data));
$self->content($self->render_metadata);
return $data;
} else {
return YAML::XS::Load(Encode::encode_utf8($self->{metadata}));
}
}
# turn $object->metadata into a string suitable for $object->content
sub render_metadata {
my ($self, $format) = @_;
$format ||= sub { $_[0] || "" };
my $metadata = $self->metadata;
my $body = $metadata->{body};
my $text = join('', map { $format->($_) } @$body);
return $metadata->{header} . $text . $metadata->{footer};
}
=head3 $message->append(\%letter)
If passed a hashref, this method will assume that the hashref is in the form
that C<C4::Letters::getletter()> returns. It will append the body of the
letter to the message.
=head3 $message->append($string)
If passed a string, it'll append the string to the message.
=cut
# $object->append($letter_or_item) -- add a new item to a message's content
sub append {
my ($self, $letter_or_item, $format) = @_;
my ( $item, $header, $footer );
if (ref($letter_or_item)) {
my $letter = $letter_or_item;
my $metadata = _metadata($letter);
$header = $metadata->{header};
$footer = $metadata->{footer};
$item = $metadata->{body}->[0];
} else {
$item = $letter_or_item;
}
if (not $self->metadata) {
carp "Can't append to messages that don't have metadata.";
return;
}
my $metadata = $self->metadata;
push @{$metadata->{body}}, $item;
$metadata->{header} = $header;
$metadata->{footer} = $footer;
$self->metadata($metadata);
my $new_content = $self->render_metadata($format);
return $self->content($new_content);
}
=head2 Attributes Accessors
=head3 $message->message_id
=cut
=head3 $message->borrowernumber
=cut
=head3 $message->subject
=cut
=head3 $message->content
=cut
=head3 $message->metadata
=cut
=head3 $message->letter_code
=cut
=head3 $message->message_transport_type
=cut
=head3 $message->status
=cut
=head3 $message->time_queued
=cut
=head3 $message->to_address
=cut
=head3 $message->from_address
=cut
=head3 $message->content_type
=cut
# $object->$method -- treat keys as methods
sub AUTOLOAD {
my ($self, @args) = @_;
my $attr = $AUTOLOAD;
$attr =~ s/.*://;
if (ref($self->{$attr}) eq 'CODE') {
$self->{$attr}->($self, @args);
} else {
if (@args) {
$self->{$attr} = $args[0];
} else {
$self->{$attr};
}
}
}
sub DESTROY { }
1;
=head1 SEE ALSO
L<C4::Circulation>, L<C4::Letters>, L<C4::Members::Messaging>
=head1 AUTHOR
John Beppu <john.beppu@liblime.com>
=cut