root/trunk/lib/AkActionMailer.php

Revision 1396, 31.8 kB (checked in by bermi, 1 year ago)

Making Action Mailer more PHP5

Line 
1 <?php
2 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
3
4 // +----------------------------------------------------------------------+
5 // | Akelos Framework - http://www.akelos.org                             |
6 // +----------------------------------------------------------------------+
7 // | Copyright (c) 2002-2006, Akelos Media, S.L.  & Bermi Ferrer Martinez |
8 // | Released under the GNU Lesser General Public License, see LICENSE.txt|
9 // +----------------------------------------------------------------------+
10
11 /**
12  * @package AkelosFramework
13  * @subpackage AkActionMailer
14  * @author Bermi Ferrer <bermi a.t akelos c.om>
15  * @copyright Copyright (c) 2002-2006, Akelos Media, S.L. http://www.akelos.org
16  * @license GNU Lesser General Public License <http://www.gnu.org/copyleft/lesser.html>
17  */
18
19 require_once(AK_LIB_DIR.DS.'AkBaseModel.php');
20 require_once(AK_LIB_DIR.DS.'AkActionMailer'.DS.'AkMailMessage.php');
21 require_once(AK_LIB_DIR.DS.'AkActionMailer'.DS.'AkMailParser.php');
22 require_once(AK_LIB_DIR.DS.'AkActionMailer'.DS.'AkActionMailerQuoting.php');
23 require_once(AK_LIB_DIR.DS.'AkActionMailer'.DS.'AkMailComposer.php');
24
25 ak_define('MAIL_EMBED_IMAGES_AUTOMATICALLY_ON_EMAILS', false);
26 ak_define('ACTION_MAILER_DEFAULT_CHARSET', AK_CHARSET);
27 ak_define('ACTION_MAILER_EOL', "\r\n");
28 ak_define('ACTION_MAILER_EMAIL_REGULAR_EXPRESSION', "([a-z0-9_\-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-z0-9\-]+\.)+))([a-z]{2,4}|[0-9]{1,3})(\]?)");
29 ak_define('ACTION_MAILER_RFC_2822_DATE_REGULAR_EXPRESSION', "(?:(Mon|Tue|Wed|Thu|Fri|Sat|Sun), *)?(\d\d?) (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) (\d\d\d\d) (\d{2}:\d{2}(?::\d\d)) (UT|GMT|EST|EDT|CST|CDT|MST|MDT|PST|PDT|[A-Z]|(?:\+|\-)\d{4})");
30
31 /**
32 * AkActionMailer allows you to send email from your application using a mailer model and views.
33 *
34 * = Mailer Models
35 *
36 * To use AkActionMailer, you need to create a mailer model.
37 *
38 *   $ ./script/generate mailer Notifier
39 *
40 * The generated model inherits from AkActionMailer. Emails are defined by
41 * creating methods within the model which are then used to set variables to be
42 * used in the mail template, to change options on the mail, or to add attachments.
43 *
44 * Examples:
45 *
46 *  class Notifier extends AkActionMailer
47 *  {
48 *    public function signupNotification($Recipient)
49 *    {
50 *      $this->setRecipients($Recipient->getEmailAddressWithName());
51 *      $this->setFrom("system@example.com");
52 *      $this->setSubject("New account information");
53 *      $this->setBody(array('account' => $Recipient ));
54 *    }
55 *  }
56 *
57 * Mailer methods have the following configuration methods available.
58 *
59 * * <tt>setRecipients</tt> - Takes one or more email addresses. These addresses are where your email will be delivered to. Sets the <tt>To:</tt> header.
60 * * <tt>setSubject</tt> - The subject of your email. Sets the <tt>Subject:</tt> header.
61 * * <tt>setFrom</tt> - Who the email you are sending is from. Sets the <tt>From:</tt> header.
62 * * <tt>setCc</tt> - Takes one or more email addresses. These addresses will receive a carbon copy of your email. Sets the <tt>Cc:</tt> header.
63 * * <tt>setBcc</tt> - Takes one or more email address. These addresses will receive a blind carbon copy of your email. Sets the <tt>Bcc</tt> header.
64 * * <tt>setSentOn</tt> - The date on which the message was sent. If not set, the header wil be set by the delivery agent.
65 * * <tt>setContentType</tt> - Specify the content type of the message. Defaults to <tt>text/plain</tt>.
66 * * <tt>setHeaders</tt> - Specify additional headers to be set for the message, e.g. <tt>$this->setHeaders(array('X-Mail-Count' => 107370));</tt>.
67 *
68 * The <tt>setBody</tt> method has special behavior. It takes an array which generates an instance variable
69 * named after each key in the array containing the value that that key points to.
70 *
71 * So, for example, <tt>setBody(array("account" => $Recipient));</tt> would result
72 * in an instance variable <tt>$account</tt> with the value of <tt>$Recipient</tt> being accessible in the
73 * view.
74 *
75 *
76 * = Mailer views
77 *
78 * Like AkAkActionController, each mailer class has a corresponding view directory
79 * in which each method of the class looks for a template with its name.
80 * To define a template to be used with a mailing, create an <tt>.tpl</tt> file with the same name as the method
81 * in your mailer model. For example, in the mailer defined above, the template at
82 * <tt>app/views/notifier/signup_notification.tpl</tt> would be used to generate the email.
83 *
84 * Variables defined in the model are accessible as instance variables in the view.
85 *
86 * Emails by default are sent in plain text, so a sample view for our model example might look like this:
87 *
88 *   Hi {account.name},
89 *   Thanks for joining our service! Please check back often.
90 *
91 * You can even use Action View helpers in these views. For example:
92 *
93 *   You got a new note!
94 *   <?=$text_helper->truncate($note->body, 25);?>
95 *
96 * = Mailer layouts
97 *
98 * Similar to the layouts for Controller/action views you can define layouts for your mailers.
99 * Each mailer can provide layouts based on the content-type.
100 *
101 * For example, if the following templates exist:
102 * * layout.text.plain.tpl
103 * * layout.text.html.tpl
104 *
105 * they will be used as the layout for the text/plain and text/html part of the message.
106 *
107 * As in normal views you define the layout:
108 *
109 * <html>
110 *   <body>
111 *     <h2>{Title}</h2>
112 *
113 *     <!-- content body comes here -->
114 *      {content_for_layout?}
115 *     <!-- end content -->
116 *
117 *     <p class="footer">
118 *       {footer_message?}
119 *     </p>
120 *   </body>
121 * </html>
122 *
123 *
124 * = Generating URLs for mailer views
125 *
126 * If your view includes URLs from the application, you need to use Ak::urlFor in
127 * the mailing method instead of the view.
128 * Unlike controllers from Action View, the mailer instance doesn't have any
129 * context about the incoming request. That's why you need to jump this little
130 * hoop and supply all the details needed for the URL.
131 *
132 * Example:
133 *
134 *   function signupNotification($Recipient)
135 *   {
136 *       // This is the same as calling each individual setter
137 *       $this->setAttributes(array(
138 *           'recipients' => $Recipient->getEmailAddressWithName(),
139 *           'from'       => "system@example.com",
140 *           'subject'    => "New account information",
141 *           'body'       => array(
142 *               'account'   => $Recipient,
143 *               'home_page' => Ak::urlFor(array('host' => "example.com", 'controller' => "welcome", 'action' => "greeting"))
144 *           )
145 *       ));
146 *   }
147 *
148 * You can now access @home_page in the template and get http://example.com/welcome/greeting.
149 *
150 * = Sending mail
151 *
152 * Once a mailer action and template are defined, you can deliver your message or
153 * create it and save it for delivery later:
154 *
155 *   Notifier::deliver('signup_notification', $David); // sends the email
156 *   $Message = Notifier::create('signup_notification', $David); // => A PEAR::Mail object
157 *   Notifier::deliver($Message);
158 *
159 * You never instantiate your mailer class. Rather, your delivery instance
160 * methods are automatically wrapped in class methods that are called statically
161 * The <tt>signup_notification</tt> method defined above is delivered by invoking
162 * <tt>$Notifier = new Notifier(); $Notifier->signupNotification(); $Notifier->deliver();</tt>.
163 *
164 *
165 *
166 * = HTML email
167 *
168 * To send mail as HTML, make sure your view (the <tt>.tpl</tt> file) generates HTML and
169 * set the content type to html.
170 *
171 *   class ApplicationMailer extends AkActionMailer
172 *   {
173 *       public function signupNotification($Recipient)
174 *       {
175 *           $this->setAttributes(array(
176 *               'recipients' => $Recipient->getEmailAddressWithName(),
177 *               'from'       => "system@example.com",
178 *               'subject'    => "New account information",
179 *               'body'       => array('account'   => $Recipient),
180 *               'content_type' => text/html" //    Here's where the magic happens
181 *           ));
182 *       }
183 *   }
184 *
185 *
186 * = Multipart email
187 *
188 * You can explicitly specify multipart messages:
189 *
190 *   class ApplicationMailer extends AkActionMailer
191 *   {
192 *       public function signupNotification($Recipient)
193 *       {
194 *           $this->setAttributes(array(
195 *               'recipients' => $Recipient->getEmailAddressWithName(),
196 *               'from'       => "system@example.com",
197 *               'subject'    => "New account information"
198 *           ));
199 *
200 *           $this->addPart(array(
201 *               'content_type' => "text/html",
202 *               'body' => $this->renderMessage('signup-as-html', 'account' => $recipient)));
203 *
204 *           $this->addPart("text/plain", array(
205 *               'transfer_encoding' = "base64",
206 *               'body' => $this->renderMessage('signup-as-plain', 'account' => $recipient)));
207 *       }
208 *   }
209 *
210 * Multipart messages can also be used implicitly because AkActionMailer will automatically
211 * detect and use multipart templates, where each template is named after the name of the action, followed
212 * by the content type. Each such detected template will be added as separate part to the message.
213 *
214 * For example, if the following templates existed:
215 * * signup_notification.text.plain.tpl
216 * * signup_notification.text.html.tpl
217 *
218 * Each would be rendered and added as a separate part to the message,
219 * with the corresponding content type. The same body array is passed to
220 * each template.
221 *
222 *
223 * = Attachments
224 *
225 * Attachments can be added by using the +addAttachment+ method.
226 *
227 * Example:
228 *
229 *   class ApplicationMailer extends AkActionMailer
230 *   {
231 *       // attachments
232 *       public function signupNotification($Recipient)
233 *       {
234 *           $this->setAttributes(array(
235 *               'recipients' => $Recipient->getEmailAddressWithName(),
236 *               'from'       => "system@example.com",
237 *               'subject'    => "New account information"
238 *           ));
239 *
240 *           $this->addAttachment(array(
241 *               'content_type' => 'image/jpeg',
242 *               'body' => Ak::file_get_contents("an-image.jpg")));
243 *
244 *           $this->addAttachment('application/pdf', generate_your_pdf_here());
245 *       }
246 *   }
247 *
248 *
249 * = Configuration options
250 *
251 * These options are specified on the class level, as class attriibutes
252 * <tt>$AkActionMailerInstance->templateRoot = "/my/templates";</tt>
253 *
254 * * <tt>templateRoot</tt> - template root determines the base from which template references will be made.
255 *
256 * * <tt>server_settings</tt> -  Allows detailed configuration of the server:
257 *   * <tt>address</tt> Allows you to use a remote mail server. Just change it
258 *       from its default "localhost" setting.
259 *   * <tt>port</tt> On the off chance that your mail server doesn't run on port 25, you can change it.
260 *   * <tt>domain</tt> If you need to specify a HELO domain, you can do it here.
261 *   * <tt>user_name</tt> If your mail server requires authentication, set the username in this setting.
262 *   * <tt>password</tt> If your mail server requires authentication, set the password in this setting.
263 *   * <tt>authentication</tt> If your mail server requires authentication, you need to specify the authentication type here.
264 *     Options are: plain, login, cram_md5
265 *
266 * * <tt>delivery_method</tt> - Defines a delivery method. Possible values are 'php' (default), 'smtp', and 'test'.
267 *
268 * * <tt>perform_deliveries</tt> - Determines whether AkActionMailer::deliver(*) methods are actually carried out. By default they are,
269 *   but this can be turned off to help functional testing.
270 *
271 * * <tt>deliveries</tt> - Keeps an array of all the emails sent out through the Action Mailer with delivery_method 'test'. Most useful
272 *   for unit and functional testing.
273 *
274 * * <tt>default_charset</tt> - The default charset used for the body and to encode the subject. Defaults to UTF-8. You can also
275 *   pick a different charset from inside a method with <tt>$this->charset</tt>.
276 * * <tt>default_content_type</tt> - The default content type used for the main part of the message. Defaults to "text/plain". You
277 *   can also pick a different content type from inside a method with <tt>$this->content_type</tt>.
278 * * <tt>default_mime_version</tt> - The default mime version used for the message. Defaults to "1.0". You
279 *   can also pick a different value from inside a method with <tt>$this->mime_version</tt>.
280 * * <tt>default_implicit_parts_order</tt> - When a message is built implicitly (i.e. multiple parts are assembled from templates
281 *   which specify the content type in their filenames) this variable controls how the parts are ordered. Defaults to
282 *   array("multipart/alternative", "text/html", "text/enriched", "text/plain"). Items that appear first in the array have higher priority in the mail client
283 *   and appear last in the mime encoded message. You can also pick a different order from inside a method with
284 *   <tt>$this->implicit_parts_order</tt>.
285 */
286 class AkActionMailer extends AkBaseModel
287 {
288     public $templateRoot;
289     public $server_settings = array(
290     'address'        => 'localhost',
291     'port'           => 25,
292     'domain'         => 'localhost.localdomain',
293     'user_name'      => null,
294     'password'       => null,
295     'authentication' => null
296     );
297
298     public $delivery_method = 'php';
299     public $perform_deliveries = true;
300     public $deliveries = array();
301     public $default_charset = AK_ACTION_MAILER_DEFAULT_CHARSET;
302     public $default_content_type = 'text/plain';
303     public $default_mime_version = '1.0';
304     public $default_implicit_parts_order = array('multipart/alternative', 'text/html', 'text/enriched', 'text/plain');
305     public $Message;
306     public $Composer;
307     public $_defaultMailDriverName = 'AkMailMessage';
308
309     public function __construct($Driver = null)
310     {
311         $this->loadSettings();
312         if(empty($Driver)){
313             $this->Message = new $this->_defaultMailDriverName();
314         }else{
315             $this->Message =& $Driver;
316         }
317     }
318
319     public function loadSettings()
320     {
321         if($settings = Ak::getSettings('mailer', false)){
322             foreach ($settings as $k=>$v){
323                 $this->$k = $v;
324             }
325         }
326     }
327
328     /**
329     * Specify the template name to use for current message. This is the "base"
330     * template name, without the extension or directory, and may be used to
331     * have multiple mailer methods share the same template.
332     */
333     public function setTemplate($template_name)
334     {
335         $this->template = $template_name;
336     }
337
338     /**
339     * Override the mailer name, which defaults to an inflected version of the
340     * mailer's class name. If you want to use a template in a non-standard
341     * location, you can use this to specify that location.
342     */
343     public function setMailerName($mailerName)
344     {
345         $this->mailerName = $mailerName;
346     }
347
348     // Mail object specific setters
349
350
351     /**
352     * Define the body of the message. This is either an array (in which case it
353     * specifies the variables to pass to the template when it is rendered),
354     * or a string, in which case it specifies the actual text of the message.
355     */
356     public function setBody($body)
357     {
358         if(is_array($body) && count($body) == 1 && array_key_exists(0,$body)){
359             $body = $body[0];
360         }
361         $this->Message->setBody($body);
362     }
363
364     /**
365     * Specify the CC addresses for the message.
366     */
367     public function setCc($cc)
368     {
369         $this->Message->setCc($cc);
370     }
371
372     /**
373     * Specify the BCC addresses for the message.
374     */
375     public function setBcc($bcc)
376     {
377         $this->Message->setBcc($bcc);
378     }
379     /**
380      * Specify the charset to use for the message. This defaults to the
381      *  +default_charset+ specified for AkActionMailer.
382      */
383     public function setCharset($charset)
384     {
385         $this->Message->setCharset($charset);
386     }
387
388     /**
389      * Specify the content type for the message. This defaults to <tt>text/plain</tt>
390      * in most cases, but can be automatically set in some situations.
391      */
392     public function setContentType($content_type)
393     {
394         $this->Message->setContentType($content_type);
395     }
396
397     /**
398      * Specify the from address for the message.
399      */
400     public function setFrom($from)
401     {
402         $this->Message->setFrom($from);
403     }
404
405     /**
406      * Specify additional headers to be added to the message.
407      */
408     public function setHeaders($headers)
409     {
410         $this->Message->setHeaders($headers);
411     }
412
413     /**
414     * Specify the order in which parts should be sorted, based on content-type.
415     * This defaults to the value for the +default_implicit_parts_order+.
416     */
417     public function setImplicitPartsOrder($implicit_parts_order)
418     {
419         $this->Message->setImplicitPartsOrder($implicit_parts_order);
420     }
421
422
423     /**
424      * Defaults to "1.0", but may be explicitly given if needed.
425      */
426     public function setMimeVersion($mime_version)
427     {
428         $this->Message->setMimeVersion($mime_version);
429     }
430
431     /**
432      * The recipient addresses for the message, either as a string (for a single
433      * address) or an array (for multiple addresses).
434      */
435     public function setRecipients($recipients)
436     {
437         $this->Message->setRecipients($recipients);
438     }
439
440     /**
441     * The date on which the message was sent. If not set (the default), the
442     * header will be set by the delivery agent.
443     */
444     public function setSentOn($date)
445     {
446         $this->Message->setSentOn($date);
447     }
448
449
450     /**
451      * Specify the subject of the message.
452      */
453     public function setSubject($subject)
454     {
455         $this->Message->setSubject($subject);
456     }
457
458     /**
459     * Add an attachment to the message.
460     *
461     * Example:
462     *
463     *   class ApplicationMailer extends AkActionMailer
464     *   {
465     *       // attachments
466     *       public function signupNotification($Recipient)
467     *       {
468     *           $this->setAttributes(array(
469     *               'recipients' => $Recipient->getEmailAddressWithName(),
470     *               'from'       => "system@example.com",
471     *               'subject'    => "New account information"
472     *           ));
473     *
474     *           $this->addAttachment(array(
475     *               'content_type' => 'image/jpeg',
476     *               'body' => Ak::file_get_contents("an-image.jpg")));
477     *
478     *           $this->addAttachment('application/pdf', generate_your_pdf_here());
479     *       }
480     *   }
481     *
482     *
483      */
484     public function addAttachment()
485     {
486         $args = func_get_args();
487         return call_user_func_array(array(&$this->Message, 'setAttachment'), $args);
488     }
489
490     /**
491      * Generic setter
492      *
493      * Calling $this->set(array('body'=>'Hello World', 'subject' => 'First subject'));
494      * is the same as calling $this->setBody('Hello World'); and $this->setSubject('First Subject');
495      *
496      * This simplifies creating mail objects from datasources.
497      *
498      * If the method does not exists the parameter will be added to the body.
499      */
500     public function set($attributes = array())
501     {
502         if(!empty($attributes['template'])){
503             $this->setTemplate($attributes['template']);
504             unset($attributes['template']);
505         }
506
507         $this->Message->set($attributes);
508
509         $this->_setter_has_been_called = true;
510     }
511
512
513     /**
514      * Gets a well formed mail in plain text
515      */
516     public function getEncoded()
517     {
518         $this->Message->getEncoded();
519     }
520
521     /**
522      * The mail object instance referenced by this mailer.
523      */
524     public function &getMail()
525     {
526         return $this->Message;
527     }
528
529
530     /**
531      * Receives a raw email, parses it into an email object, decodes it,
532      * instantiates a new mailer, and passes the email object to the mailer
533      * object's #receive method. If you want your mailer to be able to
534      * process incoming messages, you'll need to implement a #receive
535      * method that accepts the email object as a parameter and then call
536      * the AkActionMailer::recieve method using "parent::recieve($Message);"
537      *
538      *
539      *   class MyMailer extends AkActionMailer{
540      *     public function receive($Message){
541      *          parent::receive($Message);
542      *       ...
543      *     }
544      *   }
545      */
546     public function &receive($raw_mail)
547     {
548         $this->Message =& AkMailBase::parse($raw_mail);
549         return $this->Message;
550     }
551
552
553     /**
554      * Deliver the given mail object directly. This can be used to deliver
555      * a preconstructed mail object, like:
556      *
557      *   $email =& $MyMailer->createSomeMail($parameters);
558      *   $email->setHeader("frobnicate");
559      *   MyMailer::deliver($email);
560      */
561     public function deliverDirectly(&$Message)
562     {
563         $Message = new $this->_defaultMailDriverName ($Message);
564         $Message->send();
565     }
566
567     public function getRawMessage()
568     {
569         if(empty($this->Message->_has_been_created_by_mailer)){
570             trigger_error(Ak::t('You need to create() a message before getting it as raw text.'),E_USER_ERROR);
571             return false;
572         }
573         $Composer =& $this->getComposer();
574         return $Composer->getRawMessage();
575     }
576
577
578     /**
579      * Initialize the mailer via the given +method_name+. The body will be
580      * rendered and a new AkMailMessage object created.
581      */
582     public function &create($method_name, $parameters, $content_type = '')
583     {
584         $Composer =& $this->getComposer();
585         $args = func_get_args();
586         call_user_func_array(array(&$Composer, 'build'), $args);
587         $this->Message->_has_been_created_by_mailer = true;
588         return $this->Message;
589     }
590
591
592     /**
593     * Delivers an AkMailMessage object. By default, it delivers the cached mail
594     * object (from the AkActionMailer::create method). If no cached mail object exists, and
595     * no alternate has been given as the parameter, this will fail.
596     */
597     public function deliver($method_name, $parameters = null, $Message = null)
598     {
599         if(empty($Message) &&
600         (empty($this->Message) || (!empty($this->Message) && get_class($this->Message) != get_class($this)))){
601             $this->create($method_name, $parameters);
602         }elseif(!empty($Message)){
603             $this->Message =& $Message;
604         }
605
606         !empty($this->Message) or trigger_error(Ak::t('No mail object available for delivery!'), E_USER_ERROR);
607         if(!empty($this->perform_deliveries)){
608             $this->{"perform".ucfirst(strtolower($this->delivery_method))."Delivery"}($this->Message);
609         }
610         $this->Message->_has_been_delivered_by_mailer = true;
611         return $this->Message;
612     }
613
614     public function performSmtpDelivery(&$Message, $settings = array())
615     {
616         $default_settings = array(
617         'host'     =>  @$this->server_settings['address'],
618         'localhost'     =>  @$this->server_settings['domain'],
619         'port'     =>  @$this->server_settings['port'],
620         'username'     =>  @$this->server_settings['user_name'],
621         'password'     =>  @$this->server_settings['password'],
622         'auth'     =>  (!empty($this->server_settings['user_name']) || @$this->server_settings['authentication']),
623         //'debug'    =>  true
624         );
625         $settings = array_merge($default_settings, $settings);
626
627         return $this->_deliverUsingMailDeliveryMethod('Smtp', $Message, $settings);
628
629     }
630
631     public function performPhpDelivery(&$Message, $settings = array())
632     {
633         return $this->_deliverUsingMailDeliveryMethod('PhpMail', $Message, $settings);
634     }
635
636     public function performTestDelivery(&$Message)
637     {
638         return $this->_deliverUsingMailDeliveryMethod('Test', $Message, array('ActionMailer'=>&$this));
639
640     }
641
642     /**
643      * Renders a message.
644      *
645      * The template for the message to be rendered is located based on convention:
646      *
647      * AK_APP_VIEWS_DIR/mailer_name/method_name.tpl
648      *
649      * To render a multi/part message place templates with the content-type in the mailer_name folder:
650      *
651      * AK_APP_VIEWS_DIR/mailer_name/method_name.text.html.tpl
652      * AK_APP_VIEWS_DIR/mailer_name/method_name.text.plain.tpl
653      *
654      * @param string $method_name
655      * @param array $body
656      * @param array $options
657      * @return string the rendered message
658      */
659     public function renderMessage($method_name, $body, $options = array())
660     {
661
662         $file_name = basename($method_name);
663         $fparts=split('\.',$file_name);
664         $extension=array_pop($fparts);
665         array_shift($fparts);
666         $content_type = join('.',$fparts);
667         $this->current_content_type = join('/',$fparts);
668         $this->current_template_extension = $extension;
669
670         if($content_type && ($layout_path = $this->_getLayoutPath($method_name, $content_type, $extension))) {
671             $layout_options = array_merge($options, array('file' => $method_name, 'body' => $body));
672             $this->render(false,true,@$layout_options['body']);
673             return $this->_renderWithALayout($layout_options, $layout_path);
674         }
675         return $this->render(array_merge($options, array('file' => $method_name, 'body' => $body)));
676     }
677
678
679     public function render($options = array(),$set_body_only=false,$set_body=null)
680     {
681         static $body;
682         if($set_body_only===true) {
683             $body = $set_body;
684             return;
685         }
686         if(isset($options['body'])) {
687             $body = $options['body'];
688             unset($options['body']);
689             $Template =& $this->_initializeTemplateClass($body);
690         } else {
691             $Template =& $this->_initializeTemplateClass($body);
692         }
693
694         if(isset($options['partial']) && !empty($this->current_content_type)) {
695             /**
696              * choose the partial for this content-type
697              */
698             $partialExtension = $this->current_template_extension;
699             $partialParts=split('\.',basename($options['partial']));
700             $partialDir=dirname($options['partial']);
701             $contentTypePartialFilename=join('.',$partialParts).'.'.
702                                         str_replace('/','.',$this->current_content_type).'.'.
703                                         $partialExtension;
704
705             $testContentTypePartialPath=$this->getTemplatePath().DS.$partialDir.DS.'_'.$contentTypePartialFilename;
706
707             if(file_exists($testContentTypePartialPath)) {
708                 $options['partial'] = $options['partial'].'.'.str_replace('/','.',$this->current_content_type);
709             }
710
711         }
712         $options['locals'] = array_merge((array)@$options['locals'], $this->getHelpers());
713         $options['locals'] = array_merge($options['locals'], array('mailer'=>&$this,'controller'=>&$this));
714         if(!empty($body)) {
715             $options['locals']['body'] = $body;
716         }
717         return $Template->render($options);
718     }
719
720     public function getTemplatePath()
721     {
722         return $this->templateRoot.DS.$this->mailerName;
723     }
724
725     /**
726     * Set up the default values for the various instance variables of this
727     * mailer. Subclasses may override this method to provide different
728     * defaults.
729     */
730     public function initializeDefaults($method_name)
731     {
732         foreach (array('charset','content_type','implicit_parts_order', 'mime_version') as $attribute) {
733             $method = 'set'.AkInflector::camelize($attribute);
734             $this->Message->$method(empty($this->$attribute) ? $this->{'default_'.$attribute} : $this->$attribute);
735         }
736         foreach (array('parts','headers','body') as $attribute) {
737             $method = 'set'.AkInflector::camelize($attribute);
738             $this->Message->$method(empty($this->$attribute) ? array() : $this->$attribute);
739         }
740
741         $this->templateRoot = empty($this->templateRoot) ? AK_APP_DIR.DS.'views' : $this->templateRoot;
742         $this->template = empty($this->template) ? $method_name : $this->template;
743         $this->mailerName = empty($this->mailerName) ? AkInflector::underscore($this->getModelName()) : $this->mailerName;
744     }
745
746
747     public function &_initializeTemplateClass($assigns)
748     {
749         require_once(AK_LIB_DIR.DS.'AkActionView.php');
750         $TemplateInstance = new AkActionView($this->getTemplatePath(), $assigns, $this);
751         require_once (AK_LIB_DIR.DS.'AkActionView'.DS.'AkPhpTemplateHandler.php');
752         $TemplateInstance->_registerTemplateHandler('tpl','AkPhpTemplateHandler');
753         return $TemplateInstance;
754     }
755
756     public function &getComposer()
757     {
758         if(empty($this->Composer)){
759             $this->setComposer();
760         }
761         return $this->Composer;
762     }
763
764     public function setComposer($Composer = null)
765     {
766         if(!empty($Composer)){
767             $this->Composer = $Composer;
768         }else{
769             $this->Composer = new AkMailComposer();
770             $this->Composer->init($this);
771         }
772     }
773
774
775     /**
776      * Alias for getModelName
777      */
778     public function getMailerName()
779     {
780         return $this->getModelName();
781     }
782
783
784     /**
785      * Workarround for limited support of helpers on ActionMailer Views
786      *
787      * @todo refactor helpers to be controller agnostic
788      */
789     public function getControllerName()
790     {
791         return $this->getModelName();
792     }
793
794     /**
795      * This is the url_for version for helpers and emails.
796      *
797      * As we do not have the context of a host being requested, we need to know
798      * the base_url like http://example.com in oder to add it to the generated URL
799      */
800     public function urlFor()
801     {
802         $args = func_get_args();
803         $base_url = '';
804         if(isset($args[0]['base_url'])){
805             $base_url = rtrim(preg_replace('/^(?!http[s]?:\/\/)(.+)/','http://$1', (strstr($args[0]['base_url'],'.')?$args[0]['base_url']:Ak::getSetting('mailer', 'base_url', AK_HOST))),'/');
806             unset($args[0]['base_url']);
807         }
808
809         unset($args[0]['only_path'], $args[0]['base_url']);
810
811         return $base_url.call_user_func_array(array('Ak','toUrl'), $args);
812     }
813
814     /**
815      * Creates an instance of each available helper and links it into into current mailer.
816      *
817      * Mailer helpers work as Controller helpers but without the Request context
818      */
819     public function getHelpers()
820     {
821         require_once(AK_LIB_DIR.DS.'AkActionView'.DS.'AkHelperLoader.php');
822         $HelperLoader = new AkHelperLoader();
823         $HelperLoader->setHandler(&$this);
824         return $HelperLoader->getHelpersForMailer();
825     }
826
827     public function _deliverUsingMailDeliveryMethod($method, &$Message, $options)
828     {
829         $handler_name = 'Ak'.AkInflector::camelize(Ak::sanitize_include($method, 'paranoid')).'Delivery';
830         $handler_path = AK_LIB_DIR.DS.'AkActionMailer'.DS.'AkMailDelivery'.DS.$handler_name.'.php';
831         if(file_exists($handler_path)){
832             require_once($handler_path);
833         }
834
835         if(!class_exists($handler_name)){
836             trigger_error(Ak::t('Could not find message handler %handler_name', array('%handler_name'=>$handler_name)), E_USER_ERROR);
837             return false;
838         }
839         $DeliveryHandler = new $handler_name();
840         $this->Message =& $Message;
841         return $DeliveryHandler->deliver($this, $options);
842     }
843
844
845     public function _getLayoutPath($method_name, $content_type = null, $extension = null)
846     {
847         $dirname = dirname($method_name);
848         if(is_file($dirname.DS.'layout.'.$content_type.'.'.$extension)){
849             return $dirname.DS.'layout.'.$content_type.'.'.$extension;
850         }elseif(is_file($dirname.DS.'layout.'.$extension)){
851             return is_file($dirname.DS.'layout.'.$extension);
852         }
853         return false;
854     }
855
856     public function _renderWithALayout($options = array(), $layout_file)
857     {
858         static $body;
859         if(isset($options['body'])) {
860             $body = $options['body'];
861             unset($options['body']);
862             $Template =& $this->_initializeTemplateClass($body);
863         } else {
864             $Template =& $this->_initializeTemplateClass($body);
865         }
866         $options['locals'] = array_merge((array)@$options['locals'], $this->getHelpers());
867         $options['locals'] = array_merge($options['locals'], array('mailer'=>&$this,'controller'=>&$this));
868         if(!empty($body)) {
869             $options['locals']['body'] = $body;
870         } else {
871             $options['locals']['body'] = array();
872         }
873         $options['locals']['body']['content_for_layout']=$Template->render($options);
874
875         $layout_options=$options;
876         $layout_options['file'] = $layout_file;
877         $LayoutTemplate =& $this->_initializeTemplateClass($options['locals']['body']);
878         return $LayoutTemplate->render($layout_options);
879     }
880 }
881
882
883 ?>
Note: See TracBrowser for help on using the browser.