Order exporting breaks the CSV - php

So I am trying to export the orders via handle_bulk_actions-edit-shop_order and exporting is working fine.
For example:
Exporting is working fine when someone is inserting Name as First Name but it brokes the CSV when someone is inserting the value like Mr, First Name same happening with the address section as well, Below is the code I am using.
This is what I have done so far but seems like something is missing that is why it is breaking the csv format
add_filter( 'bulk_actions-edit-shop_order', 'custom_bulk_actions' );
function custom_bulk_actions( $actions ) {
$actions['export_orders_csv'] = __( 'Export Orders to CSV', 'text_domain' );
return $actions;
}
add_action( 'handle_bulk_actions-edit-shop_order', 'custom_bulk_action_handler', 10, 3 );
function custom_bulk_action_handler( $redirect_to, $doaction, $post_ids ) {
if ( $doaction !== 'export_orders_csv' ) {
return $redirect_to;
}
// Export orders as CSV code goes here...
$csv_data = ''; // Generate CSV data string
// Add headings to CSV string
$csv_data .= 'Order ID, Name, Phone Number, Address, City, Product Name, Product URL, Quanitity, Total' . "\n";
// Get order details for each order
foreach ( $post_ids as $post_id ) {
$order = wc_get_order( $post_id );
// Add order data to CSV string
$csv_data .= $order->get_id() . ',';
$csv_data .= $order->get_billing_first_name() . ',';
$csv_data .= $order->get_billing_phone() . ',';
// Add billing address to CSV string
$data = $order->get_data();
$billing_address_1 = $data['billing']['address_1'];
$csv_data .= $billing_address_1 . ',';
$csv_data .= $order->get_billing_city() . ',';
// Add product details to CSV string
$items = $order->get_items();
$product_names = array();
$product_urls = array();
foreach ( $items as $item ) {
$product = $item->get_product();
$product_names[] = $product->get_name();
$product_urls[] = $product->get_permalink();
$product_quantities['qty'] = $item->get_quantity();
}
$csv_data .= implode( ',', $product_names ) . ",";
$csv_data .= implode( ',', $product_urls ) . ",";
$csv_data .= implode( ',', $product_quantities ) . ",";
$csv_data .= $order->get_total() . "\n";
}
// Send CSV data as download
header( 'Content-Type: text/csv; charset=utf-8' );
header( 'Content-Disposition: attachment; filename=orders.csv' );
echo $csv_data;
exit;
}
[This is how the export file looks like after exporting]

Fields with commas in the string need to be quoted within a CSV file. Wrap your database fields in quotation marks.
$csv_data .= '"' . $order->get_billing_first_name() . '",';
Alternativly investigate the function fputcsv() which will help with this.

Related

Ultimate member custom tab is not working after update

