I wanted to add a field in WooCommerce > Settings > Emails tab. In this section there is a list of email template and on clicking any template you can see this screen.
I want to add another field to this section for every template.
I manage to achieve it but I'm feeling its not a proper way.
My code is as follow:
add_filter( 'woocommerce_settings_tabs_email', 'emailText' );
function emailText($args) {
?>
<div class="EmailText">
<label for="EmailText">Email Text</label>
<textarea id="EmailText" class="widefat" name="email_text"></textarea>
</div>
<?php
}
It displays the field but I cannot save anything in it.
The WooCommerce Email Settings tab has a table with all of the available email templates such as 'New Order', 'Canceled Order', 'Failed Order', etc. These are usually defined by extending the WC_Email class. Take a look in /plugins/woocommerce/includes/emails for examples.
As WildProgrammers has pointed out, the 'woocommerce_settings_api_form_fields_{email-id}' can be easily used to add a setting to a specific email class. You can see the definition on the WooCommerce Settings API Code Reference page. It provides us with the array of form fields for the email class.
For this example lets add an additional option to the 'New Order' email template. We can look in the WC_Email_New_Order class constructor (found in class-wc-email-new-order.php) to find/verify the id to replace {email-id} in our hook. In this case the WC_Email_New_Order class id is 'new_order' so to add a setting or form field to the New Order options our hook will look like, add_action( 'woocommerce_settings_api_form_fields_new_order', 'add_my_custom_email_setting',10,1);
Now in the 'add_my_custom_email_setting' function all we need to do is add our new setting to the form_fields array and return it:
function add_my_custom_email_setting($form_fields){
$form_fields['my_custom_id']=['title'=>'My Custom Option',
'description'=>'This is a text area we added to the new order email settings page.',
'type'=>'textarea',
'default'=>''
];
return $form_fields;
}
Take a look at the 'init_form_fields' function in WC_Email class for examples of how to build the options array.
You asked how to add an option to all of the available templates. To do this we could simply manually define a hook for every email class,
add_action('woocommerce_settings_api_form_fields_new_order','add_my_custom_email_setting');
add_action('woocommerce_settings_api_form_fields_failed_order','add_my_custom_email_setting');
add_action('woocommerce_settings_api_form_fields_processing_order','add_my_custom_email_setting');
...
but if we can get a list of all of the id's then we can do it programatically instead. Lucky for us WooCommerce provides us with the 'woocommerce_email_classes' filter which is called right after WC instantiates all of the email classes. Now all we need is a function that iterates over the array of email classes provided by the 'woocommerce_email_classes' filter, get the class id's, and add a hook to 'woocommerce_settings_api_form_fields_{email-id}' for each id:
add_action( 'woocommerce_email_classes', 'add_email_custom_setting');
function add_email_custom_setting($email_class_list){
foreach($email_class_list as $email_class){
add_action( 'woocommerce_settings_api_form_fields_' . $email_class->id,'add_my_custom_email_setting',10,1);
}
return $email_class_list;
}`
We can then use this option in our template files with $email->get_option('my_custom_id');.
You might ask why not simply add the option to the form_fields array in add_email_custom_setting?
Instead of using 'woocommerce_settings_tabs_email' filter try using
'woocommerce_email_settings_after' hook for creating your field.
Related
I'm creating a custom shipping calculator for my WooCommerce store. I need to collect some extra form data from the user to calculate the shipping properly.
I have the form fields added to woocommerce_checkout_after_customer_details. I can probably get this into the cart page without much trouble once I figure out the checkout page functionality, so let's focus on this.
I have a class extending WC_Shipping_Method with a calculate_shipping method a la this tutorial.
In that method, I want to use the extra form data (plus the destination) to calculate the shipping for that customer. Right now I just have this adding a dummy rate, which shows up.
I also created a custom cart-shipping.php file to not show any inputs for the available method in the order review cart, just the label and cost. Since the picking happens in my custom form, having options here is unnecessary.
Am I doing this right, or is this approach super hacky?
If this is the correct approach, how do I access the extra form fields inside the calculate_shipping method?
Have tried
Named the final options in my custom form shipping_method_* which triggers the wc-ajax=update_order_review call... which inits my custom shipping method class, but does not appear to ever call the calculate_shipping method anyhow. The only time this method seems to be called is when I'm actually adding a product to the order.
Also tried
Defined all of my possible delivery options and added them in calculate_shipping like $this->add_rate( $rate );. I think then I can somehow select one (force the user into one) when the cart updates via update_order_review ajax call? Again, the set option should be determined by these fields the user interacts with on the checkout form. But I haven't figured out how to set the shipping on that action yet.
calculate_shipping() is not always called
First off, you should know that the calculate_shipping() method of your shipping method class will only be called when the shipping rates are calculated for the first time (in the current WooCommerce session), each time after a product is added to the cart, and whenever the shipping address is changed — from the cart or checkout page.
But of course, a plugin or custom code can programmatically call the method (or re-calculate the rates) at any times. However, the default behavior is as follows:
Rates are stored in the session based on the package hash to avoid
re-calculation every page load.
And the calculate_shipping() method is executed by the WC_Shipping class through its calculate_shipping_for_package() method. In fact, the above quote was taken from the description of WC_Shipping::calculate_shipping_for_package() which calls WC_Shipping_Method::get_rates_for_package() and eventually the calculate_shipping() method in your own class.
How to get the submitted value of a form field in the checkout form from the calculate_shipping() method
So if you have this in the checkout form:
<input type="text" name="my_field"...>
Then you can get the submitted (POSTed) value using either the $_POST superglobal or the WC_Checkout::get_value() method:
$_POST['my_field']
WC()->checkout->get_value( 'my_field' )
It's as simple as that. :)
To ensure calculate_shipping is called
Run something similar to the following to clear out the session information, which will indicate that calculation still needs to be done. Be aware that if you do this on every page, it will mean that the shipping is constantly being recalculated when it doesn't need to be.
$packages = WC()->cart->get_shipping_packages();
foreach( $packages as $package_key => $package ) {
$session_key = 'shipping_for_package_'.$package_key;
$stored_rates = WC()->session->__unset( $session_key );
}
I'm not sure if this is the best way to accomplish this, but currently I am making this work by setting a session value on woocommerce_checkout_update_order_review and then accessing that value in my calculate_shipping method.
Looking at the WooCommerce documentation, it seems their desired way for you to access things such as user location from the calculate_shipping function is to use their Settings API. https://docs.woocommerce.com/document/settings-api/. Read the docs to see exactly how, but here is a simplification:
Read the location using
$this->init_settings();
$this->location = $this->settings['location'];
Write/Set the user's location with this table:
<?php function admin_options() {
?>
<h2><?php _e('You plugin name','woocommerce'); ?></h2>
<table class="form-table">
<?php $this->generate_settings_html(); ?>
</table><?php
}
The table would be made with
function init_form_fields() {
$this->form_fields = array(
'location' => array(
'title' => __( 'Location', 'woocommerce' ),
'type' => 'text',
'description' => __( 'This is the users address.', 'woocommerce' ),
'default' => __( 'none', 'woocommerce' )
)
);
}
Basiclly, I must Remove the next roles whenever the admin is creating a new user and clicks on the dropdown menu tu display all the available roles:
Contributor
Editor
Unapproved User
Author
It should only display Subscriber and Administrator.
Also I must change the name of Subscriber to something different, where is this code? This problem would seem to me like there was a plugin to it.
To remove a role you can use
<?php remove_role( 'editor'); ?>
and to rename a role add the following php t your functions.php file
function change_role_name() {
global $wp_roles;
if ( ! isset( $wp_roles ) )
$wp_roles = new WP_Roles();
//You can replace "administrator" with any other role "editor", "author", "contributor" or "subscriber"...
$wp_roles->roles['administrator']['name'] = 'Owner';
$wp_roles->role_names['administrator'] = 'Owner';
}
add_action('init', 'change_role_name');
(The above is from here
There is a function called remove_role that WordPress has in its PHP API. you use it like this:
<?php remove_role('author'); ?>
author could be replaced with any role you want. The way you want to use this command is different than most WP commands. This is because it removes the role from the DB so any future times, the role is already removed. I would recommend making a custom WP plugin that just removes the roles you want on activation.WP Plugin Help
I have a custom module that I'm using on prestashop 1.5.4.1 and I need to show some information on the new_order email.
I'm quite new to prestashop and it's the first time I'm messing with it.. I wanna know how I can get the variable of that module and show it on the email template.
I already found where the new_order mail variables are set, but don't know how I can add my new custom variable to it.
Any help will be appreciated. Thanks!
You should override the PaymentModule class and add your custom variable to the email template.
Copy the validateOrder method from classes/PaymentModule.php
Create override/classes/PaymentModule.php and paste the validateOrder method:
<?php
class PaymentModule extends PaymentModuleCore
{
//paste validateOrder here
}
Find the following line in validateOrder:
'{delivery_other}' => $delivery->other,
After that line assign your custom variable. For example:
'{delivery_date}' => $myDeliveryDate,
Remove /cache/class_index.php to clear the cache.
Copy order_conf.html and order_conf.txt from mails/en to themes/YOURTHEME/mails/en if they are not already there. This will prevent PrestaShop updates to overwrite your changes.
Add your custom variable to the new order_conf.html and order_conf.txt. For example:
<tr>
<td align="left">Delivery date: {delivery_date}</td>
</tr>
Hi i am writing a plugin in which i have displayed various entries from database by extending wordpress wp_list_table class.
To display some action links in every single row i have used this function in such following way.
function column_name($item) {
$actions = array(
'edit' => sprintf('Edit',$_REQUEST['page'],'edit',$item['id']),
'delete' => sprintf('Delete',$_REQUEST['page'],'delete',$item['id']),
);
return sprintf('%1$s %2$s', $item['Name'], $this->row_actions($actions) );
}
but from admin page when i click on those links nothing happen only the url changes i searched every ware and most of the examples are using static data in the form of array so what i want to say is how i can make it active by $_GET[] method or is there any other way to do it ?
Back in your settings page, you should add an if statement for the $GET value. (i.e. the callback from your add(sub)menu_page call.
e.g.
if( isset( $_GET['edit-hotel'] ) ) :
// Show my edit hotel form
else :
// Show my WP_List_Table
endif;
You can of course show the list table above your edit form as well if appropriate - this is just an example.
Be aware that the WP_List_table class is marked #access private so shouldn't really be used as part of the API. See this comment from WordPress core developer Andrew Nacin.
I have a module that builds a form that includes a fieldset. Instead of using the <legend> element to render the fieldset title, I want to place this content in a <div> element instead. But I want to change the behavior only for the form returned by my module, so I don't want to place any new functionality into my theme's template.php file.
In mymod.module I have defined:
// custom rendering function for fieldset elements
function theme_mymod_fieldset($element) {
return 'test';
}
// implement hook_theme
function mymod_theme() {
return array(
'mymod_fieldset' => array('arguments' => array('element' => NULL)),
'mymod_form' => array('arguments' => array())
);
}
// return a form that is based on the 'Basic Account Info' category of the user profile
function mymod_form() {
// load the user's profile
global $user;
$account = user_load($user->uid);
// load the profile form, and then edit it
$form_state = array();
$form = drupal_retrieve_form('user_profile_form', $form_state, $account, 'Basic Account Info');
// set the custom #theme function for this fieldset
$form['Basic Account Info']['#theme'] = 'mymod_fieldset';
// more form manipulations
// ...
return $form;
}
When my page gets rendered, I expected to see the fieldset representing 'Basic Account Info' to be wholly replaced by my test message 'test'. Instead what happens is that the <fieldset> and <legend> elements are rendered as normal, but with the body of the fieldset replaced by the test message instead, like this:
<fieldset>
<legend>Basic Account Info</legend>
test
</fieldset>
Why doesn't my #theme function have the chance to replace the entire <fieldset> element? If I wrap a textfield in this function instead, I am able to completely replace the <input> element along with its label. Furthermore, if I provide an override in my site's template.php for theme_fieldset, it works as expected and I am able to completely replace the <fieldset>, so I know it is possible.
What's different about providing #theme functions to fieldsets inside a module?
Have you tried overriding theme_fieldset() instead of using the #theme function? I believe you could do something like this in your .module file:
function mymodule_fieldset($element) {
// do something;
return $html;
}
This would apply to all fieldsets. You could do some kind of check on $element for the fieldsets you want to affect and then use the default implementation for all others.
Take a look at: http://api.drupal.org/api/function/theme_fieldset/6
I know this is an old post -- but I've run into the same issue. I came up with an ugly work around. This is definitely a bug in the Form API. Maybe my temporary fix will be helpful to someone.
I found (and appended) a bug report here: http://drupal.org/node/225698
Worth checking that before trying my hacky fix.
I'm not sure what the children are in $form['Basic Account Info'] in this example, but basically what you can do is use drupal_render() on that fieldset's children, and then recreate a fieldset array separate from $form['Basic Account Info'], theme it with theme() and pass it back to the form array as markup..
$fieldsetElement = array(
//$child is the key of whatever child you need in the fieldset
//you may have to alter this for multiple children, stringing
//together multiple drupal_render calls on each children
//(#children needs to be a string.. unless your theme can handle the array)
'#children'=>drupal_render($form['Basic Account Info'][$child]),
'#attributes'=>array(),//set real attributes
'#title'=>$form['Basic Account Info']['#title']
);
$form['Basic Account Info'] = array(
'#type'=>'markup',//not really needed, is default
'#value'=>theme('mymod_fieldset',$fieldsetElement)
);
super-duper hacking, likely causes disconnect with form state and potential validation failure -- but both are fixable by trial and error with the form api. I wouldn't recommend this unless you really want to get your hands dirty with PHP and drupal form API, but that's really the only way, unless you can live without variable fieldset themes in your module... Maybe try prefix and suffix?
This is just off the top of my head but maybe the difference is because a fieldset is not a form element but just a seperator or a grouper, if you will. Maybe the #theme callback is only for form elements?
The concept of your code works, meaning you can do what you want to do.
There are some things that can explain why it doesn't work.
The fieldset is not $form['Basic Account Info'].
Need to clear cache.
$form['Basic Account Info']['#theme'] is lost/overridden later in the code execution.
Try to take a look at $form before you do any of the moderations. When I tried to copy your code I run into a bug:
user.pages.inc file needed to be loaded
I was having the same issue.
You need to use #theme_wrappers instead of #theme
'#type' => 'fieldset',
'#theme_wrappers' => array('mymodule_fieldset'),