Bug 13190: Reintroduce the checkouts export feature
[koha.git] / C4 / Message.pm
bloba63b3caa9656a21e950fafddeda7e0d227521cf7
1 package C4::Message;
3 # Copyright Liblime 2009
4 # Copyright Catalyst IT 2012
6 # This file is part of Koha.
8 # Koha is free software; you can redistribute it and/or modify it under the
9 # terms of the GNU General Public License as published by the Free Software
10 # Foundation; either version 2 of the License, or (at your option) any later
11 # version.
13 # Koha is distributed in the hope that it will be useful, but WITHOUT ANY
14 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
15 # A PARTICULAR PURPOSE. See the GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License along
18 # with Koha; if not, write to the Free Software Foundation, Inc.,
19 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 use strict;
23 use warnings;
24 use C4::Context;
25 use C4::Letters;
26 use YAML::Syck;
27 use Carp;
29 =head1 NAME
31 C4::Message - object for messages in the message_queue table
33 =head1 SYNOPSIS
35 How to add a new message to the queue:
37 use C4::Message;
38 use C4::Items;
39 my $borrower = { borrowernumber => 1 };
40 my $item = C4::Items::GetItem(1);
41 my $letter = C4::Letters::GetPreparedLetter (
42 module => 'circulation',
43 letter_code => 'CHECKOUT',
44 branchcode => $branch,
45 tables => {
46 'biblio', $item->{biblionumber},
47 'biblioitems', $item->{biblionumber},
50 C4::Message->enqueue($letter, $borrower->{borrowernumber}, 'email');
52 How to update a borrower's last checkout message:
54 use C4::Message;
55 my $borrower = { borrowernumber => 1 };
56 my $message = C4::Message->find_last_message($borrower, 'CHECKOUT', 'email');
57 $message->append("you also checked out some other book....");
58 $message->update;
60 =head1 DESCRIPTION
62 This module presents an OO interface to the message_queue. Previously,
63 you could only add messages to the message_queue via
64 C<C4::Letters::EnqueueMessage()>. With this module, you can also get
65 previously inserted messages, manipulate them, and save them back to the
66 database.
68 =cut
71 our $AUTOLOAD;
74 =head2 Class Methods
76 =head3 C4::Message->new(\%attributes)
78 This method creates an in-memory version of a message object.
80 =cut
82 # C4::Message->new(\%attributes) -- constructor
83 sub new {
84 my ($class, $opts) = @_;
85 $opts ||= {};
86 bless {%$opts} => $class;
90 =head3 C4::Message->find($id)
92 This method searches the message_queue table for a row with the given
93 C<message_id> and it'll return a C4::Message object if it finds one.
95 =cut
97 # C4::Message->find($id) -- find a message by its message_id
98 sub find {
99 my ($class, $id) = @_;
100 my $dbh = C4::Context->dbh;
101 my $msgs = $dbh->selectall_arrayref(
102 qq{SELECT * FROM message_queue WHERE message_id = ?},
103 { Slice => {} },
104 $id,
106 if (@$msgs) {
107 return $class->new($msgs->[0]);
108 } else {
109 return;
113 =head3 C4::Message->find_last_message($borrower, $letter_code, $transport)
115 This method is used to get the borrower's most recent, pending, check-in or
116 checkout message. (This makes it possible to add more information to the
117 message before it gets sent out.)
119 =cut
121 # C4::Message->find_last_message($borrower, $letter_code, $transport)
122 # -- get the borrower's most recent pending checkin or checkout notification
123 sub find_last_message {
124 my ($class, $borrower, $letter_code, $transport) = @_;
125 # $type is the message_transport_type
126 $transport ||= 'email';
127 my $dbh = C4::Context->dbh;
128 my $msgs = $dbh->selectall_arrayref(
130 SELECT *
131 FROM message_queue
132 WHERE status = 'pending'
133 AND borrowernumber = ?
134 AND letter_code = ?
135 AND message_transport_type = ?
137 { Slice => {} },
138 $borrower->{borrowernumber},
139 $letter_code,
140 $transport,
142 if (@$msgs) {
143 return $class->new($msgs->[0]);
144 } else {
145 return;
150 =head3 C4::Message->enqueue($letter, $borrower, $transport)
152 This is a front-end for C<C4::Letters::EnqueueLetter()> that adds metadata to
153 the message.
155 =cut
157 # C4::Message->enqueue($letter, $borrower, $transport)
158 sub enqueue {
159 my ($class, $letter, $borrower, $transport) = @_;
160 my $metadata = _metadata($letter);
161 my $to_address = _to_address($borrower, $transport);
162 $letter->{metadata} = Dump($metadata);
163 #carp "enqueuing... to $to_address";
164 C4::Letters::EnqueueLetter({
165 letter => $letter,
166 borrowernumber => $borrower->{borrowernumber},
167 message_transport_type => $transport,
168 to_address => $to_address,
172 # based on message $transport, pick an appropriate address to send to
173 sub _to_address {
174 my ($borrower, $transport) = @_;
175 my $address;
176 if ($transport eq 'email') {
177 $address = $borrower->{email}
178 || $borrower->{emailpro}
179 || $borrower->{B_email};
180 } elsif ($transport eq 'sms') {
181 $address = $borrower->{smsalertnumber}
182 || $borrower->{phone}
183 || $borrower->{phonepro}
184 || $borrower->{B_phone};
185 } else {
186 warn "'$transport' is an unknown message transport.";
188 if (not defined $address) {
189 warn "An appropriate $transport address "
190 . "for borrower $borrower->{userid} "
191 . "could not be found.";
193 return $address;
196 # _metadata($letter) -- return the letter split into head/body/footer
197 sub _metadata {
198 my ($letter) = @_;
199 if ($letter->{content} =~ /----/) {
200 my ($header, $body, $footer) = split(/----\r?\n?/, $letter->{content});
201 return {
202 header => $header,
203 body => [$body],
204 footer => $footer,
206 } else {
207 return {
208 header => '',
209 body => [$letter->{content}],
210 footer => '',
215 =head2 Instance Methods
217 =head3 $message->update()
219 This saves the $message object back to the database. It needs to have
220 already been created via C<enqueue> for this to work.
222 =cut
224 # $object->update -- save object to database
225 sub update {
226 my ($self) = @_;
227 my $dbh = C4::Context->dbh;
228 $dbh->do(
230 UPDATE message_queue
232 borrowernumber = ?,
233 subject = ?,
234 content = ?,
235 metadata = ?,
236 letter_code = ?,
237 message_transport_type = ?,
238 status = ?,
239 time_queued = ?,
240 to_address = ?,
241 from_address = ?,
242 content_type = ?
243 WHERE message_id = ?
246 $self->borrowernumber,
247 $self->subject,
248 $self->content,
249 $self->{metadata}, # we want the raw YAML here
250 $self->letter_code,
251 $self->message_transport_type,
252 $self->status,
253 $self->time_queued,
254 $self->to_address,
255 $self->from_address,
256 $self->content_type,
257 $self->message_id
261 =head3 $message->metadata(\%new_metadata)
263 This method automatically serializes and deserializes the metadata
264 attribute. (It is stored in YAML format.)
266 =cut
268 # $object->metadata -- this is a YAML serialized column that contains a
269 # structured representation of $object->content
270 sub metadata {
271 my ($self, $data) = @_;
272 if ($data) {
273 $data->{header} ||= '';
274 $data->{body} ||= [];
275 $data->{footer} ||= '';
276 $self->{metadata} = Dump($data);
277 $self->content($self->render_metadata);
278 return $data;
279 } else {
280 return Load($self->{metadata});
284 # turn $object->metadata into a string suitable for $object->content
285 sub render_metadata {
286 my ($self, $format) = @_;
287 $format ||= sub { $_[0] || "" };
288 my $metadata = $self->metadata;
289 my $body = $metadata->{body};
290 my $text = join('', map { $format->($_) } @$body);
291 return $metadata->{header} . $text . $metadata->{footer};
294 =head3 $message->append(\%letter)
296 If passed a hashref, this method will assume that the hashref is in the form
297 that C<C4::Letters::getletter()> returns. It will append the body of the
298 letter to the message.
300 =head3 $message->append($string)
302 If passed a string, it'll append the string to the message.
304 =cut
306 # $object->append($letter_or_item) -- add a new item to a message's content
307 sub append {
308 my ($self, $letter_or_item, $format) = @_;
309 my $item;
310 if (ref($letter_or_item)) {
311 my $letter = $letter_or_item;
312 my $metadata = _metadata($letter);
313 $item = $metadata->{body}->[0];
314 } else {
315 $item = $letter_or_item;
317 if (not $self->metadata) {
318 carp "Can't append to messages that don't have metadata.";
319 return;
321 my $metadata = $self->metadata;
322 push @{$metadata->{body}}, $item;
323 $self->metadata($metadata);
324 my $new_content = $self->render_metadata($format);
325 return $self->content($new_content);
328 =head2 Attributes Accessors
330 =head3 $message->message_id
332 =cut
334 =head3 $message->borrowernumber
336 =cut
338 =head3 $message->subject
340 =cut
342 =head3 $message->content
344 =cut
346 =head3 $message->metadata
348 =cut
350 =head3 $message->letter_code
352 =cut
354 =head3 $message->message_transport_type
356 =cut
358 =head3 $message->status
360 =cut
362 =head3 $message->time_queued
364 =cut
366 =head3 $message->to_address
368 =cut
370 =head3 $message->from_address
372 =cut
374 =head3 $message->content_type
376 =cut
378 # $object->$method -- treat keys as methods
379 sub AUTOLOAD {
380 my ($self, @args) = @_;
381 my $attr = $AUTOLOAD;
382 $attr =~ s/.*://;
383 if (ref($self->{$attr}) eq 'CODE') {
384 $self->{$attr}->($self, @args);
385 } else {
386 if (@args) {
387 $self->{$attr} = $args[0];
388 } else {
389 $self->{$attr};
394 sub DESTROY { }
398 =head1 SEE ALSO
400 L<C4::Circulation>, L<C4::Letters>, L<C4::Members::Messaging>
402 =head1 AUTHOR
404 John Beppu <john.beppu@liblime.com>
406 =cut