Merge remote branch 'kc/new/bug_5563' into kcmaster
[koha.git] / C4 / Message.pm
blob16272ff4c2ddc0711166613d98572e0cbc25d209
1 package C4::Message;
2 use strict;
3 use warnings;
4 use C4::Context;
5 use C4::Letters;
6 use YAML::Syck;
7 use Carp;
9 =head1 NAME
11 C4::Message - object for messages in the message_queue table
13 =head1 SYNOPSIS
15 How to add a new message to the queue:
17 use C4::Message;
18 use C4::Items;
19 my $borrower = { borrowernumber => 1 };
20 my $item = C4::Items::GetItem(1);
21 my $letter = C4::Letters::getletter('circulation', 'CHECKOUT');
22 C4::Letters::parseletter($letter, 'biblio', $item->{biblionumber});
23 C4::Letters::parseletter($letter, 'biblioitems', $item->{biblionumber});
24 C4::Message->enqueue($letter, $borrower->{borrowernumber}, 'email');
26 How to update a borrower's last checkout message:
28 use C4::Message;
29 my $borrower = { borrowernumber => 1 };
30 my $message = C4::Message->find_last_message($borrower, 'CHECKOUT', 'email');
31 $message->append("you also checked out some other book....");
32 $message->update;
34 =head1 DESCRIPTION
36 This module presents an OO interface to the message_queue. Previously,
37 you could only add messages to the message_queue via
38 C<C4::Letters::EnqueueMessage()>. With this module, you can also get
39 previously inserted messages, manipulate them, and save them back to the
40 database.
42 =cut
45 our $AUTOLOAD;
48 =head2 Class Methods
50 =head3 C4::Message->new(\%attributes)
52 This method creates an in-memory version of a message object.
54 =cut
56 # C4::Message->new(\%attributes) -- constructor
57 sub new {
58 my ($class, $opts) = @_;
59 $opts ||= {};
60 bless {%$opts} => $class;
64 =head3 C4::Message->find($id)
66 This method searches the message_queue table for a row with the given
67 C<message_id> and it'll return a C4::Message object if it finds one.
69 =cut
71 # C4::Message->find($id) -- find a message by its message_id
72 sub find {
73 my ($class, $id) = @_;
74 my $dbh = C4::Context->dbh;
75 my $msgs = $dbh->selectall_arrayref(
76 qq{SELECT * FROM message_queue WHERE message_id = ?},
77 { Slice => {} },
78 $id,
80 if (@$msgs) {
81 return $class->new($msgs->[0]);
82 } else {
83 return undef;
87 =head3 C4::Message->find_last_message($borrower, $letter_code, $transport)
89 This method is used to get the borrower's most recent, pending, check-in or
90 checkout message. (This makes it possible to add more information to the
91 message before it gets sent out.)
93 =cut
95 # C4::Message->find_last_message($borrower, $letter_code, $transport)
96 # -- get the borrower's most recent pending checkin or checkout notification
97 sub find_last_message {
98 my ($class, $borrower, $letter_code, $transport) = @_;
99 # $type is the message_transport_type
100 $transport ||= 'email';
101 my $dbh = C4::Context->dbh;
102 my $msgs = $dbh->selectall_arrayref(
104 SELECT *
105 FROM message_queue
106 WHERE status = 'pending'
107 AND borrowernumber = ?
108 AND letter_code = ?
109 AND message_transport_type = ?
111 { Slice => {} },
112 $borrower->{borrowernumber},
113 $letter_code,
114 $transport,
116 if (@$msgs) {
117 return $class->new($msgs->[0]);
118 } else {
119 return undef;
124 =head3 C4::Message->enqueue($letter, $borrower, $transport)
126 This is a front-end for C<C4::Letters::EnqueueLetter()> that adds metadata to
127 the message.
129 =cut
131 # C4::Message->enqueue($letter, $borrower, $transport)
132 sub enqueue {
133 my ($class, $letter, $borrower, $transport) = @_;
134 my $metadata = _metadata($letter);
135 my $to_address = _to_address($borrower, $transport);
136 $letter->{metadata} = Dump($metadata);
137 #carp "enqueuing... to $to_address";
138 C4::Letters::EnqueueLetter({
139 letter => $letter,
140 borrowernumber => $borrower->{borrowernumber},
141 message_transport_type => $transport,
142 to_address => $to_address,
146 # based on message $transport, pick an appropriate address to send to
147 sub _to_address {
148 my ($borrower, $transport) = @_;
149 my $address;
150 if ($transport eq 'email') {
151 $address = $borrower->{email}
152 || $borrower->{emailpro}
153 || $borrower->{B_email};
154 } elsif ($transport eq 'sms') {
155 $address = $borrower->{smsalertnumber}
156 || $borrower->{phone}
157 || $borrower->{phonepro}
158 || $borrower->{B_phone};
159 } else {
160 warn "'$transport' is an unknown message transport.";
162 if (not defined $address) {
163 warn "An appropriate $transport address "
164 . "for borrower $borrower->{userid} "
165 . "could not be found.";
167 return $address;
170 # _metadata($letter) -- return the letter split into head/body/footer
171 sub _metadata {
172 my ($letter) = @_;
173 if ($letter->{content} =~ /----/) {
174 my ($header, $body, $footer) = split(/----\r?\n?/, $letter->{content});
175 return {
176 header => $header,
177 body => [$body],
178 footer => $footer,
180 } else {
181 return {
182 header => '',
183 body => [$letter->{content}],
184 footer => '',
189 =head2 Instance Methods
191 =head3 $message->update()
193 This saves the $message object back to the database. It needs to have
194 already been created via C<enqueue> for this to work.
196 =cut
198 # $object->update -- save object to database
199 sub update {
200 my ($self) = @_;
201 my $dbh = C4::Context->dbh;
202 $dbh->do(
204 UPDATE message_queue
206 borrowernumber = ?,
207 subject = ?,
208 content = ?,
209 metadata = ?,
210 letter_code = ?,
211 message_transport_type = ?,
212 status = ?,
213 time_queued = ?,
214 to_address = ?,
215 from_address = ?,
216 content_type = ?
217 WHERE message_id = ?
220 $self->borrowernumber,
221 $self->subject,
222 $self->content,
223 $self->{metadata}, # we want the raw YAML here
224 $self->letter_code,
225 $self->message_transport_type,
226 $self->status,
227 $self->time_queued,
228 $self->to_address,
229 $self->from_address,
230 $self->content_type,
231 $self->message_id
235 =head3 $message->metadata(\%new_metadata)
237 This method automatically serializes and deserializes the metadata
238 attribute. (It is stored in YAML format.)
240 =cut
242 # $object->metadata -- this is a YAML serialized column that contains a
243 # structured representation of $object->content
244 sub metadata {
245 my ($self, $data) = @_;
246 if ($data) {
247 $data->{header} ||= '';
248 $data->{body} ||= [];
249 $data->{footer} ||= '';
250 $self->{metadata} = Dump($data);
251 $self->content($self->render_metadata);
252 return $data;
253 } else {
254 return Load($self->{metadata});
258 # turn $object->metadata into a string suitable for $object->content
259 sub render_metadata {
260 my ($self, $format) = @_;
261 $format ||= sub { $_[0] || "" };
262 my $metadata = $self->metadata;
263 my $body = $metadata->{body};
264 my $text = join('', map { $format->($_) } @$body);
265 return $metadata->{header} . $text . $metadata->{footer};
268 =head3 $message->append(\%letter)
270 If passed a hashref, this method will assume that the hashref is in the form
271 that C<C4::Letters::getletter()> returns. It will append the body of the
272 letter to the message.
274 =head3 $message->append($string)
276 If passed a string, it'll append the string to the message.
278 =cut
280 # $object->append($letter_or_item) -- add a new item to a message's content
281 sub append {
282 my ($self, $letter_or_item, $format) = @_;
283 my $item;
284 if (ref($letter_or_item)) {
285 my $letter = $letter_or_item;
286 my $metadata = _metadata($letter);
287 $item = $metadata->{body}->[0];
288 } else {
289 $item = $letter_or_item;
291 if (not $self->metadata) {
292 carp "Can't append to messages that don't have metadata.";
293 return undef;
295 my $metadata = $self->metadata;
296 push @{$metadata->{body}}, $item;
297 $self->metadata($metadata);
298 my $new_content = $self->render_metadata($format);
299 return $self->content($new_content);
302 =head2 Attributes Accessors
304 =head3 $message->message_id
306 =cut
308 =head3 $message->borrowernumber
310 =cut
312 =head3 $message->subject
314 =cut
316 =head3 $message->content
318 =cut
320 =head3 $message->metadata
322 =cut
324 =head3 $message->letter_code
326 =cut
328 =head3 $message->message_transport_type
330 =cut
332 =head3 $message->status
334 =cut
336 =head3 $message->time_queued
338 =cut
340 =head3 $message->to_address
342 =cut
344 =head3 $message->from_address
346 =cut
348 =head3 $message->content_type
350 =cut
352 # $object->$method -- treat keys as methods
353 sub AUTOLOAD {
354 my ($self, @args) = @_;
355 my $attr = $AUTOLOAD;
356 $attr =~ s/.*://;
357 if (ref($self->{$attr}) eq 'CODE') {
358 $self->{$attr}->($self, @args);
359 } else {
360 if (@args) {
361 $self->{$attr} = $args[0];
362 } else {
363 $self->{$attr};
368 sub DESTROY { }
372 =head1 SEE ALSO
374 L<C4::Circulation>, L<C4::Letters>, L<C4::Members::Messaging>
376 =head1 AUTHOR
378 John Beppu <john.beppu@liblime.com>
380 =cut