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