Move custom column admin links from below "post title" - wordpress

It seems I can't figure out how to add the edit, remove, view, etc... to one of my custom columns in the backend of wordpress. The idea is to get the links that are attached to title when one hovers over the title, to be attached to a different column.
This is what the below code outputs.
This is what I want the link in the authors column have when mouse is hovered over the authors, just like when you hover over the title in this screenshot; all the edit links.
This is what I have so far:
add_filter( 'manage_edit-testimonial-quotes_columns', 'view_columns' ) ;
function view_columns( $columns ) {
$columns = array(
'cb' => '',
'date' => __( 'Date' ),
'tq_author' => __( 'Author' ),
'tq_quote' => __( 'Testimonial' ),
);
return $columns;
}
add_action('manage_testimonial-quotes_posts_custom_column', 'custom_view_columns', 10, 2);
function custom_view_columns($column, $post_id){
global $post;
switch ($column){
case 'tq_author':
echo '<a href="post.php?post=' . $post->ID . '&action=edit">';
$column_content = the_field('tq_author');
echo $column_content;
echo '</a>';
break;
case 'tq_quote':
$column_content = the_field('tq_quote');
echo $column_content;
break;
default:
break;
}
}

The best way of doing this since WP 4.3.0 is using
add_filter( 'list_table_primary_column', [ $this, 'list_table_primary_column' ], 10, 2 );
public function list_table_primary_column( $default, $screen ) {
if ( 'edit-yourpostype' === $screen ) {
$default = 'yourcolumn';
}
return $default;
}

I really doubt that there's a hook to deal with that. So, I'll not even check the core and go straight to the dirty solution:
add_action( 'admin_head-edit.php', 'so_13418722_move_quick_edit_links' );
function so_13418722_move_quick_edit_links()
{
global $current_screen;
if( 'post' != $current_screen->post_type )
return;
if( current_user_can( 'delete_plugins' ) )
{
?>
<script type="text/javascript">
function so_13418722_doMove()
{
jQuery('td.post-title.page-title.column-title div.row-actions').each(function() {
var $list = jQuery(this);
var $firstChecked = $list.parent().parent().find('td.author.column-author');
if ( !$firstChecked.html() )
return;
$list.appendTo($firstChecked);
});
}
jQuery(document).ready(function ($){
so_13418722_doMove();
});
</script>
<?php
}
}
Result:
Notes:
adjust your post_type: 'post' != $current_screen->post_type
adjust your column classes: find('td.author.column-author')
Bug and solution:
You'll note that, after updating, the quick-edit menu goes back to its original position. The following AJAX interception deals with it. Refer to this WordPress Developers answer for more details.
add_action( 'wp_ajax_inline-save', 'so_13418722_ajax_inline_save' , 0 );
/**
Copy of the function wp_ajax_inline_save()
http://core.trac.wordpress.org/browser/tags/3.4.2/wp-admin/includes/ajax-actions.php#L1315
Only Modification marked at the end of the function with INTERCEPT
*/
function so_13418722_ajax_inline_save()
{
global $wp_list_table;
check_ajax_referer( 'inlineeditnonce', '_inline_edit' );
if ( ! isset($_POST['post_ID']) || ! ( $post_ID = (int) $_POST['post_ID'] ) )
wp_die();
if ( 'page' == $_POST['post_type'] ) {
if ( ! current_user_can( 'edit_page', $post_ID ) )
wp_die( __( 'You are not allowed to edit this page.' ) );
} else {
if ( ! current_user_can( 'edit_post', $post_ID ) )
wp_die( __( 'You are not allowed to edit this post.' ) );
}
set_current_screen( $_POST['screen'] );
if ( $last = wp_check_post_lock( $post_ID ) ) {
$last_user = get_userdata( $last );
$last_user_name = $last_user ? $last_user->display_name : __( 'Someone' );
printf( $_POST['post_type'] == 'page' ? __( 'Saving is disabled: %s is currently editing this page.' ) : __( 'Saving is disabled: %s is currently editing this post.' ), esc_html( $last_user_name ) );
wp_die();
}
$data = &$_POST;
$post = get_post( $post_ID, ARRAY_A );
$post = add_magic_quotes($post); //since it is from db
$data['content'] = $post['post_content'];
$data['excerpt'] = $post['post_excerpt'];
// rename
$data['user_ID'] = $GLOBALS['user_ID'];
if ( isset($data['post_parent']) )
$data['parent_id'] = $data['post_parent'];
// status
if ( isset($data['keep_private']) && 'private' == $data['keep_private'] )
$data['post_status'] = 'private';
else
$data['post_status'] = $data['_status'];
if ( empty($data['comment_status']) )
$data['comment_status'] = 'closed';
if ( empty($data['ping_status']) )
$data['ping_status'] = 'closed';
// update the post
edit_post();
$wp_list_table = _get_list_table('WP_Posts_List_Table');
$mode = $_POST['post_view'];
$wp_list_table->display_rows( array( get_post( $_POST['post_ID'] ) ) );
// INTERCEPT: Check if it is our post_type, if not, do nothing
if( 'post' == $_POST['post_type'] )
{
?>
<script type="text/javascript">so_13418722_doMove();</script>
<?php
}
// end INTERCEPT
wp_die();
}

