Codex

Plugin API/Action Reference/save post

Contents

Description

save_post is an action triggered whenever a post or page is created or updated, which could be from an import, post/page edit form, xmlrpc, or post by email. The data for the post is stored in $_POST, $_GET or the global $post_data, depending on how the post was edited. For example, quick edits use $_GET.

Example

Below is a basic example that will send an email every time a post or page is updated on your website.

<?php
add_action( 'save_post', 'my_project_updated_send_email' );

function my_project_updated_send_email( $post_id ) {

	//verify post is not a revision
	if ( !wp_is_post_revision( $post_id ) ) {

		$post_title = get_the_title( $post_id );
		$post_url = get_permalink( $post_id );
		$subject = 'A post has been updated';

		$message = "A post has been updated on your website:\n\n";
		$message .= "<a href='". $post_url. "'>" .$post_title. "</a>\n\n";

		//send email to admin
		wp_mail( 'admin@example.com', $subject, $message );
		
	}

}
?>

Custom Post Type: 'book'

Suppose you have a 'book' custom post type and you add the book author, publisher and whether or not the book is in print when editing. Here's how you could save this information as metadata:

function save_book_meta($post_id) {
    /* in production code, $slug should be set only once in the plugin,
       preferably as a class property, rather than in each function that needs it.
     */
    $slug = 'book';

    /* check whether anything should be done */
    $_POST += array("{$slug}_edit_nonce" => '');
    if ( $slug != $_POST['post_type'] ) {
        return;
    }
    if ( !current_user_can( 'edit_post', $post_id ) ) {
        return;
    }
    if ( !wp_verify_nonce( $_POST["{$slug}_edit_nonce"],
                           plugin_basename( __FILE__ ) ) )
    {
        return;
    }

    /* Request passes all checks; update the post's metadata */
    if (isset($_REQUEST['book_author'])) {
        update_post_meta($post_id, 'book_author', $_REQUEST['book_author']);
    }
    if (isset($_REQUEST['publisher'])) {
        update_post_meta($post_id, 'publisher', $_REQUEST['publisher']);
    }
    # checkboxes are present if checked, absent if not
    if ( isset( $_REQUEST['inprint'] ) ) {
        update_post_meta($post_id, 'inprint', TRUE);
    } else {
        update_post_meta($post_id, 'inprint', FALSE);
    }
}

add_action( 'save_post', 'save_book_meta');

See also quick_edit_custom_box: Creating Inputs.

Avoiding infinite loops

If you are calling a function such as wp_update_post that includes the save_post hook, your hooked function will create an infinite loop. To avoid this, unhook your function before calling the function you need, then re-hook it afterward.

<?php
// this function makes all posts in the default category private
add_action('save_post', 'set_private_categories');

function set_private_categories($post_id) {
	if ($parent_id = wp_is_post_revision($post_id)) 
		$post_id = $parent_id;
	$defaultcat = get_option('default_category');
	if (in_category($defaultcat, $post_id)) {
		// unhook this function so it doesn't loop infinitely
		remove_action('save_post', 'set_private_categories');
		// update the post, which calls save_post again
		wp_update_post(array('ID' => $post_id, 'post_status' => 'private'));
		// re-hook this function
		add_action('save_post', 'set_private_categories');
	}
}
?>

Change Log

Source File

Triggered by wp_insert_post and wp_publish_post in wp-includes/post.php

Related

Actions

Filters