Prestashop 1.6 - 使用电子邮件钩子

时间:2016-11-07 14:04:09

标签: email layout smarty hook prestashop-1.6

我目前正在使用Prestashop 1.6.1.5建立一个商店。

我必须发送的电子邮件很多,我想在一个地方管理我的电子邮件骨架 我认为更简单的方法是将电子邮件管理为智能文件和[扩展我的布局] [1]。

现在有人以透明和全球的方式管理电子邮件布局吗?

谢谢,

本。

[编辑]

潜入MailCore类,我看到可以在使用三个钩子(简化和评论)发送之前操纵电子邮件模板:

// 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缓存后,类覆盖工作(从构造函数中记录)但没有调用我的钩子。
我还尝试使用$this->registerHook('...');将钩子注册到我的类constructoe中,但没有任何效果。

如果有人可以提供帮助,那就太棒了。

谢谢,

本。

2 个答案:

答案 0 :(得分:1)

@TheDrot:感谢这些有用的解释: - )

正如TheDrot指出的那样,这封电子邮件有点混乱。我想:

  • 能够编辑来自BO的电子邮件。
  • 全球管理电子邮件布局(核心,模块,自定义......)
  • 有几个布局,并选择我将直接用于模板的那个。

所以我决定覆盖MailCore类并实现以下基本但工作的“布局扩展”系统 当然,这个解决方案的主要缺点是在更新Prestashop时我必须保持我的覆盖功能是最新的。

  

我处于单一语言环境中,我不需要文本电子邮件的布局,但管理多种语言和文本电子邮件也很容易。

     

以下代码当然可以大大改进,它只是一个快速演示。

布局

布局放置在 / 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>

邮件类

这是主要功能:如果“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);
}

然后在一个地方修改发送功能,将布局功能应用于模板:

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);

    // ...
}

答案 1 :(得分:0)

对于自定义邮件,您不需要挂钩或覆盖任何内容。在致电Mail::send()之前,您需要做的就是通过聪明的{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}

由于您使用可以调用语言函数{l s='text'}的智能模板,因此您也不需要为每种语言都设置电子邮件模板。

示例仅适用于html模板,添加处理txt模板的代码。

要更改核心电子邮件......现在这就是复杂的事情。

一种方法是挂钩actionEmailAddAfterContentactionGetExtraMailTemplateVars

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 dev看到这个,他们可能会解释它。