wp_insert_post

Insert or update a post.

Description

(int|WP_Error) wp_insert_post( (array) $postarr, (bool) $wp_error = false ); 

If the $postarr parameter has ID set to a value, then post will be updated.

You can set the post date manually, by setting the values for post_date and post_date_gmt keys. You can close the comments or open the comments by setting the value for comment_status key.

Returns (int|WP_Error)

The post ID on success. The value 0 or WP_Error on failure.

Parameters (2)

0. $postarr (array)
An array of elements that make up a post to update or insert.

Options

  • ID (int) => 0

    The post ID. If equal to something other than 0, the post with that ID will be updated.

  • post_author (int) => is the current user ID

    The ID of the user who added the post.

  • post_date (string) => is the current time

    The date of the post.

  • post_date_gmt (string) => 'is the value of $post_date'

    The date of the post in the GMT timezone.

  • post_content (mixed) => null

    The post content.

  • post_content_filtered (string) => ''

    The filtered post content.

  • post_title (string) => ''

    The post title.

  • post_excerpt (string) => ''

    The post excerpt.

  • post_status (string) => 'draft'

    The post status.

  • post_type (string) => 'post'

    The post type.

  • comment_status (string) => 'default_comment_status'

    Whether the post can accept comments. Accepts open or closed..

  • ping_status (string) => 'default_ping_status'

    Whether the post can accept pings. Accepts open or closed..

  • post_password (string) => ''

    The password to access the post.

  • post_name (string) => is the sanitized post title when creating a new post

    The post name.

  • to_ping (string) => ''

    Space or carriage return-separated list of URLs to ping.

  • pinged (string) => ''

    Space or carriage return-separated list of URLs that have been pinged.

  • post_modified (string) => is the current time

    The date when the post was last modified.

  • post_modified_gmt (string) => is the current time

    The date when the post was last modified in the GMT timezone.

  • post_parent (int) => 0

    Set this for the post it belongs to, if any.

  • menu_order (int) => 0

    The order the post should be displayed in.

  • post_mime_type (string) => ''

    The mime type of the post.

  • guid (string) => ''

    Global Unique ID for referencing the post.

  • post_category (array) => 'default_category'

    Array of category names, slugs, or IDs.

  • tax_input (array) => null

    Array of taxonomy terms keyed by their taxonomy name.

