Is there any way to validate custom meta box fields without using javascript. If it doesn't validate I want to stop the post from being saved to the database.
Since the 'save_post' action is run AFTER publishing and updating, there's really no way of validating custom keys without a hackish alternative.
However, I'm thinking you can mimic the functionality you want by employing 'save_post' in the way Viral suggested, but rather than interrupt or cancel the saving process upon a validation error, you can just delete the post altogether:
add_action('save_post', 'validate_meta');
function validate_meta($post_id)
{
if ( defined('DOING_AUTOSAVE') && DOING_AUTOSAVE )
return $post_id;
/*USE THIS ONLY IF YOU ARE UTILIZING NONCE FIELDS IN A CUSTOM META BOX
if ( !wp_verify_nonce( $_POST['metabox_nonce'], basename(__FILE__) ) )
return $post_id;*/
/*Use plugin_basename(__FILE__) if this is an actual plugin, rather than
a part of your theme*/
if ( 'page' == $_POST['post_type'] )
{
if ( !current_user_can( 'edit_page', $post_id ) )
return $post_id;
}
else
{
if ( !current_user_can( 'edit_post', $post_id ) )
return $post_id;
}
/*VALIDATE YOUR METADATA HERE HOWEVER YOU LIKE
if(is_valid($_POST['metadata']))
$validated = true;
else
$validated = false;
*/
if(!$validated)
wp_delete_post($post_id, true);
else
return $post_id;
}
The only thing to watch out for with this approach is that it will run upon both Publishing and Updating as well. You may want to consider adding a check to ensure that the post is deleted only for newly published posts, and updated posts are rolled back to a previous version and the invalid revision is deleted.
The wp_insert_post_data filter is what you are looking for. Something like this should do the trick:
add_filter( 'wp_insert_post_data', 'my_validation_function' );
function my_validation_function( $data ) {
// Don't want to do this on autosave
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE )
return $data;
if ( $data['some_key'] != 'some_value' ||
$_POST['some_meta_key'] != 'some_meta_value' ) {
$data['post_status'] = 'draft'; // or whatever status to revert to
add_filter( 'redirect_post_location', 'remove_message'); // remove the publish success message
}
return $data;
}
function remove_message( $location ) {
return remove_query_arg( 'message', $location);
}
Straight from the WP Codex # http://codex.wordpress.org/Function_Reference/add_meta_box, you call the save_post hook and specify the function that will be run to validate/save your data:
/* Do something with the data entered */
add_action('save_post', 'myplugin_save_postdata');
Then you define that function, which will automatically be passed the post id. Additionally, you can access the $_POST array to get the values in your metaboxes:
/* When the post is saved, saves our custom data */
function myplugin_save_postdata( $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;
// 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['myplugin_noncename'], plugin_basename(__FILE__) ) )
return $post_id;
// Check permissions
if ( 'page' == $_POST['post_type'] )
{
if ( !current_user_can( 'edit_page', $post_id ) )
return $post_id;
}
else
{
if ( !current_user_can( 'edit_post', $post_id ) )
return $post_id;
}
// OK, we're authenticated: we need to find and save the data
$mydata = $_POST['myplugin_new_field'];
// Do something with $mydata
// probably using add_post_meta(), update_post_meta(), or
// a custom table (see Further Reading section below)
return $mydata;
}
All of your routines to valid data will be done within this function. In the end, you will likely save the data using something like:
update_post_meta('meta_key', 'meta_value');
Related
I have a php class inside my plugin that generates custom metaboxes for my post types.
Theses metaboxes contain inputs that I want to save with the "save_post" hook, as I have done a million times :
class CcmComponentMetaboxElement{
public function __construct($config){
/* Some logic */
$this->config = $config;
$this->ccm_metabox_display();
$this->ccm_metabox_save();
}
public function ccm_metabox_display(){
$metabox_id = $this->config->id;
$context = property_exists($this->config, 'context') ? $this->config->context : 'advanced';
$priority = property_exists($this->config, 'priority') ? $this->config->priority : 'default';
add_meta_box(
$metabox_id,
$this->config->title,
array($this, 'ccm_metabox_display_callback'),
$this->config->screens,
$context,
$priority
);
}
public function ccm_metabox_display_callback(){
/* Some logic that displays metabox with custom post_metadata inside */
}
public function ccm_metabox_save(){
foreach($this->config->screens as $screen){
$action_name = 'save_post_' . $screen;
add_action($action_name, function($post_id){
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
return;
}
if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
return;
}
if(empty($_POST)){
error_log( 'EMPTY POST' );
return;
}
if ( ! array_key_exists( $this->nonce_name, $_POST ) ||
!wp_verify_nonce( $_POST[$this->nonce_name], $this->nonce_value )) {
error_log( 'NONCE NOT VERIFIED' );
return;
}
foreach($this->config->fields as $field){
if(array_key_exists( $field->id, $_POST )){
update_post_meta( $post_id, $field->id, $_POST[$field->id]);
}
}
}, 10, 1);
}
}
}
The issue i'm facing this time, is that when I'm saving a post with a custom metabox, my hook from my plugin is well executed, but the script stops when checking if $_POST is empty (it records the 'EMPTY POST' inside my debug.log file).
After clicking on "update", fetch executes the save action but returns 302 from the server
The system always returns a 302 status code and $_POST is always empty, no matter what I write in my function, except when I var_dump the $_SERVER variable (for some reason, when I do this it works fine, but the Worpress fetch api doesn't like it).
Does anyone have any suggestions to help me ?
Thanks !
I tried many solutions that didn't work in my case, including refreshing permalinks, forcing https in wp-config, ....
I only activated my plugin and an empty theme of mine with the bare minimum.
I have the same problem with the hook 'restafterinsert_post'
I translated a post using WPML.
The problem is that it doesn't add a featured image to the translated post.
It takes up too many size to duplicate images using a WPML media transltion,
so I'd like to add a featured image without duplicate to a tranlsated post (wordpress).
Would you please let me know how to solve this problem?
Thank you.
In case there is someone who needs this.
I solved the problem using the foolowing code.
add_action( 'wp_insert_post', 'my_duplicate_on_publish' );
function my_duplicate_on_publish( $post_id ) {
global $post;
// don't save for autosave
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
return $post_id;
}
// dont save for revisions
if ( isset( $post->post_type ) && $post->post_type == 'revision' ) {
return $post_id;
}
if ($post->post_type=='your-post-type') {
//we need this to avoid recursion see add_action at the end
remove_action( 'wp_insert_post', 'my_duplicate_on_publish' );
// make duplicates if the post being saved
// #1. itself is not a duplicate of another or
// #2. does not already have translations
$is_translated = apply_filters( 'wpml_element_has_translations', '', $post_id, $post->post_type );
if ( !$is_translated ) {
do_action( 'wpml_admin_make_post_duplicates', $post_id );
}
//must hook again - see remove_action further up
add_action( 'wp_insert_post', 'my_duplicate_on_publish' );
}
}
My question is,
Is there any filter or action that trigger when new post is insert in database..?
The reason behind it is I want to add key in post meta when new post is insert from admin side.
I got Action called "save_post" but after refer link .This action trigger in created and update post.
but I only want to add meta key when post is created not at update time
You can use wp_insert_post so you will get post_id as soon as post inserted and you can then use that to add meta_key.
If you are not using wp_insert_post and want to use action then you can simply put below code :
if ( wp_is_post_revision( $post_id ) )
return;
which means that if you are updating the post, then it will return back from function.
EDITED
Method-1 to achieve it.
You can simply check with the get_post method that post is there or not.something like below:
add_action('save_post', 'check_for_post_in_database');
function check_for_post_in_database($post_id) {
//check if the post is in the database or not with get_post( $post_id ) == null
if( get_post( $post_id ) == null ) {
//your code to add meta
}
}
//You can do same thing with publish_post
Method-2 to achieve it.
add_action('publish_post', 'check_for_meta_in_database');
function check_for_meta_in_database($post_id) {
global $wpdb;
$your_meta = get_post_meta($post_id, 'meta_key', true);
if( empty( $your_meta ) && ! wp_is_post_revision( $post_id ) ) {
update_post_meta($post_id, 'meta_key', 'meta_value');
}
}
But as you said there are many meta there, this method will be bit long.
Method-3 to achieve it.
You can do as rnevius suggested which is the one even I would opt. Its like :
add_action( 'transition_post_status', 'check_transition_and_then_add_meta', 10, 3 );
function check_transition_and_then_add_meta( $new_status, $old_status, $post ) {
if ( ( 'draft' === $old_status || 'auto-draft' === $old_status ) && $new_status === 'publish' ) {
add_post_meta($post->ID, 'your_meta_key', 'your_meta_value');
}
}
or else you can do it with draft_to_publish like:
//as rnevius suggested {$old_status}_to_{$new_status}
add_action( 'draft_to_publish', 'add_meta_when_status_change' );
function add_meta_when_status_change() {
add_post_meta($post->ID, 'your_meta_key', 'your_meta_value');
}
You can refer codex for more information about post transition.
You're looking for draft_to_publish.
An {old_status}_to_{new_status} action will execute when a post transitions from {old_status} to {new_status}. The action is accompanied by the $post object.
I've made a function to hide the Mine filter in the screen wp-admin/edit.php, using unset:
But when I click in the Posts menu (wp-admin/edit.php), it will not go to All by default, it still goes to Mine filter.
How to make the default to be All?
We can intercept what page we arrived at the very beginning of the load process (with load-(page)) and check if redirection is needed. See comments for logic.
add_action( 'load-edit.php', function()
{
global $typenow;
// Not our post type, bail out
if( 'post' !== $typenow )
return;
// Administrator users don't need this, bail out
if( current_user_can('add_users') )
return;
// Only the Mine tab fills this conditions, redirect
if( !isset( $_GET['post_status'] ) && !isset( $_GET['all_posts'] ) )
{
wp_redirect( admin_url('edit.php?all_posts=1') );
exit();
}
});
I need an action to be triggered when a detached media is being attached to a post.
It seems that this behavior is handled in wp-admin/upload.php on line 76 by setting the parent of the attachment post with a DB query. There are no hooks in the file.
Is there a way to get an action triggered on this event (some low level DB write hook or whatever) in which I could get the ID of the parent post to which the attachment is being attached?
You could hook into wp_redirect and get the parent post ID from the $_REQUEST. The following is based on an answer I gave here:
add_filter( 'wp_redirect', 'so16798615_wp_redirect', 25, 1 );
function so16798615_wp_redirect( $location )
{
if( ! is_admin() )
return $location;
global $pagenow;
if( 'upload.php' == $pagenow && isset( $_REQUEST['found_post_id'] ) )
{
$parent_id = (int) $_REQUEST['found_post_id'];
if ( ! $parent_id )
return $location;
$parent = get_post( $parent_id );
// do stuff with parent
}
return $location;
}