| File: | C4/Message.pm |
| Coverage: | 14.3% |
| line | stmt | bran | cond | sub | time | code |
|---|---|---|---|---|---|---|
| 1 | package C4::Message; | |||||
| 2 | 24 24 24 | 41768 156 824 | use strict; | |||
| 3 | 24 24 24 | 221 177 969 | use warnings; | |||
| 4 | 24 24 24 | 587 164 335 | use C4::Context; | |||
| 5 | 24 24 24 | 579 145 4022 | use C4::Letters; | |||
| 6 | 24 24 24 | 246 112 2049 | use YAML::Syck; | |||
| 7 | 24 24 24 | 182 146 36155 | use Carp; | |||
| 8 | ||||||
| 9 - 42 | =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::getletter('circulation', 'CHECKOUT');
C4::Letters::parseletter($letter, 'biblio', $item->{biblionumber});
C4::Letters::parseletter($letter, '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 | |||||
| 43 | ||||||
| 44 | ||||||
| 45 | our $AUTOLOAD; | |||||
| 46 | ||||||
| 47 | ||||||
| 48 - 54 | =head2 Class Methods =head3 C4::Message->new(\%attributes) This method creates an in-memory version of a message object. =cut | |||||
| 55 | ||||||
| 56 | # C4::Message->new(\%attributes) -- constructor | |||||
| 57 | sub new { | |||||
| 58 | 0 | my ($class, $opts) = @_; | ||||
| 59 | 0 | $opts ||= {}; | ||||
| 60 | 0 | bless {%$opts} => $class; | ||||
| 61 | } | |||||
| 62 | ||||||
| 63 | ||||||
| 64 - 69 | =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 | |||||
| 70 | ||||||
| 71 | # C4::Message->find($id) -- find a message by its message_id | |||||
| 72 | sub find { | |||||
| 73 | 0 | my ($class, $id) = @_; | ||||
| 74 | 0 | my $dbh = C4::Context->dbh; | ||||
| 75 | 0 | my $msgs = $dbh->selectall_arrayref( | ||||
| 76 | qq{SELECT * FROM message_queue WHERE message_id = ?}, | |||||
| 77 | { Slice => {} }, | |||||
| 78 | $id, | |||||
| 79 | ); | |||||
| 80 | 0 | if (@$msgs) { | ||||
| 81 | 0 | return $class->new($msgs->[0]); | ||||
| 82 | } else { | |||||
| 83 | 0 | return undef; | ||||
| 84 | } | |||||
| 85 | } | |||||
| 86 | ||||||
| 87 - 93 | =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 | |||||
| 94 | ||||||
| 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 | 0 | my ($class, $borrower, $letter_code, $transport) = @_; | ||||
| 99 | # $type is the message_transport_type | |||||
| 100 | 0 | $transport ||= 'email'; | ||||
| 101 | 0 | my $dbh = C4::Context->dbh; | ||||
| 102 | 0 | my $msgs = $dbh->selectall_arrayref( | ||||
| 103 | qq{ | |||||
| 104 | SELECT * | |||||
| 105 | FROM message_queue | |||||
| 106 | WHERE status = 'pending' | |||||
| 107 | AND borrowernumber = ? | |||||
| 108 | AND letter_code = ? | |||||
| 109 | AND message_transport_type = ? | |||||
| 110 | }, | |||||
| 111 | { Slice => {} }, | |||||
| 112 | $borrower->{borrowernumber}, | |||||
| 113 | $letter_code, | |||||
| 114 | $transport, | |||||
| 115 | ); | |||||
| 116 | 0 | if (@$msgs) { | ||||
| 117 | 0 | return $class->new($msgs->[0]); | ||||
| 118 | } else { | |||||
| 119 | 0 | return undef; | ||||
| 120 | } | |||||
| 121 | } | |||||
| 122 | ||||||
| 123 | ||||||
| 124 - 129 | =head3 C4::Message->enqueue($letter, $borrower, $transport) This is a front-end for C<C4::Letters::EnqueueLetter()> that adds metadata to the message. =cut | |||||
| 130 | ||||||
| 131 | # C4::Message->enqueue($letter, $borrower, $transport) | |||||
| 132 | sub enqueue { | |||||
| 133 | 0 | my ($class, $letter, $borrower, $transport) = @_; | ||||
| 134 | 0 | my $metadata = _metadata($letter); | ||||
| 135 | 0 | my $to_address = _to_address($borrower, $transport); | ||||
| 136 | 0 | $letter->{metadata} = Dump($metadata); | ||||
| 137 | #carp "enqueuing... to $to_address"; | |||||
| 138 | 0 | C4::Letters::EnqueueLetter({ | ||||
| 139 | letter => $letter, | |||||
| 140 | borrowernumber => $borrower->{borrowernumber}, | |||||
| 141 | message_transport_type => $transport, | |||||
| 142 | to_address => $to_address, | |||||
| 143 | }); | |||||
| 144 | } | |||||
| 145 | ||||||
| 146 | # based on message $transport, pick an appropriate address to send to | |||||
| 147 | sub _to_address { | |||||
| 148 | 0 | my ($borrower, $transport) = @_; | ||||
| 149 | 0 | my $address; | ||||
| 150 | 0 | if ($transport eq 'email') { | ||||
| 151 | 0 | $address = $borrower->{email} | ||||
| 152 | || $borrower->{emailpro} | |||||
| 153 | || $borrower->{B_email}; | |||||
| 154 | } elsif ($transport eq 'sms') { | |||||
| 155 | 0 | $address = $borrower->{smsalertnumber} | ||||
| 156 | || $borrower->{phone} | |||||
| 157 | || $borrower->{phonepro} | |||||
| 158 | || $borrower->{B_phone}; | |||||
| 159 | } else { | |||||
| 160 | 0 | warn "'$transport' is an unknown message transport."; | ||||
| 161 | } | |||||
| 162 | 0 | if (not defined $address) { | ||||
| 163 | 0 | warn "An appropriate $transport address " | ||||
| 164 | . "for borrower $borrower->{userid} " | |||||
| 165 | . "could not be found."; | |||||
| 166 | } | |||||
| 167 | 0 | return $address; | ||||
| 168 | } | |||||
| 169 | ||||||
| 170 | # _metadata($letter) -- return the letter split into head/body/footer | |||||
| 171 | sub _metadata { | |||||
| 172 | 0 | my ($letter) = @_; | ||||
| 173 | 0 | if ($letter->{content} =~ /----/) { | ||||
| 174 | 0 | my ($header, $body, $footer) = split(/----\r?\n?/, $letter->{content}); | ||||
| 175 | return { | |||||
| 176 | 0 | header => $header, | ||||
| 177 | body => [$body], | |||||
| 178 | footer => $footer, | |||||
| 179 | }; | |||||
| 180 | } else { | |||||
| 181 | return { | |||||
| 182 | 0 | header => '', | ||||
| 183 | body => [$letter->{content}], | |||||
| 184 | footer => '', | |||||
| 185 | }; | |||||
| 186 | } | |||||
| 187 | } | |||||
| 188 | ||||||
| 189 - 196 | =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 | |||||
| 197 | ||||||
| 198 | # $object->update -- save object to database | |||||
| 199 | sub update { | |||||
| 200 | 0 | my ($self) = @_; | ||||
| 201 | 0 | my $dbh = C4::Context->dbh; | ||||
| 202 | 0 | $dbh->do( | ||||
| 203 | qq{ | |||||
| 204 | UPDATE message_queue | |||||
| 205 | SET | |||||
| 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 = ? | |||||
| 218 | }, | |||||
| 219 | {}, | |||||
| 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 | |||||
| 232 | ); | |||||
| 233 | } | |||||
| 234 | ||||||
| 235 - 240 | =head3 $message->metadata(\%new_metadata) This method automatically serializes and deserializes the metadata attribute. (It is stored in YAML format.) =cut | |||||
| 241 | ||||||
| 242 | # $object->metadata -- this is a YAML serialized column that contains a | |||||
| 243 | # structured representation of $object->content | |||||
| 244 | sub metadata { | |||||
| 245 | 0 | my ($self, $data) = @_; | ||||
| 246 | 0 | if ($data) { | ||||
| 247 | 0 | $data->{header} ||= ''; | ||||
| 248 | 0 | $data->{body} ||= []; | ||||
| 249 | 0 | $data->{footer} ||= ''; | ||||
| 250 | 0 | $self->{metadata} = Dump($data); | ||||
| 251 | 0 | $self->content($self->render_metadata); | ||||
| 252 | 0 | return $data; | ||||
| 253 | } else { | |||||
| 254 | 0 | return Load($self->{metadata}); | ||||
| 255 | } | |||||
| 256 | } | |||||
| 257 | ||||||
| 258 | # turn $object->metadata into a string suitable for $object->content | |||||
| 259 | sub render_metadata { | |||||
| 260 | 0 | my ($self, $format) = @_; | ||||
| 261 | 0 0 | $format ||= sub { $_[0] || "" }; | ||||
| 262 | 0 | my $metadata = $self->metadata; | ||||
| 263 | 0 | my $body = $metadata->{body}; | ||||
| 264 | 0 0 | my $text = join('', map { $format->($_) } @$body); | ||||
| 265 | 0 | return $metadata->{header} . $text . $metadata->{footer}; | ||||
| 266 | } | |||||
| 267 | ||||||
| 268 - 278 | =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 | |||||
| 279 | ||||||
| 280 | # $object->append($letter_or_item) -- add a new item to a message's content | |||||
| 281 | sub append { | |||||
| 282 | 0 | my ($self, $letter_or_item, $format) = @_; | ||||
| 283 | 0 | my $item; | ||||
| 284 | 0 | if (ref($letter_or_item)) { | ||||
| 285 | 0 | my $letter = $letter_or_item; | ||||
| 286 | 0 | my $metadata = _metadata($letter); | ||||
| 287 | 0 | $item = $metadata->{body}->[0]; | ||||
| 288 | } else { | |||||
| 289 | 0 | $item = $letter_or_item; | ||||
| 290 | } | |||||
| 291 | 0 | if (not $self->metadata) { | ||||
| 292 | 0 | carp "Can't append to messages that don't have metadata."; | ||||
| 293 | 0 | return undef; | ||||
| 294 | } | |||||
| 295 | 0 | my $metadata = $self->metadata; | ||||
| 296 | 0 0 | push @{$metadata->{body}}, $item; | ||||
| 297 | 0 | $self->metadata($metadata); | ||||
| 298 | 0 | my $new_content = $self->render_metadata($format); | ||||
| 299 | 0 | return $self->content($new_content); | ||||
| 300 | } | |||||
| 301 | ||||||
| 302 - 306 | =head2 Attributes Accessors =head3 $message->message_id =cut | |||||
| 307 | ||||||
| 308 - 310 | =head3 $message->borrowernumber =cut | |||||
| 311 | ||||||
| 312 - 314 | =head3 $message->subject =cut | |||||
| 315 | ||||||
| 316 - 318 | =head3 $message->content =cut | |||||
| 319 | ||||||
| 320 - 322 | =head3 $message->metadata =cut | |||||
| 323 | ||||||
| 324 - 326 | =head3 $message->letter_code =cut | |||||
| 327 | ||||||
| 328 - 330 | =head3 $message->message_transport_type =cut | |||||
| 331 | ||||||
| 332 - 334 | =head3 $message->status =cut | |||||
| 335 | ||||||
| 336 - 338 | =head3 $message->time_queued =cut | |||||
| 339 | ||||||
| 340 - 342 | =head3 $message->to_address =cut | |||||
| 343 | ||||||
| 344 - 346 | =head3 $message->from_address =cut | |||||
| 347 | ||||||
| 348 - 350 | =head3 $message->content_type =cut | |||||
| 351 | ||||||
| 352 | # $object->$method -- treat keys as methods | |||||
| 353 | sub AUTOLOAD { | |||||
| 354 | 0 | my ($self, @args) = @_; | ||||
| 355 | 0 | my $attr = $AUTOLOAD; | ||||
| 356 | 0 | $attr =~ s/.*://; | ||||
| 357 | 0 | if (ref($self->{$attr}) eq 'CODE') { | ||||
| 358 | 0 | $self->{$attr}->($self, @args); | ||||
| 359 | } else { | |||||
| 360 | 0 | if (@args) { | ||||
| 361 | 0 | $self->{$attr} = $args[0]; | ||||
| 362 | } else { | |||||
| 363 | 0 | $self->{$attr}; | ||||
| 364 | } | |||||
| 365 | } | |||||
| 366 | } | |||||
| 367 | ||||||
| 368 | 0 | sub DESTROY { } | ||||
| 369 | ||||||
| 370 | 1; | |||||
| 371 | ||||||
| 372 - 380 | =head1 SEE ALSO L<C4::Circulation>, L<C4::Letters>, L<C4::Members::Messaging> =head1 AUTHOR John Beppu <john.beppu@liblime.com> =cut | |||||