Wordpress 3.3
[autoinstalls/wordpress.git] / wp-admin / includes / post.php
1 <?php
2 /**
3  * WordPress Post Administration API.
4  *
5  * @package WordPress
6  * @subpackage Administration
7  */
8
9 /**
10  * Rename $_POST data from form names to DB post columns.
11  *
12  * Manipulates $_POST directly.
13  *
14  * @package WordPress
15  * @since 2.6.0
16  *
17  * @param bool $update Are we updating a pre-existing post?
18  * @param array $post_data Array of post data. Defaults to the contents of $_POST.
19  * @return object|bool WP_Error on failure, true on success.
20  */
21 function _wp_translate_postdata( $update = false, $post_data = null ) {
22
23         if ( empty($post_data) )
24                 $post_data = &$_POST;
25
26         if ( $update )
27                 $post_data['ID'] = (int) $post_data['post_ID'];
28
29         if ( isset( $post_data['content'] ) )
30                 $post_data['post_content'] = $post_data['content'];
31
32         if ( isset( $post_data['excerpt'] ) )
33                 $post_data['post_excerpt'] = $post_data['excerpt'];
34
35         if ( isset( $post_data['parent_id'] ) )
36                 $post_data['post_parent'] = (int) $post_data['parent_id'];
37
38         if ( isset($post_data['trackback_url']) )
39                 $post_data['to_ping'] = $post_data['trackback_url'];
40
41         if ( !isset($post_data['user_ID']) )
42                 $post_data['user_ID'] = $GLOBALS['user_ID'];
43
44         if (!empty ( $post_data['post_author_override'] ) ) {
45                 $post_data['post_author'] = (int) $post_data['post_author_override'];
46         } else {
47                 if (!empty ( $post_data['post_author'] ) ) {
48                         $post_data['post_author'] = (int) $post_data['post_author'];
49                 } else {
50                         $post_data['post_author'] = (int) $post_data['user_ID'];
51                 }
52         }
53
54         $ptype = get_post_type_object( $post_data['post_type'] );
55         if ( isset($post_data['user_ID']) && ($post_data['post_author'] != $post_data['user_ID']) ) {
56                 if ( !current_user_can( $ptype->cap->edit_others_posts ) ) {
57                         if ( 'page' == $post_data['post_type'] ) {
58                                 return new WP_Error( 'edit_others_pages', $update ?
59                                         __( 'You are not allowed to edit pages as this user.' ) :
60                                         __( 'You are not allowed to create pages as this user.' )
61                                 );
62                         } else {
63                                 return new WP_Error( 'edit_others_posts', $update ?
64                                         __( 'You are not allowed to edit posts as this user.' ) :
65                                         __( 'You are not allowed to post as this user.' )
66                                 );
67                         }
68                 }
69         }
70
71         // What to do based on which button they pressed
72         if ( isset($post_data['saveasdraft']) && '' != $post_data['saveasdraft'] )
73                 $post_data['post_status'] = 'draft';
74         if ( isset($post_data['saveasprivate']) && '' != $post_data['saveasprivate'] )
75                 $post_data['post_status'] = 'private';
76         if ( isset($post_data['publish']) && ( '' != $post_data['publish'] ) && ( !isset($post_data['post_status']) || $post_data['post_status'] != 'private' ) )
77                 $post_data['post_status'] = 'publish';
78         if ( isset($post_data['advanced']) && '' != $post_data['advanced'] )
79                 $post_data['post_status'] = 'draft';
80         if ( isset($post_data['pending']) && '' != $post_data['pending'] )
81                 $post_data['post_status'] = 'pending';
82
83         if ( isset( $post_data['ID'] ) )
84                 $post_id = $post_data['ID'];
85         else
86                 $post_id = false;
87         $previous_status = $post_id ? get_post_field( 'post_status', $post_id ) : false;
88
89         // Posts 'submitted for approval' present are submitted to $_POST the same as if they were being published.
90         // Change status from 'publish' to 'pending' if user lacks permissions to publish or to resave published posts.
91         if ( isset($post_data['post_status']) && ('publish' == $post_data['post_status'] && !current_user_can( $ptype->cap->publish_posts )) )
92                 if ( $previous_status != 'publish' || !current_user_can( 'edit_post', $post_id ) )
93                         $post_data['post_status'] = 'pending';
94
95         if ( ! isset($post_data['post_status']) )
96                 $post_data['post_status'] = $previous_status;
97
98         if (!isset( $post_data['comment_status'] ))
99                 $post_data['comment_status'] = 'closed';
100
101         if (!isset( $post_data['ping_status'] ))
102                 $post_data['ping_status'] = 'closed';
103
104         foreach ( array('aa', 'mm', 'jj', 'hh', 'mn') as $timeunit ) {
105                 if ( !empty( $post_data['hidden_' . $timeunit] ) && $post_data['hidden_' . $timeunit] != $post_data[$timeunit] ) {
106                         $post_data['edit_date'] = '1';
107                         break;
108                 }
109         }
110
111         if ( !empty( $post_data['edit_date'] ) ) {
112                 $aa = $post_data['aa'];
113                 $mm = $post_data['mm'];
114                 $jj = $post_data['jj'];
115                 $hh = $post_data['hh'];
116                 $mn = $post_data['mn'];
117                 $ss = $post_data['ss'];
118                 $aa = ($aa <= 0 ) ? date('Y') : $aa;
119                 $mm = ($mm <= 0 ) ? date('n') : $mm;
120                 $jj = ($jj > 31 ) ? 31 : $jj;
121                 $jj = ($jj <= 0 ) ? date('j') : $jj;
122                 $hh = ($hh > 23 ) ? $hh -24 : $hh;
123                 $mn = ($mn > 59 ) ? $mn -60 : $mn;
124                 $ss = ($ss > 59 ) ? $ss -60 : $ss;
125                 $post_data['post_date'] = sprintf( "%04d-%02d-%02d %02d:%02d:%02d", $aa, $mm, $jj, $hh, $mn, $ss );
126                 $post_data['post_date_gmt'] = get_gmt_from_date( $post_data['post_date'] );
127         }
128
129         return $post_data;
130 }
131
132 /**
133  * Update an existing post with values provided in $_POST.
134  *
135  * @since 1.5.0
136  *
137  * @param array $post_data Optional.
138  * @return int Post ID.
139  */
140 function edit_post( $post_data = null ) {
141
142         if ( empty($post_data) )
143                 $post_data = &$_POST;
144
145         // Clear out any data in internal vars.
146         unset( $post_data['filter'] );
147
148         $post_ID = (int) $post_data['post_ID'];
149         $post = get_post( $post_ID );
150         $post_data['post_type'] = $post->post_type;
151         $post_data['post_mime_type'] = $post->post_mime_type;
152
153         $ptype = get_post_type_object($post_data['post_type']);
154         if ( !current_user_can( $ptype->cap->edit_post, $post_ID ) ) {
155                 if ( 'page' == $post_data['post_type'] )
156                         wp_die( __('You are not allowed to edit this page.' ));
157                 else
158                         wp_die( __('You are not allowed to edit this post.' ));
159         }
160
161         // Autosave shouldn't save too soon after a real save
162         if ( 'autosave' == $post_data['action'] ) {
163                 $post =& get_post( $post_ID );
164                 $now = time();
165                 $then = strtotime($post->post_date_gmt . ' +0000');
166                 $delta = AUTOSAVE_INTERVAL / 2;
167                 if ( ($now - $then) < $delta )
168                         return $post_ID;
169         }
170
171         $post_data = _wp_translate_postdata( true, $post_data );
172         if ( is_wp_error($post_data) )
173                 wp_die( $post_data->get_error_message() );
174         if ( 'autosave' != $post_data['action']  && 'auto-draft' == $post_data['post_status'] )
175                 $post_data['post_status'] = 'draft';
176
177         if ( isset($post_data['visibility']) ) {
178                 switch ( $post_data['visibility'] ) {
179                         case 'public' :
180                                 $post_data['post_password'] = '';
181                                 break;
182                         case 'password' :
183                                 unset( $post_data['sticky'] );
184                                 break;
185                         case 'private' :
186                                 $post_data['post_status'] = 'private';
187                                 $post_data['post_password'] = '';
188                                 unset( $post_data['sticky'] );
189                                 break;
190                 }
191         }
192
193         // Post Formats
194         if ( isset( $post_data['post_format'] ) ) {
195                 if ( current_theme_supports( 'post-formats', $post_data['post_format'] ) )
196                         set_post_format( $post_ID, $post_data['post_format'] );
197                 elseif ( '0' == $post_data['post_format'] )
198                         set_post_format( $post_ID, false );
199         }
200
201         // Meta Stuff
202         if ( isset($post_data['meta']) && $post_data['meta'] ) {
203                 foreach ( $post_data['meta'] as $key => $value ) {
204                         if ( !$meta = get_post_meta_by_id( $key ) )
205                                 continue;
206                         if ( $meta->post_id != $post_ID )
207                                 continue;
208                         if ( is_protected_meta( $value['key'], 'post' ) || ! current_user_can( 'edit_post_meta', $post_ID, $value['key'] ) )
209                                 continue;
210                         update_meta( $key, $value['key'], $value['value'] );
211                 }
212         }
213
214         if ( isset($post_data['deletemeta']) && $post_data['deletemeta'] ) {
215                 foreach ( $post_data['deletemeta'] as $key => $value ) {
216                         if ( !$meta = get_post_meta_by_id( $key ) )
217                                 continue;
218                         if ( $meta->post_id != $post_ID )
219                                 continue;
220                         if ( is_protected_meta( $meta->meta_key, 'post' ) || ! current_user_can( 'delete_post_meta', $post_ID, $meta->meta_key ) )
221                                 continue;
222                         delete_meta( $key );
223                 }
224         }
225
226         add_meta( $post_ID );
227
228         update_post_meta( $post_ID, '_edit_last', $GLOBALS['current_user']->ID );
229
230         wp_update_post( $post_data );
231
232         // Now that we have an ID we can fix any attachment anchor hrefs
233         _fix_attachment_links( $post_ID );
234
235         wp_set_post_lock( $post_ID );
236
237         if ( current_user_can( $ptype->cap->edit_others_posts ) ) {
238                 if ( ! empty( $post_data['sticky'] ) )
239                         stick_post( $post_ID );
240                 else
241                         unstick_post( $post_ID );
242         }
243
244         return $post_ID;
245 }
246
247 /**
248  * Process the post data for the bulk editing of posts.
249  *
250  * Updates all bulk edited posts/pages, adding (but not removing) tags and
251  * categories. Skips pages when they would be their own parent or child.
252  *
253  * @since 2.7.0
254  *
255  * @param array $post_data Optional, the array of post data to process if not provided will use $_POST superglobal.
256  * @return array
257  */
258 function bulk_edit_posts( $post_data = null ) {
259         global $wpdb;
260
261         if ( empty($post_data) )
262                 $post_data = &$_POST;
263
264         if ( isset($post_data['post_type']) )
265                 $ptype = get_post_type_object($post_data['post_type']);
266         else
267                 $ptype = get_post_type_object('post');
268
269         if ( !current_user_can( $ptype->cap->edit_posts ) ) {
270                 if ( 'page' == $ptype->name )
271                         wp_die( __('You are not allowed to edit pages.'));
272                 else
273                         wp_die( __('You are not allowed to edit posts.'));
274         }
275
276         if ( -1 == $post_data['_status'] ) {
277                 $post_data['post_status'] = null;
278                 unset($post_data['post_status']);
279         } else {
280                 $post_data['post_status'] = $post_data['_status'];
281         }
282         unset($post_data['_status']);
283
284         $post_IDs = array_map( 'intval', (array) $post_data['post'] );
285
286         $reset = array( 'post_author', 'post_status', 'post_password', 'post_parent', 'page_template', 'comment_status', 'ping_status', 'keep_private', 'tax_input', 'post_category', 'sticky' );
287         foreach ( $reset as $field ) {
288                 if ( isset($post_data[$field]) && ( '' == $post_data[$field] || -1 == $post_data[$field] ) )
289                         unset($post_data[$field]);
290         }
291
292         if ( isset($post_data['post_category']) ) {
293                 if ( is_array($post_data['post_category']) && ! empty($post_data['post_category']) )
294                         $new_cats = array_map( 'absint', $post_data['post_category'] );
295                 else
296                         unset($post_data['post_category']);
297         }
298
299         $tax_input = array();
300         if ( isset($post_data['tax_input'])) {
301                 foreach ( $post_data['tax_input'] as $tax_name => $terms ) {
302                         if ( empty($terms) )
303                                 continue;
304                         if ( is_taxonomy_hierarchical( $tax_name ) ) {
305                                 $tax_input[$tax_name] = array_map( 'absint', $terms );
306                         } else {
307                                 $tax_input[$tax_name] = preg_replace( '/\s*,\s*/', ',', rtrim( trim($terms), ' ,' ) );
308                                 $tax_input[$tax_name] = explode(',', $tax_input[$tax_name]);
309                         }
310                 }
311         }
312
313         if ( isset($post_data['post_parent']) && ($parent = (int) $post_data['post_parent']) ) {
314                 $pages = $wpdb->get_results("SELECT ID, post_parent FROM $wpdb->posts WHERE post_type = 'page'");
315                 $children = array();
316
317                 for ( $i = 0; $i < 50 && $parent > 0; $i++ ) {
318                         $children[] = $parent;
319
320                         foreach ( $pages as $page ) {
321                                 if ( $page->ID == $parent ) {
322                                         $parent = $page->post_parent;
323                                         break;
324                                 }
325                         }
326                 }
327         }
328
329         if ( isset( $post_data['post_format'] ) ) {
330                 if ( '0' == $post_data['post_format'] )
331                         $post_data['post_format'] = false;
332                 // don't change the post format if it's not supported or not '0' (standard)
333                 elseif ( ! current_theme_supports( 'post-formats', $post_data['post_format'] ) )
334                         unset( $post_data['post_format'] );
335         }
336
337         $updated = $skipped = $locked = array();
338         foreach ( $post_IDs as $post_ID ) {
339                 $post_type_object = get_post_type_object( get_post_type( $post_ID ) );
340
341                 if ( !isset( $post_type_object ) || ( isset($children) && in_array($post_ID, $children) ) || !current_user_can( $post_type_object->cap->edit_post, $post_ID ) ) {
342                         $skipped[] = $post_ID;
343                         continue;
344                 }
345
346                 if ( wp_check_post_lock( $post_ID ) ) {
347                         $locked[] = $post_ID;
348                         continue;
349                 }
350
351                 $post = get_post( $post_ID );
352                 $tax_names = get_object_taxonomies( $post );
353                 foreach ( $tax_names as $tax_name ) {
354                         $taxonomy_obj = get_taxonomy($tax_name);
355                         if (  isset( $tax_input[$tax_name]) && current_user_can( $taxonomy_obj->cap->assign_terms ) )
356                                 $new_terms = $tax_input[$tax_name];
357                         else
358                                 $new_terms = array();
359
360                         if ( $taxonomy_obj->hierarchical )
361                                 $current_terms = (array) wp_get_object_terms( $post_ID, $tax_name, array('fields' => 'ids') );
362                         else
363                                 $current_terms = (array) wp_get_object_terms( $post_ID, $tax_name, array('fields' => 'names') );
364
365                         $post_data['tax_input'][$tax_name] = array_merge( $current_terms, $new_terms );
366                 }
367
368                 if ( isset($new_cats) && in_array( 'category', $tax_names ) ) {
369                         $cats = (array) wp_get_post_categories($post_ID);
370                         $post_data['post_category'] = array_unique( array_merge($cats, $new_cats) );
371                         unset( $post_data['tax_input']['category'] );
372                 }
373
374                 $post_data['post_mime_type'] = $post->post_mime_type;
375                 $post_data['guid'] = $post->guid;
376
377                 $post_data['ID'] = $post_ID;
378                 $updated[] = wp_update_post( $post_data );
379
380                 if ( isset( $post_data['sticky'] ) && current_user_can( $ptype->cap->edit_others_posts ) ) {
381                         if ( 'sticky' == $post_data['sticky'] )
382                                 stick_post( $post_ID );
383                         else
384                                 unstick_post( $post_ID );
385                 }
386
387                 if ( isset( $post_data['post_format'] ) )
388                         set_post_format( $post_ID, $post_data['post_format'] );
389         }
390
391         return array( 'updated' => $updated, 'skipped' => $skipped, 'locked' => $locked );
392 }
393
394 /**
395  * Default post information to use when populating the "Write Post" form.
396  *
397  * @since 2.0.0
398  *
399  * @param string $post_type A post type string, defaults to 'post'.
400  * @return object stdClass object containing all the default post data as attributes
401  */
402 function get_default_post_to_edit( $post_type = 'post', $create_in_db = false ) {
403         global $wpdb;
404
405         $post_title = '';
406         if ( !empty( $_REQUEST['post_title'] ) )
407                 $post_title = esc_html( stripslashes( $_REQUEST['post_title'] ));
408
409         $post_content = '';
410         if ( !empty( $_REQUEST['content'] ) )
411                 $post_content = esc_html( stripslashes( $_REQUEST['content'] ));
412
413         $post_excerpt = '';
414         if ( !empty( $_REQUEST['excerpt'] ) )
415                 $post_excerpt = esc_html( stripslashes( $_REQUEST['excerpt'] ));
416
417         if ( $create_in_db ) {
418                 // Cleanup old auto-drafts more than 7 days old
419                 $old_posts = $wpdb->get_col( "SELECT ID FROM $wpdb->posts WHERE post_status = 'auto-draft' AND DATE_SUB( NOW(), INTERVAL 7 DAY ) > post_date" );
420                 foreach ( (array) $old_posts as $delete )
421                         wp_delete_post( $delete, true ); // Force delete
422                 $post_id = wp_insert_post( array( 'post_title' => __( 'Auto Draft' ), 'post_type' => $post_type, 'post_status' => 'auto-draft' ) );
423                 $post = get_post( $post_id );
424                 if ( current_theme_supports( 'post-formats' ) && post_type_supports( $post->post_type, 'post-formats' ) && get_option( 'default_post_format' ) )
425                         set_post_format( $post, get_option( 'default_post_format' ) );
426         } else {
427                 $post = new stdClass;
428                 $post->ID = 0;
429                 $post->post_author = '';
430                 $post->post_date = '';
431                 $post->post_date_gmt = '';
432                 $post->post_password = '';
433                 $post->post_type = $post_type;
434                 $post->post_status = 'draft';
435                 $post->to_ping = '';
436                 $post->pinged = '';
437                 $post->comment_status = get_option( 'default_comment_status' );
438                 $post->ping_status = get_option( 'default_ping_status' );
439                 $post->post_pingback = get_option( 'default_pingback_flag' );
440                 $post->post_category = get_option( 'default_category' );
441                 $post->page_template = 'default';
442                 $post->post_parent = 0;
443                 $post->menu_order = 0;
444         }
445
446         $post->post_content = apply_filters( 'default_content', $post_content, $post );
447         $post->post_title   = apply_filters( 'default_title',   $post_title, $post   );
448         $post->post_excerpt = apply_filters( 'default_excerpt', $post_excerpt, $post );
449         $post->post_name = '';
450
451         return $post;
452 }
453
454 /**
455  * Get the default page information to use.
456  *
457  * @since 2.5.0
458  *
459  * @return object stdClass object containing all the default post data as attributes
460  */
461 function get_default_page_to_edit() {
462         $page = get_default_post_to_edit();
463         $page->post_type = 'page';
464         return $page;
465 }
466
467 /**
468  * Get an existing post and format it for editing.
469  *
470  * @since 2.0.0
471  *
472  * @param unknown_type $id
473  * @return unknown
474  */
475 function get_post_to_edit( $id ) {
476
477         $post = get_post( $id, OBJECT, 'edit' );
478
479         if ( $post->post_type == 'page' )
480                 $post->page_template = get_post_meta( $id, '_wp_page_template', true );
481
482         return $post;
483 }
484
485 /**
486  * Determine if a post exists based on title, content, and date
487  *
488  * @since 2.0.0
489  *
490  * @param string $title Post title
491  * @param string $content Optional post content
492  * @param string $date Optional post date
493  * @return int Post ID if post exists, 0 otherwise.
494  */
495 function post_exists($title, $content = '', $date = '') {
496         global $wpdb;
497
498         $post_title = stripslashes( sanitize_post_field( 'post_title', $title, 0, 'db' ) );
499         $post_content = stripslashes( sanitize_post_field( 'post_content', $content, 0, 'db' ) );
500         $post_date = stripslashes( sanitize_post_field( 'post_date', $date, 0, 'db' ) );
501
502         $query = "SELECT ID FROM $wpdb->posts WHERE 1=1";
503         $args = array();
504
505         if ( !empty ( $date ) ) {
506                 $query .= ' AND post_date = %s';
507                 $args[] = $post_date;
508         }
509
510         if ( !empty ( $title ) ) {
511                 $query .= ' AND post_title = %s';
512                 $args[] = $post_title;
513         }
514
515         if ( !empty ( $content ) ) {
516                 $query .= 'AND post_content = %s';
517                 $args[] = $post_content;
518         }
519
520         if ( !empty ( $args ) )
521                 return $wpdb->get_var( $wpdb->prepare($query, $args) );
522
523         return 0;
524 }
525
526 /**
527  * Creates a new post from the "Write Post" form using $_POST information.
528  *
529  * @since 2.1.0
530  *
531  * @return unknown
532  */
533 function wp_write_post() {
534         global $user_ID;
535
536         if ( isset($_POST['post_type']) )
537                 $ptype = get_post_type_object($_POST['post_type']);
538         else
539                 $ptype = get_post_type_object('post');
540
541         if ( !current_user_can( $ptype->cap->edit_posts ) ) {
542                 if ( 'page' == $ptype->name )
543                         return new WP_Error( 'edit_pages', __( 'You are not allowed to create pages on this site.' ) );
544                 else
545                         return new WP_Error( 'edit_posts', __( 'You are not allowed to create posts or drafts on this site.' ) );
546         }
547
548         $_POST['post_mime_type'] = '';
549
550         // Clear out any data in internal vars.
551         unset( $_POST['filter'] );
552
553         // Edit don't write if we have a post id.
554         if ( isset( $_POST['post_ID'] ) )
555                 return edit_post();
556
557         $translated = _wp_translate_postdata( false );
558         if ( is_wp_error($translated) )
559                 return $translated;
560
561         if ( isset($_POST['visibility']) ) {
562                 switch ( $_POST['visibility'] ) {
563                         case 'public' :
564                                 $_POST['post_password'] = '';
565                                 break;
566                         case 'password' :
567                                 unset( $_POST['sticky'] );
568                                 break;
569                         case 'private' :
570                                 $_POST['post_status'] = 'private';
571                                 $_POST['post_password'] = '';
572                                 unset( $_POST['sticky'] );
573                                 break;
574                 }
575         }
576
577         // Create the post.
578         $post_ID = wp_insert_post( $_POST );
579         if ( is_wp_error( $post_ID ) )
580                 return $post_ID;
581
582         if ( empty($post_ID) )
583                 return 0;
584
585         add_meta( $post_ID );
586
587         add_post_meta( $post_ID, '_edit_last', $GLOBALS['current_user']->ID );
588
589         // Now that we have an ID we can fix any attachment anchor hrefs
590         _fix_attachment_links( $post_ID );
591
592         wp_set_post_lock( $post_ID );
593
594         return $post_ID;
595 }
596
597 /**
598  * Calls wp_write_post() and handles the errors.
599  *
600  * @since 2.0.0
601
602  * @uses wp_write_post()
603  * @uses is_wp_error()
604  * @uses wp_die()
605  * @return unknown
606  */
607 function write_post() {
608         $result = wp_write_post();
609         if ( is_wp_error( $result ) )
610                 wp_die( $result->get_error_message() );
611         else
612                 return $result;
613 }
614
615 //
616 // Post Meta
617 //
618
619 /**
620  * {@internal Missing Short Description}}
621  *
622  * @since 1.2.0
623  *
624  * @param unknown_type $post_ID
625  * @return unknown
626  */
627 function add_meta( $post_ID ) {
628         global $wpdb;
629         $post_ID = (int) $post_ID;
630
631         $metakeyselect = isset($_POST['metakeyselect']) ? stripslashes( trim( $_POST['metakeyselect'] ) ) : '';
632         $metakeyinput = isset($_POST['metakeyinput']) ? stripslashes( trim( $_POST['metakeyinput'] ) ) : '';
633         $metavalue = isset($_POST['metavalue']) ? $_POST['metavalue'] : '';
634         if ( is_string( $metavalue ) )
635                 $metavalue = trim( $metavalue );
636
637         if ( ('0' === $metavalue || ! empty ( $metavalue ) ) && ( ( ( '#NONE#' != $metakeyselect ) && !empty ( $metakeyselect) ) || !empty ( $metakeyinput ) ) ) {
638                 // We have a key/value pair. If both the select and the
639                 // input for the key have data, the input takes precedence:
640
641                 if ( '#NONE#' != $metakeyselect )
642                         $metakey = $metakeyselect;
643
644                 if ( $metakeyinput )
645                         $metakey = $metakeyinput; // default
646
647                 if ( is_protected_meta( $metakey, 'post' ) || ! current_user_can( 'add_post_meta', $post_ID, $metakey ) )
648                         return false;
649
650                 $metakey = esc_sql( $metakey );
651
652                 return add_post_meta( $post_ID, $metakey, $metavalue );
653         }
654
655         return false;
656 } // add_meta
657
658 /**
659  * {@internal Missing Short Description}}
660  *
661  * @since 1.2.0
662  *
663  * @param unknown_type $mid
664  * @return unknown
665  */
666 function delete_meta( $mid ) {
667         return delete_metadata_by_mid( 'post' , $mid );
668 }
669
670 /**
671  * Get a list of previously defined keys.
672  *
673  * @since 1.2.0
674  *
675  * @return unknown
676  */
677 function get_meta_keys() {
678         global $wpdb;
679
680         $keys = $wpdb->get_col( "
681                         SELECT meta_key
682                         FROM $wpdb->postmeta
683                         GROUP BY meta_key
684                         ORDER BY meta_key" );
685
686         return $keys;
687 }
688
689 /**
690  * {@internal Missing Short Description}}
691  *
692  * @since 2.1.0
693  *
694  * @param unknown_type $mid
695  * @return unknown
696  */
697 function get_post_meta_by_id( $mid ) {
698         return get_metadata_by_mid( 'post', $mid );
699 }
700
701 /**
702  * {@internal Missing Short Description}}
703  *
704  * Some postmeta stuff.
705  *
706  * @since 1.2.0
707  *
708  * @param unknown_type $postid
709  * @return unknown
710  */
711 function has_meta( $postid ) {
712         global $wpdb;
713
714         return $wpdb->get_results( $wpdb->prepare("SELECT meta_key, meta_value, meta_id, post_id
715                         FROM $wpdb->postmeta WHERE post_id = %d
716                         ORDER BY meta_key,meta_id", $postid), ARRAY_A );
717 }
718
719 /**
720  * {@internal Missing Short Description}}
721  *
722  * @since 1.2.0
723  *
724  * @param unknown_type $meta_id
725  * @param unknown_type $meta_key Expect Slashed
726  * @param unknown_type $meta_value Expect Slashed
727  * @return unknown
728  */
729 function update_meta( $meta_id, $meta_key, $meta_value ) {
730         $meta_key = stripslashes( $meta_key );
731         $meta_value = stripslashes_deep( $meta_value );
732
733         return update_metadata_by_mid( 'post', $meta_id, $meta_value, $meta_key );
734 }
735
736 //
737 // Private
738 //
739
740 /**
741  * Replace hrefs of attachment anchors with up-to-date permalinks.
742  *
743  * @since 2.3.0
744  * @access private
745  *
746  * @param unknown_type $post_ID
747  * @return unknown
748  */
749 function _fix_attachment_links( $post_ID ) {
750         global $_fix_attachment_link_id;
751
752         $post = & get_post( $post_ID, ARRAY_A );
753
754         $search = "#<a[^>]+rel=('|\")[^'\"]*attachment[^>]*>#ie";
755
756         // See if we have any rel="attachment" links
757         if ( 0 == preg_match_all( $search, $post['post_content'], $anchor_matches, PREG_PATTERN_ORDER ) )
758                 return;
759
760         $i = 0;
761         $search = "#[\s]+rel=(\"|')(.*?)wp-att-(\d+)\\1#i";
762         foreach ( $anchor_matches[0] as $anchor ) {
763                 if ( 0 == preg_match( $search, $anchor, $id_matches ) )
764                         continue;
765
766                 $id = (int) $id_matches[3];
767
768                 // While we have the attachment ID, let's adopt any orphans.
769                 $attachment = & get_post( $id, ARRAY_A );
770                 if ( ! empty( $attachment) && ! is_object( get_post( $attachment['post_parent'] ) ) ) {
771                         $attachment['post_parent'] = $post_ID;
772                         // Escape data pulled from DB.
773                         $attachment = add_magic_quotes( $attachment );
774                         wp_update_post( $attachment );
775                 }
776
777                 $post_search[$i] = $anchor;
778                  $_fix_attachment_link_id = $id;
779                 $post_replace[$i] = preg_replace_callback( "#href=(\"|')[^'\"]*\\1#", '_fix_attachment_links_replace_cb', $anchor );
780                 ++$i;
781         }
782
783         $post['post_content'] = str_replace( $post_search, $post_replace, $post['post_content'] );
784
785         // Escape data pulled from DB.
786         $post = add_magic_quotes( $post);
787
788         return wp_update_post( $post);
789 }
790
791 function _fix_attachment_links_replace_cb($match) {
792         global $_fix_attachment_link_id;
793         return stripslashes( 'href='.$match[1] ).get_attachment_link( $_fix_attachment_link_id ).stripslashes( $match[1] );
794 }
795
796 /**
797  * Move child posts to a new parent.
798  *
799  * @since 2.3.0
800  * @access private
801  *
802  * @param unknown_type $old_ID
803  * @param unknown_type $new_ID
804  * @return unknown
805  */
806 function _relocate_children( $old_ID, $new_ID ) {
807         global $wpdb;
808         $old_ID = (int) $old_ID;
809         $new_ID = (int) $new_ID;
810
811         $children = $wpdb->get_col( $wpdb->prepare("
812                 SELECT post_id
813                 FROM $wpdb->postmeta
814                 WHERE meta_key = '_wp_attachment_temp_parent'
815                 AND meta_value = %d", $old_ID) );
816
817         foreach ( $children as $child_id ) {
818                 $wpdb->update($wpdb->posts, array('post_parent' => $new_ID), array('ID' => $child_id) );
819                 delete_post_meta($child_id, '_wp_attachment_temp_parent');
820         }
821 }
822
823 /**
824  * Get all the possible statuses for a post_type
825  *
826  * @since 2.5.0
827  *
828  * @param string $type The post_type you want the statuses for
829  * @return array As array of all the statuses for the supplied post type
830  */
831 function get_available_post_statuses($type = 'post') {
832         $stati = wp_count_posts($type);
833
834         return array_keys(get_object_vars($stati));
835 }
836
837 /**
838  * Run the wp query to fetch the posts for listing on the edit posts page
839  *
840  * @since 2.5.0
841  *
842  * @param array|bool $q Array of query variables to use to build the query or false to use $_GET superglobal.
843  * @return array
844  */
845 function wp_edit_posts_query( $q = false ) {
846         if ( false === $q )
847                 $q = $_GET;
848         $q['m'] = isset($q['m']) ? (int) $q['m'] : 0;
849         $q['cat'] = isset($q['cat']) ? (int) $q['cat'] : 0;
850         $post_stati  = get_post_stati();
851
852         if ( isset($q['post_type']) && in_array( $q['post_type'], get_post_types() ) )
853                 $post_type = $q['post_type'];
854         else
855                 $post_type = 'post';
856
857         $avail_post_stati = get_available_post_statuses($post_type);
858
859         if ( isset($q['post_status']) && in_array( $q['post_status'], $post_stati ) ) {
860                 $post_status = $q['post_status'];
861                 $perm = 'readable';
862         }
863
864         if ( isset($q['orderby']) )
865                 $orderby = $q['orderby'];
866         elseif ( isset($q['post_status']) && in_array($q['post_status'], array('pending', 'draft')) )
867                 $orderby = 'modified';
868
869         if ( isset($q['order']) )
870                 $order = $q['order'];
871         elseif ( isset($q['post_status']) && 'pending' == $q['post_status'] )
872                 $order = 'ASC';
873
874         $per_page = 'edit_' . $post_type . '_per_page';
875         $posts_per_page = (int) get_user_option( $per_page );
876         if ( empty( $posts_per_page ) || $posts_per_page < 1 )
877                 $posts_per_page = 20;
878
879         $posts_per_page = apply_filters( $per_page, $posts_per_page );
880         $posts_per_page = apply_filters( 'edit_posts_per_page', $posts_per_page, $post_type );
881
882         $query = compact('post_type', 'post_status', 'perm', 'order', 'orderby', 'posts_per_page');
883
884         // Hierarchical types require special args.
885         if ( is_post_type_hierarchical( $post_type ) && !isset($orderby) ) {
886                 $query['orderby'] = 'menu_order title';
887                 $query['order'] = 'asc';
888                 $query['posts_per_page'] = -1;
889                 $query['posts_per_archive_page'] = -1;
890         }
891
892         if ( ! empty( $q['show_sticky'] ) )
893                 $query['post__in'] = (array) get_option( 'sticky_posts' );
894
895         wp( $query );
896
897         return $avail_post_stati;
898 }
899
900 /**
901  * Get default post mime types
902  *
903  * @since 2.9.0
904  *
905  * @return array
906  */
907 function get_post_mime_types() {
908         $post_mime_types = array(       //      array( adj, noun )
909                 'image' => array(__('Images'), __('Manage Images'), _n_noop('Image <span class="count">(%s)</span>', 'Images <span class="count">(%s)</span>')),
910                 'audio' => array(__('Audio'), __('Manage Audio'), _n_noop('Audio <span class="count">(%s)</span>', 'Audio <span class="count">(%s)</span>')),
911                 'video' => array(__('Video'), __('Manage Video'), _n_noop('Video <span class="count">(%s)</span>', 'Video <span class="count">(%s)</span>')),
912         );
913
914         return apply_filters('post_mime_types', $post_mime_types);
915 }
916
917 /**
918  * {@internal Missing Short Description}}
919  *
920  * @since 2.5.0
921  *
922  * @param unknown_type $type
923  * @return unknown
924  */
925 function get_available_post_mime_types($type = 'attachment') {
926         global $wpdb;
927
928         $types = $wpdb->get_col($wpdb->prepare("SELECT DISTINCT post_mime_type FROM $wpdb->posts WHERE post_type = %s", $type));
929         return $types;
930 }
931
932 /**
933  * {@internal Missing Short Description}}
934  *
935  * @since 2.5.0
936  *
937  * @param unknown_type $q
938  * @return unknown
939  */
940 function wp_edit_attachments_query( $q = false ) {
941         if ( false === $q )
942                 $q = $_GET;
943
944         $q['m']   = isset( $q['m'] ) ? (int) $q['m'] : 0;
945         $q['cat'] = isset( $q['cat'] ) ? (int) $q['cat'] : 0;
946         $q['post_type'] = 'attachment';
947         $post_type = get_post_type_object( 'attachment' );
948         $states = 'inherit';
949         if ( current_user_can( $post_type->cap->read_private_posts ) )
950                 $states .= ',private';
951
952         $q['post_status'] = isset( $q['status'] ) && 'trash' == $q['status'] ? 'trash' : $states;
953         $media_per_page = (int) get_user_option( 'upload_per_page' );
954         if ( empty( $media_per_page ) || $media_per_page < 1 )
955                 $media_per_page = 20;
956         $q['posts_per_page'] = apply_filters( 'upload_per_page', $media_per_page );
957
958         $post_mime_types = get_post_mime_types();
959         $avail_post_mime_types = get_available_post_mime_types('attachment');
960
961         if ( isset($q['post_mime_type']) && !array_intersect( (array) $q['post_mime_type'], array_keys($post_mime_types) ) )
962                 unset($q['post_mime_type']);
963
964         if ( isset($q['detached']) )
965                 add_filter('posts_where', '_edit_attachments_query_helper');
966
967         wp( $q );
968
969         if ( isset($q['detached']) )
970                 remove_filter('posts_where', '_edit_attachments_query_helper');
971
972         return array($post_mime_types, $avail_post_mime_types);
973 }
974
975 function _edit_attachments_query_helper($where) {
976         return $where .= ' AND post_parent < 1';
977 }
978
979 /**
980  * Returns the list of classes to be used by a metabox
981  *
982  * @uses get_user_option()
983  * @since 2.5.0
984  *
985  * @param unknown_type $id
986  * @param unknown_type $page
987  * @return unknown
988  */
989 function postbox_classes( $id, $page ) {
990         if ( isset( $_GET['edit'] ) && $_GET['edit'] == $id ) {
991                 $classes = array( '' );
992         } elseif ( $closed = get_user_option('closedpostboxes_'.$page ) ) {
993                 if ( !is_array( $closed ) ) {
994                         $classes = array( '' );
995                 } else {
996                         $classes = in_array( $id, $closed ) ? array( 'closed' ) : array( '' );
997                 }
998         } else {
999                 $classes = array( '' );
1000         }
1001
1002         $classes = apply_filters( "postbox_classes_{$page}_{$id}", $classes );
1003         return implode( ' ', $classes );
1004 }
1005
1006 /**
1007  * {@internal Missing Short Description}}
1008  *
1009  * @since 2.5.0
1010  *
1011  * @param int|object $id    Post ID or post object.
1012  * @param string $title (optional) Title
1013  * @param string $name (optional) Name
1014  * @return array With two entries of type string
1015  */
1016 function get_sample_permalink($id, $title = null, $name = null) {
1017         $post = &get_post($id);
1018         if ( !$post->ID )
1019                 return array('', '');
1020
1021         $ptype = get_post_type_object($post->post_type);
1022
1023         $original_status = $post->post_status;
1024         $original_date = $post->post_date;
1025         $original_name = $post->post_name;
1026
1027         // Hack: get_permalink would return ugly permalink for
1028         // drafts, so we will fake, that our post is published
1029         if ( in_array($post->post_status, array('draft', 'pending')) ) {
1030                 $post->post_status = 'publish';
1031                 $post->post_name = sanitize_title($post->post_name ? $post->post_name : $post->post_title, $post->ID);
1032         }
1033
1034         // If the user wants to set a new name -- override the current one
1035         // Note: if empty name is supplied -- use the title instead, see #6072
1036         if ( !is_null($name) )
1037                 $post->post_name = sanitize_title($name ? $name : $title, $post->ID);
1038
1039         $post->post_name = wp_unique_post_slug($post->post_name, $post->ID, $post->post_status, $post->post_type, $post->post_parent);
1040
1041         $post->filter = 'sample';
1042
1043         $permalink = get_permalink($post, true);
1044
1045         // Replace custom post_type Token with generic pagename token for ease of use.
1046         $permalink = str_replace("%$post->post_type%", '%pagename%', $permalink);
1047
1048         // Handle page hierarchy
1049         if ( $ptype->hierarchical ) {
1050                 $uri = get_page_uri($post);
1051                 $uri = untrailingslashit($uri);
1052                 $uri = strrev( stristr( strrev( $uri ), '/' ) );
1053                 $uri = untrailingslashit($uri);
1054                 $uri = apply_filters( 'editable_slug', $uri );
1055                 if ( !empty($uri) )
1056                         $uri .= '/';
1057                 $permalink = str_replace('%pagename%', "{$uri}%pagename%", $permalink);
1058         }
1059
1060         $permalink = array($permalink, apply_filters('editable_slug', $post->post_name));
1061         $post->post_status = $original_status;
1062         $post->post_date = $original_date;
1063         $post->post_name = $original_name;
1064         unset($post->filter);
1065
1066         return $permalink;
1067 }
1068
1069 /**
1070  * sample permalink html
1071  *
1072  * intended to be used for the inplace editor of the permalink post slug on in the post (and page?) editor.
1073  *
1074  * @since 2.5.0
1075  *
1076  * @param int|object $id Post ID or post object.
1077  * @param string $new_title (optional) New title
1078  * @param string $new_slug (optional) New slug
1079  * @return string intended to be used for the inplace editor of the permalink post slug on in the post (and page?) editor.
1080  */
1081 function get_sample_permalink_html( $id, $new_title = null, $new_slug = null ) {
1082         global $wpdb;
1083         $post = &get_post($id);
1084
1085         list($permalink, $post_name) = get_sample_permalink($post->ID, $new_title, $new_slug);
1086
1087         if ( 'publish' == $post->post_status ) {
1088                 $ptype = get_post_type_object($post->post_type);
1089                 $view_post = $ptype->labels->view_item;
1090                 $title = __('Click to edit this part of the permalink');
1091         } else {
1092                 $title = __('Temporary permalink. Click to edit this part.');
1093         }
1094
1095         if ( false === strpos($permalink, '%postname%') && false === strpos($permalink, '%pagename%') ) {
1096                 $return = '<strong>' . __('Permalink:') . "</strong>\n" . '<span id="sample-permalink">' . $permalink . "</span>\n";
1097                 if ( '' == get_option( 'permalink_structure' ) && current_user_can( 'manage_options' ) && !( 'page' == get_option('show_on_front') && $id == get_option('page_on_front') ) )
1098                         $return .= '<span id="change-permalinks"><a href="options-permalink.php" class="button" target="_blank">' . __('Change Permalinks') . "</a></span>\n";
1099                 if ( isset($view_post) )
1100                         $return .= "<span id='view-post-btn'><a href='$permalink' class='button' target='_blank'>$view_post</a></span>\n";
1101
1102                 $return = apply_filters('get_sample_permalink_html', $return, $id, $new_title, $new_slug);
1103
1104                 return $return;
1105         }
1106
1107         if ( function_exists('mb_strlen') ) {
1108                 if ( mb_strlen($post_name) > 30 ) {
1109                         $post_name_abridged = mb_substr($post_name, 0, 14). '&hellip;' . mb_substr($post_name, -14);
1110                 } else {
1111                         $post_name_abridged = $post_name;
1112                 }
1113         } else {
1114                 if ( strlen($post_name) > 30 ) {
1115                         $post_name_abridged = substr($post_name, 0, 14). '&hellip;' . substr($post_name, -14);
1116                 } else {
1117                         $post_name_abridged = $post_name;
1118                 }
1119         }
1120
1121         $post_name_html = '<span id="editable-post-name" title="' . $title . '">' . $post_name_abridged . '</span>';
1122         $display_link = str_replace(array('%pagename%','%postname%'), $post_name_html, $permalink);
1123         $view_link = str_replace(array('%pagename%','%postname%'), $post_name, $permalink);
1124         $return =  '<strong>' . __('Permalink:') . "</strong>\n";
1125         $return .= '<span id="sample-permalink">' . $display_link . "</span>\n";
1126         $return .= '&lrm;'; // Fix bi-directional text display defect in RTL languages.
1127         $return .= '<span id="edit-slug-buttons"><a href="#post_name" class="edit-slug button hide-if-no-js" onclick="editPermalink(' . $id . '); return false;">' . __('Edit') . "</a></span>\n";
1128         $return .= '<span id="editable-post-name-full">' . $post_name . "</span>\n";
1129         if ( isset($view_post) )
1130                 $return .= "<span id='view-post-btn'><a href='$view_link' class='button' target='_blank'>$view_post</a></span>\n";
1131
1132         $return = apply_filters('get_sample_permalink_html', $return, $id, $new_title, $new_slug);
1133
1134         return $return;
1135 }
1136
1137 /**
1138  * Output HTML for the post thumbnail meta-box.
1139  *
1140  * @since 2.9.0
1141  *
1142  * @param int $thumbnail_id ID of the attachment used for thumbnail
1143  * @return string html
1144  */
1145 function _wp_post_thumbnail_html( $thumbnail_id = NULL ) {
1146         global $content_width, $_wp_additional_image_sizes, $post_ID;
1147         $set_thumbnail_link = '<p class="hide-if-no-js"><a title="' . esc_attr__( 'Set featured image' ) . '" href="' . esc_url( get_upload_iframe_src('image') ) . '" id="set-post-thumbnail" class="thickbox">%s</a></p>';
1148         $content = sprintf($set_thumbnail_link, esc_html__( 'Set featured image' ));
1149
1150         if ( $thumbnail_id && get_post( $thumbnail_id ) ) {
1151                 $old_content_width = $content_width;
1152                 $content_width = 266;
1153                 if ( !isset( $_wp_additional_image_sizes['post-thumbnail'] ) )
1154                         $thumbnail_html = wp_get_attachment_image( $thumbnail_id, array( $content_width, $content_width ) );
1155                 else
1156                         $thumbnail_html = wp_get_attachment_image( $thumbnail_id, 'post-thumbnail' );
1157                 if ( !empty( $thumbnail_html ) ) {
1158                         $ajax_nonce = wp_create_nonce( "set_post_thumbnail-$post_ID" );
1159                         $content = sprintf($set_thumbnail_link, $thumbnail_html);
1160                         $content .= '<p class="hide-if-no-js"><a href="#" id="remove-post-thumbnail" onclick="WPRemoveThumbnail(\'' . $ajax_nonce . '\');return false;">' . esc_html__( 'Remove featured image' ) . '</a></p>';
1161                 }
1162                 $content_width = $old_content_width;
1163         }
1164
1165         return apply_filters( 'admin_post_thumbnail_html', $content );
1166 }
1167
1168 /**
1169  * Check to see if the post is currently being edited by another user.
1170  *
1171  * @since 2.5.0
1172  *
1173  * @param int $post_id ID of the post to check for editing
1174  * @return bool|int False: not locked or locked by current user. Int: user ID of user with lock.
1175  */
1176 function wp_check_post_lock( $post_id ) {
1177         if ( !$post = get_post( $post_id ) )
1178                 return false;
1179
1180         if ( !$lock = get_post_meta( $post->ID, '_edit_lock', true ) )
1181                 return false;
1182
1183         $lock = explode( ':', $lock );
1184         $time = $lock[0];
1185         $user = isset( $lock[1] ) ? $lock[1] : get_post_meta( $post->ID, '_edit_last', true );
1186
1187         $time_window = apply_filters( 'wp_check_post_lock_window', AUTOSAVE_INTERVAL * 2 );
1188
1189         if ( $time && $time > time() - $time_window && $user != get_current_user_id() )
1190                 return $user;
1191         return false;
1192 }
1193
1194 /**
1195  * Mark the post as currently being edited by the current user
1196  *
1197  * @since 2.5.0
1198  *
1199  * @param int $post_id ID of the post to being edited
1200  * @return bool|array Returns false if the post doesn't exist of there is no current user, or
1201  *      an array of the lock time and the user ID.
1202  */
1203 function wp_set_post_lock( $post_id ) {
1204         if ( !$post = get_post( $post_id ) )
1205                 return false;
1206         if ( 0 == ($user_id = get_current_user_id()) )
1207                 return false;
1208
1209         $now = time();
1210         $lock = "$now:$user_id";
1211
1212         update_post_meta( $post->ID, '_edit_lock', $lock );
1213         return array( $now, $user_id );
1214 }
1215
1216 /**
1217  * Outputs the notice message to say that someone else is editing this post at the moment.
1218  *
1219  * @since 2.8.5
1220  * @return none
1221  */
1222 function _admin_notice_post_locked() {
1223         global $post;
1224
1225         $lock = explode( ':', get_post_meta( $post->ID, '_edit_lock', true ) );
1226         $user = isset( $lock[1] ) ? $lock[1] : get_post_meta( $post->ID, '_edit_last', true );
1227         $last_user = get_userdata( $user );
1228         $last_user_name = $last_user ? $last_user->display_name : __('Somebody');
1229
1230         switch ($post->post_type) {
1231                 case 'post':
1232                         $message = __( 'Warning: %s is currently editing this post' );
1233                         break;
1234                 case 'page':
1235                         $message = __( 'Warning: %s is currently editing this page' );
1236                         break;
1237                 default:
1238                         $message = __( 'Warning: %s is currently editing this.' );
1239         }
1240
1241         $message = sprintf( $message, esc_html( $last_user_name ) );
1242         echo "<div class='error'><p>$message</p></div>";
1243 }
1244
1245 /**
1246  * Creates autosave data for the specified post from $_POST data.
1247  *
1248  * @package WordPress
1249  * @subpackage Post_Revisions
1250  * @since 2.6.0
1251  *
1252  * @uses _wp_translate_postdata()
1253  * @uses _wp_post_revision_fields()
1254  *
1255  * @return unknown
1256  */
1257 function wp_create_post_autosave( $post_id ) {
1258         $translated = _wp_translate_postdata( true );
1259         if ( is_wp_error( $translated ) )
1260                 return $translated;
1261
1262         // Only store one autosave.  If there is already an autosave, overwrite it.
1263         if ( $old_autosave = wp_get_post_autosave( $post_id ) ) {
1264                 $new_autosave = _wp_post_revision_fields( $_POST, true );
1265                 $new_autosave['ID'] = $old_autosave->ID;
1266                 $new_autosave['post_author'] = get_current_user_id();
1267                 return wp_update_post( $new_autosave );
1268         }
1269
1270         // _wp_put_post_revision() expects unescaped.
1271         $_POST = stripslashes_deep($_POST);
1272
1273         // Otherwise create the new autosave as a special post revision
1274         return _wp_put_post_revision( $_POST, true );
1275 }
1276
1277 /**
1278  * Save draft or manually autosave for showing preview.
1279  *
1280  * @package WordPress
1281  * @since 2.7.0
1282  *
1283  * @uses get_post_status()
1284  * @uses edit_post()
1285  * @uses get_post()
1286  * @uses current_user_can()
1287  * @uses wp_die()
1288  * @uses wp_create_post_autosave()
1289  * @uses add_query_arg()
1290  * @uses wp_create_nonce()
1291  *
1292  * @return str URL to redirect to show the preview
1293  */
1294 function post_preview() {
1295
1296         $post_ID = (int) $_POST['post_ID'];
1297         $status = get_post_status( $post_ID );
1298         if ( 'auto-draft' == $status )
1299                 wp_die( __('Preview not available. Please save as a draft first.') );
1300
1301         if ( isset($_POST['catslist']) )
1302                 $_POST['post_category'] = explode(",", $_POST['catslist']);
1303
1304         if ( isset($_POST['tags_input']) )
1305                 $_POST['tags_input'] = explode(",", $_POST['tags_input']);
1306
1307         if ( $_POST['post_type'] == 'page' || empty($_POST['post_category']) )
1308                 unset($_POST['post_category']);
1309
1310         $_POST['ID'] = $post_ID;
1311         $post = get_post($post_ID);
1312
1313         if ( 'page' == $post->post_type ) {
1314                 if ( !current_user_can('edit_page', $post_ID) )
1315                         wp_die(__('You are not allowed to edit this page.'));
1316         } else {
1317                 if ( !current_user_can('edit_post', $post_ID) )
1318                         wp_die(__('You are not allowed to edit this post.'));
1319         }
1320
1321         if ( 'draft' == $post->post_status ) {
1322                 $id = edit_post();
1323         } else { // Non drafts are not overwritten.  The autosave is stored in a special post revision.
1324                 $id = wp_create_post_autosave( $post->ID );
1325                 if ( ! is_wp_error($id) )
1326                         $id = $post->ID;
1327         }
1328
1329         if ( is_wp_error($id) )
1330                 wp_die( $id->get_error_message() );
1331
1332         if ( $_POST['post_status'] == 'draft'  ) {
1333                 $url = add_query_arg( 'preview', 'true', get_permalink($id) );
1334         } else {
1335                 $nonce = wp_create_nonce('post_preview_' . $id);
1336                 $url = add_query_arg( array( 'preview' => 'true', 'preview_id' => $id, 'preview_nonce' => $nonce ), get_permalink($id) );
1337         }
1338
1339         return $url;
1340 }