]> scripts.mit.edu Git - autoinstalls/wordpress.git/blob - wp-admin/includes/post.php
12cbaa0f92c21f0688fd6c67422f6735bf910551
[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         $post_data['post_content'] = isset($post_data['content']) ? $post_data['content'] : '';
29         $post_data['post_excerpt'] = isset($post_data['excerpt']) ? $post_data['excerpt'] : '';
30         $post_data['post_parent'] = isset($post_data['parent_id'])? $post_data['parent_id'] : '';
31         if ( isset($post_data['trackback_url']) )
32                 $post_data['to_ping'] = $post_data['trackback_url'];
33
34         if ( !isset($post_data['user_ID']) )
35                 $post_data['user_ID'] = $GLOBALS['user_ID'];
36
37         if (!empty ( $post_data['post_author_override'] ) ) {
38                 $post_data['post_author'] = (int) $post_data['post_author_override'];
39         } else {
40                 if (!empty ( $post_data['post_author'] ) ) {
41                         $post_data['post_author'] = (int) $post_data['post_author'];
42                 } else {
43                         $post_data['post_author'] = (int) $post_data['user_ID'];
44                 }
45         }
46
47         $ptype = get_post_type_object( $post_data['post_type'] );
48         if ( isset($post_data['user_ID']) && ($post_data['post_author'] != $post_data['user_ID']) ) {
49                 if ( !current_user_can( $ptype->cap->edit_others_posts ) ) {
50                         if ( 'page' == $post_data['post_type'] ) {
51                                 return new WP_Error( 'edit_others_pages', $update ?
52                                         __( 'You are not allowed to edit pages as this user.' ) :
53                                         __( 'You are not allowed to create pages as this user.' )
54                                 );
55                         } else {
56                                 return new WP_Error( 'edit_others_posts', $update ?
57                                         __( 'You are not allowed to edit posts as this user.' ) :
58                                         __( 'You are not allowed to post as this user.' )
59                                 );
60                         }
61                 }
62         }
63
64         // What to do based on which button they pressed
65         if ( isset($post_data['saveasdraft']) && '' != $post_data['saveasdraft'] )
66                 $post_data['post_status'] = 'draft';
67         if ( isset($post_data['saveasprivate']) && '' != $post_data['saveasprivate'] )
68                 $post_data['post_status'] = 'private';
69         if ( isset($post_data['publish']) && ( '' != $post_data['publish'] ) && ( !isset($post_data['post_status']) || $post_data['post_status'] != 'private' ) )
70                 $post_data['post_status'] = 'publish';
71         if ( isset($post_data['advanced']) && '' != $post_data['advanced'] )
72                 $post_data['post_status'] = 'draft';
73         if ( isset($post_data['pending']) && '' != $post_data['pending'] )
74                 $post_data['post_status'] = 'pending';
75
76         if ( isset( $post_data['ID'] ) )
77                 $post_id = $post_data['ID'];
78         else
79                 $post_id = false;
80         $previous_status = $post_id ? get_post_field( 'post_status', $post_id ) : false;
81
82         // Posts 'submitted for approval' present are submitted to $_POST the same as if they were being published.
83         // Change status from 'publish' to 'pending' if user lacks permissions to publish or to resave published posts.
84         if ( isset($post_data['post_status']) && ('publish' == $post_data['post_status'] && !current_user_can( $ptype->cap->publish_posts )) )
85                 if ( $previous_status != 'publish' || !current_user_can( 'edit_post', $post_id ) )
86                         $post_data['post_status'] = 'pending';
87
88         if ( ! isset($post_data['post_status']) )
89                 $post_data['post_status'] = $previous_status;
90
91         if (!isset( $post_data['comment_status'] ))
92                 $post_data['comment_status'] = 'closed';
93
94         if (!isset( $post_data['ping_status'] ))
95                 $post_data['ping_status'] = 'closed';
96
97         foreach ( array('aa', 'mm', 'jj', 'hh', 'mn') as $timeunit ) {
98                 if ( !empty( $post_data['hidden_' . $timeunit] ) && $post_data['hidden_' . $timeunit] != $post_data[$timeunit] ) {
99                         $post_data['edit_date'] = '1';
100                         break;
101                 }
102         }
103
104         if ( !empty( $post_data['edit_date'] ) ) {
105                 $aa = $post_data['aa'];
106                 $mm = $post_data['mm'];
107                 $jj = $post_data['jj'];
108                 $hh = $post_data['hh'];
109                 $mn = $post_data['mn'];
110                 $ss = $post_data['ss'];
111                 $aa = ($aa <= 0 ) ? date('Y') : $aa;
112                 $mm = ($mm <= 0 ) ? date('n') : $mm;
113                 $jj = ($jj > 31 ) ? 31 : $jj;
114                 $jj = ($jj <= 0 ) ? date('j') : $jj;
115                 $hh = ($hh > 23 ) ? $hh -24 : $hh;
116                 $mn = ($mn > 59 ) ? $mn -60 : $mn;
117                 $ss = ($ss > 59 ) ? $ss -60 : $ss;
118                 $post_data['post_date'] = sprintf( "%04d-%02d-%02d %02d:%02d:%02d", $aa, $mm, $jj, $hh, $mn, $ss );
119                 $post_data['post_date_gmt'] = get_gmt_from_date( $post_data['post_date'] );
120         }
121
122         return $post_data;
123 }
124
125 /**
126  * Update an existing post with values provided in $_POST.
127  *
128  * @since 1.5.0
129  *
130  * @param array $post_data Optional.
131  * @return int Post ID.
132  */
133 function edit_post( $post_data = null ) {
134
135         if ( empty($post_data) )
136                 $post_data = &$_POST;
137
138         $post_ID = (int) $post_data['post_ID'];
139         $post = get_post( $post_ID );
140         $post_data['post_type'] = $post->post_type;
141
142         $ptype = get_post_type_object($post_data['post_type']);
143         if ( !current_user_can( $ptype->cap->edit_post, $post_ID ) ) {
144                 if ( 'page' == $post_data['post_type'] )
145                         wp_die( __('You are not allowed to edit this page.' ));
146                 else
147                         wp_die( __('You are not allowed to edit this post.' ));
148         }
149
150         // Autosave shouldn't save too soon after a real save
151         if ( 'autosave' == $post_data['action'] ) {
152                 $post =& get_post( $post_ID );
153                 $now = time();
154                 $then = strtotime($post->post_date_gmt . ' +0000');
155                 $delta = AUTOSAVE_INTERVAL / 2;
156                 if ( ($now - $then) < $delta )
157                         return $post_ID;
158         }
159
160         $post_data = _wp_translate_postdata( true, $post_data );
161         if ( is_wp_error($post_data) )
162                 wp_die( $post_data->get_error_message() );
163         if ( 'autosave' != $post_data['action']  && 'auto-draft' == $post_data['post_status'] )
164                 $post_data['post_status'] = 'draft';
165
166         if ( isset($post_data['visibility']) ) {
167                 switch ( $post_data['visibility'] ) {
168                         case 'public' :
169                                 $post_data['post_password'] = '';
170                                 break;
171                         case 'password' :
172                                 unset( $post_data['sticky'] );
173                                 break;
174                         case 'private' :
175                                 $post_data['post_status'] = 'private';
176                                 $post_data['post_password'] = '';
177                                 unset( $post_data['sticky'] );
178                                 break;
179                 }
180         }
181
182         // Post Formats
183         if ( current_theme_supports( 'post-formats' ) && isset( $post_data['post_format'] ) ) {
184                 $formats = get_theme_support( 'post-formats' );
185                 if ( is_array( $formats ) ) {
186                         $formats = $formats[0];
187                         if ( in_array( $post_data['post_format'], $formats ) ) {
188                                 set_post_format( $post_ID, $post_data['post_format'] );
189                         } elseif ( '0' == $post_data['post_format'] ) {
190                                 set_post_format( $post_ID, false );
191                         }
192                 }
193         }
194
195         // Meta Stuff
196         if ( isset($post_data['meta']) && $post_data['meta'] ) {
197                 foreach ( $post_data['meta'] as $key => $value ) {
198                         if ( !$meta = get_post_meta_by_id( $key ) )
199                                 continue;
200                         if ( $meta->post_id != $post_ID )
201                                 continue;
202                         update_meta( $key, $value['key'], $value['value'] );
203                 }
204         }
205
206         if ( isset($post_data['deletemeta']) && $post_data['deletemeta'] ) {
207                 foreach ( $post_data['deletemeta'] as $key => $value ) {
208                         if ( !$meta = get_post_meta_by_id( $key ) )
209                                 continue;
210                         if ( $meta->post_id != $post_ID )
211                                 continue;
212                         delete_meta( $key );
213                 }
214         }
215
216         add_meta( $post_ID );
217
218         update_post_meta( $post_ID, '_edit_last', $GLOBALS['current_user']->ID );
219
220         wp_update_post( $post_data );
221
222         // Reunite any orphaned attachments with their parent
223         if ( !$draft_ids = get_user_option( 'autosave_draft_ids' ) )
224                 $draft_ids = array();
225         if ( $draft_temp_id = (int) array_search( $post_ID, $draft_ids ) )
226                 _relocate_children( $draft_temp_id, $post_ID );
227
228         // Now that we have an ID we can fix any attachment anchor hrefs
229         _fix_attachment_links( $post_ID );
230
231         wp_set_post_lock( $post_ID, $GLOBALS['current_user']->ID );
232
233         if ( current_user_can( $ptype->cap->edit_others_posts ) ) {
234                 if ( ! empty( $post_data['sticky'] ) )
235                         stick_post( $post_ID );
236                 else
237                         unstick_post( $post_ID );
238         }
239
240         return $post_ID;
241 }
242
243 /**
244  * Process the post data for the bulk editing of posts.
245  *
246  * Updates all bulk edited posts/pages, adding (but not removing) tags and
247  * categories. Skips pages when they would be their own parent or child.
248  *
249  * @since 2.7.0
250  *
251  * @param array $post_data Optional, the array of post data to process if not provided will use $_POST superglobal.
252  * @return array
253  */
254 function bulk_edit_posts( $post_data = null ) {
255         global $wpdb;
256
257         if ( empty($post_data) )
258                 $post_data = &$_POST;
259
260         if ( isset($post_data['post_type']) )
261                 $ptype = get_post_type_object($post_data['post_type']);
262         else
263                 $ptype = get_post_type_object('post');
264
265         if ( !current_user_can( $ptype->cap->edit_posts ) ) {
266                 if ( 'page' == $ptype->name )
267                         wp_die( __('You are not allowed to edit pages.'));
268                 else
269                         wp_die( __('You are not allowed to edit posts.'));
270         }
271
272         if ( -1 == $post_data['_status'] ) {
273                 $post_data['post_status'] = null;
274                 unset($post_data['post_status']);
275         } else {
276                 $post_data['post_status'] = $post_data['_status'];
277         }
278         unset($post_data['_status']);
279
280         $post_IDs = array_map( 'intval', (array) $post_data['post'] );
281
282         $reset = array( 'post_author', 'post_status', 'post_password', 'post_parent', 'page_template', 'comment_status', 'ping_status', 'keep_private', 'tax_input', 'post_category', 'sticky' );
283         foreach ( $reset as $field ) {
284                 if ( isset($post_data[$field]) && ( '' == $post_data[$field] || -1 == $post_data[$field] ) )
285                         unset($post_data[$field]);
286         }
287
288         if ( isset($post_data['post_category']) ) {
289                 if ( is_array($post_data['post_category']) && ! empty($post_data['post_category']) )
290                         $new_cats = array_map( 'absint', $post_data['post_category'] );
291                 else
292                         unset($post_data['post_category']);
293         }
294
295         $tax_input = array();
296         if ( isset($post_data['tax_input'])) {
297                 foreach ( $post_data['tax_input'] as $tax_name => $terms ) {
298                         if ( empty($terms) )
299                                 continue;
300                         if ( is_taxonomy_hierarchical( $tax_name ) )
301                                 $tax_input[$tax_name] = array_map( 'absint', $terms );
302                         else {
303                                 $tax_input[$tax_name] = preg_replace( '/\s*,\s*/', ',', rtrim( trim($terms), ' ,' ) );
304                                 $tax_input[$tax_name] = explode(',', $tax_input[$tax_name]);
305                         }
306                 }
307         }
308
309         if ( isset($post_data['post_parent']) && ($parent = (int) $post_data['post_parent']) ) {
310                 $pages = $wpdb->get_results("SELECT ID, post_parent FROM $wpdb->posts WHERE post_type = 'page'");
311                 $children = array();
312
313                 for ( $i = 0; $i < 50 && $parent > 0; $i++ ) {
314                         $children[] = $parent;
315
316                         foreach ( $pages as $page ) {
317                                 if ( $page->ID == $parent ) {
318                                         $parent = $page->post_parent;
319                                         break;
320                                 }
321                         }
322                 }
323         }
324
325         $updated = $skipped = $locked = array();
326         foreach ( $post_IDs as $post_ID ) {
327                 $post_type_object = get_post_type_object( get_post_type( $post_ID ) );
328
329                 if ( !isset( $post_type_object ) || ( isset($children) && in_array($post_ID, $children) ) || !current_user_can( $post_type_object->cap->edit_post, $post_ID ) ) {
330                         $skipped[] = $post_ID;
331                         continue;
332                 }
333
334                 if ( wp_check_post_lock( $post_ID ) ) {
335                         $locked[] = $post_ID;
336                         continue;
337                 }
338
339                 $tax_names = get_object_taxonomies( get_post($post_ID) );
340                 foreach ( $tax_names as $tax_name ) {
341                         $taxonomy_obj = get_taxonomy($tax_name);
342                         if (  isset( $tax_input[$tax_name]) && current_user_can( $taxonomy_obj->cap->assign_terms ) )
343                                 $new_terms = $tax_input[$tax_name];
344                         else
345                                 $new_terms = array();
346
347                         if ( $taxonomy_obj->hierarchical )
348                                 $current_terms = (array) wp_get_object_terms( $post_ID, $tax_name, array('fields' => 'ids') );
349                         else
350                                 $current_terms = (array) wp_get_object_terms( $post_ID, $tax_name, array('fields' => 'names') );
351
352                         $post_data['tax_input'][$tax_name] = array_merge( $current_terms, $new_terms );
353                 }
354
355                 if ( isset($new_cats) && in_array( 'category', $tax_names ) ) {
356                         $cats = (array) wp_get_post_categories($post_ID);
357                         $post_data['post_category'] = array_unique( array_merge($cats, $new_cats) );
358                         unset( $post_data['tax_input']['category'] );
359                 }
360
361                 $post_data['ID'] = $post_ID;
362                 $updated[] = wp_update_post( $post_data );
363
364                 if ( isset( $post_data['sticky'] ) && current_user_can( $ptype->cap->edit_others_posts ) ) {
365                         if ( 'sticky' == $post_data['sticky'] )
366                                 stick_post( $post_ID );
367                         else
368                                 unstick_post( $post_ID );
369                 }
370
371         }
372
373         return array( 'updated' => $updated, 'skipped' => $skipped, 'locked' => $locked );
374 }
375
376 /**
377  * Default post information to use when populating the "Write Post" form.
378  *
379  * @since 2.0.0
380  *
381  * @param string $post_type A post type string, defaults to 'post'.
382  * @return object stdClass object containing all the default post data as attributes
383  */
384 function get_default_post_to_edit( $post_type = 'post', $create_in_db = false ) {
385         global $wpdb;
386
387         $post_title = '';
388         if ( !empty( $_REQUEST['post_title'] ) )
389                 $post_title = esc_html( stripslashes( $_REQUEST['post_title'] ));
390
391         $post_content = '';
392         if ( !empty( $_REQUEST['content'] ) )
393                 $post_content = esc_html( stripslashes( $_REQUEST['content'] ));
394
395         $post_excerpt = '';
396         if ( !empty( $_REQUEST['excerpt'] ) )
397                 $post_excerpt = esc_html( stripslashes( $_REQUEST['excerpt'] ));
398
399         if ( $create_in_db ) {
400                 // Cleanup old auto-drafts more than 7 days old
401                 $old_posts = $wpdb->get_col( "SELECT ID FROM $wpdb->posts WHERE post_status = 'auto-draft' AND DATE_SUB( NOW(), INTERVAL 7 DAY ) > post_date" );
402                 foreach ( (array) $old_posts as $delete )
403                         wp_delete_post( $delete, true ); // Force delete
404                 $post_id = wp_insert_post( array( 'post_title' => __( 'Auto Draft' ), 'post_type' => $post_type, 'post_status' => 'auto-draft' ) );
405                 $post = get_post( $post_id );
406                 if ( current_theme_supports( 'post-formats' ) && post_type_supports( $post->post_type, 'post-formats' ) && get_option( 'default_post_format' ) )
407                         set_post_format( $post, get_option( 'default_post_format' ) );
408         } else {
409                 $post->ID = 0;
410                 $post->post_author = '';
411                 $post->post_date = '';
412                 $post->post_date_gmt = '';
413                 $post->post_password = '';
414                 $post->post_type = $post_type;
415                 $post->post_status = 'draft';
416                 $post->to_ping = '';
417                 $post->pinged = '';
418                 $post->comment_status = get_option( 'default_comment_status' );
419                 $post->ping_status = get_option( 'default_ping_status' );
420                 $post->post_pingback = get_option( 'default_pingback_flag' );
421                 $post->post_category = get_option( 'default_category' );
422                 $post->page_template = 'default';
423                 $post->post_parent = 0;
424                 $post->menu_order = 0;
425         }
426
427         $post->post_content = apply_filters( 'default_content', $post_content, $post );
428         $post->post_title   = apply_filters( 'default_title',   $post_title, $post   );
429         $post->post_excerpt = apply_filters( 'default_excerpt', $post_excerpt, $post );
430         $post->post_name = '';
431
432         return $post;
433 }
434
435 /**
436  * Get the default page information to use.
437  *
438  * @since 2.5.0
439  *
440  * @return object stdClass object containing all the default post data as attributes
441  */
442 function get_default_page_to_edit() {
443         $page = get_default_post_to_edit();
444         $page->post_type = 'page';
445         return $page;
446 }
447
448 /**
449  * Get an existing post and format it for editing.
450  *
451  * @since 2.0.0
452  *
453  * @param unknown_type $id
454  * @return unknown
455  */
456 function get_post_to_edit( $id ) {
457
458         $post = get_post( $id, OBJECT, 'edit' );
459
460         if ( $post->post_type == 'page' )
461                 $post->page_template = get_post_meta( $id, '_wp_page_template', true );
462
463         return $post;
464 }
465
466 /**
467  * Determine if a post exists based on title, content, and date
468  *
469  * @since 2.0.0
470  *
471  * @param string $title Post title
472  * @param string $content Optional post content
473  * @param string $date Optional post date
474  * @return int Post ID if post exists, 0 otherwise.
475  */
476 function post_exists($title, $content = '', $date = '') {
477         global $wpdb;
478
479         $post_title = stripslashes( sanitize_post_field( 'post_title', $title, 0, 'db' ) );
480         $post_content = stripslashes( sanitize_post_field( 'post_content', $content, 0, 'db' ) );
481         $post_date = stripslashes( sanitize_post_field( 'post_date', $date, 0, 'db' ) );
482
483         $query = "SELECT ID FROM $wpdb->posts WHERE 1=1";
484         $args = array();
485
486         if ( !empty ( $date ) ) {
487                 $query .= ' AND post_date = %s';
488                 $args[] = $post_date;
489         }
490
491         if ( !empty ( $title ) ) {
492                 $query .= ' AND post_title = %s';
493                 $args[] = $post_title;
494         }
495
496         if ( !empty ( $content ) ) {
497                 $query .= 'AND post_content = %s';
498                 $args[] = $post_content;
499         }
500
501         if ( !empty ( $args ) )
502                 return $wpdb->get_var( $wpdb->prepare($query, $args) );
503
504         return 0;
505 }
506
507 /**
508  * Creates a new post from the "Write Post" form using $_POST information.
509  *
510  * @since 2.1.0
511  *
512  * @return unknown
513  */
514 function wp_write_post() {
515         global $user_ID;
516
517
518         if ( isset($_POST['post_type']) )
519                 $ptype = get_post_type_object($_POST['post_type']);
520         else
521                 $ptype = get_post_type_object('post');
522
523         if ( !current_user_can( $ptype->cap->edit_posts ) ) {
524                 if ( 'page' == $ptype->name )
525                         return new WP_Error( 'edit_pages', __( 'You are not allowed to create pages on this site.' ) );
526                 else
527                         return new WP_Error( 'edit_posts', __( 'You are not allowed to create posts or drafts on this site.' ) );
528         }
529
530         // Check for autosave collisions
531         // Does this need to be updated? ~ Mark
532         $temp_id = false;
533         if ( isset($_POST['temp_ID']) ) {
534                 $temp_id = (int) $_POST['temp_ID'];
535                 if ( !$draft_ids = get_user_option( 'autosave_draft_ids' ) )
536                         $draft_ids = array();
537                 foreach ( $draft_ids as $temp => $real )
538                         if ( time() + $temp > 86400 ) // 1 day: $temp is equal to -1 * time( then )
539                                 unset($draft_ids[$temp]);
540
541                 if ( isset($draft_ids[$temp_id]) ) { // Edit, don't write
542                         $_POST['post_ID'] = $draft_ids[$temp_id];
543                         unset($_POST['temp_ID']);
544                         update_user_option( $user_ID, 'autosave_draft_ids', $draft_ids );
545                         return edit_post();
546                 }
547         }
548
549         $translated = _wp_translate_postdata( false );
550         if ( is_wp_error($translated) )
551                 return $translated;
552
553         if ( isset($_POST['visibility']) ) {
554                 switch ( $_POST['visibility'] ) {
555                         case 'public' :
556                                 $_POST['post_password'] = '';
557                                 break;
558                         case 'password' :
559                                 unset( $_POST['sticky'] );
560                                 break;
561                         case 'private' :
562                                 $_POST['post_status'] = 'private';
563                                 $_POST['post_password'] = '';
564                                 unset( $_POST['sticky'] );
565                                 break;
566                 }
567         }
568
569         // Create the post.
570         $post_ID = wp_insert_post( $_POST );
571         if ( is_wp_error( $post_ID ) )
572                 return $post_ID;
573
574         if ( empty($post_ID) )
575                 return 0;
576
577         add_meta( $post_ID );
578
579         add_post_meta( $post_ID, '_edit_last', $GLOBALS['current_user']->ID );
580
581         // Reunite any orphaned attachments with their parent
582         // Does this need to be udpated? ~ Mark
583         if ( !$draft_ids = get_user_option( 'autosave_draft_ids' ) )
584                 $draft_ids = array();
585         if ( $draft_temp_id = (int) array_search( $post_ID, $draft_ids ) )
586                 _relocate_children( $draft_temp_id, $post_ID );
587         if ( $temp_id && $temp_id != $draft_temp_id )
588                 _relocate_children( $temp_id, $post_ID );
589
590         // Update autosave collision detection
591         if ( $temp_id ) {
592                 $draft_ids[$temp_id] = $post_ID;
593                 update_user_option( $user_ID, 'autosave_draft_ids', $draft_ids );
594         }
595
596         // Now that we have an ID we can fix any attachment anchor hrefs
597         _fix_attachment_links( $post_ID );
598
599         wp_set_post_lock( $post_ID, $GLOBALS['current_user']->ID );
600
601         return $post_ID;
602 }
603
604 /**
605  * Calls wp_write_post() and handles the errors.
606  *
607  * @since 2.0.0
608  *
609  * @return unknown
610  */
611 function write_post() {
612         $result = wp_write_post();
613         if ( is_wp_error( $result ) )
614                 wp_die( $result->get_error_message() );
615         else
616                 return $result;
617 }
618
619 //
620 // Post Meta
621 //
622
623 /**
624  * {@internal Missing Short Description}}
625  *
626  * @since 1.2.0
627  *
628  * @param unknown_type $post_ID
629  * @return unknown
630  */
631 function add_meta( $post_ID ) {
632         global $wpdb;
633         $post_ID = (int) $post_ID;
634
635         $protected = array( '_wp_attached_file', '_wp_attachment_metadata', '_wp_old_slug', '_wp_page_template' );
636
637         $metakeyselect = isset($_POST['metakeyselect']) ? stripslashes( trim( $_POST['metakeyselect'] ) ) : '';
638         $metakeyinput = isset($_POST['metakeyinput']) ? stripslashes( trim( $_POST['metakeyinput'] ) ) : '';
639         $metavalue = isset($_POST['metavalue']) ? maybe_serialize( stripslashes_deep( $_POST['metavalue'] ) ) : '';
640         if ( is_string($metavalue) )
641                 $metavalue = trim( $metavalue );
642
643         if ( ('0' === $metavalue || !empty ( $metavalue ) ) && ((('#NONE#' != $metakeyselect) && !empty ( $metakeyselect) ) || !empty ( $metakeyinput) ) ) {
644                 // We have a key/value pair. If both the select and the
645                 // input for the key have data, the input takes precedence:
646
647                 if ('#NONE#' != $metakeyselect)
648                         $metakey = $metakeyselect;
649
650                 if ( $metakeyinput)
651                         $metakey = $metakeyinput; // default
652
653                 if ( in_array($metakey, $protected) )
654                         return false;
655
656                 wp_cache_delete($post_ID, 'post_meta');
657                 $wpdb->insert( $wpdb->postmeta, array( 'post_id' => $post_ID, 'meta_key' => $metakey, 'meta_value' => $metavalue ) );
658                 $meta_id = $wpdb->insert_id;
659                 do_action( 'added_postmeta', $meta_id, $post_ID, $metakey, $metavalue );
660
661                 return $meta_id;
662         }
663         return false;
664 } // add_meta
665
666 /**
667  * {@internal Missing Short Description}}
668  *
669  * @since 1.2.0
670  *
671  * @param unknown_type $mid
672  * @return unknown
673  */
674 function delete_meta( $mid ) {
675         global $wpdb;
676         $mid = (int) $mid;
677
678         $post_id = $wpdb->get_var( $wpdb->prepare("SELECT post_id FROM $wpdb->postmeta WHERE meta_id = %d", $mid) );
679
680         do_action( 'delete_postmeta', $mid );
681         wp_cache_delete($post_id, 'post_meta');
682         $rval = $wpdb->query( $wpdb->prepare("DELETE FROM $wpdb->postmeta WHERE meta_id = %d", $mid) );
683         do_action( 'deleted_postmeta', $mid );
684
685         return $rval;
686 }
687
688 /**
689  * Get a list of previously defined keys.
690  *
691  * @since 1.2.0
692  *
693  * @return unknown
694  */
695 function get_meta_keys() {
696         global $wpdb;
697
698         $keys = $wpdb->get_col( "
699                         SELECT meta_key
700                         FROM $wpdb->postmeta
701                         GROUP BY meta_key
702                         ORDER BY meta_key" );
703
704         return $keys;
705 }
706
707 /**
708  * {@internal Missing Short Description}}
709  *
710  * @since 2.1.0
711  *
712  * @param unknown_type $mid
713  * @return unknown
714  */
715 function get_post_meta_by_id( $mid ) {
716         global $wpdb;
717         $mid = (int) $mid;
718
719         $meta = $wpdb->get_row( $wpdb->prepare("SELECT * FROM $wpdb->postmeta WHERE meta_id = %d", $mid) );
720         if ( empty($meta) )
721                 return false;
722         if ( is_serialized_string( $meta->meta_value ) )
723                 $meta->meta_value = maybe_unserialize( $meta->meta_value );
724         return $meta;
725 }
726
727 /**
728  * {@internal Missing Short Description}}
729  *
730  * Some postmeta stuff.
731  *
732  * @since 1.2.0
733  *
734  * @param unknown_type $postid
735  * @return unknown
736  */
737 function has_meta( $postid ) {
738         global $wpdb;
739
740         return $wpdb->get_results( $wpdb->prepare("SELECT meta_key, meta_value, meta_id, post_id
741                         FROM $wpdb->postmeta WHERE post_id = %d
742                         ORDER BY meta_key,meta_id", $postid), ARRAY_A );
743
744 }
745
746 /**
747  * {@internal Missing Short Description}}
748  *
749  * @since 1.2.0
750  *
751  * @param unknown_type $meta_id
752  * @param unknown_type $meta_key Expect Slashed
753  * @param unknown_type $meta_value Expect Slashed
754  * @return unknown
755  */
756 function update_meta( $meta_id, $meta_key, $meta_value ) {
757         global $wpdb;
758
759         $protected = array( '_wp_attached_file', '_wp_attachment_metadata', '_wp_old_slug', '_wp_page_template' );
760
761         $meta_key = stripslashes($meta_key);
762
763         if ( in_array($meta_key, $protected) )
764                 return false;
765
766         if ( '' === trim( $meta_value ) )
767                 return false;
768
769         $post_id = $wpdb->get_var( $wpdb->prepare("SELECT post_id FROM $wpdb->postmeta WHERE meta_id = %d", $meta_id) );
770
771         $meta_value = maybe_serialize( stripslashes_deep( $meta_value ) );
772         $meta_id = (int) $meta_id;
773
774         $data  = compact( 'meta_key', 'meta_value' );
775         $where = compact( 'meta_id' );
776
777         do_action( 'update_postmeta', $meta_id, $post_id, $meta_key, $meta_value );
778         $rval = $wpdb->update( $wpdb->postmeta, $data, $where );
779         wp_cache_delete($post_id, 'post_meta');
780         do_action( 'updated_postmeta', $meta_id, $post_id, $meta_key, $meta_value );
781
782         return $rval;
783 }
784
785 //
786 // Private
787 //
788
789 /**
790  * Replace hrefs of attachment anchors with up-to-date permalinks.
791  *
792  * @since 2.3.0
793  * @access private
794  *
795  * @param unknown_type $post_ID
796  * @return unknown
797  */
798 function _fix_attachment_links( $post_ID ) {
799         global $_fix_attachment_link_id;
800
801         $post = & get_post( $post_ID, ARRAY_A );
802
803         $search = "#<a[^>]+rel=('|\")[^'\"]*attachment[^>]*>#ie";
804
805         // See if we have any rel="attachment" links
806         if ( 0 == preg_match_all( $search, $post['post_content'], $anchor_matches, PREG_PATTERN_ORDER ) )
807                 return;
808
809         $i = 0;
810         $search = "#[\s]+rel=(\"|')(.*?)wp-att-(\d+)\\1#i";
811         foreach ( $anchor_matches[0] as $anchor ) {
812                 if ( 0 == preg_match( $search, $anchor, $id_matches ) )
813                         continue;
814
815                 $id = (int) $id_matches[3];
816
817                 // While we have the attachment ID, let's adopt any orphans.
818                 $attachment = & get_post( $id, ARRAY_A );
819                 if ( ! empty( $attachment) && ! is_object( get_post( $attachment['post_parent'] ) ) ) {
820                         $attachment['post_parent'] = $post_ID;
821                         // Escape data pulled from DB.
822                         $attachment = add_magic_quotes( $attachment );
823                         wp_update_post( $attachment );
824                 }
825
826                 $post_search[$i] = $anchor;
827                  $_fix_attachment_link_id = $id;
828                 $post_replace[$i] = preg_replace_callback( "#href=(\"|')[^'\"]*\\1#", '_fix_attachment_links_replace_cb', $anchor );
829                 ++$i;
830         }
831
832         $post['post_content'] = str_replace( $post_search, $post_replace, $post['post_content'] );
833
834         // Escape data pulled from DB.
835         $post = add_magic_quotes( $post);
836
837         return wp_update_post( $post);
838 }
839
840 function _fix_attachment_links_replace_cb($match) {
841         global $_fix_attachment_link_id;
842         return stripslashes( 'href='.$match[1] ).get_attachment_link( $_fix_attachment_link_id ).stripslashes( $match[1] );
843 }
844
845 /**
846  * Move child posts to a new parent.
847  *
848  * @since 2.3.0
849  * @access private
850  *
851  * @param unknown_type $old_ID
852  * @param unknown_type $new_ID
853  * @return unknown
854  */
855 function _relocate_children( $old_ID, $new_ID ) {
856         global $wpdb;
857         $old_ID = (int) $old_ID;
858         $new_ID = (int) $new_ID;
859
860         $children = $wpdb->get_col( $wpdb->prepare("
861                 SELECT post_id
862                 FROM $wpdb->postmeta
863                 WHERE meta_key = '_wp_attachment_temp_parent'
864                 AND meta_value = %d", $old_ID) );
865
866         foreach ( $children as $child_id ) {
867                 $wpdb->update($wpdb->posts, array('post_parent' => $new_ID), array('ID' => $child_id) );
868                 delete_post_meta($child_id, '_wp_attachment_temp_parent');
869         }
870 }
871
872 /**
873  * Get all the possible statuses for a post_type
874  *
875  * @since 2.5.0
876  *
877  * @param string $type The post_type you want the statuses for
878  * @return array As array of all the statuses for the supplied post type
879  */
880 function get_available_post_statuses($type = 'post') {
881         $stati = wp_count_posts($type);
882
883         return array_keys(get_object_vars($stati));
884 }
885
886 /**
887  * Run the wp query to fetch the posts for listing on the edit posts page
888  *
889  * @since 2.5.0
890  *
891  * @param array|bool $q Array of query variables to use to build the query or false to use $_GET superglobal.
892  * @return array
893  */
894 function wp_edit_posts_query( $q = false ) {
895         if ( false === $q )
896                 $q = $_GET;
897         $q['m'] = isset($q['m']) ? (int) $q['m'] : 0;
898         $q['cat'] = isset($q['cat']) ? (int) $q['cat'] : 0;
899         $post_stati  = get_post_stati();
900
901         if ( isset($q['post_type']) && in_array( $q['post_type'], get_post_types() ) )
902                 $post_type = $q['post_type'];
903         else
904                 $post_type = 'post';
905
906         $avail_post_stati = get_available_post_statuses($post_type);
907
908         if ( isset($q['post_status']) && in_array( $q['post_status'], $post_stati ) ) {
909                 $post_status = $q['post_status'];
910                 $perm = 'readable';
911         }
912
913         if ( isset($q['orderby']) )
914                 $orderby = $q['orderby'];
915         elseif ( isset($q['post_status']) && in_array($q['post_status'], array('pending', 'draft')) )
916                 $orderby = 'modified';
917
918         if ( isset($q['order']) )
919                 $order = $q['order'];
920         elseif ( isset($q['post_status']) && 'pending' == $q['post_status'] )
921                 $order = 'ASC';
922
923         $per_page = 'edit_' . $post_type . '_per_page';
924         $posts_per_page = (int) get_user_option( $per_page );
925         if ( empty( $posts_per_page ) || $posts_per_page < 1 )
926                 $posts_per_page = 20;
927
928         $posts_per_page = apply_filters( $per_page, $posts_per_page );
929         $posts_per_page = apply_filters( 'edit_posts_per_page', $posts_per_page, $post_type );
930
931         $query = compact('post_type', 'post_status', 'perm', 'order', 'orderby', 'posts_per_page');
932
933         // Hierarchical types require special args.
934         if ( is_post_type_hierarchical( $post_type ) && !isset($orderby) ) {
935                 $query['orderby'] = 'menu_order title';
936                 $query['order'] = 'asc';
937                 $query['posts_per_page'] = -1;
938                 $query['posts_per_archive_page'] = -1;
939         }
940
941         if ( ! empty( $q['show_sticky'] ) )
942                 $query['post__in'] = (array) get_option( 'sticky_posts' );
943
944         wp( $query );
945
946         return $avail_post_stati;
947 }
948
949 /**
950  * Get default post mime types
951  *
952  * @since 2.9.0
953  *
954  * @return array
955  */
956 function get_post_mime_types() {
957         $post_mime_types = array(       //      array( adj, noun )
958                 'image' => array(__('Images'), __('Manage Images'), _n_noop('Image <span class="count">(%s)</span>', 'Images <span class="count">(%s)</span>')),
959                 'audio' => array(__('Audio'), __('Manage Audio'), _n_noop('Audio <span class="count">(%s)</span>', 'Audio <span class="count">(%s)</span>')),
960                 'video' => array(__('Video'), __('Manage Video'), _n_noop('Video <span class="count">(%s)</span>', 'Video <span class="count">(%s)</span>')),
961         );
962
963         return apply_filters('post_mime_types', $post_mime_types);
964 }
965
966 /**
967  * {@internal Missing Short Description}}
968  *
969  * @since 2.5.0
970  *
971  * @param unknown_type $type
972  * @return unknown
973  */
974 function get_available_post_mime_types($type = 'attachment') {
975         global $wpdb;
976
977         $types = $wpdb->get_col($wpdb->prepare("SELECT DISTINCT post_mime_type FROM $wpdb->posts WHERE post_type = %s", $type));
978         return $types;
979 }
980
981 /**
982  * {@internal Missing Short Description}}
983  *
984  * @since 2.5.0
985  *
986  * @param unknown_type $q
987  * @return unknown
988  */
989 function wp_edit_attachments_query( $q = false ) {
990         if ( false === $q )
991                 $q = $_GET;
992
993         $q['m']   = isset( $q['m'] ) ? (int) $q['m'] : 0;
994         $q['cat'] = isset( $q['cat'] ) ? (int) $q['cat'] : 0;
995         $q['post_type'] = 'attachment';
996         $q['post_status'] = isset( $q['status'] ) && 'trash' == $q['status'] ? 'trash' : 'inherit';
997         $media_per_page = (int) get_user_option( 'upload_per_page' );
998         if ( empty( $media_per_page ) || $media_per_page < 1 )
999                 $media_per_page = 20;
1000         $q['posts_per_page'] = apply_filters( 'upload_per_page', $media_per_page );
1001
1002         $post_mime_types = get_post_mime_types();
1003         $avail_post_mime_types = get_available_post_mime_types('attachment');
1004
1005         if ( isset($q['post_mime_type']) && !array_intersect( (array) $q['post_mime_type'], array_keys($post_mime_types) ) )
1006                 unset($q['post_mime_type']);
1007
1008         if ( isset($q['detached']) )
1009                 add_filter('posts_where', '_edit_attachments_query_helper');
1010
1011         wp( $q );
1012
1013         if ( isset($q['detached']) )
1014                 remove_filter('posts_where', '_edit_attachments_query_helper');
1015
1016         return array($post_mime_types, $avail_post_mime_types);
1017 }
1018
1019 function _edit_attachments_query_helper($where) {
1020         return $where .= ' AND post_parent < 1';
1021 }
1022
1023 /**
1024  * {@internal Missing Short Description}}
1025  *
1026  * @uses get_user_option()
1027  * @since 2.5.0
1028  *
1029  * @param unknown_type $id
1030  * @param unknown_type $page
1031  * @return unknown
1032  */
1033 function postbox_classes( $id, $page ) {
1034         if ( isset( $_GET['edit'] ) && $_GET['edit'] == $id )
1035                 return '';
1036
1037         if ( $closed = get_user_option('closedpostboxes_'.$page ) ) {
1038                 if ( !is_array( $closed ) ) {
1039                         return '';
1040                 }
1041                 return in_array( $id, $closed )? 'closed' : '';
1042         } else {
1043                 return '';
1044         }
1045 }
1046
1047 /**
1048  * {@internal Missing Short Description}}
1049  *
1050  * @since 2.5.0
1051  *
1052  * @param int|object $id    Post ID or post object.
1053  * @param string $title (optional) Title
1054  * @param string $name (optional) Name
1055  * @return array With two entries of type string
1056  */
1057 function get_sample_permalink($id, $title = null, $name = null) {
1058         $post = &get_post($id);
1059         if ( !$post->ID )
1060                 return array('', '');
1061
1062         $ptype = get_post_type_object($post->post_type);
1063
1064         $original_status = $post->post_status;
1065         $original_date = $post->post_date;
1066         $original_name = $post->post_name;
1067
1068         // Hack: get_permalink would return ugly permalink for
1069         // drafts, so we will fake, that our post is published
1070         if ( in_array($post->post_status, array('draft', 'pending')) ) {
1071                 $post->post_status = 'publish';
1072                 $post->post_name = sanitize_title($post->post_name ? $post->post_name : $post->post_title, $post->ID);
1073         }
1074
1075         // If the user wants to set a new name -- override the current one
1076         // Note: if empty name is supplied -- use the title instead, see #6072
1077         if ( !is_null($name) )
1078                 $post->post_name = sanitize_title($name ? $name : $title, $post->ID);
1079
1080         $post->post_name = wp_unique_post_slug($post->post_name, $post->ID, $post->post_status, $post->post_type, $post->post_parent);
1081
1082         $post->filter = 'sample';
1083
1084         $permalink = get_permalink($post, true);
1085
1086         // Replace custom post_type Token with generic pagename token for ease of use.
1087         $permalink = str_replace("%$post->post_type%", '%pagename%', $permalink);
1088
1089         // Handle page hierarchy
1090         if ( $ptype->hierarchical ) {
1091                 $uri = get_page_uri($post);
1092                 $uri = untrailingslashit($uri);
1093                 $uri = strrev( stristr( strrev( $uri ), '/' ) );
1094                 $uri = untrailingslashit($uri);
1095                 if ( !empty($uri) )
1096                         $uri .= '/';
1097                 $permalink = str_replace('%pagename%', "{$uri}%pagename%", $permalink);
1098         }
1099
1100         $permalink = array($permalink, apply_filters('editable_slug', $post->post_name));
1101         $post->post_status = $original_status;
1102         $post->post_date = $original_date;
1103         $post->post_name = $original_name;
1104         unset($post->filter);
1105
1106         return $permalink;
1107 }
1108
1109 /**
1110  * sample permalink html
1111  *
1112  * intended to be used for the inplace editor of the permalink post slug on in the post (and page?) editor.
1113  *
1114  * @since 2.5.0
1115  *
1116  * @param int|object $id Post ID or post object.
1117  * @param string $new_title (optional) New title
1118  * @param string $new_slug (optional) New slug
1119  * @return string intended to be used for the inplace editor of the permalink post slug on in the post (and page?) editor.
1120  */
1121 function get_sample_permalink_html( $id, $new_title = null, $new_slug = null ) {
1122         global $wpdb;
1123         $post = &get_post($id);
1124
1125         list($permalink, $post_name) = get_sample_permalink($post->ID, $new_title, $new_slug);
1126
1127         if ( 'publish' == $post->post_status ) {
1128                 $ptype = get_post_type_object($post->post_type);
1129                 $view_post = $ptype->labels->view_item;
1130                 $title = __('Click to edit this part of the permalink');
1131         } else {
1132                 $title = __('Temporary permalink. Click to edit this part.');
1133         }
1134
1135         if ( false === strpos($permalink, '%postname%') && false === strpos($permalink, '%pagename%') ) {
1136                 $return = '<strong>' . __('Permalink:') . "</strong>\n" . '<span id="sample-permalink">' . $permalink . "</span>\n";
1137                 if ( '' == get_option( 'permalink_structure' ) && current_user_can( 'manage_options' ) && !( 'page' == get_option('show_on_front') && $id == get_option('page_on_front') ) )
1138                         $return .= '<span id="change-permalinks"><a href="options-permalink.php" class="button" target="_blank">' . __('Change Permalinks') . "</a></span>\n";
1139                 if ( isset($view_post) )
1140                         $return .= "<span id='view-post-btn'><a href='$permalink' class='button' target='_blank'>$view_post</a></span>\n";
1141
1142                 $return = apply_filters('get_sample_permalink_html', $return, $id, $new_title, $new_slug);
1143
1144                 return $return;
1145         }
1146
1147         if ( function_exists('mb_strlen') ) {
1148                 if ( mb_strlen($post_name) > 30 ) {
1149                         $post_name_abridged = mb_substr($post_name, 0, 14). '&hellip;' . mb_substr($post_name, -14);
1150                 } else {
1151                         $post_name_abridged = $post_name;
1152                 }
1153         } else {
1154                 if ( strlen($post_name) > 30 ) {
1155                         $post_name_abridged = substr($post_name, 0, 14). '&hellip;' . substr($post_name, -14);
1156                 } else {
1157                         $post_name_abridged = $post_name;
1158                 }
1159         }
1160
1161         $post_name_html = '<span id="editable-post-name" title="' . $title . '">' . $post_name_abridged . '</span>';
1162         $display_link = str_replace(array('%pagename%','%postname%'), $post_name_html, $permalink);
1163         $view_link = str_replace(array('%pagename%','%postname%'), $post_name, $permalink);
1164         $return =  '<strong>' . __('Permalink:') . "</strong>\n";
1165         $return .= '<span id="sample-permalink">' . $display_link . "</span>\n";
1166         $return .= '&lrm;'; // Fix bi-directional text display defect in RTL languages.
1167         $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";
1168         $return .= '<span id="editable-post-name-full">' . $post_name . "</span>\n";
1169         if ( isset($view_post) )
1170                 $return .= "<span id='view-post-btn'><a href='$view_link' class='button' target='_blank'>$view_post</a></span>\n";
1171
1172         $return = apply_filters('get_sample_permalink_html', $return, $id, $new_title, $new_slug);
1173
1174         return $return;
1175 }
1176
1177 /**
1178  * Output HTML for the post thumbnail meta-box.
1179  *
1180  * @since 2.9.0
1181  *
1182  * @param int $thumbnail_id ID of the attachment used for thumbnail
1183  * @return string html
1184  */
1185 function _wp_post_thumbnail_html( $thumbnail_id = NULL ) {
1186         global $content_width, $_wp_additional_image_sizes, $post_ID;
1187         $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>';
1188         $content = sprintf($set_thumbnail_link, esc_html__( 'Set featured image' ));
1189
1190         if ( $thumbnail_id && get_post( $thumbnail_id ) ) {
1191                 $old_content_width = $content_width;
1192                 $content_width = 266;
1193                 if ( !isset( $_wp_additional_image_sizes['post-thumbnail'] ) )
1194                         $thumbnail_html = wp_get_attachment_image( $thumbnail_id, array( $content_width, $content_width ) );
1195                 else
1196                         $thumbnail_html = wp_get_attachment_image( $thumbnail_id, 'post-thumbnail' );
1197                 if ( !empty( $thumbnail_html ) ) {
1198                         $ajax_nonce = wp_create_nonce( "set_post_thumbnail-$post_ID" );
1199                         $content = sprintf($set_thumbnail_link, $thumbnail_html);
1200                         $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>';
1201                 }
1202                 $content_width = $old_content_width;
1203         }
1204
1205         return apply_filters( 'admin_post_thumbnail_html', $content );
1206 }
1207
1208 /**
1209  * Check to see if the post is currently being edited by another user.
1210  *
1211  * @since 2.5.0
1212  *
1213  * @param int $post_id ID of the post to check for editing
1214  * @return bool|int False: not locked or locked by current user. Int: user ID of user with lock.
1215  */
1216 function wp_check_post_lock( $post_id ) {
1217         if ( !$post = get_post( $post_id ) )
1218                 return false;
1219
1220         if ( !$lock = get_post_meta( $post->ID, '_edit_lock', true ) )
1221                 return false;
1222
1223         $lock = explode( ':', $lock );
1224         $time = $lock[0];
1225         $user = isset( $lock[1] ) ? $lock[1] : get_post_meta( $post->ID, '_edit_last', true );
1226
1227         $time_window = apply_filters( 'wp_check_post_lock_window', AUTOSAVE_INTERVAL * 2 );
1228
1229         if ( $time && $time > time() - $time_window && $user != get_current_user_id() )
1230                 return $user;
1231         return false;
1232 }
1233
1234 /**
1235  * Mark the post as currently being edited by the current user
1236  *
1237  * @since 2.5.0
1238  *
1239  * @param int $post_id ID of the post to being edited
1240  * @return bool Returns false if the post doesn't exist of there is no current user
1241  */
1242 function wp_set_post_lock( $post_id ) {
1243         if ( !$post = get_post( $post_id ) )
1244                 return false;
1245         if ( 0 == ($user_id = get_current_user_id()) )
1246                 return false;
1247
1248         $now = time();
1249         $lock = "$now:$user_id";
1250
1251         update_post_meta( $post->ID, '_edit_lock', $lock );
1252 }
1253
1254 /**
1255  * Outputs the notice message to say that someone else is editing this post at the moment.
1256  *
1257  * @since 2.8.5
1258  * @return none
1259  */
1260 function _admin_notice_post_locked() {
1261         global $post;
1262
1263         $lock = explode( ':', get_post_meta( $post->ID, '_edit_lock', true ) );
1264         $user = isset( $lock[1] ) ? $lock[1] : get_post_meta( $post->ID, '_edit_last', true );
1265         $last_user = get_userdata( $user );
1266         $last_user_name = $last_user ? $last_user->display_name : __('Somebody');
1267
1268         switch ($post->post_type) {
1269                 case 'post':
1270                         $message = __( 'Warning: %s is currently editing this post' );
1271                         break;
1272                 case 'page':
1273                         $message = __( 'Warning: %s is currently editing this page' );
1274                         break;
1275                 default:
1276                         $message = __( 'Warning: %s is currently editing this.' );
1277         }
1278
1279         $message = sprintf( $message, esc_html( $last_user_name ) );
1280         echo "<div class='error'><p>$message</p></div>";
1281 }
1282
1283 /**
1284  * Creates autosave data for the specified post from $_POST data.
1285  *
1286  * @package WordPress
1287  * @subpackage Post_Revisions
1288  * @since 2.6.0
1289  *
1290  * @uses _wp_translate_postdata()
1291  * @uses _wp_post_revision_fields()
1292  *
1293  * @return unknown
1294  */
1295 function wp_create_post_autosave( $post_id ) {
1296         $translated = _wp_translate_postdata( true );
1297         if ( is_wp_error( $translated ) )
1298                 return $translated;
1299
1300         // Only store one autosave.  If there is already an autosave, overwrite it.
1301         if ( $old_autosave = wp_get_post_autosave( $post_id ) ) {
1302                 $new_autosave = _wp_post_revision_fields( $_POST, true );
1303                 $new_autosave['ID'] = $old_autosave->ID;
1304                 $new_autosave['post_author'] = get_current_user_id();
1305                 return wp_update_post( $new_autosave );
1306         }
1307
1308         // _wp_put_post_revision() expects unescaped.
1309         $_POST = stripslashes_deep($_POST);
1310
1311         // Otherwise create the new autosave as a special post revision
1312         return _wp_put_post_revision( $_POST, true );
1313 }
1314
1315 /**
1316  * Save draft or manually autosave for showing preview.
1317  *
1318  * @package WordPress
1319  * @since 2.7.0
1320  *
1321  * @uses wp_write_post()
1322  * @uses edit_post()
1323  * @uses get_post()
1324  * @uses current_user_can()
1325  * @uses wp_create_post_autosave()
1326  *
1327  * @return str URL to redirect to show the preview
1328  */
1329 function post_preview() {
1330
1331         $post_ID = (int) $_POST['post_ID'];
1332         $status = get_post_status( $post_ID );
1333         if ( 'auto-draft' == $status )
1334                 wp_die( __('Preview not available. Please save as a draft first.') );
1335
1336         if ( isset($_POST['catslist']) )
1337                 $_POST['post_category'] = explode(",", $_POST['catslist']);
1338
1339         if ( isset($_POST['tags_input']) )
1340                 $_POST['tags_input'] = explode(",", $_POST['tags_input']);
1341
1342         if ( $_POST['post_type'] == 'page' || empty($_POST['post_category']) )
1343                 unset($_POST['post_category']);
1344
1345         $_POST['ID'] = $post_ID;
1346         $post = get_post($post_ID);
1347
1348         if ( 'page' == $post->post_type ) {
1349                 if ( !current_user_can('edit_page', $post_ID) )
1350                         wp_die(__('You are not allowed to edit this page.'));
1351         } else {
1352                 if ( !current_user_can('edit_post', $post_ID) )
1353                         wp_die(__('You are not allowed to edit this post.'));
1354         }
1355
1356         if ( 'draft' == $post->post_status ) {
1357                 $id = edit_post();
1358         } else { // Non drafts are not overwritten.  The autosave is stored in a special post revision.
1359                 $id = wp_create_post_autosave( $post->ID );
1360                 if ( ! is_wp_error($id) )
1361                         $id = $post->ID;
1362         }
1363
1364         if ( is_wp_error($id) )
1365                 wp_die( $id->get_error_message() );
1366
1367         if ( $_POST['post_status'] == 'draft'  ) {
1368                 $url = add_query_arg( 'preview', 'true', get_permalink($id) );
1369         } else {
1370                 $nonce = wp_create_nonce('post_preview_' . $id);
1371                 $url = add_query_arg( array( 'preview' => 'true', 'preview_id' => $id, 'preview_nonce' => $nonce ), get_permalink($id) );
1372         }
1373
1374         return $url;
1375 }
1376
1377 /**
1378  * Adds the TinyMCE editor used on the Write and Edit screens.
1379  *
1380  * @package WordPress
1381  * @since 2.7.0
1382  *
1383  * TinyMCE is loaded separately from other Javascript by using wp-tinymce.php. It outputs concatenated
1384  * and optionaly pre-compressed version of the core and all default plugins. Additional plugins are loaded
1385  * directly by TinyMCE using non-blocking method. Custom plugins can be refreshed by adding a query string
1386  * to the URL when queueing them with the mce_external_plugins filter.
1387  *
1388  * @param bool $teeny optional Output a trimmed down version used in Press This.
1389  * @param mixed $settings optional An array that can add to or overwrite the default TinyMCE settings.
1390  */
1391 function wp_tiny_mce( $teeny = false, $settings = false ) {
1392         global $concatenate_scripts, $compress_scripts, $tinymce_version, $editor_styles;
1393
1394         if ( ! user_can_richedit() )
1395                 return;
1396
1397         $baseurl = includes_url('js/tinymce');
1398
1399         $mce_locale = ( '' == get_locale() ) ? 'en' : strtolower( substr(get_locale(), 0, 2) ); // only ISO 639-1
1400
1401         /*
1402         The following filter allows localization scripts to change the languages displayed in the spellchecker's drop-down menu.
1403         By default it uses Google's spellchecker API, but can be configured to use PSpell/ASpell if installed on the server.
1404         The + sign marks the default language. More information:
1405         http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/spellchecker
1406         */
1407         $mce_spellchecker_languages = apply_filters('mce_spellchecker_languages', '+English=en,Danish=da,Dutch=nl,Finnish=fi,French=fr,German=de,Italian=it,Polish=pl,Portuguese=pt,Spanish=es,Swedish=sv');
1408
1409         if ( $teeny ) {
1410                 $plugins = apply_filters( 'teeny_mce_plugins', array('inlinepopups', 'fullscreen', 'wordpress', 'wplink', 'wpdialogs') );
1411                 $ext_plugins = '';
1412         } else {
1413                 $plugins = array( 'inlinepopups', 'spellchecker', 'paste', 'wordpress', 'fullscreen', 'wpeditimage', 'wpgallery', 'tabfocus', 'wplink', 'wpdialogs' );
1414
1415                 /*
1416                 The following filter takes an associative array of external plugins for TinyMCE in the form 'plugin_name' => 'url'.
1417                 It adds the plugin's name to TinyMCE's plugins init and the call to PluginManager to load the plugin.
1418                 The url should be absolute and should include the js file name to be loaded. Example:
1419                 array( 'myplugin' => 'http://my-site.com/wp-content/plugins/myfolder/mce_plugin.js' )
1420                 If the plugin uses a button, it should be added with one of the "$mce_buttons" filters.
1421                 */
1422                 $mce_external_plugins = apply_filters('mce_external_plugins', array());
1423
1424                 $ext_plugins = '';
1425                 if ( ! empty($mce_external_plugins) ) {
1426
1427                         /*
1428                         The following filter loads external language files for TinyMCE plugins.
1429                         It takes an associative array 'plugin_name' => 'path', where path is the
1430                         include path to the file. The language file should follow the same format as
1431                         /tinymce/langs/wp-langs.php and should define a variable $strings that
1432                         holds all translated strings.
1433                         When this filter is not used, the function will try to load {mce_locale}.js.
1434                         If that is not found, en.js will be tried next.
1435                         */
1436                         $mce_external_languages = apply_filters('mce_external_languages', array());
1437
1438                         $loaded_langs = array();
1439                         $strings = '';
1440
1441                         if ( ! empty($mce_external_languages) ) {
1442                                 foreach ( $mce_external_languages as $name => $path ) {
1443                                         if ( @is_file($path) && @is_readable($path) ) {
1444                                                 include_once($path);
1445                                                 $ext_plugins .= $strings . "\n";
1446                                                 $loaded_langs[] = $name;
1447                                         }
1448                                 }
1449                         }
1450
1451                         foreach ( $mce_external_plugins as $name => $url ) {
1452
1453                                 if ( is_ssl() ) $url = str_replace('http://', 'https://', $url);
1454
1455                                 $plugins[] = '-' . $name;
1456
1457                                 $plugurl = dirname($url);
1458                                 $strings = $str1 = $str2 = '';
1459                                 if ( ! in_array($name, $loaded_langs) ) {
1460                                         $path = str_replace( WP_PLUGIN_URL, '', $plugurl );
1461                                         $path = WP_PLUGIN_DIR . $path . '/langs/';
1462
1463                                         if ( function_exists('realpath') )
1464                                                 $path = trailingslashit( realpath($path) );
1465
1466                                         if ( @is_file($path . $mce_locale . '.js') )
1467                                                 $strings .= @file_get_contents($path . $mce_locale . '.js') . "\n";
1468
1469                                         if ( @is_file($path . $mce_locale . '_dlg.js') )
1470                                                 $strings .= @file_get_contents($path . $mce_locale . '_dlg.js') . "\n";
1471
1472                                         if ( 'en' != $mce_locale && empty($strings) ) {
1473                                                 if ( @is_file($path . 'en.js') ) {
1474                                                         $str1 = @file_get_contents($path . 'en.js');
1475                                                         $strings .= preg_replace( '/([\'"])en\./', '$1' . $mce_locale . '.', $str1, 1 ) . "\n";
1476                                                 }
1477
1478                                                 if ( @is_file($path . 'en_dlg.js') ) {
1479                                                         $str2 = @file_get_contents($path . 'en_dlg.js');
1480                                                         $strings .= preg_replace( '/([\'"])en\./', '$1' . $mce_locale . '.', $str2, 1 ) . "\n";
1481                                                 }
1482                                         }
1483
1484                                         if ( ! empty($strings) )
1485                                                 $ext_plugins .= "\n" . $strings . "\n";
1486                                 }
1487
1488                                 $ext_plugins .= 'tinyMCEPreInit.load_ext("' . $plugurl . '", "' . $mce_locale . '");' . "\n";
1489                                 $ext_plugins .= 'tinymce.PluginManager.load("' . $name . '", "' . $url . '");' . "\n";
1490                         }
1491                 }
1492         }
1493
1494         if ( $teeny ) {
1495                 $mce_buttons = apply_filters( 'teeny_mce_buttons', array('bold, italic, underline, blockquote, separator, strikethrough, bullist, numlist,justifyleft, justifycenter, justifyright, undo, redo, link, unlink, fullscreen') );
1496                 $mce_buttons = implode($mce_buttons, ',');
1497                 $mce_buttons_2 = $mce_buttons_3 = $mce_buttons_4 = '';
1498         } else {
1499                 $mce_buttons = apply_filters('mce_buttons', array('bold', 'italic', 'strikethrough', '|', 'bullist', 'numlist', 'blockquote', '|', 'justifyleft', 'justifycenter', 'justifyright', '|', 'link', 'unlink', 'wp_more', '|', 'spellchecker', 'fullscreen', 'wp_adv' ));
1500                 $mce_buttons = implode($mce_buttons, ',');
1501
1502                 $mce_buttons_2 = array( 'formatselect', 'underline', 'justifyfull', 'forecolor', '|', 'pastetext', 'pasteword', 'removeformat', '|', 'charmap', '|', 'outdent', 'indent', '|', 'undo', 'redo', 'wp_help' );
1503                 $mce_buttons_2 = apply_filters('mce_buttons_2', $mce_buttons_2);
1504                 $mce_buttons_2 = implode($mce_buttons_2, ',');
1505
1506                 $mce_buttons_3 = apply_filters('mce_buttons_3', array());
1507                 $mce_buttons_3 = implode($mce_buttons_3, ',');
1508
1509                 $mce_buttons_4 = apply_filters('mce_buttons_4', array());
1510                 $mce_buttons_4 = implode($mce_buttons_4, ',');
1511         }
1512         $no_captions = (bool) apply_filters( 'disable_captions', '' );
1513
1514         // TinyMCE init settings
1515         $initArray = array (
1516                 'mode' => 'specific_textareas',
1517                 'editor_selector' => 'theEditor',
1518                 'width' => '100%',
1519                 'theme' => 'advanced',
1520                 'skin' => 'wp_theme',
1521                 'theme_advanced_buttons1' => $mce_buttons,
1522                 'theme_advanced_buttons2' => $mce_buttons_2,
1523                 'theme_advanced_buttons3' => $mce_buttons_3,
1524                 'theme_advanced_buttons4' => $mce_buttons_4,
1525                 'language' => $mce_locale,
1526                 'spellchecker_languages' => $mce_spellchecker_languages,
1527                 'theme_advanced_toolbar_location' => 'top',
1528                 'theme_advanced_toolbar_align' => 'left',
1529                 'theme_advanced_statusbar_location' => 'bottom',
1530                 'theme_advanced_resizing' => true,
1531                 'theme_advanced_resize_horizontal' => false,
1532                 'dialog_type' => 'modal',
1533                 'formats' => "{
1534                         alignleft : [
1535                                 {selector : 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'left'}},
1536                                 {selector : 'img,table', classes : 'alignleft'}
1537                         ],
1538                         aligncenter : [
1539                                 {selector : 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'center'}},
1540                                 {selector : 'img,table', classes : 'aligncenter'}
1541                         ],
1542                         alignright : [
1543                                 {selector : 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'right'}},
1544                                 {selector : 'img,table', classes : 'alignright'}
1545                         ],
1546                         strikethrough : {inline : 'del'}
1547                 }",
1548                 'relative_urls' => false,
1549                 'remove_script_host' => false,
1550                 'convert_urls' => false,
1551                 'apply_source_formatting' => false,
1552                 'remove_linebreaks' => true,
1553                 'gecko_spellcheck' => true,
1554                 'entities' => '38,amp,60,lt,62,gt',
1555                 'accessibility_focus' => true,
1556                 'tabfocus_elements' => 'major-publishing-actions',
1557                 'media_strict' => false,
1558                 'paste_remove_styles' => true,
1559                 'paste_remove_spans' => true,
1560                 'paste_strip_class_attributes' => 'all',
1561                 'paste_text_use_dialog' => true,
1562                 'wpeditimage_disable_captions' => $no_captions,
1563                 'plugins' => implode( ',', $plugins ),
1564         );
1565
1566         if ( ! empty( $editor_styles ) && is_array( $editor_styles ) ) {
1567                 $mce_css = array();
1568                 $style_uri = get_stylesheet_directory_uri();
1569                 if ( ! is_child_theme() ) {
1570                         foreach ( $editor_styles as $file )
1571                                 $mce_css[] = "$style_uri/$file";
1572                 } else {
1573                         $style_dir    = get_stylesheet_directory();
1574                         $template_uri = get_template_directory_uri();
1575                         $template_dir = get_template_directory();
1576                         foreach ( $editor_styles as $file ) {
1577                                 if ( file_exists( "$template_dir/$file" ) )
1578                                         $mce_css[] = "$template_uri/$file";
1579                                 if ( file_exists( "$style_dir/$file" ) )
1580                                         $mce_css[] = "$style_uri/$file";
1581                         }
1582                 }
1583                 $mce_css = implode( ',', $mce_css );
1584         } else {
1585                 $mce_css = '';
1586         }
1587
1588         $mce_css = trim( apply_filters( 'mce_css', $mce_css ), ' ,' );
1589
1590         if ( ! empty($mce_css) )
1591                 $initArray['content_css'] = $mce_css;
1592
1593         if ( is_array($settings) )
1594                 $initArray = array_merge($initArray, $settings);
1595
1596         // For people who really REALLY know what they're doing with TinyMCE
1597         // You can modify initArray to add, remove, change elements of the config before tinyMCE.init
1598         // Setting "valid_elements", "invalid_elements" and "extended_valid_elements" can be done through "tiny_mce_before_init".
1599         // Best is to use the default cleanup by not specifying valid_elements, as TinyMCE contains full set of XHTML 1.0.
1600         if ( $teeny ) {
1601                 $initArray = apply_filters('teeny_mce_before_init', $initArray);
1602         } else {
1603                 $initArray = apply_filters('tiny_mce_before_init', $initArray);
1604         }
1605
1606         if ( empty($initArray['theme_advanced_buttons3']) && !empty($initArray['theme_advanced_buttons4']) ) {
1607                 $initArray['theme_advanced_buttons3'] = $initArray['theme_advanced_buttons4'];
1608                 $initArray['theme_advanced_buttons4'] = '';
1609         }
1610
1611         if ( ! isset($concatenate_scripts) )
1612                 script_concat_settings();
1613
1614         $language = $initArray['language'];
1615
1616         $compressed = $compress_scripts && $concatenate_scripts && isset($_SERVER['HTTP_ACCEPT_ENCODING'])
1617                 && false !== strpos( strtolower($_SERVER['HTTP_ACCEPT_ENCODING']), 'gzip');
1618
1619         /**
1620          * Deprecated
1621          *
1622          * The tiny_mce_version filter is not needed since external plugins are loaded directly by TinyMCE.
1623          * These plugins can be refreshed by appending query string to the URL passed to mce_external_plugins filter.
1624          * If the plugin has a popup dialog, a query string can be added to the button action that opens it (in the plugin's code).
1625          */
1626         $version = apply_filters('tiny_mce_version', '');
1627         $version = 'ver=' . $tinymce_version . $version;
1628
1629         if ( 'en' != $language )
1630                 include_once(ABSPATH . WPINC . '/js/tinymce/langs/wp-langs.php');
1631
1632         $mce_options = '';
1633         foreach ( $initArray as $k => $v ) {
1634                 if ( is_bool($v) ) {
1635                         $val = $v ? 'true' : 'false';
1636                         $mce_options .= $k . ':' . $val . ', ';
1637                         continue;
1638                 } elseif ( !empty($v) && is_string($v) && ( '{' == $v{0} || '[' == $v{0} ) ) {
1639                         $mce_options .= $k . ':' . $v . ', ';
1640                         continue;
1641                 }
1642
1643                 $mce_options .= $k . ':"' . $v . '", ';
1644         }
1645
1646         $mce_options = rtrim( trim($mce_options), '\n\r,' ); ?>
1647
1648 <script type="text/javascript">
1649 /* <![CDATA[ */
1650 tinyMCEPreInit = {
1651         base : "<?php echo $baseurl; ?>",
1652         suffix : "",
1653         query : "<?php echo $version; ?>",
1654         mceInit : {<?php echo $mce_options; ?>},
1655         load_ext : function(url,lang){var sl=tinymce.ScriptLoader;sl.markDone(url+'/langs/'+lang+'.js');sl.markDone(url+'/langs/'+lang+'_dlg.js');}
1656 };
1657 /* ]]> */
1658 </script>
1659
1660 <?php
1661         if ( $compressed )
1662                 echo "<script type='text/javascript' src='$baseurl/wp-tinymce.php?c=1&amp;$version'></script>\n";
1663         else
1664                 echo "<script type='text/javascript' src='$baseurl/tiny_mce.js?$version'></script>\n";
1665
1666         if ( 'en' != $language && isset($lang) )
1667                 echo "<script type='text/javascript'>\n$lang\n</script>\n";
1668         else
1669                 echo "<script type='text/javascript' src='$baseurl/langs/wp-langs-en.js?$version'></script>\n";
1670 ?>
1671
1672 <script type="text/javascript">
1673 /* <![CDATA[ */
1674 <?php
1675         if ( $ext_plugins )
1676                 echo "$ext_plugins\n";
1677
1678         if ( ! $compressed ) {
1679 ?>
1680 (function(){var t=tinyMCEPreInit,sl=tinymce.ScriptLoader,ln=t.mceInit.language,th=t.mceInit.theme,pl=t.mceInit.plugins;sl.markDone(t.base+'/langs/'+ln+'.js');sl.markDone(t.base+'/themes/'+th+'/langs/'+ln+'.js');sl.markDone(t.base+'/themes/'+th+'/langs/'+ln+'_dlg.js');tinymce.each(pl.split(','),function(n){if(n&&n.charAt(0)!='-'){sl.markDone(t.base+'/plugins/'+n+'/langs/'+ln+'.js');sl.markDone(t.base+'/plugins/'+n+'/langs/'+ln+'_dlg.js');}});})();
1681 <?php } ?>
1682 tinyMCE.init(tinyMCEPreInit.mceInit);
1683 /* ]]> */
1684 </script>
1685 <?php
1686
1687         // Load additional inline scripts based on active plugins.
1688         if ( in_array( 'wpdialogs', $plugins ) ) {
1689                 wp_print_scripts( array( 'wpdialogs-popup' ) );
1690                 wp_print_styles('wp-jquery-ui-dialog');
1691         }
1692         if ( in_array( 'wplink', $plugins ) ) {
1693                 require_once ABSPATH . 'wp-admin/includes/internal-linking.php';
1694                 add_action('tiny_mce_preload_dialogs', 'wp_link_dialog');
1695                 wp_print_scripts('wplink');
1696                 wp_print_styles('wplink');
1697         }
1698 }
1699 function wp_tiny_mce_preload_dialogs() { ?>
1700         <div id="preloaded-dialogs" style="display:none;">
1701 <?php   do_action('tiny_mce_preload_dialogs'); ?>
1702         </div>
1703 <?php }