1. Summary
  2. Files
  3. Support
  4. Report Spam
  5. Create account
  6. Log in

root/trunk/library/classes/Gems/Email/TemplateMailer.php @ 575

Revision 575, 16.2 KB (checked in by matijsdejong, 14 months ago)

New methods for mailings
UserLoader? should no longer fold during transition to 1.5.3
New MUtil_Ra::braceKeys
A lot of docblocks

  • Property svn:keywords set to Id Rev Revision Date Author
Line 
1<?php
2
3/**
4 * Copyright (c) 2011, Erasmus MC
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
9 *    * Redistributions of source code must retain the above copyright
10 *      notice, this list of conditions and the following disclaimer.
11 *    * Redistributions in binary form must reproduce the above copyright
12 *      notice, this list of conditions and the following disclaimer in the
13 *      documentation and/or other materials provided with the distribution.
14 *    * Neither the name of Erasmus MC nor the
15 *      names of its contributors may be used to endorse or promote products
16 *      derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY
22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 *
29 * @version    $Id$
30 * @package    Gems
31 * @subpackage Email
32 * @copyright  Copyright (c) 2011 Erasmus MC
33 * @license    New BSD License
34 */
35
36/**
37 * Mailer utility class
38 *
39 * @author     Michiel Rook <michiel@touchdownconsulting.nl>
40 * @package    Gems
41 * @subpackage Email
42 * @copyright  Copyright (c) 2011 Erasmus MC
43 * @license    New BSD License
44 */
45class Gems_Email_TemplateMailer
46{
47    const MAIL_NO_ENCRYPT = 0;
48    const MAIL_SSL = 1;
49    const MAIL_TLS = 2;
50
51    /**
52     *
53     * @var Zend_Mail_Transport
54     */
55    protected $defaultTransport = null;
56
57    /**
58     * @var GemsEscort $escort
59     */
60    protected $escort;
61
62    protected $messages = array();
63
64    private $_changeDate;
65    private $_mailSubject;
66    private $_mailKeys;
67    private $_mailFields;
68    private $_mailDate;
69
70    private $_from = 'O';
71    private $_tokens = array();
72    private $_subject = null;
73    private $_body = null;
74    private $_method = 'M';
75    private $_templateId = null; // Not used for lookup
76    private $_tokenData = array();
77
78    private $_verbose = false;
79
80    /**
81     * Constructs a new Gems_Email_TemplateMailer
82     * @param GemsEscort $escort
83     */
84    public function __construct(GemsEscort $escort)
85    {
86        $this->escort = $escort;
87        $this->_mailDate = MUtil_Date::format(new Zend_Date(), 'yyyy-MM-dd');
88    }
89
90    protected function addMessage($message)
91    {
92        $this->messages[] = $message;
93        return $this;
94    }
95
96    /**
97     * Replaces fields with their values
98     * @param  string $value
99     * @return string
100     */
101    public function applyFields($value)
102    {
103        if (! $this->_mailFields) {
104            $this->getTokenMailFields();
105        }
106        if (! $this->_mailKeys) {
107            $this->_mailKeys = array_keys($this->_mailFields);
108        }
109
110        return str_replace($this->_mailKeys, $this->_mailFields, $value);
111    }
112
113    /**
114     * Returns true if the "email.bounce" setting exists in the project
115     * configuration and is true
116     * @return boolean
117     */
118    public function bounceCheck()
119    {
120        return $this->escort->project->getEmailBounce();
121    }
122
123    /**
124     * Returns Zend_Mail_Transport_Abstract when something else than the default mail protocol should be used.
125     *
126     * @staticvar array $mailServers
127     * @param email address $from
128     * @return Zend_Mail_Transport_Abstract or null
129     */
130    public function checkTransport($from)
131    {
132        static $mailServers = array();
133
134        if (! array_key_exists($from, $mailServers)) {
135            $sql = 'SELECT * FROM gems__mail_servers WHERE ? LIKE gms_from ORDER BY LENGTH(gms_from) DESC LIMIT 1';
136
137            // Always set cache, se we know when not to check for this row.
138            $serverData = $this->escort->db->fetchRow($sql, $from);
139
140            // MUtil_Echo::track($serverData);
141
142            if (isset($serverData['gms_server'])) {
143                $options = array();
144                if (isset($serverData['gms_user'], $serverData['gms_password'])) {
145                    $options['auth'] = 'login';
146                    $options['username'] = $serverData['gms_user'];
147                    $options['password'] = $serverData['gms_password'];
148                }
149                if (isset($serverData['gms_port'])) {
150                    $options['port'] = $serverData['gms_port'];
151                }
152                if (isset($serverData['gms_ssl'])) {
153                    switch ($serverData['gms_ssl']) {
154                        case self::MAIL_SSL:
155                            $options['ssl'] = 'ssl';
156                            break;
157
158                        case self::MAIL_TLS:
159                            $options['ssl'] = 'tls';
160                            break;
161
162                        default:
163                            // intentional fall through
164
165                    }
166                }
167
168                $mailServers[$from] = new Zend_Mail_Transport_Smtp($serverData['gms_server'], $options);
169            } else {
170                $mailServers[$from] = $this->defaultTransport;
171            }
172        }
173
174        return $mailServers[$from];
175    }
176
177    public function getMessages()
178    {
179        return $this->messages;
180    }
181
182    public function getTokenMailFields()
183    {
184        if (! $this->_mailFields) {
185            $this->_mailFields = $this->escort->tokenMailFields($this->_tokenData);
186        }
187
188        return $this->_mailFields;
189    }
190
191    /**
192     * Returns the name of the user mentioned in this token
193     * in human-readable format
194     *
195     * @param  array $tokenData
196     * @return string
197     */
198    public function getTokenName(array $tokenData = null)
199    {
200        $data[] = $tokenData['grs_first_name'];
201        $data[] = $tokenData['grs_surname_prefix'];
202        $data[] = $tokenData['grs_last_name'];
203
204        $data = array_filter(array_map('trim', $data)); // Remove empties
205
206        return implode(' ', $data);
207    }
208
209    /**
210     * Processes an array of token data and sends e-mails
211     * @param  array $tokensData
212     * @return boolean
213     */
214    public function process($tokensData)
215    {
216        if (isset($this->escort->project->email['block']) && $this->escort->project->email['block']) {
217            $this->addMessage($this->escort->_('The sending of emails was blocked for this installation.'));
218            return false;
219        }
220
221        switch ($this->_method) {
222            case 'M':
223                $mailAll = true;
224                // $updateOne = false;
225                break;
226
227            case 'A':
228                $mailAll   = false;
229                $updateAll = false;
230                break;
231
232            default:
233                $mailAll   = false;
234                $updateAll = true;
235                break;
236        }
237
238        $send = array();
239        $scount = 0;
240        $ucount = 0;
241
242        foreach ($tokensData as $tokenData) {
243            // Should this token be mailed?
244            if (in_array($tokenData['gto_id_token'], $this->_tokens)) {
245
246                // Should all tokens be mailed or is this the first?
247                if ($mailAll || (! isset($send[$tokenData['grs_email']]))) {
248
249                    if ($message = $this->processMail($tokenData)) {
250                        $this->addMessage($this->escort->_('Mail failed to send.'));
251                        $this->addMessage($message);
252                        return false;
253                    }
254                    $send[$tokenData['grs_email']] = true;
255                    $scount++;
256                    $ucount++;
257
258                } elseif ($updateAll) {
259                    $this->updateToken($tokenData);
260                    $ucount++;
261                }
262            }
263        }
264
265        if ($scount) {
266            $this->addMessage(sprintf($this->escort->_('Sent %d e-mails, updated %d tokens.'), $scount, $ucount));
267        }
268
269        return true;
270    }
271
272    /**
273     * Sends a single mail
274     * @param  array $tokenData
275     * @return boolean|string
276     */
277    public function processMail(array $tokenData)
278    {
279        $to_name = $this->getTokenName($tokenData);
280        $to      = $tokenData['grs_email'];
281
282        switch ($this->_from) {
283            case 'O':
284                $from = $tokenData['gor_contact_email'];
285                $from_name = $tokenData['gor_contact_name'] ? $tokenData['gor_contact_name'] : $tokenData['gor_name'];
286                break;
287
288            case 'S':
289                $from = $this->escort->project->email['site'];
290                $from_name = $this->escort->session->user_name;
291                break;
292
293            case 'U':
294                $from = $this->escort->session->user_email;
295                $from_name = $this->escort->project->name;
296                break;
297
298            default:
299                $from = $this->_from;
300                $from_name = null;
301        }
302
303        // BOUNCE CHECK
304        if ($this->bounceCheck()) {
305            $to_name = str_replace('@', ' at ', $to);
306            $to      = $from;
307        }
308
309        $style = isset($tokenData['gor_style']) ? $tokenData['gor_style'] : null;
310
311        if ($message = $this->sendMail($to, $to_name, $from, $from_name, $tokenData)) {
312            return $message;
313        } else {
314            $this->updateToken($tokenData, $to, $from);
315            return false;
316        }
317    }
318
319    /**
320     * Sends a single e-mail
321     * @param  string $to
322     * @param  string $to_name
323     * @param  string $from
324     * @param  string $from_name
325     * @param  array $tokenData
326     * @return boolean|string String = error message from protocol.
327     */
328    public function sendMail($to, $to_name, $from, $from_name, array $tokenData)
329    {
330        if (empty($from) || empty($to)) {
331            return "Need a sender and a recipient to continue";
332        }
333
334        if ($this->_verbose) {
335            MUtil_Echo::r($to, $to_name);
336            MUtil_Echo::r($from, $from_name);
337        }
338
339        $this->setTokenData($tokenData);
340
341        $mail = new Gems_Mail();
342        $mail->setTemplateStyle($tokenData['gor_style']);
343
344        $mail->setFrom($from, $from_name);
345        $mail->addTo($to, $to_name);
346        if (isset($this->escort->project->email['bcc'])) {
347            $mail->addBcc($this->escort->project->email['bcc']);
348        }
349
350        $subject = $this->applyFields($this->_subject);
351        $body    = $this->applyFields($this->_body);
352
353        $mail->setSubject($subject);
354        $mail->setBodyBBCode($body);
355
356        $this->_mailSubject = $subject;
357
358        try {
359            $mail->send($this->checkTransport($from));
360            $result = false;
361
362        } catch (Exception $e) {
363            $result = $e->getMessage();
364
365            // Log to error file
366            $this->escort->logger->logError($e, $this->escort->request);
367        }
368
369        return $result;
370    }
371
372    /**
373     * Sets the body of the mail
374     * @param string $body
375     * @return Gems_Email_TemplateMailer (continuation pattern)
376     */
377    public function setBody($body)
378    {
379        $this->_body = $body;
380        return $this;
381    }
382
383    /**
384     * Set a different default transport protocol.
385     *
386     * @param Zend_Mail_Transport_Abstract $transport
387     * @return Gems_Email_TemplateMailer (continuation pattern)
388     */
389    public function setDefaultTransport(Zend_Mail_Transport_Abstract $transport)
390    {
391        $this->defaultTransport = $transport;
392        return $this;
393    }
394
395    /**
396     * Sets sender (regular e-mail address) or one of:
397     *    'O' - Uses the contact information of the selected organization
398     *    'S' - Uses the site-wide contact information
399     *    'U' - Uses the contact information of the currently logged in user
400     *
401     * @param string $from
402     * @return Gems_Email_TemplateMailer (continuation pattern)
403     */
404    public function setFrom($from)
405    {
406        $this->_from = $from;
407        return $this;
408    }
409
410    /**
411     * Sets the sending method to use
412     *    'M' - Send multiple mails per respondent, one for each checked token.
413     *    'O' - Send one mail per respondent, mark all checked tokens as send.
414     *    'A' - Send one mail per respondent, mark only mailed tokens as send.
415     *
416     * @param string $method
417     * @return Gems_Email_TemplateMailer (continuation pattern)
418     */
419    public function setMethod($method)
420    {
421        $this->_method = $method;
422        return $this;
423    }
424
425    /**
426     * Sets the subject of the mail
427     * @param string $subject
428     * @return Gems_Email_TemplateMailer (continuation pattern)
429     */
430    public function setSubject($subject)
431    {
432        $this->_subject = $subject;
433    }
434
435    public function setTemplateId($templatedId)
436    {
437        $this->_templateId = $templatedId;
438        return $this;
439    }
440
441    public function setTokenData(array $tokenData)
442    {
443        $this->_tokenData  = $tokenData;
444        $this->_mailFields = null;
445        return $this;
446    }
447
448    public function setTokenMailFields(array $tokenData = null)
449    {
450        if (null === $tokenData) {
451            $tokenData = $this->getTokenData();
452        } else {
453            $this->setTokenData($tokenData);
454        }
455        if (! $this->_mailFields) {
456            $this->_mailFields = $this->escort->tokenMailFields($tokenData);
457        }
458
459        return $this->_mailFields;
460    }
461
462    /**
463     * Sets the list of tokens that will be mailed.
464     *
465     * @param string[] $tokens
466     * @return Gems_Email_TemplateMailer (continuation pattern)
467     */
468    public function setTokens(array $tokens)
469    {
470        $this->_tokens = $tokens;
471        return $this;
472    }
473
474    /**
475     * Sets verbose (noisy) operation
476     *
477     * @param boolean $verbose
478     * @return Gems_Email_TemplateMailer (continuation pattern)
479     */
480    public function setVerbose($verbose)
481    {
482        $this->_verbose = $verbose;
483        return $this;
484    }
485
486    /**
487     * Updates a token and log's the communication
488     *
489     * @param array $tokenData
490     * @param string $to Optional, if available the communication is logged.
491     * @param string $from Optional
492     */
493    protected function updateToken(array $tokenData, $to = null, $from = null)
494    {
495        if (null === $this->_changeDate) {
496            $this->_changeDate = new Zend_Db_Expr('CURRENT_TIMESTAMP');
497        }
498
499        $db  = $this->escort->db;
500        $uid = $this->escort->getCurrentUserId();
501
502        $tdata['gto_mail_sent_date'] = $this->_mailDate;
503
504        $db->update('gems__tokens', $tdata, $db->quoteInto('gto_id_token = ?', $tokenData['gto_id_token']));
505
506        if ($to) {
507            $cdata['grco_id_to']        = $tokenData['grs_id_user'];
508            $cdata['grco_id_by']        = $uid;
509            $cdata['grco_organization'] = $tokenData['gor_id_organization'];
510            $cdata['grco_id_token']     = $tokenData['gto_id_token'];
511
512            $cdata['grco_method']       = 'email';
513            $cdata['grco_topic']        = substr($this->_mailSubject, 0, 120);
514            $cdata['grco_address']      = substr($to, 0, 120);
515            $cdata['grco_sender']       = substr($from, 0, 120);
516
517            $cdata['grco_id_message']   = $this->_templateId ? $this->_templateId : null;
518
519            $cdata['grco_changed']      = $this->_changeDate;
520            $cdata['grco_changed_by']   = $uid;
521            $cdata['grco_created']      = $this->_changeDate;
522            $cdata['grco_created_by']   = $uid;
523
524            $db->insert('gems__log_respondent_communications', $cdata);
525        }
526    }
527}
Note: See TracBrowser for help on using the browser.