array(

    /**
     * The post ID. If equal to something other than 0, the post with that ID will be updated.
     *
     * @type int
     */
    'ID' => 0,

    /**
     * The ID of the user who added the post.
     *
     * @type int
     * @default is the current user ID
     */
    'post_author' => is the current user ID,

    /**
     * The date of the post.
     *
     * @type string
     * @default is the current time
     */
    'post_date' => is the current time,

    /**
     * The date of the post in the GMT timezone.
     *
     * @type string
     * @default 'is the value of $post_date'
     */
    'post_date_gmt' => 'is the value of $post_date',

    /**
     * The post content.
     *
     * @type mixed
     * @default null
     */
    'post_content' => null,

    /**
     * The filtered post content.
     *
     * @type string
     * @default ''
     */
    'post_content_filtered' => '',

    /**
     * The post title.
     *
     * @type string
     * @default ''
     */
    'post_title' => '',

    /**
     * The post excerpt.
     *
     * @type string
     * @default ''
     */
    'post_excerpt' => '',

    /**
     * The post status.
     *
     * @type string
     * @default 'draft'
     */
    'post_status' => 'draft',

    /**
     * The post type.
     *
     * @type string
     * @default 'post'
     */
    'post_type' => 'post',

    /**
     * Whether the post can accept comments. Accepts 'open' or 'closed'.
     *
     * @type string
     * @default 'default_comment_status'
     */
    'comment_status' => 'default_comment_status',

    /**
     * Whether the post can accept pings. Accepts 'open' or 'closed'.
     *
     * @type string
     * @default 'default_ping_status'
     */
    'ping_status' => 'default_ping_status',

    /**
     * The password to access the post.
     *
     * @type string
     * @default ''
     */
    'post_password' => '',

    /**
     * The post name.
     *
     * @type string
     * @default is the sanitized post title when creating a new post
     */
    'post_name' => is the sanitized post title when creating a new post,

    /**
     * Space or carriage return-separated list of URLs to ping.
     *
     * @type string
     * @default ''
     */
    'to_ping' => '',

    /**
     * Space or carriage return-separated list of URLs that have been pinged.
     *
     * @type string
     * @default ''
     */
    'pinged' => '',

    /**
     * The date when the post was last modified.
     *
     * @type string
     * @default is the current time
     */
    'post_modified' => is the current time,

    /**
     * The date when the post was last modified in the GMT timezone.
     *
     * @type string
     * @default is the current time
     */
    'post_modified_gmt' => is the current time,

    /**
     * Set this for the post it belongs to, if any.
     *
     * @type int
     */
    'post_parent' => 0,

    /**
     * The order the post should be displayed in.
     *
     * @type int
     */
    'menu_order' => 0,

    /**
     * The mime type of the post.
     *
     * @type string
     * @default ''
     */
    'post_mime_type' => '',

    /**
     * Global Unique ID for referencing the post.
     *
     * @type string
     * @default ''
     */
    'guid' => '',

    /**
     * Array of category names, slugs, or IDs.
     *
     * @type array
     * @default 'default_category'
     */
    'post_category' => 'default_category',

    /**
     * Array of taxonomy terms keyed by their taxonomy name.
     *
     * @type array
     * @default null
     */
    'tax_input' => null
);        

1. $wp_error — Optional. (bool) => false
Whether to return a WP_Error on failure. Default false.

