CakePHP
  • Documentation
    • Book
    • API
    • Videos
    • Reporting Security Issues
    • Privacy Policy
    • Logos & Trademarks
  • Business Solutions
  • Swag
  • Road Trip
  • Team
  • Community
    • Community
    • Get Involved
    • Issues (GitHub)
    • Bakery
    • Featured Resources
    • Training
    • Meetups
    • My CakePHP
    • CakeFest
    • Newsletter
    • Linkedin
    • YouTube
    • Facebook
    • Twitter
    • Mastodon
    • Help & Support
    • Forum
    • Stack Overflow
    • Slack
    • Paid Support
CakePHP

C CakePHP 2.3 API

  • Overview
  • Tree
  • Deprecated
  • Version:
    • 2.3
      • 4.2
      • 4.1
      • 4.0
      • 3.9
      • 3.8
      • 3.7
      • 3.6
      • 3.5
      • 3.4
      • 3.3
      • 3.2
      • 3.1
      • 3.0
      • 2.10
      • 2.9
      • 2.8
      • 2.7
      • 2.6
      • 2.5
      • 2.4
      • 2.3
      • 2.2
      • 2.1
      • 2.0
      • 1.3
      • 1.2

Packages

  • Cake
    • Cache
      • Engine
    • Configure
    • Console
      • Command
        • Task
    • Controller
      • Component
        • Acl
        • Auth
    • Core
    • Error
    • Event
    • I18n
    • Log
      • Engine
    • Model
      • Behavior
      • Datasource
        • Database
        • Session
      • Validator
    • Network
      • Email
      • Http
    • Routing
      • Filter
      • Route
    • TestSuite
      • Coverage
      • Fixture
      • Reporter
    • Utility
    • View
      • Helper

