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