Use Object-Oriented approach for mail templates

This commit is contained in:
Dmitriy Simushev 2014-12-10 11:50:57 +00:00
parent e0716bbeb2
commit 17b7bf6757
10 changed files with 335 additions and 203 deletions

View File

@ -20,6 +20,7 @@
namespace Mibew\Controller\Chat;
use Mibew\Http\Exception\NotFoundException;
use Mibew\Mail\Template as MailTemplate;
use Mibew\Mail\Utils as MailUtils;
use Mibew\Settings;
use Mibew\Thread;
@ -114,23 +115,22 @@ class MailController extends AbstractController
}
// Load mail templates and substitute placeholders there.
$mail_template = mail_template_load('user_history', get_current_locale());
$mail_template = MailTemplate::loadByName('user_history', get_current_locale());
if (!$mail_template) {
throw new \RuntimeException('Cannot load "user_history" mail template');
}
$body = str_replace(
array('{0}', '{1}', '{2}', '{3}'),
array(
$this->sendMail(MailUtils::buildMessage(
$email,
MIBEW_MAILBOX,
$mail_template->buildSubject(),
$mail_template->buildBody(array(
$thread->userName,
$history,
Settings::get('title'),
Settings::get('hosturl'),
),
$mail_template['body']
);
$this->sendMail(MailUtils::buildMessage($email, MIBEW_MAILBOX, $mail_template['subject'], $body));
))
));
$page = setup_logo($group);
$page['email'] = $email;

View File

