diff --git a/.travis.yml b/.travis.yml index a905b36..99ff95a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,7 +18,7 @@ before_script: - tests/unit/data/travis/cubrid-setup.sh script: - - phpunit --coverage-clover tests/unit/runtime/coveralls/clover.xml --verbose --exclude-group mssql,oci,wincache,xcache,zenddata + - phpunit --coverage-clover tests/unit/runtime/coveralls/clover.xml --verbose --exclude-group mssql,oci,wincache,xcache,zenddata,vendor after_script: - php vendor/bin/coveralls diff --git a/extensions/swiftmailer/LICENSE.md b/extensions/swiftmailer/LICENSE.md new file mode 100644 index 0000000..0bb1a8d --- /dev/null +++ b/extensions/swiftmailer/LICENSE.md @@ -0,0 +1,32 @@ +The Yii framework is free software. It is released under the terms of +the following BSD License. + +Copyright © 2008-2013 by Yii Software LLC (http://www.yiisoft.com) +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + * Neither the name of Yii Software LLC nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/extensions/swiftmailer/README.md b/extensions/swiftmailer/README.md new file mode 100644 index 0000000..bb04a83 --- /dev/null +++ b/extensions/swiftmailer/README.md @@ -0,0 +1,58 @@ +Yii 2.0 Public Preview - SwiftMailer Mail Solution +================================================== + +Thank you for choosing Yii - a high-performance component-based PHP framework. + +If you are looking for a production-ready PHP framework, please use +[Yii v1.1](https://github.com/yiisoft/yii). + +Yii 2.0 is still under heavy development. We may make significant changes +without prior notices. **Yii 2.0 is not ready for production use yet.** + +[![Build Status](https://secure.travis-ci.org/yiisoft/yii2.png)](http://travis-ci.org/yiisoft/yii2) + +This is the yii2-swiftmailer extension. + + +Installation +------------ + +The preferred way to install this extension is through [composer](http://getcomposer.org/download/). + +Either run +``` +php composer.phar require yiisoft/yii2-swiftmailer "*" +``` + +or add +```json +"yiisoft/yii2-swiftmailer": "*" +``` +to the require section of your composer.json. + + +*Note: You might have to run `php composer.phar selfupdate`* + + +Usage & Documentation +--------------------- + +This extension has to be registered prior to usage. +To enable this view renderer add it to the $rendereres property of your view object. + +Example: + +```php + [ + 'mail' => [ + 'class' => 'yii\swiftmailer\Mailer', + ], + ], +]; +``` + +For further instructions refer to the related section in the yii guide. diff --git a/extensions/swiftmailer/composer.json b/extensions/swiftmailer/composer.json new file mode 100644 index 0000000..bbab7eb --- /dev/null +++ b/extensions/swiftmailer/composer.json @@ -0,0 +1,28 @@ +{ + "name": "yiisoft/yii2-swiftmailer", + "description": "The SwiftMailer integration for the Yii framework", + "keywords": ["yii", "swift", "swiftmailer", "mail", "email", "mailer"], + "type": "yii2-extension", + "license": "BSD-3-Clause", + "support": { + "issues": "https://github.com/yiisoft/yii2/issues?state=open", + "forum": "http://www.yiiframework.com/forum/", + "wiki": "http://www.yiiframework.com/wiki/", + "irc": "irc://irc.freenode.net/yii", + "source": "https://github.com/yiisoft/yii2" + }, + "authors": [ + { + "name": "Paul Klimov", + "email": "klimov.paul@gmail.com" + } + ], + "minimum-stability": "dev", + "require": { + "yiisoft/yii2": "*", + "swiftmailer/swiftmailer": "@stable" + }, + "autoload": { + "psr-0": { "yii\\swiftmailer\\": "" } + } +} diff --git a/extensions/swiftmailer/yii/swiftmailer/Mailer.php b/extensions/swiftmailer/yii/swiftmailer/Mailer.php new file mode 100644 index 0000000..1d89290 --- /dev/null +++ b/extensions/swiftmailer/yii/swiftmailer/Mailer.php @@ -0,0 +1,142 @@ + array( + * ... + * 'email' => array( + * 'class' => 'yii\swiftmailer\Mailer', + * 'transport' => [ + * 'class' => 'Swift_SmtpTransport', + * 'host' => 'localhost', + * 'username' => 'username', + * 'password' => 'password', + * 'port' => '587', + * 'encryption' => 'tls', + * ], + * ), + * ... + * ), + * ~~~ + * + * @see http://swiftmailer.org + * + * @method Message compose($view = null, array $params = []) creates new message optionally filling up its body via view rendering. + * + * @author Paul Klimov + * @since 2.0 + */ +class Mailer extends BaseMailer +{ + /** + * @var string message default class name. + */ + public $messageClass = 'yii\swiftmailer\Message'; + /** + * @var \Swift_Mailer Swift mailer instance. + */ + private $_swiftMailer; + /** + * @var \Swift_Transport|array Swift transport instance or its array configuration. + */ + private $_transport = []; + + /** + * @return array|\Swift_Mailer Swift mailer instance or array configuration. + */ + public function getSwiftMailer() + { + if (!is_object($this->_swiftMailer)) { + $this->_swiftMailer = $this->createSwiftMailer(); + } + return $this->_swiftMailer; + } + + /** + * @param array|\Swift_Transport $transport + * @throws \yii\base\InvalidConfigException on invalid argument. + */ + public function setTransport($transport) + { + if (!is_array($transport) && !is_object($transport)) { + throw new InvalidConfigException('"' . get_class($this) . '::transport" should be either object or array, "' . gettype($transport) . '" given.'); + } + $this->_transport = $transport; + } + + /** + * @return array|\Swift_Transport + */ + public function getTransport() + { + if (!is_object($this->_transport)) { + $this->_transport = $this->createTransport($this->_transport); + } + return $this->_transport; + } + + /** + * @inheritdoc + */ + public function send($message) + { + Yii::trace('Sending email message', __METHOD__); + return ($this->getSwiftMailer()->send($message->getSwiftMessage()) > 0); + } + + /** + * Creates Swift mailer instance. + * @return \Swift_Mailer mailer instance. + */ + protected function createSwiftMailer() + { + return \Swift_Mailer::newInstance($this->getTransport()); + } + + /** + * Creates email transport instance by its array configuration. + * @param array $config transport configuration. + * @throws \yii\base\InvalidConfigException on invalid transport configuration. + * @return \Swift_Transport transport instance. + */ + protected function createTransport(array $config) + { + if (array_key_exists('class', $config)) { + $className = $config['class']; + unset($config['class']); + } else { + $className = 'Swift_MailTransport'; + } + $transport = call_user_func([$className, 'newInstance']); + if (!empty($config)) { + foreach ($config as $name => $value) { + if (property_exists($transport, $name)) { + $transport->$name = $value; + } else { + $setter = 'set' . $name; + if (method_exists($transport, $setter)) { + $transport->$setter($value); + } else { + throw new InvalidConfigException('Setting unknown property: ' . get_class($transport) . '::' . $name); + } + } + } + } + return $transport; + } +} \ No newline at end of file diff --git a/extensions/swiftmailer/yii/swiftmailer/Message.php b/extensions/swiftmailer/yii/swiftmailer/Message.php new file mode 100644 index 0000000..8997695 --- /dev/null +++ b/extensions/swiftmailer/yii/swiftmailer/Message.php @@ -0,0 +1,237 @@ + + * @since 2.0 + */ +class Message extends BaseMessage +{ + /** + * @var \Swift_Message Swift message instance. + */ + private $_swiftMessage; + + /** + * @return \Swift_Message Swift message instance. + */ + public function getSwiftMessage() + { + if (!is_object($this->_swiftMessage)) { + $this->_swiftMessage = $this->createSwiftMessage(); + } + return $this->_swiftMessage; + } + + /** + * @inheritdoc + */ + public function charset($charset) + { + $this->getSwiftMessage()->setCharset($charset); + return $this; + } + + /** + * @inheritdoc + */ + public function from($from) + { + $this->getSwiftMessage()->setFrom($from); + $this->getSwiftMessage()->setReplyTo($from); + return $this; + } + + /** + * @inheritdoc + */ + public function to($to) + { + $this->getSwiftMessage()->setTo($to); + return $this; + } + + /** + * @inheritdoc + */ + public function cc($cc) + { + $this->getSwiftMessage()->setCc($cc); + return $this; + } + + /** + * @inheritdoc + */ + public function bcc($bcc) + { + $this->getSwiftMessage()->setBcc($bcc); + return $this; + } + + /** + * @inheritdoc + */ + public function subject($subject) + { + $this->getSwiftMessage()->setSubject($subject); + return $this; + } + + /** + * @inheritdoc + */ + public function textBody($text) + { + $this->setBody($text, 'text/plain'); + return $this; + } + + /** + * @inheritdoc + */ + public function htmlBody($html) + { + $this->setBody($html, 'text/html'); + return $this; + } + + /** + * Sets the message body. + * If body is already set and its content type matches given one, it will + * be overridden, if content type miss match the multipart message will be composed. + * @param string $body body content. + * @param string $contentType body content type. + */ + protected function setBody($body, $contentType) + { + $message = $this->getSwiftMessage(); + $oldBody = $message->getBody(); + if (empty($oldBody)) { + $parts = $message->getChildren(); + $partFound = false; + foreach ($parts as $key => $part) { + if (!($part instanceof \Swift_Mime_Attachment)) { + /* @var $part \Swift_Mime_MimePart */ + if ($part->getContentType() == $contentType) { + unset($parts[$key]); + $partFound = true; + break; + } + } + } + if ($partFound) { + reset($parts); + $message->setChildren($parts); + $message->addPart($body, $contentType); + } else { + $message->setBody($body, $contentType); + } + } else { + $oldContentType = $message->getContentType(); + if ($oldContentType == $contentType) { + $message->setBody($body, $contentType); + } else { + $message->setBody(null); + $message->setContentType(null); + $message->addPart($oldBody, $oldContentType); + $message->addPart($body, $contentType); + } + } + } + + /** + * @inheritdoc + */ + public function attach($fileName, array $options = []) + { + $attachment = \Swift_Attachment::fromPath($fileName); + if (!empty($options['fileName'])) { + $attachment->setFilename($options['fileName']); + } + if (!empty($options['contentType'])) { + $attachment->setContentType($options['contentType']); + } + $this->getSwiftMessage()->attach($attachment); + return $this; + } + + /** + * @inheritdoc + */ + public function attachContent($content, array $options = []) + { + $attachment = \Swift_Attachment::newInstance($content); + if (!empty($options['fileName'])) { + $attachment->setFilename($options['fileName']); + } + if (!empty($options['contentType'])) { + $attachment->setContentType($options['contentType']); + } + $this->getSwiftMessage()->attach($attachment); + return $this; + } + + /** + * @inheritdoc + */ + public function embed($fileName, array $options = []) + { + $embedFile = \Swift_EmbeddedFile::fromPath($fileName); + if (!empty($options['fileName'])) { + $embedFile->setFilename($options['fileName']); + } + if (!empty($options['contentType'])) { + $embedFile->setContentType($options['contentType']); + } + return $this->getSwiftMessage()->embed($embedFile); + } + + /** + * @inheritdoc + */ + public function embedContent($content, array $options = []) + { + $embedFile = \Swift_EmbeddedFile::newInstance($content); + if (!empty($options['fileName'])) { + $embedFile->setFilename($options['fileName']); + } + if (!empty($options['contentType'])) { + $embedFile->setContentType($options['contentType']); + } + return $this->getSwiftMessage()->embed($embedFile); + } + + /** + * @inheritdoc + */ + public function toString() + { + return $this->getSwiftMessage()->toString(); + } + + /** + * Creates the Swift email message instance. + * @return \Swift_Message email message instance. + */ + protected function createSwiftMessage() + { + return new \Swift_Message(); + } +} \ No newline at end of file diff --git a/framework/yii/mail/BaseMailer.php b/framework/yii/mail/BaseMailer.php new file mode 100644 index 0000000..9636e63 --- /dev/null +++ b/framework/yii/mail/BaseMailer.php @@ -0,0 +1,212 @@ + + * @since 2.0 + */ +abstract class BaseMailer extends Component implements MailerInterface, ViewContextInterface +{ + /** + * @var \yii\base\View|array view instance or its array configuration. + */ + private $_view = []; + /** + * @var string directory containing view files for this email messages. + */ + public $viewPath = '@app/mails'; + /** + * @var string HTML layout view name. + */ + public $htmlLayout = 'layouts/html'; + /** + * @var string text layout view name. + */ + public $textLayout = 'layouts/text'; + /** + * @var array configuration, which should be applied by default to any new created + * email message instance. + * In addition to normal [[Yii::createObject()]] behavior extra config keys are available: + * - 'charset' argument for [[MessageInterface::charset()]] + * - 'from' argument for [[MessageInterface::from()]] + * - 'to' argument for [[MessageInterface::to()]] + * - 'cc' argument for [[MessageInterface::cc()]] + * - 'bcc' argument for [[MessageInterface::bcc()]] + * - 'subject' argument for [[MessageInterface::subject()]] + * - 'textBody' argument for [[MessageInterface::textBody()]] + * - 'htmlBody' argument for [[MessageInterface::htmlBody()]] + * For example: + * ~~~ + * array( + * 'charset' => 'UTF-8', + * 'from' => 'noreply@mydomain.com', + * 'bcc' => 'developer@mydomain.com', + * ) + * ~~~ + */ + public $messageConfig = []; + /** + * @var string message default class name. + */ + public $messageClass = 'yii\mail\BaseMessage'; + + /** + * @param array|\yii\base\View $view view instance or its array configuration. + * @throws \yii\base\InvalidConfigException on invalid argument. + */ + public function setView($view) + { + if (!is_array($view) && !is_object($view)) { + throw new InvalidConfigException('"' . get_class($this) . '::view" should be either object or array, "' . gettype($view) . '" given.'); + } + $this->_view = $view; + } + + /** + * @return \yii\base\View view instance. + */ + public function getView() + { + if (!is_object($this->_view)) { + $this->_view = $this->createView($this->_view); + } + return $this->_view; + } + + /** + * Creates view instance from given configuration. + * @param array $config view configuration. + * @return \yii\base\View view instance. + */ + protected function createView(array $config) + { + if (!array_key_exists('class', $config)) { + $config['class'] = 'yii\web\View'; + } + return Yii::createObject($config); + } + + /** + * @inheritdoc + */ + public function compose($view = null, array $params = []) + { + $message = $this->createMessage(); + if ($view !== null) { + $params['message'] = $message; + if (is_array($view)) { + if (array_key_exists('html', $view)) { + $message->htmlBody($this->render($view['html'], $params, $this->htmlLayout)); + } + if (array_key_exists('text', $view)) { + $message->textBody($this->render($view['text'], $params, $this->textLayout)); + } + } else { + $html = $this->render($view, $params, $this->htmlLayout); + $message->htmlBody($html); + $message->textBody(strip_tags($html)); + } + } + return $message; + } + + /** + * Creates mew message instance using configuration from [[messageConfig]]. + * If 'class' parameter is omitted, [[messageClass]] will be used. + * @return MessageInterface message instance. + */ + protected function createMessage() + { + $config = $this->messageConfig; + if (!array_key_exists('class', $config)) { + $config['class'] = $this->messageClass; + } + $directSetterNames = [ + 'charset', + 'from', + 'to', + 'cc', + 'bcc', + 'subject', + 'textBody', + 'htmlBody', + ]; + $directSetterConfig = []; + foreach ($config as $name => $value) { + if (in_array($name, $directSetterNames, true)) { + $directSetterConfig[$name] = $value; + unset($config[$name]); + } + } + $message = Yii::createObject($config); + foreach ($directSetterConfig as $name => $value) { + $message->$name($value); + } + return $message; + } + + /** + * Sends a couple of messages at once. + * Note: some particular mailers may benefit from sending messages as batch, + * saving resources, for example on open/close connection operations, + * they may override this method to create their specific implementation. + * @param array $messages list of email messages, which should be sent. + * @return integer number of successful sends. + */ + public function sendMultiple(array $messages) + { + $successCount = 0; + foreach ($messages as $message) { + if ($this->send($message)) { + $successCount++; + } + } + return $successCount; + } + + /** + * Renders a view. + * @param string $view the view name or the path alias of the view file. + * @param array $params the parameters (name-value pairs) that will be extracted and made available in the view file. + * @param string|boolean $layout layout view name, if false given no layout will be applied. + * @return string the rendering result. + */ + public function render($view, $params = [], $layout = false) + { + $output = $this->getView()->render($view, $params, $this); + if ($layout !== false) { + return $this->getView()->render($layout, ['content' => $output], $this); + } else { + return $output; + } + } + + /** + * Finds the view file corresponding to the specified relative view name. + * @param string $view a relative view name. The name does NOT start with a slash. + * @return string the view file path. Note that the file may not exist. + */ + public function findViewFile($view) + { + return Yii::getAlias($this->viewPath) . DIRECTORY_SEPARATOR . $view; + } +} \ No newline at end of file diff --git a/framework/yii/mail/BaseMessage.php b/framework/yii/mail/BaseMessage.php new file mode 100644 index 0000000..9a6fccf --- /dev/null +++ b/framework/yii/mail/BaseMessage.php @@ -0,0 +1,61 @@ + + * @since 2.0 + */ +abstract class BaseMessage extends Object implements MessageInterface +{ + /** + * @return \yii\mail\BaseMailer mailer component instance. + */ + public function getMailer() + { + return Yii::$app->getComponent('mail'); + } + + /** + * @inheritdoc + */ + public function send() + { + return $this->getMailer()->send($this); + } + + /** + * PHP magic method that returns the string representation of this object. + * @return string the string representation of this object. + */ + public function __toString() + { + // __toString cannot throw exception + // use trigger_error to bypass this limitation + try { + return $this->toString(); + } catch (\Exception $e) { + trigger_error($e->getMessage()); + return ''; + } + } +} \ No newline at end of file diff --git a/framework/yii/mail/MailerInterface.php b/framework/yii/mail/MailerInterface.php new file mode 100644 index 0000000..bf2210c --- /dev/null +++ b/framework/yii/mail/MailerInterface.php @@ -0,0 +1,58 @@ +mail->compose('contact/html', ['contactForm' => $form]) + * ->from('from@domain.com') + * ->to($form->email) + * ->subject($form->subject) + * ->send(); + * ~~~ + * + * @see MessageInterface + * + * @author Paul Klimov + * @since 2.0 + */ +interface MailerInterface +{ + /** + * Creates new message optionally filling up its body via view rendering. + * The view to be rendered can be specified in one of the following formats: + * - path alias (e.g. "@app/mails/contact/body"); + * - relative path (e.g. "contact"): the actual view file will be resolved by [[\yii\base\ViewContextInterface]]. + * @param string|array $view view, which should be used to render message body + * - if string - the view name or the path alias of the HTML body view file, in this case + * text body will be composed automatically from html one. + * - if array - list of views for each body type in format: ['html' => 'htmlView', 'text' => 'textView'] + * @param array $params the parameters (name-value pairs) that will be extracted and made available in the view file. + * @return MessageInterface message instance. + */ + public function compose($view = null, array $params = []); + + /** + * Sends the given email message. + * @param object $message email message instance + * @return boolean whether the message has been sent. + */ + public function send($message); + + /** + * Sends a couple of messages at once. + * Note: some particular mailers may benefit from sending messages as batch, + * saving resources, for example on open/close connection operations. + * @param array $messages list of email messages, which should be sent. + * @return integer number of successful sends. + */ + public function sendMultiple(array $messages); +} \ No newline at end of file diff --git a/framework/yii/mail/MessageInterface.php b/framework/yii/mail/MessageInterface.php new file mode 100644 index 0000000..648e6ff --- /dev/null +++ b/framework/yii/mail/MessageInterface.php @@ -0,0 +1,152 @@ +mail->compose() + * ->from('from@domain.com') + * ->to('to@domain.com') + * ->subject('Message Subject') + * ->textBody('Plain text content') + * ->htmlBody('HTML content') + * ->send(); + * ~~~ + * + * @see MailerInterface + * + * @author Paul Klimov + * @since 2.0 + */ +interface MessageInterface +{ + /** + * Set the character set of this message. + * @param string $charset character set name. + * @return static self reference. + */ + public function charset($charset); + + /** + * Sets message sender. + * @param string|array $from sender email address. + * You may pass an array of addresses if this message is from multiple people. + * You may also specify sender name in addition to email address using format: + * [email => name]. + * @return static self reference. + */ + public function from($from); + + /** + * Sets message receiver. + * @param string|array $to receiver email address. + * You may pass an array of addresses if multiple recipients should receive this message. + * You may also specify receiver name in addition to email address using format: + * [email => name]. + * @return static self reference. + */ + public function to($to); + + /** + * Set the Cc (additional copy receiver) addresses of this message. + * @param string|array $cc copy receiver email address. + * You may pass an array of addresses if multiple recipients should receive this message. + * You may also specify receiver name in addition to email address using format: + * [email => name]. + * @return static self reference. + */ + public function cc($cc); + + /** + * Set the Bcc (hidden copy receiver) addresses of this message. + * @param string|array $bcc hidden copy receiver email address. + * You may pass an array of addresses if multiple recipients should receive this message. + * You may also specify receiver name in addition to email address using format: + * [email => name]. + * @return static self reference. + */ + public function bcc($bcc); + + /** + * Sets message subject. + * @param string $subject message subject + * @return static self reference. + */ + public function subject($subject); + + /** + * Sets message plain text content. + * @param string $text message plain text content. + * @return static self reference. + */ + public function textBody($text); + + /** + * Sets message HTML content. + * @param string $html message HTML content. + * @return static self reference. + */ + public function htmlBody($html); + + /** + * Attaches existing file to the email message. + * @param string $fileName full file name + * @param array $options options for embed file. Valid options are: + * - fileName: name, which should be used to attach file. + * - contentType: attached file MIME type. + * @return static self reference. + */ + public function attach($fileName, array $options = []); + + /** + * Attach specified content as file for the email message. + * @param string $content attachment file content. + * @param array $options options for embed file. Valid options are: + * - fileName: name, which should be used to attach file. + * - contentType: attached file MIME type. + * @return static self reference. + */ + public function attachContent($content, array $options = []); + + /** + * Attach a file and return it's CID source. + * This method should be used when embedding images or other data in a message. + * @param string $fileName file name. + * @param array $options options for embed file. Valid options are: + * - fileName: name, which should be used to attach file. + * - contentType: attached file MIME type. + * @return string attachment CID. + */ + public function embed($fileName, array $options = []); + + /** + * Attach a content as file and return it's CID source. + * This method should be used when embedding images or other data in a message. + * @param string $content attachment file content. + * @param array $options options for embed file. Valid options are: + * - fileName: name, which should be used to attach file. + * - contentType: attached file MIME type. + * @return string attachment CID. + */ + public function embedContent($content, array $options = []); + + /** + * Sends this email message. + * @return boolean success. + */ + public function send(); + + /** + * Returns string representation of this message. + * @return string the string representation of this message. + */ + public function toString(); +} \ No newline at end of file diff --git a/tests/unit/VendorTestCase.php b/tests/unit/VendorTestCase.php new file mode 100644 index 0000000..d633d02 --- /dev/null +++ b/tests/unit/VendorTestCase.php @@ -0,0 +1,30 @@ +mockApplication([ + 'components' => [ + 'email' => $this->createTestEmailComponent() + ] + ]); + } + + /** + * @return Mailer test email component instance. + */ + protected function createTestEmailComponent() + { + $component = new Mailer(); + return $component; + } + + // Tests : + + public function testSetupTransport() + { + $mailer = new Mailer(); + + $transport = \Swift_MailTransport::newInstance(); + $mailer->setTransport($transport); + $this->assertEquals($transport, $mailer->getTransport(), 'Unable to setup transport!'); + } + + /** + * @depends testSetupTransport + */ + public function testConfigureTransport() + { + $mailer = new Mailer(); + + $transportConfig = [ + 'class' => 'Swift_SmtpTransport', + 'host' => 'localhost', + ]; + $mailer->setTransport($transportConfig); + $transport = $mailer->getTransport(); + $this->assertTrue(is_object($transport), 'Unable to setup transport via config!'); + $this->assertEquals($transportConfig['class'], get_class($transport), 'Invalid transport class!'); + $this->assertEquals($transportConfig['host'], $transport->getHost(), 'Invalid transport host!'); + } + + public function testGetSwiftMailer() + { + $mailer = new Mailer(); + $this->assertTrue(is_object($mailer->getSwiftMailer()), 'Unable to get Swift mailer instance!'); + } +} \ No newline at end of file diff --git a/tests/unit/extensions/swiftmailer/MessageTest.php b/tests/unit/extensions/swiftmailer/MessageTest.php new file mode 100644 index 0000000..33eff29 --- /dev/null +++ b/tests/unit/extensions/swiftmailer/MessageTest.php @@ -0,0 +1,311 @@ +mockApplication([ + 'components' => [ + 'mail' => $this->createTestEmailComponent() + ] + ]); + $filePath = $this->getTestFilePath(); + if (!file_exists($filePath)) { + FileHelper::createDirectory($filePath); + } + } + + public function tearDown() + { + $filePath = $this->getTestFilePath(); + if (file_exists($filePath)) { + FileHelper::removeDirectory($filePath); + } + } + + /** + * @return string test file path. + */ + protected function getTestFilePath() + { + return Yii::getAlias('@yiiunit/runtime') . DIRECTORY_SEPARATOR . basename(get_class($this)) . '_' . getmypid(); + } + + /** + * @return Mailer test email component instance. + */ + protected function createTestEmailComponent() + { + $component = new Mailer(); + return $component; + } + + /** + * @return Message test message instance. + */ + protected function createTestMessage() + { + return Yii::$app->getComponent('mail')->compose(); + } + + /** + * Creates image file with given text. + * @param string $fileName file name. + * @param string $text text to be applied on image. + * @return string image file full name. + */ + protected function createImageFile($fileName = 'test.jpg', $text = 'Test Image') + { + if (!function_exists('imagecreatetruecolor')) { + $this->markTestSkipped('GD lib required.'); + } + $fileFullName = $this->getTestFilePath() . DIRECTORY_SEPARATOR . $fileName; + $image = imagecreatetruecolor(120, 20); + $textColor = imagecolorallocate($image, 233, 14, 91); + imagestring($image, 1, 5, 5, $text, $textColor); + imagejpeg($image, $fileFullName); + imagedestroy($image); + return $fileFullName; + } + + /** + * Finds the attachment object in the message. + * @param Message $message message instance + * @return null|\Swift_Mime_Attachment attachment instance. + */ + protected function getAttachment(Message $message) + { + $messageParts = $message->getSwiftMessage()->getChildren(); + $attachment = null; + foreach ($messageParts as $part) { + if ($part instanceof \Swift_Mime_Attachment) { + $attachment = $part; + break; + } + } + return $attachment; + } + + // Tests : + + public function testGetSwiftMessage() + { + $message = new Message(); + $this->assertTrue(is_object($message->getSwiftMessage()), 'Unable to get Swift message!'); + } + + /** + * @depends testGetSwiftMessage + */ + public function testSetupHeaders() + { + $charset = 'utf-16'; + $subject = 'Test Subject'; + $to = 'someuser@somedomain.com'; + $cc = 'ccuser@somedomain.com'; + $bcc = 'bccuser@somedomain.com'; + + $messageString = $this->createTestMessage() + ->charset($charset) + ->subject($subject) + ->to($to) + ->cc($cc) + ->bcc($bcc) + ->toString(); + + $this->assertContains('charset=' . $charset, $messageString, 'Incorrect charset!'); + $this->assertContains('Subject: ' . $subject, $messageString, 'Incorrect "Subject" header!'); + $this->assertContains('To: ' . $to, $messageString, 'Incorrect "To" header!'); + $this->assertContains('Cc: ' . $cc, $messageString, 'Incorrect "Cc" header!'); + $this->assertContains('Bcc: ' . $bcc, $messageString, 'Incorrect "Bcc" header!'); + } + + /** + * @depends testGetSwiftMessage + */ + public function testSetupFrom() + { + $from = 'someuser@somedomain.com'; + $messageString = $this->createTestMessage() + ->from($from) + ->toString(); + $this->assertContains('From: ' . $from, $messageString, 'Incorrect "From" header!'); + $this->assertContains('Reply-To: ' . $from, $messageString, 'Incorrect "Reply-To" header!'); + } + + /** + * @depends testGetSwiftMessage + */ + public function testSend() + { + $message = $this->createTestMessage(); + $message->to($this->testEmailReceiver); + $message->from('someuser@somedomain.com'); + $message->subject('Yii Swift Test'); + $message->textBody('Yii Swift Test body'); + $this->assertTrue($message->send()); + } + + /** + * @depends testSend + */ + public function testAttachFile() + { + $message = $this->createTestMessage(); + + $message->to($this->testEmailReceiver); + $message->from('someuser@somedomain.com'); + $message->subject('Yii Swift Attach File Test'); + $message->textBody('Yii Swift Attach File Test body'); + $fileName = __FILE__; + $message->attach($fileName); + + $this->assertTrue($message->send()); + + $attachment = $this->getAttachment($message); + $this->assertTrue(is_object($attachment), 'No attachment found!'); + $this->assertContains($attachment->getFilename(), $fileName, 'Invalid file name!'); + } + + /** + * @depends testSend + */ + public function testAttachContent() + { + $message = $this->createTestMessage(); + + $message->to($this->testEmailReceiver); + $message->from('someuser@somedomain.com'); + $message->subject('Yii Swift Create Attachment Test'); + $message->textBody('Yii Swift Create Attachment Test body'); + $fileName = 'test.txt'; + $fileContent = 'Test attachment content'; + $message->attachContent($fileContent, ['fileName' => $fileName]); + + $this->assertTrue($message->send()); + + $attachment = $this->getAttachment($message); + $this->assertTrue(is_object($attachment), 'No attachment found!'); + $this->assertEquals($fileName, $attachment->getFilename(), 'Invalid file name!'); + } + + /** + * @depends testSend + */ + public function testEmbedFile() + { + $fileName = $this->createImageFile('embed_file.jpg', 'Embed Image File'); + + $message = $this->createTestMessage(); + + $cid = $message->embed($fileName); + + $message->to($this->testEmailReceiver); + $message->from('someuser@somedomain.com'); + $message->subject('Yii Swift Embed File Test'); + $message->htmlBody('Embed image: pic'); + + $this->assertTrue($message->send()); + + $attachment = $this->getAttachment($message); + $this->assertTrue(is_object($attachment), 'No attachment found!'); + $this->assertContains($attachment->getFilename(), $fileName, 'Invalid file name!'); + } + + /** + * @depends testSend + */ + public function testEmbedContent() + { + $fileFullName = $this->createImageFile('embed_file.jpg', 'Embed Image File'); + $message = $this->createTestMessage(); + + $fileName = basename($fileFullName); + $contentType = 'image/jpeg'; + $fileContent = file_get_contents($fileFullName); + + $cid = $message->embedContent($fileContent, ['fileName' => $fileName, 'contentType' => $contentType]); + + $message->to($this->testEmailReceiver); + $message->from('someuser@somedomain.com'); + $message->subject('Yii Swift Embed File Test'); + $message->htmlBody('Embed image: pic'); + + $this->assertTrue($message->send()); + + $attachment = $this->getAttachment($message); + $this->assertTrue(is_object($attachment), 'No attachment found!'); + $this->assertEquals($fileName, $attachment->getFilename(), 'Invalid file name!'); + $this->assertEquals($contentType, $attachment->getContentType(), 'Invalid content type!'); + } + + /** + * @depends testSend + */ + public function testSendAlternativeBody() + { + $message = $this->createTestMessage(); + + $message->to($this->testEmailReceiver); + $message->from('someuser@somedomain.com'); + $message->subject('Yii Swift Alternative Body Test'); + $message->htmlBody('Yii Swift test HTML body'); + $message->textBody('Yii Swift test plain text body'); + + $this->assertTrue($message->send()); + + $messageParts = $message->getSwiftMessage()->getChildren(); + $textPresent = false; + $htmlPresent = false; + foreach ($messageParts as $part) { + if (!($part instanceof \Swift_Mime_Attachment)) { + /* @var $part \Swift_Mime_MimePart */ + if ($part->getContentType() == 'text/plain') { + $textPresent = true; + } + if ($part->getContentType() == 'text/html') { + $htmlPresent = true; + } + } + } + $this->assertTrue($textPresent, 'No text!'); + $this->assertTrue($htmlPresent, 'No HTML!'); + } + + /** + * @depends testGetSwiftMessage + */ + public function testSerialize() + { + $message = $this->createTestMessage(); + + $message->to($this->testEmailReceiver); + $message->from('someuser@somedomain.com'); + $message->subject('Yii Swift Alternative Body Test'); + $message->textBody('Yii Swift test plain text body'); + + $serializedMessage = serialize($message); + $this->assertNotEmpty($serializedMessage, 'Unable to serialize message!'); + + $unserializedMessaage = unserialize($serializedMessage); + $this->assertEquals($message, $unserializedMessaage, 'Unable to unserialize message!'); + } +} \ No newline at end of file diff --git a/tests/unit/framework/mail/BaseMailerTest.php b/tests/unit/framework/mail/BaseMailerTest.php new file mode 100644 index 0000000..4eab7e1 --- /dev/null +++ b/tests/unit/framework/mail/BaseMailerTest.php @@ -0,0 +1,303 @@ +mockApplication([ + 'components' => [ + 'mail' => $this->createTestMailComponent(), + ] + ]); + $filePath = $this->getTestFilePath(); + if (!file_exists($filePath)) { + FileHelper::createDirectory($filePath); + } + } + + public function tearDown() + { + $filePath = $this->getTestFilePath(); + if (file_exists($filePath)) { + FileHelper::removeDirectory($filePath); + } + } + + /** + * @return string test file path. + */ + protected function getTestFilePath() + { + return Yii::getAlias('@yiiunit/runtime') . DIRECTORY_SEPARATOR . basename(get_class($this)) . '_' . getmypid(); + } + + /** + * @return Mailer test email component instance. + */ + protected function createTestMailComponent() + { + $component = new Mailer(); + $component->viewPath = $this->getTestFilePath(); + return $component; + } + + /** + * @return Mailer mailer instance + */ + protected function getTestMailComponent() + { + return Yii::$app->getComponent('mail'); + } + + // Tests : + + public function testSetupView() + { + $mailer = new Mailer(); + + $view = new View(); + $mailer->setView($view); + $this->assertEquals($view, $mailer->getView(), 'Unable to setup view!'); + + $viewConfig = [ + 'params' => [ + 'param1' => 'value1', + 'param2' => 'value2', + ] + ]; + $mailer->setView($viewConfig); + $view = $mailer->getView(); + $this->assertTrue(is_object($view), 'Unable to setup view via config!'); + $this->assertEquals($viewConfig['params'], $view->params, 'Unable to configure view via config array!'); + } + + /** + * @depends testSetupView + */ + public function testGetDefaultView() + { + $mailer = new Mailer(); + $view = $mailer->getView(); + $this->assertTrue(is_object($view), 'Unable to get default view!'); + } + + public function testCreateMessage() + { + $mailer = new Mailer(); + $message = $mailer->compose(); + $this->assertTrue(is_object($message), 'Unable to create message instance!'); + $this->assertEquals($mailer->messageClass, get_class($message), 'Invalid message class!'); + } + + /** + * @depends testCreateMessage + */ + public function testDefaultMessageConfig() + { + $mailer = new Mailer(); + + $notPropertyConfig = [ + 'charset' => 'utf-16', + 'from' => 'from@domain.com', + 'to' => 'to@domain.com', + 'cc' => 'cc@domain.com', + 'bcc' => 'bcc@domain.com', + 'subject' => 'Test subject', + 'textBody' => 'Test text body', + 'htmlBody' => 'Test HTML body', + ]; + $propertyConfig = [ + 'id' => 'test-id', + 'encoding' => 'test-encoding', + ]; + $messageConfig = array_merge($notPropertyConfig, $propertyConfig); + $mailer->messageConfig = $messageConfig; + + $message = $mailer->compose(); + + foreach ($notPropertyConfig as $name => $value) { + $this->assertEquals($value, $message->{'_' . $name}); + } + foreach ($propertyConfig as $name => $value) { + $this->assertEquals($value, $message->$name); + } + } + + /** + * @depends testGetDefaultView + */ + public function testRender() + { + $mailer = $this->getTestMailComponent(); + + $viewName = 'test_view'; + $viewFileName = $this->getTestFilePath() . DIRECTORY_SEPARATOR . $viewName . '.php'; + $viewFileContent = ''; + file_put_contents($viewFileName, $viewFileContent); + + $params = [ + 'testParam' => 'test output' + ]; + $renderResult = $mailer->render($viewName, $params); + $this->assertEquals($params['testParam'], $renderResult); + } + + /** + * @depends testRender + */ + public function testRenderLayout() + { + $mailer = $this->getTestMailComponent(); + + $filePath = $this->getTestFilePath(); + + $viewName = 'test_view'; + $viewFileName = $filePath . DIRECTORY_SEPARATOR . $viewName . '.php'; + $viewFileContent = 'view file content'; + file_put_contents($viewFileName, $viewFileContent); + + $layoutName = 'test_layout'; + $layoutFileName = $filePath . DIRECTORY_SEPARATOR . $layoutName . '.php'; + $layoutFileContent = 'Begin Layout End Layout'; + file_put_contents($layoutFileName, $layoutFileContent); + + $renderResult = $mailer->render($viewName, [], $layoutName); + $this->assertEquals('Begin Layout ' . $viewFileContent . ' End Layout', $renderResult); + } + + /** + * @depends testCreateMessage + * @depends testRender + */ + public function testCompose() + { + $mailer = $this->getTestMailComponent(); + $mailer->htmlLayout = false; + $mailer->textLayout = false; + + $htmlViewName = 'test_html_view'; + $htmlViewFileName = $this->getTestFilePath() . DIRECTORY_SEPARATOR . $htmlViewName . '.php'; + $htmlViewFileContent = 'HTML view file content'; + file_put_contents($htmlViewFileName, $htmlViewFileContent); + + $textViewName = 'test_text_view'; + $textViewFileName = $this->getTestFilePath() . DIRECTORY_SEPARATOR . $textViewName . '.php'; + $textViewFileContent = 'Plain text view file content'; + file_put_contents($textViewFileName, $textViewFileContent); + + $message = $mailer->compose([ + 'html' => $htmlViewName, + 'text' => $textViewName, + ]); + $this->assertEquals($htmlViewFileContent, $message->_htmlBody, 'Unable to render html!'); + $this->assertEquals($textViewFileContent, $message->_textBody, 'Unable to render text!'); + + $message = $mailer->compose($htmlViewName); + $this->assertEquals($htmlViewFileContent, $message->_htmlBody, 'Unable to render html by direct view!'); + $this->assertEquals(strip_tags($htmlViewFileContent), $message->_textBody, 'Unable to render text by direct view!'); + } +} + +/** + * Test Mailer class + */ +class Mailer extends BaseMailer +{ + public $messageClass = 'yiiunit\framework\mail\Message'; + public $sentMessages = []; + + public function send($message) + { + $this->sentMessages[] = $message; + } +} + +/** + * Test Message class + */ +class Message extends BaseMessage +{ + public $id; + public $encoding; + public $_charset; + public $_from; + public $_to; + public $_cc; + public $_bcc; + public $_subject; + public $_textBody; + public $_htmlBody; + + public function charset($charset) + { + $this->_charset = $charset; + return $this; + } + + public function from($from) + { + $this->_from = $from; + return $this; + } + + public function to($to) + { + $this->_to = $to; + return $this; + } + + public function cc($cc) + { + $this->_cc = $cc; + return $this; + } + + public function bcc($bcc) + { + $this->_bcc = $bcc; + return $this; + } + + public function subject($subject) + { + $this->_subject = $subject; + return $this; + } + + public function textBody($text) + { + $this->_textBody = $text; + return $this; + } + + public function htmlBody($html) + { + $this->_htmlBody = $html; + return $this; + } + + public function attachContent($content, array $options = []) {} + + public function attach($fileName, array $options = []) {} + + public function embed($fileName, array $options = []) {} + + public function embedContent($content, array $options = []) {} + + public function toString() + { + return get_class($this); + } +} \ No newline at end of file diff --git a/tests/unit/framework/mail/BaseMessageTest.php b/tests/unit/framework/mail/BaseMessageTest.php new file mode 100644 index 0000000..1820764 --- /dev/null +++ b/tests/unit/framework/mail/BaseMessageTest.php @@ -0,0 +1,120 @@ +mockApplication([ + 'components' => [ + 'mail' => $this->createTestEmailComponent() + ] + ]); + } + + /** + * @return Mailer test email component instance. + */ + protected function createTestEmailComponent() + { + $component = new TestMailer(); + return $component; + } + + /** + * @return TestMailer mailer instance. + */ + protected function getMailer() + { + return Yii::$app->getComponent('mail'); + } + + // Tests : + + public function testGetMailer() + { + $mailer = $this->getMailer(); + $message = $mailer->compose(); + $this->assertEquals($mailer, $message->getMailer()); + } + + public function testSend() + { + $mailer = $this->getMailer(); + $message = $mailer->compose(); + $message->send(); + $this->assertEquals($message, $mailer->sentMessages[0], 'Unable to send message!'); + } + + public function testToString() + { + $mailer = $this->getMailer(); + $message = $mailer->compose(); + $this->assertEquals($message->toString(), '' . $message); + } +} + +/** + * Test Mailer class + */ +class TestMailer extends BaseMailer +{ + public $messageClass = 'yiiunit\framework\mail\TestMessage'; + public $sentMessages = array(); + + public function send($message) + { + $this->sentMessages[] = $message; + } +} + +/** + * Test Message class + */ +class TestMessage extends BaseMessage +{ + public $text; + public $html; + + public function charset($charset) {} + + public function from($from) {} + + public function to($to) {} + + public function cc($cc) {} + + public function bcc($bcc) {} + + public function subject($subject) {} + + public function textBody($text) { + $this->text = $text; + } + + public function htmlBody($html) { + $this->html = $html; + } + + public function attachContent($content, array $options = []) {} + + public function attach($fileName, array $options = []) {} + + public function embed($fileName, array $options = []) {} + + public function embedContent($content, array $options = []) {} + + public function toString() + { + return get_class($this); + } +} \ No newline at end of file