| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545 | <?phpnamespace Grav\Plugin\Email;use Grav\Common\Config\Config;use Grav\Common\Grav;use Grav\Common\Language\Language;use Grav\Common\Markdown\Parsedown;use Grav\Common\Twig\Twig;use Grav\Framework\Form\Interfaces\FormInterface;use \Monolog\Logger;use \Monolog\Handler\StreamHandler;class Email{    /**     * @var \Swift_Transport     */    protected $mailer;    /**     * @var \Swift_Plugins_LoggerPlugin     */    protected $logger;    protected $queue_path;    /**     * Returns true if emails have been enabled in the system.     *     * @return bool     */    public static function enabled()    {        return Grav::instance()['config']->get('plugins.email.mailer.engine') !== 'none';    }    /**     * Returns true if debugging on emails has been enabled.     *     * @return bool     */    public static function debug()    {        return Grav::instance()['config']->get('plugins.email.debug') == 'true';    }    /**     * Creates an email message.     *     * @param string $subject     * @param string $body     * @param string $contentType     * @param string $charset     * @return \Swift_Message     */    public function message($subject = null, $body = null, $contentType = null, $charset = null)    {        return new \Swift_Message($subject, $body, $contentType, $charset);    }    /**     * Creates an attachment.     *     * @param string $data     * @param string $filename     * @param string $contentType     * @return \Swift_Attachment     */    public function attachment($data = null, $filename = null, $contentType = null)    {        return new \Swift_Attachment($data, $filename, $contentType);    }    /**     * Creates an embedded attachment.     *     * @param string $data     * @param string $filename     * @param string $contentType     * @return \Swift_EmbeddedFile     */    public function embedded($data = null, $filename = null, $contentType = null)    {        return new \Swift_EmbeddedFile($data, $filename, $contentType);    }    /**     * Creates an image attachment.     *     * @param string $data     * @param string $filename     * @param string $contentType     * @return \Swift_Image     */    public function image($data = null, $filename = null, $contentType = null)    {        return new \Swift_Image($data, $filename, $contentType);    }    /**     * Send email.     *     * @param \Swift_Message $message     * @return int     */    public function send($message)    {        $mailer = $this->getMailer();        $result = $mailer ? $mailer->send($message) : 0;        // Check if emails and debugging are both enabled.        if ($mailer && $this->debug()) {            $log = new Logger('email');            $locator = Grav::instance()['locator'];            $log_file = $locator->findResource('log://email.log', true, true);            $log->pushHandler(new StreamHandler($log_file, Logger::DEBUG));            // Append the SwiftMailer log to the log.            $log->addDebug($this->getLogs());        }        return $result;    }    /**     * Build e-mail message.     *     * @param array $params     * @param array $vars     * @return \Swift_Message     */    public function buildMessage(array $params, array $vars = [])    {        /** @var Twig $twig */        $twig = Grav::instance()['twig'];        /** @var Config $config */        $config = Grav::instance()['config'];        /** @var Language $language */        $language = Grav::instance()['language'];        // Extend parameters with defaults.        $params += [            'bcc' => $config->get('plugins.email.bcc', []),            'body' => $config->get('plugins.email.body', '{% include "forms/data.html.twig" %}'),            'cc' => $config->get('plugins.email.cc', []),            'cc_name' => $config->get('plugins.email.cc_name'),            'charset' =>  $config->get('plugins.email.charset', 'utf-8'),            'from' => $config->get('plugins.email.from'),            'from_name' => $config->get('plugins.email.from_name'),            'content_type' => $config->get('plugins.email.content_type', 'text/html'),            'reply_to' => $config->get('plugins.email.reply_to', []),            'reply_to_name' => $config->get('plugins.email.reply_to_name'),            'subject' => !empty($vars['form']) && $vars['form'] instanceof FormInterface ? $vars['form']->page()->title() : null,            'to' => $config->get('plugins.email.to'),            'to_name' => $config->get('plugins.email.to_name'),            'process_markdown' => false,            'template' => false        ];        // Create message object.        $message = $this->message();        if (!$params['to']) {            throw new \RuntimeException($language->translate('PLUGIN_EMAIL.PLEASE_CONFIGURE_A_TO_ADDRESS'));        }        if (!$params['from']) {            throw new \RuntimeException($language->translate('PLUGIN_EMAIL.PLEASE_CONFIGURE_A_FROM_ADDRESS'));        }        // Process parameters.        foreach ($params as $key => $value) {            switch ($key) {                case 'body':                    if (is_string($value)) {                        $body = $twig->processString($value, $vars);                        if ($params['process_markdown']) {                            $parsedown = new Parsedown();                            $body = $parsedown->text($body);                        }                        if ($params['template']) {                            $vars = array_merge($vars, ['content' => $body]);                            $body = $twig->processTemplate($params['template'], $vars);                        }                        $content_type = !empty($params['content_type']) ? $twig->processString($params['content_type'], $vars) : null;                        $charset = !empty($params['charset']) ? $twig->processString($params['charset'], $vars) : null;                        $message->setBody($body, $content_type, $charset);                    }                    elseif (is_array($value)) {                        foreach ($value as $body_part) {                            $body_part += [                                'charset' => $params['charset'],                                'content_type' => $params['content_type'],                            ];                            $body = !empty($body_part['body']) ? $twig->processString($body_part['body'], $vars) : null;                            if ($params['process_markdown']) {                                $parsedown = new Parsedown();                                $body = $parsedown->text($body);                            }                            $content_type = !empty($body_part['content_type']) ? $twig->processString($body_part['content_type'], $vars) : null;                            $charset = !empty($body_part['charset']) ? $twig->processString($body_part['charset'], $vars) : null;                            if (!$message->getBody()) {                                $message->setBody($body, $content_type, $charset);                            }                            else {                                $message->addPart($body, $content_type, $charset);                            }                        }                    }                    break;                case 'subject':                    $message->setSubject($twig->processString($language->translate($value), $vars));                    break;                case 'to':                    if (is_string($value) && !empty($params['to_name'])) {                        $value = [                            'mail' => $twig->processString($value, $vars),                            'name' => $twig->processString($params['to_name'], $vars),                        ];                    }                    foreach ($this->parseAddressValue($value, $vars) as $address) {                        $message->addTo($address->mail, $address->name);                    }                    break;                case 'cc':                    if (is_string($value) && !empty($params['cc_name'])) {                        $value = [                            'mail' => $twig->processString($value, $vars),                            'name' => $twig->processString($params['cc_name'], $vars),                        ];                    }                    foreach ($this->parseAddressValue($value, $vars) as $address) {                        $message->addCc($address->mail, $address->name);                    }                    break;                case 'bcc':                    foreach ($this->parseAddressValue($value, $vars) as $address) {                        $message->addBcc($address->mail, $address->name);                    }                    break;                case 'from':                    if (is_string($value) && !empty($params['from_name'])) {                        $value = [                            'mail' => $twig->processString($value, $vars),                            'name' => $twig->processString($params['from_name'], $vars),                        ];                    }                    foreach ($this->parseAddressValue($value, $vars) as $address) {                        $message->addFrom($address->mail, $address->name);                    }                    break;                case 'reply_to':                    if (is_string($value) && !empty($params['reply_to_name'])) {                        $value = [                            'mail' => $twig->processString($value, $vars),                            'name' => $twig->processString($params['reply_to_name'], $vars),                        ];                    }                    foreach ($this->parseAddressValue($value, $vars) as $address) {                        $message->addReplyTo($address->mail, $address->name);                    }                    break;            }        }        return $message;    }    /**     * Return parsed e-mail address value.     *     * @param string|string[] $value     * @param array $vars     * @return array     */    public function parseAddressValue($value, array $vars = [])    {        $parsed = [];        /** @var Twig $twig */        $twig = Grav::instance()['twig'];        // Single e-mail address string        if (is_string($value)) {            $parsed[] = (object) [                'mail' => $twig->processString($value, $vars),                'name' => null,            ];        }        else {            // Cast value as array            $value = (array) $value;            // Single e-mail address array            if (!empty($value['mail'])) {                $parsed[] = (object) [                    'mail' => $twig->processString($value['mail'], $vars),                    'name' => !empty($value['name']) ? $twig->processString($value['name'], $vars) : NULL,                ];            }            // Multiple addresses (either as strings or arrays)            elseif (!(empty($value['mail']) && !empty($value['name']))) {                foreach ($value as $y => $itemx) {                    $addresses = $this->parseAddressValue($itemx, $vars);                    if (($address = reset($addresses))) {                        $parsed[] = $address;                    }                }            }        }        return $parsed;    }    /**     * Return debugging logs if enabled     *     * @return string     */    public function getLogs()    {        if ($this->debug()) {            return $this->logger->dump();        }        return '';    }    /**     * @internal     * @return null|\Swift_Mailer     */    protected function getMailer()    {        if (!$this->enabled()) {            return null;        }        if (!$this->mailer) {            /** @var Config $config */            $config = Grav::instance()['config'];            $queue_enabled = $config->get('plugins.email.queue.enabled');            $transport = $queue_enabled === true ? $this->getQueue() : $this->getTransport();            // Create the Mailer using your created Transport            $this->mailer = new \Swift_Mailer($transport);            // Register the logger if we're debugging.            if ($this->debug()) {                $this->logger = new \Swift_Plugins_Loggers_ArrayLogger();                $this->mailer->registerPlugin(new \Swift_Plugins_LoggerPlugin($this->logger));            }        }        return $this->mailer;    }    protected static function getQueuePath()    {        $queue_path = Grav::instance()['locator']->findResource('user://data', true) . '/email-queue';        if (!file_exists($queue_path)) {            mkdir($queue_path);        }        return $queue_path;    }    protected static function getQueue()    {        $queue_path = static::getQueuePath();        $spool = new \Swift_FileSpool($queue_path);        $transport = new \Swift_SpoolTransport($spool);        return $transport;    }    public static function flushQueue()    {        $grav = Grav::instance();        $grav['debugger']->enabled(false);        $config = $grav['config']->get('plugins.email.queue');        $queue = static::getQueue();        $spool = $queue->getSpool();        $spool->setMessageLimit($config['flush_msg_limit']);        $spool->setTimeLimit($config['flush_time_limit']);        try {            $failures = [];            $result = $spool->flushQueue(static::getTransport(), $failures);            return $result . ' messages flushed from queue...';        } catch (\Exception $e) {            $grav['log']->error($e->getMessage());            return $e->getMessage();        }    }    public static function clearQueueFailures()    {        $grav = Grav::instance();        $grav['debugger']->enabled(false);        $preferences = \Swift_Preferences::getInstance();        $preferences->setTempDir(sys_get_temp_dir());        /** @var \Swift_Transport $transport */        $transport = static::getTransport();        if (!$transport->isStarted()) {            $transport->start();        }        $queue_path = static::getQueuePath();        foreach (new \GlobIterator($queue_path . '/*.sending') as $file) {            $final_message = $file->getPathname();            /** @var \Swift_Message $message */            $message = unserialize(file_get_contents($final_message));            echo(sprintf(                'Retrying "%s" to "%s"',                $message->getSubject(),                implode(', ', array_keys($message->getTo()))            ) . "\n");            try {                $clean = static::cloneMessage($message);                $transport->send($clean);                echo("sent!\n");                // DOn't want to trip up any errors from sending too fast                sleep(1);            } catch (\Swift_TransportException $e) {                echo("ERROR: Send failed - deleting spooled message\n");            }            // Remove the file            unlink($final_message);        }    }    /**     * Clean copy a message     *     * @param \Swift_Message $message     */    public static function cloneMessage($message)    {        $clean = new \Swift_Message();        $clean->setBoundary($message->getBoundary());        $clean->setBcc($message->getBcc());        $clean->setBody($message->getBody());        $clean->setCharset($message->getCharset());        $clean->setChildren($message->getChildren());        $clean->setContentType($message->getContentType());        $clean->setCc($message->getCc());        $clean->setDate($message->getDate());        $clean->setDescription($message->getDescription());        $clean->setEncoder($message->getEncoder());        $clean->setFormat($message->getFormat());        $clean->setFrom($message->getFrom());        $clean->setId($message->getId());        $clean->setMaxLineLength($message->getMaxLineLength());        $clean->setPriority($message->getPriority());        $clean->setReplyTo($message->getReplyTo());        $clean->setReturnPath($message->getReturnPath());        $clean->setSender($message->getSender());        $clean->setSubject($message->getSubject());        $clean->setTo($message->getTo());        return $clean;    }    protected static function getTransport()    {        /** @var Config $config */        $config = Grav::instance()['config'];        $engine = $config->get('plugins.email.mailer.engine');        // Create the Transport and initialize it.        switch ($engine) {            case 'smtp':                $transport = new \Swift_SmtpTransport();                $options = $config->get('plugins.email.mailer.smtp');                if (!empty($options['server'])) {                    $transport->setHost($options['server']);                }                if (!empty($options['port'])) {                    $transport->setPort($options['port']);                }                if (!empty($options['encryption']) && $options['encryption'] !== 'none') {                    $transport->setEncryption($options['encryption']);                }                if (!empty($options['user'])) {                    $transport->setUsername($options['user']);                }                if (!empty($options['password'])) {                    $transport->setPassword($options['password']);                }                break;            case 'sendmail':            default:                $options = $config->get('plugins.email.mailer.sendmail');                $bin = !empty($options['bin']) ? $options['bin'] : '/usr/sbin/sendmail';                $transport = new \Swift_SendmailTransport($bin);                break;        }        return $transport;    }}
 |