@ -19,6 +19,8 @@
namespace Mibew\Controller;
use Mibew\Http\Exception\NotFoundException;
use Mibew\Mail\Template as MailTemplate;
use Symfony\Component\HttpFoundation\Request;
/**
@ -84,12 +86,15 @@ class MailTemplateController extends AbstractController
'errors' => $request->attributes->get('errors', array()),
);
$template = $this->loadMailTemplate($template_name, $lang);
$template = MailTemplate::loadByName($template_name, $lang);
if (!$template) {
throw new NotFoundException('The template is not found');
}
// Use values from the request or the default ones if they are not
// available.
$page['formsubject'] = $request->request->get('subject', $template['subject']);
$page['formbody'] = $request->request->get('body', $template['body']);
$page['formsubject'] = $request->request->get('subject', $template->subject);
$page['formbody'] = $request->request->get('body', $template->body);
$page['formname'] = $template_name;
$page['formlang'] = $lang;
@ -140,9 +145,17 @@ class MailTemplateController extends AbstractController
return $this->showEditFormAction($request);
}
// Save the template and redirect the operator to the page with mail
// templates list.
mail_template_save($name, $lang, $subject, $body);
// Get the instance of mail template that should be modified.
$template = MailTemplate::loadByName($name, $lang, true);
if (!$template) {
// The template cannot be loaded. Create a new one.
$template = new MailTemplate($name, $lang);
}
$template->subject = $subject;
$template->body = $body;
$template->save();
$redirect_to = $this->generateUrl(
'mail_templates',
array(
@ -184,7 +197,7 @@ class MailTemplateController extends AbstractController
*/
protected function loadMailTemplate($name, $locale)
{
$template = mail_template_load($name, $locale);
$template = MailTemplate::loadByName($name, $locale);
if (!$template) {
throw new \RuntimeException(sprintf('Cannot load "%s" mail template', $name));

View File

@ -20,6 +20,7 @@
namespace Mibew\Controller;
use Mibew\Http\Exception\BadRequestException;
use Mibew\Mail\Template as MailTemplate;
use Mibew\Mail\Utils as MailUtils;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
@ -87,18 +88,20 @@ class PasswordRecoveryController extends AbstractController
);
// Load mail templates and substitute placeholders there.
$mail_template = mail_template_load('password_recovery', get_current_locale());
$mail_template = MailTemplate::loadByName('password_recovery', get_current_locale());
if (!$mail_template) {
throw new \RuntimeException('Cannot load "password_recovery" mail template');
}
$body = str_replace(
array('{0}', '{1}'),
array(get_operator_name($to_restore), $href),
$mail_template['body']
);
$this->sendMail(MailUtils::buildMessage($email, $email, $mail_template['subject'], $body));
$this->sendMail(MailUtils::buildMessage(
$email,
$email,
$mail_template->buildSubject(),
$mail_template->buildBody(array(
get_operator_name($to_restore),
$href,
))
));
$page['isdone'] = true;
return $this->render('password_recovery', $page);

View File

@ -0,0 +1,241 @@
<?php
/*
* This file is a part of Mibew Messenger.
*
* Copyright 2005-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace Mibew\Mail;
use Mibew\Database;
/**
* A class that represents Mail Template.
*/
class Template
{
/**
* Unique template ID.
*
* @var int|bool
*/
public $id;
/**
* Locale code the template belongs to.
*
* @var string
*/
public $locale;
/**
* Machine name of the template.
*
* @var string
*/
public $name;
/**
* E-mail subject.
*
* @var string
*/
public $subject;
/**
* E-mail body.
*
* @var string
*/
public $body;
/**
* Loads template by its ID.
*
* @param int $id ID of the template to load
* @return boolean|Ban Returns a Template instance or boolean false on
* failure.
*/
public static function load($id)
{
// Check $id
if (empty($id)) {
return false;
}
$template_info = Database::getInstance()->query(
"SELECT * FROM {mailtemplate} WHERE templateid = :id",
array(':id' => $id),
array('return_rows' => Database::RETURN_ONE_ROW)
);
// There is no template with such id in database
if (!$template_info) {
return false;
}
return self::buildFromDbFields($template_info);
}
/**
* Loads template by its machine name and locale.
*
* @param string $name Name of the template to load.
* @param string $locale Locale of the template to load.
* @param boolean $strict Indicates if only specified locale should be used.
* If the argument is set to false and there is no template for
* specified locale a template for default locale will be loaded.
* @return boolean|Ban Returns a Template instance or boolean false on
* failure.
*/
public static function loadByName($name, $locale, $strict = false)
{
// Try to load the template from the database.
$template_info = Database::getInstance()->query(
"SELECT * FROM {mailtemplate} WHERE name = :name AND locale = :locale",
array(
':name' => $name,
':locale' => $locale,
),
array('return_rows' => Database::RETURN_ONE_ROW)
);
if ($template_info) {
// The template exists in the database.
return self::buildFromDbFields($template_info);
}
if ($strict) {
// There is no appropriate template in the database and we cannot
// use one for default locale.
return false;
}
// Try to load a template with specified name for the default locale.
if ($locale != get_default_locale()) {
return self::loadByName($name, get_default_locale());
}
// There is no template with specified name neither for specified locale
// nor for the default one.
return false;
}
/**
* Class constructor.
*
* @param string $name Machine name of the template.
* @param string $locale A locale name the template belongs to.
*
* @return Template
*/
public function __construct($name, $locale)
{
// Set default values
$this->id = false;
$this->locale = $locale;
$this->name = $name;
$this->subject = '';
$this->body = '';
}
/**
* Saves the template to database.
*/
public function save()
{
$db = Database::getInstance();
if (!$this->id) {
// This template is new.
$db->query(
('INSERT INTO {mailtemplate} (locale, name, subject, body) '
. 'VALUES (:locale, :name, :subject, :body)'),
array(
':locale' => $this->locale,
':name' => $this->name,
':subject' => $this->subject,
':body' => $this->body,
)
);
$this->id = $db->insertedId();
} else {
// Update the existing template
$db->query(
('UPDATE {mailtemplate} SET locale = :locale, name = :name, '
. 'subject = :subject, body = :body WHERE templateid = :id'),
array(
':id' => $this->id,
':locale' => $this->locale,
':name' => $this->name,
':subject' => $this->subject,
':body' => $this->body,
)
);
}
}
/**
* Builds e-mail subject by replacing all placeholders with specified
* values.
*
* @param array $params List of values that should replace subject's
* placeholders.
* @return string Ready to use e-mail subject.
*/
public function buildSubject($params = array())
{
$subject = $this->subject;
for ($i = 0; $i < count($params); $i++) {
$subject = str_replace("{" . $i . "}", $params[$i], $subject);
}
return $subject;
}
/**
* Builds e-mail body by replacing all placeholders with specified values.
*
* @param array $params List of values that should replace body's
* placeholders.
* @return string Ready to use e-mail body.
*/
public function buildBody($params = array())
{
$body = $this->body;
for ($i = 0; $i < count($params); $i++) {
$body = str_replace("{" . $i . "}", $params[$i], $body);
}
return $body;
}
/**
* Builds and instance of Template based on fields from Database.
*
* @param array $db_fields Associative array of database fields which keys
* are fields names and the values are fields values.
* @return Template
*/
protected static function buildFromDbFields($db_fields)
{
$template = new self($db_fields['name'], $db_fields['locale']);
$template->id = $db_fields['templateid'];
$template->subject = $db_fields['subject'];
$template->body = $db_fields['body'];
return $template;
}
}

View File

@ -19,6 +19,8 @@
namespace Mibew\Mail;
use Symfony\Component\Yaml\Parser as YamlParser;
/**
* Contains a set of utility methods related with emails.
*/
@ -47,6 +49,45 @@ class Utils
->setBody(preg_replace("/\n/", "\r\n", $body));
}
/**
* Imports mail templates from a YAML file.
*
* @param string $locale Locale code.
* @param string $file Full path to the file that should be imported.
*/
public static function importTemplates($locale, $file)
{
// Get new mail templates
$parser = new YamlParser();
$new_templates = $parser->parse(file_get_contents($file));
if (empty($new_templates)) {
// Nothing to import.
return;
}
foreach ($new_templates as $name => $template_info) {
// Validate the template
$is_valid_template = is_array($template_info)
&& array_key_exists('subject', $template_info)
&& array_key_exists('body', $template_info);
if (!$is_valid_template) {
throw new \RuntimeException(sprintf(
'An invalid mail template "%s" is found in "%s".',
$name,
$file
));
}
if (!Template::loadByName($name, $locale, true)) {
// Import only templates that are not already in the database.
$template = new Template($name, $locale);
$template->subject = $template_info['subject'];
$template->body = $template_info['body'];
$template->save();
}
}
}
/**
* This class should not be instantiated
*/

