I wanted to create a plugin to batch manage posts' custom field data. I know I can add post meta by adding a meta box in the post edit screen and using add_action('save_post', 'function_to_update_meta') to trigger add meta functions.
But I don't know how to trigger the add_post_meta function in an admin menu page (such as a custom admin menu). How to do that?
Thank you in advance!
The example given in Wordpress' codex is probably the best and most secure in the way of processing information:
Add Meta Box
Copy and paste it and then fiddle around with it to get a good idea on how to control your posts and pages.
The nice part is that you don't need to worry about checking if you need to Add vs Update a given Post Meta field. Using Update Post Meta will ensure that the proper action is taken for you, even if the field doesn't exist.
The same goes for Update Option if you want to add some global controls that your plugin/theme might use.
BREAKDOWN EXAMPLE:
add_action( 'add_meta_boxes', 'myplugin_add_custom_box' );
add_action( 'save_post', 'myplugin_save_postdata' );
These are the action hooks. The first one is executed when meta boxes are being populated within the post editor, and the second is executed when a post is added OR updated.
function myplugin_add_custom_box()
{
add_meta_box(
'myplugin_sectionid',
__( 'My Post Section Title', 'myplugin_textdomain' ),
'myplugin_inner_custom_box',
'post'
);
add_meta_box(
'myplugin_sectionid',
__( 'My Post Section Title', 'myplugin_textdomain' ),
'myplugin_inner_custom_box',
'page'
);
}
This function is called by the 'add_meta_boxes' action hook. Notice the name of the function and the second argument of the action hook are exactly the same. This registers your meta boxes, which post types they're supposed to appear, and what callback is used to generate the form contained inside.
function myplugin_inner_custom_box( $post )
{
wp_nonce_field( plugin_basename( __FILE__ ), 'myplugin_noncename' );
$value = get_post_meta($post->ID, 'myplugin_new_field') ? get_post_meta($post->ID, 'myplugin_new_field') : 'New Field';
echo '<label for="myplugin_new_field">';
_e("Description for this field", 'myplugin_textdomain' );
echo '</label> ';
echo '<input type="text" id="myplugin_new_field" name="myplugin_new_field" value="'.$value.'" size="25" />';
}
This is the function that is called by the registered meta boxes to generate the form automatically. Notice how the function is called 'myplugin_inner_custom_box' and the 3rd argument in your meta box registration is also called 'myplugin_inner_custom_box'.
The wp_nonce_field() generates a hidden field in your form to verify that data being sent to the form actually came from Wordpress, and can also be used to end the function in case other plugins are using the 'save_post' action hook.
Notice also that the $post object is being passed in as an argument. This will allow you to use certain properties from the post object. I've taken the liberty of checking to see if there is get_post_meta() returns anything with the given post ID. If so, the field is filled with that value. If not, it is filled with 'New Field'.
function myplugin_save_postdata( $post_id )
{
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE )
return;
if ( !wp_verify_nonce( $_POST['myplugin_noncename'], plugin_basename( __FILE__ ) ) )
return;
if ( 'page' == $_POST['post_type'] )
{
if ( !current_user_can( 'edit_page', $post_id ) )
return;
}
else
{
if ( !current_user_can( 'edit_post', $post_id ) )
return;
}
$mydata = $_POST['myplugin_new_field'];
update_post_meta($post_id, 'myplugin_new_field', $mydata);
}
This is the function that is called by the 'save_post' action hook. Notice how the second argument of the second action hook and this function are both called 'myplugin_save_postdata'. First, there are a series of verifications our plugin must pass before it can actually save any data.
First, we don't want our meta boxes to update every time the given post is auto-updating. If the post is auto-updating, cancel the process.
Secondly, we want to make sure the nonce data is available and verify it. If no nonce data is available or is not verified, cancel the process.
Thirdly, we want to make sure the given user has the edit_page permission. The function first checks the post type, and then checks the appropriate permission. If the user does not have that permission, cancel the process.
Lastly, our plugin has finally been verified and we want to save the information. I took the liberty of adding in the final update_post_meta() line to show you how it all comes together.
Notice how $post_id was passed into the function as an argument. This is one of the pieces needed for the update_post_meta() function. The key was named 'myplugin_new_field' and the value of that metadata is now saved as whatever you input into that custom input field in your custom meta box.
That's about as easy as I can explain the whole process. Just study it, and get your hands dirty with code. The best way to learn is through application rather than theory.
The answer was from the same question I asked somewhere else
And I created my version of example
I added some console.log function for testing, but this is basically doning the same thing as #Chris_() answer:
Menu callback function to generate menu content (PHP):
function ajax_menu_callback() {
?>
<div class="wrap">
<div id="icon-themes" class="icon32"></div>
<h2>Test</h2>
<br />
<form>
<input id="meta" type ="text" name="1" value="<?php echo esc_html( get_post_meta( 1, 'your_key', true) ); ?>" />
<?php submit_button(); ?>
</form>
</div>
<?php
}
Then the javascript to print on the admin side (javascript, don't forget to include a jquery library):
jQuery(document).ready(function() {
$("form").submit(function() {
console.log('Submit Function');
var postMeta = $('input[name="1"]').val();
console.log(postMeta);
var postID = 1;
var button = $('input[type="submit"]');
button.val('saving......');
$.ajax({
data: {action: "update_meta", post_id: postID, post_meta: postMeta, },
type: 'POST',
url: ajaxurl,
success: function( response ) { console.log('Well Done and got this from sever: ' + response); }
}); // end of ajax()
return false;
}); // end of document.ready
}); // end of form.submit
Then the PHP function handle update_post_meta (PHP):
add_action( 'wp_ajax_update_meta', 'my_ajax_callback' );
function my_ajax_callback() {
$post_id = $_POST['post_id'];
$post_meta = $_POST['post_meta'];
update_post_meta( $post_id, 'your_key', $post_meta );
echo 'Meta Updated';
die();
} // end of my_ajax_callback()
Related
I have created a super simple theme. What i want to have is the ability to define an array of 3 fields for each post and page.
For example having the fields: Name, Link and a Dropdown for Type
Additionally i want to add multiple items of these "field sets" per post/page. I couldn't find any documentation on this but always results in Google which led me to WP Plugins. In general this is ood, cause in this case this seems to be possible programmatically, but bad cause i couldn't find any kind of information on this.
Hopefully someone can help me.
You are looking for custom meta boxes, with the add_meta_box() function:
Adds a meta box to one or more screens.
Source # https://developer.wordpress.org/reference/functions/add_meta_box/
And the add_meta_boxes action hook.
Fires after all built-in meta boxes have been added:
Source # https://developer.wordpress.org/reference/hooks/add_meta_boxes/
A simple example would be if we wanted to add a "Listening to..." custom meta box, to share our mood while writing a post.
<?php
add_action( 'add_meta_boxes', 'add_meta_listening_to' );
function add_meta_listening_to() {
add_meta_box(
'meta_listening_to', //id
'Listening to ...', //title
'listeningto', //callback
'post', //screen &/or post type
'normal', //context
'high', //priority
null //callback_args
);
};
function listeningto( $post ) { //function handle same as callback
$ListeningToInput = get_post_meta( $post->ID, 'ListeningToInput', true );
echo '<input name="listening_to_input" type="text" placeholder="Listening to ..." value="'. $ListeningToInput .'" style="width:100%;">';
};
add_action( 'save_post', 'save_meta_listening_to' );
function save_meta_listening_to( $post_ID ) {
if ( isset( $_POST[ 'listening_to_input' ] ) ) {
update_post_meta( $post_ID, 'ListeningToInput', esc_html( $_POST[ 'listening_to_input' ] ) );
};
}; ?>
Then to display on the front end, we would use the following:
<?php echo get_post_meta( $post->ID, 'ListeningToInput', true ); ?>
Learn more
Learn more about the Custom Meta Boxes # https://developer.wordpress.org/plugins/metadata/custom-meta-boxes/
I'm having difficulties with wp_editor(), tinyMCE and the_content filter in relation to oEmbed of video.
I am outputting a front end wp_editor() form to allow registered users to create new posts from the front end of the site. The new post created is a custom post type.
The target behaviour is:
The registered user enters content & clicks submit
The form is processed by jQuery/Ajax, with form data passed to a PHP function via post()
A new post created, and a response is generated for an Ajax callback
The response is a JSON array that contains the HTML of the new post content
The returned HTML has 'the_content' filter applied - embedded video should display properly
The Ajax callback removes the original form and appends the post HTML to a div
Everything works as expected with the exception of video oEmbed.
If a video link is added to the content (on a new line within the wp_editor), the content built by the Ajax callback includes the video URL wrapped in paragraph tags - oEmbed hasn't worked, even though the HTML has had 'the_content' filter applied.
Refreshing the page displays the new post in a loop, with content displayed by the_content() tag - and the video is displayed properly (oEmbed has worked).
Setting 'wpautop' => false in the wp_editor arguments doesn't help - messes up formatting, doesn't fix video.
Is there a tinyMCE setting that I'm missing?
Is there a problem with how I'm applying 'the_content' filter and/or building a HTML string for the Ajax callback?
Relevant code shown below.
Thanks!
JQuery
(function( $ ) { 'use strict';
$(function() {
$('#student-submission-button').click( function(event) {
// Prevent default action
// -----------------------
event.preventDefault();
var submission_nonce_id = $('#the_nonce_field').val();
var submission_title = $('#inputTitle').val();
tinyMCE.triggerSave();
var submission_content = $('#editor').val();
var Data = {
action: 'student_submission',
nonce: submission_nonce_id,
workbook_ID: submission_workbook_ID,
content: submission_content,
title: submission_title,
};
// Do AJAX request
$.post( ajax_url, Data, function(Response) {
if( Response ) {
var submissionStatus = Response.status;
var submissionMessage = Response.report;
var postHTML = Response.content;
if ( 'success' == submissionStatus ) {
$('#user-feedback').html( submissionMessage );
$('#new-post').append( postHTML );
}
// Hide the form
$('.carawebs-frontend-form').hide(800, function() {
$(this).remove();
});
}
});
});
});
})( jQuery );
PHP
/**
* Return data via Ajax (excerpt)
*
*
*/
$response = array();
if( is_int( $new_submission_ID ) ) {
// Build a success response
// ------------------------
$new_post = get_post( $new_submission_ID, OBJECT );
$new_post_content = $new_post->post_content;
$return_content = "<h2>$new_post->post_title</h2>";
$return_content .= apply_filters( 'the_content', $new_post_content );
$response['status'] = "success";
$response['report'] = "New post created, ID: $new_submission_ID";
$response['content'] = $return_content;
} else {
// error report
}
wp_send_json( $response ); // send $response as a JSON object
Form HTML and wp_editor()
<form action="<?php echo $_SERVER['REQUEST_URI']; ?>" method="post" enctype="multipart/form-data" class="carawebs-frontend-form">
<label for="inputTitle">Title</label>
<input type="text" class="form-control" id="inputTitle" name="inputTitle" placeholder="Title" value="" />
<label for="inputContent" class="topspace">Your Content</label>
<?php
$args = array(
'textarea_rows' => 45,
'teeny' => false,
'editor_height' => 400,
'editor_class' => 'cwfrontendadmin',
'quicktags' => false,
'textarea_name' => 'cw_content',
'tinymce' => array(
'content_css' => get_template_directory_uri() . '/assets/css/editor-style.css'
),
);
wp_editor( 'Enter your content...', 'editor', $args );
wp_nonce_field('name_of_action','the_nonce_field', true, true ); // name of action, name of nonce field
?>
<input id="student-submission-button" class="btn btn-primary" type="submit" name="submission-form" value="Save Content" />
Update
I've narrowed this down to the way that the_content filter is applied. I think filtered content is cached, so oEmbed may not get applied to all content if the post content is returned outside the loop.
Now I have video oEmbed working - using a different method of inserting post_content into a variable:
<?php
global $post;
$post = get_post($new_submission_ID);
setup_postdata( $post );
$new_content = apply_filters('the_content', get_the_content());
$new_post_link = get_the_permalink();
$new_post_title = get_the_title();
wp_reset_postdata( $post );
This works fine, but it would be good if someone could explain why the original method of building the HTML didn't work.
The oEmbed filter does not get applied if the post content is returned outside the loop, and global $post is not set.
This is because the video embed content is cached in the postmeta table, and is is inaccessible if the post content is returned outside the loop. The WP_Embed class which is hooked onto by the_content filter is not designed for use outside the loop - this is referenced on Trac here.
The following method returns a working Video oEmbed as per the original scenario. global $post needs to be set to make the cached video embed data available:
<?php
global $post;
$post = get_post( $new_submission_ID );
setup_postdata( $post );
$new_content = apply_filters( 'the_content', get_the_content() );
wp_reset_postdata( $post );
// return what you need via Ajax callback -
// $new_content contains the proper video embed HTML
TL;DR Version
If you need oEmbed filters to be applied outside the loop, you need to set the global post variable so that the WP_Embed class can access the cached video embed html in the post's meta.
The reason, according to other sources is because the WP_Embed::shortcode() (tasked with swapping out the video for the embedded html) is calling on the global $post variable for information.
This means you need to be sure that the global $post variable contains the information of the post you're requesting.
My AJAX response method now incorporates the global $post variable:
global $post;
$post = get_post( $id );
echo apply_filters( 'the_content', $post->post_content );
die();
This is essentially just an extension of your own findings, but I thought it might help to have a clear answer/solution to the problem of oEmbed + AJAX for WordPress.
on my baked goods site, I have WooCommerce installed and another plugin called Order Delivery Date for WooCommerce.
I installed the second plugin so my customers would be able to choose a delivery date for their items, however, I am trying to make the form field a required field. So far, I've just been able to make the field look like a required field, but have not figured out how to make sure that it is actually enforced. Any ideas?
Also, if anyone is familiar with WooCommerce, do you know how I would be able to make it so that customers receive this delivery date information in their order confirmation emails?
Thank you in advance!
My site: www.monpetitfour.com
You should try to had something like that :
add_action('woocommerce_checkout_process', 'my_custom_checkout_field_process');
function my_custom_checkout_field_process() {
// You can make your own control here
if ( ! $_POST[ 'e_deliverydate' ] )
wc_add_notice( __( 'Please select a delivery date' ), 'error' );
}
For the email, the easiest is to save the meta value ( I think it's already done by your plugin). Then you need to copy the template email (customer-processing-order.php) on your theme and change in the template :
<?php $delivery_date = get_post_meta( $order->id, 'custom_field_date', true);
// If the plugin is well developed, you can't directly use magic getters :
// $delivery_date = $order->e_deliverydate;
// Can only by use if the post meta start with _
?>
Your delivery date is <?php echo $delivery_date ?>
You can also use
date_i18n( woocommerce_date_format(), strtotime( $delivery_date ) );
In order to format the date correctly.
On the code above, you just need to find the name of the custom field used by the plugin ( you can search easily on the table wp_postmeta searching by an existing order (should be _e_deliverydate).
Add the following code to your theme's functions.php file
add_action('woocommerce_checkout_process', 'my_custom_checkout_field_process');
function my_custom_checkout_field_process() {
// Check if set, if its not set add an error.
if ( ! $_POST['e_deliverydate'] )
wc_add_notice( __( 'Please select a delivery date.' ), 'error' );
}
Now to get the email to show the custom field,
add_filter('woocommerce_email_order_meta_keys', 'my_woocommerce_email_order_meta_keys');
function my_woocommerce_email_order_meta_keys( $keys ) {
$keys['Delivery Date'] = '_e_deliverydate';
return $keys;
}
EDIT : Seems the field value isn't being saved to the database, try saving it explicitly
add_action( 'woocommerce_checkout_update_order_meta', 'my_custom_checkout_field_update_order_meta' );
function my_custom_checkout_field_update_order_meta( $order_id ) {
if ( ! empty( $_POST['e_deliverydate'] ) ) {
update_post_meta( $order_id, '_e_deliverydate', sanitize_text_field( $_POST['e_deliverydate'] ) );
}
}
i'm trying to learn the way for uploading image file through custom field but cant get the easiest code to do it. i just done a bit here:
add_action('admin_init', 'create_image_box');
function create_image_box() {
add_meta_box( 'meta-box-id', 'Image Field', 'display_image_box', 'post', 'normal', 'high' );
}
//Display the image_box
function display_image_box() {
global $post;
$image_id = get_post_meta($post->ID,'xxxx_image', true);
echo 'Upload an image: <input type="file" name="xxxx_image" id="xxxx_image" />';
// Upload done: show it now...(as thmbnail or 60 x 50)
anybody please take me to next step and show the way to display the image in blog page too.
Lets go Stepwise here:
Create custom field Meta Box for inserting Image Url in post type => post.
Update/Save the custom field value in back end.
Display the custom field value in front end.
Seeing your code it seems that you are missing #2. Try the code below to save custom field:
function save_joe_details($post_id){
global $post;
if ( defined('DOING_AUTOSAVE') && DOING_AUTOSAVE )
return $post_id;
update_post_meta($post->ID, "custom_field_image", $_POST["custom_field_image"] );
}
add_action('save_post', 'save_joe_details');
Code for #3 that displaying the custom field will be:
<?php global $post;
$custom_image = get_post_custom($post->ID); ?>
<img src="<?php echo $custom_image["custom_field_image"][0] ?>" />
you could try wrap it in wp_get_attachment_url like this:-
wp_get_attachment_url( $custom_image["custom_field_image"][0] );
I added a meta-box to the edit link page & I can't save the data whatever I put in the field. How can I only update the meta-box without saving the data in the database? Here is my code:
// backwards compatible
add_action( 'admin_init', 'blc_add_custom_link_box', 1 );
/* Do something with the data entered */
add_action( 'save_link', 'blc_save_linkdata' );
/* Adds a box to the main column on the Post and Page edit screens */
function blc_add_custom_link_box() {
add_meta_box(
'backlinkdiv',
'Backlink URL',
'blc_backlink_url_input',
'link',
'normal',
'high'
);
}
/* Prints the box content */
function blc_backlink_url_input( $post ) {
// Use nonce for verification
wp_nonce_field( plugin_basename( __FILE__ ), 'blc_noncename' );
// The actual fields for data entry
echo '<input type="text" id="backlink-url" name="backlink_url" value="put your backlink here" size="60" />';
#echo "<p> _e('Example: <code>http://Example.org/Linkpage</code> — don’t forget the <code>http://</code>')</p>";
}
How can I save or update the data of the input field of metabox? Only the data should be updated in the metabox. It should not save in database by any type of custom field.
I think it actually would be a good idea to save as a custom field, only one that doesn't show up in the custom field box. You can accomplish the latter by adding a "_" at the beginning of the custom field's name (i.e. "_my_custom_field" instead of "my_custom_field".
Here's a sample function to save your meta box data. I changed the names to match the code you have above.
:
<?php
function blc_save_postdata($post_id){
// Verify this came from the our screen and with proper authorization,
// because save_post can be triggered at other times
if ( !wp_verify_nonce( $_POST['blc_noncename'], plugin_basename(__FILE__) )) {
return $post_id;
}
// Verify if this is an auto save routine. If it is our form has not been submitted, so we dont want
// to do anything
if ( defined('DOING_AUTOSAVE') && DOING_AUTOSAVE )
return $post_id;
// Check permissions to edit pages and/or posts
if ( 'page' == $_POST['post_type'] || 'post' == $_POST['post_type']) {
if ( !current_user_can( 'edit_page', $post_id ) || !current_user_can( 'edit_post', $post_id ))
return $post_id;
}
// OK, we're authenticated: we need to find and save the data
$blc = $_POST['backlink_url'];
// save data in INVISIBLE custom field (note the "_" prefixing the custom fields' name
update_post_meta($post_id, '_backlink_url', $blc);
}
//On post save, save plugin's data
add_action('save_post', array($this, 'blc_save_postdata'));
?>
And that should be it. I used this page as a reference: http://codex.wordpress.org/Function_Reference/add_meta_box
Hook the action save_post - it receives saved post ID and allows you to update the post the way you need when submitting post editor page. Don't forget that this action will be called for EVERY post saved - you need to only handle posts having your custom meta box.
you must disable this code
if ( defined('DOING_AUTOSAVE') && DOING_AUTOSAVE )
return $post_id;
what it does is that it blocks your code below cause it detects that your doing some auto save on it.