Usage

  1. if ( !function_exists( 'wp_insert_post' ) ) { 
  2. require_once ABSPATH . WPINC . '/post.php'; 
  3.  
  4. // An array of elements that make up a post to update or insert. 
  5. $postarr = array( 
  6. 'ID' => 0, 
  7. 'post_author' => is the current user ID, 
  8. 'post_date' => is the current time, 
  9. 'post_date_gmt' => 'is the value of $post_date', 
  10. 'post_content' => null, 
  11. 'post_content_filtered' => '', 
  12. 'post_title' => '', 
  13. 'post_excerpt' => '', 
  14. 'post_status' => 'draft', 
  15. 'post_type' => 'post', 
  16. 'comment_status' => 'default_comment_status', 
  17. 'ping_status' => 'default_ping_status', 
  18. 'post_password' => '', 
  19. 'post_name' => is the sanitized post title when creating a new post, 
  20. 'to_ping' => '', 
  21. 'pinged' => '', 
  22. 'post_modified' => is the current time, 
  23. 'post_modified_gmt' => is the current time, 
  24. 'post_parent' => 0, 
  25. 'menu_order' => 0, 
  26. 'post_mime_type' => '', 
  27. 'guid' => '', 
  28. 'post_category' => 'default_category', 
  29. 'tax_input' => null 
  30. ); 
  31.  
  32. // Optional. Whether to return a WP_Error on failure. Default false. 
  33. $wp_error = false; 
  34.  
  35. // NOTICE! Understand what this does before running. 
  36. $result = wp_insert_post($postarr, $wp_error); 
  37.  

Defined (1)

The function is defined in the following location(s).

/wp-includes/post.php  
  1. function wp_insert_post( $postarr, $wp_error = false ) { 
  2. global $wpdb; 
  3.  
  4. $user_id = get_current_user_id(); 
  5.  
  6. $defaults = array( 
  7. 'post_author' => $user_id,  
  8. 'post_content' => '',  
  9. 'post_content_filtered' => '',  
  10. 'post_title' => '',  
  11. 'post_excerpt' => '',  
  12. 'post_status' => 'draft',  
  13. 'post_type' => 'post',  
  14. 'comment_status' => '',  
  15. 'ping_status' => '',  
  16. 'post_password' => '',  
  17. 'to_ping' => '',  
  18. 'pinged' => '',  
  19. 'post_parent' => 0,  
  20. 'menu_order' => 0,  
  21. 'guid' => '',  
  22. 'import_id' => 0,  
  23. 'context' => '',  
  24. ); 
  25.  
  26. $postarr = wp_parse_args($postarr, $defaults); 
  27.  
  28. unset( $postarr[ 'filter' ] ); 
  29.  
  30. $postarr = sanitize_post($postarr, 'db'); 
  31.  
  32. // Are we updating or creating? 
  33. $post_ID = 0; 
  34. $update = false; 
  35. $guid = $postarr['guid']; 
  36.  
  37. if ( ! empty( $postarr['ID'] ) ) { 
  38. $update = true; 
  39.  
  40. // Get the post ID and GUID. 
  41. $post_ID = $postarr['ID']; 
  42. $post_before = get_post( $post_ID ); 
  43. if ( is_null( $post_before ) ) { 
  44. if ( $wp_error ) { 
  45. return new WP_Error( 'invalid_post', __( 'Invalid post ID.' ) ); 
  46. return 0; 
  47.  
  48. $guid = get_post_field( 'guid', $post_ID ); 
  49. $previous_status = get_post_field('post_status', $post_ID ); 
  50. } else { 
  51. $previous_status = 'new'; 
  52.  
  53. $post_type = empty( $postarr['post_type'] ) ? 'post' : $postarr['post_type']; 
  54.  
  55. $post_title = $postarr['post_title']; 
  56. $post_content = $postarr['post_content']; 
  57. $post_excerpt = $postarr['post_excerpt']; 
  58. if ( isset( $postarr['post_name'] ) ) { 
  59. $post_name = $postarr['post_name']; 
  60. } elseif ( $update ) { 
  61. // For an update, don't modify the post_name if it wasn't supplied as an argument. 
  62. $post_name = $post_before->post_name; 
  63.  
  64. $maybe_empty = 'attachment' !== $post_type 
  65. && ! $post_content && ! $post_title && ! $post_excerpt 
  66. && post_type_supports( $post_type, 'editor' ) 
  67. && post_type_supports( $post_type, 'title' ) 
  68. && post_type_supports( $post_type, 'excerpt' ); 
  69.  
  70. /** 
  71. * Filters whether the post should be considered "empty". 
  72. * The post is considered "empty" if both: 
  73. * 1. The post type supports the title, editor, and excerpt fields 
  74. * 2. The title, editor, and excerpt fields are all empty 
  75. * Returning a truthy value to the filter will effectively short-circuit 
  76. * the new post being inserted, returning 0. If $wp_error is true, a WP_Error 
  77. * will be returned instead. 
  78. * @since 3.3.0 
  79. * @param bool $maybe_empty Whether the post should be considered "empty". 
  80. * @param array $postarr Array of post data. 
  81. */ 
  82. if ( apply_filters( 'wp_insert_post_empty_content', $maybe_empty, $postarr ) ) { 
  83. if ( $wp_error ) { 
  84. return new WP_Error( 'empty_content', __( 'Content, title, and excerpt are empty.' ) ); 
  85. } else { 
  86. return 0; 
  87.  
  88. $post_status = empty( $postarr['post_status'] ) ? 'draft' : $postarr['post_status']; 
  89. if ( 'attachment' === $post_type && ! in_array( $post_status, array( 'inherit', 'private', 'trash', 'auto-draft' ), true ) ) { 
  90. $post_status = 'inherit'; 
  91.  
  92. if ( ! empty( $postarr['post_category'] ) ) { 
  93. // Filter out empty terms. 
  94. $post_category = array_filter( $postarr['post_category'] ); 
  95.  
  96. // Make sure we set a valid category. 
  97. if ( empty( $post_category ) || 0 == count( $post_category ) || ! is_array( $post_category ) ) { 
  98. // 'post' requires at least one category. 
  99. if ( 'post' == $post_type && 'auto-draft' != $post_status ) { 
  100. $post_category = array( get_option('default_category') ); 
  101. } else { 
  102. $post_category = array(); 
  103.  
  104. // Don't allow contributors to set the post slug for pending review posts. 
  105. if ( 'pending' == $post_status && !current_user_can( 'publish_posts' ) ) { 
  106. $post_name = ''; 
  107.  
  108. /** 
  109. * Create a valid post name. Drafts and pending posts are allowed to have 
  110. * an empty post name. 
  111. */ 
  112. if ( empty($post_name) ) { 
  113. if ( !in_array( $post_status, array( 'draft', 'pending', 'auto-draft' ) ) ) { 
  114. $post_name = sanitize_title($post_title); 
  115. } else { 
  116. $post_name = ''; 
  117. } else { 
  118. // On updates, we need to check to see if it's using the old, fixed sanitization context. 
  119. $check_name = sanitize_title( $post_name, '', 'old-save' ); 
  120. if ( $update && strtolower( urlencode( $post_name ) ) == $check_name && get_post_field( 'post_name', $post_ID ) == $check_name ) { 
  121. $post_name = $check_name; 
  122. } else { // new post, or slug has changed. 
  123. $post_name = sanitize_title($post_name); 
  124.  
  125. /** 
  126. * If the post date is empty (due to having been new or a draft) and status 
  127. * is not 'draft' or 'pending', set date to now. 
  128. */ 
  129. if ( empty( $postarr['post_date'] ) || '0000-00-00 00:00:00' == $postarr['post_date'] ) { 
  130. if ( empty( $postarr['post_date_gmt'] ) || '0000-00-00 00:00:00' == $postarr['post_date_gmt'] ) { 
  131. $post_date = current_time( 'mysql' ); 
  132. } else { 
  133. $post_date = get_date_from_gmt( $postarr['post_date_gmt'] ); 
  134. } else { 
  135. $post_date = $postarr['post_date']; 
  136.  
  137. // Validate the date. 
  138. $mm = substr( $post_date, 5, 2 ); 
  139. $jj = substr( $post_date, 8, 2 ); 
  140. $aa = substr( $post_date, 0, 4 ); 
  141. $valid_date = wp_checkdate( $mm, $jj, $aa, $post_date ); 
  142. if ( ! $valid_date ) { 
  143. if ( $wp_error ) { 
  144. return new WP_Error( 'invalid_date', __( 'Invalid date.' ) ); 
  145. } else { 
  146. return 0; 
  147.  
  148. if ( empty( $postarr['post_date_gmt'] ) || '0000-00-00 00:00:00' == $postarr['post_date_gmt'] ) { 
  149. if ( ! in_array( $post_status, array( 'draft', 'pending', 'auto-draft' ) ) ) { 
  150. $post_date_gmt = get_gmt_from_date( $post_date ); 
  151. } else { 
  152. $post_date_gmt = '0000-00-00 00:00:00'; 
  153. } else { 
  154. $post_date_gmt = $postarr['post_date_gmt']; 
  155.  
  156. if ( $update || '0000-00-00 00:00:00' == $post_date ) { 
  157. $post_modified = current_time( 'mysql' ); 
  158. $post_modified_gmt = current_time( 'mysql', 1 ); 
  159. } else { 
  160. $post_modified = $post_date; 
  161. $post_modified_gmt = $post_date_gmt; 
  162.  
  163. if ( 'attachment' !== $post_type ) { 
  164. if ( 'publish' == $post_status ) { 
  165. $now = gmdate('Y-m-d H:i:59'); 
  166. if ( mysql2date('U', $post_date_gmt, false) > mysql2date('U', $now, false) ) { 
  167. $post_status = 'future'; 
  168. } elseif ( 'future' == $post_status ) { 
  169. $now = gmdate('Y-m-d H:i:59'); 
  170. if ( mysql2date('U', $post_date_gmt, false) <= mysql2date('U', $now, false) ) { 
  171. $post_status = 'publish'; 
  172.  
  173. // Comment status. 
  174. if ( empty( $postarr['comment_status'] ) ) { 
  175. if ( $update ) { 
  176. $comment_status = 'closed'; 
  177. } else { 
  178. $comment_status = get_default_comment_status( $post_type ); 
  179. } else { 
  180. $comment_status = $postarr['comment_status']; 
  181.  
  182. // These variables are needed by compact() later. 
  183. $post_content_filtered = $postarr['post_content_filtered']; 
  184. $post_author = isset( $postarr['post_author'] ) ? $postarr['post_author'] : $user_id; 
  185. $ping_status = empty( $postarr['ping_status'] ) ? get_default_comment_status( $post_type, 'pingback' ) : $postarr['ping_status']; 
  186. $to_ping = isset( $postarr['to_ping'] ) ? sanitize_trackback_urls( $postarr['to_ping'] ) : ''; 
  187. $pinged = isset( $postarr['pinged'] ) ? $postarr['pinged'] : ''; 
  188. $import_id = isset( $postarr['import_id'] ) ? $postarr['import_id'] : 0; 
  189.  
  190. /** 
  191. * The 'wp_insert_post_parent' filter expects all variables to be present. 
  192. * Previously, these variables would have already been extracted 
  193. */ 
  194. if ( isset( $postarr['menu_order'] ) ) { 
  195. $menu_order = (int) $postarr['menu_order']; 
  196. } else { 
  197. $menu_order = 0; 
  198.  
  199. $post_password = isset( $postarr['post_password'] ) ? $postarr['post_password'] : ''; 
  200. if ( 'private' == $post_status ) { 
  201. $post_password = ''; 
  202.  
  203. if ( isset( $postarr['post_parent'] ) ) { 
  204. $post_parent = (int) $postarr['post_parent']; 
  205. } else { 
  206. $post_parent = 0; 
  207.  
  208. /** 
  209. * Filters the post parent -- used to check for and prevent hierarchy loops. 
  210. * @since 3.1.0 
  211. * @param int $post_parent Post parent ID. 
  212. * @param int $post_ID Post ID. 
  213. * @param array $new_postarr Array of parsed post data. 
  214. * @param array $postarr Array of sanitized, but otherwise unmodified post data. 
  215. */ 
  216. $post_parent = apply_filters( 'wp_insert_post_parent', $post_parent, $post_ID, compact( array_keys( $postarr ) ), $postarr ); 
  217.  
  218. /** 
  219. * If the post is being untrashed and it has a desired slug stored in post meta,  
  220. * reassign it. 
  221. */ 
  222. if ( 'trash' === $previous_status && 'trash' !== $post_status ) { 
  223. $desired_post_slug = get_post_meta( $post_ID, '_wp_desired_post_slug', true ); 
  224. if ( $desired_post_slug ) { 
  225. delete_post_meta( $post_ID, '_wp_desired_post_slug' ); 
  226. $post_name = $desired_post_slug; 
  227.  
  228. // If a trashed post has the desired slug, change it and let this post have it. 
  229. if ( 'trash' !== $post_status && $post_name ) { 
  230.  
  231. // When trashing an existing post, change its slug to allow non-trashed posts to use it. 
  232. if ( 'trash' === $post_status && 'trash' !== $previous_status && 'new' !== $previous_status ) { 
  233. $post_name = wp_add_trashed_suffix_to_post_name_for_post( $post_ID ); 
  234.  
  235. $post_name = wp_unique_post_slug( $post_name, $post_ID, $post_status, $post_type, $post_parent ); 
  236.  
  237. // Don't unslash. 
  238. $post_mime_type = isset( $postarr['post_mime_type'] ) ? $postarr['post_mime_type'] : ''; 
  239.  
  240. // Expected_slashed (everything!). 
  241. $data = compact( 'post_author', 'post_date', 'post_date_gmt', 'post_content', 'post_content_filtered', 'post_title', 'post_excerpt', 'post_status', 'post_type', 'comment_status', 'ping_status', 'post_password', 'post_name', 'to_ping', 'pinged', 'post_modified', 'post_modified_gmt', 'post_parent', 'menu_order', 'post_mime_type', 'guid' ); 
  242.  
  243. $emoji_fields = array( 'post_title', 'post_content', 'post_excerpt' ); 
  244.  
  245. foreach ( $emoji_fields as $emoji_field ) { 
  246. if ( isset( $data[ $emoji_field ] ) ) { 
  247. $charset = $wpdb->get_col_charset( $wpdb->posts, $emoji_field ); 
  248. if ( 'utf8' === $charset ) { 
  249. $data[ $emoji_field ] = wp_encode_emoji( $data[ $emoji_field ] ); 
  250.  
  251. if ( 'attachment' === $post_type ) { 
  252. /** 
  253. * Filters attachment post data before it is updated in or added to the database. 
  254. * @since 3.9.0 
  255. * @param array $data An array of sanitized attachment post data. 
  256. * @param array $postarr An array of unsanitized attachment post data. 
  257. */ 
  258. $data = apply_filters( 'wp_insert_attachment_data', $data, $postarr ); 
  259. } else { 
  260. /** 
  261. * Filters slashed post data just before it is inserted into the database. 
  262. * @since 2.7.0 
  263. * @param array $data An array of slashed post data. 
  264. * @param array $postarr An array of sanitized, but otherwise unmodified post data. 
  265. */ 
  266. $data = apply_filters( 'wp_insert_post_data', $data, $postarr ); 
  267. $data = wp_unslash( $data ); 
  268. $where = array( 'ID' => $post_ID ); 
  269.  
  270. if ( $update ) { 
  271. /** 
  272. * Fires immediately before an existing post is updated in the database. 
  273. * @since 2.5.0 
  274. * @param int $post_ID Post ID. 
  275. * @param array $data Array of unslashed post data. 
  276. */ 
  277. do_action( 'pre_post_update', $post_ID, $data ); 
  278. if ( false === $wpdb->update( $wpdb->posts, $data, $where ) ) { 
  279. if ( $wp_error ) { 
  280. return new WP_Error('db_update_error', __('Could not update post in the database'), $wpdb->last_error); 
  281. } else { 
  282. return 0; 
  283. } else { 
  284. // If there is a suggested ID, use it if not already present. 
  285. if ( ! empty( $import_id ) ) { 
  286. $import_id = (int) $import_id; 
  287. if ( ! $wpdb->get_var( $wpdb->prepare("SELECT ID FROM $wpdb->posts WHERE ID = %d", $import_id) ) ) { 
  288. $data['ID'] = $import_id; 
  289. if ( false === $wpdb->insert( $wpdb->posts, $data ) ) { 
  290. if ( $wp_error ) { 
  291. return new WP_Error('db_insert_error', __('Could not insert post into the database'), $wpdb->last_error); 
  292. } else { 
  293. return 0; 
  294. $post_ID = (int) $wpdb->insert_id; 
  295.  
  296. // Use the newly generated $post_ID. 
  297. $where = array( 'ID' => $post_ID ); 
  298.  
  299. if ( empty( $data['post_name'] ) && ! in_array( $data['post_status'], array( 'draft', 'pending', 'auto-draft' ) ) ) { 
  300. $data['post_name'] = wp_unique_post_slug( sanitize_title( $data['post_title'], $post_ID ), $post_ID, $data['post_status'], $post_type, $post_parent ); 
  301. $wpdb->update( $wpdb->posts, array( 'post_name' => $data['post_name'] ), $where ); 
  302. clean_post_cache( $post_ID ); 
  303.  
  304. if ( is_object_in_taxonomy( $post_type, 'category' ) ) { 
  305. wp_set_post_categories( $post_ID, $post_category ); 
  306.  
  307. if ( isset( $postarr['tags_input'] ) && is_object_in_taxonomy( $post_type, 'post_tag' ) ) { 
  308. wp_set_post_tags( $post_ID, $postarr['tags_input'] ); 
  309.  
  310. // New-style support for all custom taxonomies. 
  311. if ( ! empty( $postarr['tax_input'] ) ) { 
  312. foreach ( $postarr['tax_input'] as $taxonomy => $tags ) { 
  313. $taxonomy_obj = get_taxonomy($taxonomy); 
  314. if ( ! $taxonomy_obj ) { 
  315. /** translators: %s: taxonomy name */ 
  316. _doing_it_wrong( __FUNCTION__, sprintf( __( 'Invalid taxonomy: %s.' ), $taxonomy ), '4.4.0' ); 
  317. continue; 
  318.  
  319. // array = hierarchical, string = non-hierarchical. 
  320. if ( is_array( $tags ) ) { 
  321. $tags = array_filter($tags); 
  322. if ( current_user_can( $taxonomy_obj->cap->assign_terms ) ) { 
  323. wp_set_post_terms( $post_ID, $tags, $taxonomy ); 
  324.  
  325. if ( ! empty( $postarr['meta_input'] ) ) { 
  326. foreach ( $postarr['meta_input'] as $field => $value ) { 
  327. update_post_meta( $post_ID, $field, $value ); 
  328.  
  329. $current_guid = get_post_field( 'guid', $post_ID ); 
  330.  
  331. // Set GUID. 
  332. if ( ! $update && '' == $current_guid ) { 
  333. $wpdb->update( $wpdb->posts, array( 'guid' => get_permalink( $post_ID ) ), $where ); 
  334.  
  335. if ( 'attachment' === $postarr['post_type'] ) { 
  336. if ( ! empty( $postarr['file'] ) ) { 
  337. update_attached_file( $post_ID, $postarr['file'] ); 
  338.  
  339. if ( ! empty( $postarr['context'] ) ) { 
  340. add_post_meta( $post_ID, '_wp_attachment_context', $postarr['context'], true ); 
  341.  
  342. // Set or remove featured image. 
  343. if ( isset( $postarr['_thumbnail_id'] ) ) { 
  344. $thumbnail_support = current_theme_supports( 'post-thumbnails', $post_type ) && post_type_supports( $post_type, 'thumbnail' ) || 'revision' === $post_type; 
  345. if ( ! $thumbnail_support && 'attachment' === $post_type && $post_mime_type ) { 
  346. if ( wp_attachment_is( 'audio', $post_ID ) ) { 
  347. $thumbnail_support = post_type_supports( 'attachment:audio', 'thumbnail' ) || current_theme_supports( 'post-thumbnails', 'attachment:audio' ); 
  348. } elseif ( wp_attachment_is( 'video', $post_ID ) ) { 
  349. $thumbnail_support = post_type_supports( 'attachment:video', 'thumbnail' ) || current_theme_supports( 'post-thumbnails', 'attachment:video' ); 
  350.  
  351. if ( $thumbnail_support ) { 
  352. $thumbnail_id = intval( $postarr['_thumbnail_id'] ); 
  353. if ( -1 === $thumbnail_id ) { 
  354. delete_post_thumbnail( $post_ID ); 
  355. } else { 
  356. set_post_thumbnail( $post_ID, $thumbnail_id ); 
  357.  
  358. clean_post_cache( $post_ID ); 
  359.  
  360. $post = get_post( $post_ID ); 
  361.  
  362. if ( ! empty( $postarr['page_template'] ) ) { 
  363. $post->page_template = $postarr['page_template']; 
  364. $page_templates = wp_get_theme()->get_page_templates( $post ); 
  365. if ( 'default' != $postarr['page_template'] && ! isset( $page_templates[ $postarr['page_template'] ] ) ) { 
  366. if ( $wp_error ) { 
  367. return new WP_Error( 'invalid_page_template', __( 'Invalid page template.' ) ); 
  368. update_post_meta( $post_ID, '_wp_page_template', 'default' ); 
  369. } else { 
  370. update_post_meta( $post_ID, '_wp_page_template', $postarr['page_template'] ); 
  371.  
  372. if ( 'attachment' !== $postarr['post_type'] ) { 
  373. wp_transition_post_status( $data['post_status'], $previous_status, $post ); 
  374. } else { 
  375. if ( $update ) { 
  376. /** 
  377. * Fires once an existing attachment has been updated. 
  378. * @since 2.0.0 
  379. * @param int $post_ID Attachment ID. 
  380. */ 
  381. do_action( 'edit_attachment', $post_ID ); 
  382. $post_after = get_post( $post_ID ); 
  383.  
  384. /** 
  385. * Fires once an existing attachment has been updated. 
  386. * @since 4.4.0 
  387. * @param int $post_ID Post ID. 
  388. * @param WP_Post $post_after Post object following the update. 
  389. * @param WP_Post $post_before Post object before the update. 
  390. */ 
  391. do_action( 'attachment_updated', $post_ID, $post_after, $post_before ); 
  392. } else { 
  393.  
  394. /** 
  395. * Fires once an attachment has been added. 
  396. * @since 2.0.0 
  397. * @param int $post_ID Attachment ID. 
  398. */ 
  399. do_action( 'add_attachment', $post_ID ); 
  400.  
  401. return $post_ID; 
  402.  
  403. if ( $update ) { 
  404. /** 
  405. * Fires once an existing post has been updated. 
  406. * @since 1.2.0 
  407. * @param int $post_ID Post ID. 
  408. * @param WP_Post $post Post object. 
  409. */ 
  410. do_action( 'edit_post', $post_ID, $post ); 
  411. $post_after = get_post($post_ID); 
  412.  
  413. /** 
  414. * Fires once an existing post has been updated. 
  415. * @since 3.0.0 
  416. * @param int $post_ID Post ID. 
  417. * @param WP_Post $post_after Post object following the update. 
  418. * @param WP_Post $post_before Post object before the update. 
  419. */ 
  420. do_action( 'post_updated', $post_ID, $post_after, $post_before); 
  421.  
  422. /** 
  423. * Fires once a post has been saved. 
  424. * The dynamic portion of the hook name, `$post->post_type`, refers to 
  425. * the post type slug. 
  426. * @since 3.7.0 
  427. * @param int $post_ID Post ID. 
  428. * @param WP_Post $post Post object. 
  429. * @param bool $update Whether this is an existing post being updated or not. 
  430. */ 
  431. do_action( "save_post_{$post->post_type}", $post_ID, $post, $update ); 
  432.  
  433. /** 
  434. * Fires once a post has been saved. 
  435. * @since 1.5.0 
  436. * @param int $post_ID Post ID. 
  437. * @param WP_Post $post Post object. 
  438. * @param bool $update Whether this is an existing post being updated or not. 
  439. */ 
  440. do_action( 'save_post', $post_ID, $post, $update ); 
  441.  
  442. /** 
  443. * Fires once a post has been saved. 
  444. * @since 2.0.0 
  445. * @param int $post_ID Post ID. 
  446. * @param WP_Post $post Post object. 
  447. * @param bool $update Whether this is an existing post being updated or not. 
  448. */ 
  449. do_action( 'wp_insert_post', $post_ID, $post, $update ); 
  450.  
  451. return $post_ID;