Fix FSF address in directory admin/
[koha.git] / C4 / Message.pm
blob9b0eee21221334fd3acbade57948078e2954d109
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, 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.
41 =cut
44 our $AUTOLOAD;
47 =head2 Class Methods
49 =head3 C4::Message->new(\%attributes)
51 This method creates an in-memory version of a message object.
53 =cut
55 # C4::Message->new(\%attributes) -- constructor
56 sub new {
57 my ($class, $opts) = @_;
58 $opts ||= {};
59 bless {%$opts} => $class;
63 =head3 C4::Message->find($id)
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.
68 =cut
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,
79 if (@$msgs) {
80 return $class->new($msgs->[0]);
81 } else {
82 return undef;
86 =head3 C4::Message->find_last_message($borrower, $letter_code, $transport)
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.)
92 =cut
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(
103 SELECT *
104 FROM message_queue
105 WHERE status = 'pending'
106 AND borrowernumber = ?
107 AND letter_code = ?
108 AND message_transport_type = ?
110 { Slice => {} },
111 $borrower->{borrowernumber},
112 $letter_code,
113 $transport,
115 if (@$msgs) {
116 return $class->new($msgs->[0]);
117 } else {
118 return undef;
123 =head3 C4::Message->enqueue($letter, $borrower, $transport)
125 This is a front-end for C<C4::Letters::EnqueueLetter()> that adds metadata to
126 the message.
128 =cut
130 # C4::Message->enqueue($letter, $borrower, $transport)
131 sub enqueue {
132 my ($class, $letter, $borrower, $transport) = @_;
133 my $metadata = _metadata($letter);
134 my $to_address = _to_address($borrower, $transport);
135 $letter->{metadata} = Dump($metadata);
136 #carp "enqueuing... to $to_address";
137 C4::Letters::EnqueueLetter({
138 letter => $letter,
139 borrowernumber => $borrower->{borrowernumber},
140 message_transport_type => $transport,
141 to_address => $to_address,
145 # based on message $transport, pick an appropriate address to send to
146 sub _to_address {
147 my ($borrower, $transport) = @_;
148 my $address;
149 if ($transport eq 'email') {
150 $address = $borrower->{email}
151 || $borrower->{emailpro}
152 || $borrower->{B_email};
153 } elsif ($transport eq 'sms') {
154 $address = $borrower->{smsalertnumber}
155 || $borrower->{phone}
156 || $borrower->{phonepro}
157 || $borrower->{B_phone};
158 } else {
159 warn "'$transport' is an unknown message transport.";
161 if (not defined $address) {
162 warn "An appropriate $transport address "
163 . "for borrower $borrower->{userid} "
164 . "could not be found.";
166 return $address;
169 # _metadata($letter) -- return the letter split into head/body/footer
170 sub _metadata {
171 my ($letter) = @_;
172 if ($letter->{content} =~ /----/) {
173 my ($header, $body, $footer) = split(/----\r?\n?/, $letter->{content});
174 return {
175 header => $header,
176 body => [$body],
177 footer => $footer,
179 } else {
180 return {
181 header => '',
182 body => [$letter->{content}],
183 footer => '',
188 =head2 Instance Methods
190 =head3 $message->update()
192 This saves the $message object back to the database. It needs to have
193 already been created via C<enqueue> for this to work.
195 =cut
197 # $object->update -- save object to database
198 sub update {
199 my ($self) = @_;
200 my $dbh = C4::Context->dbh;
201 $dbh->do(
203 UPDATE message_queue
205 borrowernumber = ?,
206 subject = ?,
207 content = ?,
208 metadata = ?,
209 letter_code = ?,
210 message_transport_type = ?,
211 status = ?,
212 time_queued = ?,
213 to_address = ?,
214 from_address = ?,
215 content_type = ?
216 WHERE message_id = ?
219 $self->borrowernumber,
220 $self->subject,
221 $self->content,
222 $self->{metadata}, # we want the raw YAML here
223 $self->letter_code,
224 $self->message_transport_type,
225 $self->status,
226 $self->time_queued,
227 $self->to_address,
228 $self->from_address,
229 $self->content_type,
230 $self->message_id
234 =head3 $message->metadata(\%new_metadata)
236 This method automatically serializes and deserializes the metadata
237 attribute. (It is stored in YAML format.)
239 =cut
241 # $object->metadata -- this is a YAML serialized column that contains a
242 # structured representation of $object->content
243 sub metadata {
244 my ($self, $data) = @_;
245 if ($data) {
246 $data->{header} ||= '';
247 $data->{body} ||= [];
248 $data->{footer} ||= '';
249 $self->{metadata} = Dump($data);
250 $self->content($self->render_metadata);
251 return $data;
252 } else {
253 return Load($self->{metadata});
257 # turn $object->metadata into a string suitable for $object->content
258 sub render_metadata {
259 my ($self, $format) = @_;
260 $format ||= sub { $_[0] || "" };
261 my $metadata = $self->metadata;
262 my $body = $metadata->{body};
263 my $text = join('', map { $format->($_) } @$body);
264 return $metadata->{header} . $text . $metadata->{footer};
267 =head3 $message->append(\%letter)
269 If passed a hashref, this method will assume that the hashref is in the form
270 that C<C4::Letters::getletter()> returns. It will append the body of the
271 letter to the message.
273 =head3 $message->append($string)
275 If passed a string, it'll append the string to the message.
277 =cut
279 # $object->append($letter_or_item) -- add a new item to a message's content
280 sub append {
281 my ($self, $letter_or_item, $format) = @_;
282 my $item;
283 if (ref($letter_or_item)) {
284 my $letter = $letter_or_item;
285 my $metadata = _metadata($letter);
286 $item = $metadata->{body}->[0];
287 } else {
288 $item = $letter_or_item;
290 if (not $self->metadata) {
291 carp "Can't append to messages that don't have metadata.";
292 return undef;
294 my $metadata = $self->metadata;
295 push @{$metadata->{body}}, $item;
296 $self->metadata($metadata);
297 my $new_content = $self->render_metadata($format);
298 return $self->content($new_content);
301 =head2 Attributes Accessors
303 =head3 $message->message_id
305 =head3 $message->borrowernumber
307 =head3 $message->subject
309 =head3 $message->content
311 =head3 $message->metadata
313 =head3 $message->letter_code
315 =head3 $message->message_transport_type
317 =head3 $message->status
319 =head3 $message->time_queued
321 =head3 $message->to_address
323 =head3 $message->from_address
325 =head3 $message->content_type
327 =cut
329 # $object->$method -- treat keys as methods
330 sub AUTOLOAD {
331 my ($self, @args) = @_;
332 my $attr = $AUTOLOAD;
333 $attr =~ s/.*://;
334 if (ref($self->{$attr}) eq 'CODE') {
335 $self->{$attr}->($self, @args);
336 } else {
337 if (@args) {
338 $self->{$attr} = $args[0];
339 } else {
340 $self->{$attr};
345 sub DESTROY { }
349 =head1 SEE ALSO
351 L<C4::Circulation>, L<C4::Letters>, L<C4::Members::Messaging>
353 =head1 AUTHOR
355 John Beppu <john.beppu@liblime.com>
357 =cut