Classes

  • AbstractTransport
  • CakeEmail
  • DebugTransport
  • MailTransport
  • SmtpTransport
   1: <?php
   2: /**
   3:  * Cake E-Mail
   4:  *
   5:  * PHP 5
   6:  *
   7:  * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
   8:  * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
   9:  *
  10:  * Licensed under The MIT License
  11:  * For full copyright and license information, please see the LICENSE.txt
  12:  * Redistributions of files must retain the above copyright notice.
  13:  *
  14:  * @copyright     Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  15:  * @link          http://cakephp.org CakePHP(tm) Project
  16:  * @package       Cake.Network.Email
  17:  * @since         CakePHP(tm) v 2.0.0
  18:  * @license       http://www.opensource.org/licenses/mit-license.php MIT License
  19:  */
  20: 
  21: App::uses('Validation', 'Utility');
  22: App::uses('Multibyte', 'I18n');
  23: App::uses('AbstractTransport', 'Network/Email');
  24: App::uses('File', 'Utility');
  25: App::uses('String', 'Utility');
  26: App::uses('View', 'View');
  27: App::import('I18n', 'Multibyte');
  28: 
  29: /**
  30:  * Cake e-mail class.
  31:  *
  32:  * This class is used for handling Internet Message Format based
  33:  * based on the standard outlined in http://www.rfc-editor.org/rfc/rfc2822.txt
  34:  *
  35:  * @package       Cake.Network.Email
  36:  */
  37: class CakeEmail {
  38: 
  39: /**
  40:  * Default X-Mailer
  41:  *
  42:  * @constant EMAIL_CLIENT
  43:  */
  44:     const EMAIL_CLIENT = 'CakePHP Email';
  45: 
  46: /**
  47:  * Line length - no should more - RFC 2822 - 2.1.1
  48:  *
  49:  * @constant LINE_LENGTH_SHOULD
  50:  */
  51:     const LINE_LENGTH_SHOULD = 78;
  52: 
  53: /**
  54:  * Line length - no must more - RFC 2822 - 2.1.1
  55:  *
  56:  * @constant LINE_LENGTH_MUST
  57:  */
  58:     const LINE_LENGTH_MUST = 998;
  59: 
  60: /**
  61:  * Type of message - HTML
  62:  *
  63:  * @constant MESSAGE_HTML
  64:  */
  65:     const MESSAGE_HTML = 'html';
  66: 
  67: /**
  68:  * Type of message - TEXT
  69:  *
  70:  * @constant MESSAGE_TEXT
  71:  */
  72:     const MESSAGE_TEXT = 'text';
  73: 
  74: /**
  75:  * Recipient of the email
  76:  *
  77:  * @var array
  78:  */
  79:     protected $_to = array();
  80: 
  81: /**
  82:  * The mail which the email is sent from
  83:  *
  84:  * @var array
  85:  */
  86:     protected $_from = array();
  87: 
  88: /**
  89:  * The sender email
  90:  *
  91:  * @var array
  92:  */
  93:     protected $_sender = array();
  94: 
  95: /**
  96:  * The email the recipient will reply to
  97:  *
  98:  * @var array
  99:  */
 100:     protected $_replyTo = array();
 101: 
 102: /**
 103:  * The read receipt email
 104:  *
 105:  * @var array
 106:  */
 107:     protected $_readReceipt = array();
 108: 
 109: /**
 110:  * The mail that will be used in case of any errors like
 111:  * - Remote mailserver down
 112:  * - Remote user has exceeded his quota
 113:  * - Unknown user
 114:  *
 115:  * @var array
 116:  */
 117:     protected $_returnPath = array();
 118: 
 119: /**
 120:  * Carbon Copy
 121:  *
 122:  * List of email's that should receive a copy of the email.
 123:  * The Recipient WILL be able to see this list
 124:  *
 125:  * @var array
 126:  */
 127:     protected $_cc = array();
 128: 
 129: /**
 130:  * Blind Carbon Copy
 131:  *
 132:  * List of email's that should receive a copy of the email.
 133:  * The Recipient WILL NOT be able to see this list
 134:  *
 135:  * @var array
 136:  */
 137:     protected $_bcc = array();
 138: 
 139: /**
 140:  * Message ID
 141:  *
 142:  * @var boolean|string True to generate, False to ignore, String with value
 143:  */
 144:     protected $_messageId = true;
 145: 
 146: /**
 147:  * Domain for messageId generation.
 148:  * Needs to be manually set for CLI mailing as env('HTTP_HOST') is empty
 149:  *
 150:  * @var string
 151:  */
 152:     protected $_domain = null;
 153: 
 154: /**
 155:  * The subject of the email
 156:  *
 157:  * @var string
 158:  */
 159:     protected $_subject = '';
 160: 
 161: /**
 162:  * Associative array of a user defined headers
 163:  * Keys will be prefixed 'X-' as per RFC2822 Section 4.7.5
 164:  *
 165:  * @var array
 166:  */
 167:     protected $_headers = array();
 168: 
 169: /**
 170:  * Layout for the View
 171:  *
 172:  * @var string
 173:  */
 174:     protected $_layout = 'default';
 175: 
 176: /**
 177:  * Template for the view
 178:  *
 179:  * @var string
 180:  */
 181:     protected $_template = '';
 182: 
 183: /**
 184:  * View for render
 185:  *
 186:  * @var string
 187:  */
 188:     protected $_viewRender = 'View';
 189: 
 190: /**
 191:  * Vars to sent to render
 192:  *
 193:  * @var array
 194:  */
 195:     protected $_viewVars = array();
 196: 
 197: /**
 198:  * Theme for the View
 199:  *
 200:  * @var array
 201:  */
 202:     protected $_theme = null;
 203: 
 204: /**
 205:  * Helpers to be used in the render
 206:  *
 207:  * @var array
 208:  */
 209:     protected $_helpers = array('Html');
 210: 
 211: /**
 212:  * Text message
 213:  *
 214:  * @var string
 215:  */
 216:     protected $_textMessage = '';
 217: 
 218: /**
 219:  * Html message
 220:  *
 221:  * @var string
 222:  */
 223:     protected $_htmlMessage = '';
 224: 
 225: /**
 226:  * Final message to send
 227:  *
 228:  * @var array
 229:  */
 230:     protected $_message = array();
 231: 
 232: /**
 233:  * Available formats to be sent.
 234:  *
 235:  * @var array
 236:  */
 237:     protected $_emailFormatAvailable = array('text', 'html', 'both');
 238: 
 239: /**
 240:  * What format should the email be sent in
 241:  *
 242:  * @var string
 243:  */
 244:     protected $_emailFormat = 'text';
 245: 
 246: /**
 247:  * What method should the email be sent
 248:  *
 249:  * @var string
 250:  */
 251:     protected $_transportName = 'Mail';
 252: 
 253: /**
 254:  * Instance of transport class
 255:  *
 256:  * @var AbstractTransport
 257:  */
 258:     protected $_transportClass = null;
 259: 
 260: /**
 261:  * Charset the email body is sent in
 262:  *
 263:  * @var string
 264:  */
 265:     public $charset = 'utf-8';
 266: 
 267: /**
 268:  * Charset the email header is sent in
 269:  * If null, the $charset property will be used as default
 270:  *
 271:  * @var string
 272:  */
 273:     public $headerCharset = null;
 274: 
 275: /**
 276:  * The application wide charset, used to encode headers and body
 277:  *
 278:  * @var string
 279:  */
 280:     protected $_appCharset = null;
 281: 
 282: /**
 283:  * List of files that should be attached to the email.
 284:  *
 285:  * Only absolute paths
 286:  *
 287:  * @var array
 288:  */
 289:     protected $_attachments = array();
 290: 
 291: /**
 292:  * If set, boundary to use for multipart mime messages
 293:  *
 294:  * @var string
 295:  */
 296:     protected $_boundary = null;
 297: 
 298: /**
 299:  * Configuration to transport
 300:  *
 301:  * @var string|array
 302:  */
 303:     protected $_config = array();
 304: 
 305: /**
 306:  * 8Bit character sets
 307:  *
 308:  * @var array
 309:  */
 310:     protected $_charset8bit = array('UTF-8', 'SHIFT_JIS');
 311: 
 312: /**
 313:  * Define Content-Type charset name
 314:  *
 315:  * @var array
 316:  */
 317:     protected $_contentTypeCharset = array(
 318:         'ISO-2022-JP-MS' => 'ISO-2022-JP'
 319:     );
 320: 
 321: /**
 322:  * Constructor
 323:  *
 324:  * @param array|string $config Array of configs, or string to load configs from email.php
 325:  */
 326:     public function __construct($config = null) {
 327:         $this->_appCharset = Configure::read('App.encoding');
 328:         if ($this->_appCharset !== null) {
 329:             $this->charset = $this->_appCharset;
 330:         }
 331:         $this->_domain = preg_replace('/\:\d+$/', '', env('HTTP_HOST'));
 332:         if (empty($this->_domain)) {
 333:             $this->_domain = php_uname('n');
 334:         }
 335: 
 336:         if ($config) {
 337:             $this->config($config);
 338:         }
 339:         if (empty($this->headerCharset)) {
 340:             $this->headerCharset = $this->charset;
 341:         }
 342:     }
 343: 
 344: /**
 345:  * From
 346:  *
 347:  * @param string|array $email
 348:  * @param string $name
 349:  * @return array|CakeEmail
 350:  * @throws SocketException
 351:  */
 352:     public function from($email = null, $name = null) {
 353:         if ($email === null) {
 354:             return $this->_from;
 355:         }
 356:         return $this->_setEmailSingle('_from', $email, $name, __d('cake_dev', 'From requires only 1 email address.'));
 357:     }
 358: 
 359: /**
 360:  * Sender
 361:  *
 362:  * @param string|array $email
 363:  * @param string $name
 364:  * @return array|CakeEmail
 365:  * @throws SocketException
 366:  */
 367:     public function sender($email = null, $name = null) {
 368:         if ($email === null) {
 369:             return $this->_sender;
 370:         }
 371:         return $this->_setEmailSingle('_sender', $email, $name, __d('cake_dev', 'Sender requires only 1 email address.'));
 372:     }
 373: 
 374: /**
 375:  * Reply-To
 376:  *
 377:  * @param string|array $email
 378:  * @param string $name
 379:  * @return array|CakeEmail
 380:  * @throws SocketException
 381:  */
 382:     public function replyTo($email = null, $name = null) {
 383:         if ($email === null) {
 384:             return $this->_replyTo;
 385:         }
 386:         return $this->_setEmailSingle('_replyTo', $email, $name, __d('cake_dev', 'Reply-To requires only 1 email address.'));
 387:     }
 388: 
 389: /**
 390:  * Read Receipt (Disposition-Notification-To header)
 391:  *
 392:  * @param string|array $email
 393:  * @param string $name
 394:  * @return array|CakeEmail
 395:  * @throws SocketException
 396:  */
 397:     public function readReceipt($email = null, $name = null) {
 398:         if ($email === null) {
 399:             return $this->_readReceipt;
 400:         }
 401:         return $this->_setEmailSingle('_readReceipt', $email, $name, __d('cake_dev', 'Disposition-Notification-To requires only 1 email address.'));
 402:     }
 403: 
 404: /**
 405:  * Return Path
 406:  *
 407:  * @param string|array $email
 408:  * @param string $name
 409:  * @return array|CakeEmail
 410:  * @throws SocketException
 411:  */
 412:     public function returnPath($email = null, $name = null) {
 413:         if ($email === null) {
 414:             return $this->_returnPath;
 415:         }
 416:         return $this->_setEmailSingle('_returnPath', $email, $name, __d('cake_dev', 'Return-Path requires only 1 email address.'));
 417:     }
 418: 
 419: /**
 420:  * To
 421:  *
 422:  * @param string|array $email Null to get, String with email, Array with email as key, name as value or email as value (without name)
 423:  * @param string $name
 424:  * @return array|CakeEmail
 425:  */
 426:     public function to($email = null, $name = null) {
 427:         if ($email === null) {
 428:             return $this->_to;
 429:         }
 430:         return $this->_setEmail('_to', $email, $name);
 431:     }
 432: 
 433: /**
 434:  * Add To
 435:  *
 436:  * @param string|array $email String with email, Array with email as key, name as value or email as value (without name)
 437:  * @param string $name
 438:  * @return CakeEmail $this
 439:  */
 440:     public function addTo($email, $name = null) {
 441:         return $this->_addEmail('_to', $email, $name);
 442:     }
 443: 
 444: /**
 445:  * Cc
 446:  *
 447:  * @param string|array $email String with email, Array with email as key, name as value or email as value (without name)
 448:  * @param string $name
 449:  * @return array|CakeEmail
 450:  */
 451:     public function cc($email = null, $name = null) {
 452:         if ($email === null) {
 453:             return $this->_cc;
 454:         }
 455:         return $this->_setEmail('_cc', $email, $name);
 456:     }
 457: 
 458: /**
 459:  * Add Cc
 460:  *
 461:  * @param string|array $email String with email, Array with email as key, name as value or email as value (without name)
 462:  * @param string $name
 463:  * @return CakeEmail $this
 464:  */
 465:     public function addCc($email, $name = null) {
 466:         return $this->_addEmail('_cc', $email, $name);
 467:     }
 468: 
 469: /**
 470:  * Bcc
 471:  *
 472:  * @param string|array $email String with email, Array with email as key, name as value or email as value (without name)
 473:  * @param string $name
 474:  * @return array|CakeEmail
 475:  */
 476:     public function bcc($email = null, $name = null) {
 477:         if ($email === null) {
 478:             return $this->_bcc;
 479:         }
 480:         return $this->_setEmail('_bcc', $email, $name);
 481:     }
 482: 
 483: /**
 484:  * Add Bcc
 485:  *
 486:  * @param string|array $email String with email, Array with email as key, name as value or email as value (without name)
 487:  * @param string $name
 488:  * @return CakeEmail $this
 489:  */
 490:     public function addBcc($email, $name = null) {
 491:         return $this->_addEmail('_bcc', $email, $name);
 492:     }
 493: 
 494: /**
 495:  * Charset setter/getter
 496:  *
 497:  * @param string $charset
 498:  * @return string $this->charset
 499:  */
 500:     public function charset($charset = null) {
 501:         if ($charset === null) {
 502:             return $this->charset;
 503:         }
 504:         $this->charset = $charset;
 505:         if (empty($this->headerCharset)) {
 506:             $this->headerCharset = $charset;
 507:         }
 508:         return $this->charset;
 509:     }
 510: 
 511: /**
 512:  * HeaderCharset setter/getter
 513:  *
 514:  * @param string $charset
 515:  * @return string $this->charset
 516:  */
 517:     public function headerCharset($charset = null) {
 518:         if ($charset === null) {
 519:             return $this->headerCharset;
 520:         }
 521:         return $this->headerCharset = $charset;
 522:     }
 523: 
 524: /**
 525:  * Set email
 526:  *
 527:  * @param string $varName
 528:  * @param string|array $email
 529:  * @param string $name
 530:  * @return CakeEmail $this
 531:  * @throws SocketException
 532:  */
 533:     protected function _setEmail($varName, $email, $name) {
 534:         if (!is_array($email)) {
 535:             if (!Validation::email($email)) {
 536:                 throw new SocketException(__d('cake_dev', 'Invalid email: "%s"', $email));
 537:             }
 538:             if ($name === null) {
 539:                 $name = $email;
 540:             }
 541:             $this->{$varName} = array($email => $name);
 542:             return $this;
 543:         }
 544:         $list = array();
 545:         foreach ($email as $key => $value) {
 546:             if (is_int($key)) {
 547:                 $key = $value;
 548:             }
 549:             if (!Validation::email($key)) {
 550:                 throw new SocketException(__d('cake_dev', 'Invalid email: "%s"', $key));
 551:             }
 552:             $list[$key] = $value;
 553:         }
 554:         $this->{$varName} = $list;
 555:         return $this;
 556:     }
 557: 
 558: /**
 559:  * Set only 1 email
 560:  *
 561:  * @param string $varName
 562:  * @param string|array $email
 563:  * @param string $name
 564:  * @param string $throwMessage
 565:  * @return CakeEmail $this
 566:  * @throws SocketException
 567:  */
 568:     protected function _setEmailSingle($varName, $email, $name, $throwMessage) {
 569:         $current = $this->{$varName};
 570:         $this->_setEmail($varName, $email, $name);
 571:         if (count($this->{$varName}) !== 1) {
 572:             $this->{$varName} = $current;
 573:             throw new SocketException($throwMessage);
 574:         }
 575:         return $this;
 576:     }
 577: 
 578: /**
 579:  * Add email
 580:  *
 581:  * @param string $varName
 582:  * @param string|array $email
 583:  * @param string $name
 584:  * @return CakeEmail $this
 585:  * @throws SocketException
 586:  */
 587:     protected function _addEmail($varName, $email, $name) {
 588:         if (!is_array($email)) {
 589:             if (!Validation::email($email)) {
 590:                 throw new SocketException(__d('cake_dev', 'Invalid email: "%s"', $email));
 591:             }
 592:             if ($name === null) {
 593:                 $name = $email;
 594:             }
 595:             $this->{$varName}[$email] = $name;
 596:             return $this;
 597:         }
 598:         $list = array();
 599:         foreach ($email as $key => $value) {
 600:             if (is_int($key)) {
 601:                 $key = $value;
 602:             }
 603:             if (!Validation::email($key)) {
 604:                 throw new SocketException(__d('cake_dev', 'Invalid email: "%s"', $key));
 605:             }
 606:             $list[$key] = $value;
 607:         }
 608:         $this->{$varName} = array_merge($this->{$varName}, $list);
 609:         return $this;
 610:     }
 611: 
 612: /**
 613:  * Get/Set Subject.
 614:  *
 615:  * @param string $subject
 616:  * @return string|CakeEmail
 617:  */
 618:     public function subject($subject = null) {
 619:         if ($subject === null) {
 620:             return $this->_subject;
 621:         }
 622:         $this->_subject = $this->_encode((string)$subject);
 623:         return $this;
 624:     }
 625: 
 626: /**
 627:  * Sets headers for the message
 628:  *
 629:  * @param array $headers Associative array containing headers to be set.
 630:  * @return CakeEmail $this
 631:  * @throws SocketException
 632:  */
 633:     public function setHeaders($headers) {
 634:         if (!is_array($headers)) {
 635:             throw new SocketException(__d('cake_dev', '$headers should be an array.'));
 636:         }
 637:         $this->_headers = $headers;
 638:         return $this;
 639:     }
 640: 
 641: /**
 642:  * Add header for the message
 643:  *
 644:  * @param array $headers
 645:  * @return object $this
 646:  * @throws SocketException
 647:  */
 648:     public function addHeaders($headers) {
 649:         if (!is_array($headers)) {
 650:             throw new SocketException(__d('cake_dev', '$headers should be an array.'));
 651:         }
 652:         $this->_headers = array_merge($this->_headers, $headers);
 653:         return $this;
 654:     }
 655: 
 656: /**
 657:  * Get list of headers
 658:  *
 659:  * ### Includes:
 660:  *
 661:  * - `from`
 662:  * - `replyTo`
 663:  * - `readReceipt`
 664:  * - `returnPath`
 665:  * - `to`
 666:  * - `cc`
 667:  * - `bcc`
 668:  * - `subject`
 669:  *
 670:  * @param array $include
 671:  * @return array
 672:  */
 673:     public function getHeaders($include = array()) {
 674:         if ($include == array_values($include)) {
 675:             $include = array_fill_keys($include, true);
 676:         }
 677:         $defaults = array_fill_keys(array('from', 'sender', 'replyTo', 'readReceipt', 'returnPath', 'to', 'cc', 'bcc', 'subject'), false);
 678:         $include += $defaults;
 679: 
 680:         $headers = array();
 681:         $relation = array(
 682:             'from' => 'From',
 683:             'replyTo' => 'Reply-To',
 684:             'readReceipt' => 'Disposition-Notification-To',
 685:             'returnPath' => 'Return-Path'
 686:         );
 687:         foreach ($relation as $var => $header) {
 688:             if ($include[$var]) {
 689:                 $var = '_' . $var;
 690:                 $headers[$header] = current($this->_formatAddress($this->{$var}));
 691:             }
 692:         }
 693:         if ($include['sender']) {
 694:             if (key($this->_sender) === key($this->_from)) {
 695:                 $headers['Sender'] = '';
 696:             } else {
 697:                 $headers['Sender'] = current($this->_formatAddress($this->_sender));
 698:             }
 699:         }
 700: 
 701:         foreach (array('to', 'cc', 'bcc') as $var) {
 702:             if ($include[$var]) {
 703:                 $classVar = '_' . $var;
 704:                 $headers[ucfirst($var)] = implode(', ', $this->_formatAddress($this->{$classVar}));
 705:             }
 706:         }
 707: 
 708:         $headers += $this->_headers;
 709:         if (!isset($headers['X-Mailer'])) {
 710:             $headers['X-Mailer'] = self::EMAIL_CLIENT;
 711:         }
 712:         if (!isset($headers['Date'])) {
 713:             $headers['Date'] = date(DATE_RFC2822);
 714:         }
 715:         if ($this->_messageId !== false) {
 716:             if ($this->_messageId === true) {
 717:                 $headers['Message-ID'] = '<' . str_replace('-', '', String::UUID()) . '@' . $this->_domain . '>';
 718:             } else {
 719:                 $headers['Message-ID'] = $this->_messageId;
 720:             }
 721:         }
 722: 
 723:         if ($include['subject']) {
 724:             $headers['Subject'] = $this->_subject;
 725:         }
 726: 
 727:         $headers['MIME-Version'] = '1.0';
 728:         if (!empty($this->_attachments) || $this->_emailFormat === 'both') {
 729:             $headers['Content-Type'] = 'multipart/mixed; boundary="' . $this->_boundary . '"';
 730:         } elseif ($this->_emailFormat === 'text') {
 731:             $headers['Content-Type'] = 'text/plain; charset=' . $this->_getContentTypeCharset();
 732:         } elseif ($this->_emailFormat === 'html') {
 733:             $headers['Content-Type'] = 'text/html; charset=' . $this->_getContentTypeCharset();
 734:         }
 735:         $headers['Content-Transfer-Encoding'] = $this->_getContentTransferEncoding();
 736: 
 737:         return $headers;
 738:     }
 739: 
 740: /**
 741:  * Format addresses
 742:  *
 743:  * @param array $address
 744:  * @return array
 745:  */
 746:     protected function _formatAddress($address) {
 747:         $return = array();
 748:         foreach ($address as $email => $alias) {
 749:             if ($email === $alias) {
 750:                 $return[] = $email;
 751:             } else {
 752:                 if (strpos($alias, ',') !== false) {
 753:                     $alias = '"' . $alias . '"';
 754:                 }
 755:                 $return[] = sprintf('%s <%s>', $this->_encode($alias), $email);
 756:             }
 757:         }
 758:         return $return;
 759:     }
 760: 
 761: /**
 762:  * Template and layout
 763:  *
 764:  * @param boolean|string $template Template name or null to not use
 765:  * @param boolean|string $layout Layout name or null to not use
 766:  * @return array|CakeEmail
 767:  */
 768:     public function template($template = false, $layout = false) {
 769:         if ($template === false) {
 770:             return array(
 771:                 'template' => $this->_template,
 772:                 'layout' => $this->_layout
 773:             );
 774:         }
 775:         $this->_template = $template;
 776:         if ($layout !== false) {
 777:             $this->_layout = $layout;
 778:         }
 779:         return $this;
 780:     }
 781: 
 782: /**
 783:  * View class for render
 784:  *
 785:  * @param string $viewClass
 786:  * @return string|CakeEmail
 787:  */
 788:     public function viewRender($viewClass = null) {
 789:         if ($viewClass === null) {
 790:             return $this->_viewRender;
 791:         }
 792:         $this->_viewRender = $viewClass;
 793:         return $this;
 794:     }
 795: 
 796: /**
 797:  * Variables to be set on render
 798:  *
 799:  * @param array $viewVars
 800:  * @return array|CakeEmail
 801:  */
 802:     public function viewVars($viewVars = null) {
 803:         if ($viewVars === null) {
 804:             return $this->_viewVars;
 805:         }
 806:         $this->_viewVars = array_merge($this->_viewVars, (array)$viewVars);
 807:         return $this;
 808:     }
 809: 
 810: /**
 811:  * Theme to use when rendering
 812:  *
 813:  * @param string $theme
 814:  * @return string|CakeEmail
 815:  */
 816:     public function theme($theme = null) {
 817:         if ($theme === null) {
 818:             return $this->_theme;
 819:         }
 820:         $this->_theme = $theme;
 821:         return $this;
 822:     }
 823: 
 824: /**
 825:  * Helpers to be used in render
 826:  *
 827:  * @param array $helpers
 828:  * @return array|CakeEmail
 829:  */
 830:     public function helpers($helpers = null) {
 831:         if ($helpers === null) {
 832:             return $this->_helpers;
 833:         }
 834:         $this->_helpers = (array)$helpers;
 835:         return $this;
 836:     }
 837: 
 838: /**
 839:  * Email format
 840:  *
 841:  * @param string $format
 842:  * @return string|CakeEmail
 843:  * @throws SocketException
 844:  */
 845:     public function emailFormat($format = null) {
 846:         if ($format === null) {
 847:             return $this->_emailFormat;
 848:         }
 849:         if (!in_array($format, $this->_emailFormatAvailable)) {
 850:             throw new SocketException(__d('cake_dev', 'Format not available.'));
 851:         }
 852:         $this->_emailFormat = $format;
 853:         return $this;
 854:     }
 855: 
 856: /**
 857:  * Transport name
 858:  *
 859:  * @param string $name
 860:  * @return string|CakeEmail
 861:  */
 862:     public function transport($name = null) {
 863:         if ($name === null) {
 864:             return $this->_transportName;
 865:         }
 866:         $this->_transportName = (string)$name;
 867:         $this->_transportClass = null;
 868:         return $this;
 869:     }
 870: 
 871: /**
 872:  * Return the transport class
 873:  *
 874:  * @return CakeEmail
 875:  * @throws SocketException
 876:  */
 877:     public function transportClass() {
 878:         if ($this->_transportClass) {
 879:             return $this->_transportClass;
 880:         }
 881:         list($plugin, $transportClassname) = pluginSplit($this->_transportName, true);
 882:         $transportClassname .= 'Transport';
 883:         App::uses($transportClassname, $plugin . 'Network/Email');
 884:         if (!class_exists($transportClassname)) {
 885:             throw new SocketException(__d('cake_dev', 'Class "%s" not found.', $transportClassname));
 886:         } elseif (!method_exists($transportClassname, 'send')) {
 887:             throw new SocketException(__d('cake_dev', 'The "%s" do not have send method.', $transportClassname));
 888:         }
 889: 
 890:         return $this->_transportClass = new $transportClassname();
 891:     }
 892: 
 893: /**
 894:  * Message-ID
 895:  *
 896:  * @param boolean|string $message True to generate a new Message-ID, False to ignore (not send in email), String to set as Message-ID
 897:  * @return boolean|string|CakeEmail
 898:  * @throws SocketException
 899:  */
 900:     public function messageId($message = null) {
 901:         if ($message === null) {
 902:             return $this->_messageId;
 903:         }
 904:         if (is_bool($message)) {
 905:             $this->_messageId = $message;
 906:         } else {
 907:             if (!preg_match('/^\<.+@.+\>$/', $message)) {
 908:                 throw new SocketException(__d('cake_dev', 'Invalid format for Message-ID. The text should be something like "<[email protected]>"'));
 909:             }
 910:             $this->_messageId = $message;
 911:         }
 912:         return $this;
 913:     }
 914: 
 915: /**
 916:  * Domain as top level (the part after @)
 917:  *
 918:  * @param string $domain Manually set the domain for CLI mailing
 919:  * @return string|CakeEmail
 920:  */
 921:     public function domain($domain = null) {
 922:         if ($domain === null) {
 923:             return $this->_domain;
 924:         }
 925:         $this->_domain = $domain;
 926:         return $this;
 927:     }
 928: 
 929: /**
 930:  * Add attachments to the email message
 931:  *
 932:  * Attachments can be defined in a few forms depending on how much control you need:
 933:  *
 934:  * Attach a single file:
 935:  *
 936:  * {{{
 937:  * $email->attachments('path/to/file');
 938:  * }}}
 939:  *
 940:  * Attach a file with a different filename:
 941:  *
 942:  * {{{
 943:  * $email->attachments(array('custom_name.txt' => 'path/to/file.txt'));
 944:  * }}}
 945:  *
 946:  * Attach a file and specify additional properties:
 947:  *
 948:  * {{{
 949:  * $email->attachments(array('custom_name.png' => array(
 950:  *      'file' => 'path/to/file',
 951:  *      'mimetype' => 'image/png',
 952:  *      'contentId' => 'abc123',
 953:  *      'contentDisposition' => false
 954:  * ));
 955:  * }}}
 956:  *
 957:  * The `contentId` key allows you to specify an inline attachment. In your email text, you
 958:  * can use `<img src="cid:abc123" />` to display the image inline.
 959:  *
 960:  * The `contentDisposition` key allows you to disable the `Content-Disposition` header, this can improve
 961:  * attachment compatibility with outlook email clients.
 962:  *
 963:  * @param string|array $attachments String with the filename or array with filenames
 964:  * @return array|CakeEmail Either the array of attachments when getting or $this when setting.
 965:  * @throws SocketException
 966:  */
 967:     public function attachments($attachments = null) {
 968:         if ($attachments === null) {
 969:             return $this->_attachments;
 970:         }
 971:         $attach = array();
 972:         foreach ((array)$attachments as $name => $fileInfo) {
 973:             if (!is_array($fileInfo)) {
 974:                 $fileInfo = array('file' => $fileInfo);
 975:             }
 976:             if (!isset($fileInfo['file'])) {
 977:                 throw new SocketException(__d('cake_dev', 'File not specified.'));
 978:             }
 979:             $fileInfo['file'] = realpath($fileInfo['file']);
 980:             if ($fileInfo['file'] === false || !file_exists($fileInfo['file'])) {
 981:                 throw new SocketException(__d('cake_dev', 'File not found: "%s"', $fileInfo['file']));
 982:             }
 983:             if (is_int($name)) {
 984:                 $name = basename($fileInfo['file']);
 985:             }
 986:             if (!isset($fileInfo['mimetype'])) {
 987:                 $fileInfo['mimetype'] = 'application/octet-stream';
 988:             }
 989:             $attach[$name] = $fileInfo;
 990:         }
 991:         $this->_attachments = $attach;
 992:         return $this;
 993:     }
 994: 
 995: /**
 996:  * Add attachments
 997:  *
 998:  * @param string|array $attachments String with the filename or array with filenames
 999:  * @return CakeEmail $this
1000:  * @throws SocketException
1001:  * @see CakeEmail::attachments()
1002:  */
1003:     public function addAttachments($attachments) {
1004:         $current = $this->_attachments;
1005:         $this->attachments($attachments);
1006:         $this->_attachments = array_merge($current, $this->_attachments);
1007:         return $this;
1008:     }
1009: 
1010: /**
1011:  * Get generated message (used by transport classes)
1012:  *
1013:  * @param string $type Use MESSAGE_* constants or null to return the full message as array
1014:  * @return string|array String if have type, array if type is null
1015:  */
1016:     public function message($type = null) {
1017:         switch ($type) {
1018:             case self::MESSAGE_HTML:
1019:                 return $this->_htmlMessage;
1020:             case self::MESSAGE_TEXT:
1021:                 return $this->_textMessage;
1022:         }
1023:         return $this->_message;
1024:     }
1025: 
1026: /**
1027:  * Configuration to use when send email
1028:  *
1029:  * ### Usage
1030:  *
1031:  * Load configuration from `app/Config/email.php`:
1032:  *
1033:  * `$email->config('default');`
1034:  *
1035:  * Merge an array of configuration into the instance:
1036:  *
1037:  * `$email->config(array('to' => '[email protected]'));`
1038:  *
1039:  * @param string|array $config String with configuration name (from email.php), array with config or null to return current config
1040:  * @return string|array|CakeEmail
1041:  */
1042:     public function config($config = null) {
1043:         if ($config === null) {
1044:             return $this->_config;
1045:         }
1046:         if (!is_array($config)) {
1047:             $config = (string)$config;
1048:         }
1049: 
1050:         $this->_applyConfig($config);
1051:         return $this;
1052:     }
1053: 
1054: /**
1055:  * Send an email using the specified content, template and layout
1056:  *
1057:  * @param string|array $content String with message or array with messages
1058:  * @return array
1059:  * @throws SocketException
1060:  */
1061:     public function send($content = null) {
1062:         if (empty($this->_from)) {
1063:             throw new SocketException(__d('cake_dev', 'From is not specified.'));
1064:         }
1065:         if (empty($this->_to) && empty($this->_cc) && empty($this->_bcc)) {
1066:             throw new SocketException(__d('cake_dev', 'You need to specify at least one destination for to, cc or bcc.'));
1067:         }
1068: 
1069:         if (is_array($content)) {
1070:             $content = implode("\n", $content) . "\n";
1071:         }
1072: 
1073:         $this->_message = $this->_render($this->_wrap($content));
1074: 
1075:         $contents = $this->transportClass()->send($this);
1076:         if (!empty($this->_config['log'])) {
1077:             $level = LOG_DEBUG;
1078:             if ($this->_config['log'] !== true) {
1079:                 $level = $this->_config['log'];
1080:             }
1081:             CakeLog::write($level, PHP_EOL . $contents['headers'] . PHP_EOL . $contents['message']);
1082:         }
1083:         return $contents;
1084:     }
1085: 
1086: /**
1087:  * Static method to fast create an instance of CakeEmail
1088:  *
1089:  * @param string|array $to Address to send (see CakeEmail::to()). If null, will try to use 'to' from transport config
1090:  * @param string $subject String of subject or null to use 'subject' from transport config
1091:  * @param string|array $message String with message or array with variables to be used in render
1092:  * @param string|array $transportConfig String to use config from EmailConfig or array with configs
1093:  * @param boolean $send Send the email or just return the instance pre-configured
1094:  * @return CakeEmail Instance of CakeEmail
1095:  * @throws SocketException
1096:  */
1097:     public static function deliver($to = null, $subject = null, $message = null, $transportConfig = 'fast', $send = true) {
1098:         $class = __CLASS__;
1099:         $instance = new $class($transportConfig);
1100:         if ($to !== null) {
1101:             $instance->to($to);
1102:         }
1103:         if ($subject !== null) {
1104:             $instance->subject($subject);
1105:         }
1106:         if (is_array($message)) {
1107:             $instance->viewVars($message);
1108:             $message = null;
1109:         } elseif ($message === null && array_key_exists('message', $config = $instance->config())) {
1110:             $message = $config['message'];
1111:         }
1112: 
1113:         if ($send === true) {
1114:             $instance->send($message);
1115:         }
1116: 
1117:         return $instance;
1118:     }
1119: 
1120: /**
1121:  * Apply the config to an instance
1122:  *
1123:  * @param array $config
1124:  * @return void
1125:  * @throws ConfigureException When configuration file cannot be found, or is missing
1126:  *   the named config.
1127:  */
1128:     protected function _applyConfig($config) {
1129:         if (is_string($config)) {
1130:             if (!class_exists('EmailConfig') && !config('email')) {
1131:                 throw new ConfigureException(__d('cake_dev', '%s not found.', APP . 'Config' . DS . 'email.php'));
1132:             }
1133:             $configs = new EmailConfig();
1134:             if (!isset($configs->{$config})) {
1135:                 throw new ConfigureException(__d('cake_dev', 'Unknown email configuration "%s".', $config));
1136:             }
1137:             $config = $configs->{$config};
1138:         }
1139:         $this->_config = array_merge($this->_config, $config);
1140:         if (!empty($config['charset'])) {
1141:             $this->charset = $config['charset'];
1142:         }
1143:         if (!empty($config['headerCharset'])) {
1144:             $this->headerCharset = $config['headerCharset'];
1145:         }
1146:         if (empty($this->headerCharset)) {
1147:             $this->headerCharset = $this->charset;
1148:         }
1149:         $simpleMethods = array(
1150:             'from', 'sender', 'to', 'replyTo', 'readReceipt', 'returnPath', 'cc', 'bcc',
1151:             'messageId', 'domain', 'subject', 'viewRender', 'viewVars', 'attachments',
1152:             'transport', 'emailFormat', 'theme', 'helpers'
1153:         );
1154:         foreach ($simpleMethods as $method) {
1155:             if (isset($config[$method])) {
1156:                 $this->$method($config[$method]);
1157:                 unset($config[$method]);
1158:             }
1159:         }
1160:         if (isset($config['headers'])) {
1161:             $this->setHeaders($config['headers']);
1162:             unset($config['headers']);
1163:         }
1164:         if (array_key_exists('template', $config)) {
1165:             $layout = false;
1166:             if (array_key_exists('layout', $config)) {
1167:                 $layout = $config['layout'];
1168:                 unset($config['layout']);
1169:             }
1170:             $this->template($config['template'], $layout);
1171:             unset($config['template']);
1172:         }
1173:         $this->transportClass()->config($config);
1174:     }
1175: 
1176: /**
1177:  * Reset all CakeEmail internal variables to be able to send out a new email.
1178:  *
1179:  * @return CakeEmail $this
1180:  */
1181:     public function reset() {
1182:         $this->_to = array();
1183:         $this->_from = array();
1184:         $this->_sender = array();
1185:         $this->_replyTo = array();
1186:         $this->_readReceipt = array();
1187:         $this->_returnPath = array();
1188:         $this->_cc = array();
1189:         $this->_bcc = array();
1190:         $this->_messageId = true;
1191:         $this->_subject = '';
1192:         $this->_headers = array();
1193:         $this->_layout = 'default';
1194:         $this->_template = '';
1195:         $this->_viewRender = 'View';
1196:         $this->_viewVars = array();
1197:         $this->_theme = null;
1198:         $this->_helpers = array('Html');
1199:         $this->_textMessage = '';
1200:         $this->_htmlMessage = '';
1201:         $this->_message = '';
1202:         $this->_emailFormat = 'text';
1203:         $this->_transportName = 'Mail';
1204:         $this->_transportClass = null;
1205:         $this->charset = 'utf-8';
1206:         $this->headerCharset = null;
1207:         $this->_attachments = array();
1208:         $this->_config = array();
1209:         return $this;
1210:     }
1211: 
1212: /**
1213:  * Encode the specified string using the current charset
1214:  *
1215:  * @param string $text String to encode
1216:  * @return string Encoded string
1217:  */
1218:     protected function _encode($text) {
1219:         $internalEncoding = function_exists('mb_internal_encoding');
1220:         if ($internalEncoding) {
1221:             $restore = mb_internal_encoding();
1222:             mb_internal_encoding($this->_appCharset);
1223:         }
1224:         if (empty($this->headerCharset)) {
1225:             $this->headerCharset = $this->charset;
1226:         }
1227:         $return = mb_encode_mimeheader($text, $this->headerCharset, 'B');
1228:         if ($internalEncoding) {
1229:             mb_internal_encoding($restore);
1230:         }
1231:         return $return;
1232:     }
1233: 
1234: /**
1235:  * Translates a string for one charset to another if the App.encoding value
1236:  * differs and the mb_convert_encoding function exists
1237:  *
1238:  * @param string $text The text to be converted
1239:  * @param string $charset the target encoding
1240:  * @return string
1241:  */
1242:     protected function _encodeString($text, $charset) {
1243:         if ($this->_appCharset === $charset || !function_exists('mb_convert_encoding')) {
1244:             return $text;
1245:         }
1246:         return mb_convert_encoding($text, $charset, $this->_appCharset);
1247:     }
1248: 
1249: /**
1250:  * Wrap the message to follow the RFC 2822 - 2.1.1
1251:  *
1252:  * @param string $message Message to wrap
1253:  * @return array Wrapped message
1254:  */
1255:     protected function _wrap($message, $wrapLength = CakeEmail::LINE_LENGTH_MUST) {
1256:         $message = str_replace(array("\r\n", "\r"), "\n", $message);
1257:         $lines = explode("\n", $message);
1258:         $formatted = array();
1259:         $cut = ($wrapLength == CakeEmail::LINE_LENGTH_MUST);
1260: 
1261:         foreach ($lines as $line) {
1262:             if (empty($line)) {
1263:                 $formatted[] = '';
1264:                 continue;
1265:             }
1266:             if (strlen($line) < $wrapLength) {
1267:                 $formatted[] = $line;
1268:                 continue;
1269:             }
1270:             if (!preg_match('/<[a-z]+.*>/i', $line)) {
1271:                 $formatted = array_merge(
1272:                     $formatted,
1273:                     explode("\n", wordwrap($line, $wrapLength, "\n", $cut))
1274:                 );
1275:                 continue;
1276:             }
1277: 
1278:             $tagOpen = false;
1279:             $tmpLine = $tag = '';
1280:             $tmpLineLength = 0;
1281:             for ($i = 0, $count = strlen($line); $i < $count; $i++) {
1282:                 $char = $line[$i];
1283:                 if ($tagOpen) {
1284:                     $tag .= $char;
1285:                     if ($char === '>') {
1286:                         $tagLength = strlen($tag);
1287:                         if ($tagLength + $tmpLineLength < $wrapLength) {
1288:                             $tmpLine .= $tag;
1289:                             $tmpLineLength += $tagLength;
1290:                         } else {
1291:                             if ($tmpLineLength > 0) {
1292:                                 $formatted = array_merge(
1293:                                     $formatted,
1294:                                     explode("\n", wordwrap(trim($tmpLine), $wrapLength, "\n", $cut))
1295:                                 );
1296:                                 $tmpLine = '';
1297:                                 $tmpLineLength = 0;
1298:                             }
1299:                             if ($tagLength > $wrapLength) {
1300:                                 $formatted[] = $tag;
1301:                             } else {
1302:                                 $tmpLine = $tag;
1303:                                 $tmpLineLength = $tagLength;
1304:                             }
1305:                         }
1306:                         $tag = '';
1307:                         $tagOpen = false;
1308:                     }
1309:                     continue;
1310:                 }
1311:                 if ($char === '<') {
1312:                     $tagOpen = true;
1313:                     $tag = '<';
1314:                     continue;
1315:                 }
1316:                 if ($char === ' ' && $tmpLineLength >= $wrapLength) {
1317:                     $formatted[] = $tmpLine;
1318:                     $tmpLineLength = 0;
1319:                     continue;
1320:                 }
1321:                 $tmpLine .= $char;
1322:                 $tmpLineLength++;
1323:                 if ($tmpLineLength === $wrapLength) {
1324:                     $nextChar = $line[$i + 1];
1325:                     if ($nextChar === ' ' || $nextChar === '<') {
1326:                         $formatted[] = trim($tmpLine);
1327:                         $tmpLine = '';
1328:                         $tmpLineLength = 0;
1329:                         if ($nextChar === ' ') {
1330:                             $i++;
1331:                         }
1332:                     } else {
1333:                         $lastSpace = strrpos($tmpLine, ' ');
1334:                         if ($lastSpace === false) {
1335:                             continue;
1336:                         }
1337:                         $formatted[] = trim(substr($tmpLine, 0, $lastSpace));
1338:                         $tmpLine = substr($tmpLine, $lastSpace + 1);
1339: 
1340:                         $tmpLineLength = strlen($tmpLine);
1341:                     }
1342:                 }
1343:             }
1344:             if (!empty($tmpLine)) {
1345:                 $formatted[] = $tmpLine;
1346:             }
1347:         }
1348:         $formatted[] = '';
1349:         return $formatted;
1350:     }
1351: 
1352: /**
1353:  * Create unique boundary identifier
1354:  *
1355:  * @return void
1356:  */
1357:     protected function _createBoundary() {
1358:         if (!empty($this->_attachments) || $this->_emailFormat === 'both') {
1359:             $this->_boundary = md5(uniqid(time()));
1360:         }
1361:     }
1362: 
1363: /**
1364:  * Attach non-embedded files by adding file contents inside boundaries.
1365:  *
1366:  * @param string $boundary Boundary to use. If null, will default to $this->_boundary
1367:  * @return array An array of lines to add to the message
1368:  */
1369:     protected function _attachFiles($boundary = null) {
1370:         if ($boundary === null) {
1371:             $boundary = $this->_boundary;
1372:         }
1373: 
1374:         $msg = array();
1375:         foreach ($this->_attachments as $filename => $fileInfo) {
1376:             if (!empty($fileInfo['contentId'])) {
1377:                 continue;
1378:             }
1379:             $data = $this->_readFile($fileInfo['file']);
1380: 
1381:             $msg[] = '--' . $boundary;
1382:             $msg[] = 'Content-Type: ' . $fileInfo['mimetype'];
1383:             $msg[] = 'Content-Transfer-Encoding: base64';
1384:             if (
1385:                 !isset($fileInfo['contentDisposition']) ||
1386:                 $fileInfo['contentDisposition']
1387:             ) {
1388:                 $msg[] = 'Content-Disposition: attachment; filename="' . $filename . '"';
1389:             }
1390:             $msg[] = '';
1391:             $msg[] = $data;
1392:             $msg[] = '';
1393:         }
1394:         return $msg;
1395:     }
1396: 
1397: /**
1398:  * Read the file contents and return a base64 version of the file contents.
1399:  *
1400:  * @param string $path The absolute path to the file to read.
1401:  * @return string File contents in base64 encoding
1402:  */
1403:     protected function _readFile($path) {
1404:         $File = new File($path);
1405:         return chunk_split(base64_encode($File->read()));
1406:     }
1407: 
1408: /**
1409:  * Attach inline/embedded files to the message.
1410:  *
1411:  * @param string $boundary Boundary to use. If null, will default to $this->_boundary
1412:  * @return array An array of lines to add to the message
1413:  */
1414:     protected function _attachInlineFiles($boundary = null) {
1415:         if ($boundary === null) {
1416:             $boundary = $this->_boundary;
1417:         }
1418: 
1419:         $msg = array();
1420:         foreach ($this->_attachments as $filename => $fileInfo) {
1421:             if (empty($fileInfo['contentId'])) {
1422:                 continue;
1423:             }
1424:             $data = $this->_readFile($fileInfo['file']);
1425: 
1426:             $msg[] = '--' . $boundary;
1427:             $msg[] = 'Content-Type: ' . $fileInfo['mimetype'];
1428:             $msg[] = 'Content-Transfer-Encoding: base64';
1429:             $msg[] = 'Content-ID: <' . $fileInfo['contentId'] . '>';
1430:             $msg[] = 'Content-Disposition: inline; filename="' . $filename . '"';
1431:             $msg[] = '';
1432:             $msg[] = $data;
1433:             $msg[] = '';
1434:         }
1435:         return $msg;
1436:     }
1437: 
1438: /**
1439:  * Render the body of the email.
1440:  *
1441:  * @param array $content Content to render
1442:  * @return array Email body ready to be sent
1443:  */
1444:     protected function _render($content) {
1445:         $this->_textMessage = $this->_htmlMessage = '';
1446: 
1447:         $content = implode("\n", $content);
1448:         $rendered = $this->_renderTemplates($content);
1449: 
1450:         $this->_createBoundary();
1451:         $msg = array();
1452: 
1453:         $contentIds = array_filter((array)Hash::extract($this->_attachments, '{s}.contentId'));
1454:         $hasInlineAttachments = count($contentIds) > 0;
1455:         $hasAttachments = !empty($this->_attachments);
1456:         $hasMultipleTypes = count($rendered) > 1;
1457: 
1458:         $boundary = $relBoundary = $textBoundary = $this->_boundary;
1459: 
1460:         if ($hasInlineAttachments) {
1461:             $msg[] = '--' . $boundary;
1462:             $msg[] = 'Content-Type: multipart/related; boundary="rel-' . $boundary . '"';
1463:             $msg[] = '';
1464:             $relBoundary = $textBoundary = 'rel-' . $boundary;
1465:         }
1466: 
1467:         if ($hasMultipleTypes) {
1468:             $msg[] = '--' . $relBoundary;
1469:             $msg[] = 'Content-Type: multipart/alternative; boundary="alt-' . $boundary . '"';
1470:             $msg[] = '';
1471:             $textBoundary = 'alt-' . $boundary;
1472:         }
1473: 
1474:         if (isset($rendered['text'])) {
1475:             if ($textBoundary !== $boundary || $hasAttachments) {
1476:                 $msg[] = '--' . $textBoundary;
1477:                 $msg[] = 'Content-Type: text/plain; charset=' . $this->_getContentTypeCharset();
1478:                 $msg[] = 'Content-Transfer-Encoding: ' . $this->_getContentTransferEncoding();
1479:                 $msg[] = '';
1480:             }
1481:             $this->_textMessage = $rendered['text'];
1482:             $content = explode("\n", $this->_textMessage);
1483:             $msg = array_merge($msg, $content);
1484:             $msg[] = '';
1485:         }
1486: 
1487:         if (isset($rendered['html'])) {
1488:             if ($textBoundary !== $boundary || $hasAttachments) {
1489:                 $msg[] = '--' . $textBoundary;
1490:                 $msg[] = 'Content-Type: text/html; charset=' . $this->_getContentTypeCharset();
1491:                 $msg[] = 'Content-Transfer-Encoding: ' . $this->_getContentTransferEncoding();
1492:                 $msg[] = '';
1493:             }
1494:             $this->_htmlMessage = $rendered['html'];
1495:             $content = explode("\n", $this->_htmlMessage);
1496:             $msg = array_merge($msg, $content);
1497:             $msg[] = '';
1498:         }
1499: 
1500:         if ($hasMultipleTypes) {
1501:             $msg[] = '--' . $textBoundary . '--';
1502:             $msg[] = '';
1503:         }
1504: 
1505:         if ($hasInlineAttachments) {
1506:             $attachments = $this->_attachInlineFiles($relBoundary);
1507:             $msg = array_merge($msg, $attachments);
1508:             $msg[] = '';
1509:             $msg[] = '--' . $relBoundary . '--';
1510:             $msg[] = '';
1511:         }
1512: 
1513:         if ($hasAttachments) {
1514:             $attachments = $this->_attachFiles($boundary);
1515:             $msg = array_merge($msg, $attachments);
1516:         }
1517:         if ($hasAttachments || $hasMultipleTypes) {
1518:             $msg[] = '';
1519:             $msg[] = '--' . $boundary . '--';
1520:             $msg[] = '';
1521:         }
1522:         return $msg;
1523:     }
1524: 
1525: /**
1526:  * Gets the text body types that are in this email message
1527:  *
1528:  * @return array Array of types. Valid types are 'text' and 'html'
1529:  */
1530:     protected function _getTypes() {
1531:         $types = array($this->_emailFormat);
1532:         if ($this->_emailFormat === 'both') {
1533:             $types = array('html', 'text');
1534:         }
1535:         return $types;
1536:     }
1537: 
1538: /**
1539:  * Build and set all the view properties needed to render the templated emails.
1540:  * If there is no template set, the $content will be returned in a hash
1541:  * of the text content types for the email.
1542:  *
1543:  * @param string $content The content passed in from send() in most cases.
1544:  * @return array The rendered content with html and text keys.
1545:  */
1546:     protected function _renderTemplates($content) {
1547:         $types = $this->_getTypes();
1548:         $rendered = array();
1549:         if (empty($this->_template)) {
1550:             foreach ($types as $type) {
1551:                 $rendered[$type] = $this->_encodeString($content, $this->charset);
1552:             }
1553:             return $rendered;
1554:         }
1555:         $viewClass = $this->_viewRender;
1556:         if ($viewClass !== 'View') {
1557:             list($plugin, $viewClass) = pluginSplit($viewClass, true);
1558:             $viewClass .= 'View';
1559:             App::uses($viewClass, $plugin . 'View');
1560:         }
1561: 
1562:         $View = new $viewClass(null);
1563:         $View->viewVars = $this->_viewVars;
1564:         $View->helpers = $this->_helpers;
1565: 
1566:         list($templatePlugin, $template) = pluginSplit($this->_template);
1567:         list($layoutPlugin, $layout) = pluginSplit($this->_layout);
1568:         if ($templatePlugin) {
1569:             $View->plugin = $templatePlugin;
1570:         } elseif ($layoutPlugin) {
1571:             $View->plugin = $layoutPlugin;
1572:         }
1573:         if ($this->_theme) {
1574:             $View->theme = $this->_theme;
1575:         }
1576:         // Convert null to false, as View needs false to disable
1577:         // the layout.
1578:         if ($layout === null) {
1579:             $layout = false;
1580:         }
1581: 
1582:         foreach ($types as $type) {
1583:             $View->set('content', $content);
1584:             $View->hasRendered = false;
1585:             $View->viewPath = $View->layoutPath = 'Emails' . DS . $type;
1586: 
1587:             $render = $View->render($template, $layout);
1588:             $render = str_replace(array("\r\n", "\r"), "\n", $render);
1589:             $rendered[$type] = $this->_encodeString($render, $this->charset);
1590:         }
1591: 
1592:         foreach ($rendered as $type => $content) {
1593:             $rendered[$type] = $this->_wrap($content);
1594:             $rendered[$type] = implode("\n", $rendered[$type]);
1595:             $rendered[$type] = rtrim($rendered[$type], "\n");
1596:         }
1597:         return $rendered;
1598:     }
1599: 
1600: /**
1601:  * Return the Content-Transfer Encoding value based on the set charset
1602:  *
1603:  * @return void
1604:  */
1605:     protected function _getContentTransferEncoding() {
1606:         $charset = strtoupper($this->charset);
1607:         if (in_array($charset, $this->_charset8bit)) {
1608:             return '8bit';
1609:         }
1610:         return '7bit';
1611:     }
1612: 
1613: /**
1614:  * Return charset value for Content-Type.
1615:  *
1616:  * Checks fallback/compatibility types which include workarounds
1617:  * for legacy japanese character sets.
1618:  *
1619:  * @return string
1620:  */
1621:     protected function _getContentTypeCharset() {
1622:         $charset = strtoupper($this->charset);
1623:         if (array_key_exists($charset, $this->_contentTypeCharset)) {
1624:             return strtoupper($this->_contentTypeCharset[$charset]);
1625:         }
1626:         return strtoupper($this->charset);
1627:     }
1628: 
1629: }
1630: 
OpenHub
Rackspace
Rackspace
  • Business Solutions
  • Showcase
  • Documentation
  • Book
  • API
  • Videos
  • Reporting Security Issues
  • Privacy Policy
  • Logos & Trademarks
  • Community
  • Get Involved
  • Issues (GitHub)
  • Bakery
  • Featured Resources
  • Training
  • Meetups
  • My CakePHP
  • CakeFest
  • Newsletter
  • Linkedin
  • YouTube
  • Facebook
  • Twitter
  • Mastodon
  • Help & Support
  • Forum
  • Stack Overflow
  • Slack
  • Paid Support

Generated using CakePHP API Docs