WordPress 4.5
[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                 $file['error'] = sprintf( __( 'Not enough space to upload. %1$s KB needed.' ), number_format( ( $file_size - $space_left ) / KB_IN_BYTES ) );
33         }
34
35         if ( $file_size > ( KB_IN_BYTES * get_site_option( 'fileupload_maxk', 1500 ) ) ) {
36                 $file['error'] = sprintf( __( 'This file is too big. Files must be less than %1$s KB in size.' ), get_site_option( 'fileupload_maxk', 1500 ) );
37         }
38
39         if ( upload_is_user_over_quota( false ) ) {
40                 $file['error'] = __( 'You have used your space quota. Please delete files before uploading.' );
41         }
42
43         if ( $file['error'] != '0' && ! isset( $_POST['html-upload'] ) && ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX ) ) {
44                 wp_die( $file['error'] . ' <a href="javascript:history.go(-1)">' . __( 'Back' ) . '</a>' );
45         }
46
47         return $file;
48 }
49
50 /**
51  * Delete a site.
52  *
53  * @since 3.0.0
54  *
55  * @global wpdb $wpdb WordPress database abstraction object.
56  *
57  * @param int  $blog_id Site ID.
58  * @param bool $drop    True if site's database tables should be dropped. Default is false.
59  */
60 function wpmu_delete_blog( $blog_id, $drop = false ) {
61         global $wpdb;
62
63         $switch = false;
64         if ( get_current_blog_id() != $blog_id ) {
65                 $switch = true;
66                 switch_to_blog( $blog_id );
67         }
68
69         $blog = get_blog_details( $blog_id );
70         /**
71          * Fires before a site is deleted.
72          *
73          * @since MU
74          *
75          * @param int  $blog_id The site ID.
76          * @param bool $drop    True if site's table should be dropped. Default is false.
77          */
78         do_action( 'delete_blog', $blog_id, $drop );
79
80         $users = get_users( array( 'blog_id' => $blog_id, 'fields' => 'ids' ) );
81
82         // Remove users from this blog.
83         if ( ! empty( $users ) ) {
84                 foreach ( $users as $user_id ) {
85                         remove_user_from_blog( $user_id, $blog_id );
86                 }
87         }
88
89         update_blog_status( $blog_id, 'deleted', 1 );
90
91         $current_site = get_current_site();
92
93         // If a full blog object is not available, do not destroy anything.
94         if ( $drop && ! $blog ) {
95                 $drop = false;
96         }
97
98         // Don't destroy the initial, main, or root blog.
99         if ( $drop && ( 1 == $blog_id || is_main_site( $blog_id ) || ( $blog->path == $current_site->path && $blog->domain == $current_site->domain ) ) ) {
100                 $drop = false;
101         }
102
103         $upload_path = trim( get_option( 'upload_path' ) );
104
105         // If ms_files_rewriting is enabled and upload_path is empty, wp_upload_dir is not reliable.
106         if ( $drop && get_site_option( 'ms_files_rewriting' ) && empty( $upload_path ) ) {
107                 $drop = false;
108         }
109
110         if ( $drop ) {
111                 $uploads = wp_get_upload_dir();
112
113                 $tables = $wpdb->tables( 'blog' );
114                 /**
115                  * Filter the tables to drop when the site is deleted.
116                  *
117                  * @since MU
118                  *
119                  * @param array $tables  The site tables to be dropped.
120                  * @param int   $blog_id The ID of the site to drop tables for.
121                  */
122                 $drop_tables = apply_filters( 'wpmu_drop_tables', $tables, $blog_id );
123
124                 foreach ( (array) $drop_tables as $table ) {
125                         $wpdb->query( "DROP TABLE IF EXISTS `$table`" );
126                 }
127
128                 $wpdb->delete( $wpdb->blogs, array( 'blog_id' => $blog_id ) );
129
130                 /**
131                  * Filter the upload base directory to delete when the site is deleted.
132                  *
133                  * @since MU
134                  *
135                  * @param string $uploads['basedir'] Uploads path without subdirectory. @see wp_upload_dir()
136                  * @param int    $blog_id            The site ID.
137                  */
138                 $dir = apply_filters( 'wpmu_delete_blog_upload_dir', $uploads['basedir'], $blog_id );
139                 $dir = rtrim( $dir, DIRECTORY_SEPARATOR );
140                 $top_dir = $dir;
141                 $stack = array($dir);
142                 $index = 0;
143
144                 while ( $index < count( $stack ) ) {
145                         // Get indexed directory from stack
146                         $dir = $stack[$index];
147
148                         $dh = @opendir( $dir );
149                         if ( $dh ) {
150                                 while ( ( $file = @readdir( $dh ) ) !== false ) {
151                                         if ( $file == '.' || $file == '..' )
152                                                 continue;
153
154                                         if ( @is_dir( $dir . DIRECTORY_SEPARATOR . $file ) ) {
155                                                 $stack[] = $dir . DIRECTORY_SEPARATOR . $file;
156                                         } elseif ( @is_file( $dir . DIRECTORY_SEPARATOR . $file ) ) {
157                                                 @unlink( $dir . DIRECTORY_SEPARATOR . $file );
158                                         }
159                                 }
160                                 @closedir( $dh );
161                         }
162                         $index++;
163                 }
164
165                 $stack = array_reverse( $stack ); // Last added dirs are deepest
166                 foreach ( (array) $stack as $dir ) {
167                         if ( $dir != $top_dir)
168                         @rmdir( $dir );
169                 }
170
171                 clean_blog_cache( $blog );
172         }
173
174         if ( $switch )
175                 restore_current_blog();
176 }
177
178 /**
179  * Delete a user from the network and remove from all sites.
180  *
181  * @since 3.0.0
182  *
183  * @todo Merge with wp_delete_user() ?
184  *
185  * @global wpdb $wpdb WordPress database abstraction object.
186  *
187  * @param int $id The user ID.
188  * @return bool True if the user was deleted, otherwise false.
189  */
190 function wpmu_delete_user( $id ) {
191         global $wpdb;
192
193         if ( ! is_numeric( $id ) ) {
194                 return false;
195         }
196
197         $id = (int) $id;
198         $user = new WP_User( $id );
199
200         if ( !$user->exists() )
201                 return false;
202
203         // Global super-administrators are protected, and cannot be deleted.
204         $_super_admins = get_super_admins();
205         if ( in_array( $user->user_login, $_super_admins, true ) ) {
206                 return false;
207         }
208
209         /**
210          * Fires before a user is deleted from the network.
211          *
212          * @since MU
213          *
214          * @param int $id ID of the user about to be deleted from the network.
215          */
216         do_action( 'wpmu_delete_user', $id );
217
218         $blogs = get_blogs_of_user( $id );
219
220         if ( ! empty( $blogs ) ) {
221                 foreach ( $blogs as $blog ) {
222                         switch_to_blog( $blog->userblog_id );
223                         remove_user_from_blog( $id, $blog->userblog_id );
224
225                         $post_ids = $wpdb->get_col( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_author = %d", $id ) );
226                         foreach ( (array) $post_ids as $post_id ) {
227                                 wp_delete_post( $post_id );
228                         }
229
230                         // Clean links
231                         $link_ids = $wpdb->get_col( $wpdb->prepare( "SELECT link_id FROM $wpdb->links WHERE link_owner = %d", $id ) );
232
233                         if ( $link_ids ) {
234                                 foreach ( $link_ids as $link_id )
235                                         wp_delete_link( $link_id );
236                         }
237
238                         restore_current_blog();
239                 }
240         }
241
242         $meta = $wpdb->get_col( $wpdb->prepare( "SELECT umeta_id FROM $wpdb->usermeta WHERE user_id = %d", $id ) );
243         foreach ( $meta as $mid )
244                 delete_metadata_by_mid( 'user', $mid );
245
246         $wpdb->delete( $wpdb->users, array( 'ID' => $id ) );
247
248         clean_user_cache( $user );
249
250         /** This action is documented in wp-admin/includes/user.php */
251         do_action( 'deleted_user', $id );
252
253         return true;
254 }
255
256 /**
257  * Sends an email when a site administrator email address is changed.
258  *
259  * @since 3.0.0
260  *
261  * @param string $old_value The old email address. Not currently used.
262  * @param string $value     The new email address.
263  */
264 function update_option_new_admin_email( $old_value, $value ) {
265         if ( $value == get_option( 'admin_email' ) || !is_email( $value ) )
266                 return;
267
268         $hash = md5( $value. time() .mt_rand() );
269         $new_admin_email = array(
270                 'hash' => $hash,
271                 'newemail' => $value
272         );
273         update_option( 'adminhash', $new_admin_email );
274
275         /* translators: Do not translate USERNAME, ADMIN_URL, EMAIL, SITENAME, SITEURL: those are placeholders. */
276         $email_text = __( 'Howdy ###USERNAME###,
277
278 You recently requested to have the administration email address on
279 your site changed.
280
281 If this is correct, please click on the following link to change it:
282 ###ADMIN_URL###
283
284 You can safely ignore and delete this email if you do not want to
285 take this action.
286
287 This email has been sent to ###EMAIL###
288
289 Regards,
290 All at ###SITENAME###
291 ###SITEURL###' );
292
293         /**
294          * Filter the email text sent when the site admin email is changed.
295          *
296          * The following strings have a special meaning and will get replaced dynamically:
297          * ###USERNAME###  The current user's username.
298          * ###ADMIN_URL### The link to click on to confirm the email change.
299          * ###EMAIL###     The new email.
300          * ###SITENAME###  The name of the site.
301          * ###SITEURL###   The URL to the site.
302          *
303          * @since MU
304          *
305          * @param string $email_text      Text in the email.
306          * @param string $new_admin_email New admin email that the current administration email was changed to.
307          */
308         $content = apply_filters( 'new_admin_email_content', $email_text, $new_admin_email );
309
310         $current_user = wp_get_current_user();
311         $content = str_replace( '###USERNAME###', $current_user->user_login, $content );
312         $content = str_replace( '###ADMIN_URL###', esc_url( self_admin_url( 'options.php?adminhash='.$hash ) ), $content );
313         $content = str_replace( '###EMAIL###', $value, $content );
314         $content = str_replace( '###SITENAME###', get_site_option( 'site_name' ), $content );
315         $content = str_replace( '###SITEURL###', network_home_url(), $content );
316
317         wp_mail( $value, sprintf( __( '[%s] New Admin Email Address' ), wp_specialchars_decode( get_option( 'blogname' ) ) ), $content );
318 }
319
320 /**
321  * Sends an email when an email address change is requested.
322  *
323  * @since 3.0.0
324  *
325  * @global WP_Error $errors WP_Error object.
326  * @global wpdb     $wpdb   WordPress database object.
327  */
328 function send_confirmation_on_profile_email() {
329         global $errors, $wpdb;
330         $current_user = wp_get_current_user();
331         if ( ! is_object($errors) )
332                 $errors = new WP_Error();
333
334         if ( $current_user->ID != $_POST['user_id'] )
335                 return false;
336
337         if ( $current_user->user_email != $_POST['email'] ) {
338                 if ( !is_email( $_POST['email'] ) ) {
339                         $errors->add( 'user_email', __( "<strong>ERROR</strong>: The email address isn&#8217;t correct." ), array( 'form-field' => 'email' ) );
340                         return;
341                 }
342
343                 if ( $wpdb->get_var( $wpdb->prepare( "SELECT user_email FROM {$wpdb->users} WHERE user_email=%s", $_POST['email'] ) ) ) {
344                         $errors->add( 'user_email', __( "<strong>ERROR</strong>: The email address is already used." ), array( 'form-field' => 'email' ) );
345                         delete_user_meta( $current_user->ID, '_new_email' );
346                         return;
347                 }
348
349                 $hash = md5( $_POST['email'] . time() . mt_rand() );
350                 $new_user_email = array(
351                         'hash' => $hash,
352                         'newemail' => $_POST['email']
353                 );
354                 update_user_meta( $current_user->ID, '_new_email', $new_user_email );
355
356                 /* translators: Do not translate USERNAME, ADMIN_URL, EMAIL, SITENAME, SITEURL: those are placeholders. */
357                 $email_text = __( 'Howdy ###USERNAME###,
358
359 You recently requested to have the email address on your account changed.
360
361 If this is correct, please click on the following link to change it:
362 ###ADMIN_URL###
363
364 You can safely ignore and delete this email if you do not want to
365 take this action.
366
367 This email has been sent to ###EMAIL###
368
369 Regards,
370 All at ###SITENAME###
371 ###SITEURL###' );
372
373                 /**
374                  * Filter the email text sent when a user changes emails.
375                  *
376                  * The following strings have a special meaning and will get replaced dynamically:
377                  * ###USERNAME###  The current user's username.
378                  * ###ADMIN_URL### The link to click on to confirm the email change.
379                  * ###EMAIL###     The new email.
380                  * ###SITENAME###  The name of the site.
381                  * ###SITEURL###   The URL to the site.
382                  *
383                  * @since MU
384                  *
385                  * @param string $email_text     Text in the email.
386                  * @param string $new_user_email New user email that the current user has changed to.
387                  */
388                 $content = apply_filters( 'new_user_email_content', $email_text, $new_user_email );
389
390                 $content = str_replace( '###USERNAME###', $current_user->user_login, $content );
391                 $content = str_replace( '###ADMIN_URL###', esc_url( admin_url( 'profile.php?newuseremail='.$hash ) ), $content );
392                 $content = str_replace( '###EMAIL###', $_POST['email'], $content);
393                 $content = str_replace( '###SITENAME###', get_site_option( 'site_name' ), $content );
394                 $content = str_replace( '###SITEURL###', network_home_url(), $content );
395
396                 wp_mail( $_POST['email'], sprintf( __( '[%s] New Email Address' ), wp_specialchars_decode( get_option( 'blogname' ) ) ), $content );
397                 $_POST['email'] = $current_user->user_email;
398         }
399 }
400
401 /**
402  * Adds an admin notice alerting the user to check for confirmation email
403  * after email address change.
404  *
405  * @since 3.0.0
406  *
407  * @global string $pagenow
408  */
409 function new_user_email_admin_notice() {
410         global $pagenow;
411         if ( 'profile.php' === $pagenow && isset( $_GET['updated'] ) && $email = get_user_meta( get_current_user_id(), '_new_email', true ) ) {
412                 /* translators: %s: New email address */
413                 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>';
414         }
415 }
416
417 /**
418  * Check whether a site has used its allotted upload space.
419  *
420  * @since MU
421  *
422  * @param bool $echo Optional. If $echo is set and the quota is exceeded, a warning message is echoed. Default is true.
423  * @return bool True if user is over upload space quota, otherwise false.
424  */
425 function upload_is_user_over_quota( $echo = true ) {
426         if ( get_site_option( 'upload_space_check_disabled' ) )
427                 return false;
428
429         $space_allowed = get_space_allowed();
430         if ( ! is_numeric( $space_allowed ) ) {
431                 $space_allowed = 10; // Default space allowed is 10 MB
432         }
433         $space_used = get_space_used();
434
435         if ( ( $space_allowed - $space_used ) < 0 ) {
436                 if ( $echo )
437                         _e( 'Sorry, you have used your space allocation. Please delete some files to upload more files.' );
438                 return true;
439         } else {
440                 return false;
441         }
442 }
443
444 /**
445  * Displays the amount of disk space used by the current site. Not used in core.
446  *
447  * @since MU
448  */
449 function display_space_usage() {
450         $space_allowed = get_space_allowed();
451         $space_used = get_space_used();
452
453         $percent_used = ( $space_used / $space_allowed ) * 100;
454
455         if ( $space_allowed > 1000 ) {
456                 $space = number_format( $space_allowed / KB_IN_BYTES );
457                 /* translators: Gigabytes */
458                 $space .= __( 'GB' );
459         } else {
460                 $space = number_format( $space_allowed );
461                 /* translators: Megabytes */
462                 $space .= __( 'MB' );
463         }
464         ?>
465         <strong><?php printf( __( 'Used: %1$s%% of %2$s' ), number_format( $percent_used ), $space ); ?></strong>
466         <?php
467 }
468
469 /**
470  * Get the remaining upload space for this site.
471  *
472  * @since MU
473  *
474  * @param int $size Current max size in bytes
475  * @return int Max size in bytes
476  */
477 function fix_import_form_size( $size ) {
478         if ( upload_is_user_over_quota( false ) ) {
479                 return 0;
480         }
481         $available = get_upload_space_available();
482         return min( $size, $available );
483 }
484
485 /**
486  * Displays the site upload space quota setting form on the Edit Site Settings screen.
487  *
488  * @since 3.0.0
489  *
490  * @param int $id The ID of the site to display the setting for.
491  */
492 function upload_space_setting( $id ) {
493         switch_to_blog( $id );
494         $quota = get_option( 'blog_upload_space' );
495         restore_current_blog();
496
497         if ( !$quota )
498                 $quota = '';
499
500         ?>
501         <tr>
502                 <th><label for="blog-upload-space-number"><?php _e( 'Site Upload Space Quota' ); ?></label></th>
503                 <td>
504                         <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; ?>" />
505                         <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>
506                 </td>
507         </tr>
508         <?php
509 }
510
511 /**
512  * Update the status of a user in the database.
513  *
514  * Used in core to mark a user as spam or "ham" (not spam) in Multisite.
515  *
516  * @since 3.0.0
517  *
518  * @global wpdb $wpdb WordPress database abstraction object.
519  *
520  * @param int    $id         The user ID.
521  * @param string $pref       The column in the wp_users table to update the user's status
522  *                           in (presumably user_status, spam, or deleted).
523  * @param int    $value      The new status for the user.
524  * @param null   $deprecated Deprecated as of 3.0.2 and should not be used.
525  * @return int   The initially passed $value.
526  */
527 function update_user_status( $id, $pref, $value, $deprecated = null ) {
528         global $wpdb;
529
530         if ( null !== $deprecated )
531                 _deprecated_argument( __FUNCTION__, '3.1' );
532
533         $wpdb->update( $wpdb->users, array( sanitize_key( $pref ) => $value ), array( 'ID' => $id ) );
534
535         $user = new WP_User( $id );
536         clean_user_cache( $user );
537
538         if ( $pref == 'spam' ) {
539                 if ( $value == 1 ) {
540                         /**
541                          * Fires after the user is marked as a SPAM user.
542                          *
543                          * @since 3.0.0
544                          *
545                          * @param int $id ID of the user marked as SPAM.
546                          */
547                         do_action( 'make_spam_user', $id );
548                 } else {
549                         /**
550                          * Fires after the user is marked as a HAM user. Opposite of SPAM.
551                          *
552                          * @since 3.0.0
553                          *
554                          * @param int $id ID of the user marked as HAM.
555                          */
556                         do_action( 'make_ham_user', $id );
557                 }
558         }
559
560         return $value;
561 }
562
563 /**
564  * Cleans the user cache for a specific user.
565  *
566  * @since 3.0.0
567  *
568  * @param int $id The user ID.
569  * @return bool|int The ID of the refreshed user or false if the user does not exist.
570  */
571 function refresh_user_details( $id ) {
572         $id = (int) $id;
573
574         if ( !$user = get_userdata( $id ) )
575                 return false;
576
577         clean_user_cache( $user );
578
579         return $id;
580 }
581
582 /**
583  * Returns the language for a language code.
584  *
585  * @since 3.0.0
586  *
587  * @param string $code Optional. The two-letter language code. Default empty.
588  * @return string The language corresponding to $code if it exists. If it does not exist,
589  *                then the first two letters of $code is returned.
590  */
591 function format_code_lang( $code = '' ) {
592         $code = strtolower( substr( $code, 0, 2 ) );
593         $lang_codes = array(
594                 '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',
595                 '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',
596                 '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',
597                 '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',
598                 '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',
599                 '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',
600                 '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',
601                 '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',
602                 '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',
603                 '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',
604                 've' => 'Venda', 'vi' => 'Vietnamese', 'vo' => 'Volapük', 'cy' => 'Welsh','wa' => 'Walloon','wo' => 'Wolof', 'xh' => 'Xhosa', 'yi' => 'Yiddish', 'yo' => 'Yoruba', 'za' => 'Zhuang; Chuang', 'zu' => 'Zulu' );
605
606         /**
607          * Filter the language codes.
608          *
609          * @since MU
610          *
611          * @param array  $lang_codes Key/value pair of language codes where key is the short version.
612          * @param string $code       A two-letter designation of the language.
613          */
614         $lang_codes = apply_filters( 'lang_codes', $lang_codes, $code );
615         return strtr( $code, $lang_codes );
616 }
617
618 /**
619  * Synchronize category and post tag slugs when global terms are enabled.
620  *
621  * @since 3.0.0
622  *
623  * @param object $term     The term.
624  * @param string $taxonomy The taxonomy for $term. Should be 'category' or 'post_tag', as these are
625  *                         the only taxonomies which are processed by this function; anything else
626  *                         will be returned untouched.
627  * @return object|array Returns `$term`, after filtering the 'slug' field with {@see sanitize_title()}
628  *                      if $taxonomy is 'category' or 'post_tag'.
629  */
630 function sync_category_tag_slugs( $term, $taxonomy ) {
631         if ( global_terms_enabled() && ( $taxonomy == 'category' || $taxonomy == 'post_tag' ) ) {
632                 if ( is_object( $term ) ) {
633                         $term->slug = sanitize_title( $term->name );
634                 } else {
635                         $term['slug'] = sanitize_title( $term['name'] );
636                 }
637         }
638         return $term;
639 }
640
641 /**
642  * Displays an access denied message when a user tries to view a site's dashboard they
643  * do not have access to.
644  *
645  * @since 3.2.0
646  * @access private
647  */
648 function _access_denied_splash() {
649         if ( ! is_user_logged_in() || is_network_admin() )
650                 return;
651
652         $blogs = get_blogs_of_user( get_current_user_id() );
653
654         if ( wp_list_filter( $blogs, array( 'userblog_id' => get_current_blog_id() ) ) )
655                 return;
656
657         $blog_name = get_bloginfo( 'name' );
658
659         if ( empty( $blogs ) )
660                 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 );
661
662         $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>';
663         $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>';
664
665         $output .= '<h3>' . __('Your Sites') . '</h3>';
666         $output .= '<table>';
667
668         foreach ( $blogs as $blog ) {
669                 $output .= '<tr>';
670                 $output .= "<td>{$blog->blogname}</td>";
671                 $output .= '<td><a href="' . esc_url( get_admin_url( $blog->userblog_id ) ) . '">' . __( 'Visit Dashboard' ) . '</a> | ' .
672                         '<a href="' . esc_url( get_home_url( $blog->userblog_id ) ). '">' . __( 'View Site' ) . '</a></td>';
673                 $output .= '</tr>';
674         }
675
676         $output .= '</table>';
677
678         wp_die( $output, 403 );
679 }
680
681 /**
682  * Checks if the current user has permissions to import new users.
683  *
684  * @since 3.0.0
685  *
686  * @param string $permission A permission to be checked. Currently not used.
687  * @return bool True if the user has proper permissions, false if they do not.
688  */
689 function check_import_new_users( $permission ) {
690         if ( !is_super_admin() )
691                 return false;
692         return true;
693 }
694 // See "import_allow_fetch_attachments" and "import_attachment_size_limit" filters too.
695
696 /**
697  * Generates and displays a drop-down of available languages.
698  *
699  * @since 3.0.0
700  *
701  * @param array  $lang_files Optional. An array of the language files. Default empty array.
702  * @param string $current    Optional. The current language code. Default empty.
703  */
704 function mu_dropdown_languages( $lang_files = array(), $current = '' ) {
705         $flag = false;
706         $output = array();
707
708         foreach ( (array) $lang_files as $val ) {
709                 $code_lang = basename( $val, '.mo' );
710
711                 if ( $code_lang == 'en_US' ) { // American English
712                         $flag = true;
713                         $ae = __( 'American English' );
714                         $output[$ae] = '<option value="' . esc_attr( $code_lang ) . '"' . selected( $current, $code_lang, false ) . '> ' . $ae . '</option>';
715                 } elseif ( $code_lang == 'en_GB' ) { // British English
716                         $flag = true;
717                         $be = __( 'British English' );
718                         $output[$be] = '<option value="' . esc_attr( $code_lang ) . '"' . selected( $current, $code_lang, false ) . '> ' . $be . '</option>';
719                 } else {
720                         $translated = format_code_lang( $code_lang );
721                         $output[$translated] = '<option value="' . esc_attr( $code_lang ) . '"' . selected( $current, $code_lang, false ) . '> ' . esc_html ( $translated ) . '</option>';
722                 }
723
724         }
725
726         if ( $flag === false ) // WordPress english
727                 $output[] = '<option value=""' . selected( $current, '', false ) . '>' . __( 'English' ) . "</option>";
728
729         // Order by name
730         uksort( $output, 'strnatcasecmp' );
731
732         /**
733          * Filter the languages available in the dropdown.
734          *
735          * @since MU
736          *
737          * @param array $output     HTML output of the dropdown.
738          * @param array $lang_files Available language files.
739          * @param string $current   The current language code.
740          */
741         $output = apply_filters( 'mu_dropdown_languages', $output, $lang_files, $current );
742
743         echo implode( "\n\t", $output );
744 }
745
746 /**
747  * Displays an admin notice to upgrade all sites after a core upgrade.
748  *
749  * @since 3.0.0
750  *
751  * @global int    $wp_db_version The version number of the database.
752  * @global string $pagenow
753  *
754  * @return false False if the current user is not a super admin.
755  */
756 function site_admin_notice() {
757         global $wp_db_version, $pagenow;
758
759         if ( ! is_super_admin() ) {
760                 return false;
761         }
762
763         if ( 'upgrade.php' == $pagenow ) {
764                 return;
765         }
766
767         if ( get_site_option( 'wpmu_upgrade_site' ) != $wp_db_version ) {
768                 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>";
769         }
770 }
771
772 /**
773  * Avoids a collision between a site slug and a permalink slug.
774  *
775  * In a subdirectory install this will make sure that a site and a post do not use the
776  * same subdirectory by checking for a site with the same name as a new post.
777  *
778  * @since 3.0.0
779  *
780  * @param array $data    An array of post data.
781  * @param array $postarr An array of posts. Not currently used.
782  * @return array The new array of post data after checking for collisions.
783  */
784 function avoid_blog_page_permalink_collision( $data, $postarr ) {
785         if ( is_subdomain_install() )
786                 return $data;
787         if ( $data['post_type'] != 'page' )
788                 return $data;
789         if ( !isset( $data['post_name'] ) || $data['post_name'] == '' )
790                 return $data;
791         if ( !is_main_site() )
792                 return $data;
793
794         $post_name = $data['post_name'];
795         $c = 0;
796         while( $c < 10 && get_id_from_blogname( $post_name ) ) {
797                 $post_name .= mt_rand( 1, 10 );
798                 $c ++;
799         }
800         if ( $post_name != $data['post_name'] ) {
801                 $data['post_name'] = $post_name;
802         }
803         return $data;
804 }
805
806 /**
807  * Handles the display of choosing a user's primary site.
808  *
809  * This displays the user's primary site and allows the user to choose
810  * which site is primary.
811  *
812  * @since 3.0.0
813  */
814 function choose_primary_blog() {
815         ?>
816         <table class="form-table">
817         <tr>
818         <?php /* translators: My sites label */ ?>
819                 <th scope="row"><label for="primary_blog"><?php _e( 'Primary Site' ); ?></label></th>
820                 <td>
821                 <?php
822                 $all_blogs = get_blogs_of_user( get_current_user_id() );
823                 $primary_blog = get_user_meta( get_current_user_id(), 'primary_blog', true );
824                 if ( count( $all_blogs ) > 1 ) {
825                         $found = false;
826                         ?>
827                         <select name="primary_blog" id="primary_blog">
828                                 <?php foreach ( (array) $all_blogs as $blog ) {
829                                         if ( $primary_blog == $blog->userblog_id )
830                                                 $found = true;
831                                         ?><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
832                                 } ?>
833                         </select>
834                         <?php
835                         if ( !$found ) {
836                                 $blog = reset( $all_blogs );
837                                 update_user_meta( get_current_user_id(), 'primary_blog', $blog->userblog_id );
838                         }
839                 } elseif ( count( $all_blogs ) == 1 ) {
840                         $blog = reset( $all_blogs );
841                         echo esc_url( get_home_url( $blog->userblog_id ) );
842                         if ( $primary_blog != $blog->userblog_id ) // Set the primary blog again if it's out of sync with blog list.
843                                 update_user_meta( get_current_user_id(), 'primary_blog', $blog->userblog_id );
844                 } else {
845                         echo "N/A";
846                 }
847                 ?>
848                 </td>
849         </tr>
850         </table>
851         <?php
852 }
853
854 /**
855  * Grants Super Admin privileges.
856  *
857  * @since 3.0.0
858  *
859  * @global array $super_admins
860  *
861  * @param int $user_id ID of the user to be granted Super Admin privileges.
862  * @return bool True on success, false on failure. This can fail when the user is
863  *              already a super admin or when the `$super_admins` global is defined.
864  */
865 function grant_super_admin( $user_id ) {
866         // If global super_admins override is defined, there is nothing to do here.
867         if ( isset( $GLOBALS['super_admins'] ) ) {
868                 return false;
869         }
870
871         /**
872          * Fires before the user is granted Super Admin privileges.
873          *
874          * @since 3.0.0
875          *
876          * @param int $user_id ID of the user that is about to be granted Super Admin privileges.
877          */
878         do_action( 'grant_super_admin', $user_id );
879
880         // Directly fetch site_admins instead of using get_super_admins()
881         $super_admins = get_site_option( 'site_admins', array( 'admin' ) );
882
883         $user = get_userdata( $user_id );
884         if ( $user && ! in_array( $user->user_login, $super_admins ) ) {
885                 $super_admins[] = $user->user_login;
886                 update_site_option( 'site_admins' , $super_admins );
887
888                 /**
889                  * Fires after the user is granted Super Admin privileges.
890                  *
891                  * @since 3.0.0
892                  *
893                  * @param int $user_id ID of the user that was granted Super Admin privileges.
894                  */
895                 do_action( 'granted_super_admin', $user_id );
896                 return true;
897         }
898         return false;
899 }
900
901 /**
902  * Revokes Super Admin privileges.
903  *
904  * @since 3.0.0
905  *
906  * @global array $super_admins
907  *
908  * @param int $user_id ID of the user Super Admin privileges to be revoked from.
909  * @return bool True on success, false on failure. This can fail when the user's email
910  *              is the network admin email or when the `$super_admins` global is defined.
911  */
912 function revoke_super_admin( $user_id ) {
913         // If global super_admins override is defined, there is nothing to do here.
914         if ( isset( $GLOBALS['super_admins'] ) ) {
915                 return false;
916         }
917
918         /**
919          * Fires before the user's Super Admin privileges are revoked.
920          *
921          * @since 3.0.0
922          *
923          * @param int $user_id ID of the user Super Admin privileges are being revoked from.
924          */
925         do_action( 'revoke_super_admin', $user_id );
926
927         // Directly fetch site_admins instead of using get_super_admins()
928         $super_admins = get_site_option( 'site_admins', array( 'admin' ) );
929
930         $user = get_userdata( $user_id );
931         if ( $user && 0 !== strcasecmp( $user->user_email, get_site_option( 'admin_email' ) ) ) {
932                 if ( false !== ( $key = array_search( $user->user_login, $super_admins ) ) ) {
933                         unset( $super_admins[$key] );
934                         update_site_option( 'site_admins', $super_admins );
935
936                         /**
937                          * Fires after the user's Super Admin privileges are revoked.
938                          *
939                          * @since 3.0.0
940                          *
941                          * @param int $user_id ID of the user Super Admin privileges were revoked from.
942                          */
943                         do_action( 'revoked_super_admin', $user_id );
944                         return true;
945                 }
946         }
947         return false;
948 }
949
950 /**
951  * Whether or not we can edit this network from this page.
952  *
953  * By default editing of network is restricted to the Network Admin for that `$site_id`
954  * this allows for this to be overridden.
955  *
956  * @since 3.1.0
957  *
958  * @global wpdb $wpdb WordPress database abstraction object.
959  *
960  * @param int $site_id The network/site ID to check.
961  * @return bool True if network can be edited, otherwise false.
962  */
963 function can_edit_network( $site_id ) {
964         global $wpdb;
965
966         if ( $site_id == $wpdb->siteid )
967                 $result = true;
968         else
969                 $result = false;
970
971         /**
972          * Filter whether this network can be edited from this page.
973          *
974          * @since 3.1.0
975          *
976          * @param bool $result  Whether the network can be edited from this page.
977          * @param int  $site_id The network/site ID to check.
978          */
979         return apply_filters( 'can_edit_network', $result, $site_id );
980 }
981
982 /**
983  * Thickbox image paths for Network Admin.
984  *
985  * @since 3.1.0
986  *
987  * @access private
988  */
989 function _thickbox_path_admin_subfolder() {
990 ?>
991 <script type="text/javascript">
992 var tb_pathToImage = "<?php echo includes_url( 'js/thickbox/loadingAnimation.gif', 'relative' ); ?>";
993 </script>
994 <?php
995 }
996
997 /**
998  *
999  * @param array $users
1000  */
1001 function confirm_delete_users( $users ) {
1002         $current_user = wp_get_current_user();
1003         if ( ! is_array( $users ) || empty( $users ) ) {
1004                 return false;
1005         }
1006         ?>
1007         <h1><?php esc_html_e( 'Users' ); ?></h1>
1008
1009         <?php if ( 1 == count( $users ) ) : ?>
1010                 <p><?php _e( 'You have chosen to delete the user from all networks and sites.' ); ?></p>
1011         <?php else : ?>
1012                 <p><?php _e( 'You have chosen to delete the following users from all networks and sites.' ); ?></p>
1013         <?php endif; ?>
1014
1015         <form action="users.php?action=dodelete" method="post">
1016         <input type="hidden" name="dodelete" />
1017         <?php
1018         wp_nonce_field( 'ms-users-delete' );
1019         $site_admins = get_super_admins();
1020         $admin_out = '<option value="' . esc_attr( $current_user->ID ) . '">' . $current_user->user_login . '</option>'; ?>
1021         <table class="form-table">
1022         <?php foreach ( ( $allusers = (array) $_POST['allusers'] ) as $user_id ) {
1023                 if ( $user_id != '' && $user_id != '0' ) {
1024                         $delete_user = get_userdata( $user_id );
1025
1026                         if ( ! current_user_can( 'delete_user', $delete_user->ID ) ) {
1027                                 wp_die( sprintf( __( 'Warning! User %s cannot be deleted.' ), $delete_user->user_login ) );
1028                         }
1029
1030                         if ( in_array( $delete_user->user_login, $site_admins ) ) {
1031                                 wp_die( sprintf( __( 'Warning! User cannot be deleted. The user %s is a network administrator.' ), '<em>' . $delete_user->user_login . '</em>' ) );
1032                         }
1033                         ?>
1034                         <tr>
1035                                 <th scope="row"><?php echo $delete_user->user_login; ?>
1036                                         <?php echo '<input type="hidden" name="user[]" value="' . esc_attr( $user_id ) . '" />' . "\n"; ?>
1037                                 </th>
1038                         <?php $blogs = get_blogs_of_user( $user_id, true );
1039
1040                         if ( ! empty( $blogs ) ) {
1041                                 ?>
1042                                 <td><fieldset><p><legend><?php printf(
1043                                         /* translators: user login */
1044                                         __( 'What should be done with content owned by %s?' ),
1045                                         '<em>' . $delete_user->user_login . '</em>'
1046                                 ); ?></legend></p>
1047                                 <?php
1048                                 foreach ( (array) $blogs as $key => $details ) {
1049                                         $blog_users = get_users( array( 'blog_id' => $details->userblog_id, 'fields' => array( 'ID', 'user_login' ) ) );
1050                                         if ( is_array( $blog_users ) && !empty( $blog_users ) ) {
1051                                                 $user_site = "<a href='" . esc_url( get_home_url( $details->userblog_id ) ) . "'>{$details->blogname}</a>";
1052                                                 $user_dropdown = '<label for="reassign_user" class="screen-reader-text">' . __( 'Select a user' ) . '</label>';
1053                                                 $user_dropdown .= "<select name='blog[$user_id][$key]' id='reassign_user'>";
1054                                                 $user_list = '';
1055                                                 foreach ( $blog_users as $user ) {
1056                                                         if ( ! in_array( $user->ID, $allusers ) ) {
1057                                                                 $user_list .= "<option value='{$user->ID}'>{$user->user_login}</option>";
1058                                                         }
1059                                                 }
1060                                                 if ( '' == $user_list ) {
1061                                                         $user_list = $admin_out;
1062                                                 }
1063                                                 $user_dropdown .= $user_list;
1064                                                 $user_dropdown .= "</select>\n";
1065                                                 ?>
1066                                                 <ul style="list-style:none;">
1067                                                         <li><?php printf( __( 'Site: %s' ), $user_site ); ?></li>
1068                                                         <li><label><input type="radio" id="delete_option0" name="delete[<?php echo $details->userblog_id . '][' . $delete_user->ID ?>]" value="delete" checked="checked" />
1069                                                         <?php _e( 'Delete all content.' ); ?></label></li>
1070                                                         <li><label><input type="radio" id="delete_option1" name="delete[<?php echo $details->userblog_id . '][' . $delete_user->ID ?>]" value="reassign" />
1071                                                         <?php _e( 'Attribute all content to:' ); ?></label>
1072                                                         <?php echo $user_dropdown; ?></li>
1073                                                 </ul>
1074                                                 <?php
1075                                         }
1076                                 }
1077                                 echo "</fieldset></td></tr>";
1078                         } else {
1079                                 ?>
1080                                 <td><fieldset><p><legend><?php _e( 'User has no sites or content and will be deleted.' ); ?></legend></p>
1081                         <?php } ?>
1082                         </tr>
1083                 <?php
1084                 }
1085         }
1086
1087         ?>
1088         </table>
1089         <?php
1090         /** This action is documented in wp-admin/users.php */
1091         do_action( 'delete_user_form', $current_user, $allusers );
1092
1093         if ( 1 == count( $users ) ) : ?>
1094                 <p><?php _e( 'Once you hit &#8220;Confirm Deletion&#8221;, the user will be permanently removed.' ); ?></p>
1095         <?php else : ?>
1096                 <p><?php _e( 'Once you hit &#8220;Confirm Deletion&#8221;, these users will be permanently removed.' ); ?></p>
1097         <?php endif;
1098
1099         submit_button( __('Confirm Deletion'), 'primary' );
1100         ?>
1101         </form>
1102         <?php
1103         return true;
1104 }
1105
1106 /**
1107  * Print JavaScript in the header on the Network Settings screen.
1108  *
1109  * @since 4.1.0
1110  */
1111 function network_settings_add_js() {
1112 ?>
1113 <script type="text/javascript">
1114 jQuery(document).ready( function($) {
1115         var languageSelect = $( '#WPLANG' );
1116         $( 'form' ).submit( function() {
1117                 // Don't show a spinner for English and installed languages,
1118                 // as there is nothing to download.
1119                 if ( ! languageSelect.find( 'option:selected' ).data( 'installed' ) ) {
1120                         $( '#submit', this ).after( '<span class="spinner language-install-spinner" />' );
1121                 }
1122         });
1123 });
1124 </script>
1125 <?php
1126 }