Perl Modules
[koha.git] / C4 / Message.pm
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;
8
9 =head1 NAME
10
11 C4::Message - object for messages in the message_queue table
12
13 =head1 SYNOPSIS
14
15 How to add a new message to the queue:
16
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');
25
26 How to update a borrower's last checkout message:
27
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;
33
34 =head1 DESCRIPTION
35
36 This module presents an OO interface to the message_queue.  Previously, you could
37 only add messages to the message_queue via C<C4::Letters::EnqueueMessage()>.  With
38 this module, you can also get previously inserted messages, manipulate them, and
39 save them back to the database.
40
41 =cut
42
43
44 our $AUTOLOAD;
45
46
47 =head2 Class Methods
48
49 =head3 C4::Message->new(\%attributes)
50
51 This method creates an in-memory version of a message object.
52
53 =cut
54
55 # C4::Message->new(\%attributes) -- constructor
56 sub new {
57     my ($class, $opts) = @_;
58     $opts ||= {};
59     bless {%$opts} => $class;
60 }
61
62
63 =head3 C4::Message->find($id)
64
65 This method searches the message_queue table for a row with the given
66 C<message_id> and it'll return a C4::Message object if it finds one.
67
68 =cut
69
70 # C4::Message->find($id) -- find a message by its message_id
71 sub find {
72     my ($class, $id) = @_;
73     my $dbh = C4::Context->dbh;
74     my $msgs = $dbh->selectall_arrayref(
75         qq{SELECT * FROM message_queue WHERE message_id = ?},
76         { Slice => {} },
77         $id,
78     );
79     if (@$msgs) {
80         return $class->new($msgs->[0]);
81     } else {
82         return undef;
83     }
84 }
85
86 =head3 C4::Message->find_last_message($borrower, $letter_code, $transport)
87
88 This method is used to get the borrower's most recent, pending, check-in or
89 checkout message.  (This makes it possible to add more information to the
90 message before it gets sent out.)
91
92 =cut
93
94 # C4::Message->find_last_message($borrower, $letter_code, $transport)
95 # -- get the borrower's most recent pending checkin or checkout notification
96 sub find_last_message {
97     my ($class, $borrower, $letter_code, $transport) = @_;
98     # $type is the message_transport_type
99     $transport ||= 'email';
100     my $dbh = C4::Context->dbh;
101     my $msgs = $dbh->selectall_arrayref(
102         qq{
103             SELECT *
104             FROM   message_queue
105             WHERE  status                 = 'pending'
106             AND    borrowernumber         = ?
107             AND    letter_code            = ?
108             AND    message_transport_type = ?
109         },
110         { Slice => {} },
111         $borrower->{borrowernumber},
112         $letter_code,
113         $transport,
114     );
115     if (@$msgs) {
116         return $class->new($msgs->[0]);
117     } else {
118         return undef;
119     }
120 }
121
122
123 =head3 C4::Message->enqueue($letter, $borrowernumber, $transport)
124
125 This is a front-end for C<C4::Letters::EnqueueLetter()> that adds metadata to
126 the message.
127
128 =cut
129
130 # C4::Message->enqueue($letter, $borrowernumber, $transport)
131 sub enqueue {
132     my ($class, $letter, $borrowernumber, $transport) = @_;
133     my $metadata = _make_metadata($letter);
134     $letter->{metadata} = Dump($metadata);
135     carp 'enqueuing...';
136     C4::Letters::EnqueueLetter({
137       letter                 => $letter,
138       borrowernumber         => $borrowernumber,
139       message_transport_type => $transport,
140     });
141 }
142
143 # _make_metadata($letter) -- return the letter split into head/body/footer
144 sub _make_metadata {
145     my ($letter) = @_;
146     if ($letter->{content} =~ /----/) {
147         my ($header, $body, $footer) = split(/----\r?\n?/, $letter->{content});
148         return {
149             header => $header,
150             body   => [$body],
151             footer => $footer,
152         };
153     } else {
154         return {
155             header => '',
156             body   => [$letter->{content}],
157             footer => '',
158         };
159     }
160 }
161
162 =head2 Instance Methods
163
164 =head3 $message->update()
165
166 This saves the $message object back to the database.  It needs to have
167 already been created via C<enqueue> for this to work.
168
169 =cut
170
171 # $object->update -- save object to database
172 sub update {
173     my ($self) = @_;
174     my $dbh = C4::Context->dbh;
175     $dbh->do(
176         qq{
177             UPDATE message_queue
178             SET
179                 borrowernumber         = ?,
180                 subject                = ?,
181                 content                = ?,
182                 metadata               = ?,
183                 letter_code            = ?,
184                 message_transport_type = ?,
185                 status                 = ?,
186                 time_queued            = ?,
187                 to_address             = ?,
188                 from_address           = ?,
189                 content_type           = ?
190             WHERE message_id = ?
191         },
192         {},
193         $self->borrowernumber,
194         $self->subject,
195         $self->content,
196         $self->{metadata}, # we want the raw YAML here
197         $self->letter_code,
198         $self->message_transport_type,
199         $self->status,
200         $self->time_queued,
201         $self->to_address,
202         $self->from_address,
203         $self->content_type,
204         $self->message_id
205     );
206 }
207
208 =head3 $message->metadata(\%new_metadata)
209
210 This method automatically serializes and deserializes the metadata
211 attribute.  (It is stored in YAML format.)
212
213 =cut
214
215 # $object->metadata -- this is a YAML serialized column that contains a
216 # structured representation of $object->content
217 sub metadata {
218     my ($self, $data) = @_;
219     if ($data) {
220         $data->{header} ||= '';
221         $data->{body}   ||= [];
222         $data->{footer} ||= '';
223         $self->{metadata} = Dump($data);
224         $self->content($self->render_metadata);
225         return $data;
226     } else {
227         return Load($self->{metadata});
228     }
229 }
230
231 # turn $object->metadata into a string suitable for $object->content
232 sub render_metadata {
233     my ($self, $format) = @_;
234     $format ||= sub { $_[0] || "" };
235     my $metadata = $self->metadata;
236     my $body     = $metadata->{body};
237     my $text     = join('', map { $format->($_) } @$body);
238     return $metadata->{header} . $text . $metadata->{footer};
239 }
240
241 =head3 $message->append(\%letter)
242
243 If passed a hashref, this method will assume that the hashref is in the form
244 that C<C4::Letters::getletter()> returns.  It will append the body of the
245 letter to the message.
246
247 =head3 $message->append($string)
248
249 If passed a string, it'll append the string to the message.
250
251 =cut
252
253 # $object->append($letter_or_item) -- add a new item to a message's content
254 sub append {
255     my ($self, $letter_or_item, $format) = @_;
256     my $item;
257     if (ref($letter_or_item)) {
258         my $letter   = $letter_or_item;
259         my $metadata = _make_metadata($letter);
260         $item = $metadata->{body}->[0];
261     } else {
262         $item = $letter_or_item;
263     }
264     if (not $self->metadata) {
265         carp "Can't append to messages that don't have metadata.";
266         return undef;
267     }
268     my $metadata = $self->metadata;
269     push @{$metadata->{body}}, $item;
270     $self->metadata($metadata);
271     my $new_content = $self->render_metadata($format);
272     return $self->content($new_content);
273 }
274
275 =head2 Attributes Accessors
276
277 =head3 $message->message_id
278
279 =head3 $message->borrowernumber
280
281 =head3 $message->subject
282
283 =head3 $message->content
284
285 =head3 $message->metadata
286
287 =head3 $message->letter_code
288
289 =head3 $message->message_transport_type
290
291 =head3 $message->status
292
293 =head3 $message->time_queued
294
295 =head3 $message->to_address
296
297 =head3 $message->from_address
298
299 =head3 $message->content_type
300
301 =cut
302
303 # $object->$method -- treat keys as methods
304 sub AUTOLOAD {
305     my ($self, @args) = @_;
306     my $attr = $AUTOLOAD;
307     $attr =~ s/.*://;
308     if (ref($self->{$attr}) eq 'CODE') {
309         $self->{$attr}->($self, @args);
310     } else {
311         if (@args) {
312             $self->{$attr} = $args[0];
313         } else {
314             $self->{$attr};
315         }
316     }
317 }
318
319 sub DESTROY { }
320
321 1;
322
323 =head1 SEE ALSO
324
325 L<C4::Circulation>, L<C4::Letters>, L<C4::Members::Messaging>
326
327 =head1 AUTHOR
328
329 John Beppu <john.beppu@liblime.com>
330
331 =cut
332
333 # Local Variables: ***
334 # mode: cperl ***
335 # indent-tabs-mode: nil ***
336 # cperl-close-paren-offset: -4 ***
337 # cperl-continued-statement-offset: 4 ***
338 # cperl-indent-level: 4 ***
339 # cperl-indent-parens-as-block: t ***
340 # cperl-tab-always-indent: nil ***
341 # End: ***
342 # vim:tabstop=8 softtabstop=4 shiftwidth=4 shiftround expandtab