I had the same need.
Nicola's solution worked for me except I was getting an "Undefined variable" notice/error. Then I realized that the parameter has to be without the "[$this ... ]" part.
I guess that was a copy/paste from the documentation.
So this worked:
add_filter( 'list_table_primary_column', 'list_table_primary_column', 10, 2 );
function list_table_primary_column( $default, $screen ) {
if ( 'edit-your_post_type' === $screen ) {
// Set default columns to Minutes Spent.
$default = 'your_column';
}
return $default;
}
If you don't know what your_column is, just inspect the title of that column and get the ID.

You can't move the actions per se without resorting to JS, as the accepted answer does. However, you can very easily build your own version of the actions popup with php and inbuilt Wordpress functions. This version will work even if the user has JS turned off.
Presuming you use a switch to populate your custom columns, do something likes this, if not adapt to your own function:
switch ( $column ) {
case 'your_column_name':
echo "Your custom content here";
my_custom_column_actions($post_id);
break;
}
Then have a separate function that recreates the actions popup.
function my_custom_column_actions($post_id) {
if($_GET['post_status']!='trash') :
$bare_url = "/wp-admin/post.php?post=$post_id&action=trash";
$nonce_url = wp_nonce_url( $bare_url, 'trash-post_'.$post_id );
echo " <div class='row-actions'>
<span class='edit'>
<a href='/wp-admin/post.php?post=$post_id&action=edit'>Edit</a> |
</span>
<span class='trash'>
<a href='$nonce_url' class='submitdelete'>Trash</a>
</span>
<span class='edit'>
<a href='".get_the_permalink($post_id)."'>View</a> |
</span>
</div>";
else:
$bare_url = "/wp-admin/post.php?post=$post_id&action=untrash";
$nonce_url = wp_nonce_url( $bare_url, 'untrash-post_'.$post_id );
$delete_url = "/wp-admin/post.php?post=$post_id&action=delete";
$nonce_delete_url = wp_nonce_url( $delete_url, 'delete-post_'.$post_id );
echo " <div class='row-actions'>
<span class='untrash'>
<a href='$nonce_url' class='untrash'>Restore</a> |
</span>
<span class='delete'>
<a href='$nonce_delete_url' class='submitdelete'>Delete Permanently</a>
</span>
</div>";
endif;
}
All the business with nonce_urls is important for trashing, restoring or deleting; these actions won't work without it.
If you want to include this on a column that would normally appear, e.g. author or date published, you'll need to not include the standard column when you're declaring your custom columns and instead include a custom column that gets the same data (plus a call to the function above).
The same goes for if you want to include the title but not have the actions appear under it - otherwise they'll appear twice. Don't include the title column and instead include your own custom column that grabs the title but not the new function.
You can also very easily add your own actions by just editing the content that the function echoes.

Related

WooCommerce: Display SKU number in admin side Order listing page custom column

