/** * SendMail. * * @return bool True if it was sent or pushed */ public function sendMail() { try { $emailTo = $this->emailTo; $nameTo = $this->nameTo; if ($this->userTo) { $user = $this->UserModel; if (!$user->id) { $this->status = 'invalid'; $this->save(); $message = "Sending an email to an invalid user <{$this->userTo}> with action <{$this->mailAction}>."; Log::notifySlack(":bangbang: {$message}"); return false; } if (empty($emailTo)) { $emailTo = $user->email; } if (empty($nameTo)) { if (!empty($user->name)) { $nameTo = $user->name . ' ' . $user->surname; } elseif (!empty($user->first_name)) { $nameTo = $user->first_name . ' ' . $user->last_name; } } } else { $user = false; } if (empty($emailTo)) { $this->status = 'invalid'; $this->save(); $with = $this->id ? "with id <{$this->id}> and" : 'with'; $message = "Sending an email without email address {$with} action <{$this->mailAction}>."; Log::notifySlack(":bangbang: {$message}"); return false; } $mail = new \Quaver\App\Model\Mail(); $objMail = $mail->getFromAction($this->mailAction, $user ? $user->language : Config::get('core.LANG')); if (empty($objMail)) { $this->status = 'invalid'; $this->save(); $message = "An invalid mail action <{$this->mailAction}>."; Log::notifySlack(":bangbang: {$message}"); return false; } if ($this->getBlock()) { return $this->push(); } $title = $objMail->title; $template = htmlspecialchars_decode($objMail->template, ENT_QUOTES); $actionSender = $this->getActionSender($user); $this->status = 'sending'; if ($this->exists()) { // Avoid concurrency with two cron at the same time $statusChange = $this->getFieldChange('status'); if ($statusChange) { $this->once('preSave', function ($carry, $event, $stopPropagation) { $stopPropagation(); $model = $event['target']; $db = new DB(); $sql = "UPDATE {$this->table} SET status = 'sending' WHERE id = ? AND status != 'sending'"; $result = $db->query($sql, $this->id); return (bool) $result->rowCount(); }); } if (!$statusChange || !$this->save(['status'])) { throw new \LengthException("Concurrency detected sending the mail {$this->id}"); } } // Update all possible changes and send the mail if ($this->save()) { try { $ret = parent::send($subject = $actionSender->renderSubject($title), $body = \Quaver\App\Model\Mail::getMailTemplate($actionSender->renderBody($template)), $emailTo, $nameTo, $this->emailFrom, $this->nameFrom, $this->bcc, $actionSender->getAttachments(), $actionSender->getReplyTo()); } catch (\Mandrill_Error $e) { $ret = false; } catch (QException $e) { $with = $this->id ? "with id <{$this->id}>," : 'with'; $objects = json_encode($this->mailObjects); $message = "An unexpected mail error occurred {$with} email <{$emailTo}>, action <{$this->mailAction}> and objects <{$objects}>.\n"; $message .= get_class($e) . ' - ' . $e->getMessage(); Log::notifySlack(":bangbang: {$message}"); $ret = false; } if (!empty($ret)) { $this->mandrill = $ret[0]['_id']; $this->status = in_array($ret[0]['status'], ['rejected', 'invalid']) ? $ret[0]['status'] : 'sent'; $this->titleSent = $subject; $this->bodySent = $body; $this->sent = date('Y-m-d H:i:s'); if ($this->save()) { $actionSender->onSent($this); } } else { // Fallback if (++$this->retries >= self::MAX_RETRIES) { $this->status = 'timeout'; $this->save(); $objects = json_encode($this->mailObjects); $message = "A mailcenter timeout error occurred {$with} email <{$emailTo}>, action <{$this->mailAction}> and objects <{$objects}>."; Log::notifySlack(":bangbang: {$message}"); } else { return $this->push(); } } } else { $message = "An unexpected error occurred updating the mail {$this->id} before sending.\n"; Log::notifySlack(":bangbang: {$message}"); } } catch (QException $e) { $with = $this->id ? "with id <{$this->id}>," : 'with'; $objects = json_encode($this->mailObjects); $message = "An unexpected mail error occurred {$with} action <{$this->mailAction}> and objects <{$objects}>.\n"; $message .= get_class($e) . ' - ' . $e->getMessage(); Log::notifySlack(":bangbang: {$message}"); } return $this->status === 'sent'; }