WordPress 4.7
[autoinstalls/wordpress.git] / wp-admin / includes / ms.php
1 <?php
2 /**
3  * Multisite administration functions.
4  *
5  * @package WordPress
6  * @subpackage Multisite
7  * @since 3.0.0
8  */
9
10 /**
11  * Determine if uploaded file exceeds space quota.
12  *
13  * @since 3.0.0
14  *
15  * @param array $file $_FILES array for a given file.
16  * @return array $_FILES array with 'error' key set if file exceeds quota. 'error' is empty otherwise.
17  */
18 function check_upload_size( $file ) {
19         if ( get_site_option( 'upload_space_check_disabled' ) )
20                 return $file;
21
22         if ( $file['error'] != '0' ) // there's already an error
23                 return $file;
24
25         if ( defined( 'WP_IMPORTING' ) )
26                 return $file;
27
28         $space_left = get_upload_space_available();
29
30         $file_size = filesize( $file['tmp_name'] );
31         if ( $space_left < $file_size ) {
32                 /* translators: 1: Required disk space in kilobytes */
33                 $file['error'] = sprintf( __( 'Not enough space to upload. %1$s KB needed.' ), number_format( ( $file_size - $space_left ) / KB_IN_BYTES ) );
34         }
35
36         if ( $file_size > ( KB_IN_BYTES * get_site_option( 'fileupload_maxk', 1500 ) ) ) {
37                 /* translators: 1: Maximum allowed file size in kilobytes */
38                 $file['error'] = sprintf( __( 'This file is too big. Files must be less than %1$s KB in size.' ), get_site_option( 'fileupload_maxk', 1500 ) );
39         }
40
41         if ( upload_is_user_over_quota( false ) ) {
42                 $file['error'] = __( 'You have used your space quota. Please delete files before uploading.' );
43         }
44
45         if ( $file['error'] != '0' && ! isset( $_POST['html-upload'] ) && ! wp_doing_ajax() ) {
46                 wp_die( $file['error'] . ' <a href="javascript:history.go(-1)">' . __( 'Back' ) . '</a>' );
47         }
48
49         return $file;
50 }
51
52 /**
53  * Delete a site.
54  *
55  * @since 3.0.0
56  *
57  * @global wpdb $wpdb WordPress database abstraction object.
58  *
59  * @param int  $blog_id Site ID.
60  * @param bool $drop    True if site's database tables should be dropped. Default is false.
61  */
62 function wpmu_delete_blog( $blog_id, $drop = false ) {
63         global $wpdb;
64
65         $switch = false;
66         if ( get_current_blog_id() != $blog_id ) {
67                 $switch = true;
68                 switch_to_blog( $blog_id );
69         }
70
71         $blog = get_site( $blog_id );
72         /**
73          * Fires before a site is deleted.
74          *
75          * @since MU
76          *
77          * @param int  $blog_id The site ID.
78          * @param bool $drop    True if site's table should be dropped. Default is false.
79          */
80         do_action( 'delete_blog', $blog_id, $drop );
81
82         $users = get_users( array( 'blog_id' => $blog_id, 'fields' => 'ids' ) );
83
84         // Remove users from this blog.
85         if ( ! empty( $users ) ) {
86                 foreach ( $users as $user_id ) {
87                         remove_user_from_blog( $user_id, $blog_id );
88                 }
89         }
90
91         update_blog_status( $blog_id, 'deleted', 1 );
92
93         $current_network = get_network();
94
95         // If a full blog object is not available, do not destroy anything.
96         if ( $drop && ! $blog ) {
97                 $drop = false;
98         }
99
100         // Don't destroy the initial, main, or root blog.
101         if ( $drop && ( 1 == $blog_id || is_main_site( $blog_id ) || ( $blog->path == $current_network->path && $blog->domain == $current_network->domain ) ) ) {
102                 $drop = false;
103         }
104
105         $upload_path = trim( get_option( 'upload_path' ) );
106
107         // If ms_files_rewriting is enabled and upload_path is empty, wp_upload_dir is not reliable.
108         if ( $drop && get_site_option( 'ms_files_rewriting' ) && empty( $upload_path ) ) {
109                 $drop = false;
110         }
111
112         if ( $drop ) {
113                 $uploads = wp_get_upload_dir();
114
115                 $tables = $wpdb->tables( 'blog' );
116                 /**
117                  * Filters the tables to drop when the site is deleted.
118                  *
119                  * @since MU
120                  *
121                  * @param array $tables  The site tables to be dropped.
122                  * @param int   $blog_id The ID of the site to drop tables for.
123                  */
124                 $drop_tables = apply_filters( 'wpmu_drop_tables', $tables, $blog_id );
125
126                 foreach ( (array) $drop_tables as $table ) {
127                         $wpdb->query( "DROP TABLE IF EXISTS `$table`" );
128                 }
129
130                 $wpdb->delete( $wpdb->blogs, array( 'blog_id' => $blog_id ) );
131
132                 /**
133                  * Filters the upload base directory to delete when the site is deleted.
134                  *
135                  * @since MU
136                  *
137                  * @param string $uploads['basedir'] Uploads path without subdirectory. @see wp_upload_dir()
138                  * @param int    $blog_id            The site ID.
139                  */
140                 $dir = apply_filters( 'wpmu_delete_blog_upload_dir', $uploads['basedir'], $blog_id );
141                 $dir = rtrim( $dir, DIRECTORY_SEPARATOR );
142                 $top_dir = $dir;
143                 $stack = array($dir);
144                 $index = 0;
145
146                 while ( $index < count( $stack ) ) {
147                         // Get indexed directory from stack
148                         $dir = $stack[$index];
149
150                         $dh = @opendir( $dir );
151                         if ( $dh ) {
152                                 while ( ( $file = @readdir( $dh ) ) !== false ) {
153                                         if ( $file == '.' || $file == '..' )
154                                                 continue;
155
156                                         if ( @is_dir( $dir . DIRECTORY_SEPARATOR . $file ) ) {
157                                                 $stack[] = $dir . DIRECTORY_SEPARATOR . $file;
158                                         } elseif ( @is_file( $dir . DIRECTORY_SEPARATOR . $file ) ) {
159                                                 @unlink( $dir . DIRECTORY_SEPARATOR . $file );
160                                         }
161                                 }
162                                 @closedir( $dh );
163                         }
164                         $index++;
165                 }
166
167                 $stack = array_reverse( $stack ); // Last added dirs are deepest
168                 foreach ( (array) $stack as $dir ) {
169                         if ( $dir != $top_dir)
170                         @rmdir( $dir );
171                 }
172
173                 clean_blog_cache( $blog );
174         }
175
176         if ( $switch )
177                 restore_current_blog();
178 }
179
180 /**
181  * Delete a user from the network and remove from all sites.
182  *
183  * @since 3.0.0
184  *
185  * @todo Merge with wp_delete_user() ?
186  *
187  * @global wpdb $wpdb WordPress database abstraction object.
188  *
189  * @param int $id The user ID.
190  * @return bool True if the user was deleted, otherwise false.
191  */
192 function wpmu_delete_user( $id ) {
193         global $wpdb;
194
195         if ( ! is_numeric( $id ) ) {
196                 return false;
197         }
198
199         $id = (int) $id;
200         $user = new WP_User( $id );
201
202         if ( !$user->exists() )
203                 return false;
204
205         // Global super-administrators are protected, and cannot be deleted.
206         $_super_admins = get_super_admins();
207         if ( in_array( $user->user_login, $_super_admins, true ) ) {
208                 return false;
209         }
210
211         /**
212          * Fires before a user is deleted from the network.
213          *
214          * @since MU
215          *
216          * @param int $id ID of the user about to be deleted from the network.
217          */
218         do_action( 'wpmu_delete_user', $id );
219
220         $blogs = get_blogs_of_user( $id );
221
222         if ( ! empty( $blogs ) ) {
223                 foreach ( $blogs as $blog ) {
224                         switch_to_blog( $blog->userblog_id );
225                         remove_user_from_blog( $id, $blog->userblog_id );
226
227                         $post_ids = $wpdb->get_col( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_author = %d", $id ) );
228                         foreach ( (array) $post_ids as $post_id ) {
229                                 wp_delete_post( $post_id );
230                         }
231
232                         // Clean links
233                         $link_ids = $wpdb->get_col( $wpdb->prepare( "SELECT link_id FROM $wpdb->links WHERE link_owner = %d", $id ) );
234
235                         if ( $link_ids ) {
236                                 foreach ( $link_ids as $link_id )
237                                         wp_delete_link( $link_id );
238                         }
239
240                         restore_current_blog();
241                 }
242         }
243
244         $meta = $wpdb->get_col( $wpdb->prepare( "SELECT umeta_id FROM $wpdb->usermeta WHERE user_id = %d", $id ) );
245         foreach ( $meta as $mid )
246                 delete_metadata_by_mid( 'user', $mid );
247
248         $wpdb->delete( $wpdb->users, array( 'ID' => $id ) );
249
250         clean_user_cache( $user );
251
252         /** This action is documented in wp-admin/includes/user.php */
253         do_action( 'deleted_user', $id );
254
255         return true;
256 }
257
258 /**
259  * Sends an email when a site administrator email address is changed.
260  *
261  * @since 3.0.0
262  *
263  * @param string $old_value The old email address. Not currently used.
264  * @param string $value     The new email address.
265  */
266 function update_option_new_admin_email( $old_value, $value ) {
267         if ( $value == get_option( 'admin_email' ) || !is_email( $value ) )
268                 return;
269
270         $hash = md5( $value. time() .mt_rand() );
271         $new_admin_email = array(
272                 'hash' => $hash,
273                 'newemail' => $value
274         );
275         update_option( 'adminhash', $new_admin_email );
276
277         $switched_locale = switch_to_locale( get_user_locale() );
278
279         /* translators: Do not translate USERNAME, ADMIN_URL, EMAIL, SITENAME, SITEURL: those are placeholders. */
280         $email_text = __( 'Howdy ###USERNAME###,
281
282 You recently requested to have the administration email address on
283 your site changed.
284
285 If this is correct, please click on the following link to change it:
286 ###ADMIN_URL###
287
288 You can safely ignore and delete this email if you do not want to
289 take this action.
290
291 This email has been sent to ###EMAIL###
292
293 Regards,
294 All at ###SITENAME###
295 ###SITEURL###' );
296
297         /**
298          * Filters the email text sent when the site admin email is changed.
299          *
300          * The following strings have a special meaning and will get replaced dynamically:
301          * ###USERNAME###  The current user's username.
302          * ###ADMIN_URL### The link to click on to confirm the email change.
303          * ###EMAIL###     The new email.
304          * ###SITENAME###  The name of the site.
305          * ###SITEURL###   The URL to the site.
306          *
307          * @since MU
308          *
309          * @param string $email_text      Text in the email.
310          * @param string $new_admin_email New admin email that the current administration email was changed to.
311          */
312         $content = apply_filters( 'new_admin_email_content', $email_text, $new_admin_email );
313
314         $current_user = wp_get_current_user();
315         $content = str_replace( '###USERNAME###', $current_user->user_login, $content );
316         $content = str_replace( '###ADMIN_URL###', esc_url( self_admin_url( 'options.php?adminhash='.$hash ) ), $content );
317         $content = str_replace( '###EMAIL###', $value, $content );
318         $content = str_replace( '###SITENAME###', get_site_option( 'site_name' ), $content );
319         $content = str_replace( '###SITEURL###', network_home_url(), $content );
320
321         wp_mail( $value, sprintf( __( '[%s] New Admin Email Address' ), wp_specialchars_decode( get_option( 'blogname' ) ) ), $content );
322
323         if ( $switched_locale ) {
324                 restore_previous_locale();
325         }
326 }
327
328 /**
329  * Sends an email when an email address change is requested.
330  *
331  * @since 3.0.0
332  *
333  * @global WP_Error $errors WP_Error object.
334  * @global wpdb     $wpdb   WordPress database object.
335  */
336 function send_confirmation_on_profile_email() {
337         global $errors, $wpdb;
338         $current_user = wp_get_current_user();
339         if ( ! is_object($errors) )
340                 $errors = new WP_Error();
341
342         if ( $current_user->ID != $_POST['user_id'] )
343                 return false;
344
345         if ( $current_user->user_email != $_POST['email'] ) {
346                 if ( !is_email( $_POST['email'] ) ) {
347                         $errors->add( 'user_email', __( "<strong>ERROR</strong>: The email address isn&#8217;t correct." ), array( 'form-field' => 'email' ) );
348                         return;
349                 }
350
351                 if ( $wpdb->get_var( $wpdb->prepare( "SELECT user_email FROM {$wpdb->users} WHERE user_email=%s", $_POST['email'] ) ) ) {
352                         $errors->add( 'user_email', __( "<strong>ERROR</strong>: The email address is already used." ), array( 'form-field' => 'email' ) );
353                         delete_user_meta( $current_user->ID, '_new_email' );
354                         return;
355                 }
356
357                 $hash = md5( $_POST['email'] . time() . mt_rand() );
358                 $new_user_email = array(
359                         'hash' => $hash,
360                         'newemail' => $_POST['email']
361                 );
362                 update_user_meta( $current_user->ID, '_new_email', $new_user_email );
363
364                 $switched_locale = switch_to_locale( get_user_locale() );
365
366                 /* translators: Do not translate USERNAME, ADMIN_URL, EMAIL, SITENAME, SITEURL: those are placeholders. */
367                 $email_text = __( 'Howdy ###USERNAME###,
368
369 You recently requested to have the email address on your account changed.
370
371 If this is correct, please click on the following link to change it:
372 ###ADMIN_URL###
373
374 You can safely ignore and delete this email if you do not want to
375 take this action.
376
377 This email has been sent to ###EMAIL###
378
379 Regards,
380 All at ###SITENAME###
381 ###SITEURL###' );
382
383                 /**
384                  * Filters the email text sent when a user changes emails.
385                  *
386                  * The following strings have a special meaning and will get replaced dynamically:
387                  * ###USERNAME###  The current user's username.
388                  * ###ADMIN_URL### The link to click on to confirm the email change.
389                  * ###EMAIL###     The new email.
390                  * ###SITENAME###  The name of the site.
391                  * ###SITEURL###   The URL to the site.
392                  *
393                  * @since MU
394                  *
395                  * @param string $email_text     Text in the email.
396                  * @param string $new_user_email New user email that the current user has changed to.
397                  */
398                 $content = apply_filters( 'new_user_email_content', $email_text, $new_user_email );
399
400                 $content = str_replace( '###USERNAME###', $current_user->user_login, $content );
401                 $content = str_replace( '###ADMIN_URL###', esc_url( self_admin_url( 'profile.php?newuseremail=' . $hash ) ), $content );
402                 $content = str_replace( '###EMAIL###', $_POST['email'], $content);
403                 $content = str_replace( '###SITENAME###', get_site_option( 'site_name' ), $content );
404                 $content = str_replace( '###SITEURL###', network_home_url(), $content );
405
406                 wp_mail( $_POST['email'], sprintf( __( '[%s] New Email Address' ), wp_specialchars_decode( get_option( 'blogname' ) ) ), $content );
407                 $_POST['email'] = $current_user->user_email;
408
409                 if ( $switched_locale ) {
410                         restore_previous_locale();
411                 }
412         }
413 }
414
415 /**
416  * Adds an admin notice alerting the user to check for confirmation email
417  * after email address change.
418  *
419  * @since 3.0.0
420  *
421  * @global string $pagenow
422  */
423 function new_user_email_admin_notice() {
424         global $pagenow;
425         if ( 'profile.php' === $pagenow && isset( $_GET['updated'] ) && $email = get_user_meta( get_current_user_id(), '_new_email', true ) ) {
426                 /* translators: %s: New email address */
427                 echo '<div class="notice notice-info"><p>' . sprintf( __( 'Your email address has not been updated yet. Please check your inbox at %s for a confirmation email.' ), '<code>' . esc_html( $email['newemail'] ) . '</code>' ) . '</p></div>';
428         }
429 }
430
431 /**
432  * Check whether a site has used its allotted upload space.
433  *
434  * @since MU
435  *
436  * @param bool $echo Optional. If $echo is set and the quota is exceeded, a warning message is echoed. Default is true.
437  * @return bool True if user is over upload space quota, otherwise false.
438  */
439 function upload_is_user_over_quota( $echo = true ) {
440         if ( get_site_option( 'upload_space_check_disabled' ) )
441                 return false;
442
443         $space_allowed = get_space_allowed();
444         if ( ! is_numeric( $space_allowed ) ) {
445                 $space_allowed = 10; // Default space allowed is 10 MB
446         }
447         $space_used = get_space_used();
448
449         if ( ( $space_allowed - $space_used ) < 0 ) {
450                 if ( $echo )
451                         _e( 'Sorry, you have used your space allocation. Please delete some files to upload more files.' );
452                 return true;
453         } else {
454                 return false;
455         }
456 }
457
458 /**
459  * Displays the amount of disk space used by the current site. Not used in core.
460  *
461  * @since MU
462  */
463 function display_space_usage() {
464         $space_allowed = get_space_allowed();
465         $space_used = get_space_used();
466
467         $percent_used = ( $space_used / $space_allowed ) * 100;
468
469         if ( $space_allowed > 1000 ) {
470                 $space = number_format( $space_allowed / KB_IN_BYTES );
471                 /* translators: Gigabytes */
472                 $space .= __( 'GB' );
473         } else {
474                 $space = number_format( $space_allowed );
475                 /* translators: Megabytes */
476                 $space .= __( 'MB' );
477         }
478         ?>
479         <strong><?php
480                 /* translators: Storage space that's been used. 1: Percentage of used space, 2: Total space allowed in megabytes or gigabytes */
481                 printf( __( 'Used: %1$s%% of %2$s' ), number_format( $percent_used ), $space );
482         ?></strong>
483         <?php
484 }
485
486 /**
487  * Get the remaining upload space for this site.
488  *
489  * @since MU
490  *
491  * @param int $size Current max size in bytes
492  * @return int Max size in bytes
493  */
494 function fix_import_form_size( $size ) {
495         if ( upload_is_user_over_quota( false ) ) {
496                 return 0;
497         }
498         $available = get_upload_space_available();
499         return min( $size, $available );
500 }
501
502 /**
503  * Displays the site upload space quota setting form on the Edit Site Settings screen.
504  *
505  * @since 3.0.0
506  *
507  * @param int $id The ID of the site to display the setting for.
508  */
509 function upload_space_setting( $id ) {
510         switch_to_blog( $id );
511         $quota = get_option( 'blog_upload_space' );
512         restore_current_blog();
513
514         if ( !$quota )
515                 $quota = '';
516
517         ?>
518         <tr>
519                 <th><label for="blog-upload-space-number"><?php _e( 'Site Upload Space Quota' ); ?></label></th>
520                 <td>
521                         <input type="number" step="1" min="0" style="width: 100px" name="option[blog_upload_space]" id="blog-upload-space-number" aria-describedby="blog-upload-space-desc" value="<?php echo $quota; ?>" />
522                         <span id="blog-upload-space-desc"><span class="screen-reader-text"><?php _e( 'Size in megabytes' ); ?></span> <?php _e( 'MB (Leave blank for network default)' ); ?></span>
523                 </td>
524         </tr>
525         <?php
526 }
527
528 /**
529  * Update the status of a user in the database.
530  *
531  * Used in core to mark a user as spam or "ham" (not spam) in Multisite.
532  *
533  * @since 3.0.0
534  *
535  * @global wpdb $wpdb WordPress database abstraction object.
536  *
537  * @param int    $id         The user ID.
538  * @param string $pref       The column in the wp_users table to update the user's status
539  *                           in (presumably user_status, spam, or deleted).
540  * @param int    $value      The new status for the user.
541  * @param null   $deprecated Deprecated as of 3.0.2 and should not be used.
542  * @return int   The initially passed $value.
543  */
544 function update_user_status( $id, $pref, $value, $deprecated = null ) {
545         global $wpdb;
546
547         if ( null !== $deprecated )
548                 _deprecated_argument( __FUNCTION__, '3.0.2' );
549
550         $wpdb->update( $wpdb->users, array( sanitize_key( $pref ) => $value ), array( 'ID' => $id ) );
551
552         $user = new WP_User( $id );
553         clean_user_cache( $user );
554
555         if ( $pref == 'spam' ) {
556                 if ( $value == 1 ) {
557                         /**
558                          * Fires after the user is marked as a SPAM user.
559                          *
560                          * @since 3.0.0
561                          *
562                          * @param int $id ID of the user marked as SPAM.
563                          */
564                         do_action( 'make_spam_user', $id );
565                 } else {
566                         /**
567                          * Fires after the user is marked as a HAM user. Opposite of SPAM.
568                          *
569                          * @since 3.0.0
570                          *
571                          * @param int $id ID of the user marked as HAM.
572                          */
573                         do_action( 'make_ham_user', $id );
574                 }
575         }
576
577         return $value;
578 }
579
580 /**
581  * Cleans the user cache for a specific user.
582  *
583  * @since 3.0.0
584  *
585  * @param int $id The user ID.
586  * @return bool|int The ID of the refreshed user or false if the user does not exist.
587  */
588 function refresh_user_details( $id ) {
589         $id = (int) $id;
590
591         if ( !$user = get_userdata( $id ) )
592                 return false;
593
594         clean_user_cache( $user );
595
596         return $id;
597 }
598
599 /**
600  * Returns the language for a language code.
601  *
602  * @since 3.0.0
603  *
604  * @param string $code Optional. The two-letter language code. Default empty.
605  * @return string The language corresponding to $code if it exists. If it does not exist,
606  *                then the first two letters of $code is returned.
607  */
608 function format_code_lang( $code = '' ) {
609         $code = strtolower( substr( $code, 0, 2 ) );
610         $lang_codes = array(
611                 'aa' => 'Afar', 'ab' => 'Abkhazian', 'af' => 'Afrikaans', 'ak' => 'Akan', 'sq' => 'Albanian', 'am' => 'Amharic', 'ar' => 'Arabic', 'an' => 'Aragonese', 'hy' => 'Armenian', 'as' => 'Assamese', 'av' => 'Avaric', 'ae' => 'Avestan', 'ay' => 'Aymara', 'az' => 'Azerbaijani', 'ba' => 'Bashkir', 'bm' => 'Bambara', 'eu' => 'Basque', 'be' => 'Belarusian', 'bn' => 'Bengali',
612                 'bh' => 'Bihari', 'bi' => 'Bislama', 'bs' => 'Bosnian', 'br' => 'Breton', 'bg' => 'Bulgarian', 'my' => 'Burmese', 'ca' => 'Catalan; Valencian', 'ch' => 'Chamorro', 'ce' => 'Chechen', 'zh' => 'Chinese', 'cu' => 'Church Slavic; Old Slavonic; Church Slavonic; Old Bulgarian; Old Church Slavonic', 'cv' => 'Chuvash', 'kw' => 'Cornish', 'co' => 'Corsican', 'cr' => 'Cree',
613                 'cs' => 'Czech', 'da' => 'Danish', 'dv' => 'Divehi; Dhivehi; Maldivian', 'nl' => 'Dutch; Flemish', 'dz' => 'Dzongkha', 'en' => 'English', 'eo' => 'Esperanto', 'et' => 'Estonian', 'ee' => 'Ewe', 'fo' => 'Faroese', 'fj' => 'Fijjian', 'fi' => 'Finnish', 'fr' => 'French', 'fy' => 'Western Frisian', 'ff' => 'Fulah', 'ka' => 'Georgian', 'de' => 'German', 'gd' => 'Gaelic; Scottish Gaelic',
614                 'ga' => 'Irish', 'gl' => 'Galician', 'gv' => 'Manx', 'el' => 'Greek, Modern', 'gn' => 'Guarani', 'gu' => 'Gujarati', 'ht' => 'Haitian; Haitian Creole', 'ha' => 'Hausa', 'he' => 'Hebrew', 'hz' => 'Herero', 'hi' => 'Hindi', 'ho' => 'Hiri Motu', 'hu' => 'Hungarian', 'ig' => 'Igbo', 'is' => 'Icelandic', 'io' => 'Ido', 'ii' => 'Sichuan Yi', 'iu' => 'Inuktitut', 'ie' => 'Interlingue',
615                 'ia' => 'Interlingua (International Auxiliary Language Association)', 'id' => 'Indonesian', 'ik' => 'Inupiaq', 'it' => 'Italian', 'jv' => 'Javanese', 'ja' => 'Japanese', 'kl' => 'Kalaallisut; Greenlandic', 'kn' => 'Kannada', 'ks' => 'Kashmiri', 'kr' => 'Kanuri', 'kk' => 'Kazakh', 'km' => 'Central Khmer', 'ki' => 'Kikuyu; Gikuyu', 'rw' => 'Kinyarwanda', 'ky' => 'Kirghiz; Kyrgyz',
616                 'kv' => 'Komi', 'kg' => 'Kongo', 'ko' => 'Korean', 'kj' => 'Kuanyama; Kwanyama', 'ku' => 'Kurdish', 'lo' => 'Lao', 'la' => 'Latin', 'lv' => 'Latvian', 'li' => 'Limburgan; Limburger; Limburgish', 'ln' => 'Lingala', 'lt' => 'Lithuanian', 'lb' => 'Luxembourgish; Letzeburgesch', 'lu' => 'Luba-Katanga', 'lg' => 'Ganda', 'mk' => 'Macedonian', 'mh' => 'Marshallese', 'ml' => 'Malayalam',
617                 'mi' => 'Maori', 'mr' => 'Marathi', 'ms' => 'Malay', 'mg' => 'Malagasy', 'mt' => 'Maltese', 'mo' => 'Moldavian', 'mn' => 'Mongolian', 'na' => 'Nauru', 'nv' => 'Navajo; Navaho', 'nr' => 'Ndebele, South; South Ndebele', 'nd' => 'Ndebele, North; North Ndebele', 'ng' => 'Ndonga', 'ne' => 'Nepali', 'nn' => 'Norwegian Nynorsk; Nynorsk, Norwegian', 'nb' => 'Bokmål, Norwegian, Norwegian Bokmål',
618                 'no' => 'Norwegian', 'ny' => 'Chichewa; Chewa; Nyanja', 'oc' => 'Occitan, Provençal', 'oj' => 'Ojibwa', 'or' => 'Oriya', 'om' => 'Oromo', 'os' => 'Ossetian; Ossetic', 'pa' => 'Panjabi; Punjabi', 'fa' => 'Persian', 'pi' => 'Pali', 'pl' => 'Polish', 'pt' => 'Portuguese', 'ps' => 'Pushto', 'qu' => 'Quechua', 'rm' => 'Romansh', 'ro' => 'Romanian', 'rn' => 'Rundi', 'ru' => 'Russian',
619                 'sg' => 'Sango', 'sa' => 'Sanskrit', 'sr' => 'Serbian', 'hr' => 'Croatian', 'si' => 'Sinhala; Sinhalese', 'sk' => 'Slovak', 'sl' => 'Slovenian', 'se' => 'Northern Sami', 'sm' => 'Samoan', 'sn' => 'Shona', 'sd' => 'Sindhi', 'so' => 'Somali', 'st' => 'Sotho, Southern', 'es' => 'Spanish; Castilian', 'sc' => 'Sardinian', 'ss' => 'Swati', 'su' => 'Sundanese', 'sw' => 'Swahili',
620                 'sv' => 'Swedish', 'ty' => 'Tahitian', 'ta' => 'Tamil', 'tt' => 'Tatar', 'te' => 'Telugu', 'tg' => 'Tajik', 'tl' => 'Tagalog', 'th' => 'Thai', 'bo' => 'Tibetan', 'ti' => 'Tigrinya', 'to' => 'Tonga (Tonga Islands)', 'tn' => 'Tswana', 'ts' => 'Tsonga', 'tk' => 'Turkmen', 'tr' => 'Turkish', 'tw' => 'Twi', 'ug' => 'Uighur; Uyghur', 'uk' => 'Ukrainian', 'ur' => 'Urdu', 'uz' => 'Uzbek',
621                 've' => 'Venda', 'vi' => 'Vietnamese', 'vo' => 'Volapük', 'cy' => 'Welsh','wa' => 'Walloon','wo' => 'Wolof', 'xh' => 'Xhosa', 'yi' => 'Yiddish', 'yo' => 'Yoruba', 'za' => 'Zhuang; Chuang', 'zu' => 'Zulu' );
622
623         /**
624          * Filters the language codes.
625          *
626          * @since MU
627          *
628          * @param array  $lang_codes Key/value pair of language codes where key is the short version.
629          * @param string $code       A two-letter designation of the language.
630          */
631         $lang_codes = apply_filters( 'lang_codes', $lang_codes, $code );
632         return strtr( $code, $lang_codes );
633 }
634
635 /**
636  * Synchronize category and post tag slugs when global terms are enabled.
637  *
638  * @since 3.0.0
639  *
640  * @param object $term     The term.
641  * @param string $taxonomy The taxonomy for `$term`. Should be 'category' or 'post_tag', as these are
642  *                         the only taxonomies which are processed by this function; anything else
643  *                         will be returned untouched.
644  * @return object|array Returns `$term`, after filtering the 'slug' field with sanitize_title()
645  *                      if $taxonomy is 'category' or 'post_tag'.
646  */
647 function sync_category_tag_slugs( $term, $taxonomy ) {
648         if ( global_terms_enabled() && ( $taxonomy == 'category' || $taxonomy == 'post_tag' ) ) {
649                 if ( is_object( $term ) ) {
650                         $term->slug = sanitize_title( $term->name );
651                 } else {
652                         $term['slug'] = sanitize_title( $term['name'] );
653                 }
654         }
655         return $term;
656 }
657
658 /**
659  * Displays an access denied message when a user tries to view a site's dashboard they
660  * do not have access to.
661  *
662  * @since 3.2.0
663  * @access private
664  */
665 function _access_denied_splash() {
666         if ( ! is_user_logged_in() || is_network_admin() )
667                 return;
668
669         $blogs = get_blogs_of_user( get_current_user_id() );
670
671         if ( wp_list_filter( $blogs, array( 'userblog_id' => get_current_blog_id() ) ) )
672                 return;
673
674         $blog_name = get_bloginfo( 'name' );
675
676         if ( empty( $blogs ) )
677                 wp_die( sprintf( __( 'You attempted to access the "%1$s" dashboard, but you do not currently have privileges on this site. If you believe you should be able to access the "%1$s" dashboard, please contact your network administrator.' ), $blog_name ), 403 );
678
679         $output = '<p>' . sprintf( __( 'You attempted to access the "%1$s" dashboard, but you do not currently have privileges on this site. If you believe you should be able to access the "%1$s" dashboard, please contact your network administrator.' ), $blog_name ) . '</p>';
680         $output .= '<p>' . __( 'If you reached this screen by accident and meant to visit one of your own sites, here are some shortcuts to help you find your way.' ) . '</p>';
681
682         $output .= '<h3>' . __('Your Sites') . '</h3>';
683         $output .= '<table>';
684
685         foreach ( $blogs as $blog ) {
686                 $output .= '<tr>';
687                 $output .= "<td>{$blog->blogname}</td>";
688                 $output .= '<td><a href="' . esc_url( get_admin_url( $blog->userblog_id ) ) . '">' . __( 'Visit Dashboard' ) . '</a> | ' .
689                         '<a href="' . esc_url( get_home_url( $blog->userblog_id ) ). '">' . __( 'View Site' ) . '</a></td>';
690                 $output .= '</tr>';
691         }
692
693         $output .= '</table>';
694
695         wp_die( $output, 403 );
696 }
697
698 /**
699  * Checks if the current user has permissions to import new users.
700  *
701  * @since 3.0.0
702  *
703  * @param string $permission A permission to be checked. Currently not used.
704  * @return bool True if the user has proper permissions, false if they do not.
705  */
706 function check_import_new_users( $permission ) {
707         if ( !is_super_admin() )
708                 return false;
709         return true;
710 }
711 // See "import_allow_fetch_attachments" and "import_attachment_size_limit" filters too.
712
713 /**
714  * Generates and displays a drop-down of available languages.
715  *
716  * @since 3.0.0
717  *
718  * @param array  $lang_files Optional. An array of the language files. Default empty array.
719  * @param string $current    Optional. The current language code. Default empty.
720  */
721 function mu_dropdown_languages( $lang_files = array(), $current = '' ) {
722         $flag = false;
723         $output = array();
724
725         foreach ( (array) $lang_files as $val ) {
726                 $code_lang = basename( $val, '.mo' );
727
728                 if ( $code_lang == 'en_US' ) { // American English
729                         $flag = true;
730                         $ae = __( 'American English' );
731                         $output[$ae] = '<option value="' . esc_attr( $code_lang ) . '"' . selected( $current, $code_lang, false ) . '> ' . $ae . '</option>';
732                 } elseif ( $code_lang == 'en_GB' ) { // British English
733                         $flag = true;
734                         $be = __( 'British English' );
735                         $output[$be] = '<option value="' . esc_attr( $code_lang ) . '"' . selected( $current, $code_lang, false ) . '> ' . $be . '</option>';
736                 } else {
737                         $translated = format_code_lang( $code_lang );
738                         $output[$translated] = '<option value="' . esc_attr( $code_lang ) . '"' . selected( $current, $code_lang, false ) . '> ' . esc_html ( $translated ) . '</option>';
739                 }
740
741         }
742
743         if ( $flag === false ) // WordPress english
744                 $output[] = '<option value=""' . selected( $current, '', false ) . '>' . __( 'English' ) . "</option>";
745
746         // Order by name
747         uksort( $output, 'strnatcasecmp' );
748
749         /**
750          * Filters the languages available in the dropdown.
751          *
752          * @since MU
753          *
754          * @param array $output     HTML output of the dropdown.
755          * @param array $lang_files Available language files.
756          * @param string $current   The current language code.
757          */
758         $output = apply_filters( 'mu_dropdown_languages', $output, $lang_files, $current );
759
760         echo implode( "\n\t", $output );
761 }
762
763 /**
764  * Displays an admin notice to upgrade all sites after a core upgrade.
765  *
766  * @since 3.0.0
767  *
768  * @global int    $wp_db_version The version number of the database.
769  * @global string $pagenow
770  *
771  * @return false False if the current user is not a super admin.
772  */
773 function site_admin_notice() {
774         global $wp_db_version, $pagenow;
775
776         if ( ! is_super_admin() ) {
777                 return false;
778         }
779
780         if ( 'upgrade.php' == $pagenow ) {
781                 return;
782         }
783
784         if ( get_site_option( 'wpmu_upgrade_site' ) != $wp_db_version ) {
785                 echo "<div class='update-nag'>" . sprintf( __( 'Thank you for Updating! Please visit the <a href="%s">Upgrade Network</a> page to update all your sites.' ), esc_url( network_admin_url( 'upgrade.php' ) ) ) . "</div>";
786         }
787 }
788
789 /**
790  * Avoids a collision between a site slug and a permalink slug.
791  *
792  * In a subdirectory install this will make sure that a site and a post do not use the
793  * same subdirectory by checking for a site with the same name as a new post.
794  *
795  * @since 3.0.0
796  *
797  * @param array $data    An array of post data.
798  * @param array $postarr An array of posts. Not currently used.
799  * @return array The new array of post data after checking for collisions.
800  */
801 function avoid_blog_page_permalink_collision( $data, $postarr ) {
802         if ( is_subdomain_install() )
803                 return $data;
804         if ( $data['post_type'] != 'page' )
805                 return $data;
806         if ( !isset( $data['post_name'] ) || $data['post_name'] == '' )
807                 return $data;
808         if ( !is_main_site() )
809                 return $data;
810
811         $post_name = $data['post_name'];
812         $c = 0;
813         while( $c < 10 && get_id_from_blogname( $post_name ) ) {
814                 $post_name .= mt_rand( 1, 10 );
815                 $c ++;
816         }
817         if ( $post_name != $data['post_name'] ) {
818                 $data['post_name'] = $post_name;
819         }
820         return $data;
821 }
822
823 /**
824  * Handles the display of choosing a user's primary site.
825  *
826  * This displays the user's primary site and allows the user to choose
827  * which site is primary.
828  *
829  * @since 3.0.0
830  */
831 function choose_primary_blog() {
832         ?>
833         <table class="form-table">
834         <tr>
835         <?php /* translators: My sites label */ ?>
836                 <th scope="row"><label for="primary_blog"><?php _e( 'Primary Site' ); ?></label></th>
837                 <td>
838                 <?php
839                 $all_blogs = get_blogs_of_user( get_current_user_id() );
840                 $primary_blog = get_user_meta( get_current_user_id(), 'primary_blog', true );
841                 if ( count( $all_blogs ) > 1 ) {
842                         $found = false;
843                         ?>
844                         <select name="primary_blog" id="primary_blog">
845                                 <?php foreach ( (array) $all_blogs as $blog ) {
846                                         if ( $primary_blog == $blog->userblog_id )
847                                                 $found = true;
848                                         ?><option value="<?php echo $blog->userblog_id ?>"<?php selected( $primary_blog, $blog->userblog_id ); ?>><?php echo esc_url( get_home_url( $blog->userblog_id ) ) ?></option><?php
849                                 } ?>
850                         </select>
851                         <?php
852                         if ( !$found ) {
853                                 $blog = reset( $all_blogs );
854                                 update_user_meta( get_current_user_id(), 'primary_blog', $blog->userblog_id );
855                         }
856                 } elseif ( count( $all_blogs ) == 1 ) {
857                         $blog = reset( $all_blogs );
858                         echo esc_url( get_home_url( $blog->userblog_id ) );
859                         if ( $primary_blog != $blog->userblog_id ) // Set the primary blog again if it's out of sync with blog list.
860                                 update_user_meta( get_current_user_id(), 'primary_blog', $blog->userblog_id );
861                 } else {
862                         echo "N/A";
863                 }
864                 ?>
865                 </td>
866         </tr>
867         </table>
868         <?php
869 }
870
871 /**
872  * Whether or not we can edit this network from this page.
873  *
874  * By default editing of network is restricted to the Network Admin for that `$site_id`
875  * this allows for this to be overridden.
876  *
877  * @since 3.1.0
878  *
879  * @global wpdb $wpdb WordPress database abstraction object.
880  *
881  * @param int $site_id The network/site ID to check.
882  * @return bool True if network can be edited, otherwise false.
883  */
884 function can_edit_network( $site_id ) {
885         global $wpdb;
886
887         if ( $site_id == $wpdb->siteid )
888                 $result = true;
889         else
890                 $result = false;
891
892         /**
893          * Filters whether this network can be edited from this page.
894          *
895          * @since 3.1.0
896          *
897          * @param bool $result  Whether the network can be edited from this page.
898          * @param int  $site_id The network/site ID to check.
899          */
900         return apply_filters( 'can_edit_network', $result, $site_id );
901 }
902
903 /**
904  * Thickbox image paths for Network Admin.
905  *
906  * @since 3.1.0
907  *
908  * @access private
909  */
910 function _thickbox_path_admin_subfolder() {
911 ?>
912 <script type="text/javascript">
913 var tb_pathToImage = "<?php echo includes_url( 'js/thickbox/loadingAnimation.gif', 'relative' ); ?>";
914 </script>
915 <?php
916 }
917
918 /**
919  *
920  * @param array $users
921  */
922 function confirm_delete_users( $users ) {
923         $current_user = wp_get_current_user();
924         if ( ! is_array( $users ) || empty( $users ) ) {
925                 return false;
926         }
927         ?>
928         <h1><?php esc_html_e( 'Users' ); ?></h1>
929
930         <?php if ( 1 == count( $users ) ) : ?>
931                 <p><?php _e( 'You have chosen to delete the user from all networks and sites.' ); ?></p>
932         <?php else : ?>
933                 <p><?php _e( 'You have chosen to delete the following users from all networks and sites.' ); ?></p>
934         <?php endif; ?>
935
936         <form action="users.php?action=dodelete" method="post">
937         <input type="hidden" name="dodelete" />
938         <?php
939         wp_nonce_field( 'ms-users-delete' );
940         $site_admins = get_super_admins();
941         $admin_out = '<option value="' . esc_attr( $current_user->ID ) . '">' . $current_user->user_login . '</option>'; ?>
942         <table class="form-table">
943         <?php foreach ( ( $allusers = (array) $_POST['allusers'] ) as $user_id ) {
944                 if ( $user_id != '' && $user_id != '0' ) {
945                         $delete_user = get_userdata( $user_id );
946
947                         if ( ! current_user_can( 'delete_user', $delete_user->ID ) ) {
948                                 wp_die( sprintf( __( 'Warning! User %s cannot be deleted.' ), $delete_user->user_login ) );
949                         }
950
951                         if ( in_array( $delete_user->user_login, $site_admins ) ) {
952                                 wp_die( sprintf( __( 'Warning! User cannot be deleted. The user %s is a network administrator.' ), '<em>' . $delete_user->user_login . '</em>' ) );
953                         }
954                         ?>
955                         <tr>
956                                 <th scope="row"><?php echo $delete_user->user_login; ?>
957                                         <?php echo '<input type="hidden" name="user[]" value="' . esc_attr( $user_id ) . '" />' . "\n"; ?>
958                                 </th>
959                         <?php $blogs = get_blogs_of_user( $user_id, true );
960
961                         if ( ! empty( $blogs ) ) {
962                                 ?>
963                                 <td><fieldset><p><legend><?php printf(
964                                         /* translators: user login */
965                                         __( 'What should be done with content owned by %s?' ),
966                                         '<em>' . $delete_user->user_login . '</em>'
967                                 ); ?></legend></p>
968                                 <?php
969                                 foreach ( (array) $blogs as $key => $details ) {
970                                         $blog_users = get_users( array( 'blog_id' => $details->userblog_id, 'fields' => array( 'ID', 'user_login' ) ) );
971                                         if ( is_array( $blog_users ) && !empty( $blog_users ) ) {
972                                                 $user_site = "<a href='" . esc_url( get_home_url( $details->userblog_id ) ) . "'>{$details->blogname}</a>";
973                                                 $user_dropdown = '<label for="reassign_user" class="screen-reader-text">' . __( 'Select a user' ) . '</label>';
974                                                 $user_dropdown .= "<select name='blog[$user_id][$key]' id='reassign_user'>";
975                                                 $user_list = '';
976                                                 foreach ( $blog_users as $user ) {
977                                                         if ( ! in_array( $user->ID, $allusers ) ) {
978                                                                 $user_list .= "<option value='{$user->ID}'>{$user->user_login}</option>";
979                                                         }
980                                                 }
981                                                 if ( '' == $user_list ) {
982                                                         $user_list = $admin_out;
983                                                 }
984                                                 $user_dropdown .= $user_list;
985                                                 $user_dropdown .= "</select>\n";
986                                                 ?>
987                                                 <ul style="list-style:none;">
988                                                         <li><?php printf( __( 'Site: %s' ), $user_site ); ?></li>
989                                                         <li><label><input type="radio" id="delete_option0" name="delete[<?php echo $details->userblog_id . '][' . $delete_user->ID ?>]" value="delete" checked="checked" />
990                                                         <?php _e( 'Delete all content.' ); ?></label></li>
991                                                         <li><label><input type="radio" id="delete_option1" name="delete[<?php echo $details->userblog_id . '][' . $delete_user->ID ?>]" value="reassign" />
992                                                         <?php _e( 'Attribute all content to:' ); ?></label>
993                                                         <?php echo $user_dropdown; ?></li>
994                                                 </ul>
995                                                 <?php
996                                         }
997                                 }
998                                 echo "</fieldset></td></tr>";
999                         } else {
1000                                 ?>
1001                                 <td><fieldset><p><legend><?php _e( 'User has no sites or content and will be deleted.' ); ?></legend></p>
1002                         <?php } ?>
1003                         </tr>
1004                 <?php
1005                 }
1006         }
1007
1008         ?>
1009         </table>
1010         <?php
1011         /** This action is documented in wp-admin/users.php */
1012         do_action( 'delete_user_form', $current_user, $allusers );
1013
1014         if ( 1 == count( $users ) ) : ?>
1015                 <p><?php _e( 'Once you hit &#8220;Confirm Deletion&#8221;, the user will be permanently removed.' ); ?></p>
1016         <?php else : ?>
1017                 <p><?php _e( 'Once you hit &#8220;Confirm Deletion&#8221;, these users will be permanently removed.' ); ?></p>
1018         <?php endif;
1019
1020         submit_button( __('Confirm Deletion'), 'primary' );
1021         ?>
1022         </form>
1023         <?php
1024         return true;
1025 }
1026
1027 /**
1028  * Print JavaScript in the header on the Network Settings screen.
1029  *
1030  * @since 4.1.0
1031  */
1032 function network_settings_add_js() {
1033 ?>
1034 <script type="text/javascript">
1035 jQuery(document).ready( function($) {
1036         var languageSelect = $( '#WPLANG' );
1037         $( 'form' ).submit( function() {
1038                 // Don't show a spinner for English and installed languages,
1039                 // as there is nothing to download.
1040                 if ( ! languageSelect.find( 'option:selected' ).data( 'installed' ) ) {
1041                         $( '#submit', this ).after( '<span class="spinner language-install-spinner" />' );
1042                 }
1043         });
1044 });
1045 </script>
1046 <?php
1047 }
1048
1049 /**
1050  * Outputs the HTML for a network's "Edit Site" tabular interface.
1051  *
1052  * @since 4.6.0
1053  *
1054  * @param $args {
1055  *     Optional. Array or string of Query parameters. Default empty array.
1056  *
1057  *     @type int    $blog_id  The site ID. Default is the current site.
1058  *     @type array  $links    The tabs to include with (label|url|cap) keys.
1059  *     @type string $selected The ID of the selected link.
1060  * }
1061  */
1062 function network_edit_site_nav( $args = array() ) {
1063
1064         /**
1065          * Filters the links that appear on site-editing network pages.
1066          *
1067          * Default links: 'site-info', 'site-users', 'site-themes', and 'site-settings'.
1068          *
1069          * @since 4.6.0
1070          *
1071          * @param array $links {
1072          *     An array of link data representing individual network admin pages.
1073          *
1074          *     @type array $link_slug {
1075          *         An array of information about the individual link to a page.
1076          *
1077          *         $type string $label Label to use for the link.
1078          *         $type string $url   URL, relative to `network_admin_url()` to use for the link.
1079          *         $type string $cap   Capability required to see the link.
1080          *     }
1081          * }
1082          */
1083         $links = apply_filters( 'network_edit_site_nav_links', array(
1084                 'site-info'     => array( 'label' => __( 'Info' ),     'url' => 'site-info.php',     'cap' => 'manage_sites' ),
1085                 'site-users'    => array( 'label' => __( 'Users' ),    'url' => 'site-users.php',    'cap' => 'manage_sites' ),
1086                 'site-themes'   => array( 'label' => __( 'Themes' ),   'url' => 'site-themes.php',   'cap' => 'manage_sites' ),
1087                 'site-settings' => array( 'label' => __( 'Settings' ), 'url' => 'site-settings.php', 'cap' => 'manage_sites' )
1088         ) );
1089
1090         // Parse arguments
1091         $r = wp_parse_args( $args, array(
1092                 'blog_id'  => isset( $_GET['blog_id'] ) ? (int) $_GET['blog_id'] : 0,
1093                 'links'    => $links,
1094                 'selected' => 'site-info',
1095         ) );
1096
1097         // Setup the links array
1098         $screen_links = array();
1099
1100         // Loop through tabs
1101         foreach ( $r['links'] as $link_id => $link ) {
1102
1103                 // Skip link if user can't access
1104                 if ( ! current_user_can( $link['cap'], $r['blog_id'] ) ) {
1105                         continue;
1106                 }
1107
1108                 // Link classes
1109                 $classes = array( 'nav-tab' );
1110
1111                 // Selected is set by the parent OR assumed by the $pagenow global
1112                 if ( $r['selected'] === $link_id || $link['url'] === $GLOBALS['pagenow'] ) {
1113                         $classes[] = 'nav-tab-active';
1114                 }
1115
1116                 // Escape each class
1117                 $esc_classes = implode( ' ', $classes );
1118
1119                 // Get the URL for this link
1120                 $url = add_query_arg( array( 'id' => $r['blog_id'] ), network_admin_url( $link['url'] ) );
1121
1122                 // Add link to nav links
1123                 $screen_links[ $link_id ] = '<a href="' . esc_url( $url ) . '" id="' . esc_attr( $link_id ) . '" class="' . $esc_classes . '">' . esc_html( $link['label'] ) . '</a>';
1124         }
1125
1126         // All done!
1127         echo '<h2 class="nav-tab-wrapper wp-clearfix">';
1128         echo implode( '', $screen_links );
1129         echo '</h2>';
1130 }