除了更改WooCommerce邮件的样子外,还可以创建自定义的邮件。发件人、发送内容都可以定制。比如每个产品有不同的供货商,客户下单后要给每个供货商发送邮件,或者要发一些产品你使用说明给客户等等。
目录
创建插件
在wp-content/plugins
目录下创建文件夹my-custom-wc-email
来保存代码,插件的目录结构如下:
└─my-custom-wc-email
│ class-wc-custom-email.php
│ plugin.php
│
└─emails
my-custom-email-plain.php
my-custom-email.php
每个文件的用途如下:
- plugin.php: 插件主文件,包含插件的header信息,以及核心功能:注册自定义邮件
- class-wc-custom-email.php:实现自定义邮件类
- emails/***.php:邮件的html模板和plain text模板
注册自定义邮件
plugin.php
的内容如下:
<?php
/**
* Plugin Name: Custom WooCommerce Email Example
* Plugin URI: https://www.solagirl.net
* Description: Custom WooCommerce Email Example
* Version: 1.0.0
* Author: Sola
* Author URI: https://www.solagirl.net
* License: GPL v2 or later
* License URI: https://www.gnu.org/licenses/gpl-2.0.html
* Update URI: https://example.com/my-plugin/
* Text Domain: cwe
* Domain Path: /languages
*/
// Exit if accessed directly
if (!defined('ABSPATH')) exit;
function sola_add_custom_woocommerce_email( $email_classes ) {
// include our custom email class
include( 'class-wc-custom-email.php' );
// add the email class to the list of email classes that WooCommerce loads
$email_classes['Sola_WC_Custom_Email'] = new Sola_WC_Custom_Email();
return $email_classes;
}
add_filter( 'woocommerce_email_classes', 'sola_add_custom_woocommerce_email' );
- 定义插件header信息
- 通过filter:
woocommerce_email_classes
向WooCommerce注册一个新的邮件,具体实现email的class需要通过include文件的方式引入。因为该class要以WC_Email
class为模板来创建,如果不在这里include,可能会遇到WC_Email
未定义的错误。
以WC_Email为模板创建自定义邮件的class
class-wc-custom-email.php
的内容如下:
<?php
// Exit if accessed directly
if (!defined('ABSPATH')) exit;
if (!class_exists('Sola_WC_Custom_Email')) :
class Sola_WC_Custom_Email extends WC_Email {
/**
* Constructor.
*/
public function __construct() {
$this->id = 'my_custom_email';
$this->title = __('My Custom Email', 'woocommerce');
$this->description = __('This is an example of WooCommerce Custom Email', 'woocommerce');
// 将email templates存储在插件的目录下
$this->template_base = __DIR__ . '/emails/';
$this->template_html = 'my-custom-email.php';
$this->template_plain = 'my-custom-email-plain.php';
$this->placeholders = array(
'{order_date}' => '',
'{order_number}' => '',
);
// Triggers for this email.
add_action('woocommerce_order_status_pending_to_processing_notification', array($this, 'trigger'), 10, 2);
add_action('woocommerce_order_status_pending_to_completed_notification', array($this, 'trigger'), 10, 2);
add_action('woocommerce_order_status_pending_to_on-hold_notification', array($this, 'trigger'), 10, 2);
add_action('woocommerce_order_status_failed_to_processing_notification', array($this, 'trigger'), 10, 2);
add_action('woocommerce_order_status_failed_to_completed_notification', array($this, 'trigger'), 10, 2);
add_action('woocommerce_order_status_failed_to_on-hold_notification', array($this, 'trigger'), 10, 2);
add_action('woocommerce_order_status_cancelled_to_processing_notification', array($this, 'trigger'), 10, 2);
add_action('woocommerce_order_status_cancelled_to_completed_notification', array($this, 'trigger'), 10, 2);
add_action('woocommerce_order_status_cancelled_to_on-hold_notification', array($this, 'trigger'), 10, 2);
// Call parent constructor.
parent::__construct();
// Other settings.
$this->recipient = $this->get_option('recipient', get_option('admin_email'));
}
/**
* Get email subject.
*
* @since 3.1.0
* @return string
*/
public function get_default_subject() {
return __('[{site_title}]: My Custom Email #{order_number}', 'woocommerce');
}
/**
* Get email heading.
*
* @since 3.1.0
* @return string
*/
public function get_default_heading() {
return __('My Custom Email: #{order_number}', 'woocommerce');
}
/**
* Trigger the sending of this email.
*
* @param int $order_id The order ID.
* @param WC_Order|false $order Order object.
*/
public function trigger($order_id, $order = false) {
$this->setup_locale();
if ($order_id && !is_a($order, 'WC_Order')) {
$order = wc_get_order($order_id);
}
if (is_a($order, 'WC_Order')) {
$this->object = $order;
$this->placeholders['{order_date}'] = wc_format_datetime($this->object->get_date_created());
$this->placeholders['{order_number}'] = $this->object->get_order_number();
$email_already_sent = $order->get_meta('_new_order_email_sent');
}
/**
* Controls if new order emails can be resend multiple times.
*
* @since 5.0.0
* @param bool $allows Defaults to false.
*/
if ('true' === $email_already_sent && !apply_filters('woocommerce_new_order_email_allows_resend', false)) {
return;
}
if ($this->is_enabled() && $this->get_recipient()) {
// 发送email
$this->send($this->get_recipient(), $this->get_subject(), $this->get_content(), $this->get_headers(), $this->get_attachments());
$order->update_meta_data('_new_order_email_sent', 'true');
$order->save();
}
$this->restore_locale();
}
/**
* Get content html.
*
* @return string
*/
public function get_content_html() {
return wc_get_template_html(
$this->template_html,
array(
'order' => $this->object,
'email_heading' => $this->get_heading(),
'additional_content' => $this->get_additional_content(),
'sent_to_admin' => true,
'plain_text' => false,
'email' => $this,
),
$this->template_base,
$this->template_base
);
}
/**
* Get content plain.
*
* @return string
*/
public function get_content_plain() {
return wc_get_template_html(
$this->template_plain,
array(
'order' => $this->object,
'email_heading' => $this->get_heading(),
'additional_content' => $this->get_additional_content(),
'sent_to_admin' => true,
'plain_text' => true,
'email' => $this,
),
$this->template_base,
$this->template_base
);
}
/**
* Default content to show below main email content.
*
* @since 3.7.0
* @return string
*/
public function get_default_additional_content() {
return __('Congratulations on the sale.', 'woocommerce');
}
/**
* Initialise settings form fields.
*/
public function init_form_fields() {
/* translators: %s: list of placeholders */
$placeholder_text = sprintf(__('Available placeholders: %s', 'woocommerce'), '<code>' . implode('</code>, <code>', array_keys($this->placeholders)) . '</code>');
$this->form_fields = array(
'enabled' => array(
'title' => __('Enable/Disable', 'woocommerce'),
'type' => 'checkbox',
'label' => __('Enable this email notification', 'woocommerce'),
'default' => 'yes',
),
'recipient' => array(
'title' => __('Recipient(s)', 'woocommerce'),
'type' => 'text',
/* translators: %s: WP admin email */
'description' => sprintf(__('Enter recipients (comma separated) for this email. Defaults to %s.', 'woocommerce'), '<code>' . esc_attr(get_option('admin_email')) . '</code>'),
'placeholder' => '',
'default' => '',
'desc_tip' => true,
),
'subject' => array(
'title' => __('Subject', 'woocommerce'),
'type' => 'text',
'desc_tip' => true,
'description' => $placeholder_text,
'placeholder' => $this->get_default_subject(),
'default' => '',
),
'heading' => array(
'title' => __('Email heading', 'woocommerce'),
'type' => 'text',
'desc_tip' => true,
'description' => $placeholder_text,
'placeholder' => $this->get_default_heading(),
'default' => '',
),
'additional_content' => array(
'title' => __('Additional content', 'woocommerce'),
'description' => __('Text to appear below the main email content.', 'woocommerce') . ' ' . $placeholder_text,
'css' => 'width:400px; height: 75px;',
'placeholder' => __('N/A', 'woocommerce'),
'type' => 'textarea',
'default' => $this->get_default_additional_content(),
'desc_tip' => true,
),
'email_type' => array(
'title' => __('Email type', 'woocommerce'),
'type' => 'select',
'description' => __('Choose which format of email to send.', 'woocommerce'),
'default' => 'html',
'class' => 'email_type wc-enhanced-select',
'options' => $this->get_email_type_options(),
'desc_tip' => true,
),
);
}
}
endif;
- 在
__construct()
方法中定义邮件的ID、title、description等等。要想将email模板保存在自定义目录下,还要定义template_base,就是代码中的$this->template_base = __DIR__ . '/emails/'
。 $this->recipient
必须是有效的邮件地址,或者逗号分隔的邮件列表。- trigger()函数负责判断是否发送邮件和实际发送邮件,该函数被触发的条件定义在__construct()函数中,例如
add_action('woocommerce_order_status_pending_to_processing_notification', array($this, 'trigger'), 10, 2);
,当订单状态从pending变成processing时看情况发送邮件。 get_content_html()
和get_content_plain()
负责获取邮件模板内容,因为模板的位置被修改了,所以这里也要告知模板位置,方法是向wc_get_template_html()
函数传入额外参数。
创建邮件模板
邮件模板可以参考现有的邮件,这些文件位于woocommerce/templates/emails
下,写一个简单的模板来测试,以下是my-custom-email.php
的内容
<?php
// Exit if accessed directly
if (!defined('ABSPATH')) exit;
/*
* @hooked WC_Emails::email_header() Output the email header
*/
do_action( 'woocommerce_email_header', $email_heading, $email ); ?>
<h2>这是一封自定义邮件</h2>
<?php
/*
* @hooked WC_Emails::email_footer() Output the email footer
*/
do_action( 'woocommerce_email_footer', $email );
测试效果
首先,可以在WooCommerce > Settings >Emails
下的邮件列表里找到”My Custom Email“,打开编辑界面,如下图所示,可以看到模板位置是位于插件下面的。
用WooCommerce付款测试插件下一个订单,并安装邮件log插件,例如Mail logging – WP Mail Catcher,查看下单后的邮件,可以找到我们的自定义邮件。
使邮件模板易于编辑
虽然可以通过邮件模板来编辑邮件,但还有更好的方式,比如所见即所得的page builder——yaymail。由于内容较长,感兴趣请移步Yaymail WooCommerce邮件模板生成器,支持自定义模版。
发送自定义邮件的按钮是否可以直接提到 WC 的「订单」这个页面来呢?那样会方便很多,不用每次点进每个订单详情中去。也就是在「订单」页面增加一个自定义的栏目?这样也可以把一些自定义字段直接显示,会不会更方便些?
发送邮件的按钮应该说原本就在订单页面里,你看看order actions里,不是有所有邮件的发送选项吗,如下图所示
谢谢…我的意思是,它的上一层级的那个订单列表的页面。因为左侧菜单中那个页面叫「订单」页面,你截图的这个叫订单详情页…那个列表是否能增加一个栏目,显示自定义字段呢,以及增加一个栏目,可以直接发邮件的…
好吧,「订单」这个页面就是订单列表页,我记住了。
你说的当然可以,custom post type列表页显示自定义字段,谷歌搜,文章很多。
直接发送邮件,把woocommerce订单详情页面发送邮件的代码读懂,移植到订单列表页面就行了,用ajax+php实现。你可以搜索下有没有相关插件。
你好,我前几个小时的那个评论,是问「能否在用户购买后发送一封邮件,此邮件包含两个变量,手动输入」。
后来我想到,用户订单详情中有一个「订单 提示」(Order Notes)模块,能否将这个模块复制一下,修改生成另一个「发送账号信息」的模块。每次在此输入不同的账号,发送出去后,别的内容是固定的,比如包含「如何使用此账号」的说明链接等?
因为你的模板发送出去的数据,都是固定的,只是自己修改邮件主题什么的。
或者,通过添加「自定义项目」(Custom Fields)的方式发送?
比如 Custom Fields「账号」「密码」,然后执行一个「自定义模板」的动作,发送新邮件呢?如何在模板中只显示这两个元数据,订单详情不显示呢?
你说的是WooCommerce吗?如果是的话,你可以创建一封新的邮件模板,包含你要在QQ企业邮箱HTML模式下编辑的信息,在客户结账后发送给你自己,然后手动输入对方的账号和密码,转发给对方。
或者这封邮件不自动发送给你,而是在订单产生后,到订单详情里填写账号密码,再用order actions发送这个邮件,当然也要你手动操作,除非你能让填写账号密码这步自动化。
给WooCommerce创建新邮件不难
谢谢!第一种方法很机智。
已在「订单」类型中添加 4 个自定义字段,能在订单管理页呈现一个填写字段值的模块。希望在填写后执行一个动作发送信息邮件。
1、现在不知如何在邮件中 PHP 呈现当前 POST 的指定字段值;
2、选用了 processing 模板,导致一下单就触发了邮件,如何修改触发机制呢?
3、准备在「新订单」邮件中加一个指向当前订单管理页的地址,发现是 …/wp-admin/post.php?post=1991&action=edit
动态修改的话是这样吗:
……/wp-admin/post.php?post=get_order_number(); ?>&action=edit
第一点,如果是自定义字段的话,保存订单后直接从数据库读取就行了,应该跟POST没什么关系。
二三点我没有试过,我想控制触发条件的代码就在email class的construct里
模板里$order变量记录了订单的信息,通过这个变量可以获取订单号。
1、删除了两行 add_action,还是会触发发送。如果一同删除下方的 parent::__construct(); 就不会自动触发了,但是执行动作也不能发送邮件了。
2、PHP 如何将字段值显示出来呢?搜了好久都没找到。
var_dump和get_post_meta没什么关系,而且var_dump仅在调试时使用,前者是php基本函数,后者是WordPress基于php定义的函数。
很抱歉这样说,但如果你弄不清这两个东西,改动代码是非常困难的,建议先学习php的基础知识。
你说的border是1的问题没那么简单,邮件大部分样式都不是在模板里定义的,而是通过css和一个将css转换为内联样式的php库来写的。改动这个需要你懂php和css。
哈哈,其实你说的我都懂。get_post_meta 很明显 post 就是 WordPress 定义的概念,本身不属于 php 语法中的函数。CSS 我懂。只是改了这么久有点累了,还得找找在哪里- –
去读一下parent::__construct()的代码可能就明白了,这个class不是extend的一个class吗
php显示字段值种类太多了,如果你是说custom field,可以用get_post_meta()
如果要打印在屏幕上,是不是这样:
username 是字段名。
后来我用了 invoice 收据的模板,是必须手动发送的。只是它有支付和未支付,感觉有点累赘,但不太敢乱改。
你确定删除trigger代码不管用吗?parent constructor里并没有发送邮件的代码,我本地用admin-new-order模板试了下,删除trigger就不会发送了。
我也觉得,至少理论上是不会的。可能当时由于缓存之类的问题导致的吧,我只试了一次。
刚才我发送了一个 php 代码,但是没有显示出来,可能作为代码执行了,而你这里没有那个字段,因此是空的。
我在前后加了几个星号,看看这回能否显示~
php 中输入 echo get_post_meta ( get_the_ID(), ‘username’, true );
如果你想让一个变量显示,无论有没有值,可以用var_dump
用我那条成功了~ var_dump 和 get_post_meta 是一样的效果?
然后就是发现我模板做出来的表格,右侧和下放有较深的细边线,但是其他的邮件是正常的,灰色的粗线。border 没动过,值是默认的”1″,我改成 solid 之后还是一样的。
确实,给woocommerce加新邮件很简单,难度全在定制化那个模板上,邮件模板本来就很罗嗦,如果再需要显示与默认数据不同的东西更麻烦。
其实这篇文章记录的东西正是我给一个客户写的插件,但我只写了最简单的部分。如果要写后面咋改的模板,估计我就要跟你一样的心情了。
要自己改的话恐怕必须把WooCommerce模板原理搞明白。
恩,辛苦。
我已经搞定了,主要是表格中的字段值是动态的,别的部分都是固定的。
现在就差那个框线了,虽然可以忽略,但是看着怪怪的。我去找找相关的 CSS 在哪里好了。
疑点是表格的样式竟然没动也会产生差异。不知道是不是我删了 tbody 和 tfoot 标记的缘故。
关于修改woocommerce样式,可以看这篇文章https://www.solagirl.net/woocommerce-2-3-email-customization.html
还有一个问题就是,要保护商店的经营数据,避免被回头客知晓他回访期间我们的销量,需要将订单号变一种样式。比如在前面加上 年份+月份。看起来就像是这个月的第几单,而不是从一开始的第几单。或者其他的算法,来保护这个数值不被联想。
这个没改过,不过order就是custom post type,搜下custom post type permalink之类的应该有解决方案。
因为发的货是帐号,有使用期限的。有一个字段就是时间,yymmdd。
在这一天需要发送一封邮件,说到期了。
那么再新建一个邮件模板,能否设置一个 trigger,在那天的 15:00 触发,自动发送提醒邮件呢?
如果不行,记得有个定时任务的插件叫 WP-Cron,如果要在那时候执行任务,「让 WC 发送某封邮件」的函数是什么?
每个class里的trigger()函数就是发送邮件的,至于定时发送,自己写吧。
如果不需要显示订单详情之类的,不需要用WooCommerce的邮件写法。
sola, 你现在仍然用的是 Hostgator的主机吗? 最近有看到一个帖子,说HG支持成人/赌博站,影响SEO之类的。你觉得有这回事吗?
是hostgator,我不清楚,但我也不是很关心这个
分享最美,这个功能很有用。