Wordpress 3.0.5
[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_id = wp_insert_post( array( 'post_title' => __( 'Auto Draft' ), 'post_type' => $post_type, 'post_status' => 'auto-draft' ) );
380                 $post = get_post( $post_id );
381         } else {
382                 $post->ID = 0;
383                 $post->post_author = '';
384                 $post->post_date = '';
385                 $post->post_date_gmt = '';
386                 $post->post_password = '';
387                 $post->post_type = $post_type;
388                 $post->post_status = 'draft';
389                 $post->to_ping = '';
390                 $post->pinged = '';
391                 $post->comment_status = get_option( 'default_comment_status' );
392                 $post->ping_status = get_option( 'default_ping_status' );
393                 $post->post_pingback = get_option( 'default_pingback_flag' );
394                 $post->post_category = get_option( 'default_category' );
395                 $post->page_template = 'default';
396                 $post->post_parent = 0;
397                 $post->menu_order = 0;
398         }
399
400         $post->post_content = apply_filters( 'default_content', $post_content, $post );
401         $post->post_title   = apply_filters( 'default_title',   $post_title, $post   );
402         $post->post_excerpt = apply_filters( 'default_excerpt', $post_excerpt, $post );
403         $post->post_name = '';
404
405         return $post;
406 }
407
408 /**
409  * Get the default page information to use.
410  *
411  * @since 2.5.0
412  *
413  * @return object stdClass object containing all the default post data as attributes
414  */
415 function get_default_page_to_edit() {
416         $page = get_default_post_to_edit();
417         $page->post_type = 'page';
418         return $page;
419 }
420
421 /**
422  * Get an existing post and format it for editing.
423  *
424  * @since unknown
425  *
426  * @param unknown_type $id
427  * @return unknown
428  */
429 function get_post_to_edit( $id ) {
430
431         $post = get_post( $id, OBJECT, 'edit' );
432
433         if ( $post->post_type == 'page' )
434                 $post->page_template = get_post_meta( $id, '_wp_page_template', true );
435
436         return $post;
437 }
438
439 /**
440  * Determine if a post exists based on title, content, and date
441  *
442  * @since unknown
443  *
444  * @param string $title Post title
445  * @param string $content Optional post content
446  * @param string $date Optional post date
447  * @return int Post ID if post exists, 0 otherwise.
448  */
449 function post_exists($title, $content = '', $date = '') {
450         global $wpdb;
451
452         $post_title = stripslashes( sanitize_post_field( 'post_title', $title, 0, 'db' ) );
453         $post_content = stripslashes( sanitize_post_field( 'post_content', $content, 0, 'db' ) );
454         $post_date = stripslashes( sanitize_post_field( 'post_date', $date, 0, 'db' ) );
455
456         $query = "SELECT ID FROM $wpdb->posts WHERE 1=1";
457         $args = array();
458
459         if ( !empty ( $date ) ) {
460                 $query .= ' AND post_date = %s';
461                 $args[] = $post_date;
462         }
463
464         if ( !empty ( $title ) ) {
465                 $query .= ' AND post_title = %s';
466                 $args[] = $post_title;
467         }
468
469         if ( !empty ( $content ) ) {
470                 $query .= 'AND post_content = %s';
471                 $args[] = $post_content;
472         }
473
474         if ( !empty ( $args ) )
475                 return $wpdb->get_var( $wpdb->prepare($query, $args) );
476
477         return 0;
478 }
479
480 /**
481  * Creates a new post from the "Write Post" form using $_POST information.
482  *
483  * @since unknown
484  *
485  * @return unknown
486  */
487 function wp_write_post() {
488         global $user_ID;
489
490
491         if ( isset($_POST['post_type']) )
492                 $ptype = get_post_type_object($_POST['post_type']);
493         else
494                 $ptype = get_post_type_object('post');
495
496         if ( !current_user_can( $ptype->cap->edit_posts ) ) {
497                 if ( 'page' == $ptype->name )
498                         return new WP_Error( 'edit_pages', __( 'You are not allowed to create pages on this site.' ) );
499                 else
500                         return new WP_Error( 'edit_posts', __( 'You are not allowed to create posts or drafts on this site.' ) );
501         }
502
503         // Check for autosave collisions
504         // Does this need to be updated? ~ Mark
505         $temp_id = false;
506         if ( isset($_POST['temp_ID']) ) {
507                 $temp_id = (int) $_POST['temp_ID'];
508                 if ( !$draft_ids = get_user_option( 'autosave_draft_ids' ) )
509                         $draft_ids = array();
510                 foreach ( $draft_ids as $temp => $real )
511                         if ( time() + $temp > 86400 ) // 1 day: $temp is equal to -1 * time( then )
512                                 unset($draft_ids[$temp]);
513
514                 if ( isset($draft_ids[$temp_id]) ) { // Edit, don't write
515                         $_POST['post_ID'] = $draft_ids[$temp_id];
516                         unset($_POST['temp_ID']);
517                         update_user_option( $user_ID, 'autosave_draft_ids', $draft_ids );
518                         return edit_post();
519                 }
520         }
521
522         $translated = _wp_translate_postdata( false );
523         if ( is_wp_error($translated) )
524                 return $translated;
525
526         if ( isset($_POST['visibility']) ) {
527                 switch ( $_POST['visibility'] ) {
528                         case 'public' :
529                                 $_POST['post_password'] = '';
530                                 break;
531                         case 'password' :
532                                 unset( $_POST['sticky'] );
533                                 break;
534                         case 'private' :
535                                 $_POST['post_status'] = 'private';
536                                 $_POST['post_password'] = '';
537                                 unset( $_POST['sticky'] );
538                                 break;
539                 }
540         }
541
542         // Create the post.
543         $post_ID = wp_insert_post( $_POST );
544         if ( is_wp_error( $post_ID ) )
545                 return $post_ID;
546
547         if ( empty($post_ID) )
548                 return 0;
549
550         add_meta( $post_ID );
551
552         add_post_meta( $post_ID, '_edit_last', $GLOBALS['current_user']->ID );
553
554         // Reunite any orphaned attachments with their parent
555         // Does this need to be udpated? ~ Mark
556         if ( !$draft_ids = get_user_option( 'autosave_draft_ids' ) )
557                 $draft_ids = array();
558         if ( $draft_temp_id = (int) array_search( $post_ID, $draft_ids ) )
559                 _relocate_children( $draft_temp_id, $post_ID );
560         if ( $temp_id && $temp_id != $draft_temp_id )
561                 _relocate_children( $temp_id, $post_ID );
562
563         // Update autosave collision detection
564         if ( $temp_id ) {
565                 $draft_ids[$temp_id] = $post_ID;
566                 update_user_option( $user_ID, 'autosave_draft_ids', $draft_ids );
567         }
568
569         // Now that we have an ID we can fix any attachment anchor hrefs
570         _fix_attachment_links( $post_ID );
571
572         wp_set_post_lock( $post_ID, $GLOBALS['current_user']->ID );
573
574         return $post_ID;
575 }
576
577 /**
578  * Calls wp_write_post() and handles the errors.
579  *
580  * @since unknown
581  *
582  * @return unknown
583  */
584 function write_post() {
585         $result = wp_write_post();
586         if ( is_wp_error( $result ) )
587                 wp_die( $result->get_error_message() );
588         else
589                 return $result;
590 }
591
592 //
593 // Post Meta
594 //
595
596 /**
597  * {@internal Missing Short Description}}
598  *
599  * @since unknown
600  *
601  * @param unknown_type $post_ID
602  * @return unknown
603  */
604 function add_meta( $post_ID ) {
605         global $wpdb;
606         $post_ID = (int) $post_ID;
607
608         $protected = array( '_wp_attached_file', '_wp_attachment_metadata', '_wp_old_slug', '_wp_page_template' );
609
610         $metakeyselect = isset($_POST['metakeyselect']) ? stripslashes( trim( $_POST['metakeyselect'] ) ) : '';
611         $metakeyinput = isset($_POST['metakeyinput']) ? stripslashes( trim( $_POST['metakeyinput'] ) ) : '';
612         $metavalue = isset($_POST['metavalue']) ? maybe_serialize( stripslashes_deep( $_POST['metavalue'] ) ) : '';
613         if ( is_string($metavalue) )
614                 $metavalue = trim( $metavalue );
615
616         if ( ('0' === $metavalue || !empty ( $metavalue ) ) && ((('#NONE#' != $metakeyselect) && !empty ( $metakeyselect) ) || !empty ( $metakeyinput) ) ) {
617                 // We have a key/value pair. If both the select and the
618                 // input for the key have data, the input takes precedence:
619
620                 if ('#NONE#' != $metakeyselect)
621                         $metakey = $metakeyselect;
622
623                 if ( $metakeyinput)
624                         $metakey = $metakeyinput; // default
625
626                 if ( in_array($metakey, $protected) )
627                         return false;
628
629                 wp_cache_delete($post_ID, 'post_meta');
630                 $wpdb->insert( $wpdb->postmeta, array( 'post_id' => $post_ID, 'meta_key' => $metakey, 'meta_value' => $metavalue ) );
631                 do_action( 'added_postmeta', $wpdb->insert_id, $post_ID, $metakey, $metavalue );
632
633                 return $wpdb->insert_id;
634         }
635         return false;
636 } // add_meta
637
638 /**
639  * {@internal Missing Short Description}}
640  *
641  * @since unknown
642  *
643  * @param unknown_type $mid
644  * @return unknown
645  */
646 function delete_meta( $mid ) {
647         global $wpdb;
648         $mid = (int) $mid;
649
650         $post_id = $wpdb->get_var( $wpdb->prepare("SELECT post_id FROM $wpdb->postmeta WHERE meta_id = %d", $mid) );
651
652         do_action( 'delete_postmeta', $mid );
653         wp_cache_delete($post_id, 'post_meta');
654         $rval = $wpdb->query( $wpdb->prepare("DELETE FROM $wpdb->postmeta WHERE meta_id = %d", $mid) );
655         do_action( 'deleted_postmeta', $mid );
656
657         return $rval;
658 }
659
660 /**
661  * Get a list of previously defined keys.
662  *
663  * @since unknown
664  *
665  * @return unknown
666  */
667 function get_meta_keys() {
668         global $wpdb;
669
670         $keys = $wpdb->get_col( "
671                         SELECT meta_key
672                         FROM $wpdb->postmeta
673                         GROUP BY meta_key
674                         ORDER BY meta_key" );
675
676         return $keys;
677 }
678
679 /**
680  * {@internal Missing Short Description}}
681  *
682  * @since unknown
683  *
684  * @param unknown_type $mid
685  * @return unknown
686  */
687 function get_post_meta_by_id( $mid ) {
688         global $wpdb;
689         $mid = (int) $mid;
690
691         $meta = $wpdb->get_row( $wpdb->prepare("SELECT * FROM $wpdb->postmeta WHERE meta_id = %d", $mid) );
692         if ( empty($meta) )
693                 return false;
694         if ( is_serialized_string( $meta->meta_value ) )
695                 $meta->meta_value = maybe_unserialize( $meta->meta_value );
696         return $meta;
697 }
698
699 /**
700  * {@internal Missing Short Description}}
701  *
702  * Some postmeta stuff.
703  *
704  * @since unknown
705  *
706  * @param unknown_type $postid
707  * @return unknown
708  */
709 function has_meta( $postid ) {
710         global $wpdb;
711
712         return $wpdb->get_results( $wpdb->prepare("SELECT meta_key, meta_value, meta_id, post_id
713                         FROM $wpdb->postmeta WHERE post_id = %d
714                         ORDER BY meta_key,meta_id", $postid), ARRAY_A );
715
716 }
717
718 /**
719  * {@internal Missing Short Description}}
720  *
721  * @since unknown
722  *
723  * @param unknown_type $meta_id
724  * @param unknown_type $meta_key Expect Slashed
725  * @param unknown_type $meta_value Expect Slashed
726  * @return unknown
727  */
728 function update_meta( $meta_id, $meta_key, $meta_value ) {
729         global $wpdb;
730
731         $protected = array( '_wp_attached_file', '_wp_attachment_metadata', '_wp_old_slug', '_wp_page_template' );
732
733         $meta_key = stripslashes($meta_key);
734
735         if ( in_array($meta_key, $protected) )
736                 return false;
737
738         if ( '' === trim( $meta_value ) )
739                 return false;
740
741         $post_id = $wpdb->get_var( $wpdb->prepare("SELECT post_id FROM $wpdb->postmeta WHERE meta_id = %d", $meta_id) );
742
743         $meta_value = maybe_serialize( stripslashes_deep( $meta_value ) );
744         $meta_id = (int) $meta_id;
745
746         $data  = compact( 'meta_key', 'meta_value' );
747         $where = compact( 'meta_id' );
748
749         do_action( 'update_postmeta', $meta_id, $post_id, $meta_key, $meta_value );
750         $rval = $wpdb->update( $wpdb->postmeta, $data, $where );
751         wp_cache_delete($post_id, 'post_meta');
752         do_action( 'updated_postmeta', $meta_id, $post_id, $meta_key, $meta_value );
753
754         return $rval;
755 }
756
757 //
758 // Private
759 //
760
761 /**
762  * Replace hrefs of attachment anchors with up-to-date permalinks.
763  *
764  * @since unknown
765  * @access private
766  *
767  * @param unknown_type $post_ID
768  * @return unknown
769  */
770 function _fix_attachment_links( $post_ID ) {
771         global $_fix_attachment_link_id;
772
773         $post = & get_post( $post_ID, ARRAY_A );
774
775         $search = "#<a[^>]+rel=('|\")[^'\"]*attachment[^>]*>#ie";
776
777         // See if we have any rel="attachment" links
778         if ( 0 == preg_match_all( $search, $post['post_content'], $anchor_matches, PREG_PATTERN_ORDER ) )
779                 return;
780
781         $i = 0;
782         $search = "#[\s]+rel=(\"|')(.*?)wp-att-(\d+)\\1#i";
783         foreach ( $anchor_matches[0] as $anchor ) {
784                 if ( 0 == preg_match( $search, $anchor, $id_matches ) )
785                         continue;
786
787                 $id = (int) $id_matches[3];
788
789                 // While we have the attachment ID, let's adopt any orphans.
790                 $attachment = & get_post( $id, ARRAY_A );
791                 if ( ! empty( $attachment) && ! is_object( get_post( $attachment['post_parent'] ) ) ) {
792                         $attachment['post_parent'] = $post_ID;
793                         // Escape data pulled from DB.
794                         $attachment = add_magic_quotes( $attachment );
795                         wp_update_post( $attachment );
796                 }
797
798                 $post_search[$i] = $anchor;
799                  $_fix_attachment_link_id = $id;
800                 $post_replace[$i] = preg_replace_callback( "#href=(\"|')[^'\"]*\\1#", '_fix_attachment_links_replace_cb', $anchor );
801                 ++$i;
802         }
803
804         $post['post_content'] = str_replace( $post_search, $post_replace, $post['post_content'] );
805
806         // Escape data pulled from DB.
807         $post = add_magic_quotes( $post);
808
809         return wp_update_post( $post);
810 }
811
812 function _fix_attachment_links_replace_cb($match) {
813         global $_fix_attachment_link_id;
814         return stripslashes( 'href='.$match[1] ).get_attachment_link( $_fix_attachment_link_id ).stripslashes( $match[1] );
815 }
816
817 /**
818  * Move child posts to a new parent.
819  *
820  * @since unknown
821  * @access private
822  *
823  * @param unknown_type $old_ID
824  * @param unknown_type $new_ID
825  * @return unknown
826  */
827 function _relocate_children( $old_ID, $new_ID ) {
828         global $wpdb;
829         $old_ID = (int) $old_ID;
830         $new_ID = (int) $new_ID;
831
832         $children = $wpdb->get_col( $wpdb->prepare("
833                 SELECT post_id
834                 FROM $wpdb->postmeta
835                 WHERE meta_key = '_wp_attachment_temp_parent'
836                 AND meta_value = %d", $old_ID) );
837
838         foreach ( $children as $child_id ) {
839                 $wpdb->update($wpdb->posts, array('post_parent' => $new_ID), array('ID' => $child_id) );
840                 delete_post_meta($child_id, '_wp_attachment_temp_parent');
841         }
842 }
843
844 /**
845  * Get all the possible statuses for a post_type
846  *
847  * @since 2.5.0
848  *
849  * @param string $type The post_type you want the statuses for
850  * @return array As array of all the statuses for the supplied post type
851  */
852 function get_available_post_statuses($type = 'post') {
853         $stati = wp_count_posts($type);
854
855         return array_keys(get_object_vars($stati));
856 }
857
858 /**
859  * Run the wp query to fetch the posts for listing on the edit posts page
860  *
861  * @since 2.5.0
862  *
863  * @param array|bool $q Array of query variables to use to build the query or false to use $_GET superglobal.
864  * @return array
865  */
866 function wp_edit_posts_query( $q = false ) {
867         if ( false === $q )
868                 $q = $_GET;
869         $q['m'] = isset($q['m']) ? (int) $q['m'] : 0;
870         $q['cat'] = isset($q['cat']) ? (int) $q['cat'] : 0;
871         $post_stati  = get_post_stati();
872
873         if ( isset($q['post_type']) && in_array( $q['post_type'], get_post_types() ) )
874                 $post_type = $q['post_type'];
875         else
876                 $post_type = 'post';
877
878         $avail_post_stati = get_available_post_statuses($post_type);
879
880         if ( isset($q['post_status']) && in_array( $q['post_status'], $post_stati ) ) {
881                 $post_status = $q['post_status'];
882                 $perm = 'readable';
883         }
884
885         if ( isset($q['post_status']) && 'pending' === $q['post_status'] ) {
886                 $order = 'ASC';
887                 $orderby = 'modified';
888         } elseif ( isset($q['post_status']) && 'draft' === $q['post_status'] ) {
889                 $order = 'DESC';
890                 $orderby = 'modified';
891         } else {
892                 $order = 'DESC';
893                 $orderby = 'date';
894         }
895
896         $per_page = 'edit_' . $post_type . '_per_page';
897         $posts_per_page = (int) get_user_option( $per_page );
898         if ( empty( $posts_per_page ) || $posts_per_page < 1 )
899                 $posts_per_page = 20;
900
901         $posts_per_page = apply_filters( $per_page, $posts_per_page );
902         $posts_per_page = apply_filters( 'edit_posts_per_page', $posts_per_page, $post_type );
903
904         $query = compact('post_type', 'post_status', 'perm', 'order', 'orderby', 'posts_per_page');
905
906         // Hierarchical types require special args.
907         if ( is_post_type_hierarchical( $post_type ) ) {
908                 $query['orderby'] = 'menu_order title';
909                 $query['order'] = 'asc';
910                 $query['posts_per_page'] = -1;
911                 $query['posts_per_archive_page'] = -1;
912         }
913
914         wp( $query );
915
916         return $avail_post_stati;
917 }
918
919 /**
920  * Get default post mime types
921  *
922  * @since 2.9.0
923  *
924  * @return array
925  */
926 function get_post_mime_types() {
927         $post_mime_types = array(       //      array( adj, noun )
928                 'image' => array(__('Images'), __('Manage Images'), _n_noop('Image <span class="count">(%s)</span>', 'Images <span class="count">(%s)</span>')),
929                 'audio' => array(__('Audio'), __('Manage Audio'), _n_noop('Audio <span class="count">(%s)</span>', 'Audio <span class="count">(%s)</span>')),
930                 'video' => array(__('Video'), __('Manage Video'), _n_noop('Video <span class="count">(%s)</span>', 'Video <span class="count">(%s)</span>')),
931         );
932
933         return apply_filters('post_mime_types', $post_mime_types);
934 }
935
936 /**
937  * {@internal Missing Short Description}}
938  *
939  * @since unknown
940  *
941  * @param unknown_type $type
942  * @return unknown
943  */
944 function get_available_post_mime_types($type = 'attachment') {
945         global $wpdb;
946
947         $types = $wpdb->get_col($wpdb->prepare("SELECT DISTINCT post_mime_type FROM $wpdb->posts WHERE post_type = %s", $type));
948         return $types;
949 }
950
951 /**
952  * {@internal Missing Short Description}}
953  *
954  * @since unknown
955  *
956  * @param unknown_type $q
957  * @return unknown
958  */
959 function wp_edit_attachments_query( $q = false ) {
960         if ( false === $q )
961                 $q = $_GET;
962
963         $q['m']   = isset( $q['m'] ) ? (int) $q['m'] : 0;
964         $q['cat'] = isset( $q['cat'] ) ? (int) $q['cat'] : 0;
965         $q['post_type'] = 'attachment';
966         $q['post_status'] = isset( $q['status'] ) && 'trash' == $q['status'] ? 'trash' : 'inherit';
967         $media_per_page = (int) get_user_option( 'upload_per_page' );
968         if ( empty( $media_per_page ) || $media_per_page < 1 )
969                 $media_per_page = 20;
970         $q['posts_per_page'] = apply_filters( 'upload_per_page', $media_per_page );
971
972         $post_mime_types = get_post_mime_types();
973         $avail_post_mime_types = get_available_post_mime_types('attachment');
974
975         if ( isset($q['post_mime_type']) && !array_intersect( (array) $q['post_mime_type'], array_keys($post_mime_types) ) )
976                 unset($q['post_mime_type']);
977
978         wp($q);
979
980         return array($post_mime_types, $avail_post_mime_types);
981 }
982
983 /**
984  * {@internal Missing Short Description}}
985  *
986  * @uses get_user_option()
987  * @since unknown
988  *
989  * @param unknown_type $id
990  * @param unknown_type $page
991  * @return unknown
992  */
993 function postbox_classes( $id, $page ) {
994         if ( isset( $_GET['edit'] ) && $_GET['edit'] == $id )
995                 return '';
996
997         if ( $closed = get_user_option('closedpostboxes_'.$page ) ) {
998                 if ( !is_array( $closed ) ) {
999                         return '';
1000                 }
1001                 return in_array( $id, $closed )? 'closed' : '';
1002         } else {
1003                 return '';
1004         }
1005 }
1006
1007 /**
1008  * {@internal Missing Short Description}}
1009  *
1010  * @since unknown
1011  *
1012  * @param int|object $id    Post ID or post object.
1013  * @param string $title (optional) Title
1014  * @param string $name (optional) Name
1015  * @return array With two entries of type string
1016  */
1017 function get_sample_permalink($id, $title = null, $name = null) {
1018         $post = &get_post($id);
1019         if ( !$post->ID )
1020                 return array('', '');
1021
1022         $ptype = get_post_type_object($post->post_type);
1023
1024         $original_status = $post->post_status;
1025         $original_date = $post->post_date;
1026         $original_name = $post->post_name;
1027
1028         // Hack: get_permalink would return ugly permalink for
1029         // drafts, so we will fake, that our post is published
1030         if ( in_array($post->post_status, array('draft', 'pending')) ) {
1031                 $post->post_status = 'publish';
1032                 $post->post_name = sanitize_title($post->post_name ? $post->post_name : $post->post_title, $post->ID);
1033         }
1034
1035         // If the user wants to set a new name -- override the current one
1036         // Note: if empty name is supplied -- use the title instead, see #6072
1037         if ( !is_null($name) )
1038                 $post->post_name = sanitize_title($name ? $name : $title, $post->ID);
1039
1040         $post->post_name = wp_unique_post_slug($post->post_name, $post->ID, $post->post_status, $post->post_type, $post->post_parent);
1041
1042         $post->filter = 'sample';
1043
1044         $permalink = get_permalink($post, true);
1045
1046         // Replace custom post_type Token with generic pagename token for ease of use.
1047         $permalink = str_replace("%$post->post_type%", '%pagename%', $permalink);
1048
1049         // Handle page hierarchy
1050         if ( $ptype->hierarchical ) {
1051                 $uri = get_page_uri($post);
1052                 $uri = untrailingslashit($uri);
1053                 $uri = strrev( stristr( strrev( $uri ), '/' ) );
1054                 $uri = untrailingslashit($uri);
1055                 if ( !empty($uri) )
1056                         $uri .= '/';
1057                 $permalink = str_replace('%pagename%', "${uri}%pagename%", $permalink);
1058         }
1059
1060         $permalink = array($permalink, apply_filters('editable_slug', $post->post_name));
1061         $post->post_status = $original_status;
1062         $post->post_date = $original_date;
1063         $post->post_name = $original_name;
1064         unset($post->filter);
1065
1066         return $permalink;
1067 }
1068
1069 /**
1070  * sample permalink html
1071  *
1072  * intended to be used for the inplace editor of the permalink post slug on in the post (and page?) editor.
1073  *
1074  * @since unknown
1075  *
1076  * @param int|object $id Post ID or post object.
1077  * @param string $new_title (optional) New title
1078  * @param string $new_slug (optional) New slug
1079  * @return string intended to be used for the inplace editor of the permalink post slug on in the post (and page?) editor.
1080  */
1081 function get_sample_permalink_html( $id, $new_title = null, $new_slug = null ) {
1082         global $wpdb;
1083         $post = &get_post($id);
1084
1085         list($permalink, $post_name) = get_sample_permalink($post->ID, $new_title, $new_slug);
1086
1087         if ( 'publish' == $post->post_status ) {
1088                 $ptype = get_post_type_object($post->post_type);
1089                 $view_post = $ptype->labels->view_item;
1090                 $title = __('Click to edit this part of the permalink');
1091         } else {
1092                 $title = __('Temporary permalink. Click to edit this part.');
1093         }
1094
1095         if ( false === strpos($permalink, '%postname%') && false === strpos($permalink, '%pagename%') ) {
1096                 $return = '<strong>' . __('Permalink:') . "</strong>\n" . '<span id="sample-permalink">' . $permalink . "</span>\n";
1097                 if ( current_user_can( 'manage_options' ) && !( 'page' == get_option('show_on_front') && $id == get_option('page_on_front') ) )
1098                         $return .= '<span id="change-permalinks"><a href="options-permalink.php" class="button" target="_blank">' . __('Change Permalinks') . "</a></span>\n";
1099                 if ( isset($view_post) )
1100                         $return .= "<span id='view-post-btn'><a href='$permalink' class='button' target='_blank'>$view_post</a></span>\n";
1101
1102                 $return = apply_filters('get_sample_permalink_html', $return, $id, $new_title, $new_slug);
1103
1104                 return $return;
1105         }
1106
1107         if ( function_exists('mb_strlen') ) {
1108                 if ( mb_strlen($post_name) > 30 ) {
1109                         $post_name_abridged = mb_substr($post_name, 0, 14). '&hellip;' . mb_substr($post_name, -14);
1110                 } else {
1111                         $post_name_abridged = $post_name;
1112                 }
1113         } else {
1114                 if ( strlen($post_name) > 30 ) {
1115                         $post_name_abridged = substr($post_name, 0, 14). '&hellip;' . substr($post_name, -14);
1116                 } else {
1117                         $post_name_abridged = $post_name;
1118                 }
1119         }
1120
1121         $post_name_html = '<span id="editable-post-name" title="' . $title . '">' . $post_name_abridged . '</span>';
1122         $display_link = str_replace(array('%pagename%','%postname%'), $post_name_html, $permalink);
1123         $view_link = str_replace(array('%pagename%','%postname%'), $post_name, $permalink);
1124         $return =  '<strong>' . __('Permalink:') . "</strong>\n";
1125         $return .= '<span id="sample-permalink">' . $display_link . "</span>\n";
1126         $return .= '&lrm;'; // Fix bi-directional text display defect in RTL languages.
1127         $return .= '<span id="edit-slug-buttons"><a href="#post_name" class="edit-slug button hide-if-no-js" onclick="editPermalink(' . $id . '); return false;">' . __('Edit') . "</a></span>\n";
1128         $return .= '<span id="editable-post-name-full">' . $post_name . "</span>\n";
1129         if ( isset($view_post) )
1130                 $return .= "<span id='view-post-btn'><a href='$view_link' class='button' target='_blank'>$view_post</a></span>\n";
1131
1132         $return = apply_filters('get_sample_permalink_html', $return, $id, $new_title, $new_slug);
1133
1134         return $return;
1135 }
1136
1137 /**
1138  * Output HTML for the post thumbnail meta-box.
1139  *
1140  * @since 2.9.0
1141  *
1142  * @param int $thumbnail_id ID of the attachment used for thumbnail
1143  * @return string html
1144  */
1145 function _wp_post_thumbnail_html( $thumbnail_id = NULL ) {
1146         global $content_width, $_wp_additional_image_sizes, $post_ID;
1147         $set_thumbnail_link = '<p class="hide-if-no-js"><a title="' . esc_attr__( 'Set featured image' ) . '" href="' . esc_url( get_upload_iframe_src('image') ) . '" id="set-post-thumbnail" class="thickbox">%s</a></p>';
1148         $content = sprintf($set_thumbnail_link, esc_html__( 'Set featured image' ));
1149
1150         if ( $thumbnail_id && get_post( $thumbnail_id ) ) {
1151                 $old_content_width = $content_width;
1152                 $content_width = 266;
1153                 if ( !isset( $_wp_additional_image_sizes['post-thumbnail'] ) )
1154                         $thumbnail_html = wp_get_attachment_image( $thumbnail_id, array( $content_width, $content_width ) );
1155                 else
1156                         $thumbnail_html = wp_get_attachment_image( $thumbnail_id, 'post-thumbnail' );
1157                 if ( !empty( $thumbnail_html ) ) {
1158                         $ajax_nonce = wp_create_nonce( "set_post_thumbnail-$post_ID" );
1159                         $content = sprintf($set_thumbnail_link, $thumbnail_html);
1160                         $content .= '<p class="hide-if-no-js"><a href="#" id="remove-post-thumbnail" onclick="WPRemoveThumbnail(\'' . $ajax_nonce . '\');return false;">' . esc_html__( 'Remove featured image' ) . '</a></p>';
1161                 }
1162                 $content_width = $old_content_width;
1163         }
1164
1165         return apply_filters( 'admin_post_thumbnail_html', $content );
1166 }
1167
1168 /**
1169  * Check to see if the post is currently being edited by another user.
1170  *
1171  * @since 2.5.0
1172  *
1173  * @param int $post_id ID of the post to check for editing
1174  * @return bool|int False: not locked or locked by current user. Int: user ID of user with lock.
1175  */
1176 function wp_check_post_lock( $post_id ) {
1177         if ( !$post = get_post( $post_id ) )
1178                 return false;
1179
1180         $lock = get_post_meta( $post->ID, '_edit_lock', true );
1181         $last = get_post_meta( $post->ID, '_edit_last', true );
1182
1183         $time_window = apply_filters( 'wp_check_post_lock_window', AUTOSAVE_INTERVAL * 2 );
1184
1185         if ( $lock && $lock > time() - $time_window && $last != get_current_user_id() )
1186                 return $last;
1187         return false;
1188 }
1189
1190 /**
1191  * Mark the post as currently being edited by the current user
1192  *
1193  * @since 2.5.0
1194  *
1195  * @param int $post_id ID of the post to being edited
1196  * @return bool Returns false if the post doesn't exist of there is no current user
1197  */
1198 function wp_set_post_lock( $post_id ) {
1199         if ( !$post = get_post( $post_id ) )
1200                 return false;
1201         if ( 0 == get_current_user_id() )
1202                 return false;
1203
1204         $now = time();
1205
1206         update_post_meta( $post->ID, '_edit_lock', $now );
1207 }
1208
1209 /**
1210  * Outputs the notice message to say that someone else is editing this post at the moment.
1211  *
1212  * @since 2.8.5
1213  * @return none
1214  */
1215 function _admin_notice_post_locked() {
1216         global $post;
1217         $last_user = get_userdata( get_post_meta( $post->ID, '_edit_last', true ) );
1218         $last_user_name = $last_user ? $last_user->display_name : __('Somebody');
1219
1220         switch ($post->post_type) {
1221                 case 'post':
1222                         $message = __( 'Warning: %s is currently editing this post' );
1223                         break;
1224                 case 'page':
1225                         $message = __( 'Warning: %s is currently editing this page' );
1226                         break;
1227                 default:
1228                         $message = __( 'Warning: %s is currently editing this.' );
1229         }
1230
1231         $message = sprintf( $message, esc_html( $last_user_name ) );
1232         echo "<div class='error'><p>$message</p></div>";
1233 }
1234
1235 /**
1236  * Creates autosave data for the specified post from $_POST data.
1237  *
1238  * @package WordPress
1239  * @subpackage Post_Revisions
1240  * @since 2.6.0
1241  *
1242  * @uses _wp_translate_postdata()
1243  * @uses _wp_post_revision_fields()
1244  *
1245  * @return unknown
1246  */
1247 function wp_create_post_autosave( $post_id ) {
1248         $translated = _wp_translate_postdata( true );
1249         if ( is_wp_error( $translated ) )
1250                 return $translated;
1251
1252         // Only store one autosave.  If there is already an autosave, overwrite it.
1253         if ( $old_autosave = wp_get_post_autosave( $post_id ) ) {
1254                 $new_autosave = _wp_post_revision_fields( $_POST, true );
1255                 $new_autosave['ID'] = $old_autosave->ID;
1256                 $new_autosave['post_author'] = get_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 }