I have a custom order status called Shipping Details which will be selected after the Order complete trigger is fired.
Code for the Order Status
function register_shipping_details_status() {
register_post_status( 'wc-shipping-details', array(
'label' => 'Send Shipping Details',
'public' => true,
'exclude_from_search' => false,
'show_in_admin_all_list' => true,
'show_in_admin_status_list' => true
//'label_count' => _n_noop( 'Shipping Details <span class="count">(%s)</span>', 'Shipping Details <span class="count">(%s)</span>' )
) );
}
add_action( 'init', 'register_shipping_details_status' );
// Add to list of WC Order statuses
function add_shipping_details_to_order_statuses( $order_statuses ) {
$new_order_statuses = array();
// add new order status after completed
foreach ( $order_statuses as $key => $status ) {
$new_order_statuses[ $key ] = $status;
if ( 'wc-completed' === $key ) {
$new_order_statuses['wc-shipping-details'] = 'Send Shipping Details';
}
}
return $new_order_statuses;
}
add_filter( 'wc_order_statuses', 'add_shipping_details_to_order_statuses' );
and my email trigger code is here.
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
if ( ! class_exists( 'WC_Shipping_Details_Email' ) ) :
/**
* Customer Shipping Details Email
*/
class WC_Shipping_Details_Email extends WC_Email {
/**
* Constructor.
*/
public function __construct() {
$this->id = 'wc_shipping_details';
$this->customer_email = true;
$this->title = __( 'Shipping Details', 'woocommerce' );
$this->description = __( 'Shipping details emails are sent after the order has been marked completed .', 'woocommerce' );
$this->heading = __( 'Shipping Details for your order', 'woocommerce' );
$this->subject = __( 'Shipping details for your order from INAI', 'woocommerce' );
$this->template_html = 'emails/customer-shipping-details.php';
$this->template_plain = 'emails/plain/customer-shipping-details.php';
// Triggers for this email
add_action( 'woocommerce_order_status_shipping_details_notification', array( $this, 'trigger' ) );
// Call parent constuctor
parent::__construct();
}
/**
* Trigger.
*
* #param int $order_id
*/
public function trigger( $order_id ) {
if ( $order_id ) {
$this->object = wc_get_order( $order_id );
$this->recipient = $this->object->billing_email;
$this->find['order-date'] = '{order_date}';
$this->find['order-number'] = '{order_number}';
$this->replace['order-date'] = date_i18n( wc_date_format(), strtotime( $this->object->order_date ) );
$this->replace['order-number'] = $this->object->get_order_number();
}
if ( ! $this->is_enabled() || ! $this->get_recipient() ) {
return;
}
$this->send( $this->get_recipient(), $this->get_subject(), $this->get_content(), $this->get_headers(), $this->get_attachments() );
}
The action for registering the email
//custom hooks for custom woocommerce order email
function register_custom_order_status_action( $actions ){
$actions[] = 'woocommerce_order_status_shipping_details';
return $actions;
}
add_filter( 'woocommerce_email_actions', 'register_custom_order_status_action' );
For some reason the mail is not getting triggered. I have looked around a lot and even found a few others for whom the problem has been solved. WooCommerce - send custom email on custom order status change.
My code is almost exactly the same, still don't know what i'm missing. Please help.
Related
I'm trying to unset COD payment gateway based on a custom product type based on the value from the checkbox, added as WooCommerce admin product option.
But it seems the code doesn't do anything with product type: doarcard.
If I set it to simple then it will work:
//new product type
add_filter("product_type_options", function ($product_type_options) {
$product_type_options['doarcard'] = array(
'id' => '_doarcard',
'wrapper_class' => 'show_if_simple show_if_variable',
'label' => __( 'Doar Card', 'woodmart' ),
'description' => __( 'Activare doar plata cu card sau transfer bancar', 'woodmart' ),
'default' => 'no');
return $product_type_options;
});
add_action("save_post_product", function ($post_ID, $product, $update) {
update_post_meta(
$product->ID
, "_doarcard"
, isset($_POST["_doarcard"]) ? "yes" : "no");
}, 10, 3);
//disable cod for doarcard
add_filter('woocommerce_available_payment_gateways', 'conditional_payment_gateways', 10, 1);
function conditional_payment_gateways( $available_gateways ) {
if( is_admin() )
return $available_gateways;
$prod_doarcard = false;
foreach (WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
$product = wc_get_product($cart_item['product_id']);
// Get the product types in cart (example)
if($product->is_type('doarcard')) $prod_doarcard = true;
}
if($prod_doarcard)
unset($available_gateways['cod']); // unset 'cod'
return $available_gateways;
}
Any advice?
A custom product type has nothing to do with your question. The value of the checkbox is a boolean with value yes or no
and based on that you can then unset the $payment_gateways
So use:
// Add a checkbox as WooCommerce admin product option
function filter_product_type_options( $product_type_options ) {
$product_type_options['doarcard'] = array(
'id' => '_doarcard',
'wrapper_class' => 'show_if_simple show_if_variable',
'label' => __( 'Doar Card', 'woocommerce' ),
'description' => __( 'Activare doar plata cu card sau transfer bancar', 'woocommerce' ),
'default' => 'no',
);
return $product_type_options;
}
add_filter( 'product_type_options', 'filter_product_type_options', 10, 1 );
// Save checkbox
function action_woocommerce_admin_process_product_object( $product ) {
$product->update_meta_data( '_doarcard', isset( $_POST['_doarcard'] ) ? 'yes' : 'no' );
}
add_action( 'woocommerce_admin_process_product_object', 'action_woocommerce_admin_process_product_object', 10, 1 );
// Payment gateways
function filter_woocommerce_available_payment_gateways( $payment_gateways ) {
// Not on admin
if ( is_admin() ) return $payment_gateways;
// Initialize
$prod_doarcard = false;
// WC Cart
if ( WC()->cart ) {
// Loop through cart items
foreach ( WC()->cart->get_cart() as $cart_item ) {
// Get meta
$doarcard = $cart_item['data']->get_meta( '_doarcard', true );
// Equal to yes = checked
if ( $doarcard == 'yes' ) {
$prod_doarcard = true;
// Product present with the right condition, so break the loop
break;
}
}
// True
if ( $prod_doarcard ) {
// Cod
if ( isset( $payment_gateways['cod'] ) ) {
unset( $payment_gateways['cod'] );
}
}
}
return $payment_gateways;
}
add_filter( 'woocommerce_available_payment_gateways', 'filter_woocommerce_available_payment_gateways', 10, 1 );
you should use unset inside foreach loop like this:
foreach (WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
$product = wc_get_product($cart_item['product_id']);
// Get the product types in cart (example)
if($product->is_type('doarcard')){
unset($available_gateways['cod']); // unset 'cod'
}
}
I really hope someone can help me, cause i'm running out of ideas.
I made this custom shipping methon on Woocommerce, copying and pasting from the website documentation and other StakO topics.
Everything seems to be okay, apart of when i try to update my only one field called "Store Address", it doesn't works and values are not updated.
Here's the code. Thanks to everyone
if ( ! defined( 'WPINC' ) ) {
die;
}
if ( in_array( 'woocommerce/woocommerce.php', apply_filters( 'active_plugins', get_option( 'active_plugins' ) ) ) ) {
function pickupinstore_shipping_method() {
if ( ! class_exists( 'PickupInStore_Shipping_Method' ) ) {
class PickupInStore_Shipping_Method extends WC_Shipping_Method {
/**
* Constructor for your shipping class
*
* #access public
* #return void
*/
public function __construct( $instance_id = 0 ) {
$this->id = 'pickupinstore';
$this->instance_id = absint( $instance_id );
$this->method_title = __( 'Pickup in Store', 'pickupinstore' );
$this->method_description = __( 'Custom Shipping Method - Pickup in Store', 'pickupinstore' );
$this->supports = array(
'shipping-zones',
'instance-settings',
'instance-settings-modal',
);
$this->init();
$this->title = isset( $this->settings['title'] ) ? "Ritiro in Negozio | ".$this->settings['title'].": GRATIS" : __( 'Pickup in Store', 'pickupinstore' ); }
/**
* Init your settings
*
* #access public
* #return void
*/
function init() {
// Load the settings API
$this->init_form_fields();
$this->init_settings();
// Save settings in admin if you have any defined
add_action( 'woocommerce_update_options_shipping_' . $this->id, array( $this, 'process_admin_options' ) );
}
/**
* Define settings field for this shipping
* #return void
*/
function init_form_fields() {
$this->form_fields = array(
'title' => array(
'title' => __( 'Ritiro in Negozio', 'pickupinstore' ),
'type' => 'text',
'description' => __( 'Store Address', 'pickupinstore' ),
'default' => __( '197, Brooklyn Road', 'New York' ),
'desc_tip' => true,
),
);
}
/**
* This function is used to calculate the shipping cost. Within this function we can check for weights, dimensions and other parameters.
*
* #access public
* #param mixed $package
* #return void
*/
public function calculate_shipping( $package = array() ) {
$cost = 0;
$this->add_rate( array(
'id' => $this->id,
'label' => $this->title,
'cost' => $cost
) );
}
}
}
}
add_action( 'woocommerce_shipping_init', 'pickupinstore_shipping_method' );
function add_pickupinstore_shipping_method( $methods ) {
$methods['pickupinstore'] = 'PickupInStore_Shipping_Method';
return $methods;
}
add_filter( 'woocommerce_shipping_methods', 'add_pickupinstore_shipping_method' );
}
Edit:
I tried using #docker code, but when I save changes nothing compares in the title section (backend). Otherwise the form field seems to be saved, but as you can see, not diplayed.
In front end, it displays the name of the custom shipping method instead of the title I save.
Move the $this->title definition to the init() method and use $this->get_option() instead of $this->settings.
function init() {
// Load the settings API
$this->init_form_fields();
$this->init_settings();
$this->title = null != $this->get_option('title') ? "Ritiro in Negozio | " . $this->get_option('title') . ": GRATIS" : __( 'Pickup in Store', 'pickupinstore' );
// Save settings in admin if you have any defined
add_action( 'woocommerce_update_options_shipping_' . $this->id, array( $this, 'process_admin_options' ) );
}
Additionally in the init_form_fields() method you should use $this->instance_form_fields.
function init_form_fields() {
$this->instance_form_fields = array(
'title' => array(
'title' => __( 'Ritiro in Negozio', 'pickupinstore' ),
'type' => 'text',
'description' => __( 'Store Address', 'pickupinstore' ),
'default' => __( '197, Brooklyn Road', 'New York' ),
'desc_tip' => true,
),
);
}
In some cases you may need the get_instance_from_fields() method but I have a working solution without it:
/**
* Get setting form fields for instances of this shipping method within zones.
*
* #return array
*/
public function get_instance_form_fields() {
return parent::get_instance_form_fields();
}
EDIT:
This is the full working code:
<?php
/**
* Plugin Name: Pickup in Store
* Plugin URI: https://www.perspectiveweb.it/
* Description: Pickup in store - Custom Shipping Method
* Version: 1.0.0
* Author: Perspective Web
* Author URI: https://www.perspectiveweb.it/
* License: GPL-3.0+
* License URI: http://www.gnu.org/licenses/gpl-3.0.html
* Domain Path: /lang
* Text Domain: perspectiveweb
*/
if ( ! defined( 'WPINC' ) ) {
die;
}
if ( in_array( 'woocommerce/woocommerce.php', apply_filters( 'active_plugins', get_option( 'active_plugins' ) ) ) ) {
function pickupinstore_shipping_method() {
if ( ! class_exists( 'PickupInStore_Shipping_Method' ) ) {
class PickupInStore_Shipping_Method extends WC_Shipping_Method {
/**
* Constructor for your shipping class
*
* #access public
* #return void
*/
public function __construct( $instance_id = 0 ) {
$this->id = 'pickupinstore';
$this->instance_id = absint( $instance_id );
$this->method_title = __( 'Pickup in Store', 'pickupinstore' );
$this->method_description = __( 'Custom Shipping Method - Pickup in Store', 'pickupinstore' );
$this->supports = array(
'shipping-zones',
'instance-settings',
'instance-settings-modal',
);
$this->init();
}
/**
* Init your settings
*
* #access public
* #return void
*/
public function init() {
// Load the settings API
$this->init_form_fields();
$this->init_settings();
$this->title = null != $this->get_option('title') ? "Ritiro in Negozio | " . $this->get_option('title') . ": GRATIS" : __( 'Pickup in Store', 'pickupinstore' );
// Save settings in admin if you have any defined
add_action( 'woocommerce_update_options_shipping_' . $this->id, array( $this, 'process_admin_options' ) );
}
/**
* Define settings field for this shipping
* #return void
*/
public function init_form_fields() {
$this->instance_form_fields = array(
'title' => array(
'title' => __( 'Ritiro in Negozio', 'pickupinstore' ),
'type' => 'text',
'description' => __( 'Store Address', 'pickupinstore' ),
'default' => __( '197, Brooklyn Road', 'New York' ),
'desc_tip' => true,
),
);
}
/**
* This function is used to calculate the shipping cost. Within this function we can check for weights, dimensions and other parameters.
*
* #access public
* #param mixed $package
* #return void
*/
public function calculate_shipping( $package = array() ) {
$cost = 0;
$this->add_rate( array(
'id' => $this->id,
'label' => $this->title,
'cost' => $cost
) );
}
}
}
}
add_action( 'woocommerce_shipping_init', 'pickupinstore_shipping_method' );
function add_pickupinstore_shipping_method( $methods ) {
$methods['pickupinstore'] = 'PickupInStore_Shipping_Method';
return $methods;
}
add_filter( 'woocommerce_shipping_methods', 'add_pickupinstore_shipping_method' );
}
How can I automatically assign a new email to a user in WordPress AND restrict any further update after the first login ?
For example setting the email to the following structure:
{user_id}#example.com
I've tried a few things but couldn't figure it out, any help is appreciated.
I wrote this code, but only users whose email has been registered, their email has been edited, other people who have not registered email, no email has been registered for them. How do I solve this?
my code:
add_action ('admin_head','set_auto_mail');
function set_auto_mail() {
$users = get_users(array('fields'=>'all'));
foreach($users as $user){
$user = get_userdata($user->ID);
if ( in_array( 'subscriber', (array) $user->roles ) ) {
$mail = ($user->ID)."#example.com";
if($mail!=' ') wp_update_user( array ('ID' => $user->ID, 'user_email' => $mail) );
else wp_update_user( array ('ID' => $user->ID, 'user_email' => $user->user_email) );
if($user->user_email == '')
wp_update_user( array ('ID' => $user->ID, 'user_email' => $user->user_email) );
}
}
}
This should do exactly what you wanted.
We set a new user meta data called is_new_user which we will use later on to detect if this is the first ever login for a specific user.
We set a hook to update the user_email on first time login and we update is_new_user to specify that this isn't a new user anymore. Like that we prevent permanent update on login.
Then we fire up the necessary functions to disable email updates. Visually and from the backend for all non-new users.
I'm using first_name, las_name and ID for the email structure. Output:
$first_name.$last_name$ID#bogus.com
<?php
/**
* Adds meta data is_new_user to a user, which let us detect new users on first login.
*
* Fires immediately after a new user is registered.
*
* #link https://developer.wordpress.org/reference/hooks/wp_login/
*/
add_action( 'user_register', 'wpso66983605_add_user_meta_is_new_user' );
function wpso66983605_add_user_meta_is_new_user( $user_id ) {
add_user_meta( $user_id, 'is_new_user', 1, true );
};
/**
* Update user email on first login.
*
* Fires after the user has successfully logged in.
*
* #link https://developer.wordpress.org/reference/hooks/wp_login/
*/
add_action( 'wp_login', 'wpso66983605_set_user_email_on_first_login', 10, 2 );
function wpso66983605_set_user_email_on_first_login( $user_login, $user ) {
if ( ! get_user_meta( $user->ID, 'is_new_user', true ) ) {
return;
};
if ( get_user_meta( $user->ID, 'is_new_user', true ) ) {
update_user_meta( $user->ID, 'is_new_user', 0, 1 );
$userdata = array (
'ID' => $user->ID,
'user_email' => sanitize_email( sanitize_title_with_dashes( $user->first_name . '.' . $user->last_name . $user->ID) . '#bogus.com' ),
);
wp_update_user( $userdata );
};
};
/**
* Prevent users from changing their email address
*
* Modified to ONLY fire when a user is considered as non-new user (user has already made at least 1 login).
*
* #link https://wordpress.stackexchange.com/a/363376/190376
*/
if ( ! class_exists( 'DisableMailChange' ) ) {
class DisableMailChange {
public function __construct() {
add_action( 'personal_options_update', array( $this, 'disable_mail_change_BACKEND' ), 5 );
add_action( 'show_user_profile', array( $this, 'disable_mail_change_HTML' ) );
}
public function disable_mail_change_BACKEND( $user_id ) {
if ( ! get_user_meta( get_current_user_id(), 'is_new_user', true ) ) {
if ( ! current_user_can( 'manage_options' ) ) {
$user = get_user_by( 'id', $user_id );
$_POST['email'] = $user->user_email;
};
};
}
public function disable_mail_change_HTML( $user ) {
if ( ! get_user_meta( get_current_user_id(), 'is_new_user', true ) ) {
if ( ! current_user_can( 'manage_options' ) ) {
echo '<script>document.getElementById("email").setAttribute("disabled","disabled");</script>';
};
};
}
};
new DisableMailChange();
};
A number of questions have been raised on StackOverflow in regards to using email address as username when registering via My Account. I have the working code included below.
$this->loader->add_filter('pre_user_login', $plugin_public, 'audp_wc_register_email_as_username' );
public function audp_wc_register_email_as_username( $user_login ) {
if ( isset ( $_POST['email'] ) ) {
$user_login = $_POST['email'];
}
return $user_login;
}
I've updated my own question with the following code to update the user_login details.
$this->loader->add_action( 'woocommerce_save_account_details', $plugin_public, 'audp_wc_update_email_and_username', 20, 1 );
public function audp_wc_update_email_and_username() {
if ( isset( $_POST['account_email'] ) ) {
global $wpdb;
$user_id = get_current_user_id();
$new_login = $_POST['account_email'];
// Update user user_login
$wpdb -> update( $wpdb -> users,
array( 'user_login' => $new_login, 'user_nicename' => $new_nicename ),
array( 'ID' => $user_id )
);
}
// Update nickname
update_user_meta( $user_id, 'nickname', $new_login );
}
I've updated the question to include the full code. Because we are dealing with user_login we cannot use wp_update_user(), I have used the class description above, but the below code is standard (non-class) functions.
add_action( 'woocommerce_save_account_details', 'audp_wc_update_email_and_username', 20, 1 );
function audp_wc_update_email_and_username() {
if ( isset( $_POST['account_email'] ) ) {
global $wpdb;
$user_id = get_current_user_id();
$new_login = $_POST['account_email'];
// Update user user_login
$wpdb -> update( $wpdb -> users,
array( 'user_login' => $new_login, 'user_nicename' => $new_nicename ),
array( 'ID' => $user_id )
);
// Update nickname
update_user_meta( $user_id, 'nickname', $new_login );
}
I have a plugin for allowing duplicate emails on registration and that is working fine if I have used the plugin. I want to use plugin code in functions.php (meaning to say run the plugin code functionality with themes).
Below is my plugin code:
/**
* Plugin Name: Allow duplicate emails on registration
* Plugin URI: http://wordpress.stackexchange.com/a/125129/26350
*/
add_action( 'plugins_loaded', array( 'Allow_Duplicate_Emails_Registration', 'get_instance' ) );
class Allow_Duplicate_Emails_Registratn
{
private $rand = '';
static private $instance = NULL;
static public function get_instance()
{
if ( NULL === self::$instance )
self::$instance = new self;
return self::$instance;
}
public function __construct()
{
// Generate some random string:
$this->rand = '_remove_' . rand( 0, 99999);
add_filter( 'user_registration_email', array( $this, 'user_registration_email' ) );
add_action( 'user_register', array( $this, 'user_register' ) );
}
public function user_registration_email( $user_email )
{
// inject random string into the email
$user_email = str_replace( '#', $this->rand . '#', $user_email );
return $user_email;
}
public function user_register( $user_id )
{
// retrieve the newly registered user object
$user = get_user_by( 'id', $user_id );
// remove the random string
$user_email = str_replace( $this->rand . '#', '#', $user->user_email );
// update the email of the newly registered user
$args = array(
'ID' => $user_id,
'user_email' => $user_email,
);
$result = wp_update_user( $args );
}
}
And I have changed the above code below and put in functions.php, but it is not working:
add_action( 'init', array( 'Allow_Duplicate_Emails_Registration', 'get_instance' ) );
I have solved the problem. Add the below code in the wp-content/themes/my-themes/functions.php file:
<?php
add_filter( 'user_registration_email', array( $this, 'user_registration_email' ) );
add_action( 'user_register', array( $this, 'user_register' ) );
function user_registration_email( $user_email )
{
// Inject a random string into the email
$user_email = str_replace( '#', $this->rand . '#', $user_email );
return $user_email;
}
function user_register( $user_id )
{
// Retrieve the newly registered user object
$user = get_user_by( 'id', $user_id );
// Remove the random string
$user_email = str_replace( $this->rand . '#', '#', $user->user_email );
// Update the email of the newly registered user
$args = array(
'ID' => $user_id,
'user_email' => $user_email,
);
$result = wp_update_user( $args );
}
?>