I have added a custom column in Woocommerce admin side order listing page to display Ordered item Products name and total Quantity like this -> [http://prntscr.com/p11rdl] by using the following code:
add_filter('manage_edit-shop_order_columns', 'misha_order_items_column' );
function misha_order_items_column( $order_columns ) {
$order_columns['order_products'] = "Qty-Products Name-SKU";
return $order_columns;
}
add_action( 'manage_shop_order_posts_custom_column' , 'misha_order_items_column_cnt' );
function misha_order_items_column_cnt( $colname ) {
global $the_order; // the global order object
if( $colname == 'order_products' ) {
// get items from the order global object
$order_items = $the_order->get_items();
if ( !is_wp_error( $order_items ) ) {
foreach( $order_items as $order_item ) {
echo $order_item['quantity'] .' × '. $order_item['name'] .'<br />';
}
}
}
}
Now I also want to display ordered product SKU number after the product name. So anyone have solution for this then please provide help me.
Thank you,
Ketan
Just replace with follows code snippets to add SKU -
add_action( 'manage_shop_order_posts_custom_column' , 'misha_order_items_column_cnt' );
function misha_order_items_column_cnt( $colname ) {
global $the_order; // the global order object
if( $colname == 'order_products' ) {
// get items from the order global object
$order_items = $the_order->get_items();
if ( !is_wp_error( $order_items ) ) {
foreach( $order_items as $order_item ) {
$product = $order_item->get_product();
// product checking
$sku = ( $product && $product->get_sku() ) ? ' - ' . $product->get_sku() : '';
echo $order_item['quantity'] .' × '. $order_item['name'] . ''. $sku . '<br />';
}
}
}
}
Thank you so much I've found this thread extremely helpful, I couldn't thank the original poster enough as well the help from #itzmekhokan and various other contributors, this community is awesome.
What I'm needing now is to add product shelve location straight after SKU.
I have the below code for location fields but not sure how to amend to the code above to make it work. I'm new to coding in general, but have found snippets of code and made it work so far.
Your help and guidance would be greatly appreciated.
// Add Location field
add_action( 'woocommerce_product_options_stock_fields', 'product_shelf' );
function product_shelf() {
global $woocommerce, $post;
woocommerce_wp_text_input(
array(
'id' => 'product_shelf',
'placeholder' => 'Enter product shelf location here',
'label' => 'Shelf Location',
'description' => 'Shelf location of product',
'desc_tip' => 'true',
)
);
}
// Save location data
add_action( 'woocommerce_process_product_meta', 'product_shelf_data' );
function product_shelf_data( $post_id ) {
// grab the location from $_POST
$product_shelf = isset( $_POST[ 'product_shelf' ] ) ? sanitize_text_field( $_POST[ 'product_shelf' ] ) : '';
// grab the product
$product = wc_get_product( $post_id );
// save the location using WooCommerce built-in functions
$product->update_meta_data( 'product_shelf', $product_shelf_data );
$product->save();
}

How to remove Author Tag from being visible in Discord's previews?

When sharing a post to Discord, the preview Discord generates shows the author name and URL. We removed all information about the author but it didn't stop the author tag from showing.
That’s done via oEmbed. Add below code in your functions.php file
add_filter( 'oembed_response_data', 'disable_embeds_filter_oembed_response_data_' );
function disable_embeds_filter_oembed_response_data_( $data ) {
unset($data['author_url']);
unset($data['author_name']);
return $data;
}
**disorc may have stored the response in cache so create new post or page and test that **
#hrak has the right idea but his answer lacks context for those of us not used to dealing with PHP.
What I ended up doing was check if there was already a filter for 'oembed_response_data' in /wp-includes/default-filters.php. Mine looked like this:
add_filter( 'oembed_response_data', 'get_oembed_response_data_rich', 10, 4 );
Add the previous line to the specified file if, for whatever reason, it isn't there already.
Afterward, I checked in wp-includes/embed.php for the get_oembed_response_data_rich function, which looked like this:
function get_oembed_response_data_rich( $data, $post, $width, $height ) {
$data['width'] = absint( $width );
$data['height'] = absint( $height );
$data['type'] = 'rich';
$data['html'] = get_post_embed_html( $width, $height, $post );
// Add post thumbnail to response if available.
$thumbnail_id = false;
if ( has_post_thumbnail( $post->ID ) ) {
$thumbnail_id = get_post_thumbnail_id( $post->ID );
}
if ( 'attachment' === get_post_type( $post ) ) {
if ( wp_attachment_is_image( $post ) ) {
$thumbnail_id = $post->ID;
} elseif ( wp_attachment_is( 'video', $post ) ) {
$thumbnail_id = get_post_thumbnail_id( $post );
$data['type'] = 'video';
}
}
if ( $thumbnail_id ) {
list( $thumbnail_url, $thumbnail_width, $thumbnail_height ) = wp_get_attachment_image_src( $thumbnail_id, array( $width, 99999 ) );
$data['thumbnail_url'] = $thumbnail_url;
$data['thumbnail_width'] = $thumbnail_width;
$data['thumbnail_height'] = $thumbnail_height;
}
return $data;
}
I just added the two lines of code that #hrak introduced in his answer to remove the author tag (name and URL) from $data before it was returned:
function get_oembed_response_data_rich( $data, $post, $width, $height ) {
(...)
if ( $thumbnail_id ) {
list( $thumbnail_url, $thumbnail_width, $thumbnail_height ) = wp_get_attachment_image_src( $thumbnail_id, array( $width, 99999 ) );
$data['thumbnail_url'] = $thumbnail_url;
$data['thumbnail_width'] = $thumbnail_width;
$data['thumbnail_height'] = $thumbnail_height;
}
unset($data['author_url']);
unset($data['author_name']);
return $data;
}
As before, add the get_oembed_response_data_rich function if it does not already exist. After about 5-10 minutes, Discord link embeds stopped showing the author tag.
Source:
http://hookr.io/filters/oembed_response_data/
https://developer.wordpress.org/reference/functions/get_oembed_response_data_rich/
I've done this by emptying the posted_by function like this article shows, in the "Use Code to Remove the Author Name" section
https://wpdatatables.com/how-to-hide-the-author-in-wordpress/
basically find the posted_by function and empty it
function twentynineteen_posted_by() {
}
endif;

Custom permalink with dynamic rewrite tags on a Wordpress custom post type

I have a custom post type named location and I'm trying to set posts of this type as children of existing pages, to achieve a permalink structure like this:
example.com/alabama <-- Page with generic content
example.com/alabama/locations <-- Another page, child of a state page
example.com/alabama/locations/location-name <- Custom type, child of alabama/locations seen above and titled `Location Name`
The closest I've got to create hierarchical relationships between two distinct post types was through a meta box where I can assign a post ID as the post_parent of my custom type posts when saving them. However despite the page ID being indeed saved to the CPT post_parent field, it has no effect on the permalinks. They are coming as defined in the rewrite CPT option. But I don't know how to make the ['rewrite']['slug'] option dynamic, or if it's even possible.
This is how my post type is being defined:
add_action( 'init', function() {
register_post_type( 'location', [
'label' => 'Location',
'menu_icon' => 'dashicons-location-alt',
'supports' => [ 'title', 'editor', 'custom-fields' ],
'public' => true,
'hierarchical' => false,
'has_archive' => false,
'rewrite' => false,
] );
} );
How can I configure the rewrite rules for locations to get the permalinks I need?
I'm assuming that all the location posts will have a permalink structure that looks like this:
example.com/{STATE NAME}/locations/{CPT SLUG}
Sample URL addresses:
http://example.com/alabama/locations/location-1
http://example.com/alabama/locations/location-2
http://example.com/new-york/locations/location-3
So if that is correct, then:
• Use the add_rewrite_rule() function to add a custom rewrite rule for those permalinks.
• You don't need the /locations/ Page.
add_action( 'init', function(){
// Handles requests to `your-site-domain.com/{STATE NAME}/locations/{CPT SLUG}`
add_rewrite_rule(
'([^/]+)/locations/([^/]+)(?:/([0-9]+))?/?$',
'index.php?location=$matches[2]&page=$matches[3]&state_name=$matches[1]',
'top'
);
// Allows you to retrieve the `state_name`; for example using `get_query_var()`.
add_rewrite_tag( '%state_name%', '([\w\-]+)' );
} );
(You can change state_name to another name; it's up to you. And don't forget to flush the rewrite rules — go to the Permalink Settings page and click on the Save Changes button without having to make any changes.)
Next, when you create or edit a location post, set the value of the post_parent custom field to the ID of the 'state Page' — e.g. the /alabama/ Page.
And this code will filter the get_permalink() output, and returns the appropriate permalink for a location post:
add_filter( 'post_type_link', 'so51217355_post_type_link', 10, 2 );
function so51217355_post_type_link( $permalink, $post ) {
if ( 'location' === $post->post_type ) {
$page_id = get_post_meta( $post->ID, 'post_parent', true );
$state_name = ( is_numeric( $page_id ) && $page_id ) ?
get_post_field( 'post_name', $page_id ) : null;
// Make sure the post is associated to a valid 'state Page'.
if ( $state_name ) {
$permalink = $state_name . '/locations/' . $post->post_name;
$permalink = home_url( user_trailingslashit( $permalink ) );
}
}
return $permalink;
}
So for example, get_permalink( 123 ) would return http://example.com/alabama/locations/location-1, if the location post's slug is location-1, and its 'state Page' is /alabama/.
UPDATE
When the permalink is requested (i.e. users visit example.com/{STATE NAME}/locations/{CPT SLUG}), and you want to make sure the 'state Page' and location post both exist, and that the 'state Page' was indeed associated to the location post, then this code can help you:
// Validates the `state_name` of the current page/URL.
add_action( 'parse_request', 'so51217355_parse_request' );
function so51217355_parse_request( $wp ) {
if ( ! empty( $wp->query_vars['state_name'] ) &&
! empty( $wp->query_vars['location'] ) ) {
global $wpdb;
$page_id = $wpdb->get_var( $wpdb->prepare(
"SELECT ID FROM {$wpdb->posts} WHERE post_name = %s",
$wp->query_vars['state_name']
) );
if ( ! is_numeric( $page_id ) || ! $page_id ) {
$wp->query_vars['error'] = '404';
// Don't let WordPress finds a post with nearest match.
remove_action( 'template_redirect', 'redirect_canonical' );
return;
}
$post_id = $wpdb->get_var( $wpdb->prepare(
"SELECT ID FROM {$wpdb->posts} WHERE post_name = %s",
$wp->query_vars['location']
) );
$page_id2 = get_post_meta( $post_id, 'post_parent', true );
if ( (int) $page_id2 !== (int) $page_id ) {
$wp->query_vars['error'] = '404';
// Don't let WordPress finds a post with nearest match.
remove_action( 'template_redirect', 'redirect_canonical' );
}
}
}
UPDATE #2
Refer to the // Comment in the code below this image — see the _so51217355_admin_ajax_js() function.
add_action( 'wp_ajax_so51217355_admin_ajax', '_so51217355_admin_ajax_php' );
function _so51217355_admin_ajax_php() {
$post_id = filter_input( INPUT_POST, 'post_id' );
echo get_sample_permalink_html( $post_id );
wp_die();
}
add_action( 'admin_print_footer_scripts', '_so51217355_admin_ajax_js', 11 );
function _so51217355_admin_ajax_js() {
$screen = get_current_screen();
if ( 'location' === $screen->id ) :
?>
<script>
// This script will sync the Permalink under the big/main post title box on
// the Edit Post page; but only if and when editing or deleting the custom
// field as in `meta_key` below. Make sure to change it, if necessary.
jQuery( function( $ ){
var meta_key = 'post_parent';
function ajax() {
$.post( ajaxurl, {
action: 'so51217355_admin_ajax',
post_id: $( '#post_ID' ).val()
}, function( s ){
$( '#edit-slug-box' ).html( s );
} );
}
function _go( e, a ) {
var $input = $( a.target ),
mid, mkey;
if ( /^meta\-(\d+)\-submit$/.test( $input.attr( 'name' ) ) ||
/^deletemeta\[(\d+)\]$/.test( $input.attr( 'name' ) ) ) {
mid = RegExp.$1;
mkey = $( 'input[name="meta[' + mid + '][key]"]' ).val();
if ( meta_key === mkey ) {
ajax();
}
}
}
$( '#the-list' )
.on( 'wpListAddEnd', _go )
.on( 'wpListDelEnd', _go );
} );
</script>
<?php
endif;
}

Add input field to every item in cart

I am trying to add a new field on my cart.php file.
I actually want to insert a URL field, so user can set a URL for each order item.
I tried to use a code from another post here but I can't get it to work.
The first and the second functions are working but when it comes to the third one, 'woocommerce_get_item_data' the $cart_item['url'] doesn't contain anything even if I add something in the field and I press Update Cart.
$cart_totals[ $cart_item_key ]['url'] from the first function is outputting the right value when the page load.
I don't know what to do now, thanks for any help.
Here is the code
Add the field
cart/cart.php
<td class="product-url">
<?php
$html = sprintf( '<div class="url"><input type="text" name="cart[%s][url]" value="%s" size="4" title="Url" class="input-text url text" /></div>', $cart_item_key, esc_attr( $values['url'] ) );
echo $html;
?>
</td>
functions.php
// get from session your URL variable and add it to item
add_filter('woocommerce_get_cart_item_from_session', 'cart_item_from_session', 99, 3);
function cart_item_from_session( $data, $values, $key ) {
$data['url'] = isset( $values['url'] ) ? $values['url'] : '';
return $data;
}
// this one does the same as woocommerce_update_cart_action() in plugins\woocommerce\woocommerce-functions.php
// but with your URL variable
// this might not be the best way but it works
add_action( 'init', 'update_cart_action', 9);
function update_cart_action() {
global $woocommerce;
if ( ( ! empty( $_POST['update_cart'] ) || ! empty( $_POST['proceed'] ) ) ) {
$cart_totals = isset( $_POST['cart'] ) ? $_POST['cart'] : '';
if ( sizeof( $woocommerce->cart->get_cart() ) > 0 ) {
foreach ( $woocommerce->cart->get_cart() as $cart_item_key => $values ) {
if ( isset( $cart_totals[ $cart_item_key ]['url'] ) ) {
$woocommerce->cart->cart_contents[ $cart_item_key ]['url'] = $cart_totals[ $cart_item_key ]['url'];
}
}
}
}
}
// this is in Order summary. It show Url variable under product name. Same place where Variations are shown.
add_filter( 'woocommerce_get_item_data', 'item_data', 10, 2 );
function item_data( $data, $cart_item ) {
if ( isset( $cart_item['url'] ) ) {
$data['url'] = array('name' => 'Url', 'value' => $cart_item['url']);
}
return $data;
}
// this adds Url as meta in Order for item
add_action ('woocommerce_add_order_item_meta', 'add_item_meta', 10, 2);
function add_item_meta( $item_id, $values ) {
woocommerce_add_order_item_meta( $item_id, 'Url', $values['url'] );
}
Add a textarea field to a WooCommerce cart item
First, we just need to add the textarea field. We use the woocommerce_after_cart_item_name hook so our textarea will appear after the product name.
<?php
/**
* Add a text field to each cart item
*/
function prefix_after_cart_item_name( $cart_item, $cart_item_key ) {
$notes = isset( $cart_item['notes'] ) ? $cart_item['notes'] : '';
printf(
'<div><textarea class="%s" id="cart_notes_%s" data-cart-id="%s">%s</textarea></div>',
'prefix-cart-notes',
$cart_item_key,
$cart_item_key,
$notes
);
}
add_action( 'woocommerce_after_cart_item_name', 'prefix_after_cart_item_name', 10, 2 );
/**
* Enqueue our JS file
*/
function prefix_enqueue_scripts() {
wp_register_script( 'prefix-script', trailingslashit( plugin_dir_url( __FILE__ ) ) . 'update-cart-item-ajax.js', array( 'jquery-blockui' ), time(), true );
wp_localize_script(
'prefix-script',
'prefix_vars',
array(
'ajaxurl' => admin_url( 'admin-ajax.php' )
)
);
wp_enqueue_script( 'prefix-script' );
}
add_action( 'wp_enqueue_scripts', 'prefix_enqueue_scripts' );
´´´
At the moment, the user will be able to enter text into the field but the text won’t save. We are going to use some AJAX to save the text.
The code above not only adds the textarea to the cart item, it also enqueues a JavaScript file ready for our AJAX.
It’s assumed that you’re using the code on this page to create a new plugin. If so, you should create a new JS file with the code below and place the file in the root directory of your plugin.
However, if you’ve added the PHP above to your theme functions.php or as a snippet on your site, you’ll need to change the location of the JS file by updating line 21 of the snippet above to identify the location of the JS file.
´´´
(function($){
$(document).ready(function(){
$('.prefix-cart-notes').on('change keyup paste',function(){
$('.cart_totals').block({
message: null,
overlayCSS: {
background: '#fff',
opacity: 0.6
}
});
var cart_id = $(this).data('cart-id');
$.ajax(
{
type: 'POST',
url: prefix_vars.ajaxurl,
data: {
action: 'prefix_update_cart_notes',
security: $('#woocommerce-cart-nonce').val(),
notes: $('#cart_notes_' + cart_id).val(),
cart_id: cart_id
},
success: function( response ) {
$('.cart_totals').unblock();
}
}
)
});
});
})(jQuery);
´´´
Now, when the user types anything, the contents of the text field get sent back to the server ready to be saved as meta data to the cart item.
´´´
<?php
/**
* Update cart item notes
*/
function prefix_update_cart_notes() {
// Do a nonce check
if( ! isset( $_POST['security'] ) || ! wp_verify_nonce( $_POST['security'], 'woocommerce-cart' ) ) {
wp_send_json( array( 'nonce_fail' => 1 ) );
exit;
}
// Save the notes to the cart meta
$cart = WC()->cart->cart_contents;
$cart_id = $_POST['cart_id'];
$notes = $_POST['notes'];
$cart_item = $cart[$cart_id];
$cart_item['notes'] = $notes;
WC()->cart->cart_contents[$cart_id] = $cart_item;
WC()->cart->set_session();
wp_send_json( array( 'success' => 1 ) );
exit;
}
add_action( 'wp_ajax_prefix_update_cart_notes', 'prefix_update_cart_notes' );
function prefix_checkout_create_order_line_item( $item, $cart_item_key, $values, $order ) {
foreach( $item as $cart_item_key=>$cart_item ) {
if( isset( $cart_item['notes'] ) ) {
$item->add_meta_data( 'notes', $cart_item['notes'], true );
}
}
}
add_action( 'woocommerce_checkout_create_order_line_item', 'prefix_checkout_create_order_line_item', 10, 4 );
´´´
The prefix_update_cart_notes function does a security check using the WooCommerce cart nonce then saves the content of the textarea as meta data in the cart item. You can check out this article for more information about updating cart meta for items that have already been added to the cart.
Add the custom text to the order meta
Finally, we want to pass our meta data to the order so that we can use it after the customer has checked out. The prefix_checkout_create_order_line_item function takes care of that, iterating through each item and saving notes when it finds them.
https://pluginrepublic.com/how-to-add-an-input-field-to-woocommerce-cart-items/

Wordpress custom metabox input value with AJAX

I am using Wordpress 3.5, I have a custom post (sp_product) with a metabox and some input field. One of those input (sp_title).
I want to Search by the custom post title name by typing in my input (sp_title) field and when i press add button (that also in my custom meta box), It will find that post by that Title name and bring some post meta data into this Meta box and show into other field.
Here in this picture (Example)
Search
Click Button
Get some value by AJAX from a custom post.
Please give me a example code (just simple)
I will search a simple custom post Title,
Click a button
Get the Title of that post (that i search or match) with any other post meta value, By AJAX (jQuery-AJAX).
Please Help me.
I was able to find the lead because one of my plugins uses something similar to Re-attach images.
So, the relevant Javascript function is findPosts.open('action','find_posts').
It doesn't seem well documented, and I could only found two articles about it:
Find Posts Dialog Box
Using Built-in Post Finder in Plugins
Tried to implement both code samples, the modal window opens but dumps a -1 error. And that's because the Ajax call is not passing the check_ajax_referer in the function wp_ajax_find_posts.
So, the following works and it's based on the second article. But it has a security breach that has to be tackled, which is wp_nonce_field --> check_ajax_referer. It is indicated in the code comments.
To open the Post Selector, double click the text field.
The jQuery Select needs to be worked out.
Plugin file
add_action( 'load-post.php', 'enqueue_scripts_so_14416409' );
add_action( 'add_meta_boxes', 'add_custom_box_so_14416409' );
add_action( 'wp_ajax_find_posts', 'replace_default_ajax_so_14416409', 1 );
/* Scripts */
function enqueue_scripts_so_14416409() {
# Enqueue scripts
wp_enqueue_script( 'open-posts-scripts', plugins_url('open-posts.js', __FILE__), array('media', 'wp-ajax-response'), '0.1', true );
# Add the finder dialog box
add_action( 'admin_footer', 'find_posts_div', 99 );
}
/* Meta box create */
function add_custom_box_so_14416409()
{
add_meta_box(
'sectionid_so_14416409',
__( 'Select a Post' ),
'inner_custom_box_so_14416409',
'post'
);
}
/* Meta box content */
function inner_custom_box_so_14416409( $post )
{
?>
<form id="emc2pdc_form" method="post" action="">
<?php wp_nonce_field( 'find-posts', '_ajax_nonce', false); ?>
<input type="text" name="kc-find-post" id="kc-find-post" class="kc-find-post">
</form>
<?php
}
/* Ajax replacement - Verbatim copy from wp_ajax_find_posts() */
function replace_default_ajax_so_14416409()
{
global $wpdb;
// SECURITY BREACH
// check_ajax_referer( '_ajax_nonce' );
$post_types = get_post_types( array( 'public' => true ), 'objects' );
unset( $post_types['attachment'] );
$s = stripslashes( $_POST['ps'] );
$searchand = $search = '';
$args = array(
'post_type' => array_keys( $post_types ),
'post_status' => 'any',
'posts_per_page' => 50,
);
if ( '' !== $s )
$args['s'] = $s;
$posts = get_posts( $args );
if ( ! $posts )
wp_die( __('No items found.') );
$html = '<table class="widefat" cellspacing="0"><thead><tr><th class="found-radio"><br /></th><th>'.__('Title').'</th><th class="no-break">'.__('Type').'</th><th class="no-break">'.__('Date').'</th><th class="no-break">'.__('Status').'</th></tr></thead><tbody>';
foreach ( $posts as $post ) {
$title = trim( $post->post_title ) ? $post->post_title : __( '(no title)' );
switch ( $post->post_status ) {
case 'publish' :
case 'private' :
$stat = __('Published');
break;
case 'future' :
$stat = __('Scheduled');
break;
case 'pending' :
$stat = __('Pending Review');
break;
case 'draft' :
$stat = __('Draft');
break;
}
if ( '0000-00-00 00:00:00' == $post->post_date ) {
$time = '';
} else {
/* translators: date format in table columns, see http://php.net/date */
$time = mysql2date(__('Y/m/d'), $post->post_date);
}
$html .= '<tr class="found-posts"><td class="found-radio"><input type="radio" id="found-'.$post->ID.'" name="found_post_id" value="' . esc_attr($post->ID) . '"></td>';
$html .= '<td><label for="found-'.$post->ID.'">' . esc_html( $title ) . '</label></td><td class="no-break">' . esc_html( $post_types[$post->post_type]->labels->singular_name ) . '</td><td class="no-break">'.esc_html( $time ) . '</td><td class="no-break">' . esc_html( $stat ). ' </td></tr>' . "\n\n";
}
$html .= '</tbody></table>';
$x = new WP_Ajax_Response();
$x->add( array(
'data' => $html
));
$x->send();
}
Javascript file open-posts.js
jQuery(document).ready(function($) {
// Find posts
var $findBox = $('#find-posts'),
$found = $('#find-posts-response'),
$findBoxSubmit = $('#find-posts-submit');
// Open
$('input.kc-find-post').live('dblclick', function() {
$findBox.data('kcTarget', $(this));
findPosts.open();
});
// Insert
$findBoxSubmit.click(function(e) {
e.preventDefault();
// Be nice!
if ( !$findBox.data('kcTarget') )
return;
var $selected = $found.find('input:checked');
if ( !$selected.length )
return false;
var $target = $findBox.data('kcTarget'),
current = $target.val(),
current = current === '' ? [] : current.split(','),
newID = $selected.val();
if ( $.inArray(newID, current) < 0 ) {
current.push(newID);
$target.val( current.join(',') );
}
});
// Double click on the radios
$('input[name="found_post_id"]', $findBox).live('dblclick', function() {
$findBoxSubmit.trigger('click');
});
// Close
$( '#find-posts-close' ).click(function() {
$findBox.removeData('kcTarget');
});
});

Resources