I use custom plugin for Woocommerce shipping method. The plugin run a new class which extends WC_Shipping_MEthod, but the problem is the 'cost' price is calculated in other function outsite the class.
So in the current class I have:
public function calculate_shipping( $package=array() ) {
/* $this->add_rate( array(
'id' => $this->id . $this->instance_id,
'label' => $this->title,
'cost' => 0,
) ); */
$rate = array(
'id' => $this->id,
'label' => $this->title,
'cost' => 0,
'taxes' =>false,
//'calc_tax' => 'per_item'
);
// Register the rate
$this->add_rate( $rate );
}
Outside the class, in another file, I have a function, which calculates the cost for shipping:
//add shipping cost to checkout total
add_action('woocommerce_cart_calculate_fees', 'woo_add_cart_fee');
function woo_add_cart_fee() {
global $woocommerce;
$woocommerce->shipping;
$wc_econt = new WC_Econt_Shipping_Method;
if ( is_checkout() && (bool)$wc_econt->inc_shipping_cost == TRUE ) {
if(!isset($_SESSION)){
session_start();
}
//write_log('session econt customer shipping cost:'.$_SESSION['econt_shipping_cost']);
$extracost = (isset($_SESSION['econt_shipping_cost']) ? $_SESSION['econt_shipping_cost'] : 0 );
if ($extracost != '0') {
WC()->cart->add_fee(__('Econt Express Shipping Method','woocommerce-econt'), $extracost);
}
}
}
If just change 0 to $extracode in the class function it doesn't update the shipping cost on checkout page:
public function calculate_shipping( $package=array() ) {
/* $this->add_rate( array(
'id' => $this->id . $this->instance_id,
'label' => $this->title,
'cost' => $extracost,
) ); */
$rate = array(
'id' => $this->id,
'label' => $this->title,
'cost' => $extracost,
'taxes' =>false,
//'calc_tax' => 'per_item'
);
// Register the rate
$this->add_rate( $rate );
}
I guess the class run before the woo_add_cart_fee function, and that's why it cannot get the value of $extracost variable.
How to resolve this?
In the function to calculate the shipping cost, add a session variable with your new calculated value like this:
//add shipping cost to checkout total
add_action('woocommerce_cart_calculate_fees', 'woo_add_cart_fee');
function woo_add_cart_fee() {
global $woocommerce;
$woocommerce->shipping;
$wc_econt = new WC_Econt_Shipping_Method;
if ( is_checkout() && (bool)$wc_econt->inc_shipping_cost == TRUE ) {
if(!isset($_SESSION)){
session_start();
}
//write_log('session econt customer shipping cost:'.$_SESSION['econt_shipping_cost']);
$extracost = (isset($_SESSION['econt_shipping_cost']) ? $_SESSION['econt_shipping_cost'] : 0 );
if ($extracost != '0') {
WC()->session->set( 'Econt_Express_Shipping_Method' , $extracost );
}
}
}
Then on the calculate shipping function use the WC session variable to set the value for the cost like below:
public function calculate_shipping( $package=array() ) {
/* $this->add_rate( array(
'id' => $this->id . $this->instance_id,
'label' => $this->title,
'cost' => 0,
) ); */
$rate = array(
'id' => $this->id,
'label' => $this->title,
'cost' => WC()->session->get('Econt_Express_Shipping_Method' ),
'taxes' =>false,
//'calc_tax' => 'per_item'
);
// Register the rate
$this->add_rate( $rate );
}
Use the javascript below to update your new shipping value at checkout:
$('body').trigger('update_checkout', { update_shipping_method: true });
You may want to change the value of billing address or shipping address or country to be able to update your value if you use the above code more than once on checkout. This is also where I stuck and am forced to fake these changes so that Woocommerce can update my shipping value...am still looking for the solution though...lol :)
$("#billing_address_1").trigger("keypress").val(function(i,val){return val + 'a';});
Related
Let's say you create a WordPress post and assign it a meta key foo with value bar. You only want to display an ACF field if foo is equal to bar. However, there's no built-in location rule in ACF to do this. How would you solve this problem by creating an ACF custom location rule?
In order to display ACF fields if a post meta key is equal or not equal to some value, use the following snippet. It's based off of the ACF Custom Location Rules guide.
if( ! defined( 'ABSPATH' ) ) exit;
class My_ACF_Location_Post_Meta extends ACF_Location {
public function initialize() {
$this->name = 'post_meta';
$this->label = __( "Post Meta", 'acf' );
$this->category = 'post';
$this->object_type = 'post';
}
public function rule_values($choices, $rule){
if(!acf_is_screen('acf-field-group') && !acf_is_ajax('acf/field_group/render_location_rule')){
return array(
$rule['meta_key'] => $rule['meta_key'],
$rule['meta_value'] => $rule['meta_value']
);
}
ob_start();
acf_render_field(array(
'type' => 'text',
'name' => 'meta_key',
'prefix' => 'acf_field_group[location]['.$rule['group'].']['.$rule['id'].']',
'value' => (isset($rule['meta_key']) ? $rule['meta_key'] : ''),
'placeholder' => 'Meta Key'
));
acf_render_field(array(
'type' => 'text',
'name' => 'meta_value',
'prefix' => 'acf_field_group[location]['.$rule['group'].']['.$rule['id'].']',
'value' => (isset($rule['meta_value']) ? $rule['meta_value'] : ''),
'placeholder' => 'Meta Value'
));
return ob_get_clean();
}
public function rule_operators($choices, $rule){
$choices = [];
$choices['key_is_equal_to_value'] = __('key is equal to value', 'acf');
$choices['key_is_not_equal_to_value'] = __('key is not equal to value', 'acf');
return $choices;
}
public function match( $rule, $screen, $field_group ) {
// Check screen args for "post_id" which will exist when editing a post.
// Return false for all other edit screens.
if( isset($screen['post_id']) ) {
$post_id = $screen['post_id'];
} elseif (isset($screen['attachment_id'])) {
$post_id = $screen['attachment_id'];
} else {
return false;
}
// Load the post meta object for this edit screen.
$post_meta_value = get_post_meta( $post_id, $rule['meta_key'], true );
if( !$post_meta_value ) {
return false;
}
// Compare the Post's meta value to rule meta value.
$result = ( strval($post_meta_value) == $rule['meta_value'] );
// Return result taking into account the operator type.
if( $rule['operator'] == 'key_is_not_equal_to_value' ) {
return !$result;
}
return $result;
}
}
add_action('acf/init', 'my_acf_init_location_types');
function my_acf_init_location_types() {
// Check function exists, then include and register the custom location type class.
if( function_exists('acf_register_location_type') ) {
acf_register_location_type( 'My_ACF_Location_Post_Meta' );
}
}
Please consider the following php class extends the WP_REST_Controller class of Wordpress related to Rest API:
<?php
class MCQAcademy_Endpoint extends WP_REST_Controller {
/**
* Register the routes for the objects of the controller.
*/
public function register_routes() {
$version = '1';
$namespace = 'custompath/v' . $version;
$base = 'endpointbase';
register_rest_route(
$namespace,
'/' . $base,
array(
array(
'methods' => WP_REST_Server::READABLE,
'callback' => array( $this, 'get_items' ),
'permission_callback' => array( $this, 'get_items_permissions_check' ),
'args' => array(),
)
)
);
}
/**
*
*/
public function get_items( $request ) {
$rs = array(
'data' => array(),
'request' => array(
'lang' => 'en',
),
);
$args = array();
$items = get_posts( $args );
foreach( $items as $item ) {
$itemdata = $this->prepare_item_for_response( $item, $request );
$rs['data'][] = $this->prepare_response_for_collection( $itemdata );
}
$rs['wp_get_current_user'] = wp_get_current_user(); // Does not output as expected
return new WP_REST_Response( $rs, 200 );
}
/**
* Check if a given request has access to get items
*/
public function get_items_permissions_check( $request ) {
return true; // to make readable by all
}
/**
* Prepare the item for create or update operation
*/
protected function prepare_item_for_database( $request ) {
return $request;
}
/**
* Prepare the item for the REST response
*/
public function prepare_item_for_response( $item, $request ) {
$data = array(
'ID' => $item->ID,
'post_content' => wpautop($item->post_content),
'post_title' => $item->post_title,
);
return $data;
}
/**
* Get the query params for collections
*/
public function get_collection_params() {
return array(
'page' => array(
'description' => 'Current page of the collection.',
'type' => 'integer',
'default' => 1,
'sanitize_callback' => 'absint',
),
'per_page' => array(
'description' => 'Maximum number of items to be returned in result set.',
'type' => 'integer',
'default' => 10,
'sanitize_callback' => 'absint',
),
'search' => array(
'description' => 'Limit results to those matching a string.',
'type' => 'string',
'sanitize_callback' => 'sanitize_text_field',
),
);
}
// Register our REST Server
public function hook_rest_server(){
add_action( 'rest_api_init', array( $this, 'register_routes' ) );
}
}
$myEndpoint = new MCQAcademy_Endpoint();
$myEndpoint->hook_rest_server();
Everything is going well except calling the wp_get_current_user() function in the get_items() function return empty user even though the user is loggedin in the website.
What is the solution to get the loggedin user info in Rest API endpoint function?
I'm working on a Wordpress/Buddypress project and I would like to make the members list of group always visible whether or not the group is private and the logged member belong to the group.
I was thinking I had to change access and visibility of the nav items so I did that:
function change_access_group_nav_tabs() {
if(bp_is_group()) {
buddypress()->groups->nav->edit_nav( array('visibility' => 'public'), 'members', bp_current_item() );
buddypress()->groups->nav->edit_nav( array('access' => 'anyone'), 'members', bp_current_item() );
}
}
add_action( 'bp_actions', 'change_access_group_nav_tabs' );
But it didn't work…
Any suggestion how to proceed?
I found a workaround, I'am quite not completely satisfied but...
First of all, disable members list on group:
function change_access_group_nav_tabs() {
if(bp_is_group()) {
buddypress()->groups->nav->edit_nav( array( 'user_has_access' => false ), 'members', bp_current_item() );
}
}
add_action( 'bp_actions', 'change_access_group_nav_tabs' );`
(btw, setting the value to true actually make the the nav items always here, but we still can't access the group list on click)
And then I simply add a custom BP Group Extension to make my own members list:
class Group_Extension_List_Members extends BP_Group_Extension {
function __construct() {
$args = array(
'slug' => 'members-list',
'name' => 'Membres',
'access' => array( 'anyone'),
'show_tab' => array( 'anyone'),
'nav_item_position' => 12,
'screens' => array(
'create' => array(
'enabled' => false
),
'edit' => array(
'enabled' => false
),
)
);
parent::init( $args );
}
function display( $group_id = NULL ) {
//Remove user who do not belong to the group on members loop
function filter_for_groups( $members_template_has_members, $members_template, $r ) {
for ($i=sizeof($members_template->members)-1; $i >= 0 ; $i--) {
$user_id = $members_template->members[$i]->id;
if(!groups_is_user_member( $user_id, bp_get_group_id() )){
$members_template->member_count = $members_template->member_count-1;
array_splice($members_template->members, $i, 1);
}
}
if ($members_template->member_count <= 0) {
return '';
} else {
return $members_template_has_members;
}
};
add_filter( 'bp_has_members', 'filter_for_groups', 10, 3 );
require('/Your/theme/custom/members/loop/members-loop.php');
}
}
bp_register_group_extension( 'Group_Extension_List_Members' );
Hope it will help other in the future, And I'm still open to know the good way to proceed.
I am running a WooCommerce v. 3 store and using a payment plugin "Quickpay". The payment plugin supports adding the transactions fee to the order which is great, but it is added without tax to the order i WooCommerce.
I have located the code where the transactions fee is set. I have tried digging through the documentation fra WC, but still can't figure it out.
Can any help me setting it up, so that tax is calculated for the transactions fee?
This is the current code:
/**
* add_transaction_fee function.
*
* Adds order transaction fee to the order before sending out the order confirmation
*
* #access public
*
* #param $fee_amount
*
* #return bool
*/
public function add_transaction_fee($fee_amount)
{
if ($fee_amount > 0) {
$amount = $fee_amount / 100;
$fee = (object) array(
'name' => __('Payment Fee', 'woo-quickpay'),
'amount' => wc_format_decimal($amount),
'taxable' => FALSE,
'tax_class' => NULL,
'tax_data' => array(),
'tax' => 0,
);
if (version_compare( WC_VERSION, '3.0', '<' )) {
$this->add_fee($fee);
} else {
$item = new WC_Order_Item_Fee();
$item->set_props( array(
'name' => $fee->name,
'tax_class' => $fee->tax_class,
'total' => $amount,
'total_tax' => 0,
'order_id' => $this->get_id(),
) );
$item->save();
$this->add_item( $item );
}
$this->set_total( $this->get_total() + $amount );
return TRUE;
}
return FALSE;
}
I have products from some API and want to insert these products. I was able to insert products and use already existing attributes ( or added by UI ).
Adding new term (My Color) to the existing attribute (Color) :
// Add term to the attribute
wp_set_object_terms( $post_id, "My Color", 'pa_color' , true );
Use the added attribute with current product :
// Data for the term to be used
$theData = array(
'pa_color'=>
array(
'name'=>'pa_color',
'value'='My Color',
'is_visible' => '1',
'is_variation' => '1',
'is_taxonomy' => '1'
)
);
// Add this attribute to this product
update_post_meta( $post_id,'_product_attributes',$theData);
How can I add new attribute and use it with the current product, for ex :
RAM : 4GB
I have tried this :
register_taxonomy(
'pa_ram',
'product',
array(
'label' => __( 'RAM' ),
'rewrite' => array( 'slug' => 'size' ),
'hierarchical' => true,
)
);
}
Here is the URL of the attributes I can see/add in UI :
wp-admin/edit.php?post_type=product&page=product_attributes
This did the job for me :
// Insert new attribute
function process_add_attribute($attribute) {
global $wpdb;
if (empty($attribute['attribute_type'])) {
$attribute['attribute_type'] = 'select';
}
if (empty($attribute['attribute_orderby'])) {
$attribute['attribute_orderby'] = 'name';
}
if (empty($attribute['attribute_public'])) {
$attribute['attribute_public'] = 1;
}
if (empty($attribute['attribute_name']) || empty($attribute['attribute_label'])) {
return new WP_Error('error', __('Please, provide an attribute name and slug.', 'woocommerce'));
}
elseif(($valid_attribute_name = valid_attribute_name($attribute['attribute_name'])) && is_wp_error($valid_attribute_name)) {
return $valid_attribute_name;
}
$wpdb->insert($wpdb->prefix.'woocommerce_attribute_taxonomies', $attribute);
do_action('woocommerce_attribute_added', $wpdb->insert_id, $attribute);
flush_rewrite_rules();
delete_transient('wc_attribute_taxonomies');
return true;
}
function valid_attribute_name( $attribute_name ) {
if ( strlen( $attribute_name ) >= 28 ) {
return new WP_Error( 'error', sprintf( __( 'Slug "%s" is too long (28 characters max). Shorten it, please.', 'woocommerce' ), sanitize_title( $attribute_name ) ) );
} elseif ( wc_check_if_attribute_name_is_reserved( $attribute_name ) ) {
return new WP_Error( 'error', sprintf( __( 'Slug "%s" is not allowed because it is a reserved term. Change it, please.', 'woocommerce' ), sanitize_title( $attribute_name ) ) );
}
return true;
}
Call it like :
// Add new attribute
$status = process_add_attribute(array(
'attribute_name' => 'myattribute',
'attribute_label' => 'My Attribute'
));
// Added successfully
if (!is_wp_error($status)) {
// Continue
}