I'm working on a property feed importer for a website I'm having some trouble getting the Taxonomy to synchronize correctly.
This is the code to add the translations
foreach($languages as $lang => $data){
$translation = 0;
$trans_arr = array(
'post_author'=>$author,
'post_content'=>$property->get_description($lang),
'post_title'=>$property->get_title($lang),
'post_status'=>'importing', //Don't show the property while its still importing
'post_type'=>'property',
'comment_status'=>'closed',
'ping_status'=>'closed',
'post_date'=>$property->date, //Use the property date to keep track of changes
'meta_input'=>array("kyero_data"=>json_encode(array('feed' => $feed_token,"kyeor_id" => $property->ref, "lang"=>$lang ))),
);
// Check if the translation already exists so we update it instead
$_translation = icl_object_id( $post, 'property', false, $lang );
if($_translation){
$trans_arr['ID'] = $_translation;
}
// Create/update the translation
$translation = wp_insert_post($trans_arr);
// If successfull set the correct language information of the post.
if(is_int($translation) && $translation !== 0){
wp_set_post_terms( $translation, $tax['property_feature'], 'property_feature', false );
wp_set_post_terms( $translation, $tax['property_status'], 'property_status', false );
$sitepress->set_element_language_details($translation, 'post_property', $def_trid, $lang);
set_post_thumbnail($translation, $feat);
wp_publish_post($translation);
}
}
$languages is an array with all the languages (keys are the language codes) except for the default language.
$tax['property_feature'] and $tax['property_status'] contain a list of IDs are filled by the feed and are only available in the default language inside the feed (ID are fetched by get_term_by using the name) however they are translated in the website.
Please can some one help me out, is there a way to synchronize the the taxonomy of a single WP_Post object with its parent programmatically
I found a solution and taught I mention it here in case some one else may need it at some point.
Instead of using the wpml class oject to set the language $sitepress->set_element_language_details using the hook wpml_set_element_language_details
foreach($languages as $lang => $data){
$translation = 0;
$trans_arr = array(
'post_author'=>$author,
'post_content'=>$property->get_description($lang),
'post_title'=>$property->get_title($lang),
'post_status'=>'importing', //Don't show the property while its still importing
'post_type'=>'property',
'comment_status'=>'closed',
'ping_status'=>'closed',
'post_date'=>$property->date, //Use the property date to keep track of changes
'meta_input'=>array("kyero_data"=>json_encode(array('feed' => $feed_token,"kyeor_id" => $property->ref, "lang"=>$lang ))),
);
// Check if the translation already exists so we update it instead
$_translation = icl_object_id( $post, 'property', false, $lang );
if($_translation){
$trans_arr['ID'] = $_translation;
}
// Create/update the translation
$translation = wp_insert_post($trans_arr);
// If successfull set the correct language information of the post.
if(is_int($translation) && $translation !== 0){
do_action('wpml_set_element_language_details',array('element_id'=>$translation, 'element_type'=>'post_property', 'trid'=>$def_trid, 'language_code'=>$lang, 'source_language_code'=>$default_lang));
set_post_thumbnail($translation, $feat);
wp_publish_post($translation);
}
}
Related
I am trying to add some download permissions to all previous orders via a script to do them in batch. The script seems to work fine expect for one thing. Here is the script…
function update_download_permissions(){
$orders = get_posts( array(
'post_type' => 'shop_order',
'post_status' => 'wc-completed',
'posts_per_page' => -1
) );
foreach ( $orders as $order ) {
wc_downloadable_product_permissions( $order->ID, true );
}
}
The problem is the wc_downloadable_product_permissions function is producing duplicate entries in the wp_woocommerce_downloadable_product_permissions table.
I tried to set the second argument to false (the default) but that resulted in no permissions being created.
Does anybody have any ideas as to why duplicate download permissions are being set?
Cheers!
I came across your question after digging through some of the WooCommerce source code, while attempting to add an item to an existing order and then regenerate the permissions.
The reason wc_downloadable_product_permissions() will create duplicate permission entries is because it does not check for any existing permissions. It simply inserts another entry into the permissions table for every item in the order, which is no good because this will then show up as another download in both the admin and user account frontend.
The second force parameter (poorly documented), is related to a boolean flag that indicates whether wc_downloadable_product_permissions() has run before. The boolean is set to true at the end of the function via the set_download_permissions_granted method. If force is true, it will ignore the boolean. If force is false, and the boolean is true, the function will return near the start.
I created this function which uses the same functions as used by the admin Order action "Regenerate download permissions":
/**
* Regenerate the WooCommerce download permissions for an order
* #param Integer $order_id
*/
function regen_woo_downloadable_product_permissions( $order_id ){
// Remove all existing download permissions for this order.
// This uses the same code as the "regenerate download permissions" action in the WP admin (https://github.com/woocommerce/woocommerce/blob/3.5.2/includes/admin/meta-boxes/class-wc-meta-box-order-actions.php#L129-L131)
// An instance of the download's Data Store (WC_Customer_Download_Data_Store) is created and
// uses its method to delete a download permission from the database by order ID.
$data_store = WC_Data_Store::load( 'customer-download' );
$data_store->delete_by_order_id( $order_id );
// Run WooCommerce's built in function to create the permissions for an order (https://docs.woocommerce.com/wc-apidocs/function-wc_downloadable_product_permissions.html)
// Setting the second "force" argument to true makes sure that this ignores the fact that permissions
// have already been generated on the order.
wc_downloadable_product_permissions( $order_id, true );
}
I found the best way to update order download is to hook into the save_post action hook and check if it's a product that's being updated
there you can get order ids by product id and update just orders that relate to that specific product.
it's more efficient
function get_orders_ids_by_product_id($product_id) {
global $wpdb;
$orders_statuses = "'wc-completed', 'wc-processing', 'wc-on-hold'";
return $wpdb->get_col(
"
SELECT DISTINCT woi.order_id
FROM {$wpdb->prefix}woocommerce_order_itemmeta as woim,
{$wpdb->prefix}woocommerce_order_items as woi,
{$wpdb->prefix}posts as p
WHERE woi.order_item_id = woim.order_item_id
AND woi.order_id = p.ID
AND p.post_status IN ( $orders_statuses )
AND woim.meta_key IN ( '_product_id', '_variation_id' )
AND woim.meta_value LIKE '$product_id'
ORDER BY woi.order_item_id DESC"
);
}
// if you don't add 3 as as 4th argument, this will not work as expected
add_action('save_post', 'prefix_on_post_update', 10, 3);
function prefix_on_post_update($post_id, $post, $update) {
if ($post->post_type == 'product') {
$orders_ids = get_orders_ids_by_product_id($post_id);
foreach ($orders_ids as $order_id) {
$data_store = WC_Data_Store::load('customer-download');
$data_store->delete_by_order_id($order_id);
wc_downloadable_product_permissions($order_id, true);
}
}
}
I've been stuck trying to apply a Drupal commerce coupon for about 2 days now. I have taken care of validating the coupon and currently stumped when I'm trying to redeem it.
So inside my callback function I'm calling:
my_module_coupons_coupon_redeem($coupon);
And inside the redeem function I have:
function my_module_coupons_coupon_redeem($coupon) {
global $user;
$uid = $user->uid;
$order = commerce_cart_order_load($uid);
// Wrap the order for easy access to field data.
$order_wrapper = entity_metadata_wrapper('commerce_order', $order);
// Create the new line item.
$line_item = commerce_coupon_line_item_new($coupon, $order->order_id);
$line_item->commerce_unit_price = array('und' => array(
'0' => array('amount' => 500, 'currency_code' => commerce_default_currency())
));
$line_item_wrapper = entity_metadata_wrapper('commerce_line_item', $line_item);
if (!is_null($line_item_wrapper->commerce_unit_price->value())) {
// Add the base price to the components array.
if (!commerce_price_component_load($line_item_wrapper->commerce_unit_price->value(), 'base_price')) {
$line_item_wrapper->commerce_unit_price->data = commerce_price_component_add(
$line_item_wrapper->commerce_unit_price->value(),
'base_price',
$line_item_wrapper->commerce_unit_price->value(),
TRUE
);
}
}
$line_item_wrapper->commerce_total->data = $line_item_wrapper->commerce_unit_price->data;
//$line_item_wrapper->commerce_product->data = $coupon;
// Save the line item now so we get its ID.
commerce_line_item_save($line_item);
// Add it to the order's line item reference value.
$order_wrapper->commerce_line_items[] = $line_item;
commerce_order_save($order);
}
The coupon line_item is being saved on the DB but when I refresh the cart page I get the following error:
EntityMetadataWrapperException: Unknown data property commerce_product. in EntityStructureWrapper->getPropertyInfo()
Just wondering if this is the correct way of applying coupons without the use of Rules and should I be saving it as a line item in the first place?
Take a look in sites/all/modules/commerce_coupon/oncludes/commerce_coupon.checkout_pane.inc
global $user;
$uid = $user->uid;
$error = '';
// load the cart
$order = commerce_cart_order_load($uid);
// load the coupon from the coupon code
// see MySQL -> your-schema, table: commerce_coupon, column: code
$coupon = commerce_coupon_redeem_coupon_code($code, $order, $error);
// 302 to the cart
drupal_goto('checkout/' . $order->order_id);
I realized this was posted a few years ago, but there was no working answer and I still couldn't find a complete solution for it anywhere.
After you've added the line item you still need to add the commerce coupon reference to the order like so:
$order_wrapper->commerce_coupon_order_reference[] = $coupon;
This way it won't get removed when commerce_coupon_commerce_cart_order_refresh() runs.
I am using the gravity form on my site. I am working on create the custom report for this I have need gravity form fields name and id based on specific form id.Please let me know how I can do it.
I am using the following function but it is showing all forms info based on it. it is looking very hard to parse it. Please let me know any function so I can get fields name easily.
$forms = RGFormsModel::get_forms_by_id(13);
try this
function get_all_form_fields($form_id){
$form = RGFormsModel::get_form_meta($form_id);
$fields = array();
if(is_array($form["fields"])){
foreach($form["fields"] as $field){
if(isset($field["inputs"]) && is_array($field["inputs"])){
foreach($field["inputs"] as $input)
$fields[] = array($input["id"], GFCommon::get_label($field, $input["id"]));
}
else if(!rgar($field, 'displayOnly')){
$fields[] = array($field["id"], GFCommon::get_label($field));
}
}
}
//echo "<pre>";
//print_r($fields);
//echo "</pre>";
return $fields;
}
It's not that hard to parse:
$fields=array();
foreach ( $forms['fields'] as $field ) {
$fields[]=array($field['id'] => $field['inputName']);
}
P.S. I'm assuming you use Gravity Forms < 1.7 as RGFormsModel::get_forms_by_id is a deprecated function since 1.7
// Get the Form fields
$form = RGFormsModel::get_form_meta($form_id);
// Run through the fields to grab an object of the desired field
$field = RGFormsModel::get_field( $form, $field_id );
I use the above to get a specific field I want to filter the value of. The $field contains an object with all the properties you want.
echo $field->label // Gets the label
echo $field->inputName // Gets the name
echo $field->type // Gets the type
echo $field->cssClass // Gets the CSS Classes as a string
You are able to get the entered value/content of a field by using rgpost() and by referencing the id ($field->id).
// Check the entered value of every field
foreach( $form['fields'] as &$field ) {
// Get the content for the specific field
$fieldContent = rgpost( "input_".$field->id );
// Check the content
if ( $fieldContent == ... ){}
}
I have to add to my cart some line items with a custom amount.
The commerce product is saved with price = 0, and my module compute the price and add the line item to the cart/order, but i dont understand how to set programmatically the price.
I've read about using Rules, but I need my module to be able to set/alter the price, without invoking rules.
I've tryed with an entity wrapper, i tryed to alter the line item created with commerce_product_line_item_new(), but nothing, when the line item gets into the cart always has the original product price (in my case, 0).
How to alter a line item price programmatically?
My code so far looks like:
// For debugging, this function is called by hook_menu()
function mymodule_test($product_id)
{
global $user;
$user = user_load($user->uid);
$order = commerce_cart_order_load($user->uid);
$order_wrapper = entity_metadata_wrapper('commerce_order', $order);
$product = commerce_product_load($product_id);
$line_item = commerce_product_line_item_new(
$product,
1,
0,
array(
),
'cover'
);
$line_item_wrapper = entity_metadata_wrapper("commerce_line_item", $line_item);
$line_item_wrapper->commerce_unit_price->data = commerce_price_component_add(
$line_item_wrapper->commerce_unit_price->value(),
'base_price',
array(
'amount' => 1234,
'currency_code' => 'EUR',
'data' => array(),
),
TRUE
);
$insert_line_item = commerce_cart_product_add($user->uid, $line_item_wrapper->value(), FALSE);
return 'done';
}
The strange thing, is that I tryed to adapt the code of commerce_line_item_unit_price_amount() found in commerce/modules/line_item/commerce_line_item.rules.inc, but this test:
<?php
global $user;
$product = commerce_product_load(4); // my commerce product for test
$line_item = commerce_product_line_item_new(
$product,
1,
0,
array(
),
'cover' // I do have this line_items type
);
// manually set amount and component name
$amount = 1234;
$component_name = 'base_price'; // tryed with discount, nothing change
$wrapper = entity_metadata_wrapper('commerce_line_item', $line_item);
$unit_price = commerce_price_wrapper_value($wrapper, 'commerce_unit_price', TRUE);
// Calculate the updated amount and create a price array representing the
// difference between it and the current amount.
$current_amount = $unit_price['amount'];
$updated_amount = commerce_round(COMMERCE_ROUND_HALF_UP, $amount);
$difference = array(
'amount' => $updated_amount - $current_amount,
'currency_code' => $unit_price['currency_code'],
'data' => array(),
);
// Set the amount of the unit price and add the difference as a component.
$wrapper->commerce_unit_price->amount = $updated_amount;
$wrapper->commerce_unit_price->data = commerce_price_component_add(
$wrapper->commerce_unit_price->value(),
$component_name,
$difference,
TRUE
);
$insert_line_item = commerce_cart_product_add($user->uid, $line_item, FALSE);
?>
still fail, the line_item get into the cart but with the original price of the referenced product.
Any idea?
For those people who don't want to use rules and hope to alter the price directly. Here is my solution:
// Alter the price in list and single product page.
function my_module_commerce_product_calculate_sell_price_line_item_alter($line_item){
$price = 100; //1 dollar
$line_item->commerce_unit_price[LANGUAGE_NONE]['0']['amount'] = $price;
}
// Alter the price in cart & order.
function my_module_commerce_cart_line_item_refresh($line_item, $order_wrapper){
$price = 100; //1 dollar
$line_item->commerce_unit_price[LANGUAGE_NONE]['0']['amount'] = $price;
// Alter the base_price component.
$line_item->commerce_unit_price[LANGUAGE_NONE]['0']['data']['components']['0']['price']['amount'] = $price;
}
If you're looking to ignore whatever previous values have been saved to a line item and recalculate the total from your new amount the function you're looking for is commerce_line_item_rebase_unit_price.
Set the new amount value and then run your line item through there, save the line item and the order:
$line_item_wrapper->commerce_unit_price->amount = 13;
commerce_line_item_rebase_unit_price($line_item_wrapper->value());
commerce_line_item_save($line_item_wrapper->value());
I struggled through this issue all day today and final figured out the correct path to altering line items prices. The problem is that, even if you successfully change the line item price to a custom value, on the next page refresh the cart will reset the line items to match the original product price. Take a look at the commerce_cart_order_refresh() function for details. This function is executed every time an order/cart is loaded on the page and there is no way around it.
It turns out that the proper way to alter a line item price is to either use Rules or to implement the hook_commerce_cart_line_item_refresh() function. Either way, Drupal Commerce need to be able to apply the alteration logic each time the cart/order is loaded.
I ended up creating a custom field in the Line Item where I stored the custom price value I wanted. I then used a Pricing Rule to copy the custom price value to the product price value whenever the cart is refreshed.
The following blog post was very helpful in figuring this out. It shows you how to add a custom field to a line item type and how to setup a pricing rule to copy the custom amount to the unit price.
http://commerceguys.com/blog/using-custom-line-items-provide-donation-feature-drupal-commerce
Recently I had to implement a donation form in Commerce but the Commerce Express Checkout module doesn't handle custom line items. Since it was a donation and all (who is trying to screw the house?), I felt it appropriate to pass the donation amount as a 3rd parameter in URL the Express Checkout module provides. Here is how I went about hacking the module:
I added a new entry to the router:
$items['commerce-express-checkout/%/%/%'] = array(
'title' => 'Express Checkout w/ extra argument',
// 'page callback' => 'commerce_express_checkout_create_order',
'page callback' => 'commerce_express_checkout_create_order_extra',
'page arguments' => array(1, 2, 3),
'access arguments' => array('access checkout'),
'type' => MENU_CALLBACK,
);
I duplicated and tweaked the default callback and tacked '_extra' to it. Note that the "data" property seems to be a static variable store for occasions just such as this and persists the life of the line item.
function commerce_express_checkout_create_order_extra($product_id, $token, $amount) {
if (drupal_hmac_base64($product_id, drupal_get_private_key().drupal_get_hash_salt()) == $token && is_numeric($amount)) {
global $user;
$product = commerce_product_load($product_id);
$product->commerce_price['und'][0]['amount'] = (int)$amount;
$order = ($user->uid) ? commerce_order_new($user->uid, 'checkout_checkout') : commerce_cart_order_new();
commerce_order_save($order);
$price = array('amount' => commerce_round(COMMERCE_ROUND_HALF_UP, $amount), 'currency_code' => commerce_default_currency());
$line_item = commerce_product_line_item_new($product, 1, $order->order_id);
$line_item->data = array('und' => array('0' => $price));
commerce_line_item_save($line_item);
$order_wrapper = entity_metadata_wrapper('commerce_order', $order);
$order_wrapper->commerce_line_items[] = $line_item;
$order->data['type'] = 'commerce_express_checkout_order';
commerce_order_save($order);
drupal_goto('checkout/' . $order->order_id);
return "";
}
return "";
}
Here is the part that ended up being most tricky simply due to the learning curve and not knowing what the heck function to use:
/**
* Implements hook_commerce_cart_line_item_refresh().
*/
function commerce_express_checkout_commerce_cart_line_item_refresh($line_item, $order_wrapper) {
if ($line_item->commerce_product['und'][0]['line_item_label'] == 'DONATE' || $line_item->commerce_product['und'][0]['product_id'] == '11') {
$price = array('amount' => commerce_round(COMMERCE_ROUND_HALF_UP, $line_item->data['und'][0]['amount']), 'currency_code' => commerce_default_currency());
$line_item->commerce_unit_price = array('und' => array('0' => $price));
$line_item_wrapper = entity_metadata_wrapper('commerce_line_item', $line_item);
$line_item_wrapper->commerce_unit_price->data = commerce_price_component_add(
$line_item_wrapper->commerce_unit_price->value(), 'base_price', $price, TRUE
);
}
}
Every single time the cart is modified, it refreshes and attempts to set the products in the cart to their in-code prototype. It seems pretty inefficient to me too, but I could be missing something.
This post pointed me in the right direction for programmatically altering a drupal commerce line item by using hook_commerce_cart_line_item_refersh(). However, some of the answers here are either outright wrong, or very inefficient and sloppy.
This would be a correct working solution for altering the line item type in Drupal Commerce:
/*
* implements hook_commerce_cart_line_item_refresh()
*
*/
function MYMODULE_commerce_cart_line_item_refresh($line_item, $order_wrapper){
$line_wrapper = entity_metadata_wrapper('commerce_line_item', $line_item);
$new_price = 100; //I use a function to calculate the value of $new_price
if(!empty($new_price)){
$line_wrapper->commerce_unit_price->amount->set($new_price);
$line_wrapper->save();
}
}
I created the following token; however, when I try to use site:coupons as a data selector in a loop action
It does not appear in data selection browser. Note that it does appear as replacement pattern when i use for example "Show a message on the site" action.
I spent lot of time searching in the internet and rules' token' issue queue, i tried to read the source codes of core token , token and rules as well. I also found some information too like data selector are no tokens! or rules only works with entities!
So far i couldn't get this to work no matter hard i tried. My data is not entity. Is there anyway to integrate it with rules?
I couldn't find any official documentation on this so i created an issue with hope that some of the rule's experts can help me out.
Note : if i replace site with coupon-link in the following code, it won't even appear as replacement pattern in rules. but it works fine as token anywhere else but in rules
Thanks in advance
<?php
/**
* Implements hook_token_info().
*/
function coupon_link_token_info() {
$types['coupon-link'] = array(
'name' => t("Coupon link coupon info"),
'description' => t("Info about linked coupon via url."),
);
// Andy Pangus specific tokens.
$tokens['site']['coupon-code'] = array(
'name' => t("Coupon Link Coupon Code"),
'description' => t("The code of the coupon entered via url."),
);
$tokens['site']['coupon'] = array(
'name' => t("Coupon Link Coupon"),
'description' => t("The coupon entered via url."),
'type' => 'commerce_coupon'
);
$tokens['site']['coupons'] = array(
'name' => t("Coupon Link List Coupons"),
'description' => t("The coupons entered via url."),
'type' => 'array'
);
return array(
'types' => $types,
'tokens' => $tokens,
);
}
/**
* Implements hook_tokens().
*
* #ingroup token_example
*/
function coupon_link_tokens($type, $tokens, array $data = array(), array $options = array()) {
$replacements = array();
$sanitize = !empty($options['sanitize']);
// Text format tokens.
if ($type == 'site' && __coupon_link_get_coupon_code()) {
//$format = $data['format'];
foreach ($tokens as $name => $original) {
switch ($name) {
case 'coupon-code':
// Since {filter_format}.format is an integer and not user-entered
// text, it does not need to ever be sanitized.
$replacements[$original] = $sanitize ? filter_xss(__coupon_link_get_coupon_code()) : __coupon_link_get_coupon_code();
break;
case 'coupon':
// Since the format name is user-entered text, santize when requested.
$replacements[$original] = __coupon_link_get_coupon(__coupon_link_get_coupon_code());
break;
case 'coupons':
// Since the format name is user-entered text, santize when requested.
$replacements[$original] = array(__coupon_link_get_coupon(__coupon_link_get_coupon_code()));
break;
}
}
}
return $replacements;
}
?>
A few things.
Tokens are formatted as [type:token] as explained on the hook_token_info api page. For your example, it would be [coupon-link:coupon]. I'm not sure why you're appending your tokens to the site array, as your custom coupon token probably has nothing to do with sitewide tokens like *site_url* or *site_name*.
Because the types are machine names, you should change it to coupon_link as machine names with dashes are not Drupal standard.
If you truly get lost, I suggest also looking at the token example from the examples module.