| File: | C4/Message.pm |
| Coverage: | 14.3% |
| line | stmt | bran | cond | sub | time | code |
|---|---|---|---|---|---|---|
| 1 | package C4::Message; | |||||
| 2 | 14 14 14 | 475 71 879 | use strict; | |||
| 3 | 14 14 14 | 133 56 622 | use warnings; | |||
| 4 | 14 14 14 | 326 62 236 | use C4::Context; | |||
| 5 | 14 14 14 | 229 71 2346 | use C4::Letters; | |||
| 6 | 14 14 14 | 165 34 1115 | use YAML::Syck; | |||
| 7 | 14 14 14 | 119 64 20687 | use Carp; | |||
| 8 | ||||||
| 9 - 48 | =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 $borrower = { borrowernumber => 1 };
my $item = C4::Items::GetItem(1);
my $letter = C4::Letters::GetPreparedLetter (
module => 'circulation',
letter_code => 'CHECKOUT',
branchcode => $branch,
tables => {
'biblio', $item->{biblionumber},
'biblioitems', $item->{biblionumber},
},
);
C4::Message->enqueue($letter, $borrower->{borrowernumber}, '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 | |||||
| 49 | ||||||
| 50 | ||||||
| 51 | our $AUTOLOAD; | |||||
| 52 | ||||||
| 53 | ||||||
| 54 - 60 | =head2 Class Methods =head3 C4::Message->new(\%attributes) This method creates an in-memory version of a message object. =cut | |||||
| 61 | ||||||
| 62 | # C4::Message->new(\%attributes) -- constructor | |||||
| 63 | sub new { | |||||
| 64 | 0 | my ($class, $opts) = @_; | ||||
| 65 | 0 | $opts ||= {}; | ||||
| 66 | 0 | bless {%$opts} => $class; | ||||
| 67 | } | |||||
| 68 | ||||||
| 69 | ||||||
| 70 - 75 | =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 | |||||
| 76 | ||||||
| 77 | # C4::Message->find($id) -- find a message by its message_id | |||||
| 78 | sub find { | |||||
| 79 | 0 | my ($class, $id) = @_; | ||||
| 80 | 0 | my $dbh = C4::Context->dbh; | ||||
| 81 | 0 | my $msgs = $dbh->selectall_arrayref( | ||||
| 82 | qq{SELECT * FROM message_queue WHERE message_id = ?}, | |||||
| 83 | { Slice => {} }, | |||||
| 84 | $id, | |||||
| 85 | ); | |||||
| 86 | 0 | if (@$msgs) { | ||||
| 87 | 0 | return $class->new($msgs->[0]); | ||||
| 88 | } else { | |||||
| 89 | 0 | return undef; | ||||
| 90 | } | |||||
| 91 | } | |||||
| 92 | ||||||
| 93 - 99 | =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 | |||||
| 100 | ||||||
| 101 | # C4::Message->find_last_message($borrower, $letter_code, $transport) | |||||
| 102 | # -- get the borrower's most recent pending checkin or checkout notification | |||||
| 103 | sub find_last_message { | |||||
| 104 | 0 | my ($class, $borrower, $letter_code, $transport) = @_; | ||||
| 105 | # $type is the message_transport_type | |||||
| 106 | 0 | $transport ||= 'email'; | ||||
| 107 | 0 | my $dbh = C4::Context->dbh; | ||||
| 108 | 0 | my $msgs = $dbh->selectall_arrayref( | ||||
| 109 | qq{ | |||||
| 110 | SELECT * | |||||
| 111 | FROM message_queue | |||||
| 112 | WHERE status = 'pending' | |||||
| 113 | AND borrowernumber = ? | |||||
| 114 | AND letter_code = ? | |||||
| 115 | AND message_transport_type = ? | |||||
| 116 | }, | |||||
| 117 | { Slice => {} }, | |||||
| 118 | $borrower->{borrowernumber}, | |||||
| 119 | $letter_code, | |||||
| 120 | $transport, | |||||
| 121 | ); | |||||
| 122 | 0 | if (@$msgs) { | ||||
| 123 | 0 | return $class->new($msgs->[0]); | ||||
| 124 | } else { | |||||
| 125 | 0 | return undef; | ||||
| 126 | } | |||||
| 127 | } | |||||
| 128 | ||||||
| 129 | ||||||
| 130 - 135 | =head3 C4::Message->enqueue($letter, $borrower, $transport) This is a front-end for C<C4::Letters::EnqueueLetter()> that adds metadata to the message. =cut | |||||
| 136 | ||||||
| 137 | # C4::Message->enqueue($letter, $borrower, $transport) | |||||
| 138 | sub enqueue { | |||||
| 139 | 0 | my ($class, $letter, $borrower, $transport) = @_; | ||||
| 140 | 0 | my $metadata = _metadata($letter); | ||||
| 141 | 0 | my $to_address = _to_address($borrower, $transport); | ||||
| 142 | 0 | $letter->{metadata} = Dump($metadata); | ||||
| 143 | #carp "enqueuing... to $to_address"; | |||||
| 144 | 0 | C4::Letters::EnqueueLetter({ | ||||
| 145 | letter => $letter, | |||||
| 146 | borrowernumber => $borrower->{borrowernumber}, | |||||
| 147 | message_transport_type => $transport, | |||||
| 148 | to_address => $to_address, | |||||
| 149 | }); | |||||
| 150 | } | |||||
| 151 | ||||||
| 152 | # based on message $transport, pick an appropriate address to send to | |||||
| 153 | sub _to_address { | |||||
| 154 | 0 | my ($borrower, $transport) = @_; | ||||
| 155 | 0 | my $address; | ||||
| 156 | 0 | if ($transport eq 'email') { | ||||
| 157 | 0 | $address = $borrower->{email} | ||||
| 158 | || $borrower->{emailpro} | |||||
| 159 | || $borrower->{B_email}; | |||||
| 160 | } elsif ($transport eq 'sms') { | |||||
| 161 | 0 | $address = $borrower->{smsalertnumber} | ||||
| 162 | || $borrower->{phone} | |||||
| 163 | || $borrower->{phonepro} | |||||
| 164 | || $borrower->{B_phone}; | |||||
| 165 | } else { | |||||
| 166 | 0 | warn "'$transport' is an unknown message transport."; | ||||
| 167 | } | |||||
| 168 | 0 | if (not defined $address) { | ||||
| 169 | 0 | warn "An appropriate $transport address " | ||||
| 170 | . "for borrower $borrower->{userid} " | |||||
| 171 | . "could not be found."; | |||||
| 172 | } | |||||
| 173 | 0 | return $address; | ||||
| 174 | } | |||||
| 175 | ||||||
| 176 | # _metadata($letter) -- return the letter split into head/body/footer | |||||
| 177 | sub _metadata { | |||||
| 178 | 0 | my ($letter) = @_; | ||||
| 179 | 0 | if ($letter->{content} =~ /----/) { | ||||
| 180 | 0 | my ($header, $body, $footer) = split(/----\r?\n?/, $letter->{content}); | ||||
| 181 | return { | |||||
| 182 | 0 | header => $header, | ||||
| 183 | body => [$body], | |||||
| 184 | footer => $footer, | |||||
| 185 | }; | |||||
| 186 | } else { | |||||
| 187 | return { | |||||
| 188 | 0 | header => '', | ||||
| 189 | body => [$letter->{content}], | |||||
| 190 | footer => '', | |||||
| 191 | }; | |||||
| 192 | } | |||||
| 193 | } | |||||
| 194 | ||||||
| 195 - 202 | =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 | |||||
| 203 | ||||||
| 204 | # $object->update -- save object to database | |||||
| 205 | sub update { | |||||
| 206 | 0 | my ($self) = @_; | ||||
| 207 | 0 | my $dbh = C4::Context->dbh; | ||||
| 208 | 0 | $dbh->do( | ||||
| 209 | qq{ | |||||
| 210 | UPDATE message_queue | |||||
| 211 | SET | |||||
| 212 | borrowernumber = ?, | |||||
| 213 | subject = ?, | |||||
| 214 | content = ?, | |||||
| 215 | metadata = ?, | |||||
| 216 | letter_code = ?, | |||||
| 217 | message_transport_type = ?, | |||||
| 218 | status = ?, | |||||
| 219 | time_queued = ?, | |||||
| 220 | to_address = ?, | |||||
| 221 | from_address = ?, | |||||
| 222 | content_type = ? | |||||
| 223 | WHERE message_id = ? | |||||
| 224 | }, | |||||
| 225 | {}, | |||||
| 226 | $self->borrowernumber, | |||||
| 227 | $self->subject, | |||||
| 228 | $self->content, | |||||
| 229 | $self->{metadata}, # we want the raw YAML here | |||||
| 230 | $self->letter_code, | |||||
| 231 | $self->message_transport_type, | |||||
| 232 | $self->status, | |||||
| 233 | $self->time_queued, | |||||
| 234 | $self->to_address, | |||||
| 235 | $self->from_address, | |||||
| 236 | $self->content_type, | |||||
| 237 | $self->message_id | |||||
| 238 | ); | |||||
| 239 | } | |||||
| 240 | ||||||
| 241 - 246 | =head3 $message->metadata(\%new_metadata) This method automatically serializes and deserializes the metadata attribute. (It is stored in YAML format.) =cut | |||||
| 247 | ||||||
| 248 | # $object->metadata -- this is a YAML serialized column that contains a | |||||
| 249 | # structured representation of $object->content | |||||
| 250 | sub metadata { | |||||
| 251 | 0 | my ($self, $data) = @_; | ||||
| 252 | 0 | if ($data) { | ||||
| 253 | 0 | $data->{header} ||= ''; | ||||
| 254 | 0 | $data->{body} ||= []; | ||||
| 255 | 0 | $data->{footer} ||= ''; | ||||
| 256 | 0 | $self->{metadata} = Dump($data); | ||||
| 257 | 0 | $self->content($self->render_metadata); | ||||
| 258 | 0 | return $data; | ||||
| 259 | } else { | |||||
| 260 | 0 | return Load($self->{metadata}); | ||||
| 261 | } | |||||
| 262 | } | |||||
| 263 | ||||||
| 264 | # turn $object->metadata into a string suitable for $object->content | |||||
| 265 | sub render_metadata { | |||||
| 266 | 0 | my ($self, $format) = @_; | ||||
| 267 | 0 0 | $format ||= sub { $_[0] || "" }; | ||||
| 268 | 0 | my $metadata = $self->metadata; | ||||
| 269 | 0 | my $body = $metadata->{body}; | ||||
| 270 | 0 0 | my $text = join('', map { $format->($_) } @$body); | ||||
| 271 | 0 | return $metadata->{header} . $text . $metadata->{footer}; | ||||
| 272 | } | |||||
| 273 | ||||||
| 274 - 284 | =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 | |||||
| 285 | ||||||
| 286 | # $object->append($letter_or_item) -- add a new item to a message's content | |||||
| 287 | sub append { | |||||
| 288 | 0 | my ($self, $letter_or_item, $format) = @_; | ||||
| 289 | 0 | my $item; | ||||
| 290 | 0 | if (ref($letter_or_item)) { | ||||
| 291 | 0 | my $letter = $letter_or_item; | ||||
| 292 | 0 | my $metadata = _metadata($letter); | ||||
| 293 | 0 | $item = $metadata->{body}->[0]; | ||||
| 294 | } else { | |||||
| 295 | 0 | $item = $letter_or_item; | ||||
| 296 | } | |||||
| 297 | 0 | if (not $self->metadata) { | ||||
| 298 | 0 | carp "Can't append to messages that don't have metadata."; | ||||
| 299 | 0 | return undef; | ||||
| 300 | } | |||||
| 301 | 0 | my $metadata = $self->metadata; | ||||
| 302 | 0 0 | push @{$metadata->{body}}, $item; | ||||
| 303 | 0 | $self->metadata($metadata); | ||||
| 304 | 0 | my $new_content = $self->render_metadata($format); | ||||
| 305 | 0 | return $self->content($new_content); | ||||
| 306 | } | |||||
| 307 | ||||||
| 308 - 312 | =head2 Attributes Accessors =head3 $message->message_id =cut | |||||
| 313 | ||||||
| 314 - 316 | =head3 $message->borrowernumber =cut | |||||
| 317 | ||||||
| 318 - 320 | =head3 $message->subject =cut | |||||
| 321 | ||||||
| 322 - 324 | =head3 $message->content =cut | |||||
| 325 | ||||||
| 326 - 328 | =head3 $message->metadata =cut | |||||
| 329 | ||||||
| 330 - 332 | =head3 $message->letter_code =cut | |||||
| 333 | ||||||
| 334 - 336 | =head3 $message->message_transport_type =cut | |||||
| 337 | ||||||
| 338 - 340 | =head3 $message->status =cut | |||||
| 341 | ||||||
| 342 - 344 | =head3 $message->time_queued =cut | |||||
| 345 | ||||||
| 346 - 348 | =head3 $message->to_address =cut | |||||
| 349 | ||||||
| 350 - 352 | =head3 $message->from_address =cut | |||||
| 353 | ||||||
| 354 - 356 | =head3 $message->content_type =cut | |||||
| 357 | ||||||
| 358 | # $object->$method -- treat keys as methods | |||||
| 359 | sub AUTOLOAD { | |||||
| 360 | 0 | my ($self, @args) = @_; | ||||
| 361 | 0 | my $attr = $AUTOLOAD; | ||||
| 362 | 0 | $attr =~ s/.*://; | ||||
| 363 | 0 | if (ref($self->{$attr}) eq 'CODE') { | ||||
| 364 | 0 | $self->{$attr}->($self, @args); | ||||
| 365 | } else { | |||||
| 366 | 0 | if (@args) { | ||||
| 367 | 0 | $self->{$attr} = $args[0]; | ||||
| 368 | } else { | |||||
| 369 | 0 | $self->{$attr}; | ||||
| 370 | } | |||||
| 371 | } | |||||
| 372 | } | |||||
| 373 | ||||||
| 374 | 0 | sub DESTROY { } | ||||
| 375 | ||||||
| 376 | 1; | |||||
| 377 | ||||||
| 378 - 386 | =head1 SEE ALSO L<C4::Circulation>, L<C4::Letters>, L<C4::Members::Messaging> =head1 AUTHOR John Beppu <john.beppu@liblime.com> =cut | |||||