Prestashop 1.6 - 使用电子邮件挂钩
Prestashop 1.6 - Using emails hooks
我目前正在使用 Prestashop 1.6.1.5 建立商店。
由于我必须发送的电子邮件很多,我想在一个地方管理我的电子邮件骨架。
我认为更简单的方法是将电子邮件管理到 smarty 文件中并[扩展我的布局][1]。
现在有没有人可以通过透明的全局方式来管理电子邮件布局?
谢谢,
本.
[编辑]
深入了解 MailCore class,我看到可以在发送前使用三个挂钩(简化和评论)操作电子邮件模板:
// Init empty templates strings.
$template_html = '';
$template_txt = '';
// Manipulate strings before importing templates.
Hook::exec('actionEmailAddBeforeContent', array(
'template_html' => &$template_html,
'template_txt' => &$template_txt,
// ...
), null, true);
// Import templates.
$template_html .= Tools::file_get_contents(/* ... */);
$template_txt .= strip_tags(html_entity_decode(Tools::file_get_contents(/* ... */), null, 'utf-8'));
// Manipulate strings after importing templates.
Hook::exec('actionEmailAddAfterContent', array(
'template_html' => &$template_html,
'template_txt' => &$template_txt,
// ...
), null, true);
// Some MailCore stuff.
// Inject custom vars before generate email content
$extra_template_vars = array();
Hook::exec('actionGetExtraMailTemplateVars', array(
'template_vars' => $template_vars,
'extra_template_vars' => &$extra_template_vars,
// ...
), null, true);
$template_vars = array_merge($template_vars, $extra_template_vars);
// Generate and send email
所以在我看来,使用这些挂钩是管理我的电子邮件布局的好方法,但我不明白定义挂钩调用的函数是如何工作的。
为了使其全球化,我尝试覆盖 MailCore(进入 /override/classes/Mail。php):
class Mail extends MailCore {
public function __construct($id = null, $id_lang = null, $id_shop = null) {
parent::__construct($id, $id_lang, $id_shop);
PrestaShopLogger::addLog('MailCore overrided!');
}
// Prepend a header to template.
public function hookActionEmailAddBeforeContent($params) {
PrestaShopLogger::addLog('hookActionEmailAddBeforeContent called!');
$params['template_html'] .= '<h1>{myheader}</h1>';
}
// Append a footer to template.
public function hookActionEmailAddAfterContent($params) {
PrestaShopLogger::addLog('hookActionEmailAddAfterContent called!');
$params['template_html'] .= '<h1>{myfooter}</h1>';
}
// Add my custom vars.
public function hookActionGetExtraMailTemplateVars($params) {
PrestaShopLogger::addLog('hookActionGetExtraMailTemplateVars called!');
$params['extra_template_vars']['myheader'] = 'This is a header';
$params['extra_template_vars']['myfooter'] = 'This is a footer';
}
}
清除 Prestashop 缓存后,class 覆盖工作(来自构造函数的日志)但是 none 我的挂钩被调用。
我还尝试使用 $this->registerHook('...');
将钩子注册到我的 class 构造中,但没有任何效果。
如果有人能帮忙,那就太好了。
谢谢,
本.
仅对于自定义邮件,您不需要挂钩或覆盖任何内容。在调用 Mail::send()
之前,您在这里要做的就是通过 smarty {header}
、{content}
、{footer}
.
生成三个模板变量
public function someModuleMailFunc($email_template)
{
$header_tpl = $this->context->smarty->createTemplate('path_to_header_tpl_smarty');
$header_tpl->assign(array(
// assign header template vars
));
// put some conditionals here to load a proper content for specific mail
if ($email_template == 'some_mail_template') {
$content_tpl = $this->context->smarty->createTemplate('path_to_content_tpl_smarty');
$content_tpl->assign(array(
// assign content template vars
));
}
$footer_tpl = $this->context->smarty->createTemplate('path_to_footer_tpl_smarty');
$footer_tpl->assign(array(
// assign footer template vars
));
$email_vars = array(
'{header}' => $header_tpl->fetch(),
'{content}' => $content_tpl->fetch(),
'{footer}' => $footer_tpl->fetch()
);
Mail::send('en', $email_template, $subject, $email_vars, $to);
}
现在确保您的 some_email_template.html
结构像这样。
{header}
{content}
{footer}
您也不需要为每种语言准备电子邮件模板,因为您使用可以调用语言函数的 smarty 模板 {l s='text'}
。
并且该示例仅适用于 html 模板,添加处理 txt 模板的代码。
要更改核心电子邮件...现在事情变得复杂了。
一种方法是挂接到 actionEmailAddAfterContent
和 actionGetExtraMailTemplateVars
。
public function hookActionEmailAddAfterContent($params)
{
// here we can replace the email templates with {header} {content} {footer}
// this param is passed by reference
$params['template_html'] = '{header}{content}{footer}';
}
public function hookActionGetExtraMailTemplateVars($params)
{
// here we generate vars for {header} {content} {footer}
$header_tpl = $this->context->smarty->createTemplate('path_to_header_tpl_smarty');
$header_tpl->assign(array(
// assign header template vars
));
// existing template vars can be accessed with $params['template_vars']
// so they can be reinserted into your templates
// put some conditionals here to load a proper content for specific mail
if ($params['template'] == 'some_mail_template') {
$content_tpl = $this->context->smarty->createTemplate('path_to_content_tpl_smarty');
$content_tpl->assign(array(
// assign content template vars
));
}
$footer_tpl = $this->context->smarty->createTemplate('path_to_footer_tpl_smarty');
$footer_tpl->assign(array(
// assign footer template vars
));
// this param is also passed by reference
$params['extra_template_vars']['{header}'] = $header_tpl->fetch();
$params['extra_template_vars']['{content}'] = $content_tpl->fetch();
$params['extra_template_vars']['{footer}'] = $footer_tpl->fetch();
}
这样额外的参数将被 Mail::send()
拾取,并且由于我们之前修改了模板,它将在其中插入页眉内容和页脚。
问题是您仍然需要为每种语言添加电子邮件模板文件,即使它们从未被使用过,因为 Mail::send()
仍然会尝试在执行这些挂钩之前加载它们。除非你想覆盖当然的方法。
此外,通过后台为经理编辑电子邮件变得毫无用处,因为除了 {header}{content}{footer}
自定义模块邮件和编辑核心邮件之外,他们看不到任何内容。
电子邮件模板的整个事情有点混乱,我不知道为什么开发人员决定以这种方式创建邮件系统而不使用 smarty。也许是为了更轻松地实施后台电子邮件编辑……我不知道。也许如果任何 prestashop 开发人员看到这个,他们可能会解释它。
@TheDrot : 感谢这些有用的解释:-)
正如 TheDrot 指出的那样,这个电子邮件有点乱。我想:
- 能够编辑来自 BO 的电子邮件。
- 全局管理电子邮件布局(核心、模块、自定义...)
- 有几个布局,select我将直接在模板中使用的布局。
所以我决定覆盖 MailCore class 并实现以下基本但有效的“布局扩展”系统。
当然,这个解决方案的主要缺点是我必须在更新 Prestashop 时使我的重写函数保持最新。
I am in a single language context and I don't need layouts for text emails, but managing multiple language and texts emails is easy too.
Following code can of course be highly improved, it's just a quick demo.
布局
布局放置在 /themes/{theme}/mails/layouts 目录中。
可以使用电子邮件中可用的任何变量,内容位置使用 {{CONTENT}}
标签定义。
/themes/mytheme/mails/layouts/my-layout.html :
<h1>A header</h1>
{{CONTENT}}
<h1>A footer</h1>
电子邮件模板
进入电子邮件模板,要继承的布局是使用 {extends:name-of-layout}
标签定义的:
/themes/mytheme/mails/en/password_query.html :
{{extends:my-layout}}
<p>
<b>
Hi {firstname} {lastname},
</b>
</p>
<p>
You have requested to reset your {shop_name} login details.<br/>
Please note that this will change your current password.<br/>
To confirm this action, please use the following link:
</p>
<p>
<a href="{url}" class="button">Change my pasword</a>
</p>
邮件class
这里是主要功能:如果“extends”标签存在于模板中并且创建了所需的布局,则使用模板填充布局。
/override/classes/Mail.php :
public static function layout($theme_path, $template, $content) {
preg_match("/^\{\{extends\:(\w+)\}\}/", ltrim($content), $m);
if (!isset($m[1]) || !file_exists($theme_path . 'mails/layout/' . $m[1] . '.html')) {
return $content;
}
$content = ltrim(str_replace('{{extends:' . $m[1] . '}}', '', $content));
$layout = Tools::file_get_contents($theme_path . 'mails/layout/' . $m[1] . '.html');
return str_replace('{{CONTENT}}', $content, $layout);
}
然后 Send 函数在一个地方被修改以将 layout 函数应用于模板:
public static function Send($id_lang, $template, $subject, $template_vars, $to, $to_name = null, $from = null, $from_name = null, $file_attachment = null, $mode_smtp = null, $template_path = _PS_MAIL_DIR_, $die = false, $id_shop = null, $bcc = null, $reply_to = null) {
// ...
$template_html = '';
$template_txt = '';
Hook::exec('actionEmailAddBeforeContent', array(
'template' => $template,
'template_html' => &$template_html,
'template_txt' => &$template_txt,
'id_lang' => (int) $id_lang
), null, true);
$template_html .= Tools::file_get_contents($template_path . $iso_template . '.html');
$template_txt .= strip_tags(html_entity_decode(Tools::file_get_contents($template_path . $iso_template . '.txt'), null, 'utf-8'));
Hook::exec('actionEmailAddAfterContent', array(
'template' => $template,
'template_html' => &$template_html,
'template_txt' => &$template_txt,
'id_lang' => (int) $id_lang
), null, true);
// Apply self::layout function to template when acquired.
$template_html = self::layout($theme_path, $template_path . $iso_template . '.html', $template_html);
// ...
}
我目前正在使用 Prestashop 1.6.1.5 建立商店。
由于我必须发送的电子邮件很多,我想在一个地方管理我的电子邮件骨架。
我认为更简单的方法是将电子邮件管理到 smarty 文件中并[扩展我的布局][1]。
现在有没有人可以通过透明的全局方式来管理电子邮件布局?
谢谢,
本.
[编辑]
深入了解 MailCore class,我看到可以在发送前使用三个挂钩(简化和评论)操作电子邮件模板:
// Init empty templates strings.
$template_html = '';
$template_txt = '';
// Manipulate strings before importing templates.
Hook::exec('actionEmailAddBeforeContent', array(
'template_html' => &$template_html,
'template_txt' => &$template_txt,
// ...
), null, true);
// Import templates.
$template_html .= Tools::file_get_contents(/* ... */);
$template_txt .= strip_tags(html_entity_decode(Tools::file_get_contents(/* ... */), null, 'utf-8'));
// Manipulate strings after importing templates.
Hook::exec('actionEmailAddAfterContent', array(
'template_html' => &$template_html,
'template_txt' => &$template_txt,
// ...
), null, true);
// Some MailCore stuff.
// Inject custom vars before generate email content
$extra_template_vars = array();
Hook::exec('actionGetExtraMailTemplateVars', array(
'template_vars' => $template_vars,
'extra_template_vars' => &$extra_template_vars,
// ...
), null, true);
$template_vars = array_merge($template_vars, $extra_template_vars);
// Generate and send email
所以在我看来,使用这些挂钩是管理我的电子邮件布局的好方法,但我不明白定义挂钩调用的函数是如何工作的。
为了使其全球化,我尝试覆盖 MailCore(进入 /override/classes/Mail。php):
class Mail extends MailCore {
public function __construct($id = null, $id_lang = null, $id_shop = null) {
parent::__construct($id, $id_lang, $id_shop);
PrestaShopLogger::addLog('MailCore overrided!');
}
// Prepend a header to template.
public function hookActionEmailAddBeforeContent($params) {
PrestaShopLogger::addLog('hookActionEmailAddBeforeContent called!');
$params['template_html'] .= '<h1>{myheader}</h1>';
}
// Append a footer to template.
public function hookActionEmailAddAfterContent($params) {
PrestaShopLogger::addLog('hookActionEmailAddAfterContent called!');
$params['template_html'] .= '<h1>{myfooter}</h1>';
}
// Add my custom vars.
public function hookActionGetExtraMailTemplateVars($params) {
PrestaShopLogger::addLog('hookActionGetExtraMailTemplateVars called!');
$params['extra_template_vars']['myheader'] = 'This is a header';
$params['extra_template_vars']['myfooter'] = 'This is a footer';
}
}
清除 Prestashop 缓存后,class 覆盖工作(来自构造函数的日志)但是 none 我的挂钩被调用。
我还尝试使用 $this->registerHook('...');
将钩子注册到我的 class 构造中,但没有任何效果。
如果有人能帮忙,那就太好了。
谢谢,
本.
仅对于自定义邮件,您不需要挂钩或覆盖任何内容。在调用 Mail::send()
之前,您在这里要做的就是通过 smarty {header}
、{content}
、{footer}
.
public function someModuleMailFunc($email_template)
{
$header_tpl = $this->context->smarty->createTemplate('path_to_header_tpl_smarty');
$header_tpl->assign(array(
// assign header template vars
));
// put some conditionals here to load a proper content for specific mail
if ($email_template == 'some_mail_template') {
$content_tpl = $this->context->smarty->createTemplate('path_to_content_tpl_smarty');
$content_tpl->assign(array(
// assign content template vars
));
}
$footer_tpl = $this->context->smarty->createTemplate('path_to_footer_tpl_smarty');
$footer_tpl->assign(array(
// assign footer template vars
));
$email_vars = array(
'{header}' => $header_tpl->fetch(),
'{content}' => $content_tpl->fetch(),
'{footer}' => $footer_tpl->fetch()
);
Mail::send('en', $email_template, $subject, $email_vars, $to);
}
现在确保您的 some_email_template.html
结构像这样。
{header}
{content}
{footer}
您也不需要为每种语言准备电子邮件模板,因为您使用可以调用语言函数的 smarty 模板 {l s='text'}
。
并且该示例仅适用于 html 模板,添加处理 txt 模板的代码。
要更改核心电子邮件...现在事情变得复杂了。
一种方法是挂接到 actionEmailAddAfterContent
和 actionGetExtraMailTemplateVars
。
public function hookActionEmailAddAfterContent($params)
{
// here we can replace the email templates with {header} {content} {footer}
// this param is passed by reference
$params['template_html'] = '{header}{content}{footer}';
}
public function hookActionGetExtraMailTemplateVars($params)
{
// here we generate vars for {header} {content} {footer}
$header_tpl = $this->context->smarty->createTemplate('path_to_header_tpl_smarty');
$header_tpl->assign(array(
// assign header template vars
));
// existing template vars can be accessed with $params['template_vars']
// so they can be reinserted into your templates
// put some conditionals here to load a proper content for specific mail
if ($params['template'] == 'some_mail_template') {
$content_tpl = $this->context->smarty->createTemplate('path_to_content_tpl_smarty');
$content_tpl->assign(array(
// assign content template vars
));
}
$footer_tpl = $this->context->smarty->createTemplate('path_to_footer_tpl_smarty');
$footer_tpl->assign(array(
// assign footer template vars
));
// this param is also passed by reference
$params['extra_template_vars']['{header}'] = $header_tpl->fetch();
$params['extra_template_vars']['{content}'] = $content_tpl->fetch();
$params['extra_template_vars']['{footer}'] = $footer_tpl->fetch();
}
这样额外的参数将被 Mail::send()
拾取,并且由于我们之前修改了模板,它将在其中插入页眉内容和页脚。
问题是您仍然需要为每种语言添加电子邮件模板文件,即使它们从未被使用过,因为 Mail::send()
仍然会尝试在执行这些挂钩之前加载它们。除非你想覆盖当然的方法。
此外,通过后台为经理编辑电子邮件变得毫无用处,因为除了 {header}{content}{footer}
自定义模块邮件和编辑核心邮件之外,他们看不到任何内容。
电子邮件模板的整个事情有点混乱,我不知道为什么开发人员决定以这种方式创建邮件系统而不使用 smarty。也许是为了更轻松地实施后台电子邮件编辑……我不知道。也许如果任何 prestashop 开发人员看到这个,他们可能会解释它。
@TheDrot : 感谢这些有用的解释:-)
正如 TheDrot 指出的那样,这个电子邮件有点乱。我想:
- 能够编辑来自 BO 的电子邮件。
- 全局管理电子邮件布局(核心、模块、自定义...)
- 有几个布局,select我将直接在模板中使用的布局。
所以我决定覆盖 MailCore class 并实现以下基本但有效的“布局扩展”系统。
当然,这个解决方案的主要缺点是我必须在更新 Prestashop 时使我的重写函数保持最新。
I am in a single language context and I don't need layouts for text emails, but managing multiple language and texts emails is easy too.
Following code can of course be highly improved, it's just a quick demo.
布局
布局放置在 /themes/{theme}/mails/layouts 目录中。
可以使用电子邮件中可用的任何变量,内容位置使用 {{CONTENT}}
标签定义。
/themes/mytheme/mails/layouts/my-layout.html :
<h1>A header</h1>
{{CONTENT}}
<h1>A footer</h1>
电子邮件模板
进入电子邮件模板,要继承的布局是使用 {extends:name-of-layout}
标签定义的:
/themes/mytheme/mails/en/password_query.html :
{{extends:my-layout}}
<p>
<b>
Hi {firstname} {lastname},
</b>
</p>
<p>
You have requested to reset your {shop_name} login details.<br/>
Please note that this will change your current password.<br/>
To confirm this action, please use the following link:
</p>
<p>
<a href="{url}" class="button">Change my pasword</a>
</p>
邮件class
这里是主要功能:如果“extends”标签存在于模板中并且创建了所需的布局,则使用模板填充布局。
/override/classes/Mail.php :
public static function layout($theme_path, $template, $content) {
preg_match("/^\{\{extends\:(\w+)\}\}/", ltrim($content), $m);
if (!isset($m[1]) || !file_exists($theme_path . 'mails/layout/' . $m[1] . '.html')) {
return $content;
}
$content = ltrim(str_replace('{{extends:' . $m[1] . '}}', '', $content));
$layout = Tools::file_get_contents($theme_path . 'mails/layout/' . $m[1] . '.html');
return str_replace('{{CONTENT}}', $content, $layout);
}
然后 Send 函数在一个地方被修改以将 layout 函数应用于模板:
public static function Send($id_lang, $template, $subject, $template_vars, $to, $to_name = null, $from = null, $from_name = null, $file_attachment = null, $mode_smtp = null, $template_path = _PS_MAIL_DIR_, $die = false, $id_shop = null, $bcc = null, $reply_to = null) {
// ...
$template_html = '';
$template_txt = '';
Hook::exec('actionEmailAddBeforeContent', array(
'template' => $template,
'template_html' => &$template_html,
'template_txt' => &$template_txt,
'id_lang' => (int) $id_lang
), null, true);
$template_html .= Tools::file_get_contents($template_path . $iso_template . '.html');
$template_txt .= strip_tags(html_entity_decode(Tools::file_get_contents($template_path . $iso_template . '.txt'), null, 'utf-8'));
Hook::exec('actionEmailAddAfterContent', array(
'template' => $template,
'template_html' => &$template_html,
'template_txt' => &$template_txt,
'id_lang' => (int) $id_lang
), null, true);
// Apply self::layout function to template when acquired.
$template_html = self::layout($theme_path, $template_path . $iso_template . '.html', $template_html);
// ...
}