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