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