I'm using ultimate member for years, I created a custom tab in Edit Profile to update a value. In fact my users (students) can edit (add or remove) their pre-registered courses. The problem is that when they try to add or remove course, an error occurs, "An error has encountered". Surprisingly, they can remove the course even though it shows an error, but they can't add any course. It was working flawlessly till the latest update of Ultimate member. The code is as follows:
* */
/* create new tab */
add_filter('um_account_page_default_tabs_hook', 'CoursesTab', 100 );
function CoursesTab( $tabs ) {
$tabs[800]['CoursesTab']['icon'] = 'um-faicon-pencil'; // tab icon
$tabs[800]['CoursesTab']['title'] = 'Registered Courses'; // tab title
$tabs[800]['CoursesTab']['submit_title'] = 'Update'; // button text
$tabs[800]['CoursesTab']['custom'] = true;
return $tabs;
}
/* make our new tab hookable */
add_action('um_account_tab__CoursesTab', 'um_account_tab__CoursesTab');
function um_account_tab__CoursesTab( $info ) {
extract( $info );
$output = UM()->account->get_tab_output('CoursesTab');
if ( $output ) { echo $output; }
}
/* Finally we add some content in the tab */
add_filter('um_account_content_hook_CoursesTab', 'um_account_content_hook_CoursesTab');
function um_account_content_hook_CoursesTab( $output ){
ob_start();
$id = um_user('ID');
$output = '<div class="um-field">';
$names = array('course','course');
$fields = array();
foreach( $names as $name ){
$fields[ $name ] = UM()->builtin()->get_specific_field( $name );
}
$fields = apply_filters('um_account_secure_fields', $fields, $id);
foreach( $fields as $key => $data ){
$output .= UM()->fields()->edit_field( $key, $data );
}
$output .= '</div>';
$output .= ob_get_contents();
ob_end_clean();
return $output;
}
/* ensure that the custom fields are updated when the account is updated */
add_action('um_account_pre_update_profile', 'getUMFormData', 100);
function getUMFormData(){
$id = um_user('ID');
$names = array('course','course'); // ADD THE META-KEYS HERE
foreach( $names as $name )
update_user_meta( $id, $name, $_POST[$name] );
}
I searched a lot, however, found no clue how to solve the issue.
Thank you
I’ve added codes in this function:
add_filter('um_account_content_hook_CoursesTab', 'um_account_content_hook_CoursesTab');
function um_account_content_hook_CoursesTab( $output ){
ob_start();
$id = um_user('ID');
$output = '<div class="um-field">';
$names = array('course','course');
$fields = array();
foreach( $names as $name ){
$fields[ $name ] = UM()->builtin()->get_specific_field( $name );
}
$fields = apply_filters('um_account_secure_fields', $fields, $id);
foreach( $fields as $key => $data ){
$output .= UM()->fields()->edit_field( $key, $data );
}
$output .= '<input type="hidden" name="um_account_nonce_'. esc_attr( 'coursestab' ).'" value="'.esc_attr( wp_create_nonce( 'um_update_account_coursestab' ) ).'" />';
$output .= '</div>';
$output .= ob_get_contents();
ob_end_clean();
return $output;
}
I’ve added a hidden nonce field:
$output .= '<input type="hidden" name="um_account_nonce_'. esc_attr( 'coursestab' ).'" value="'.esc_attr( wp_create_nonce( 'um_update_account_coursestab' ) ).'" />';
Please try it and let me know if you’re still encountering issue.
Regards,

Display certain attributes on custom tab in woocommerce