View File

@ -20,6 +20,7 @@
namespace Mibew\Maintenance;
use Mibew\Database;
use Mibew\Mail\Utils as MailUtils;
use Symfony\Component\Yaml\Parser as YamlParser;
/**
@ -731,7 +732,7 @@ class Installer
$mail_templates_file = MIBEW_FS_ROOT . '/locales/' . $locale
. '/mail_templates.yml';
if (is_readable($mail_templates_file)) {
import_mail_templates($locale, $mail_templates_file);
MailUtils::importTemplates($locale, $mail_templates_file);
}
// Mark the locale as "enabled" to indicate that all its content

View File

@ -25,6 +25,7 @@ use Mibew\Authentication\AuthenticationManagerInterface;
use Mibew\Http\Exception\AccessDeniedException;
use Mibew\Mail\MailerFactoryAwareInterface;
use Mibew\Mail\MailerFactoryInterface;
use Mibew\Mail\Template as MailTemplate;
use Mibew\Mail\Utils as MailUtils;
use Mibew\Settings;
use Mibew\Thread;
@ -789,25 +790,18 @@ class ThreadProcessor extends ClientSideProcessor implements
// Send email
if ($inbox_mail) {
// Prepare message to send by email
$mail_template = mail_template_load('leave_message', $message_locale);
$mail_template = MailTemplate::loadByName('leave_message', $message_locale);
if (!$mail_template) {
throw new \RuntimeException('Cannot load "leave_message" mail template');
}
$subject = str_replace(
'{0}',
$args['name'],
$mail_template['subject']
);
$body = str_replace(
array('{0}', '{1}', '{2}', '{3}'),
array(
$subject = $mail_template->buildSubject(array($args['name']));
$body = $mail_template->buildBody(array(
$args['name'],
$email,
$message,
($info ? $info . "\n" : "")
),
$mail_template['body']
);
($info ? $info . "\n" : ""),
));
// Send
$this->getMailerFactory()->getMailer()->send(

View File

@ -18,6 +18,7 @@
*/
use Mibew\Database;
use Mibew\Mail\Utils as MailUtils;
use Symfony\Component\Translation\Loader\PoFileLoader;
/**
@ -969,7 +970,7 @@ function enable_locale($locale)
// files.
$mail_templates_file = MIBEW_FS_ROOT . '/locales/' . $locale . '/mail_templates.yml';
if (is_readable($mail_templates_file)) {
import_mail_templates($locale, $mail_templates_file);
MailUtils::importTemplates($locale, $mail_templates_file);
}
} else {
// The locale exists in the database. Update it.

View File

@ -89,7 +89,6 @@ require_once(MIBEW_FS_ROOT . '/libs/captcha.php');
require_once(MIBEW_FS_ROOT . '/libs/chat.php');
require_once(MIBEW_FS_ROOT . '/libs/groups.php');
require_once(MIBEW_FS_ROOT . '/libs/invitation.php');
require_once(MIBEW_FS_ROOT . '/libs/notify.php');
require_once(MIBEW_FS_ROOT . '/libs/operator.php');
require_once(MIBEW_FS_ROOT . '/libs/pagination.php');
require_once(MIBEW_FS_ROOT . '/libs/statistics.php');

View File

@ -1,161 +0,0 @@
<?php
/*
* This file is a part of Mibew Messenger.
*
* Copyright 2005-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
use Mibew\Database;
use Symfony\Component\Yaml\Parser as YamlParser;
/**
* Loads an email template.
*
* @param string $name Machine name of the template.
* @param string $locale Locale code for the mail template.
* @param boolean $strict If it is set to true no fall back to default locale
* will be made.
* @return array|boolean Associative array with localized mail template data. If
* the template with specified locale is not found a template for default
* locale will be loaded. If the last one does not found too boolean FALSE
* will be returned.
* Mail template array contains the following keys:
* - templateid: int, internale id of the template. It should not be used
* directly.
* - name: string, machine name of the template.
* - locale: string, locale code the templates belongs to.
* - title: string, localized human-readable mail template title.
* - subject: string, localized value which will be used as subject field in
* an email.
* - body: string, localized value which will be used as body in an email.
*/
function mail_template_load($name, $locale, $strict = false)
{
static $templates = array();
if (!isset($templates[$locale][$name])) {
// Try to load the template from the database.
$template = Database::getInstance()->query(
"SELECT * FROM {mailtemplate} WHERE name = :name AND locale = :locale",
array(
':name' => $name,
':locale' => $locale,
),
array(
'return_rows' => Database::RETURN_ONE_ROW,
)
);
if (!$template) {
if ($strict) {
return false;
}
// There is no template in the database.
if ($locale == get_default_locale()) {
// The template is still not found.
$template = false;
} else {
// Try to load the template for the default locale.
$template = mail_template_load($name, get_default_locale());
}
}
$templates[$locale][$name] = $template;
}
return $templates[$locale][$name];
}
/**
* Saves a mail template to the database.
*
* @param string $name Machine name of the template to save.
* @param string $locale Locale code the template belongs to.
* @param string $subject Localized string that is used as email subject.
* @param string $body Localized string that is used as email body.
*/
function mail_template_save($name, $locale, $subject, $body)
{
$db = Database::getInstance();
$template = mail_template_load($name, $locale);
if ($template && $template['locale'] == $locale) {
// Update existing mail template
$db->query(
("UPDATE {mailtemplate} "
. "SET subject = :subject, body = :body "
. "WHERE templateid = :id"),
array(
':id' => $template['templateid'],
':subject' => $subject,
':body' => $body,
)
);
} else {
// Insert a new mail template
$db->query(
("INSERT INTO {mailtemplate} (name, locale, subject, body) "
. "VALUES (:name, :locale, :subject, :body)"),
array(
':name' => $name,
':locale' => $locale,
':subject' => $subject,
':body' => $body,
)
);
}
}
/**
* Import mail templates from a YAML file.
*
* @param string $locale Locale code.
* @param string $file Full path to the file that should be imported.
*/
function import_mail_templates($locale, $file)
{
// Get new mail templates
$parser = new YamlParser();
$new_templates = $parser->parse(file_get_contents($file));
if (empty($new_templates)) {
// Nothing to import.
return;
}
foreach ($new_templates as $name => $template) {
// Validate the template
$is_valid_template = is_array($template)
&& array_key_exists('subject', $template)
&& array_key_exists('body', $template);
if (!$is_valid_template) {
throw new \RuntimeException(sprintf(
'An invalid mail template "%s" is found in "%s".',
$name,
$file
));
}
if (!mail_template_load($name, $locale, true)) {
// Import only templates that are not already in the database.
mail_template_save(
$name,
$locale,
$template['subject'],
$template['body']
);
}
}
}