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