I am trying to display certain attributes that have data on a custom tab in woocommerce. I have found several examples of code but none are working for me.
I was given the following code
add_filter( 'woocommerce_display_product_attributes', 'remove_product_information', 10, 2 );
function remove_product_information( $product_attributes, $product ) {
// Remove an attribute from the array
unset($product_attributes['color']);
return $product_attributes;
}
echo wc_display_product_attributes( $product );
but its not filtering anything out it still displays 'color' or any other attribute name I put in there. Also I need t filter out several attributes, so do I just add additional unset lines? or is there a cleaner way to do that? Any insight on this?
Solution 1:
If you want to customize single product page attributes, You need to customize/override the product attribute page with your custom attribute page, after that, you can modify the attribute accordingly.
To customise the page like:
Copy
plugins/woocommerce/templates/single-product/product-attributes.php
To
themes/your-theme/woocommerce/single-product/product-attributes.php
and modify that.
Solution 2:
You have to define first the desired product attributes slugs in an array, after that you will get in single product pages:
add_action( 'display_product_attributes', 'display_product_attributes', 25 );
function display_some_product_attributes(){
// HERE define the desired product attributes to be displayed
$defined_attributes = array('fyllighet', 'carrier', 'billing-e-number');
global $product;
$attributes = $product->get_attributes();
if ( ! $attributes ) {
return;
}
$out = '<ul class="taste-attributes">';
foreach ( $attributes as $attribute ) {
// Get the product attribute slug from the taxonomy
$attribute_slug = str_replace( 'pa_', '', $attribute->get_name() );
// skip all non desired product attributes
if ( ! in_array($attribute_slug, $defined_attributes) ) {
continue;
}
// skip variations
if ( $attribute->get_variation() ) {
continue;
}
$name = $attribute->get_name();
if ( $attribute->is_taxonomy() ) {
$terms = wp_get_post_terms( $product->get_id(), $name, 'all' );
// get the taxonomy
$tax = $terms[0]->taxonomy;
// get the tax object
$tax_object = get_taxonomy($tax);
// get tax label
if ( isset ( $tax_object->labels->singular_name ) ) {
$tax_label = $tax_object->labels->singular_name;
} elseif ( isset( $tax_object->label ) ) {
$tax_label = $tax_object->label;
// Trim label prefix since WC 3.0
if ( 0 === strpos( $tax_label, 'Product ' ) ) {
$tax_label = substr( $tax_label, 8 );
}
}
$out .= '<li class="' . esc_attr( $name ) . '">';
$out .= '<p class="attribute-label">' . esc_html( $tax_label ) . ': </p> ';
$tax_terms = array();
foreach ( $terms as $term ) {
$single_term = esc_html( $term->name );
// Insert extra code here if you want to show terms as links.
array_push( $tax_terms, $single_term );
}
$out .= '<span class="attribute-value">' . implode(', ', $tax_terms) . '</span><progress value="' . implode(', ', $tax_terms) .
'" max="10"><div class="progress-bar"><span style="width:'
. implode(', ', $tax_terms) . '0%">'
. implode(', ', $tax_terms) . '</span></div></progress></li>';
} else {
$value_string = implode( ', ', $attribute->get_options() );
$out .= '<li class="' . sanitize_title($name) . ' ' . sanitize_title( $value_string ) . '">';
$out .= '<p class="attribute-label">' . $name . ': </p> ';
$out .= '<progress value="' . esc_html( $value_string ) . '" max="10"></progress></li>';
}
}
$out .= '</ul>';
echo $out;
}
So the code I posted does work, I was just using the wrong name for the attribute. I needed to add 'attribute_pa_' so for example 'attribute_pa_brand'

WooCommerce: function with included php file doesn't get executed at the right time

For the past week I have been trying to solve a problem related to a custom php script for WooCommerce.
The goal is to add functionality to the bulk order and retrieve the orders I selected and print them in an excel file.
The code is separated into two php files. One to grab the orders and print them on a text file. The second is the printing of the text file to Excel.
Based on How to get WooCommerce order details answer code here is my main script:
// Adding to admin order list bulk dropdown a custom action 'custom_downloads'
add_filter( 'bulk_actions-edit-shop_order', 'downloads_bulk_actions_edit_product', 20, 1 );
function downloads_bulk_actions_edit_product( $actions ) {
$actions['write_downloads'] = __( 'Download orders', 'woocommerce' );
return $actions;
}
// Make the action from selected orders
add_filter( 'handle_bulk_actions-edit-shop_order', 'downloads_handle_bulk_action_edit_shop_order', 10, 3 );
function downloads_handle_bulk_action_edit_shop_order( $redirect_to, $action, $post_ids ) {
if ( $action !== 'write_downloads' ) return $redirect_to; // Exit
$processed_ids = array();
$file = WP_PLUGIN_DIR."/xplugin/Order.txt";
$myfile = fopen($file, "w") or die("STOPP,unable to open file!");
foreach ( $post_ids as $post_id ) {
$order = wc_get_order( $post_id );
$order_data = $order->get_data();
$order_billing_first_name = $order_data['shipping']['first_name'];
$order_billing_last_name = $order_data['shipping']['last_name'];
$order_billing_address_1 = $order_data['shipping']['address_1'];
$order_billing_address_2 = $order_data['shipping']['address_2'];
$order_billing_postcode = $order_data['shipping']['postcode'];
//Hvis nummeret starter på 0 så må du fikse excel
$order_billing_phone = $order_data['billing']['phone'];
//hvis antallet tegn er 8 eller mindre må du legge på "+47"
if( mb_strlen($order_billing_phone) <= 8) {
$order_billing_phone = '+47'. $order_billing_phone;
}
$order_billing_email = $order_data['billing']['email'];
$Pose_på_dør = 'YES';
//Skriver til en den nyåpne tekstfila
fwrite($myfile,
$order_billing_first_name. ' ' .
$order_billing_last_name. '; ' .
$order_billing_address_1. '; ' .
$order_billing_address_2.'; ' .
$order_billing_postcode.'; ' .
$order_billing_first_name. ' ' .
$order_billing_last_name. '; ' .
$order_billing_phone.'; ' .
$order_billing_email.'; ' .
$order_billing_first_name. ' ' .
$order_billing_last_name. '; ' .
$order_billing_first_name. ' ' .
$order_billing_last_name. '; ' .
$Pose_på_dør. "\n"
);
$processed_ids[] = $post_id;
}
printExcel();
fclose($myfile);
return $redirect_to = add_query_arg( array(
'write_downloads' => '1',
'processed_count' => count( $processed_ids ),
'processed_ids' => implode( ',', $processed_ids ),
), $redirect_to );
}
// The results notice from bulk action on orders
add_action( 'admin_notices', 'downloads_bulk_action_admin_notice' );
function downloads_bulk_action_admin_notice() {
if ( empty( $_REQUEST['write_downloads'] ) ) return; // Exit
$count = intval( $_REQUEST['processed_count'] );
printf( '<div id="message" class="updated fade"><p>' .
_n( 'Processed %s Order for downloads.',
'Processed %s Orders for downloads.',
$count,
'write_downloads'
) . '</p></div>', $count );
}
function printExcel() {
include 'print.php';
}
Both scripts behave well, but when I go about combining them with an
function printExcel() {
include 'print.php';
}
it goes wrong.
I'm not able to execute the "print.php" script at the right time.I would like it to run after admin notice is done updating.
I have tried to make an action for different hooks, but not successfully. Can anyone point me in the right direction?
Any suggestions on how to improve the script are much appreciated.

Woocommerce set customer provided note also return html tags

I am making some changes to customer provided note and order note for client. Here is my code
add_action( 'woocommerce_checkout_update_order_meta', 'dvs_add_tm_data_order_note' );
function dvs_add_tm_data_order_note($order_id) {
$options = THEMECOMPLETE_EPO_API()->get_option( $order_id ,'all' );
foreach ($options as $item_id => $epos){
$item = new WC_Order_Item_Product($item_id);
$product = $item->get_product();
$output .= "<br><strong>{$product->get_name()}</strong><br>";
foreach ($epos as $epo){
$output .= '<i> -- '. $epo['name'] .' : </i>'. $epo['value'] . "<br>";
}
}
$order = new WC_Order($order_id);
$output .= "<br><strong>Customer Details</strong><br>";
$output .= "Name: {$order->get_billing_first_name()}<br>";
$output .= "Phone: <a href='tel:92{$order->get_billing_phone()}'>{$order->get_billing_phone()}</a><br>";
$output .= "Address: {$order->get_billing_address_1()}<br>";
$output .= "City: {$order->get_billing_city()}<br>";
$output .= "Total: {$order->get_formatted_order_total()}<br>";
$output .= "Payment: {$order->get_payment_method_title()}<br>";
$order->add_order_note( $output );
$order->set_customer_note( $output );
$order->save();
}
The output is working perfect on order_note
but it in customer provided note it also returns html tags
How to fix this ?
Regards
Since Woocommerce 3 you can't access anymore properties From the WC_Order object. You need to use the WC_Order method get_customer_note():
// Get the note
$customer_note = $order->get_customer_note();
// decode entity to regular HTML
$customer_note = html_entity_decode($customer_note);
// Print somwhere
echo $customer_note;
Your problem is encoded HTML code to the HTML entities and you must decode it to regular HTML using PHP function html_entity_decode() and print decoded string.
I hope this will help you to understand your problem.

How to send specific line item data to third party script

I'm trying to send price, name and id of each item in the order, plus an extra static value to a single js variable formatted in this way,
items = eventID::price::name|eventID::price::name|eventID::price::name
I was trying to do it this way, but I'm getting an error on the last line which I mean to add a pipe if there is more than one product.
$line_items = $order->get_items();
//loop over line items
$wgItemCount = $order->get_item_count();
$wgItems = array();
foreach ( $line_items as $item ) {
$wgItem = array();
$wgItem ['eventId'] = '777777';
$wgItem ['eventId'] .= '::';
$wgItem ['price'] = $order->get_line_total( $item, true, true );
$wgItem ['price'] .= '::';
$wgItem ['name'] = $item->get_name();
if ($wgItemCount > 1) { //add divider if more than one item
wgItem .= '|';
}
$wgItems[] = $wgItem;
}
That hasn't worked so far, so I thought maybe I should create a custom object that contains the variables instead:
wgProduct = "77777", $order->get_line_total( $item, true, true ), $item->get_name();
and then call the items in a list later like
echo print ("|", wgProduct)
I had also tried using JSON to encode the data but the third party told me that is not going to work for their needs.
Edit: Code is now doing everything I need it to, but it's only pulling one item from the list instead of a string of all of them.
Here's what it looks like now based on the help I got here:
add_action( 'woocommerce_thankyou','wg_tracking' );
function wg_tracking( $order_id ) {
$order = wc_get_order( $order_id );
$shipping_total = $order->get_shipping_total();
$order_total = $order->get_total();
$currency = $order->get_currency();
$coupons = $order->get_coupon_codes();
$items = $order->get_items();
$total_exc_shipping = $order_total - $shipping_total;
$order_discount = $order->discount_total;
foreach( $coupons as $coupon ){
$coupon_post_object = get_page_by_title($coupon, OBJECT, 'shop_coupon');
$coupon_id = $coupon_post_object->ID;
$coupon = new WC_Coupon($coupon_id);
}
$wgItems = array();
foreach ( $items as $item ) {
$line = '';
$line .= '77777';
$line.= '::' . $order->get_line_total( $item, true, true );
$line .= '::' . $item->get_name();
$line .= '::' . $item->get_id();
}
$wgItems[] = $line;
$itemsList = implode('|', $wgItems);
?>
<script>
(function(w,e,b,g,a,i,n,s){w['ITCVROBJ']=a;w[a]=w[a]||function(){
(w[a].q=w[a].q||[]).push(arguments)},w[a].l=1*new Date();i=e.createElement(b),
n=e.getElementsByTagName(b)[0];i.async=1;i.src=g;n.parentNode.insertBefore(i,n)
})(window,document,'script','https://analytics.webgains.io/cvr.min.js','ITCVRQ');
ITCVRQ('set', 'trk.programId', 88888);
ITCVRQ('set', 'cvr', {
value: '<?php echo $total_exc_shipping ?>',
currency: '<?php echo $currency ?>',
language: 'de_DE',
eventId: 77777,
orderReference : '<?php echo $order_id ?>',
comment: '',
multiple: '',
checksum: '',
items: '<?php echo $itemsList ?>',
voucherId: '<?php if ( $order_discount > 0 ) echo $coupon->get_code(); ?>'
});
ITCVRQ('conversion');
</script>
<?php
}
?>
I think this all can be simplified to just use array's implode() method.
Below are what I would do in your situation:
$wgItems = array();
foreach ( $line_items as $item ) {
$line = '';
$line .= '777777';
$line .= '::' . $order->get_line_total( $item, true, true );
$line .= '::' . $item->get_name();
$wgItems[] = $line;
}
$items = implode('|', $wgItems);

Categories