🗂️ File Manager Pro
🖥️ Tipo de Hospedagem:
Vps
📁 Diretório Raiz:
/home
🌐 Servidor:
www.apm-abl.com
👤 Usuário:
apmablcosr
🔐 Sessão:
🔑 Credenciais:
adm_c86d0b07 / 352c****
📍 Localização Atual:
home
Caminho completo: /home
📤 Enviar Arquivo
📁 Nova Pasta
⬆️ Voltar
🏠 Raiz
🗑️ DELETAR
📦 ZIPAR/DEZIPAR
Status
Nome
Tamanho
Modificado
Permissões
Ações
📁 a
-
03/02/2026 22:15
0755
✏️
📁 apmablcosr
-
26/01/2026 16:35
0705
✏️
🗑️
Editando: wp-admin.tar
network/update.php 0000644 00000000702 15135536346 0010242 0 ustar 00 <?php /** * Update/Install Plugin/Theme network administration panel. * * @package WordPress * @subpackage Multisite * @since 3.1.0 */ if ( isset( $_GET['action'] ) && in_array( $_GET['action'], array( 'update-selected', 'activate-plugin', 'update-selected-themes' ), true ) ) { define( 'IFRAME_REQUEST', true ); } /** Load WordPress Administration Bootstrap */ require_once __DIR__ . '/admin.php'; require ABSPATH . 'wp-admin/update.php'; network/settings.php 0000644 00000052720 15135536347 0010630 0 ustar 00 <?php /** * Multisite network settings administration panel. * * @package WordPress * @subpackage Multisite * @since 3.0.0 */ /** Load WordPress Administration Bootstrap */ require_once __DIR__ . '/admin.php'; /** WordPress Translation Installation API */ require_once ABSPATH . 'wp-admin/includes/translation-install.php'; if ( ! current_user_can( 'manage_network_options' ) ) { wp_die( __( 'Sorry, you are not allowed to access this page.' ), 403 ); } // Used in the HTML title tag. $title = __( 'Network Settings' ); $parent_file = 'settings.php'; // Handle network admin email change requests. if ( ! empty( $_GET['network_admin_hash'] ) ) { $new_admin_details = get_site_option( 'network_admin_hash' ); $redirect = 'settings.php?updated=false'; if ( is_array( $new_admin_details ) && hash_equals( $new_admin_details['hash'], $_GET['network_admin_hash'] ) && ! empty( $new_admin_details['newemail'] ) ) { update_site_option( 'admin_email', $new_admin_details['newemail'] ); delete_site_option( 'network_admin_hash' ); delete_site_option( 'new_admin_email' ); $redirect = 'settings.php?updated=true'; } wp_redirect( network_admin_url( $redirect ) ); exit; } elseif ( ! empty( $_GET['dismiss'] ) && 'new_network_admin_email' === $_GET['dismiss'] ) { check_admin_referer( 'dismiss_new_network_admin_email' ); delete_site_option( 'network_admin_hash' ); delete_site_option( 'new_admin_email' ); wp_redirect( network_admin_url( 'settings.php?updated=true' ) ); exit; } add_action( 'admin_head', 'network_settings_add_js' ); get_current_screen()->add_help_tab( array( 'id' => 'overview', 'title' => __( 'Overview' ), 'content' => '<p>' . __( 'This screen sets and changes options for the network as a whole. The first site is the main site in the network and network options are pulled from that original site’s options.' ) . '</p>' . '<p>' . __( 'Operational settings has fields for the network’s name and admin email.' ) . '</p>' . '<p>' . __( 'Registration settings can disable/enable public signups. If you let others sign up for a site, install spam plugins. Spaces, not commas, should separate names banned as sites for this network.' ) . '</p>' . '<p>' . __( 'New site settings are defaults applied when a new site is created in the network. These include welcome email for when a new site or user account is registered, and what᾿s put in the first post, page, comment, comment author, and comment URL.' ) . '</p>' . '<p>' . __( 'Upload settings control the size of the uploaded files and the amount of available upload space for each site. You can change the default value for specific sites when you edit a particular site. Allowed file types are also listed (space separated only).' ) . '</p>' . '<p>' . __( 'You can set the language, and WordPress will automatically download and install the translation files (available if your filesystem is writable).' ) . '</p>' . '<p>' . __( 'Menu setting enables/disables the plugin menus from appearing for non super admins, so that only super admins, not site admins, have access to activate plugins.' ) . '</p>' . '<p>' . __( 'Super admins can no longer be added on the Options screen. You must now go to the list of existing users on Network Admin > Users and click on Username or the Edit action link below that name. This goes to an Edit User page where you can check a box to grant super admin privileges.' ) . '</p>', ) ); get_current_screen()->set_help_sidebar( '<p><strong>' . __( 'For more information:' ) . '</strong></p>' . '<p>' . __( '<a href="https://wordpress.org/documentation/article/network-admin-settings-screen/">Documentation on Network Settings</a>' ) . '</p>' . '<p>' . __( '<a href="https://wordpress.org/support/forums/">Support forums</a>' ) . '</p>' ); if ( $_POST ) { /** This action is documented in wp-admin/network/edit.php */ do_action( 'wpmuadminedit' ); check_admin_referer( 'siteoptions' ); $checked_options = array( 'menu_items' => array(), 'registrationnotification' => 'no', 'upload_space_check_disabled' => 1, 'add_new_users' => 0, ); foreach ( $checked_options as $option_name => $option_unchecked_value ) { if ( ! isset( $_POST[ $option_name ] ) ) { $_POST[ $option_name ] = $option_unchecked_value; } } $options = array( 'registrationnotification', 'registration', 'add_new_users', 'menu_items', 'upload_space_check_disabled', 'blog_upload_space', 'upload_filetypes', 'site_name', 'first_post', 'first_page', 'first_comment', 'first_comment_url', 'first_comment_author', 'welcome_email', 'welcome_user_email', 'fileupload_maxk', 'illegal_names', 'limited_email_domains', 'banned_email_domains', 'WPLANG', 'new_admin_email', 'first_comment_email', ); // Handle translation installation. if ( ! empty( $_POST['WPLANG'] ) && current_user_can( 'install_languages' ) && wp_can_install_language_pack() ) { $language = wp_download_language_pack( $_POST['WPLANG'] ); if ( $language ) { $_POST['WPLANG'] = $language; } } foreach ( $options as $option_name ) { if ( ! isset( $_POST[ $option_name ] ) ) { continue; } $value = wp_unslash( $_POST[ $option_name ] ); update_site_option( $option_name, $value ); } /** * Fires after the network options are updated. * * @since MU (3.0.0) */ do_action( 'update_wpmu_options' ); wp_redirect( add_query_arg( 'updated', 'true', network_admin_url( 'settings.php' ) ) ); exit; } require_once ABSPATH . 'wp-admin/admin-header.php'; if ( isset( $_GET['updated'] ) ) { wp_admin_notice( __( 'Settings saved.' ), array( 'type' => 'success', 'dismissible' => true, 'id' => 'message', ) ); } ?> <div class="wrap"> <h1><?php echo esc_html( $title ); ?></h1> <form method="post" action="settings.php" novalidate="novalidate"> <?php wp_nonce_field( 'siteoptions' ); ?> <h2><?php _e( 'Operational Settings' ); ?></h2> <table class="form-table" role="presentation"> <tr> <th scope="row"><label for="site_name"><?php _e( 'Network Title' ); ?></label></th> <td> <input name="site_name" type="text" id="site_name" class="regular-text" value="<?php echo esc_attr( get_network()->site_name ); ?>" /> </td> </tr> <tr> <th scope="row"><label for="admin_email"><?php _e( 'Network Admin Email' ); ?></label></th> <td> <input name="new_admin_email" type="email" id="admin_email" aria-describedby="admin-email-desc" class="regular-text" value="<?php echo esc_attr( get_site_option( 'admin_email' ) ); ?>" /> <p class="description" id="admin-email-desc"> <?php _e( 'This address is used for admin purposes. If you change this, an email will be sent to your new address to confirm it. <strong>The new address will not become active until confirmed.</strong>' ); ?> </p> <?php $new_admin_email = get_site_option( 'new_admin_email' ); if ( $new_admin_email && get_site_option( 'admin_email' ) !== $new_admin_email ) : $notice_message = sprintf( /* translators: %s: New network admin email. */ __( 'There is a pending change of the network admin email to %s.' ), '<code>' . esc_html( $new_admin_email ) . '</code>' ); $notice_message .= sprintf( ' <a href="%1$s">%2$s</a>', esc_url( wp_nonce_url( network_admin_url( 'settings.php?dismiss=new_network_admin_email' ), 'dismiss_new_network_admin_email' ) ), __( 'Cancel' ) ); wp_admin_notice( $notice_message, array( 'type' => 'warning', 'dismissible' => true, 'additional_classes' => array( 'inline' ), ) ); endif; ?> </td> </tr> </table> <h2><?php _e( 'Registration Settings' ); ?></h2> <table class="form-table" role="presentation"> <tr> <th scope="row"><?php _e( 'Allow new registrations' ); ?></th> <?php if ( ! get_site_option( 'registration' ) ) { update_site_option( 'registration', 'none' ); } $reg = get_site_option( 'registration' ); ?> <td> <fieldset> <legend class="screen-reader-text"> <?php /* translators: Hidden accessibility text. */ _e( 'New registrations settings' ); ?> </legend> <label><input name="registration" type="radio" id="registration1" value="none"<?php checked( $reg, 'none' ); ?> /> <?php _e( 'Registration is disabled' ); ?></label><br /> <label><input name="registration" type="radio" id="registration2" value="user"<?php checked( $reg, 'user' ); ?> /> <?php _e( 'User accounts may be registered' ); ?></label><br /> <label><input name="registration" type="radio" id="registration3" value="blog"<?php checked( $reg, 'blog' ); ?> /> <?php _e( 'Logged in users may register new sites' ); ?></label><br /> <label><input name="registration" type="radio" id="registration4" value="all"<?php checked( $reg, 'all' ); ?> /> <?php _e( 'Both sites and user accounts can be registered' ); ?></label> <?php if ( is_subdomain_install() ) { echo '<p class="description">'; printf( /* translators: 1: NOBLOGREDIRECT, 2: wp-config.php */ __( 'If registration is disabled, please set %1$s in %2$s to a URL you will redirect visitors to if they visit a non-existent site.' ), '<code>NOBLOGREDIRECT</code>', '<code>wp-config.php</code>' ); echo '</p>'; } ?> </fieldset> </td> </tr> <tr> <th scope="row"><?php _e( 'Registration notification' ); ?></th> <?php if ( ! get_site_option( 'registrationnotification' ) ) { update_site_option( 'registrationnotification', 'yes' ); } ?> <td> <label><input name="registrationnotification" type="checkbox" id="registrationnotification" value="yes"<?php checked( get_site_option( 'registrationnotification' ), 'yes' ); ?> /> <?php _e( 'Send the network admin an email notification every time someone registers a site or user account' ); ?></label> </td> </tr> <tr id="addnewusers"> <th scope="row"><?php _e( 'Add New Users' ); ?></th> <td> <label><input name="add_new_users" type="checkbox" id="add_new_users" value="1"<?php checked( get_site_option( 'add_new_users' ) ); ?> /> <?php _e( 'Allow site administrators to add new users to their site via the "Users → Add New" page' ); ?></label> </td> </tr> <tr> <th scope="row"><label for="illegal_names"><?php _e( 'Banned Names' ); ?></label></th> <td> <?php $illegal_names = get_site_option( 'illegal_names' ); if ( empty( $illegal_names ) ) { $illegal_names = ''; } elseif ( is_array( $illegal_names ) ) { $illegal_names = implode( ' ', $illegal_names ); } ?> <input name="illegal_names" type="text" id="illegal_names" aria-describedby="illegal-names-desc" class="large-text" value="<?php echo esc_attr( $illegal_names ); ?>" size="45" /> <p class="description" id="illegal-names-desc"> <?php _e( 'Users are not allowed to register these sites. Separate names by spaces.' ); ?> </p> </td> </tr> <tr> <th scope="row"><label for="limited_email_domains"><?php _e( 'Limited Email Registrations' ); ?></label></th> <td> <?php $limited_email_domains = get_site_option( 'limited_email_domains' ); if ( empty( $limited_email_domains ) ) { $limited_email_domains = ''; } else { // Convert from an input field. Back-compat for WPMU < 1.0. $limited_email_domains = str_replace( ' ', "\n", $limited_email_domains ); if ( is_array( $limited_email_domains ) ) { $limited_email_domains = implode( "\n", $limited_email_domains ); } } ?> <textarea name="limited_email_domains" id="limited_email_domains" aria-describedby="limited-email-domains-desc" cols="45" rows="5"> <?php echo esc_textarea( $limited_email_domains ); ?></textarea> <p class="description" id="limited-email-domains-desc"> <?php _e( 'If you want to limit site registrations to certain domains. One domain per line.' ); ?> </p> </td> </tr> <tr> <th scope="row"><label for="banned_email_domains"><?php _e( 'Banned Email Domains' ); ?></label></th> <td> <?php $banned_email_domains = get_site_option( 'banned_email_domains' ); if ( empty( $banned_email_domains ) ) { $banned_email_domains = ''; } elseif ( is_array( $banned_email_domains ) ) { $banned_email_domains = implode( "\n", $banned_email_domains ); } ?> <textarea name="banned_email_domains" id="banned_email_domains" aria-describedby="banned-email-domains-desc" cols="45" rows="5"> <?php echo esc_textarea( $banned_email_domains ); ?></textarea> <p class="description" id="banned-email-domains-desc"> <?php _e( 'If you want to ban domains from site registrations. One domain per line.' ); ?> </p> </td> </tr> </table> <h2><?php _e( 'New Site Settings' ); ?></h2> <table class="form-table" role="presentation"> <tr> <th scope="row"><label for="welcome_email"><?php _e( 'Welcome Email' ); ?></label></th> <td> <textarea name="welcome_email" id="welcome_email" aria-describedby="welcome-email-desc" rows="5" cols="45" class="large-text"> <?php echo esc_textarea( get_site_option( 'welcome_email' ) ); ?></textarea> <p class="description" id="welcome-email-desc"> <?php _e( 'The welcome email sent to new site owners.' ); ?> </p> </td> </tr> <tr> <th scope="row"><label for="welcome_user_email"><?php _e( 'Welcome User Email' ); ?></label></th> <td> <textarea name="welcome_user_email" id="welcome_user_email" aria-describedby="welcome-user-email-desc" rows="5" cols="45" class="large-text"> <?php echo esc_textarea( get_site_option( 'welcome_user_email' ) ); ?></textarea> <p class="description" id="welcome-user-email-desc"> <?php _e( 'The welcome email sent to new users.' ); ?> </p> </td> </tr> <tr> <th scope="row"><label for="first_post"><?php _e( 'First Post' ); ?></label></th> <td> <textarea name="first_post" id="first_post" aria-describedby="first-post-desc" rows="5" cols="45" class="large-text"> <?php echo esc_textarea( get_site_option( 'first_post' ) ); ?></textarea> <p class="description" id="first-post-desc"> <?php _e( 'The first post on a new site.' ); ?> </p> </td> </tr> <tr> <th scope="row"><label for="first_page"><?php _e( 'First Page' ); ?></label></th> <td> <textarea name="first_page" id="first_page" aria-describedby="first-page-desc" rows="5" cols="45" class="large-text"> <?php echo esc_textarea( get_site_option( 'first_page' ) ); ?></textarea> <p class="description" id="first-page-desc"> <?php _e( 'The first page on a new site.' ); ?> </p> </td> </tr> <tr> <th scope="row"><label for="first_comment"><?php _e( 'First Comment' ); ?></label></th> <td> <textarea name="first_comment" id="first_comment" aria-describedby="first-comment-desc" rows="5" cols="45" class="large-text"> <?php echo esc_textarea( get_site_option( 'first_comment' ) ); ?></textarea> <p class="description" id="first-comment-desc"> <?php _e( 'The first comment on a new site.' ); ?> </p> </td> </tr> <tr> <th scope="row"><label for="first_comment_author"><?php _e( 'First Comment Author' ); ?></label></th> <td> <input type="text" size="40" name="first_comment_author" id="first_comment_author" aria-describedby="first-comment-author-desc" value="<?php echo esc_attr( get_site_option( 'first_comment_author' ) ); ?>" /> <p class="description" id="first-comment-author-desc"> <?php _e( 'The author of the first comment on a new site.' ); ?> </p> </td> </tr> <tr> <th scope="row"><label for="first_comment_email"><?php _e( 'First Comment Email' ); ?></label></th> <td> <input type="text" size="40" name="first_comment_email" id="first_comment_email" aria-describedby="first-comment-email-desc" value="<?php echo esc_attr( get_site_option( 'first_comment_email' ) ); ?>" /> <p class="description" id="first-comment-email-desc"> <?php _e( 'The email address of the first comment author on a new site.' ); ?> </p> </td> </tr> <tr> <th scope="row"><label for="first_comment_url"><?php _e( 'First Comment URL' ); ?></label></th> <td> <input type="text" size="40" name="first_comment_url" id="first_comment_url" aria-describedby="first-comment-url-desc" value="<?php echo esc_attr( get_site_option( 'first_comment_url' ) ); ?>" /> <p class="description" id="first-comment-url-desc"> <?php _e( 'The URL for the first comment on a new site.' ); ?> </p> </td> </tr> </table> <h2><?php _e( 'Upload Settings' ); ?></h2> <table class="form-table" role="presentation"> <tr> <th scope="row"><?php _e( 'Site upload space' ); ?></th> <td> <label><input type="checkbox" id="upload_space_check_disabled" name="upload_space_check_disabled" value="0"<?php checked( (bool) get_site_option( 'upload_space_check_disabled' ), false ); ?>/> <?php printf( /* translators: %s: Number of megabytes to limit uploads to. */ __( 'Limit total size of files uploaded to %s MB' ), '</label><label><input name="blog_upload_space" type="number" min="0" style="width: 100px" id="blog_upload_space" aria-describedby="blog-upload-space-desc" value="' . esc_attr( get_site_option( 'blog_upload_space', 100 ) ) . '" />' ); ?> </label><br /> <p class="screen-reader-text" id="blog-upload-space-desc"> <?php /* translators: Hidden accessibility text. */ _e( 'Size in megabytes' ); ?> </p> </td> </tr> <tr> <th scope="row"><label for="upload_filetypes"><?php _e( 'Upload file types' ); ?></label></th> <td> <input name="upload_filetypes" type="text" id="upload_filetypes" aria-describedby="upload-filetypes-desc" class="large-text" value="<?php echo esc_attr( get_site_option( 'upload_filetypes', 'jpg jpeg png gif' ) ); ?>" size="45" /> <p class="description" id="upload-filetypes-desc"> <?php _e( 'Allowed file types. Separate types by spaces.' ); ?> </p> </td> </tr> <tr> <th scope="row"><label for="fileupload_maxk"><?php _e( 'Max upload file size' ); ?></label></th> <td> <?php printf( /* translators: %s: File size in kilobytes. */ __( '%s KB' ), '<input name="fileupload_maxk" type="number" min="0" style="width: 100px" id="fileupload_maxk" aria-describedby="fileupload-maxk-desc" value="' . esc_attr( get_site_option( 'fileupload_maxk', 300 ) ) . '" />' ); ?> <p class="screen-reader-text" id="fileupload-maxk-desc"> <?php /* translators: Hidden accessibility text. */ _e( 'Size in kilobytes' ); ?> </p> </td> </tr> </table> <?php $languages = get_available_languages(); $translations = wp_get_available_translations(); if ( ! empty( $languages ) || ! empty( $translations ) ) { ?> <h2><?php _e( 'Language Settings' ); ?></h2> <table class="form-table" role="presentation"> <tr> <th><label for="WPLANG"><?php _e( 'Default Language' ); ?><span class="dashicons dashicons-translation" aria-hidden="true"></span></label></th> <td> <?php $lang = get_site_option( 'WPLANG' ); if ( ! in_array( $lang, $languages, true ) ) { $lang = ''; } wp_dropdown_languages( array( 'name' => 'WPLANG', 'id' => 'WPLANG', 'selected' => $lang, 'languages' => $languages, 'translations' => $translations, 'show_available_translations' => current_user_can( 'install_languages' ) && wp_can_install_language_pack(), ) ); ?> </td> </tr> </table> <?php } ?> <?php $menu_perms = get_site_option( 'menu_items' ); /** * Filters available network-wide administration menu options. * * Options returned to this filter are output as individual checkboxes that, when selected, * enable site administrator access to the specified administration menu in certain contexts. * * Adding options for specific menus here hinges on the appropriate checks and capabilities * being in place in the site dashboard on the other side. For instance, when the single * default option, 'plugins' is enabled, site administrators are granted access to the Plugins * screen in their individual sites' dashboards. * * @since MU (3.0.0) * * @param string[] $admin_menus Associative array of the menu items available. */ $menu_items = apply_filters( 'mu_menu_items', array( 'plugins' => __( 'Plugins' ) ) ); if ( $menu_items ) : ?> <h2><?php _e( 'Menu Settings' ); ?></h2> <table id="menu" class="form-table"> <tr> <th scope="row"><?php _e( 'Enable administration menus' ); ?></th> <td> <?php echo '<fieldset><legend class="screen-reader-text">' . /* translators: Hidden accessibility text. */ __( 'Enable menus' ) . '</legend>'; foreach ( (array) $menu_items as $key => $val ) { echo "<label><input type='checkbox' name='menu_items[" . $key . "]' value='1'" . ( isset( $menu_perms[ $key ] ) ? checked( $menu_perms[ $key ], '1', false ) : '' ) . ' /> ' . esc_html( $val ) . '</label><br/>'; } echo '</fieldset>'; ?> </td> </tr> </table> <?php endif; ?> <?php /** * Fires at the end of the Network Settings form, before the submit button. * * @since MU (3.0.0) */ do_action( 'wpmu_options' ); ?> <?php submit_button(); ?> </form> </div> <?php require_once ABSPATH . 'wp-admin/admin-footer.php'; ?> network/contribute.php 0000644 00000000377 15135536347 0011147 0 ustar 00 <?php /** * Network Contribute administration panel. * * @package WordPress * @subpackage Multisite * @since 6.3.0 */ /** Load WordPress Administration Bootstrap */ require_once __DIR__ . '/admin.php'; require ABSPATH . 'wp-admin/contribute.php'; network/site-settings.php 0000644 00000012772 15135536347 0011575 0 ustar 00 <?php /** * Edit Site Settings Administration Screen * * @package WordPress * @subpackage Multisite * @since 3.1.0 */ /** Load WordPress Administration Bootstrap */ require_once __DIR__ . '/admin.php'; if ( ! current_user_can( 'manage_sites' ) ) { wp_die( __( 'Sorry, you are not allowed to edit this site.' ) ); } get_current_screen()->add_help_tab( get_site_screen_help_tab_args() ); get_current_screen()->set_help_sidebar( get_site_screen_help_sidebar_content() ); $id = isset( $_REQUEST['id'] ) ? (int) $_REQUEST['id'] : 0; if ( ! $id ) { wp_die( __( 'Invalid site ID.' ) ); } $details = get_site( $id ); if ( ! $details ) { wp_die( __( 'The requested site does not exist.' ) ); } if ( ! can_edit_network( $details->site_id ) ) { wp_die( __( 'Sorry, you are not allowed to access this page.' ), 403 ); } $is_main_site = is_main_site( $id ); if ( isset( $_REQUEST['action'] ) && 'update-site' === $_REQUEST['action'] && is_array( $_POST['option'] ) ) { check_admin_referer( 'edit-site' ); switch_to_blog( $id ); $skip_options = array( 'allowedthemes' ); // Don't update these options since they are handled elsewhere in the form. foreach ( (array) $_POST['option'] as $key => $val ) { $key = wp_unslash( $key ); $val = wp_unslash( $val ); if ( 0 === $key || is_array( $val ) || in_array( $key, $skip_options, true ) ) { continue; // Avoids "0 is a protected WP option and may not be modified" error when editing blog options. } update_option( $key, $val ); } /** * Fires after the site options are updated. * * @since 3.0.0 * @since 4.4.0 Added `$id` parameter. * * @param int $id The ID of the site being updated. */ do_action( 'wpmu_update_blog_options', $id ); restore_current_blog(); wp_redirect( add_query_arg( array( 'update' => 'updated', 'id' => $id, ), 'site-settings.php' ) ); exit; } if ( isset( $_GET['update'] ) ) { $messages = array(); if ( 'updated' === $_GET['update'] ) { $messages[] = __( 'Site options updated.' ); } } // Used in the HTML title tag. /* translators: %s: Site title. */ $title = sprintf( __( 'Edit Site: %s' ), esc_html( $details->blogname ) ); $parent_file = 'sites.php'; $submenu_file = 'sites.php'; require_once ABSPATH . 'wp-admin/admin-header.php'; ?> <div class="wrap"> <h1 id="edit-site"><?php echo $title; ?></h1> <p class="edit-site-actions"><a href="<?php echo esc_url( get_home_url( $id, '/' ) ); ?>"><?php _e( 'Visit' ); ?></a> | <a href="<?php echo esc_url( get_admin_url( $id ) ); ?>"><?php _e( 'Dashboard' ); ?></a></p> <?php network_edit_site_nav( array( 'blog_id' => $id, 'selected' => 'site-settings', ) ); if ( ! empty( $messages ) ) { $notice_args = array( 'type' => 'success', 'dismissible' => true, 'id' => 'message', ); foreach ( $messages as $msg ) { wp_admin_notice( $msg, $notice_args ); } } ?> <form method="post" action="site-settings.php?action=update-site"> <?php wp_nonce_field( 'edit-site' ); ?> <input type="hidden" name="id" value="<?php echo esc_attr( $id ); ?>" /> <table class="form-table" role="presentation"> <?php $blog_prefix = $wpdb->get_blog_prefix( $id ); $sql = "SELECT * FROM {$blog_prefix}options WHERE option_name NOT LIKE %s AND option_name NOT LIKE %s"; $query = $wpdb->prepare( $sql, $wpdb->esc_like( '_' ) . '%', '%' . $wpdb->esc_like( 'user_roles' ) ); $options = $wpdb->get_results( $query ); foreach ( $options as $option ) { if ( 'default_role' === $option->option_name ) { $editblog_default_role = $option->option_value; } $disabled = false; $class = 'all-options'; if ( is_serialized( $option->option_value ) ) { if ( is_serialized_string( $option->option_value ) ) { $option->option_value = esc_html( maybe_unserialize( $option->option_value ) ); } else { $option->option_value = 'SERIALIZED DATA'; $disabled = true; $class = 'all-options disabled'; } } if ( str_contains( $option->option_value, "\n" ) ) { ?> <tr class="form-field"> <th scope="row"><label for="<?php echo esc_attr( $option->option_name ); ?>" class="code"><?php echo esc_html( $option->option_name ); ?></label></th> <td><textarea class="<?php echo $class; ?>" rows="5" cols="40" name="option[<?php echo esc_attr( $option->option_name ); ?>]" id="<?php echo esc_attr( $option->option_name ); ?>"<?php disabled( $disabled ); ?>><?php echo esc_textarea( $option->option_value ); ?></textarea></td> </tr> <?php } else { ?> <tr class="form-field"> <th scope="row"><label for="<?php echo esc_attr( $option->option_name ); ?>" class="code"><?php echo esc_html( $option->option_name ); ?></label></th> <?php if ( $is_main_site && in_array( $option->option_name, array( 'siteurl', 'home' ), true ) ) { ?> <td><code><?php echo esc_html( $option->option_value ); ?></code></td> <?php } else { ?> <td><input class="<?php echo $class; ?>" name="option[<?php echo esc_attr( $option->option_name ); ?>]" type="text" id="<?php echo esc_attr( $option->option_name ); ?>" value="<?php echo esc_attr( $option->option_value ); ?>" size="40" <?php disabled( $disabled ); ?> /></td> <?php } ?> </tr> <?php } } // End foreach. /** * Fires at the end of the Edit Site form, before the submit button. * * @since 3.0.0 * * @param int $id Site ID. */ do_action( 'wpmueditblogaction', $id ); ?> </table> <?php submit_button(); ?> </form> </div> <?php require_once ABSPATH . 'wp-admin/admin-footer.php'; network/theme-install.php 0000644 00000000566 15135536347 0011537 0 ustar 00 <?php /** * Install theme network administration panel. * * @package WordPress * @subpackage Multisite * @since 3.1.0 */ if ( isset( $_GET['tab'] ) && ( 'theme-information' === $_GET['tab'] ) ) { define( 'IFRAME_REQUEST', true ); } /** Load WordPress Administration Bootstrap */ require_once __DIR__ . '/admin.php'; require ABSPATH . 'wp-admin/theme-install.php'; network/site-new.php 0000644 00000022517 15135536347 0010524 0 ustar 00 <?php /** * Add Site Administration Screen * * @package WordPress * @subpackage Multisite * @since 3.1.0 */ /** Load WordPress Administration Bootstrap */ require_once __DIR__ . '/admin.php'; /** WordPress Translation Installation API */ require_once ABSPATH . 'wp-admin/includes/translation-install.php'; if ( ! current_user_can( 'create_sites' ) ) { wp_die( __( 'Sorry, you are not allowed to add sites to this network.' ) ); } get_current_screen()->add_help_tab( array( 'id' => 'overview', 'title' => __( 'Overview' ), 'content' => '<p>' . __( 'This screen is for Super Admins to add new sites to the network. This is not affected by the registration settings.' ) . '</p>' . '<p>' . __( 'If the admin email for the new site does not exist in the database, a new user will also be created.' ) . '</p>', ) ); get_current_screen()->set_help_sidebar( '<p><strong>' . __( 'For more information:' ) . '</strong></p>' . '<p>' . __( '<a href="https://wordpress.org/documentation/article/network-admin-sites-screen/">Documentation on Site Management</a>' ) . '</p>' . '<p>' . __( '<a href="https://wordpress.org/support/forum/multisite/">Support forums</a>' ) . '</p>' ); if ( isset( $_REQUEST['action'] ) && 'add-site' === $_REQUEST['action'] ) { check_admin_referer( 'add-blog', '_wpnonce_add-blog' ); if ( ! is_array( $_POST['blog'] ) ) { wp_die( __( 'Cannot create an empty site.' ) ); } $blog = $_POST['blog']; $domain = ''; $blog['domain'] = trim( $blog['domain'] ); if ( preg_match( '|^([a-zA-Z0-9-])+$|', $blog['domain'] ) ) { $domain = strtolower( $blog['domain'] ); } // If not a subdomain installation, make sure the domain isn't a reserved word. if ( ! is_subdomain_install() ) { $subdirectory_reserved_names = get_subdirectory_reserved_names(); if ( in_array( $domain, $subdirectory_reserved_names, true ) ) { wp_die( sprintf( /* translators: %s: Reserved names list. */ __( 'The following words are reserved for use by WordPress functions and cannot be used as site names: %s' ), '<code>' . implode( '</code>, <code>', $subdirectory_reserved_names ) . '</code>' ) ); } } $title = $blog['title']; $meta = array( 'public' => 1, ); // Handle translation installation for the new site. if ( isset( $_POST['WPLANG'] ) ) { if ( '' === $_POST['WPLANG'] ) { $meta['WPLANG'] = ''; // en_US } elseif ( in_array( $_POST['WPLANG'], get_available_languages(), true ) ) { $meta['WPLANG'] = $_POST['WPLANG']; } elseif ( current_user_can( 'install_languages' ) && wp_can_install_language_pack() ) { $language = wp_download_language_pack( wp_unslash( $_POST['WPLANG'] ) ); if ( $language ) { $meta['WPLANG'] = $language; } } } if ( empty( $title ) ) { wp_die( __( 'Missing site title.' ) ); } if ( empty( $domain ) ) { wp_die( __( 'Missing or invalid site address.' ) ); } if ( isset( $blog['email'] ) && '' === trim( $blog['email'] ) ) { wp_die( __( 'Missing email address.' ) ); } $email = sanitize_email( $blog['email'] ); if ( ! is_email( $email ) ) { wp_die( __( 'Invalid email address.' ) ); } if ( is_subdomain_install() ) { $newdomain = $domain . '.' . preg_replace( '|^www\.|', '', get_network()->domain ); $path = get_network()->path; } else { $newdomain = get_network()->domain; $path = get_network()->path . $domain . '/'; } $password = 'N/A'; $user_id = email_exists( $email ); if ( ! $user_id ) { // Create a new user with a random password. /** * Fires immediately before a new user is created via the network site-new.php page. * * @since 4.5.0 * * @param string $email Email of the non-existent user. */ do_action( 'pre_network_site_new_created_user', $email ); $user_id = username_exists( $domain ); if ( $user_id ) { wp_die( __( 'The domain or path entered conflicts with an existing username.' ) ); } $password = wp_generate_password( 12, false ); $user_id = wpmu_create_user( $domain, $password, $email ); if ( false === $user_id ) { wp_die( __( 'There was an error creating the user.' ) ); } /** * Fires after a new user has been created via the network site-new.php page. * * @since 4.4.0 * * @param int $user_id ID of the newly created user. */ do_action( 'network_site_new_created_user', $user_id ); } $wpdb->hide_errors(); $id = wpmu_create_blog( $newdomain, $path, $title, $user_id, $meta, get_current_network_id() ); $wpdb->show_errors(); if ( ! is_wp_error( $id ) ) { if ( ! is_super_admin( $user_id ) && ! get_user_option( 'primary_blog', $user_id ) ) { update_user_option( $user_id, 'primary_blog', $id, true ); } wpmu_new_site_admin_notification( $id, $user_id ); wpmu_welcome_notification( $id, $user_id, $password, $title, array( 'public' => 1 ) ); wp_redirect( add_query_arg( array( 'update' => 'added', 'id' => $id, ), 'site-new.php' ) ); exit; } else { wp_die( $id->get_error_message() ); } } if ( isset( $_GET['update'] ) ) { $messages = array(); if ( 'added' === $_GET['update'] ) { $messages[] = sprintf( /* translators: 1: Dashboard URL, 2: Network admin edit URL. */ __( 'Site added. <a href="%1$s">Visit Dashboard</a> or <a href="%2$s">Edit Site</a>' ), esc_url( get_admin_url( absint( $_GET['id'] ) ) ), network_admin_url( 'site-info.php?id=' . absint( $_GET['id'] ) ) ); } } // Used in the HTML title tag. $title = __( 'Add New Site' ); $parent_file = 'sites.php'; wp_enqueue_script( 'user-suggest' ); require_once ABSPATH . 'wp-admin/admin-header.php'; ?> <div class="wrap"> <h1 id="add-new-site"><?php _e( 'Add New Site' ); ?></h1> <?php if ( ! empty( $messages ) ) { $notice_args = array( 'type' => 'success', 'dismissible' => true, 'id' => 'message', ); foreach ( $messages as $msg ) { wp_admin_notice( $msg, $notice_args ); } } ?> <p><?php echo wp_required_field_message(); ?></p> <form method="post" action="<?php echo esc_url( network_admin_url( 'site-new.php?action=add-site' ) ); ?>" novalidate="novalidate"> <?php wp_nonce_field( 'add-blog', '_wpnonce_add-blog' ); ?> <table class="form-table" role="presentation"> <tr class="form-field form-required"> <th scope="row"> <label for="site-address"> <?php _e( 'Site Address (URL)' ); echo ' ' . wp_required_field_indicator(); ?> </label> </th> <td> <?php if ( is_subdomain_install() ) { ?> <input name="blog[domain]" type="text" class="regular-text ltr" id="site-address" aria-describedby="site-address-desc" autocapitalize="none" autocorrect="off" required /><span class="no-break">.<?php echo preg_replace( '|^www\.|', '', get_network()->domain ); ?></span> <?php } else { echo get_network()->domain . get_network()->path ?> <input name="blog[domain]" type="text" class="regular-text ltr" id="site-address" aria-describedby="site-address-desc" autocapitalize="none" autocorrect="off" required /> <?php } echo '<p class="description" id="site-address-desc">' . __( 'Only lowercase letters (a-z), numbers, and hyphens are allowed.' ) . '</p>'; ?> </td> </tr> <tr class="form-field form-required"> <th scope="row"> <label for="site-title"> <?php _e( 'Site Title' ); echo ' ' . wp_required_field_indicator(); ?> </label> </th> <td><input name="blog[title]" type="text" class="regular-text" id="site-title" required /></td> </tr> <?php $languages = get_available_languages(); $translations = wp_get_available_translations(); if ( ! empty( $languages ) || ! empty( $translations ) ) : ?> <tr class="form-field form-required"> <th scope="row"><label for="site-language"><?php _e( 'Site Language' ); ?></label></th> <td> <?php // Network default. $lang = get_site_option( 'WPLANG' ); // Use English if the default isn't available. if ( ! in_array( $lang, $languages, true ) ) { $lang = ''; } wp_dropdown_languages( array( 'name' => 'WPLANG', 'id' => 'site-language', 'selected' => $lang, 'languages' => $languages, 'translations' => $translations, 'show_available_translations' => current_user_can( 'install_languages' ) && wp_can_install_language_pack(), ) ); ?> </td> </tr> <?php endif; // Languages. ?> <tr class="form-field form-required"> <th scope="row"> <label for="admin-email"> <?php _e( 'Admin Email' ); echo ' ' . wp_required_field_indicator(); ?> </label> </th> <td><input name="blog[email]" type="email" class="regular-text wp-suggest-user" id="admin-email" data-autocomplete-type="search" data-autocomplete-field="user_email" aria-describedby="site-admin-email" required /></td> </tr> <tr class="form-field"> <td colspan="2" class="td-full"><p id="site-admin-email"><?php _e( 'A new user will be created if the above email address is not in the database.' ); ?><br /><?php _e( 'The username and a link to set the password will be mailed to this email address.' ); ?></p></td> </tr> </table> <?php /** * Fires at the end of the new site form in network admin. * * @since 4.5.0 */ do_action( 'network_site_new_form' ); submit_button( __( 'Add Site' ), 'primary', 'add-site' ); ?> </form> </div> <?php require_once ABSPATH . 'wp-admin/admin-footer.php'; network/upgrade.php 0000644 00000011512 15135536347 0010411 0 ustar 00 <?php /** * Multisite upgrade administration panel. * * @package WordPress * @subpackage Multisite * @since 3.0.0 */ /** Load WordPress Administration Bootstrap */ require_once __DIR__ . '/admin.php'; require_once ABSPATH . WPINC . '/http.php'; // Used in the HTML title tag. $title = __( 'Upgrade Network' ); $parent_file = 'upgrade.php'; get_current_screen()->add_help_tab( array( 'id' => 'overview', 'title' => __( 'Overview' ), 'content' => '<p>' . __( 'Only use this screen once you have updated to a new version of WordPress through Updates/Available Updates (via the Network Administration navigation menu or the Toolbar). Clicking the Upgrade Network button will step through each site in the network, five at a time, and make sure any database updates are applied.' ) . '</p>' . '<p>' . __( 'If a version update to core has not happened, clicking this button will not affect anything.' ) . '</p>' . '<p>' . __( 'If this process fails for any reason, users logging in to their sites will force the same update.' ) . '</p>', ) ); get_current_screen()->set_help_sidebar( '<p><strong>' . __( 'For more information:' ) . '</strong></p>' . '<p>' . __( '<a href="https://wordpress.org/documentation/article/network-admin-updates-screen/">Documentation on Upgrade Network</a>' ) . '</p>' . '<p>' . __( '<a href="https://wordpress.org/support/forums/">Support forums</a>' ) . '</p>' ); require_once ABSPATH . 'wp-admin/admin-header.php'; if ( ! current_user_can( 'upgrade_network' ) ) { wp_die( __( 'Sorry, you are not allowed to access this page.' ), 403 ); } echo '<div class="wrap">'; echo '<h1>' . __( 'Upgrade Network' ) . '</h1>'; $action = isset( $_GET['action'] ) ? $_GET['action'] : 'show'; switch ( $action ) { case 'upgrade': $n = ( isset( $_GET['n'] ) ) ? (int) $_GET['n'] : 0; if ( $n < 5 ) { /** * @global int $wp_db_version WordPress database version. */ global $wp_db_version; update_site_option( 'wpmu_upgrade_site', $wp_db_version ); } $site_ids = get_sites( array( 'spam' => 0, 'deleted' => 0, 'archived' => 0, 'network_id' => get_current_network_id(), 'number' => 5, 'offset' => $n, 'fields' => 'ids', 'order' => 'DESC', 'orderby' => 'id', 'update_site_meta_cache' => false, ) ); if ( empty( $site_ids ) ) { echo '<p>' . __( 'All done!' ) . '</p>'; break; } echo '<ul>'; foreach ( (array) $site_ids as $site_id ) { switch_to_blog( $site_id ); $siteurl = site_url(); $upgrade_url = admin_url( 'upgrade.php?step=upgrade_db' ); restore_current_blog(); echo "<li>$siteurl</li>"; $response = wp_remote_get( $upgrade_url, array( 'timeout' => 120, 'httpversion' => '1.1', 'sslverify' => false, ) ); if ( is_wp_error( $response ) ) { wp_die( sprintf( /* translators: 1: Site URL, 2: Server error message. */ __( 'Warning! Problem updating %1$s. Your server may not be able to connect to sites running on it. Error message: %2$s' ), $siteurl, '<em>' . $response->get_error_message() . '</em>' ) ); } /** * Fires after the Multisite DB upgrade for each site is complete. * * @since MU (3.0.0) * * @param array $response The upgrade response array. */ do_action( 'after_mu_upgrade', $response ); /** * Fires after each site has been upgraded. * * @since MU (3.0.0) * * @param int $site_id The Site ID. */ do_action( 'wpmu_upgrade_site', $site_id ); } echo '</ul>'; ?><p><?php _e( 'If your browser does not start loading the next page automatically, click this link:' ); ?> <a class="button" href="upgrade.php?action=upgrade&n=<?php echo ( $n + 5 ); ?>"><?php _e( 'Next Sites' ); ?></a></p> <script type="text/javascript"> <!-- function nextpage() { location.href = "upgrade.php?action=upgrade&n=<?php echo ( $n + 5 ); ?>"; } setTimeout( "nextpage()", 250 ); //--> </script> <?php break; case 'show': default: if ( (int) get_site_option( 'wpmu_upgrade_site' ) !== $GLOBALS['wp_db_version'] ) : ?> <h2><?php _e( 'Database Update Required' ); ?></h2> <p><?php _e( 'WordPress has been updated! Next and final step is to individually upgrade the sites in your network.' ); ?></p> <?php endif; ?> <p><?php _e( 'The database update process may take a little while, so please be patient.' ); ?></p> <p><a class="button button-primary" href="upgrade.php?action=upgrade"><?php _e( 'Upgrade Network' ); ?></a></p> <?php /** * Fires before the footer on the network upgrade screen. * * @since MU (3.0.0) */ do_action( 'wpmu_upgrade_page' ); break; } ?> </div> <?php require_once ABSPATH . 'wp-admin/admin-footer.php'; ?> network/.network.php 0000644 00000000000 15135536347 0010517 0 ustar 00 network/themes.php 0000644 00000037171 15135536347 0010260 0 ustar 00 <?php /** * Multisite themes administration panel. * * @package WordPress * @subpackage Multisite * @since 3.1.0 */ /** Load WordPress Administration Bootstrap */ require_once __DIR__ . '/admin.php'; if ( ! current_user_can( 'manage_network_themes' ) ) { wp_die( __( 'Sorry, you are not allowed to manage network themes.' ) ); } $wp_list_table = _get_list_table( 'WP_MS_Themes_List_Table' ); $pagenum = $wp_list_table->get_pagenum(); $action = $wp_list_table->current_action(); $s = isset( $_REQUEST['s'] ) ? $_REQUEST['s'] : ''; // Clean up request URI from temporary args for screen options/paging uri's to work as expected. $temp_args = array( 'enabled', 'disabled', 'deleted', 'error', 'enabled-auto-update', 'disabled-auto-update', ); $_SERVER['REQUEST_URI'] = remove_query_arg( $temp_args, $_SERVER['REQUEST_URI'] ); $referer = remove_query_arg( $temp_args, wp_get_referer() ); if ( $action ) { switch ( $action ) { case 'enable': check_admin_referer( 'enable-theme_' . $_GET['theme'] ); WP_Theme::network_enable_theme( $_GET['theme'] ); if ( ! str_contains( $referer, '/network/themes.php' ) ) { wp_redirect( network_admin_url( 'themes.php?enabled=1' ) ); } else { wp_safe_redirect( add_query_arg( 'enabled', 1, $referer ) ); } exit; case 'disable': check_admin_referer( 'disable-theme_' . $_GET['theme'] ); WP_Theme::network_disable_theme( $_GET['theme'] ); wp_safe_redirect( add_query_arg( 'disabled', '1', $referer ) ); exit; case 'enable-selected': check_admin_referer( 'bulk-themes' ); $themes = isset( $_POST['checked'] ) ? (array) $_POST['checked'] : array(); if ( empty( $themes ) ) { wp_safe_redirect( add_query_arg( 'error', 'none', $referer ) ); exit; } WP_Theme::network_enable_theme( (array) $themes ); wp_safe_redirect( add_query_arg( 'enabled', count( $themes ), $referer ) ); exit; case 'disable-selected': check_admin_referer( 'bulk-themes' ); $themes = isset( $_POST['checked'] ) ? (array) $_POST['checked'] : array(); if ( empty( $themes ) ) { wp_safe_redirect( add_query_arg( 'error', 'none', $referer ) ); exit; } WP_Theme::network_disable_theme( (array) $themes ); wp_safe_redirect( add_query_arg( 'disabled', count( $themes ), $referer ) ); exit; case 'update-selected': check_admin_referer( 'bulk-themes' ); if ( isset( $_GET['themes'] ) ) { $themes = explode( ',', $_GET['themes'] ); } elseif ( isset( $_POST['checked'] ) ) { $themes = (array) $_POST['checked']; } else { $themes = array(); } // Used in the HTML title tag. $title = __( 'Update Themes' ); $parent_file = 'themes.php'; require_once ABSPATH . 'wp-admin/admin-header.php'; echo '<div class="wrap">'; echo '<h1>' . esc_html( $title ) . '</h1>'; $url = self_admin_url( 'update.php?action=update-selected-themes&themes=' . urlencode( implode( ',', $themes ) ) ); $url = wp_nonce_url( $url, 'bulk-update-themes' ); echo "<iframe src='$url' style='width: 100%; height:100%; min-height:850px;'></iframe>"; echo '</div>'; require_once ABSPATH . 'wp-admin/admin-footer.php'; exit; case 'delete-selected': if ( ! current_user_can( 'delete_themes' ) ) { wp_die( __( 'Sorry, you are not allowed to delete themes for this site.' ) ); } check_admin_referer( 'bulk-themes' ); $themes = isset( $_REQUEST['checked'] ) ? (array) $_REQUEST['checked'] : array(); if ( empty( $themes ) ) { wp_safe_redirect( add_query_arg( 'error', 'none', $referer ) ); exit; } $themes = array_diff( $themes, array( get_option( 'stylesheet' ), get_option( 'template' ) ) ); if ( empty( $themes ) ) { wp_safe_redirect( add_query_arg( 'error', 'main', $referer ) ); exit; } $theme_info = array(); foreach ( $themes as $key => $theme ) { $theme_info[ $theme ] = wp_get_theme( $theme ); } require ABSPATH . 'wp-admin/update.php'; $parent_file = 'themes.php'; if ( ! isset( $_REQUEST['verify-delete'] ) ) { wp_enqueue_script( 'jquery' ); require_once ABSPATH . 'wp-admin/admin-header.php'; $themes_to_delete = count( $themes ); ?> <div class="wrap"> <?php if ( 1 === $themes_to_delete ) : ?> <h1><?php _e( 'Delete Theme' ); ?></h1> <?php wp_admin_notice( '<strong>' . __( 'Caution:' ) . '</strong> ' . __( 'This theme may be active on other sites in the network.' ), array( 'additional_classes' => array( 'error' ), ) ); ?> <p><?php _e( 'You are about to remove the following theme:' ); ?></p> <?php else : ?> <h1><?php _e( 'Delete Themes' ); ?></h1> <?php wp_admin_notice( '<strong>' . __( 'Caution:' ) . '</strong> ' . __( 'These themes may be active on other sites in the network.' ), array( 'additional_classes' => array( 'error' ), ) ); ?> <p><?php _e( 'You are about to remove the following themes:' ); ?></p> <?php endif; ?> <ul class="ul-disc"> <?php foreach ( $theme_info as $theme ) { echo '<li>' . sprintf( /* translators: 1: Theme name, 2: Theme author. */ _x( '%1$s by %2$s', 'theme' ), '<strong>' . $theme->display( 'Name' ) . '</strong>', '<em>' . $theme->display( 'Author' ) . '</em>' ) . '</li>'; } ?> </ul> <?php if ( 1 === $themes_to_delete ) : ?> <p><?php _e( 'Are you sure you want to delete this theme?' ); ?></p> <?php else : ?> <p><?php _e( 'Are you sure you want to delete these themes?' ); ?></p> <?php endif; ?> <form method="post" action="<?php echo esc_url( $_SERVER['REQUEST_URI'] ); ?>" style="display:inline;"> <input type="hidden" name="verify-delete" value="1" /> <input type="hidden" name="action" value="delete-selected" /> <?php foreach ( (array) $themes as $theme ) { echo '<input type="hidden" name="checked[]" value="' . esc_attr( $theme ) . '" />'; } wp_nonce_field( 'bulk-themes' ); if ( 1 === $themes_to_delete ) { submit_button( __( 'Yes, delete this theme' ), '', 'submit', false ); } else { submit_button( __( 'Yes, delete these themes' ), '', 'submit', false ); } ?> </form> <?php $referer = wp_get_referer(); ?> <form method="post" action="<?php echo $referer ? esc_url( $referer ) : ''; ?>" style="display:inline;"> <?php submit_button( __( 'No, return me to the theme list' ), '', 'submit', false ); ?> </form> </div> <?php require_once ABSPATH . 'wp-admin/admin-footer.php'; exit; } // End if verify-delete. foreach ( $themes as $theme ) { $delete_result = delete_theme( $theme, esc_url( add_query_arg( array( 'verify-delete' => 1, 'action' => 'delete-selected', 'checked' => $_REQUEST['checked'], '_wpnonce' => $_REQUEST['_wpnonce'], ), network_admin_url( 'themes.php' ) ) ) ); } $paged = ( $_REQUEST['paged'] ) ? $_REQUEST['paged'] : 1; wp_redirect( add_query_arg( array( 'deleted' => count( $themes ), 'paged' => $paged, 's' => $s, ), network_admin_url( 'themes.php' ) ) ); exit; case 'enable-auto-update': case 'disable-auto-update': case 'enable-auto-update-selected': case 'disable-auto-update-selected': if ( ! ( current_user_can( 'update_themes' ) && wp_is_auto_update_enabled_for_type( 'theme' ) ) ) { wp_die( __( 'Sorry, you are not allowed to change themes automatic update settings.' ) ); } if ( 'enable-auto-update' === $action || 'disable-auto-update' === $action ) { check_admin_referer( 'updates' ); } else { if ( empty( $_POST['checked'] ) ) { // Nothing to do. wp_safe_redirect( add_query_arg( 'error', 'none', $referer ) ); exit; } check_admin_referer( 'bulk-themes' ); } $auto_updates = (array) get_site_option( 'auto_update_themes', array() ); if ( 'enable-auto-update' === $action ) { $auto_updates[] = $_GET['theme']; $auto_updates = array_unique( $auto_updates ); $referer = add_query_arg( 'enabled-auto-update', 1, $referer ); } elseif ( 'disable-auto-update' === $action ) { $auto_updates = array_diff( $auto_updates, array( $_GET['theme'] ) ); $referer = add_query_arg( 'disabled-auto-update', 1, $referer ); } else { // Bulk enable/disable. $themes = (array) wp_unslash( $_POST['checked'] ); if ( 'enable-auto-update-selected' === $action ) { $auto_updates = array_merge( $auto_updates, $themes ); $auto_updates = array_unique( $auto_updates ); $referer = add_query_arg( 'enabled-auto-update', count( $themes ), $referer ); } else { $auto_updates = array_diff( $auto_updates, $themes ); $referer = add_query_arg( 'disabled-auto-update', count( $themes ), $referer ); } } $all_items = wp_get_themes(); // Remove themes that don't exist or have been deleted since the option was last updated. $auto_updates = array_intersect( $auto_updates, array_keys( $all_items ) ); update_site_option( 'auto_update_themes', $auto_updates ); wp_safe_redirect( $referer ); exit; default: $themes = isset( $_POST['checked'] ) ? (array) $_POST['checked'] : array(); if ( empty( $themes ) ) { wp_safe_redirect( add_query_arg( 'error', 'none', $referer ) ); exit; } check_admin_referer( 'bulk-themes' ); /** This action is documented in wp-admin/network/site-themes.php */ $referer = apply_filters( 'handle_network_bulk_actions-' . get_current_screen()->id, $referer, $action, $themes ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores wp_safe_redirect( $referer ); exit; } } $wp_list_table->prepare_items(); add_thickbox(); add_screen_option( 'per_page' ); get_current_screen()->add_help_tab( array( 'id' => 'overview', 'title' => __( 'Overview' ), 'content' => '<p>' . __( 'This screen enables and disables the inclusion of themes available to choose in the Appearance menu for each site. It does not activate or deactivate which theme a site is currently using.' ) . '</p>' . '<p>' . __( 'If the network admin disables a theme that is in use, it can still remain selected on that site. If another theme is chosen, the disabled theme will not appear in the site’s Appearance > Themes screen.' ) . '</p>' . '<p>' . __( 'Themes can be enabled on a site by site basis by the network admin on the Edit Site screen (which has a Themes tab); get there via the Edit action link on the All Sites screen. Only network admins are able to install or edit themes.' ) . '</p>', ) ); $help_sidebar_autoupdates = ''; if ( current_user_can( 'update_themes' ) && wp_is_auto_update_enabled_for_type( 'theme' ) ) { get_current_screen()->add_help_tab( array( 'id' => 'plugins-themes-auto-updates', 'title' => __( 'Auto-updates' ), 'content' => '<p>' . __( 'Auto-updates can be enabled or disabled for each individual theme. Themes with auto-updates enabled will display the estimated date of the next auto-update. Auto-updates depends on the WP-Cron task scheduling system.' ) . '</p>' . '<p>' . __( 'Please note: Third-party themes and plugins, or custom code, may override WordPress scheduling.' ) . '</p>', ) ); $help_sidebar_autoupdates = '<p>' . __( '<a href="https://wordpress.org/documentation/article/plugins-themes-auto-updates/">Documentation on Auto-updates</a>' ) . '</p>'; } get_current_screen()->set_help_sidebar( '<p><strong>' . __( 'For more information:' ) . '</strong></p>' . '<p>' . __( '<a href="https://codex.wordpress.org/Network_Admin_Themes_Screen">Documentation on Network Themes</a>' ) . '</p>' . $help_sidebar_autoupdates . '<p>' . __( '<a href="https://wordpress.org/support/forums/">Support forums</a>' ) . '</p>' ); get_current_screen()->set_screen_reader_content( array( 'heading_views' => __( 'Filter themes list' ), 'heading_pagination' => __( 'Themes list navigation' ), 'heading_list' => __( 'Themes list' ), ) ); // Used in the HTML title tag. $title = __( 'Themes' ); $parent_file = 'themes.php'; wp_enqueue_script( 'updates' ); wp_enqueue_script( 'theme-preview' ); require_once ABSPATH . 'wp-admin/admin-header.php'; ?> <div class="wrap"> <h1 class="wp-heading-inline"><?php echo esc_html( $title ); ?></h1> <?php if ( current_user_can( 'install_themes' ) ) : ?> <a href="theme-install.php" class="page-title-action"><?php echo esc_html__( 'Add New Theme' ); ?></a> <?php endif; ?> <?php if ( isset( $_REQUEST['s'] ) && strlen( $_REQUEST['s'] ) ) { echo '<span class="subtitle">'; printf( /* translators: %s: Search query. */ __( 'Search results for: %s' ), '<strong>' . esc_html( $s ) . '</strong>' ); echo '</span>'; } ?> <hr class="wp-header-end"> <?php $message = ''; $type = 'success'; if ( isset( $_GET['enabled'] ) ) { $enabled = absint( $_GET['enabled'] ); if ( 1 === $enabled ) { $message = __( 'Theme enabled.' ); } else { $message = sprintf( /* translators: %s: Number of themes. */ _n( '%s theme enabled.', '%s themes enabled.', $enabled ), number_format_i18n( $enabled ) ); } } elseif ( isset( $_GET['disabled'] ) ) { $disabled = absint( $_GET['disabled'] ); if ( 1 === $disabled ) { $message = __( 'Theme disabled.' ); } else { $message = sprintf( /* translators: %s: Number of themes. */ _n( '%s theme disabled.', '%s themes disabled.', $disabled ), number_format_i18n( $disabled ) ); } } elseif ( isset( $_GET['deleted'] ) ) { $deleted = absint( $_GET['deleted'] ); if ( 1 === $deleted ) { $message = __( 'Theme deleted.' ); } else { $message = sprintf( /* translators: %s: Number of themes. */ _n( '%s theme deleted.', '%s themes deleted.', $deleted ), number_format_i18n( $deleted ) ); } } elseif ( isset( $_GET['enabled-auto-update'] ) ) { $enabled = absint( $_GET['enabled-auto-update'] ); if ( 1 === $enabled ) { $message = __( 'Theme will be auto-updated.' ); } else { $message = sprintf( /* translators: %s: Number of themes. */ _n( '%s theme will be auto-updated.', '%s themes will be auto-updated.', $enabled ), number_format_i18n( $enabled ) ); } } elseif ( isset( $_GET['disabled-auto-update'] ) ) { $disabled = absint( $_GET['disabled-auto-update'] ); if ( 1 === $disabled ) { $message = __( 'Theme will no longer be auto-updated.' ); } else { $message = sprintf( /* translators: %s: Number of themes. */ _n( '%s theme will no longer be auto-updated.', '%s themes will no longer be auto-updated.', $disabled ), number_format_i18n( $disabled ) ); } } elseif ( isset( $_GET['error'] ) && 'none' === $_GET['error'] ) { $message = __( 'No theme selected.' ); $type = 'error'; } elseif ( isset( $_GET['error'] ) && 'main' === $_GET['error'] ) { $message = __( 'You cannot delete a theme while it is active on the main site.' ); $type = 'error'; } if ( '' !== $message ) { wp_admin_notice( $message, array( 'type' => $type, 'dismissible' => true, 'id' => 'message', ) ); } ?> <form method="get"> <?php $wp_list_table->search_box( __( 'Search Installed Themes' ), 'theme' ); ?> </form> <?php $wp_list_table->views(); if ( 'broken' === $status ) { echo '<p class="clear">' . __( 'The following themes are installed but incomplete.' ) . '</p>'; } ?> <form id="bulk-action-form" method="post"> <input type="hidden" name="theme_status" value="<?php echo esc_attr( $status ); ?>" /> <input type="hidden" name="paged" value="<?php echo esc_attr( $page ); ?>" /> <?php $wp_list_table->display(); ?> </form> </div> <?php wp_print_request_filesystem_credentials_modal(); wp_print_admin_notice_templates(); wp_print_update_row_templates(); require_once ABSPATH . 'wp-admin/admin-footer.php'; network/admin.php 0000644 00000002000 15135536347 0010042 0 ustar 00 <?php /** * WordPress Network Administration Bootstrap * * @package WordPress * @subpackage Multisite * @since 3.1.0 */ define( 'WP_NETWORK_ADMIN', true ); /** Load WordPress Administration Bootstrap */ require_once dirname( __DIR__ ) . '/admin.php'; // Do not remove this check. It is required by individual network admin pages. if ( ! is_multisite() ) { wp_die( __( 'Multisite support is not enabled.' ) ); } $redirect_network_admin_request = ( 0 !== strcasecmp( $current_blog->domain, $current_site->domain ) || 0 !== strcasecmp( $current_blog->path, $current_site->path ) ); /** * Filters whether to redirect the request to the Network Admin. * * @since 3.2.0 * * @param bool $redirect_network_admin_request Whether the request should be redirected. */ $redirect_network_admin_request = apply_filters( 'redirect_network_admin_request', $redirect_network_admin_request ); if ( $redirect_network_admin_request ) { wp_redirect( network_admin_url() ); exit; } unset( $redirect_network_admin_request ); network/about.php 0000644 00000000365 15135536347 0010100 0 ustar 00 <?php /** * Network About administration panel. * * @package WordPress * @subpackage Multisite * @since 3.4.0 */ /** Load WordPress Administration Bootstrap */ require_once __DIR__ . '/admin.php'; require ABSPATH . 'wp-admin/about.php'; network/users.php 0000644 00000022432 15135536347 0010126 0 ustar 00 <?php /** * Multisite users administration panel. * * @package WordPress * @subpackage Multisite * @since 3.0.0 */ /** Load WordPress Administration Bootstrap */ require_once __DIR__ . '/admin.php'; if ( ! current_user_can( 'manage_network_users' ) ) { wp_die( __( 'Sorry, you are not allowed to access this page.' ), 403 ); } if ( isset( $_GET['action'] ) ) { /** This action is documented in wp-admin/network/edit.php */ do_action( 'wpmuadminedit' ); switch ( $_GET['action'] ) { case 'deleteuser': if ( ! current_user_can( 'manage_network_users' ) ) { wp_die( __( 'Sorry, you are not allowed to access this page.' ), 403 ); } check_admin_referer( 'deleteuser' ); $id = (int) $_GET['id']; if ( $id > 1 ) { $_POST['allusers'] = array( $id ); // confirm_delete_users() can only handle arrays. // Used in the HTML title tag. $title = __( 'Users' ); $parent_file = 'users.php'; require_once ABSPATH . 'wp-admin/admin-header.php'; echo '<div class="wrap">'; confirm_delete_users( $_POST['allusers'] ); echo '</div>'; require_once ABSPATH . 'wp-admin/admin-footer.php'; } else { wp_redirect( network_admin_url( 'users.php' ) ); } exit; case 'allusers': if ( ! current_user_can( 'manage_network_users' ) ) { wp_die( __( 'Sorry, you are not allowed to access this page.' ), 403 ); } if ( isset( $_POST['action'] ) && isset( $_POST['allusers'] ) ) { check_admin_referer( 'bulk-users-network' ); $doaction = $_POST['action']; $userfunction = ''; foreach ( (array) $_POST['allusers'] as $user_id ) { if ( ! empty( $user_id ) ) { switch ( $doaction ) { case 'delete': if ( ! current_user_can( 'delete_users' ) ) { wp_die( __( 'Sorry, you are not allowed to access this page.' ), 403 ); } // Used in the HTML title tag. $title = __( 'Users' ); $parent_file = 'users.php'; require_once ABSPATH . 'wp-admin/admin-header.php'; echo '<div class="wrap">'; confirm_delete_users( $_POST['allusers'] ); echo '</div>'; require_once ABSPATH . 'wp-admin/admin-footer.php'; exit; case 'spam': $user = get_userdata( $user_id ); if ( is_super_admin( $user->ID ) ) { wp_die( sprintf( /* translators: %s: User login. */ __( 'Warning! User cannot be modified. The user %s is a network administrator.' ), esc_html( $user->user_login ) ) ); } $userfunction = 'all_spam'; $blogs = get_blogs_of_user( $user_id, true ); foreach ( (array) $blogs as $details ) { if ( ! is_main_site( $details->userblog_id ) ) { // Main site is not a spam! update_blog_status( $details->userblog_id, 'spam', '1' ); } } $user_data = $user->to_array(); $user_data['spam'] = '1'; wp_update_user( $user_data ); break; case 'notspam': $user = get_userdata( $user_id ); $userfunction = 'all_notspam'; $blogs = get_blogs_of_user( $user_id, true ); foreach ( (array) $blogs as $details ) { update_blog_status( $details->userblog_id, 'spam', '0' ); } $user_data = $user->to_array(); $user_data['spam'] = '0'; wp_update_user( $user_data ); break; } } } if ( ! in_array( $doaction, array( 'delete', 'spam', 'notspam' ), true ) ) { $sendback = wp_get_referer(); $user_ids = (array) $_POST['allusers']; /** This action is documented in wp-admin/network/site-themes.php */ $sendback = apply_filters( 'handle_network_bulk_actions-' . get_current_screen()->id, $sendback, $doaction, $user_ids ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores wp_safe_redirect( $sendback ); exit; } wp_safe_redirect( add_query_arg( array( 'updated' => 'true', 'action' => $userfunction, ), wp_get_referer() ) ); } else { $location = network_admin_url( 'users.php' ); if ( ! empty( $_REQUEST['paged'] ) ) { $location = add_query_arg( 'paged', (int) $_REQUEST['paged'], $location ); } wp_redirect( $location ); } exit; case 'dodelete': check_admin_referer( 'ms-users-delete' ); if ( ! ( current_user_can( 'manage_network_users' ) && current_user_can( 'delete_users' ) ) ) { wp_die( __( 'Sorry, you are not allowed to access this page.' ), 403 ); } if ( ! empty( $_POST['blog'] ) && is_array( $_POST['blog'] ) ) { foreach ( $_POST['blog'] as $id => $users ) { foreach ( $users as $blogid => $user_id ) { if ( ! current_user_can( 'delete_user', $id ) ) { continue; } if ( ! empty( $_POST['delete'] ) && 'reassign' === $_POST['delete'][ $blogid ][ $id ] ) { remove_user_from_blog( $id, $blogid, (int) $user_id ); } else { remove_user_from_blog( $id, $blogid ); } } } } $i = 0; if ( is_array( $_POST['user'] ) && ! empty( $_POST['user'] ) ) { foreach ( $_POST['user'] as $id ) { if ( ! current_user_can( 'delete_user', $id ) ) { continue; } wpmu_delete_user( $id ); ++$i; } } if ( 1 === $i ) { $deletefunction = 'delete'; } else { $deletefunction = 'all_delete'; } wp_redirect( add_query_arg( array( 'updated' => 'true', 'action' => $deletefunction, ), network_admin_url( 'users.php' ) ) ); exit; } } $wp_list_table = _get_list_table( 'WP_MS_Users_List_Table' ); $pagenum = $wp_list_table->get_pagenum(); $wp_list_table->prepare_items(); $total_pages = $wp_list_table->get_pagination_arg( 'total_pages' ); if ( $pagenum > $total_pages && $total_pages > 0 ) { wp_redirect( add_query_arg( 'paged', $total_pages ) ); exit; } // Used in the HTML title tag. $title = __( 'Users' ); $parent_file = 'users.php'; add_screen_option( 'per_page' ); get_current_screen()->add_help_tab( array( 'id' => 'overview', 'title' => __( 'Overview' ), 'content' => '<p>' . __( 'This table shows all users across the network and the sites to which they are assigned.' ) . '</p>' . '<p>' . __( 'Hover over any user on the list to make the edit links appear. The Edit link on the left will take you to their Edit User profile page; the Edit link on the right by any site name goes to an Edit Site screen for that site.' ) . '</p>' . '<p>' . __( 'You can also go to the user’s profile page by clicking on the individual username.' ) . '</p>' . '<p>' . __( 'You can sort the table by clicking on any of the table headings and switch between list and excerpt views by using the icons above the users list.' ) . '</p>' . '<p>' . __( 'The bulk action will permanently delete selected users, or mark/unmark those selected as spam. Spam users will have posts removed and will be unable to sign up again with the same email addresses.' ) . '</p>' . '<p>' . __( 'You can make an existing user an additional super admin by going to the Edit User profile page and checking the box to grant that privilege.' ) . '</p>', ) ); get_current_screen()->set_help_sidebar( '<p><strong>' . __( 'For more information:' ) . '</strong></p>' . '<p>' . __( '<a href="https://codex.wordpress.org/Network_Admin_Users_Screen">Documentation on Network Users</a>' ) . '</p>' . '<p>' . __( '<a href="https://wordpress.org/support/forum/multisite/">Support forums</a>' ) . '</p>' ); get_current_screen()->set_screen_reader_content( array( 'heading_views' => __( 'Filter users list' ), 'heading_pagination' => __( 'Users list navigation' ), 'heading_list' => __( 'Users list' ), ) ); require_once ABSPATH . 'wp-admin/admin-header.php'; if ( isset( $_REQUEST['updated'] ) && 'true' === $_REQUEST['updated'] && ! empty( $_REQUEST['action'] ) ) { $message = ''; switch ( $_REQUEST['action'] ) { case 'delete': $message = __( 'User deleted.' ); break; case 'all_spam': $message = __( 'Users marked as spam.' ); break; case 'all_notspam': $message = __( 'Users removed from spam.' ); break; case 'all_delete': $message = __( 'Users deleted.' ); break; case 'add': $message = __( 'User added.' ); break; } wp_admin_notice( $message, array( 'type' => 'success', 'dismissible' => true, 'id' => 'message', ) ); } ?> <div class="wrap"> <h1 class="wp-heading-inline"><?php esc_html_e( 'Users' ); ?></h1> <?php if ( current_user_can( 'create_users' ) ) : ?> <a href="<?php echo esc_url( network_admin_url( 'user-new.php' ) ); ?>" class="page-title-action"><?php echo esc_html__( 'Add New User' ); ?></a> <?php endif; if ( strlen( $usersearch ) ) { echo '<span class="subtitle">'; printf( /* translators: %s: Search query. */ __( 'Search results for: %s' ), '<strong>' . esc_html( $usersearch ) . '</strong>' ); echo '</span>'; } ?> <hr class="wp-header-end"> <?php $wp_list_table->views(); ?> <form method="get" class="search-form"> <?php $wp_list_table->search_box( __( 'Search Users' ), 'all-user' ); ?> </form> <form id="form-user-list" action="users.php?action=allusers" method="post"> <?php $wp_list_table->display(); ?> </form> </div> <?php require_once ABSPATH . 'wp-admin/admin-footer.php'; ?> network/credits.php 0000644 00000000371 15135536347 0010420 0 ustar 00 <?php /** * Network Credits administration panel. * * @package WordPress * @subpackage Multisite * @since 3.4.0 */ /** Load WordPress Administration Bootstrap */ require_once __DIR__ . '/admin.php'; require ABSPATH . 'wp-admin/credits.php'; network/plugin-editor.php 0000644 00000000412 15135536347 0011541 0 ustar 00 <?php /** * Plugin file editor network administration panel. * * @package WordPress * @subpackage Multisite * @since 3.1.0 */ /** Load WordPress Administration Bootstrap */ require_once __DIR__ . '/admin.php'; require ABSPATH . 'wp-admin/plugin-editor.php'; network/setup.php 0000644 00000000367 15135536347 0010130 0 ustar 00 <?php /** * Network Setup administration panel. * * @package WordPress * @subpackage Multisite * @since 3.1.0 */ /** Load WordPress Administration Bootstrap */ require_once __DIR__ . '/admin.php'; require ABSPATH . 'wp-admin/network.php'; network/profile.php 0000644 00000000376 15135536347 0010430 0 ustar 00 <?php /** * User profile network administration panel. * * @package WordPress * @subpackage Multisite * @since 3.1.0 */ /** Load WordPress Administration Bootstrap */ require_once __DIR__ . '/admin.php'; require ABSPATH . 'wp-admin/profile.php'; network/plugins.php 0000644 00000000371 15135536347 0010444 0 ustar 00 <?php /** * Network Plugins administration panel. * * @package WordPress * @subpackage Multisite * @since 3.1.0 */ /** Load WordPress Administration Bootstrap */ require_once __DIR__ . '/admin.php'; require ABSPATH . 'wp-admin/plugins.php'; network/update-core.php 0000644 00000000375 15135536347 0011177 0 ustar 00 <?php /** * Updates network administration panel. * * @package WordPress * @subpackage Multisite * @since 3.1.0 */ /** Load WordPress Administration Bootstrap */ require_once __DIR__ . '/admin.php'; require ABSPATH . 'wp-admin/update-core.php'; network/user-edit.php 0000644 00000000375 15135536347 0010670 0 ustar 00 <?php /** * Edit user network administration panel. * * @package WordPress * @subpackage Multisite * @since 3.1.0 */ /** Load WordPress Administration Bootstrap */ require_once __DIR__ . '/admin.php'; require ABSPATH . 'wp-admin/user-edit.php'; network/site-themes.php 0000644 00000015324 15135536347 0011216 0 ustar 00 <?php /** * Edit Site Themes Administration Screen * * @package WordPress * @subpackage Multisite * @since 3.1.0 */ /** Load WordPress Administration Bootstrap */ require_once __DIR__ . '/admin.php'; if ( ! current_user_can( 'manage_sites' ) ) { wp_die( __( 'Sorry, you are not allowed to manage themes for this site.' ) ); } get_current_screen()->add_help_tab( get_site_screen_help_tab_args() ); get_current_screen()->set_help_sidebar( get_site_screen_help_sidebar_content() ); get_current_screen()->set_screen_reader_content( array( 'heading_views' => __( 'Filter site themes list' ), 'heading_pagination' => __( 'Site themes list navigation' ), 'heading_list' => __( 'Site themes list' ), ) ); $wp_list_table = _get_list_table( 'WP_MS_Themes_List_Table' ); $action = $wp_list_table->current_action(); $s = isset( $_REQUEST['s'] ) ? $_REQUEST['s'] : ''; // Clean up request URI from temporary args for screen options/paging uri's to work as expected. $temp_args = array( 'enabled', 'disabled', 'error' ); $_SERVER['REQUEST_URI'] = remove_query_arg( $temp_args, $_SERVER['REQUEST_URI'] ); $referer = remove_query_arg( $temp_args, wp_get_referer() ); if ( ! empty( $_REQUEST['paged'] ) ) { $referer = add_query_arg( 'paged', (int) $_REQUEST['paged'], $referer ); } $id = isset( $_REQUEST['id'] ) ? (int) $_REQUEST['id'] : 0; if ( ! $id ) { wp_die( __( 'Invalid site ID.' ) ); } $wp_list_table->prepare_items(); $details = get_site( $id ); if ( ! $details ) { wp_die( __( 'The requested site does not exist.' ) ); } if ( ! can_edit_network( $details->site_id ) ) { wp_die( __( 'Sorry, you are not allowed to access this page.' ), 403 ); } $is_main_site = is_main_site( $id ); if ( $action ) { switch_to_blog( $id ); $allowed_themes = get_option( 'allowedthemes' ); switch ( $action ) { case 'enable': check_admin_referer( 'enable-theme_' . $_GET['theme'] ); $theme = $_GET['theme']; $action = 'enabled'; $n = 1; if ( ! $allowed_themes ) { $allowed_themes = array( $theme => true ); } else { $allowed_themes[ $theme ] = true; } break; case 'disable': check_admin_referer( 'disable-theme_' . $_GET['theme'] ); $theme = $_GET['theme']; $action = 'disabled'; $n = 1; if ( ! $allowed_themes ) { $allowed_themes = array(); } else { unset( $allowed_themes[ $theme ] ); } break; case 'enable-selected': check_admin_referer( 'bulk-themes' ); if ( isset( $_POST['checked'] ) ) { $themes = (array) $_POST['checked']; $action = 'enabled'; $n = count( $themes ); foreach ( (array) $themes as $theme ) { $allowed_themes[ $theme ] = true; } } else { $action = 'error'; $n = 'none'; } break; case 'disable-selected': check_admin_referer( 'bulk-themes' ); if ( isset( $_POST['checked'] ) ) { $themes = (array) $_POST['checked']; $action = 'disabled'; $n = count( $themes ); foreach ( (array) $themes as $theme ) { unset( $allowed_themes[ $theme ] ); } } else { $action = 'error'; $n = 'none'; } break; default: if ( isset( $_POST['checked'] ) ) { check_admin_referer( 'bulk-themes' ); $themes = (array) $_POST['checked']; $n = count( $themes ); $screen = get_current_screen()->id; /** * Fires when a custom bulk action should be handled. * * The redirect link should be modified with success or failure feedback * from the action to be used to display feedback to the user. * * The dynamic portion of the hook name, `$screen`, refers to the current screen ID. * * @since 4.7.0 * * @param string $redirect_url The redirect URL. * @param string $action The action being taken. * @param array $items The items to take the action on. * @param int $site_id The site ID. */ $referer = apply_filters( "handle_network_bulk_actions-{$screen}", $referer, $action, $themes, $id ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores } else { $action = 'error'; $n = 'none'; } } update_option( 'allowedthemes', $allowed_themes ); restore_current_blog(); wp_safe_redirect( add_query_arg( array( 'id' => $id, $action => $n, ), $referer ) ); exit; } if ( isset( $_GET['action'] ) && 'update-site' === $_GET['action'] ) { wp_safe_redirect( $referer ); exit; } add_thickbox(); add_screen_option( 'per_page' ); // Used in the HTML title tag. /* translators: %s: Site title. */ $title = sprintf( __( 'Edit Site: %s' ), esc_html( $details->blogname ) ); $parent_file = 'sites.php'; $submenu_file = 'sites.php'; require_once ABSPATH . 'wp-admin/admin-header.php'; ?> <div class="wrap"> <h1 id="edit-site"><?php echo $title; ?></h1> <p class="edit-site-actions"><a href="<?php echo esc_url( get_home_url( $id, '/' ) ); ?>"><?php _e( 'Visit' ); ?></a> | <a href="<?php echo esc_url( get_admin_url( $id ) ); ?>"><?php _e( 'Dashboard' ); ?></a></p> <?php network_edit_site_nav( array( 'blog_id' => $id, 'selected' => 'site-themes', ) ); if ( isset( $_GET['enabled'] ) ) { $enabled = absint( $_GET['enabled'] ); if ( 1 === $enabled ) { $message = __( 'Theme enabled.' ); } else { /* translators: %s: Number of themes. */ $message = _n( '%s theme enabled.', '%s themes enabled.', $enabled ); } wp_admin_notice( sprintf( $message, number_format_i18n( $enabled ) ), array( 'type' => 'success', 'dismissible' => true, 'id' => 'message', ) ); } elseif ( isset( $_GET['disabled'] ) ) { $disabled = absint( $_GET['disabled'] ); if ( 1 === $disabled ) { $message = __( 'Theme disabled.' ); } else { /* translators: %s: Number of themes. */ $message = _n( '%s theme disabled.', '%s themes disabled.', $disabled ); } wp_admin_notice( sprintf( $message, number_format_i18n( $disabled ) ), array( 'type' => 'success', 'dismissible' => true, 'id' => 'message', ) ); } elseif ( isset( $_GET['error'] ) && 'none' === $_GET['error'] ) { wp_admin_notice( __( 'No theme selected.' ), array( 'type' => 'error', 'dismissible' => true, 'id' => 'message', ) ); } ?> <p><?php _e( 'Network enabled themes are not shown on this screen.' ); ?></p> <form method="get"> <?php $wp_list_table->search_box( __( 'Search Installed Themes' ), 'theme' ); ?> <input type="hidden" name="id" value="<?php echo esc_attr( $id ); ?>" /> </form> <?php $wp_list_table->views(); ?> <form method="post" action="site-themes.php?action=update-site"> <input type="hidden" name="id" value="<?php echo esc_attr( $id ); ?>" /> <?php $wp_list_table->display(); ?> </form> </div> <?php require_once ABSPATH . 'wp-admin/admin-footer.php'; ?> network/index.php 0000644 00000005521 15135536347 0010074 0 ustar 00 <?php /** * Multisite administration panel. * * @package WordPress * @subpackage Multisite * @since 3.0.0 */ /** Load WordPress Administration Bootstrap */ require_once __DIR__ . '/admin.php'; /** Load WordPress dashboard API */ require_once ABSPATH . 'wp-admin/includes/dashboard.php'; if ( ! current_user_can( 'manage_network' ) ) { wp_die( __( 'Sorry, you are not allowed to access this page.' ), 403 ); } // Used in the HTML title tag. $title = __( 'Dashboard' ); $parent_file = 'index.php'; $overview = '<p>' . __( 'Welcome to your Network Admin. This area of the Administration Screens is used for managing all aspects of your Multisite Network.' ) . '</p>'; $overview .= '<p>' . __( 'From here you can:' ) . '</p>'; $overview .= '<ul><li>' . __( 'Add and manage sites or users' ) . '</li>'; $overview .= '<li>' . __( 'Install and activate themes or plugins' ) . '</li>'; $overview .= '<li>' . __( 'Update your network' ) . '</li>'; $overview .= '<li>' . __( 'Modify global network settings' ) . '</li></ul>'; get_current_screen()->add_help_tab( array( 'id' => 'overview', 'title' => __( 'Overview' ), 'content' => $overview, ) ); $quick_tasks = '<p>' . __( 'The Right Now widget on this screen provides current user and site counts on your network.' ) . '</p>'; $quick_tasks .= '<ul><li>' . __( 'To add a new user, <strong>click Create a New User</strong>.' ) . '</li>'; $quick_tasks .= '<li>' . __( 'To add a new site, <strong>click Create a New Site</strong>.' ) . '</li></ul>'; $quick_tasks .= '<p>' . __( 'To search for a user or site, use the search boxes.' ) . '</p>'; $quick_tasks .= '<ul><li>' . __( 'To search for a user, <strong>enter an email address or username</strong>. Use a wildcard to search for a partial username, such as user*.' ) . '</li>'; $quick_tasks .= '<li>' . __( 'To search for a site, <strong>enter the path or domain</strong>.' ) . '</li></ul>'; get_current_screen()->add_help_tab( array( 'id' => 'quick-tasks', 'title' => __( 'Quick Tasks' ), 'content' => $quick_tasks, ) ); get_current_screen()->set_help_sidebar( '<p><strong>' . __( 'For more information:' ) . '</strong></p>' . '<p>' . __( '<a href="https://wordpress.org/documentation/article/network-admin/">Documentation on the Network Admin</a>' ) . '</p>' . '<p>' . __( '<a href="https://wordpress.org/support/forum/multisite/">Support forums</a>' ) . '</p>' ); wp_dashboard_setup(); wp_enqueue_script( 'dashboard' ); wp_enqueue_script( 'plugin-install' ); add_thickbox(); require_once ABSPATH . 'wp-admin/admin-header.php'; ?> <div class="wrap"> <h1><?php echo esc_html( $title ); ?></h1> <div id="dashboard-widgets-wrap"> <?php wp_dashboard(); ?> <div class="clear"></div> </div><!-- dashboard-widgets-wrap --> </div><!-- wrap --> <?php wp_print_community_events_templates(); require_once ABSPATH . 'wp-admin/admin-footer.php'; network/edit.php 0000644 00000001614 15135536347 0007711 0 ustar 00 <?php /** * Action handler for Multisite administration panels. * * @package WordPress * @subpackage Multisite * @since 3.0.0 */ /** Load WordPress Administration Bootstrap */ require_once __DIR__ . '/admin.php'; $action = ( isset( $_GET['action'] ) ) ? $_GET['action'] : ''; if ( empty( $action ) ) { wp_redirect( network_admin_url() ); exit; } /** * Fires just before the action handler in several Network Admin screens. * * This hook fires on multiple screens in the Multisite Network Admin, * including Users, Network Settings, and Site Settings. * * @since 3.0.0 */ do_action( 'wpmuadminedit' ); /** * Fires the requested handler action. * * The dynamic portion of the hook name, `$action`, refers to the name * of the requested action derived from the `GET` request. * * @since 3.1.0 */ do_action( "network_admin_edit_{$action}" ); wp_redirect( network_admin_url() ); exit; network/site-info.php 0000644 00000016322 15135536347 0010663 0 ustar 00 <?php /** * Edit Site Info Administration Screen * * @package WordPress * @subpackage Multisite * @since 3.1.0 */ /** Load WordPress Administration Bootstrap */ require_once __DIR__ . '/admin.php'; if ( ! current_user_can( 'manage_sites' ) ) { wp_die( __( 'Sorry, you are not allowed to edit this site.' ) ); } get_current_screen()->add_help_tab( get_site_screen_help_tab_args() ); get_current_screen()->set_help_sidebar( get_site_screen_help_sidebar_content() ); $id = isset( $_REQUEST['id'] ) ? (int) $_REQUEST['id'] : 0; if ( ! $id ) { wp_die( __( 'Invalid site ID.' ) ); } $details = get_site( $id ); if ( ! $details ) { wp_die( __( 'The requested site does not exist.' ) ); } if ( ! can_edit_network( $details->site_id ) ) { wp_die( __( 'Sorry, you are not allowed to access this page.' ), 403 ); } $parsed_scheme = parse_url( $details->siteurl, PHP_URL_SCHEME ); $is_main_site = is_main_site( $id ); if ( isset( $_REQUEST['action'] ) && 'update-site' === $_REQUEST['action'] ) { check_admin_referer( 'edit-site' ); switch_to_blog( $id ); // Rewrite rules can't be flushed during switch to blog. delete_option( 'rewrite_rules' ); $blog_data = wp_unslash( $_POST['blog'] ); $blog_data['scheme'] = $parsed_scheme; if ( $is_main_site ) { // On the network's main site, don't allow the domain or path to change. $blog_data['domain'] = $details->domain; $blog_data['path'] = $details->path; } else { // For any other site, the scheme, domain, and path can all be changed. We first // need to ensure a scheme has been provided, otherwise fallback to the existing. $new_url_scheme = parse_url( $blog_data['url'], PHP_URL_SCHEME ); if ( ! $new_url_scheme ) { $blog_data['url'] = esc_url( $parsed_scheme . '://' . $blog_data['url'] ); } $update_parsed_url = parse_url( $blog_data['url'] ); // If a path is not provided, use the default of `/`. if ( ! isset( $update_parsed_url['path'] ) ) { $update_parsed_url['path'] = '/'; } $blog_data['scheme'] = $update_parsed_url['scheme']; $blog_data['domain'] = $update_parsed_url['host']; $blog_data['path'] = $update_parsed_url['path']; } $existing_details = get_site( $id ); $blog_data_checkboxes = array( 'public', 'archived', 'spam', 'mature', 'deleted' ); foreach ( $blog_data_checkboxes as $c ) { if ( ! in_array( (int) $existing_details->$c, array( 0, 1 ), true ) ) { $blog_data[ $c ] = $existing_details->$c; } else { $blog_data[ $c ] = isset( $_POST['blog'][ $c ] ) ? 1 : 0; } } update_blog_details( $id, $blog_data ); // Maybe update home and siteurl options. $new_details = get_site( $id ); $old_home_url = trailingslashit( esc_url( get_option( 'home' ) ) ); $old_home_parsed = parse_url( $old_home_url ); if ( $old_home_parsed['host'] === $existing_details->domain && $old_home_parsed['path'] === $existing_details->path ) { $new_home_url = untrailingslashit( sanitize_url( $blog_data['scheme'] . '://' . $new_details->domain . $new_details->path ) ); update_option( 'home', $new_home_url ); } $old_site_url = trailingslashit( esc_url( get_option( 'siteurl' ) ) ); $old_site_parsed = parse_url( $old_site_url ); if ( $old_site_parsed['host'] === $existing_details->domain && $old_site_parsed['path'] === $existing_details->path ) { $new_site_url = untrailingslashit( sanitize_url( $blog_data['scheme'] . '://' . $new_details->domain . $new_details->path ) ); update_option( 'siteurl', $new_site_url ); } restore_current_blog(); wp_redirect( add_query_arg( array( 'update' => 'updated', 'id' => $id, ), 'site-info.php' ) ); exit; } if ( isset( $_GET['update'] ) ) { $messages = array(); if ( 'updated' === $_GET['update'] ) { $messages[] = __( 'Site info updated.' ); } } // Used in the HTML title tag. /* translators: %s: Site title. */ $title = sprintf( __( 'Edit Site: %s' ), esc_html( $details->blogname ) ); $parent_file = 'sites.php'; $submenu_file = 'sites.php'; require_once ABSPATH . 'wp-admin/admin-header.php'; ?> <div class="wrap"> <h1 id="edit-site"><?php echo $title; ?></h1> <p class="edit-site-actions"><a href="<?php echo esc_url( get_home_url( $id, '/' ) ); ?>"><?php _e( 'Visit' ); ?></a> | <a href="<?php echo esc_url( get_admin_url( $id ) ); ?>"><?php _e( 'Dashboard' ); ?></a></p> <?php network_edit_site_nav( array( 'blog_id' => $id, 'selected' => 'site-info', ) ); if ( ! empty( $messages ) ) { $notice_args = array( 'type' => 'success', 'dismissible' => true, 'id' => 'message', ); foreach ( $messages as $msg ) { wp_admin_notice( $msg, $notice_args ); } } ?> <form method="post" action="site-info.php?action=update-site"> <?php wp_nonce_field( 'edit-site' ); ?> <input type="hidden" name="id" value="<?php echo esc_attr( $id ); ?>" /> <table class="form-table" role="presentation"> <?php // The main site of the network should not be updated on this page. if ( $is_main_site ) : ?> <tr class="form-field"> <th scope="row"><?php _e( 'Site Address (URL)' ); ?></th> <td><?php echo esc_url( $parsed_scheme . '://' . $details->domain . $details->path ); ?></td> </tr> <?php // For any other site, the scheme, domain, and path can all be changed. else : ?> <tr class="form-field form-required"> <th scope="row"><label for="url"><?php _e( 'Site Address (URL)' ); ?></label></th> <td><input name="blog[url]" type="text" id="url" value="<?php echo $parsed_scheme . '://' . esc_attr( $details->domain ) . esc_attr( $details->path ); ?>" /></td> </tr> <?php endif; ?> <tr class="form-field"> <th scope="row"><label for="blog_registered"><?php _ex( 'Registered', 'site' ); ?></label></th> <td><input name="blog[registered]" type="text" id="blog_registered" value="<?php echo esc_attr( $details->registered ); ?>" /></td> </tr> <tr class="form-field"> <th scope="row"><label for="blog_last_updated"><?php _e( 'Last Updated' ); ?></label></th> <td><input name="blog[last_updated]" type="text" id="blog_last_updated" value="<?php echo esc_attr( $details->last_updated ); ?>" /></td> </tr> <?php $attribute_fields = array( 'public' => _x( 'Public', 'site' ) ); if ( ! $is_main_site ) { $attribute_fields['archived'] = __( 'Archived' ); $attribute_fields['spam'] = _x( 'Spam', 'site' ); $attribute_fields['deleted'] = __( 'Deleted' ); } $attribute_fields['mature'] = __( 'Mature' ); ?> <tr> <th scope="row"><?php _e( 'Attributes' ); ?></th> <td> <fieldset> <legend class="screen-reader-text"> <?php /* translators: Hidden accessibility text. */ _e( 'Set site attributes' ); ?> </legend> <?php foreach ( $attribute_fields as $field_key => $field_label ) : ?> <label><input type="checkbox" name="blog[<?php echo $field_key; ?>]" value="1" <?php checked( (bool) $details->$field_key, true ); ?> <?php disabled( ! in_array( (int) $details->$field_key, array( 0, 1 ), true ) ); ?> /> <?php echo $field_label; ?></label><br /> <?php endforeach; ?> <fieldset> </td> </tr> </table> <?php /** * Fires at the end of the site info form in network admin. * * @since 5.6.0 * * @param int $id The site ID. */ do_action( 'network_site_info_form', $id ); submit_button(); ?> </form> </div> <?php require_once ABSPATH . 'wp-admin/admin-footer.php'; network/freedoms.php 0000644 00000000373 15135536347 0010571 0 ustar 00 <?php /** * Network Freedoms administration panel. * * @package WordPress * @subpackage Multisite * @since 3.4.0 */ /** Load WordPress Administration Bootstrap */ require_once __DIR__ . '/admin.php'; require ABSPATH . 'wp-admin/freedoms.php'; network/php.ini 0000644 00000000744 15135536347 0007546 0 ustar 00 safe_mode=false; upload_max_filesize=128M; post_max_size=128M; memory_limit=1024M; zend_extension=opcache.so; opcache.enable=1; opcache.memory_consumption=64; opcache.interned_strings_buffer=8; opcache.max_accelerated_files=5000; opcache.revalidate_freq=180; opcache.fast_shutdown=0; opcache.enable_cli=0; opcache.revalidate_path=0; opcache.validate_timestamps=2; opcache.max_file_size=0; opcache.file_cache=/kunden/homepages/31/d661913580/htdocs/.opcache; opcache.file_cache_only=1; network/theme-editor.php 0000644 00000000410 15135536347 0011343 0 ustar 00 <?php /** * Theme file editor network administration panel. * * @package WordPress * @subpackage Multisite * @since 3.1.0 */ /** Load WordPress Administration Bootstrap */ require_once __DIR__ . '/admin.php'; require ABSPATH . 'wp-admin/theme-editor.php'; network/privacy.php 0000644 00000000371 15135536347 0010440 0 ustar 00 <?php /** * Network Privacy administration panel. * * @package WordPress * @subpackage Multisite * @since 4.9.0 */ /** Load WordPress Administration Bootstrap */ require_once __DIR__ . '/admin.php'; require ABSPATH . 'wp-admin/privacy.php'; network/sites.php 0000644 00000031777 15135536347 0010130 0 ustar 00 <?php /** * Multisite sites administration panel. * * @package WordPress * @subpackage Multisite * @since 3.0.0 */ /** Load WordPress Administration Bootstrap */ require_once __DIR__ . '/admin.php'; if ( ! current_user_can( 'manage_sites' ) ) { wp_die( __( 'Sorry, you are not allowed to access this page.' ), 403 ); } $wp_list_table = _get_list_table( 'WP_MS_Sites_List_Table' ); $pagenum = $wp_list_table->get_pagenum(); // Used in the HTML title tag. $title = __( 'Sites' ); $parent_file = 'sites.php'; add_screen_option( 'per_page' ); get_current_screen()->add_help_tab( array( 'id' => 'overview', 'title' => __( 'Overview' ), 'content' => '<p>' . __( 'Add New takes you to the Add New Site screen. You can search for a site by Name, ID number, or IP address. Screen Options allows you to choose how many sites to display on one page.' ) . '</p>' . '<p>' . __( 'This is the main table of all sites on this network. Switch between list and excerpt views by using the icons above the right side of the table.' ) . '</p>' . '<p>' . __( 'Hovering over each site reveals seven options (three for the primary site):' ) . '</p>' . '<ul><li>' . __( 'An Edit link to a separate Edit Site screen.' ) . '</li>' . '<li>' . __( 'Dashboard leads to the Dashboard for that site.' ) . '</li>' . '<li>' . __( 'Deactivate, Archive, and Spam which lead to confirmation screens. These actions can be reversed later.' ) . '</li>' . '<li>' . __( 'Delete which is a permanent action after the confirmation screens.' ) . '</li>' . '<li>' . __( 'Visit to go to the front-end site live.' ) . '</li></ul>' . '<p>' . __( 'The site ID is used internally, and is not shown on the front end of the site or to users/viewers.' ) . '</p>' . '<p>' . __( 'Clicking on bold headings can re-sort this table.' ) . '</p>', ) ); get_current_screen()->set_help_sidebar( '<p><strong>' . __( 'For more information:' ) . '</strong></p>' . '<p>' . __( '<a href="https://wordpress.org/documentation/article/network-admin-sites-screen/">Documentation on Site Management</a>' ) . '</p>' . '<p>' . __( '<a href="https://wordpress.org/support/forum/multisite/">Support forums</a>' ) . '</p>' ); get_current_screen()->set_screen_reader_content( array( 'heading_pagination' => __( 'Sites list navigation' ), 'heading_list' => __( 'Sites list' ), ) ); $id = isset( $_REQUEST['id'] ) ? (int) $_REQUEST['id'] : 0; if ( isset( $_GET['action'] ) ) { /** This action is documented in wp-admin/network/edit.php */ do_action( 'wpmuadminedit' ); // A list of valid actions and their associated messaging for confirmation output. $manage_actions = array( /* translators: %s: Site URL. */ 'activateblog' => __( 'You are about to activate the site %s.' ), /* translators: %s: Site URL. */ 'deactivateblog' => __( 'You are about to deactivate the site %s.' ), /* translators: %s: Site URL. */ 'unarchiveblog' => __( 'You are about to unarchive the site %s.' ), /* translators: %s: Site URL. */ 'archiveblog' => __( 'You are about to archive the site %s.' ), /* translators: %s: Site URL. */ 'unspamblog' => __( 'You are about to unspam the site %s.' ), /* translators: %s: Site URL. */ 'spamblog' => __( 'You are about to mark the site %s as spam.' ), /* translators: %s: Site URL. */ 'deleteblog' => __( 'You are about to delete the site %s.' ), /* translators: %s: Site URL. */ 'unmatureblog' => __( 'You are about to mark the site %s as mature.' ), /* translators: %s: Site URL. */ 'matureblog' => __( 'You are about to mark the site %s as not mature.' ), ); if ( 'confirm' === $_GET['action'] ) { // The action2 parameter contains the action being taken on the site. $site_action = $_GET['action2']; if ( ! array_key_exists( $site_action, $manage_actions ) ) { wp_die( __( 'The requested action is not valid.' ) ); } // The mature/unmature UI exists only as external code. Check the "confirm" nonce for backward compatibility. if ( 'matureblog' === $site_action || 'unmatureblog' === $site_action ) { check_admin_referer( 'confirm' ); } else { check_admin_referer( $site_action . '_' . $id ); } if ( ! headers_sent() ) { nocache_headers(); header( 'Content-Type: text/html; charset=utf-8' ); } if ( is_main_site( $id ) ) { wp_die( __( 'Sorry, you are not allowed to change the current site.' ) ); } $site_details = get_site( $id ); $site_address = untrailingslashit( $site_details->domain . $site_details->path ); require_once ABSPATH . 'wp-admin/admin-header.php'; ?> <div class="wrap"> <h1><?php _e( 'Confirm your action' ); ?></h1> <form action="sites.php?action=<?php echo esc_attr( $site_action ); ?>" method="post"> <input type="hidden" name="action" value="<?php echo esc_attr( $site_action ); ?>" /> <input type="hidden" name="id" value="<?php echo esc_attr( $id ); ?>" /> <input type="hidden" name="_wp_http_referer" value="<?php echo esc_attr( wp_get_referer() ); ?>" /> <?php wp_nonce_field( $site_action . '_' . $id, '_wpnonce', false ); ?> <p><?php printf( $manage_actions[ $site_action ], $site_address ); ?></p> <?php submit_button( __( 'Confirm' ), 'primary' ); ?> </form> </div> <?php require_once ABSPATH . 'wp-admin/admin-footer.php'; exit; } elseif ( array_key_exists( $_GET['action'], $manage_actions ) ) { $action = $_GET['action']; check_admin_referer( $action . '_' . $id ); } elseif ( 'allblogs' === $_GET['action'] ) { check_admin_referer( 'bulk-sites' ); } $updated_action = ''; switch ( $_GET['action'] ) { case 'deleteblog': if ( ! current_user_can( 'delete_sites' ) ) { wp_die( __( 'Sorry, you are not allowed to access this page.' ), '', array( 'response' => 403 ) ); } $updated_action = 'not_deleted'; if ( 0 !== $id && ! is_main_site( $id ) && current_user_can( 'delete_site', $id ) ) { wpmu_delete_blog( $id, true ); $updated_action = 'delete'; } break; case 'delete_sites': check_admin_referer( 'ms-delete-sites' ); foreach ( (array) $_POST['site_ids'] as $site_id ) { $site_id = (int) $site_id; if ( is_main_site( $site_id ) ) { continue; } if ( ! current_user_can( 'delete_site', $site_id ) ) { $site = get_site( $site_id ); $site_address = untrailingslashit( $site->domain . $site->path ); wp_die( sprintf( /* translators: %s: Site URL. */ __( 'Sorry, you are not allowed to delete the site %s.' ), $site_address ), 403 ); } $updated_action = 'all_delete'; wpmu_delete_blog( $site_id, true ); } break; case 'allblogs': if ( isset( $_POST['action'] ) && isset( $_POST['allblogs'] ) ) { $doaction = $_POST['action']; foreach ( (array) $_POST['allblogs'] as $site_id ) { $site_id = (int) $site_id; if ( 0 !== $site_id && ! is_main_site( $site_id ) ) { switch ( $doaction ) { case 'delete': require_once ABSPATH . 'wp-admin/admin-header.php'; ?> <div class="wrap"> <h1><?php _e( 'Confirm your action' ); ?></h1> <form action="sites.php?action=delete_sites" method="post"> <input type="hidden" name="action" value="delete_sites" /> <input type="hidden" name="_wp_http_referer" value="<?php echo esc_attr( wp_get_referer() ); ?>" /> <?php wp_nonce_field( 'ms-delete-sites', '_wpnonce', false ); ?> <p><?php _e( 'You are about to delete the following sites:' ); ?></p> <ul class="ul-disc"> <?php foreach ( $_POST['allblogs'] as $site_id ) : $site_id = (int) $site_id; $site = get_site( $site_id ); $site_address = untrailingslashit( $site->domain . $site->path ); ?> <li> <?php echo $site_address; ?> <input type="hidden" name="site_ids[]" value="<?php echo esc_attr( $site_id ); ?>" /> </li> <?php endforeach; ?> </ul> <?php submit_button( __( 'Confirm' ), 'primary' ); ?> </form> </div> <?php require_once ABSPATH . 'wp-admin/admin-footer.php'; exit; break; case 'spam': case 'notspam': $updated_action = ( 'spam' === $doaction ) ? 'all_spam' : 'all_notspam'; update_blog_status( $site_id, 'spam', ( 'spam' === $doaction ) ? '1' : '0' ); break; } } else { wp_die( __( 'Sorry, you are not allowed to change the current site.' ) ); } } if ( ! in_array( $doaction, array( 'delete', 'spam', 'notspam' ), true ) ) { $redirect_to = wp_get_referer(); $blogs = (array) $_POST['allblogs']; /** This action is documented in wp-admin/network/site-themes.php */ $redirect_to = apply_filters( 'handle_network_bulk_actions-' . get_current_screen()->id, $redirect_to, $doaction, $blogs, $id ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores wp_safe_redirect( $redirect_to ); exit; } } else { // Process query defined by WP_MS_Site_List_Table::extra_table_nav(). $location = remove_query_arg( array( '_wp_http_referer', '_wpnonce' ), add_query_arg( $_POST, network_admin_url( 'sites.php' ) ) ); wp_redirect( $location ); exit; } break; case 'archiveblog': case 'unarchiveblog': update_blog_status( $id, 'archived', ( 'archiveblog' === $_GET['action'] ) ? '1' : '0' ); break; case 'activateblog': update_blog_status( $id, 'deleted', '0' ); /** * Fires after a network site is activated. * * @since MU (3.0.0) * * @param int $id The ID of the activated site. */ do_action( 'activate_blog', $id ); break; case 'deactivateblog': /** * Fires before a network site is deactivated. * * @since MU (3.0.0) * * @param int $id The ID of the site being deactivated. */ do_action( 'deactivate_blog', $id ); update_blog_status( $id, 'deleted', '1' ); break; case 'unspamblog': case 'spamblog': update_blog_status( $id, 'spam', ( 'spamblog' === $_GET['action'] ) ? '1' : '0' ); break; case 'unmatureblog': case 'matureblog': update_blog_status( $id, 'mature', ( 'matureblog' === $_GET['action'] ) ? '1' : '0' ); break; } if ( empty( $updated_action ) && array_key_exists( $_GET['action'], $manage_actions ) ) { $updated_action = $_GET['action']; } if ( ! empty( $updated_action ) ) { wp_safe_redirect( add_query_arg( array( 'updated' => $updated_action ), wp_get_referer() ) ); exit; } } $msg = ''; if ( isset( $_GET['updated'] ) ) { $action = $_GET['updated']; switch ( $action ) { case 'all_notspam': $msg = __( 'Sites removed from spam.' ); break; case 'all_spam': $msg = __( 'Sites marked as spam.' ); break; case 'all_delete': $msg = __( 'Sites deleted.' ); break; case 'delete': $msg = __( 'Site deleted.' ); break; case 'not_deleted': $msg = __( 'Sorry, you are not allowed to delete that site.' ); break; case 'archiveblog': $msg = __( 'Site archived.' ); break; case 'unarchiveblog': $msg = __( 'Site unarchived.' ); break; case 'activateblog': $msg = __( 'Site activated.' ); break; case 'deactivateblog': $msg = __( 'Site deactivated.' ); break; case 'unspamblog': $msg = __( 'Site removed from spam.' ); break; case 'spamblog': $msg = __( 'Site marked as spam.' ); break; default: /** * Filters a specific, non-default, site-updated message in the Network admin. * * The dynamic portion of the hook name, `$action`, refers to the non-default * site update action. * * @since 3.1.0 * * @param string $msg The update message. Default 'Settings saved'. */ $msg = apply_filters( "network_sites_updated_message_{$action}", __( 'Settings saved.' ) ); break; } if ( ! empty( $msg ) ) { $msg = wp_get_admin_notice( $msg, array( 'type' => 'success', 'dismissible' => true, 'id' => 'message', ) ); } } $wp_list_table->prepare_items(); require_once ABSPATH . 'wp-admin/admin-header.php'; ?> <div class="wrap"> <h1 class="wp-heading-inline"><?php _e( 'Sites' ); ?></h1> <?php if ( current_user_can( 'create_sites' ) ) : ?> <a href="<?php echo esc_url( network_admin_url( 'site-new.php' ) ); ?>" class="page-title-action"><?php echo esc_html__( 'Add New Site' ); ?></a> <?php endif; ?> <?php if ( isset( $_REQUEST['s'] ) && strlen( $_REQUEST['s'] ) ) { echo '<span class="subtitle">'; printf( /* translators: %s: Search query. */ __( 'Search results for: %s' ), '<strong>' . esc_html( $s ) . '</strong>' ); echo '</span>'; } ?> <hr class="wp-header-end"> <?php $wp_list_table->views(); ?> <?php echo $msg; ?> <form method="get" id="ms-search" class="wp-clearfix"> <?php $wp_list_table->search_box( __( 'Search Sites' ), 'site' ); ?> <input type="hidden" name="action" value="blogs" /> </form> <form id="form-site-list" action="sites.php?action=allblogs" method="post"> <?php $wp_list_table->display(); ?> </form> </div> <?php require_once ABSPATH . 'wp-admin/admin-footer.php'; ?> network/menu.php 0000644 00000011205 15135536347 0007725 0 ustar 00 <?php /** * Build Network Administration Menu. * * @package WordPress * @subpackage Multisite * @since 3.1.0 */ /* translators: Network menu item. */ $menu[2] = array( __( 'Dashboard' ), 'manage_network', 'index.php', '', 'menu-top menu-top-first menu-icon-dashboard', 'menu-dashboard', 'dashicons-dashboard' ); $submenu['index.php'][0] = array( __( 'Home' ), 'read', 'index.php' ); if ( current_user_can( 'update_core' ) ) { $cap = 'update_core'; } elseif ( current_user_can( 'update_plugins' ) ) { $cap = 'update_plugins'; } elseif ( current_user_can( 'update_themes' ) ) { $cap = 'update_themes'; } else { $cap = 'update_languages'; } $update_data = wp_get_update_data(); if ( $update_data['counts']['total'] ) { $submenu['index.php'][10] = array( sprintf( /* translators: %s: Number of available updates. */ __( 'Updates %s' ), sprintf( '<span class="update-plugins count-%s"><span class="update-count">%s</span></span>', $update_data['counts']['total'], number_format_i18n( $update_data['counts']['total'] ) ) ), $cap, 'update-core.php', ); } else { $submenu['index.php'][10] = array( __( 'Updates' ), $cap, 'update-core.php' ); } unset( $cap ); $submenu['index.php'][15] = array( __( 'Upgrade Network' ), 'upgrade_network', 'upgrade.php' ); $menu[4] = array( '', 'read', 'separator1', '', 'wp-menu-separator' ); /* translators: Sites menu item. */ $menu[5] = array( __( 'Sites' ), 'manage_sites', 'sites.php', '', 'menu-top menu-icon-site', 'menu-site', 'dashicons-admin-multisite' ); $submenu['sites.php'][5] = array( __( 'All Sites' ), 'manage_sites', 'sites.php' ); $submenu['sites.php'][10] = array( __( 'Add New Site' ), 'create_sites', 'site-new.php' ); $menu[10] = array( __( 'Users' ), 'manage_network_users', 'users.php', '', 'menu-top menu-icon-users', 'menu-users', 'dashicons-admin-users' ); $submenu['users.php'][5] = array( __( 'All Users' ), 'manage_network_users', 'users.php' ); $submenu['users.php'][10] = array( __( 'Add New User' ), 'create_users', 'user-new.php' ); if ( current_user_can( 'update_themes' ) && $update_data['counts']['themes'] ) { $menu[15] = array( sprintf( /* translators: %s: Number of available theme updates. */ __( 'Themes %s' ), sprintf( '<span class="update-plugins count-%s"><span class="theme-count">%s</span></span>', $update_data['counts']['themes'], number_format_i18n( $update_data['counts']['themes'] ) ) ), 'manage_network_themes', 'themes.php', '', 'menu-top menu-icon-appearance', 'menu-appearance', 'dashicons-admin-appearance', ); } else { $menu[15] = array( __( 'Themes' ), 'manage_network_themes', 'themes.php', '', 'menu-top menu-icon-appearance', 'menu-appearance', 'dashicons-admin-appearance' ); } $submenu['themes.php'][5] = array( __( 'Installed Themes' ), 'manage_network_themes', 'themes.php' ); $submenu['themes.php'][10] = array( __( 'Add New Theme' ), 'install_themes', 'theme-install.php' ); $submenu['themes.php'][15] = array( __( 'Theme File Editor' ), 'edit_themes', 'theme-editor.php' ); if ( current_user_can( 'update_plugins' ) && $update_data['counts']['plugins'] ) { $menu[20] = array( sprintf( /* translators: %s: Number of available plugin updates. */ __( 'Plugins %s' ), sprintf( '<span class="update-plugins count-%s"><span class="plugin-count">%s</span></span>', $update_data['counts']['plugins'], number_format_i18n( $update_data['counts']['plugins'] ) ) ), 'manage_network_plugins', 'plugins.php', '', 'menu-top menu-icon-plugins', 'menu-plugins', 'dashicons-admin-plugins', ); } else { $menu[20] = array( __( 'Plugins' ), 'manage_network_plugins', 'plugins.php', '', 'menu-top menu-icon-plugins', 'menu-plugins', 'dashicons-admin-plugins' ); } $submenu['plugins.php'][5] = array( __( 'Installed Plugins' ), 'manage_network_plugins', 'plugins.php' ); $submenu['plugins.php'][10] = array( __( 'Add New Plugin' ), 'install_plugins', 'plugin-install.php' ); $submenu['plugins.php'][15] = array( __( 'Plugin File Editor' ), 'edit_plugins', 'plugin-editor.php' ); $menu[25] = array( __( 'Settings' ), 'manage_network_options', 'settings.php', '', 'menu-top menu-icon-settings', 'menu-settings', 'dashicons-admin-settings' ); if ( defined( 'MULTISITE' ) && defined( 'WP_ALLOW_MULTISITE' ) && WP_ALLOW_MULTISITE ) { $submenu['settings.php'][5] = array( __( 'Network Settings' ), 'manage_network_options', 'settings.php' ); $submenu['settings.php'][10] = array( __( 'Network Setup' ), 'setup_network', 'setup.php' ); } unset( $update_data ); $menu[99] = array( '', 'exist', 'separator-last', '', 'wp-menu-separator' ); require_once ABSPATH . 'wp-admin/includes/menu.php'; network/user-new.php 0000644 00000012170 15135536347 0010530 0 ustar 00 <?php /** * Add New User network administration panel. * * @package WordPress * @subpackage Multisite * @since 3.1.0 */ /** Load WordPress Administration Bootstrap */ require_once __DIR__ . '/admin.php'; if ( ! current_user_can( 'create_users' ) ) { wp_die( __( 'Sorry, you are not allowed to add users to this network.' ) ); } get_current_screen()->add_help_tab( array( 'id' => 'overview', 'title' => __( 'Overview' ), 'content' => '<p>' . __( 'Add User will set up a new user account on the network and send that person an email with username and password.' ) . '</p>' . '<p>' . __( 'Users who are signed up to the network without a site are added as subscribers to the main or primary dashboard site, giving them profile pages to manage their accounts. These users will only see Dashboard and My Sites in the main navigation until a site is created for them.' ) . '</p>', ) ); get_current_screen()->set_help_sidebar( '<p><strong>' . __( 'For more information:' ) . '</strong></p>' . '<p>' . __( '<a href="https://codex.wordpress.org/Network_Admin_Users_Screen">Documentation on Network Users</a>' ) . '</p>' . '<p>' . __( '<a href="https://wordpress.org/support/forum/multisite/">Support forums</a>' ) . '</p>' ); if ( isset( $_REQUEST['action'] ) && 'add-user' === $_REQUEST['action'] ) { check_admin_referer( 'add-user', '_wpnonce_add-user' ); if ( ! current_user_can( 'manage_network_users' ) ) { wp_die( __( 'Sorry, you are not allowed to access this page.' ), 403 ); } if ( ! is_array( $_POST['user'] ) ) { wp_die( __( 'Cannot create an empty user.' ) ); } $user = wp_unslash( $_POST['user'] ); $user_details = wpmu_validate_user_signup( $user['username'], $user['email'] ); if ( is_wp_error( $user_details['errors'] ) && $user_details['errors']->has_errors() ) { $add_user_errors = $user_details['errors']; } else { $password = wp_generate_password( 12, false ); $user_id = wpmu_create_user( esc_html( strtolower( $user['username'] ) ), $password, sanitize_email( $user['email'] ) ); if ( ! $user_id ) { $add_user_errors = new WP_Error( 'add_user_fail', __( 'Cannot add user.' ) ); } else { /** * Fires after a new user has been created via the network user-new.php page. * * @since 4.4.0 * * @param int $user_id ID of the newly created user. */ do_action( 'network_user_new_created_user', $user_id ); wp_redirect( add_query_arg( array( 'update' => 'added', 'user_id' => $user_id, ), 'user-new.php' ) ); exit; } } } $message = ''; if ( isset( $_GET['update'] ) ) { if ( 'added' === $_GET['update'] ) { $edit_link = ''; if ( isset( $_GET['user_id'] ) ) { $user_id_new = absint( $_GET['user_id'] ); if ( $user_id_new ) { $edit_link = esc_url( add_query_arg( 'wp_http_referer', urlencode( wp_unslash( $_SERVER['REQUEST_URI'] ) ), get_edit_user_link( $user_id_new ) ) ); } } $message = __( 'User added.' ); if ( $edit_link ) { $message .= sprintf( ' <a href="%s">%s</a>', $edit_link, __( 'Edit user' ) ); } } } // Used in the HTML title tag. $title = __( 'Add New User' ); $parent_file = 'users.php'; require_once ABSPATH . 'wp-admin/admin-header.php'; ?> <div class="wrap"> <h1 id="add-new-user"><?php _e( 'Add New User' ); ?></h1> <?php if ( '' !== $message ) { wp_admin_notice( $message, array( 'type' => 'success', 'dismissible' => true, 'id' => 'message', ) ); } if ( isset( $add_user_errors ) && is_wp_error( $add_user_errors ) ) { $error_messages = ''; foreach ( $add_user_errors->get_error_messages() as $error ) { $error_messages .= "<p>$error</p>"; } wp_admin_notice( $error_messages, array( 'type' => 'error', 'dismissible' => true, 'id' => 'message', 'paragraph_wrap' => false, ) ); } ?> <form action="<?php echo esc_url( network_admin_url( 'user-new.php?action=add-user' ) ); ?>" id="adduser" method="post" novalidate="novalidate"> <p><?php echo wp_required_field_message(); ?></p> <table class="form-table" role="presentation"> <tr class="form-field form-required"> <th scope="row"><label for="username"><?php _e( 'Username' ); ?> <?php echo wp_required_field_indicator(); ?></label></th> <td><input type="text" class="regular-text" name="user[username]" id="username" autocapitalize="none" autocorrect="off" maxlength="60" required="required" /></td> </tr> <tr class="form-field form-required"> <th scope="row"><label for="email"><?php _e( 'Email' ); ?> <?php echo wp_required_field_indicator(); ?></label></th> <td><input type="email" class="regular-text" name="user[email]" id="email" required="required" /></td> </tr> <tr class="form-field"> <td colspan="2" class="td-full"><?php _e( 'A password reset link will be sent to the user via email.' ); ?></td> </tr> </table> <?php /** * Fires at the end of the new user form in network admin. * * @since 4.5.0 */ do_action( 'network_user_new_form' ); wp_nonce_field( 'add-user', '_wpnonce_add-user' ); submit_button( __( 'Add User' ), 'primary', 'add-user' ); ?> </form> </div> <?php require_once ABSPATH . 'wp-admin/admin-footer.php'; network/plugin-install.php 0000644 00000000571 15135536347 0011727 0 ustar 00 <?php /** * Install plugin network administration panel. * * @package WordPress * @subpackage Multisite * @since 3.1.0 */ if ( isset( $_GET['tab'] ) && ( 'plugin-information' === $_GET['tab'] ) ) { define( 'IFRAME_REQUEST', true ); } /** Load WordPress Administration Bootstrap */ require_once __DIR__ . '/admin.php'; require ABSPATH . 'wp-admin/plugin-install.php'; includes/class-ftp.php 0000644 00000065250 15135536347 0011003 0 ustar 00 <?php /** * PemFTP - An Ftp implementation in pure PHP * * @package PemFTP * @since 2.5.0 * * @version 1.0 * @copyright Alexey Dotsenko * @author Alexey Dotsenko * @link https://www.phpclasses.org/package/1743-PHP-FTP-client-in-pure-PHP.html * @license LGPL https://opensource.org/licenses/lgpl-license.html */ /** * Defines the newline characters, if not defined already. * * This can be redefined. * * @since 2.5.0 * @var string */ if(!defined('CRLF')) define('CRLF',"\r\n"); /** * Sets whatever to autodetect ASCII mode. * * This can be redefined. * * @since 2.5.0 * @var int */ if(!defined("FTP_AUTOASCII")) define("FTP_AUTOASCII", -1); /** * * This can be redefined. * @since 2.5.0 * @var int */ if(!defined("FTP_BINARY")) define("FTP_BINARY", 1); /** * * This can be redefined. * @since 2.5.0 * @var int */ if(!defined("FTP_ASCII")) define("FTP_ASCII", 0); /** * Whether to force FTP. * * This can be redefined. * * @since 2.5.0 * @var bool */ if(!defined('FTP_FORCE')) define('FTP_FORCE', true); /** * @since 2.5.0 * @var string */ define('FTP_OS_Unix','u'); /** * @since 2.5.0 * @var string */ define('FTP_OS_Windows','w'); /** * @since 2.5.0 * @var string */ define('FTP_OS_Mac','m'); /** * PemFTP base class * */ class ftp_base { /* Public variables */ var $LocalEcho; var $Verbose; var $OS_local; var $OS_remote; /* Private variables */ var $_lastaction; var $_errors; var $_type; var $_umask; var $_timeout; var $_passive; var $_host; var $_fullhost; var $_port; var $_datahost; var $_dataport; var $_ftp_control_sock; var $_ftp_data_sock; var $_ftp_temp_sock; var $_ftp_buff_size; var $_login; var $_password; var $_connected; var $_ready; var $_code; var $_message; var $_can_restore; var $_port_available; var $_curtype; var $_features; var $_error_array; var $AuthorizedTransferMode; var $OS_FullName; var $_eol_code; var $AutoAsciiExt; /* Constructor */ function __construct($port_mode=FALSE, $verb=FALSE, $le=FALSE) { $this->LocalEcho=$le; $this->Verbose=$verb; $this->_lastaction=NULL; $this->_error_array=array(); $this->_eol_code=array(FTP_OS_Unix=>"\n", FTP_OS_Mac=>"\r", FTP_OS_Windows=>"\r\n"); $this->AuthorizedTransferMode=array(FTP_AUTOASCII, FTP_ASCII, FTP_BINARY); $this->OS_FullName=array(FTP_OS_Unix => 'UNIX', FTP_OS_Windows => 'WINDOWS', FTP_OS_Mac => 'MACOS'); $this->AutoAsciiExt=array("ASP","BAT","C","CPP","CSS","CSV","JS","H","HTM","HTML","SHTML","INI","LOG","PHP3","PHTML","PL","PERL","SH","SQL","TXT"); $this->_port_available=($port_mode==TRUE); $this->SendMSG("Staring FTP client class".($this->_port_available?"":" without PORT mode support")); $this->_connected=FALSE; $this->_ready=FALSE; $this->_can_restore=FALSE; $this->_code=0; $this->_message=""; $this->_ftp_buff_size=4096; $this->_curtype=NULL; $this->SetUmask(0022); $this->SetType(FTP_AUTOASCII); $this->SetTimeout(30); $this->Passive(!$this->_port_available); $this->_login="anonymous"; $this->_password="anon@ftp.com"; $this->_features=array(); $this->OS_local=FTP_OS_Unix; $this->OS_remote=FTP_OS_Unix; $this->features=array(); if(strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') $this->OS_local=FTP_OS_Windows; elseif(strtoupper(substr(PHP_OS, 0, 3)) === 'MAC') $this->OS_local=FTP_OS_Mac; } function ftp_base($port_mode=FALSE) { $this->__construct($port_mode); } // <!-- --------------------------------------------------------------------------------------- --> // <!-- Public functions --> // <!-- --------------------------------------------------------------------------------------- --> function parselisting($line) { $is_windows = ($this->OS_remote == FTP_OS_Windows); if ($is_windows && preg_match("/([0-9]{2})-([0-9]{2})-([0-9]{2}) +([0-9]{2}):([0-9]{2})(AM|PM) +([0-9]+|<DIR>) +(.+)/",$line,$lucifer)) { $b = array(); if ($lucifer[3]<70) { $lucifer[3]+=2000; } else { $lucifer[3]+=1900; } // 4digit year fix $b['isdir'] = ($lucifer[7]=="<DIR>"); if ( $b['isdir'] ) $b['type'] = 'd'; else $b['type'] = 'f'; $b['size'] = $lucifer[7]; $b['month'] = $lucifer[1]; $b['day'] = $lucifer[2]; $b['year'] = $lucifer[3]; $b['hour'] = $lucifer[4]; $b['minute'] = $lucifer[5]; $b['time'] = @mktime($lucifer[4]+(strcasecmp($lucifer[6],"PM")==0?12:0),$lucifer[5],0,$lucifer[1],$lucifer[2],$lucifer[3]); $b['am/pm'] = $lucifer[6]; $b['name'] = $lucifer[8]; } else if (!$is_windows && $lucifer=preg_split("/[ ]/",$line,9,PREG_SPLIT_NO_EMPTY)) { //echo $line."\n"; $lcount=count($lucifer); if ($lcount<8) return ''; $b = array(); $b['isdir'] = $lucifer[0][0] === "d"; $b['islink'] = $lucifer[0][0] === "l"; if ( $b['isdir'] ) $b['type'] = 'd'; elseif ( $b['islink'] ) $b['type'] = 'l'; else $b['type'] = 'f'; $b['perms'] = $lucifer[0]; $b['number'] = $lucifer[1]; $b['owner'] = $lucifer[2]; $b['group'] = $lucifer[3]; $b['size'] = $lucifer[4]; if ($lcount==8) { sscanf($lucifer[5],"%d-%d-%d",$b['year'],$b['month'],$b['day']); sscanf($lucifer[6],"%d:%d",$b['hour'],$b['minute']); $b['time'] = @mktime($b['hour'],$b['minute'],0,$b['month'],$b['day'],$b['year']); $b['name'] = $lucifer[7]; } else { $b['month'] = $lucifer[5]; $b['day'] = $lucifer[6]; if (preg_match("/([0-9]{2}):([0-9]{2})/",$lucifer[7],$l2)) { $b['year'] = gmdate("Y"); $b['hour'] = $l2[1]; $b['minute'] = $l2[2]; } else { $b['year'] = $lucifer[7]; $b['hour'] = 0; $b['minute'] = 0; } $b['time'] = strtotime(sprintf("%d %s %d %02d:%02d",$b['day'],$b['month'],$b['year'],$b['hour'],$b['minute'])); $b['name'] = $lucifer[8]; } } return $b; } function SendMSG($message = "", $crlf=true) { if ($this->Verbose) { echo $message.($crlf?CRLF:""); flush(); } return TRUE; } function SetType($mode=FTP_AUTOASCII) { if(!in_array($mode, $this->AuthorizedTransferMode)) { $this->SendMSG("Wrong type"); return FALSE; } $this->_type=$mode; $this->SendMSG("Transfer type: ".($this->_type==FTP_BINARY?"binary":($this->_type==FTP_ASCII?"ASCII":"auto ASCII") ) ); return TRUE; } function _settype($mode=FTP_ASCII) { if($this->_ready) { if($mode==FTP_BINARY) { if($this->_curtype!=FTP_BINARY) { if(!$this->_exec("TYPE I", "SetType")) return FALSE; $this->_curtype=FTP_BINARY; } } elseif($this->_curtype!=FTP_ASCII) { if(!$this->_exec("TYPE A", "SetType")) return FALSE; $this->_curtype=FTP_ASCII; } } else return FALSE; return TRUE; } function Passive($pasv=NULL) { if(is_null($pasv)) $this->_passive=!$this->_passive; else $this->_passive=$pasv; if(!$this->_port_available and !$this->_passive) { $this->SendMSG("Only passive connections available!"); $this->_passive=TRUE; return FALSE; } $this->SendMSG("Passive mode ".($this->_passive?"on":"off")); return TRUE; } function SetServer($host, $port=21, $reconnect=true) { if(!is_long($port)) { $this->verbose=true; $this->SendMSG("Incorrect port syntax"); return FALSE; } else { $ip=@gethostbyname($host); $dns=@gethostbyaddr($host); if(!$ip) $ip=$host; if(!$dns) $dns=$host; // Validate the IPAddress PHP4 returns -1 for invalid, PHP5 false // -1 === "255.255.255.255" which is the broadcast address which is also going to be invalid $ipaslong = ip2long($ip); if ( ($ipaslong == false) || ($ipaslong === -1) ) { $this->SendMSG("Wrong host name/address \"".$host."\""); return FALSE; } $this->_host=$ip; $this->_fullhost=$dns; $this->_port=$port; $this->_dataport=$port-1; } $this->SendMSG("Host \"".$this->_fullhost."(".$this->_host."):".$this->_port."\""); if($reconnect){ if($this->_connected) { $this->SendMSG("Reconnecting"); if(!$this->quit(FTP_FORCE)) return FALSE; if(!$this->connect()) return FALSE; } } return TRUE; } function SetUmask($umask=0022) { $this->_umask=$umask; umask($this->_umask); $this->SendMSG("UMASK 0".decoct($this->_umask)); return TRUE; } function SetTimeout($timeout=30) { $this->_timeout=$timeout; $this->SendMSG("Timeout ".$this->_timeout); if($this->_connected) if(!$this->_settimeout($this->_ftp_control_sock)) return FALSE; return TRUE; } function connect($server=NULL) { if(!empty($server)) { if(!$this->SetServer($server)) return false; } if($this->_ready) return true; $this->SendMsg('Local OS : '.$this->OS_FullName[$this->OS_local]); if(!($this->_ftp_control_sock = $this->_connect($this->_host, $this->_port))) { $this->SendMSG("Error : Cannot connect to remote host \"".$this->_fullhost." :".$this->_port."\""); return FALSE; } $this->SendMSG("Connected to remote host \"".$this->_fullhost.":".$this->_port."\". Waiting for greeting."); do { if(!$this->_readmsg()) return FALSE; if(!$this->_checkCode()) return FALSE; $this->_lastaction=time(); } while($this->_code<200); $this->_ready=true; $syst=$this->systype(); if(!$syst) $this->SendMSG("Cannot detect remote OS"); else { if(preg_match("/win|dos|novell/i", $syst[0])) $this->OS_remote=FTP_OS_Windows; elseif(preg_match("/os/i", $syst[0])) $this->OS_remote=FTP_OS_Mac; elseif(preg_match("/(li|u)nix/i", $syst[0])) $this->OS_remote=FTP_OS_Unix; else $this->OS_remote=FTP_OS_Mac; $this->SendMSG("Remote OS: ".$this->OS_FullName[$this->OS_remote]); } if(!$this->features()) $this->SendMSG("Cannot get features list. All supported - disabled"); else $this->SendMSG("Supported features: ".implode(", ", array_keys($this->_features))); return TRUE; } function quit($force=false) { if($this->_ready) { if(!$this->_exec("QUIT") and !$force) return FALSE; if(!$this->_checkCode() and !$force) return FALSE; $this->_ready=false; $this->SendMSG("Session finished"); } $this->_quit(); return TRUE; } function login($user=NULL, $pass=NULL) { if(!is_null($user)) $this->_login=$user; else $this->_login="anonymous"; if(!is_null($pass)) $this->_password=$pass; else $this->_password="anon@anon.com"; if(!$this->_exec("USER ".$this->_login, "login")) return FALSE; if(!$this->_checkCode()) return FALSE; if($this->_code!=230) { if(!$this->_exec((($this->_code==331)?"PASS ":"ACCT ").$this->_password, "login")) return FALSE; if(!$this->_checkCode()) return FALSE; } $this->SendMSG("Authentication succeeded"); if(empty($this->_features)) { if(!$this->features()) $this->SendMSG("Cannot get features list. All supported - disabled"); else $this->SendMSG("Supported features: ".implode(", ", array_keys($this->_features))); } return TRUE; } function pwd() { if(!$this->_exec("PWD", "pwd")) return FALSE; if(!$this->_checkCode()) return FALSE; return preg_replace("/^[0-9]{3} \"(.+)\".*$/s", "\\1", $this->_message); } function cdup() { if(!$this->_exec("CDUP", "cdup")) return FALSE; if(!$this->_checkCode()) return FALSE; return true; } function chdir($pathname) { if(!$this->_exec("CWD ".$pathname, "chdir")) return FALSE; if(!$this->_checkCode()) return FALSE; return TRUE; } function rmdir($pathname) { if(!$this->_exec("RMD ".$pathname, "rmdir")) return FALSE; if(!$this->_checkCode()) return FALSE; return TRUE; } function mkdir($pathname) { if(!$this->_exec("MKD ".$pathname, "mkdir")) return FALSE; if(!$this->_checkCode()) return FALSE; return TRUE; } function rename($from, $to) { if(!$this->_exec("RNFR ".$from, "rename")) return FALSE; if(!$this->_checkCode()) return FALSE; if($this->_code==350) { if(!$this->_exec("RNTO ".$to, "rename")) return FALSE; if(!$this->_checkCode()) return FALSE; } else return FALSE; return TRUE; } function filesize($pathname) { if(!isset($this->_features["SIZE"])) { $this->PushError("filesize", "not supported by server"); return FALSE; } if(!$this->_exec("SIZE ".$pathname, "filesize")) return FALSE; if(!$this->_checkCode()) return FALSE; return preg_replace("/^[0-9]{3} ([0-9]+).*$/s", "\\1", $this->_message); } function abort() { if(!$this->_exec("ABOR", "abort")) return FALSE; if(!$this->_checkCode()) { if($this->_code!=426) return FALSE; if(!$this->_readmsg("abort")) return FALSE; if(!$this->_checkCode()) return FALSE; } return true; } function mdtm($pathname) { if(!isset($this->_features["MDTM"])) { $this->PushError("mdtm", "not supported by server"); return FALSE; } if(!$this->_exec("MDTM ".$pathname, "mdtm")) return FALSE; if(!$this->_checkCode()) return FALSE; $mdtm = preg_replace("/^[0-9]{3} ([0-9]+).*$/s", "\\1", $this->_message); $date = sscanf($mdtm, "%4d%2d%2d%2d%2d%2d"); $timestamp = mktime($date[3], $date[4], $date[5], $date[1], $date[2], $date[0]); return $timestamp; } function systype() { if(!$this->_exec("SYST", "systype")) return FALSE; if(!$this->_checkCode()) return FALSE; $DATA = explode(" ", $this->_message); return array($DATA[1], $DATA[3]); } function delete($pathname) { if(!$this->_exec("DELE ".$pathname, "delete")) return FALSE; if(!$this->_checkCode()) return FALSE; return TRUE; } function site($command, $fnction="site") { if(!$this->_exec("SITE ".$command, $fnction)) return FALSE; if(!$this->_checkCode()) return FALSE; return TRUE; } function chmod($pathname, $mode) { if(!$this->site( sprintf('CHMOD %o %s', $mode, $pathname), "chmod")) return FALSE; return TRUE; } function restore($from) { if(!isset($this->_features["REST"])) { $this->PushError("restore", "not supported by server"); return FALSE; } if($this->_curtype!=FTP_BINARY) { $this->PushError("restore", "cannot restore in ASCII mode"); return FALSE; } if(!$this->_exec("REST ".$from, "restore")) return FALSE; if(!$this->_checkCode()) return FALSE; return TRUE; } function features() { if(!$this->_exec("FEAT", "features")) return FALSE; if(!$this->_checkCode()) return FALSE; $f=preg_split("/[".CRLF."]+/", preg_replace("/[0-9]{3}[ -].*[".CRLF."]+/", "", $this->_message), -1, PREG_SPLIT_NO_EMPTY); $this->_features=array(); foreach($f as $k=>$v) { $v=explode(" ", trim($v)); $this->_features[array_shift($v)]=$v; } return true; } function rawlist($pathname="", $arg="") { return $this->_list(($arg?" ".$arg:"").($pathname?" ".$pathname:""), "LIST", "rawlist"); } function nlist($pathname="", $arg="") { return $this->_list(($arg?" ".$arg:"").($pathname?" ".$pathname:""), "NLST", "nlist"); } function is_exists($pathname) { return $this->file_exists($pathname); } function file_exists($pathname) { $exists=true; if(!$this->_exec("RNFR ".$pathname, "rename")) $exists=FALSE; else { if(!$this->_checkCode()) $exists=FALSE; $this->abort(); } if($exists) $this->SendMSG("Remote file ".$pathname." exists"); else $this->SendMSG("Remote file ".$pathname." does not exist"); return $exists; } function fget($fp, $remotefile, $rest=0) { if($this->_can_restore and $rest!=0) fseek($fp, $rest); $pi=pathinfo($remotefile); if($this->_type==FTP_ASCII or ($this->_type==FTP_AUTOASCII and in_array(strtoupper($pi["extension"]), $this->AutoAsciiExt))) $mode=FTP_ASCII; else $mode=FTP_BINARY; if(!$this->_data_prepare($mode)) { return FALSE; } if($this->_can_restore and $rest!=0) $this->restore($rest); if(!$this->_exec("RETR ".$remotefile, "get")) { $this->_data_close(); return FALSE; } if(!$this->_checkCode()) { $this->_data_close(); return FALSE; } $out=$this->_data_read($mode, $fp); $this->_data_close(); if(!$this->_readmsg()) return FALSE; if(!$this->_checkCode()) return FALSE; return $out; } function get($remotefile, $localfile=NULL, $rest=0) { if(is_null($localfile)) $localfile=$remotefile; if (@file_exists($localfile)) $this->SendMSG("Warning : local file will be overwritten"); $fp = @fopen($localfile, "w"); if (!$fp) { $this->PushError("get","cannot open local file", "Cannot create \"".$localfile."\""); return FALSE; } if($this->_can_restore and $rest!=0) fseek($fp, $rest); $pi=pathinfo($remotefile); if($this->_type==FTP_ASCII or ($this->_type==FTP_AUTOASCII and in_array(strtoupper($pi["extension"]), $this->AutoAsciiExt))) $mode=FTP_ASCII; else $mode=FTP_BINARY; if(!$this->_data_prepare($mode)) { fclose($fp); return FALSE; } if($this->_can_restore and $rest!=0) $this->restore($rest); if(!$this->_exec("RETR ".$remotefile, "get")) { $this->_data_close(); fclose($fp); return FALSE; } if(!$this->_checkCode()) { $this->_data_close(); fclose($fp); return FALSE; } $out=$this->_data_read($mode, $fp); fclose($fp); $this->_data_close(); if(!$this->_readmsg()) return FALSE; if(!$this->_checkCode()) return FALSE; return $out; } function fput($remotefile, $fp, $rest=0) { if($this->_can_restore and $rest!=0) fseek($fp, $rest); $pi=pathinfo($remotefile); if($this->_type==FTP_ASCII or ($this->_type==FTP_AUTOASCII and in_array(strtoupper($pi["extension"]), $this->AutoAsciiExt))) $mode=FTP_ASCII; else $mode=FTP_BINARY; if(!$this->_data_prepare($mode)) { return FALSE; } if($this->_can_restore and $rest!=0) $this->restore($rest); if(!$this->_exec("STOR ".$remotefile, "put")) { $this->_data_close(); return FALSE; } if(!$this->_checkCode()) { $this->_data_close(); return FALSE; } $ret=$this->_data_write($mode, $fp); $this->_data_close(); if(!$this->_readmsg()) return FALSE; if(!$this->_checkCode()) return FALSE; return $ret; } function put($localfile, $remotefile=NULL, $rest=0) { if(is_null($remotefile)) $remotefile=$localfile; if (!file_exists($localfile)) { $this->PushError("put","cannot open local file", "No such file or directory \"".$localfile."\""); return FALSE; } $fp = @fopen($localfile, "r"); if (!$fp) { $this->PushError("put","cannot open local file", "Cannot read file \"".$localfile."\""); return FALSE; } if($this->_can_restore and $rest!=0) fseek($fp, $rest); $pi=pathinfo($localfile); if($this->_type==FTP_ASCII or ($this->_type==FTP_AUTOASCII and in_array(strtoupper($pi["extension"]), $this->AutoAsciiExt))) $mode=FTP_ASCII; else $mode=FTP_BINARY; if(!$this->_data_prepare($mode)) { fclose($fp); return FALSE; } if($this->_can_restore and $rest!=0) $this->restore($rest); if(!$this->_exec("STOR ".$remotefile, "put")) { $this->_data_close(); fclose($fp); return FALSE; } if(!$this->_checkCode()) { $this->_data_close(); fclose($fp); return FALSE; } $ret=$this->_data_write($mode, $fp); fclose($fp); $this->_data_close(); if(!$this->_readmsg()) return FALSE; if(!$this->_checkCode()) return FALSE; return $ret; } function mput($local=".", $remote=NULL, $continious=false) { $local=realpath($local); if(!@file_exists($local)) { $this->PushError("mput","cannot open local folder", "Cannot stat folder \"".$local."\""); return FALSE; } if(!is_dir($local)) return $this->put($local, $remote); if(empty($remote)) $remote="."; elseif(!$this->file_exists($remote) and !$this->mkdir($remote)) return FALSE; if($handle = opendir($local)) { $list=array(); while (false !== ($file = readdir($handle))) { if ($file != "." && $file != "..") $list[]=$file; } closedir($handle); } else { $this->PushError("mput","cannot open local folder", "Cannot read folder \"".$local."\""); return FALSE; } if(empty($list)) return TRUE; $ret=true; foreach($list as $el) { if(is_dir($local."/".$el)) $t=$this->mput($local."/".$el, $remote."/".$el); else $t=$this->put($local."/".$el, $remote."/".$el); if(!$t) { $ret=FALSE; if(!$continious) break; } } return $ret; } function mget($remote, $local=".", $continious=false) { $list=$this->rawlist($remote, "-lA"); if($list===false) { $this->PushError("mget","cannot read remote folder list", "Cannot read remote folder \"".$remote."\" contents"); return FALSE; } if(empty($list)) return true; if(!@file_exists($local)) { if(!@mkdir($local)) { $this->PushError("mget","cannot create local folder", "Cannot create folder \"".$local."\""); return FALSE; } } foreach($list as $k=>$v) { $list[$k]=$this->parselisting($v); if( ! $list[$k] or $list[$k]["name"]=="." or $list[$k]["name"]=="..") unset($list[$k]); } $ret=true; foreach($list as $el) { if($el["type"]=="d") { if(!$this->mget($remote."/".$el["name"], $local."/".$el["name"], $continious)) { $this->PushError("mget", "cannot copy folder", "Cannot copy remote folder \"".$remote."/".$el["name"]."\" to local \"".$local."/".$el["name"]."\""); $ret=false; if(!$continious) break; } } else { if(!$this->get($remote."/".$el["name"], $local."/".$el["name"])) { $this->PushError("mget", "cannot copy file", "Cannot copy remote file \"".$remote."/".$el["name"]."\" to local \"".$local."/".$el["name"]."\""); $ret=false; if(!$continious) break; } } @chmod($local."/".$el["name"], $el["perms"]); $t=strtotime($el["date"]); if($t!==-1 and $t!==false) @touch($local."/".$el["name"], $t); } return $ret; } function mdel($remote, $continious=false) { $list=$this->rawlist($remote, "-la"); if($list===false) { $this->PushError("mdel","cannot read remote folder list", "Cannot read remote folder \"".$remote."\" contents"); return false; } foreach($list as $k=>$v) { $list[$k]=$this->parselisting($v); if( ! $list[$k] or $list[$k]["name"]=="." or $list[$k]["name"]=="..") unset($list[$k]); } $ret=true; foreach($list as $el) { if ( empty($el) ) continue; if($el["type"]=="d") { if(!$this->mdel($remote."/".$el["name"], $continious)) { $ret=false; if(!$continious) break; } } else { if (!$this->delete($remote."/".$el["name"])) { $this->PushError("mdel", "cannot delete file", "Cannot delete remote file \"".$remote."/".$el["name"]."\""); $ret=false; if(!$continious) break; } } } if(!$this->rmdir($remote)) { $this->PushError("mdel", "cannot delete folder", "Cannot delete remote folder \"".$remote."/".$el["name"]."\""); $ret=false; } return $ret; } function mmkdir($dir, $mode = 0777) { if(empty($dir)) return FALSE; if($this->is_exists($dir) or $dir == "/" ) return TRUE; if(!$this->mmkdir(dirname($dir), $mode)) return false; $r=$this->mkdir($dir, $mode); $this->chmod($dir,$mode); return $r; } function glob($pattern, $handle=NULL) { $path=$output=null; if(PHP_OS=='WIN32') $slash='\\'; else $slash='/'; $lastpos=strrpos($pattern,$slash); if(!($lastpos===false)) { $path=substr($pattern,0,-$lastpos-1); $pattern=substr($pattern,$lastpos); } else $path=getcwd(); if(is_array($handle) and !empty($handle)) { foreach($handle as $dir) { if($this->glob_pattern_match($pattern,$dir)) $output[]=$dir; } } else { $handle=@opendir($path); if($handle===false) return false; while($dir=readdir($handle)) { if($this->glob_pattern_match($pattern,$dir)) $output[]=$dir; } closedir($handle); } if(is_array($output)) return $output; return false; } function glob_pattern_match($pattern,$subject) { $out=null; $chunks=explode(';',$pattern); foreach($chunks as $pattern) { $escape=array('$','^','.','{','}','(',')','[',']','|'); while(str_contains($pattern,'**')) $pattern=str_replace('**','*',$pattern); foreach($escape as $probe) $pattern=str_replace($probe,"\\$probe",$pattern); $pattern=str_replace('?*','*', str_replace('*?','*', str_replace('*',".*", str_replace('?','.{1,1}',$pattern)))); $out[]=$pattern; } if(count($out)==1) return($this->glob_regexp("^$out[0]$",$subject)); else { foreach($out as $tester) // TODO: This should probably be glob_regexp(), but needs tests. if($this->my_regexp("^$tester$",$subject)) return true; } return false; } function glob_regexp($pattern,$subject) { $sensitive=(PHP_OS!='WIN32'); return ($sensitive? preg_match( '/' . preg_quote( $pattern, '/' ) . '/', $subject ) : preg_match( '/' . preg_quote( $pattern, '/' ) . '/i', $subject ) ); } function dirlist($remote) { $list=$this->rawlist($remote, "-la"); if($list===false) { $this->PushError("dirlist","cannot read remote folder list", "Cannot read remote folder \"".$remote."\" contents"); return false; } $dirlist = array(); foreach($list as $k=>$v) { $entry=$this->parselisting($v); if ( empty($entry) ) continue; if($entry["name"]=="." or $entry["name"]=="..") continue; $dirlist[$entry['name']] = $entry; } return $dirlist; } // <!-- --------------------------------------------------------------------------------------- --> // <!-- Private functions --> // <!-- --------------------------------------------------------------------------------------- --> function _checkCode() { return ($this->_code<400 and $this->_code>0); } function _list($arg="", $cmd="LIST", $fnction="_list") { if(!$this->_data_prepare()) return false; if(!$this->_exec($cmd.$arg, $fnction)) { $this->_data_close(); return FALSE; } if(!$this->_checkCode()) { $this->_data_close(); return FALSE; } $out=""; if($this->_code<200) { $out=$this->_data_read(); $this->_data_close(); if(!$this->_readmsg()) return FALSE; if(!$this->_checkCode()) return FALSE; if($out === FALSE ) return FALSE; $out=preg_split("/[".CRLF."]+/", $out, -1, PREG_SPLIT_NO_EMPTY); // $this->SendMSG(implode($this->_eol_code[$this->OS_local], $out)); } return $out; } // <!-- --------------------------------------------------------------------------------------- --> // <!-- Partie : gestion des erreurs --> // <!-- --------------------------------------------------------------------------------------- --> // Gnre une erreur pour traitement externe la classe function PushError($fctname,$msg,$desc=false){ $error=array(); $error['time']=time(); $error['fctname']=$fctname; $error['msg']=$msg; $error['desc']=$desc; if($desc) $tmp=' ('.$desc.')'; else $tmp=''; $this->SendMSG($fctname.': '.$msg.$tmp); return(array_push($this->_error_array,$error)); } // Rcupre une erreur externe function PopError(){ if(count($this->_error_array)) return(array_pop($this->_error_array)); else return(false); } } $mod_sockets = extension_loaded( 'sockets' ); if ( ! $mod_sockets && function_exists( 'dl' ) && is_callable( 'dl' ) ) { $prefix = ( PHP_SHLIB_SUFFIX == 'dll' ) ? 'php_' : ''; @dl( $prefix . 'sockets.' . PHP_SHLIB_SUFFIX ); // phpcs:ignore PHPCompatibility.FunctionUse.RemovedFunctions.dlDeprecated $mod_sockets = extension_loaded( 'sockets' ); } require_once __DIR__ . "/class-ftp-" . ( $mod_sockets ? "sockets" : "pure" ) . ".php"; if ( $mod_sockets ) { class ftp extends ftp_sockets {} } else { class ftp extends ftp_pure {} } includes/class-wp-screen.php 0000644 00000110641 15135536347 0012110 0 ustar 00 <?php /** * Screen API: WP_Screen class * * @package WordPress * @subpackage Administration * @since 4.4.0 */ /** * Core class used to implement an admin screen API. * * @since 3.3.0 */ #[AllowDynamicProperties] final class WP_Screen { /** * Any action associated with the screen. * * 'add' for *-add.php and *-new.php screens. Empty otherwise. * * @since 3.3.0 * @var string */ public $action; /** * The base type of the screen. * * This is typically the same as `$id` but with any post types and taxonomies stripped. * For example, for an `$id` of 'edit-post' the base is 'edit'. * * @since 3.3.0 * @var string */ public $base; /** * The number of columns to display. Access with get_columns(). * * @since 3.4.0 * @var int */ private $columns = 0; /** * The unique ID of the screen. * * @since 3.3.0 * @var string */ public $id; /** * Which admin the screen is in. network | user | site | false * * @since 3.5.0 * @var string */ protected $in_admin; /** * Whether the screen is in the network admin. * * Deprecated. Use in_admin() instead. * * @since 3.3.0 * @deprecated 3.5.0 * @var bool */ public $is_network; /** * Whether the screen is in the user admin. * * Deprecated. Use in_admin() instead. * * @since 3.3.0 * @deprecated 3.5.0 * @var bool */ public $is_user; /** * The base menu parent. * * This is derived from `$parent_file` by removing the query string and any .php extension. * `$parent_file` values of 'edit.php?post_type=page' and 'edit.php?post_type=post' * have a `$parent_base` of 'edit'. * * @since 3.3.0 * @var string|null */ public $parent_base; /** * The parent_file for the screen per the admin menu system. * * Some `$parent_file` values are 'edit.php?post_type=page', 'edit.php', and 'options-general.php'. * * @since 3.3.0 * @var string|null */ public $parent_file; /** * The post type associated with the screen, if any. * * The 'edit.php?post_type=page' screen has a post type of 'page'. * The 'edit-tags.php?taxonomy=$taxonomy&post_type=page' screen has a post type of 'page'. * * @since 3.3.0 * @var string */ public $post_type; /** * The taxonomy associated with the screen, if any. * * The 'edit-tags.php?taxonomy=category' screen has a taxonomy of 'category'. * * @since 3.3.0 * @var string */ public $taxonomy; /** * The help tab data associated with the screen, if any. * * @since 3.3.0 * @var array */ private $_help_tabs = array(); /** * The help sidebar data associated with screen, if any. * * @since 3.3.0 * @var string */ private $_help_sidebar = ''; /** * The accessible hidden headings and text associated with the screen, if any. * * @since 4.4.0 * @var string[] */ private $_screen_reader_content = array(); /** * Stores old string-based help. * * @var array */ private static $_old_compat_help = array(); /** * The screen options associated with screen, if any. * * @since 3.3.0 * @var array */ private $_options = array(); /** * The screen object registry. * * @since 3.3.0 * * @var array */ private static $_registry = array(); /** * Stores the result of the public show_screen_options function. * * @since 3.3.0 * @var bool */ private $_show_screen_options; /** * Stores the 'screen_settings' section of screen options. * * @since 3.3.0 * @var string */ private $_screen_settings; /** * Whether the screen is using the block editor. * * @since 5.0.0 * @var bool */ public $is_block_editor = false; /** * Fetches a screen object. * * @since 3.3.0 * * @global string $hook_suffix * * @param string|WP_Screen $hook_name Optional. The hook name (also known as the hook suffix) used to determine the screen. * Defaults to the current $hook_suffix global. * @return WP_Screen Screen object. */ public static function get( $hook_name = '' ) { if ( $hook_name instanceof WP_Screen ) { return $hook_name; } $id = ''; $post_type = null; $taxonomy = null; $in_admin = false; $action = ''; $is_block_editor = false; if ( $hook_name ) { $id = $hook_name; } elseif ( ! empty( $GLOBALS['hook_suffix'] ) ) { $id = $GLOBALS['hook_suffix']; } // For those pesky meta boxes. if ( $hook_name && post_type_exists( $hook_name ) ) { $post_type = $id; $id = 'post'; // Changes later. Ends up being $base. } else { if ( str_ends_with( $id, '.php' ) ) { $id = substr( $id, 0, -4 ); } if ( in_array( $id, array( 'post-new', 'link-add', 'media-new', 'user-new' ), true ) ) { $id = substr( $id, 0, -4 ); $action = 'add'; } } if ( ! $post_type && $hook_name ) { if ( str_ends_with( $id, '-network' ) ) { $id = substr( $id, 0, -8 ); $in_admin = 'network'; } elseif ( str_ends_with( $id, '-user' ) ) { $id = substr( $id, 0, -5 ); $in_admin = 'user'; } $id = sanitize_key( $id ); if ( 'edit-comments' !== $id && 'edit-tags' !== $id && str_starts_with( $id, 'edit-' ) ) { $maybe = substr( $id, 5 ); if ( taxonomy_exists( $maybe ) ) { $id = 'edit-tags'; $taxonomy = $maybe; } elseif ( post_type_exists( $maybe ) ) { $id = 'edit'; $post_type = $maybe; } } if ( ! $in_admin ) { $in_admin = 'site'; } } else { if ( defined( 'WP_NETWORK_ADMIN' ) && WP_NETWORK_ADMIN ) { $in_admin = 'network'; } elseif ( defined( 'WP_USER_ADMIN' ) && WP_USER_ADMIN ) { $in_admin = 'user'; } else { $in_admin = 'site'; } } if ( 'index' === $id ) { $id = 'dashboard'; } elseif ( 'front' === $id ) { $in_admin = false; } $base = $id; // If this is the current screen, see if we can be more accurate for post types and taxonomies. if ( ! $hook_name ) { if ( isset( $_REQUEST['post_type'] ) ) { $post_type = post_type_exists( $_REQUEST['post_type'] ) ? $_REQUEST['post_type'] : false; } if ( isset( $_REQUEST['taxonomy'] ) ) { $taxonomy = taxonomy_exists( $_REQUEST['taxonomy'] ) ? $_REQUEST['taxonomy'] : false; } switch ( $base ) { case 'post': if ( isset( $_GET['post'] ) && isset( $_POST['post_ID'] ) && (int) $_GET['post'] !== (int) $_POST['post_ID'] ) { wp_die( __( 'A post ID mismatch has been detected.' ), __( 'Sorry, you are not allowed to edit this item.' ), 400 ); } elseif ( isset( $_GET['post'] ) ) { $post_id = (int) $_GET['post']; } elseif ( isset( $_POST['post_ID'] ) ) { $post_id = (int) $_POST['post_ID']; } else { $post_id = 0; } if ( $post_id ) { $post = get_post( $post_id ); if ( $post ) { $post_type = $post->post_type; /** This filter is documented in wp-admin/post.php */ $replace_editor = apply_filters( 'replace_editor', false, $post ); if ( ! $replace_editor ) { $is_block_editor = use_block_editor_for_post( $post ); } } } break; case 'edit-tags': case 'term': if ( null === $post_type && is_object_in_taxonomy( 'post', $taxonomy ? $taxonomy : 'post_tag' ) ) { $post_type = 'post'; } break; case 'upload': $post_type = 'attachment'; break; } } switch ( $base ) { case 'post': if ( null === $post_type ) { $post_type = 'post'; } // When creating a new post, use the default block editor support value for the post type. if ( empty( $post_id ) ) { $is_block_editor = use_block_editor_for_post_type( $post_type ); } $id = $post_type; break; case 'edit': if ( null === $post_type ) { $post_type = 'post'; } $id .= '-' . $post_type; break; case 'edit-tags': case 'term': if ( null === $taxonomy ) { $taxonomy = 'post_tag'; } // The edit-tags ID does not contain the post type. Look for it in the request. if ( null === $post_type ) { $post_type = 'post'; if ( isset( $_REQUEST['post_type'] ) && post_type_exists( $_REQUEST['post_type'] ) ) { $post_type = $_REQUEST['post_type']; } } $id = 'edit-' . $taxonomy; break; } if ( 'network' === $in_admin ) { $id .= '-network'; $base .= '-network'; } elseif ( 'user' === $in_admin ) { $id .= '-user'; $base .= '-user'; } if ( isset( self::$_registry[ $id ] ) ) { $screen = self::$_registry[ $id ]; if ( get_current_screen() === $screen ) { return $screen; } } else { $screen = new self(); $screen->id = $id; } $screen->base = $base; $screen->action = $action; $screen->post_type = (string) $post_type; $screen->taxonomy = (string) $taxonomy; $screen->is_user = ( 'user' === $in_admin ); $screen->is_network = ( 'network' === $in_admin ); $screen->in_admin = $in_admin; $screen->is_block_editor = $is_block_editor; self::$_registry[ $id ] = $screen; return $screen; } /** * Makes the screen object the current screen. * * @see set_current_screen() * @since 3.3.0 * * @global WP_Screen $current_screen WordPress current screen object. * @global string $typenow The post type of the current screen. * @global string $taxnow The taxonomy of the current screen. */ public function set_current_screen() { global $current_screen, $taxnow, $typenow; $current_screen = $this; $typenow = $this->post_type; $taxnow = $this->taxonomy; /** * Fires after the current screen has been set. * * @since 3.0.0 * * @param WP_Screen $current_screen Current WP_Screen object. */ do_action( 'current_screen', $current_screen ); } /** * Constructor * * @since 3.3.0 */ private function __construct() {} /** * Indicates whether the screen is in a particular admin. * * @since 3.5.0 * * @param string $admin The admin to check against (network | user | site). * If empty any of the three admins will result in true. * @return bool True if the screen is in the indicated admin, false otherwise. */ public function in_admin( $admin = null ) { if ( empty( $admin ) ) { return (bool) $this->in_admin; } return ( $admin === $this->in_admin ); } /** * Sets or returns whether the block editor is loading on the current screen. * * @since 5.0.0 * * @param bool $set Optional. Sets whether the block editor is loading on the current screen or not. * @return bool True if the block editor is being loaded, false otherwise. */ public function is_block_editor( $set = null ) { if ( null !== $set ) { $this->is_block_editor = (bool) $set; } return $this->is_block_editor; } /** * Sets the old string-based contextual help for the screen for backward compatibility. * * @since 3.3.0 * * @param WP_Screen $screen A screen object. * @param string $help Help text. */ public static function add_old_compat_help( $screen, $help ) { self::$_old_compat_help[ $screen->id ] = $help; } /** * Sets the parent information for the screen. * * This is called in admin-header.php after the menu parent for the screen has been determined. * * @since 3.3.0 * * @param string $parent_file The parent file of the screen. Typically the $parent_file global. */ public function set_parentage( $parent_file ) { $this->parent_file = $parent_file; list( $this->parent_base ) = explode( '?', $parent_file ); $this->parent_base = str_replace( '.php', '', $this->parent_base ); } /** * Adds an option for the screen. * * Call this in template files after admin.php is loaded and before admin-header.php is loaded * to add screen options. * * @since 3.3.0 * * @param string $option Option ID. * @param mixed $args Option-dependent arguments. */ public function add_option( $option, $args = array() ) { $this->_options[ $option ] = $args; } /** * Removes an option from the screen. * * @since 3.8.0 * * @param string $option Option ID. */ public function remove_option( $option ) { unset( $this->_options[ $option ] ); } /** * Removes all options from the screen. * * @since 3.8.0 */ public function remove_options() { $this->_options = array(); } /** * Gets the options registered for the screen. * * @since 3.8.0 * * @return array Options with arguments. */ public function get_options() { return $this->_options; } /** * Gets the arguments for an option for the screen. * * @since 3.3.0 * * @param string $option Option name. * @param string|false $key Optional. Specific array key for when the option is an array. * Default false. * @return string The option value if set, null otherwise. */ public function get_option( $option, $key = false ) { if ( ! isset( $this->_options[ $option ] ) ) { return null; } if ( $key ) { if ( isset( $this->_options[ $option ][ $key ] ) ) { return $this->_options[ $option ][ $key ]; } return null; } return $this->_options[ $option ]; } /** * Gets the help tabs registered for the screen. * * @since 3.4.0 * @since 4.4.0 Help tabs are ordered by their priority. * * @return array Help tabs with arguments. */ public function get_help_tabs() { $help_tabs = $this->_help_tabs; $priorities = array(); foreach ( $help_tabs as $help_tab ) { if ( isset( $priorities[ $help_tab['priority'] ] ) ) { $priorities[ $help_tab['priority'] ][] = $help_tab; } else { $priorities[ $help_tab['priority'] ] = array( $help_tab ); } } ksort( $priorities ); $sorted = array(); foreach ( $priorities as $list ) { foreach ( $list as $tab ) { $sorted[ $tab['id'] ] = $tab; } } return $sorted; } /** * Gets the arguments for a help tab. * * @since 3.4.0 * * @param string $id Help Tab ID. * @return array Help tab arguments. */ public function get_help_tab( $id ) { if ( ! isset( $this->_help_tabs[ $id ] ) ) { return null; } return $this->_help_tabs[ $id ]; } /** * Adds a help tab to the contextual help for the screen. * * Call this on the `load-$pagenow` hook for the relevant screen, * or fetch the `$current_screen` object, or use get_current_screen() * and then call the method from the object. * * You may need to filter `$current_screen` using an if or switch statement * to prevent new help tabs from being added to ALL admin screens. * * @since 3.3.0 * @since 4.4.0 The `$priority` argument was added. * * @param array $args { * Array of arguments used to display the help tab. * * @type string $title Title for the tab. Default false. * @type string $id Tab ID. Must be HTML-safe and should be unique for this menu. * It is NOT allowed to contain any empty spaces. Default false. * @type string $content Optional. Help tab content in plain text or HTML. Default empty string. * @type callable $callback Optional. A callback to generate the tab content. Default false. * @type int $priority Optional. The priority of the tab, used for ordering. Default 10. * } */ public function add_help_tab( $args ) { $defaults = array( 'title' => false, 'id' => false, 'content' => '', 'callback' => false, 'priority' => 10, ); $args = wp_parse_args( $args, $defaults ); $args['id'] = sanitize_html_class( $args['id'] ); // Ensure we have an ID and title. if ( ! $args['id'] || ! $args['title'] ) { return; } // Allows for overriding an existing tab with that ID. $this->_help_tabs[ $args['id'] ] = $args; } /** * Removes a help tab from the contextual help for the screen. * * @since 3.3.0 * * @param string $id The help tab ID. */ public function remove_help_tab( $id ) { unset( $this->_help_tabs[ $id ] ); } /** * Removes all help tabs from the contextual help for the screen. * * @since 3.3.0 */ public function remove_help_tabs() { $this->_help_tabs = array(); } /** * Gets the content from a contextual help sidebar. * * @since 3.4.0 * * @return string Contents of the help sidebar. */ public function get_help_sidebar() { return $this->_help_sidebar; } /** * Adds a sidebar to the contextual help for the screen. * * Call this in template files after admin.php is loaded and before admin-header.php is loaded * to add a sidebar to the contextual help. * * @since 3.3.0 * * @param string $content Sidebar content in plain text or HTML. */ public function set_help_sidebar( $content ) { $this->_help_sidebar = $content; } /** * Gets the number of layout columns the user has selected. * * The layout_columns option controls the max number and default number of * columns. This method returns the number of columns within that range selected * by the user via Screen Options. If no selection has been made, the default * provisioned in layout_columns is returned. If the screen does not support * selecting the number of layout columns, 0 is returned. * * @since 3.4.0 * * @return int Number of columns to display. */ public function get_columns() { return $this->columns; } /** * Gets the accessible hidden headings and text used in the screen. * * @since 4.4.0 * * @see set_screen_reader_content() For more information on the array format. * * @return string[] An associative array of screen reader text strings. */ public function get_screen_reader_content() { return $this->_screen_reader_content; } /** * Gets a screen reader text string. * * @since 4.4.0 * * @param string $key Screen reader text array named key. * @return string Screen reader text string. */ public function get_screen_reader_text( $key ) { if ( ! isset( $this->_screen_reader_content[ $key ] ) ) { return null; } return $this->_screen_reader_content[ $key ]; } /** * Adds accessible hidden headings and text for the screen. * * @since 4.4.0 * * @param array $content { * An associative array of screen reader text strings. * * @type string $heading_views Screen reader text for the filter links heading. * Default 'Filter items list'. * @type string $heading_pagination Screen reader text for the pagination heading. * Default 'Items list navigation'. * @type string $heading_list Screen reader text for the items list heading. * Default 'Items list'. * } */ public function set_screen_reader_content( $content = array() ) { $defaults = array( 'heading_views' => __( 'Filter items list' ), 'heading_pagination' => __( 'Items list navigation' ), 'heading_list' => __( 'Items list' ), ); $content = wp_parse_args( $content, $defaults ); $this->_screen_reader_content = $content; } /** * Removes all the accessible hidden headings and text for the screen. * * @since 4.4.0 */ public function remove_screen_reader_content() { $this->_screen_reader_content = array(); } /** * Renders the screen's help section. * * This will trigger the deprecated filters for backward compatibility. * * @since 3.3.0 * * @global string $screen_layout_columns */ public function render_screen_meta() { /** * Filters the legacy contextual help list. * * @since 2.7.0 * @deprecated 3.3.0 Use {@see get_current_screen()->add_help_tab()} or * {@see get_current_screen()->remove_help_tab()} instead. * * @param array $old_compat_help Old contextual help. * @param WP_Screen $screen Current WP_Screen instance. */ self::$_old_compat_help = apply_filters_deprecated( 'contextual_help_list', array( self::$_old_compat_help, $this ), '3.3.0', 'get_current_screen()->add_help_tab(), get_current_screen()->remove_help_tab()' ); $old_help = isset( self::$_old_compat_help[ $this->id ] ) ? self::$_old_compat_help[ $this->id ] : ''; /** * Filters the legacy contextual help text. * * @since 2.7.0 * @deprecated 3.3.0 Use {@see get_current_screen()->add_help_tab()} or * {@see get_current_screen()->remove_help_tab()} instead. * * @param string $old_help Help text that appears on the screen. * @param string $screen_id Screen ID. * @param WP_Screen $screen Current WP_Screen instance. */ $old_help = apply_filters_deprecated( 'contextual_help', array( $old_help, $this->id, $this ), '3.3.0', 'get_current_screen()->add_help_tab(), get_current_screen()->remove_help_tab()' ); // Default help only if there is no old-style block of text and no new-style help tabs. if ( empty( $old_help ) && ! $this->get_help_tabs() ) { /** * Filters the default legacy contextual help text. * * @since 2.8.0 * @deprecated 3.3.0 Use {@see get_current_screen()->add_help_tab()} or * {@see get_current_screen()->remove_help_tab()} instead. * * @param string $old_help_default Default contextual help text. */ $default_help = apply_filters_deprecated( 'default_contextual_help', array( '' ), '3.3.0', 'get_current_screen()->add_help_tab(), get_current_screen()->remove_help_tab()' ); if ( $default_help ) { $old_help = '<p>' . $default_help . '</p>'; } } if ( $old_help ) { $this->add_help_tab( array( 'id' => 'old-contextual-help', 'title' => __( 'Overview' ), 'content' => $old_help, ) ); } $help_sidebar = $this->get_help_sidebar(); $help_class = 'hidden'; if ( ! $help_sidebar ) { $help_class .= ' no-sidebar'; } // Time to render! ?> <div id="screen-meta" class="metabox-prefs"> <div id="contextual-help-wrap" class="<?php echo esc_attr( $help_class ); ?>" tabindex="-1" aria-label="<?php esc_attr_e( 'Contextual Help Tab' ); ?>"> <div id="contextual-help-back"></div> <div id="contextual-help-columns"> <div class="contextual-help-tabs"> <ul> <?php $class = ' class="active"'; foreach ( $this->get_help_tabs() as $tab ) : $link_id = "tab-link-{$tab['id']}"; $panel_id = "tab-panel-{$tab['id']}"; ?> <li id="<?php echo esc_attr( $link_id ); ?>"<?php echo $class; ?>> <a href="<?php echo esc_url( "#$panel_id" ); ?>" aria-controls="<?php echo esc_attr( $panel_id ); ?>"> <?php echo esc_html( $tab['title'] ); ?> </a> </li> <?php $class = ''; endforeach; ?> </ul> </div> <?php if ( $help_sidebar ) : ?> <div class="contextual-help-sidebar"> <?php echo $help_sidebar; ?> </div> <?php endif; ?> <div class="contextual-help-tabs-wrap"> <?php $classes = 'help-tab-content active'; foreach ( $this->get_help_tabs() as $tab ) : $panel_id = "tab-panel-{$tab['id']}"; ?> <div id="<?php echo esc_attr( $panel_id ); ?>" class="<?php echo $classes; ?>"> <?php // Print tab content. echo $tab['content']; // If it exists, fire tab callback. if ( ! empty( $tab['callback'] ) ) { call_user_func_array( $tab['callback'], array( $this, $tab ) ); } ?> </div> <?php $classes = 'help-tab-content'; endforeach; ?> </div> </div> </div> <?php // Setup layout columns. /** * Filters the array of screen layout columns. * * This hook provides back-compat for plugins using the back-compat * Filters instead of add_screen_option(). * * @since 2.8.0 * * @param array $empty_columns Empty array. * @param string $screen_id Screen ID. * @param WP_Screen $screen Current WP_Screen instance. */ $columns = apply_filters( 'screen_layout_columns', array(), $this->id, $this ); if ( ! empty( $columns ) && isset( $columns[ $this->id ] ) ) { $this->add_option( 'layout_columns', array( 'max' => $columns[ $this->id ] ) ); } if ( $this->get_option( 'layout_columns' ) ) { $this->columns = (int) get_user_option( "screen_layout_$this->id" ); if ( ! $this->columns && $this->get_option( 'layout_columns', 'default' ) ) { $this->columns = $this->get_option( 'layout_columns', 'default' ); } } $GLOBALS['screen_layout_columns'] = $this->columns; // Set the global for back-compat. // Add screen options. if ( $this->show_screen_options() ) { $this->render_screen_options(); } ?> </div> <?php if ( ! $this->get_help_tabs() && ! $this->show_screen_options() ) { return; } ?> <div id="screen-meta-links"> <?php if ( $this->show_screen_options() ) : ?> <div id="screen-options-link-wrap" class="hide-if-no-js screen-meta-toggle"> <button type="button" id="show-settings-link" class="button show-settings" aria-controls="screen-options-wrap" aria-expanded="false"><?php _e( 'Screen Options' ); ?></button> </div> <?php endif; if ( $this->get_help_tabs() ) : ?> <div id="contextual-help-link-wrap" class="hide-if-no-js screen-meta-toggle"> <button type="button" id="contextual-help-link" class="button show-settings" aria-controls="contextual-help-wrap" aria-expanded="false"><?php _e( 'Help' ); ?></button> </div> <?php endif; ?> </div> <?php } /** * @global array $wp_meta_boxes * * @return bool */ public function show_screen_options() { global $wp_meta_boxes; if ( is_bool( $this->_show_screen_options ) ) { return $this->_show_screen_options; } $columns = get_column_headers( $this ); $show_screen = ! empty( $wp_meta_boxes[ $this->id ] ) || $columns || $this->get_option( 'per_page' ); $this->_screen_settings = ''; if ( 'post' === $this->base ) { $expand = '<fieldset class="editor-expand hidden"><legend>' . __( 'Additional settings' ) . '</legend><label for="editor-expand-toggle">'; $expand .= '<input type="checkbox" id="editor-expand-toggle"' . checked( get_user_setting( 'editor_expand', 'on' ), 'on', false ) . ' />'; $expand .= __( 'Enable full-height editor and distraction-free functionality.' ) . '</label></fieldset>'; $this->_screen_settings = $expand; } /** * Filters the screen settings text displayed in the Screen Options tab. * * @since 3.0.0 * * @param string $screen_settings Screen settings. * @param WP_Screen $screen WP_Screen object. */ $this->_screen_settings = apply_filters( 'screen_settings', $this->_screen_settings, $this ); if ( $this->_screen_settings || $this->_options ) { $show_screen = true; } /** * Filters whether to show the Screen Options tab. * * @since 3.2.0 * * @param bool $show_screen Whether to show Screen Options tab. * Default true. * @param WP_Screen $screen Current WP_Screen instance. */ $this->_show_screen_options = apply_filters( 'screen_options_show_screen', $show_screen, $this ); return $this->_show_screen_options; } /** * Renders the screen options tab. * * @since 3.3.0 * * @param array $options { * Options for the tab. * * @type bool $wrap Whether the screen-options-wrap div will be included. Defaults to true. * } */ public function render_screen_options( $options = array() ) { $options = wp_parse_args( $options, array( 'wrap' => true, ) ); $wrapper_start = ''; $wrapper_end = ''; $form_start = ''; $form_end = ''; // Output optional wrapper. if ( $options['wrap'] ) { $wrapper_start = '<div id="screen-options-wrap" class="hidden" tabindex="-1" aria-label="' . esc_attr__( 'Screen Options Tab' ) . '">'; $wrapper_end = '</div>'; } // Don't output the form and nonce for the widgets accessibility mode links. if ( 'widgets' !== $this->base ) { $form_start = "\n<form id='adv-settings' method='post'>\n"; $form_end = "\n" . wp_nonce_field( 'screen-options-nonce', 'screenoptionnonce', false, false ) . "\n</form>\n"; } echo $wrapper_start . $form_start; $this->render_meta_boxes_preferences(); $this->render_list_table_columns_preferences(); $this->render_screen_layout(); $this->render_per_page_options(); $this->render_view_mode(); echo $this->_screen_settings; /** * Filters whether to show the Screen Options submit button. * * @since 4.4.0 * * @param bool $show_button Whether to show Screen Options submit button. * Default false. * @param WP_Screen $screen Current WP_Screen instance. */ $show_button = apply_filters( 'screen_options_show_submit', false, $this ); if ( $show_button ) { submit_button( __( 'Apply' ), 'primary', 'screen-options-apply', true ); } echo $form_end . $wrapper_end; } /** * Renders the meta boxes preferences. * * @since 4.4.0 * * @global array $wp_meta_boxes */ public function render_meta_boxes_preferences() { global $wp_meta_boxes; if ( ! isset( $wp_meta_boxes[ $this->id ] ) ) { return; } ?> <fieldset class="metabox-prefs"> <legend><?php _e( 'Screen elements' ); ?></legend> <p> <?php _e( 'Some screen elements can be shown or hidden by using the checkboxes.' ); ?> <?php _e( 'Expand or collapse the elements by clicking on their headings, and arrange them by dragging their headings or by clicking on the up and down arrows.' ); ?> </p> <div class="metabox-prefs-container"> <?php meta_box_prefs( $this ); if ( 'dashboard' === $this->id && has_action( 'welcome_panel' ) && current_user_can( 'edit_theme_options' ) ) { if ( isset( $_GET['welcome'] ) ) { $welcome_checked = empty( $_GET['welcome'] ) ? 0 : 1; update_user_meta( get_current_user_id(), 'show_welcome_panel', $welcome_checked ); } else { $welcome_checked = (int) get_user_meta( get_current_user_id(), 'show_welcome_panel', true ); if ( 2 === $welcome_checked && wp_get_current_user()->user_email !== get_option( 'admin_email' ) ) { $welcome_checked = false; } } echo '<label for="wp_welcome_panel-hide">'; echo '<input type="checkbox" id="wp_welcome_panel-hide"' . checked( (bool) $welcome_checked, true, false ) . ' />'; echo _x( 'Welcome', 'Welcome panel' ) . "</label>\n"; } ?> </div> </fieldset> <?php } /** * Renders the list table columns preferences. * * @since 4.4.0 */ public function render_list_table_columns_preferences() { $columns = get_column_headers( $this ); $hidden = get_hidden_columns( $this ); if ( ! $columns ) { return; } $legend = ! empty( $columns['_title'] ) ? $columns['_title'] : __( 'Columns' ); ?> <fieldset class="metabox-prefs"> <legend><?php echo $legend; ?></legend> <?php $special = array( '_title', 'cb', 'comment', 'media', 'name', 'title', 'username', 'blogname' ); foreach ( $columns as $column => $title ) { // Can't hide these for they are special. if ( in_array( $column, $special, true ) ) { continue; } if ( empty( $title ) ) { continue; } /* * The Comments column uses HTML in the display name with some screen * reader text. Make sure to strip tags from the Comments column * title and any other custom column title plugins might add. */ $title = wp_strip_all_tags( $title ); $id = "$column-hide"; echo '<label>'; echo '<input class="hide-column-tog" name="' . $id . '" type="checkbox" id="' . $id . '" value="' . $column . '"' . checked( ! in_array( $column, $hidden, true ), true, false ) . ' />'; echo "$title</label>\n"; } ?> </fieldset> <?php } /** * Renders the option for number of columns on the page. * * @since 3.3.0 */ public function render_screen_layout() { if ( ! $this->get_option( 'layout_columns' ) ) { return; } $screen_layout_columns = $this->get_columns(); $num = $this->get_option( 'layout_columns', 'max' ); ?> <fieldset class='columns-prefs'> <legend class="screen-layout"><?php _e( 'Layout' ); ?></legend> <?php for ( $i = 1; $i <= $num; ++$i ) : ?> <label class="columns-prefs-<?php echo $i; ?>"> <input type='radio' name='screen_columns' value='<?php echo esc_attr( $i ); ?>' <?php checked( $screen_layout_columns, $i ); ?> /> <?php printf( /* translators: %s: Number of columns on the page. */ _n( '%s column', '%s columns', $i ), number_format_i18n( $i ) ); ?> </label> <?php endfor; ?> </fieldset> <?php } /** * Renders the items per page option. * * @since 3.3.0 */ public function render_per_page_options() { if ( null === $this->get_option( 'per_page' ) ) { return; } $per_page_label = $this->get_option( 'per_page', 'label' ); if ( null === $per_page_label ) { $per_page_label = __( 'Number of items per page:' ); } $option = $this->get_option( 'per_page', 'option' ); if ( ! $option ) { $option = str_replace( '-', '_', "{$this->id}_per_page" ); } $per_page = (int) get_user_option( $option ); if ( empty( $per_page ) || $per_page < 1 ) { $per_page = $this->get_option( 'per_page', 'default' ); if ( ! $per_page ) { $per_page = 20; } } if ( 'edit_comments_per_page' === $option ) { $comment_status = isset( $_REQUEST['comment_status'] ) ? $_REQUEST['comment_status'] : 'all'; /** This filter is documented in wp-admin/includes/class-wp-comments-list-table.php */ $per_page = apply_filters( 'comments_per_page', $per_page, $comment_status ); } elseif ( 'categories_per_page' === $option ) { /** This filter is documented in wp-admin/includes/class-wp-terms-list-table.php */ $per_page = apply_filters( 'edit_categories_per_page', $per_page ); } else { /** This filter is documented in wp-admin/includes/class-wp-list-table.php */ $per_page = apply_filters( "{$option}", $per_page ); } // Back compat. if ( isset( $this->post_type ) ) { /** This filter is documented in wp-admin/includes/post.php */ $per_page = apply_filters( 'edit_posts_per_page', $per_page, $this->post_type ); } // This needs a submit button. add_filter( 'screen_options_show_submit', '__return_true' ); ?> <fieldset class="screen-options"> <legend><?php _e( 'Pagination' ); ?></legend> <?php if ( $per_page_label ) : ?> <label for="<?php echo esc_attr( $option ); ?>"><?php echo $per_page_label; ?></label> <input type="number" step="1" min="1" max="999" class="screen-per-page" name="wp_screen_options[value]" id="<?php echo esc_attr( $option ); ?>" value="<?php echo esc_attr( $per_page ); ?>" /> <?php endif; ?> <input type="hidden" name="wp_screen_options[option]" value="<?php echo esc_attr( $option ); ?>" /> </fieldset> <?php } /** * Renders the list table view mode preferences. * * @since 4.4.0 * * @global string $mode List table view mode. */ public function render_view_mode() { global $mode; $screen = get_current_screen(); // Currently only enabled for posts and comments lists. if ( 'edit' !== $screen->base && 'edit-comments' !== $screen->base ) { return; } $view_mode_post_types = get_post_types( array( 'show_ui' => true ) ); /** * Filters the post types that have different view mode options. * * @since 4.4.0 * * @param string[] $view_mode_post_types Array of post types that can change view modes. * Default post types with show_ui on. */ $view_mode_post_types = apply_filters( 'view_mode_post_types', $view_mode_post_types ); if ( 'edit' === $screen->base && ! in_array( $this->post_type, $view_mode_post_types, true ) ) { return; } if ( ! isset( $mode ) ) { $mode = get_user_setting( 'posts_list_mode', 'list' ); } // This needs a submit button. add_filter( 'screen_options_show_submit', '__return_true' ); ?> <fieldset class="metabox-prefs view-mode"> <legend><?php _e( 'View mode' ); ?></legend> <label for="list-view-mode"> <input id="list-view-mode" type="radio" name="mode" value="list" <?php checked( 'list', $mode ); ?> /> <?php _e( 'Compact view' ); ?> </label> <label for="excerpt-view-mode"> <input id="excerpt-view-mode" type="radio" name="mode" value="excerpt" <?php checked( 'excerpt', $mode ); ?> /> <?php _e( 'Extended view' ); ?> </label> </fieldset> <?php } /** * Renders screen reader text. * * @since 4.4.0 * * @param string $key The screen reader text array named key. * @param string $tag Optional. The HTML tag to wrap the screen reader text. Default h2. */ public function render_screen_reader_content( $key = '', $tag = 'h2' ) { if ( ! isset( $this->_screen_reader_content[ $key ] ) ) { return; } echo "<$tag class='screen-reader-text'>" . $this->_screen_reader_content[ $key ] . "</$tag>"; } } includes/comment.php 0000644 00000013751 15135536347 0010550 0 ustar 00 <?php /** * WordPress Comment Administration API. * * @package WordPress * @subpackage Administration * @since 2.3.0 */ /** * Determines if a comment exists based on author and date. * * For best performance, use `$timezone = 'gmt'`, which queries a field that is properly indexed. The default value * for `$timezone` is 'blog' for legacy reasons. * * @since 2.0.0 * @since 4.4.0 Added the `$timezone` parameter. * * @global wpdb $wpdb WordPress database abstraction object. * * @param string $comment_author Author of the comment. * @param string $comment_date Date of the comment. * @param string $timezone Timezone. Accepts 'blog' or 'gmt'. Default 'blog'. * @return string|null Comment post ID on success. */ function comment_exists( $comment_author, $comment_date, $timezone = 'blog' ) { global $wpdb; $date_field = 'comment_date'; if ( 'gmt' === $timezone ) { $date_field = 'comment_date_gmt'; } return $wpdb->get_var( $wpdb->prepare( "SELECT comment_post_ID FROM $wpdb->comments WHERE comment_author = %s AND $date_field = %s", stripslashes( $comment_author ), stripslashes( $comment_date ) ) ); } /** * Updates a comment with values provided in $_POST. * * @since 2.0.0 * @since 5.5.0 A return value was added. * * @return int|WP_Error The value 1 if the comment was updated, 0 if not updated. * A WP_Error object on failure. */ function edit_comment() { if ( ! current_user_can( 'edit_comment', (int) $_POST['comment_ID'] ) ) { wp_die( __( 'Sorry, you are not allowed to edit comments on this post.' ) ); } if ( isset( $_POST['newcomment_author'] ) ) { $_POST['comment_author'] = $_POST['newcomment_author']; } if ( isset( $_POST['newcomment_author_email'] ) ) { $_POST['comment_author_email'] = $_POST['newcomment_author_email']; } if ( isset( $_POST['newcomment_author_url'] ) ) { $_POST['comment_author_url'] = $_POST['newcomment_author_url']; } if ( isset( $_POST['comment_status'] ) ) { $_POST['comment_approved'] = $_POST['comment_status']; } if ( isset( $_POST['content'] ) ) { $_POST['comment_content'] = $_POST['content']; } if ( isset( $_POST['comment_ID'] ) ) { $_POST['comment_ID'] = (int) $_POST['comment_ID']; } foreach ( array( 'aa', 'mm', 'jj', 'hh', 'mn' ) as $timeunit ) { if ( ! empty( $_POST[ 'hidden_' . $timeunit ] ) && $_POST[ 'hidden_' . $timeunit ] !== $_POST[ $timeunit ] ) { $_POST['edit_date'] = '1'; break; } } if ( ! empty( $_POST['edit_date'] ) ) { $aa = $_POST['aa']; $mm = $_POST['mm']; $jj = $_POST['jj']; $hh = $_POST['hh']; $mn = $_POST['mn']; $ss = $_POST['ss']; $jj = ( $jj > 31 ) ? 31 : $jj; $hh = ( $hh > 23 ) ? $hh - 24 : $hh; $mn = ( $mn > 59 ) ? $mn - 60 : $mn; $ss = ( $ss > 59 ) ? $ss - 60 : $ss; $_POST['comment_date'] = "$aa-$mm-$jj $hh:$mn:$ss"; } return wp_update_comment( $_POST, true ); } /** * Returns a WP_Comment object based on comment ID. * * @since 2.0.0 * * @param int $id ID of comment to retrieve. * @return WP_Comment|false Comment if found. False on failure. */ function get_comment_to_edit( $id ) { $comment = get_comment( $id ); if ( ! $comment ) { return false; } $comment->comment_ID = (int) $comment->comment_ID; $comment->comment_post_ID = (int) $comment->comment_post_ID; $comment->comment_content = format_to_edit( $comment->comment_content ); /** * Filters the comment content before editing. * * @since 2.0.0 * * @param string $comment_content Comment content. */ $comment->comment_content = apply_filters( 'comment_edit_pre', $comment->comment_content ); $comment->comment_author = format_to_edit( $comment->comment_author ); $comment->comment_author_email = format_to_edit( $comment->comment_author_email ); $comment->comment_author_url = format_to_edit( $comment->comment_author_url ); $comment->comment_author_url = esc_url( $comment->comment_author_url ); return $comment; } /** * Gets the number of pending comments on a post or posts. * * @since 2.3.0 * * @global wpdb $wpdb WordPress database abstraction object. * * @param int|int[] $post_id Either a single Post ID or an array of Post IDs * @return int|int[] Either a single Posts pending comments as an int or an array of ints keyed on the Post IDs */ function get_pending_comments_num( $post_id ) { global $wpdb; $single = false; if ( ! is_array( $post_id ) ) { $post_id_array = (array) $post_id; $single = true; } else { $post_id_array = $post_id; } $post_id_array = array_map( 'intval', $post_id_array ); $post_id_in = "'" . implode( "', '", $post_id_array ) . "'"; $pending = $wpdb->get_results( "SELECT comment_post_ID, COUNT(comment_ID) as num_comments FROM $wpdb->comments WHERE comment_post_ID IN ( $post_id_in ) AND comment_approved = '0' GROUP BY comment_post_ID", ARRAY_A ); if ( $single ) { if ( empty( $pending ) ) { return 0; } else { return absint( $pending[0]['num_comments'] ); } } $pending_keyed = array(); // Default to zero pending for all posts in request. foreach ( $post_id_array as $id ) { $pending_keyed[ $id ] = 0; } if ( ! empty( $pending ) ) { foreach ( $pending as $pend ) { $pending_keyed[ $pend['comment_post_ID'] ] = absint( $pend['num_comments'] ); } } return $pending_keyed; } /** * Adds avatars to relevant places in admin. * * @since 2.5.0 * * @param string $name User name. * @return string Avatar with the user name. */ function floated_admin_avatar( $name ) { $avatar = get_avatar( get_comment(), 32, 'mystery' ); return "$avatar $name"; } /** * Enqueues comment shortcuts jQuery script. * * @since 2.7.0 */ function enqueue_comment_hotkeys_js() { if ( 'true' === get_user_option( 'comment_shortcuts' ) ) { wp_enqueue_script( 'jquery-table-hotkeys' ); } } /** * Displays error message at bottom of comments. * * @param string $msg Error Message. Assumed to contain HTML and be sanitized. */ function comment_footer_die( $msg ) { echo "<div class='wrap'><p>$msg</p></div>"; require_once ABSPATH . 'wp-admin/admin-footer.php'; die; } includes/credits.php 0000644 00000013465 15135536347 0010545 0 ustar 00 <?php /** * WordPress Credits Administration API. * * @package WordPress * @subpackage Administration * @since 4.4.0 */ /** * Retrieves the contributor credits. * * @since 3.2.0 * @since 5.6.0 Added the `$version` and `$locale` parameters. * * @param string $version WordPress version. Defaults to the current version. * @param string $locale WordPress locale. Defaults to the current user's locale. * @return array|false A list of all of the contributors, or false on error. */ function wp_credits( $version = '', $locale = '' ) { if ( ! $version ) { // Include an unmodified $wp_version. require ABSPATH . WPINC . '/version.php'; $version = $wp_version; } if ( ! $locale ) { $locale = get_user_locale(); } $results = get_site_transient( 'wordpress_credits_' . $locale ); if ( ! is_array( $results ) || str_contains( $version, '-' ) || ( isset( $results['data']['version'] ) && ! str_starts_with( $version, $results['data']['version'] ) ) ) { $url = "http://api.wordpress.org/core/credits/1.1/?version={$version}&locale={$locale}"; $options = array( 'user-agent' => 'WordPress/' . $version . '; ' . home_url( '/' ) ); if ( wp_http_supports( array( 'ssl' ) ) ) { $url = set_url_scheme( $url, 'https' ); } $response = wp_remote_get( $url, $options ); if ( is_wp_error( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) { return false; } $results = json_decode( wp_remote_retrieve_body( $response ), true ); if ( ! is_array( $results ) ) { return false; } set_site_transient( 'wordpress_credits_' . $locale, $results, DAY_IN_SECONDS ); } return $results; } /** * Retrieves the link to a contributor's WordPress.org profile page. * * @access private * @since 3.2.0 * * @param string $display_name The contributor's display name (passed by reference). * @param string $username The contributor's username. * @param string $profiles URL to the contributor's WordPress.org profile page. */ function _wp_credits_add_profile_link( &$display_name, $username, $profiles ) { $display_name = '<a href="' . esc_url( sprintf( $profiles, $username ) ) . '">' . esc_html( $display_name ) . '</a>'; } /** * Retrieves the link to an external library used in WordPress. * * @access private * @since 3.2.0 * * @param string $data External library data (passed by reference). */ function _wp_credits_build_object_link( &$data ) { $data = '<a href="' . esc_url( $data[1] ) . '">' . esc_html( $data[0] ) . '</a>'; } /** * Displays the title for a given group of contributors. * * @since 5.3.0 * * @param array $group_data The current contributor group. */ function wp_credits_section_title( $group_data = array() ) { if ( ! count( $group_data ) ) { return; } if ( $group_data['name'] ) { if ( 'Translators' === $group_data['name'] ) { // Considered a special slug in the API response. (Also, will never be returned for en_US.) $title = _x( 'Translators', 'Translate this to be the equivalent of English Translators in your language for the credits page Translators section' ); } elseif ( isset( $group_data['placeholders'] ) ) { // phpcs:ignore WordPress.WP.I18n.LowLevelTranslationFunction,WordPress.WP.I18n.NonSingularStringLiteralText $title = vsprintf( translate( $group_data['name'] ), $group_data['placeholders'] ); } else { // phpcs:ignore WordPress.WP.I18n.LowLevelTranslationFunction,WordPress.WP.I18n.NonSingularStringLiteralText $title = translate( $group_data['name'] ); } echo '<h2 class="wp-people-group-title">' . esc_html( $title ) . "</h2>\n"; } } /** * Displays a list of contributors for a given group. * * @since 5.3.0 * * @param array $credits The credits groups returned from the API. * @param string $slug The current group to display. */ function wp_credits_section_list( $credits = array(), $slug = '' ) { $group_data = isset( $credits['groups'][ $slug ] ) ? $credits['groups'][ $slug ] : array(); $credits_data = $credits['data']; if ( ! count( $group_data ) ) { return; } if ( ! empty( $group_data['shuffle'] ) ) { shuffle( $group_data['data'] ); // We were going to sort by ability to pronounce "hierarchical," but that wouldn't be fair to Matt. } switch ( $group_data['type'] ) { case 'list': array_walk( $group_data['data'], '_wp_credits_add_profile_link', $credits_data['profiles'] ); echo '<p class="wp-credits-list">' . wp_sprintf( '%l.', $group_data['data'] ) . "</p>\n\n"; break; case 'libraries': array_walk( $group_data['data'], '_wp_credits_build_object_link' ); echo '<p class="wp-credits-list">' . wp_sprintf( '%l.', $group_data['data'] ) . "</p>\n\n"; break; default: $compact = 'compact' === $group_data['type']; $classes = 'wp-people-group ' . ( $compact ? 'compact' : '' ); echo '<ul class="' . $classes . '" id="wp-people-group-' . $slug . '">' . "\n"; foreach ( $group_data['data'] as $person_data ) { echo '<li class="wp-person" id="wp-person-' . esc_attr( $person_data[2] ) . '">' . "\n\t"; echo '<a href="' . esc_url( sprintf( $credits_data['profiles'], $person_data[2] ) ) . '" class="web">'; $size = $compact ? 80 : 160; $data = get_avatar_data( $person_data[1] . '@md5.gravatar.com', array( 'size' => $size ) ); $data2x = get_avatar_data( $person_data[1] . '@md5.gravatar.com', array( 'size' => $size * 2 ) ); echo '<span class="wp-person-avatar"><img src="' . esc_url( $data['url'] ) . '" srcset="' . esc_url( $data2x['url'] ) . ' 2x" class="gravatar" alt="" /></span>' . "\n"; echo esc_html( $person_data[0] ) . "</a>\n\t"; if ( ! $compact && ! empty( $person_data[3] ) ) { // phpcs:ignore WordPress.WP.I18n.LowLevelTranslationFunction,WordPress.WP.I18n.NonSingularStringLiteralText echo '<span class="title">' . translate( $person_data[3] ) . "</span>\n"; } echo "</li>\n"; } echo "</ul>\n"; break; } } includes/ms.php 0000644 00000102212 15135536347 0007514 0 ustar 00 <?php /** * Multisite administration functions. * * @package WordPress * @subpackage Multisite * @since 3.0.0 */ /** * Determines whether uploaded file exceeds space quota. * * @since 3.0.0 * * @param array $file An element from the `$_FILES` array for a given file. * @return array The `$_FILES` array element with 'error' key set if file exceeds quota. 'error' is empty otherwise. */ function check_upload_size( $file ) { if ( get_site_option( 'upload_space_check_disabled' ) ) { return $file; } if ( $file['error'] > 0 ) { // There's already an error. return $file; } if ( defined( 'WP_IMPORTING' ) ) { return $file; } $space_left = get_upload_space_available(); $file_size = filesize( $file['tmp_name'] ); if ( $space_left < $file_size ) { /* translators: %s: Required disk space in kilobytes. */ $file['error'] = sprintf( __( 'Not enough space to upload. %s KB needed.' ), number_format( ( $file_size - $space_left ) / KB_IN_BYTES ) ); } if ( $file_size > ( KB_IN_BYTES * get_site_option( 'fileupload_maxk', 1500 ) ) ) { /* translators: %s: Maximum allowed file size in kilobytes. */ $file['error'] = sprintf( __( 'This file is too big. Files must be less than %s KB in size.' ), get_site_option( 'fileupload_maxk', 1500 ) ); } if ( upload_is_user_over_quota( false ) ) { $file['error'] = __( 'You have used your space quota. Please delete files before uploading.' ); } if ( $file['error'] > 0 && ! isset( $_POST['html-upload'] ) && ! wp_doing_ajax() ) { wp_die( $file['error'] . ' <a href="javascript:history.go(-1)">' . __( 'Back' ) . '</a>' ); } return $file; } /** * Deletes a site. * * @since 3.0.0 * @since 5.1.0 Use wp_delete_site() internally to delete the site row from the database. * * @param int $blog_id Site ID. * @param bool $drop True if site's database tables should be dropped. Default false. */ function wpmu_delete_blog( $blog_id, $drop = false ) { $blog_id = (int) $blog_id; $switch = false; if ( get_current_blog_id() !== $blog_id ) { $switch = true; switch_to_blog( $blog_id ); } $blog = get_site( $blog_id ); $current_network = get_network(); // If a full blog object is not available, do not destroy anything. if ( $drop && ! $blog ) { $drop = false; } // Don't destroy the initial, main, or root blog. if ( $drop && ( 1 === $blog_id || is_main_site( $blog_id ) || ( $blog->path === $current_network->path && $blog->domain === $current_network->domain ) ) ) { $drop = false; } $upload_path = trim( get_option( 'upload_path' ) ); // If ms_files_rewriting is enabled and upload_path is empty, wp_upload_dir is not reliable. if ( $drop && get_site_option( 'ms_files_rewriting' ) && empty( $upload_path ) ) { $drop = false; } if ( $drop ) { wp_delete_site( $blog_id ); } else { /** This action is documented in wp-includes/ms-blogs.php */ do_action_deprecated( 'delete_blog', array( $blog_id, false ), '5.1.0' ); $users = get_users( array( 'blog_id' => $blog_id, 'fields' => 'ids', ) ); // Remove users from this blog. if ( ! empty( $users ) ) { foreach ( $users as $user_id ) { remove_user_from_blog( $user_id, $blog_id ); } } update_blog_status( $blog_id, 'deleted', 1 ); /** This action is documented in wp-includes/ms-blogs.php */ do_action_deprecated( 'deleted_blog', array( $blog_id, false ), '5.1.0' ); } if ( $switch ) { restore_current_blog(); } } /** * Deletes a user and all of their posts from the network. * * This function: * * - Deletes all posts (of all post types) authored by the user on all sites on the network * - Deletes all links owned by the user on all sites on the network * - Removes the user from all sites on the network * - Deletes the user from the database * * @since 3.0.0 * * @global wpdb $wpdb WordPress database abstraction object. * * @param int $id The user ID. * @return bool True if the user was deleted, false otherwise. */ function wpmu_delete_user( $id ) { global $wpdb; if ( ! is_numeric( $id ) ) { return false; } $id = (int) $id; $user = new WP_User( $id ); if ( ! $user->exists() ) { return false; } // Global super-administrators are protected, and cannot be deleted. $_super_admins = get_super_admins(); if ( in_array( $user->user_login, $_super_admins, true ) ) { return false; } /** * Fires before a user is deleted from the network. * * @since MU (3.0.0) * @since 5.5.0 Added the `$user` parameter. * * @param int $id ID of the user about to be deleted from the network. * @param WP_User $user WP_User object of the user about to be deleted from the network. */ do_action( 'wpmu_delete_user', $id, $user ); $blogs = get_blogs_of_user( $id ); if ( ! empty( $blogs ) ) { foreach ( $blogs as $blog ) { switch_to_blog( $blog->userblog_id ); remove_user_from_blog( $id, $blog->userblog_id ); $post_ids = $wpdb->get_col( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_author = %d", $id ) ); foreach ( (array) $post_ids as $post_id ) { wp_delete_post( $post_id ); } // Clean links. $link_ids = $wpdb->get_col( $wpdb->prepare( "SELECT link_id FROM $wpdb->links WHERE link_owner = %d", $id ) ); if ( $link_ids ) { foreach ( $link_ids as $link_id ) { wp_delete_link( $link_id ); } } restore_current_blog(); } } $meta = $wpdb->get_col( $wpdb->prepare( "SELECT umeta_id FROM $wpdb->usermeta WHERE user_id = %d", $id ) ); foreach ( $meta as $mid ) { delete_metadata_by_mid( 'user', $mid ); } $wpdb->delete( $wpdb->users, array( 'ID' => $id ) ); clean_user_cache( $user ); /** This action is documented in wp-admin/includes/user.php */ do_action( 'deleted_user', $id, null, $user ); return true; } /** * Checks whether a site has used its allotted upload space. * * @since MU (3.0.0) * * @param bool $display_message Optional. If set to true and the quota is exceeded, * a warning message is displayed. Default true. * @return bool True if user is over upload space quota, otherwise false. */ function upload_is_user_over_quota( $display_message = true ) { if ( get_site_option( 'upload_space_check_disabled' ) ) { return false; } $space_allowed = get_space_allowed(); if ( ! is_numeric( $space_allowed ) ) { $space_allowed = 10; // Default space allowed is 10 MB. } $space_used = get_space_used(); if ( ( $space_allowed - $space_used ) < 0 ) { if ( $display_message ) { printf( /* translators: %s: Allowed space allocation. */ __( 'Sorry, you have used your space allocation of %s. Please delete some files to upload more files.' ), size_format( $space_allowed * MB_IN_BYTES ) ); } return true; } else { return false; } } /** * Displays the amount of disk space used by the current site. Not used in core. * * @since MU (3.0.0) */ function display_space_usage() { $space_allowed = get_space_allowed(); $space_used = get_space_used(); $percent_used = ( $space_used / $space_allowed ) * 100; $space = size_format( $space_allowed * MB_IN_BYTES ); ?> <strong> <?php /* translators: Storage space that's been used. 1: Percentage of used space, 2: Total space allowed in megabytes or gigabytes. */ printf( __( 'Used: %1$s%% of %2$s' ), number_format( $percent_used ), $space ); ?> </strong> <?php } /** * Gets the remaining upload space for this site. * * @since MU (3.0.0) * * @param int $size Current max size in bytes. * @return int Max size in bytes. */ function fix_import_form_size( $size ) { if ( upload_is_user_over_quota( false ) ) { return 0; } $available = get_upload_space_available(); return min( $size, $available ); } /** * Displays the site upload space quota setting form on the Edit Site Settings screen. * * @since 3.0.0 * * @param int $id The ID of the site to display the setting for. */ function upload_space_setting( $id ) { switch_to_blog( $id ); $quota = get_option( 'blog_upload_space' ); restore_current_blog(); if ( ! $quota ) { $quota = ''; } ?> <tr> <th><label for="blog-upload-space-number"><?php _e( 'Site Upload Space Quota' ); ?></label></th> <td> <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 esc_attr( $quota ); ?>" /> <span id="blog-upload-space-desc"><span class="screen-reader-text"> <?php /* translators: Hidden accessibility text. */ _e( 'Size in megabytes' ); ?> </span> <?php _e( 'MB (Leave blank for network default)' ); ?></span> </td> </tr> <?php } /** * Cleans the user cache for a specific user. * * @since 3.0.0 * * @param int $id The user ID. * @return int|false The ID of the refreshed user or false if the user does not exist. */ function refresh_user_details( $id ) { $id = (int) $id; $user = get_userdata( $id ); if ( ! $user ) { return false; } clean_user_cache( $user ); return $id; } /** * Returns the language for a language code. * * @since 3.0.0 * * @param string $code Optional. The two-letter language code. Default empty. * @return string The language corresponding to $code if it exists. If it does not exist, * then the first two letters of $code is returned. */ function format_code_lang( $code = '' ) { $code = strtolower( substr( $code, 0, 2 ) ); $lang_codes = array( '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', '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', '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', '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', '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', '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', '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', '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', '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', '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', 've' => 'Venda', 'vi' => 'Vietnamese', 'vo' => 'Volapük', 'cy' => 'Welsh', 'wa' => 'Walloon', 'wo' => 'Wolof', 'xh' => 'Xhosa', 'yi' => 'Yiddish', 'yo' => 'Yoruba', 'za' => 'Zhuang; Chuang', 'zu' => 'Zulu', ); /** * Filters the language codes. * * @since MU (3.0.0) * * @param string[] $lang_codes Array of key/value pairs of language codes where key is the short version. * @param string $code A two-letter designation of the language. */ $lang_codes = apply_filters( 'lang_codes', $lang_codes, $code ); return strtr( $code, $lang_codes ); } /** * Displays an access denied message when a user tries to view a site's dashboard they * do not have access to. * * @since 3.2.0 * @access private */ function _access_denied_splash() { if ( ! is_user_logged_in() || is_network_admin() ) { return; } $blogs = get_blogs_of_user( get_current_user_id() ); if ( wp_list_filter( $blogs, array( 'userblog_id' => get_current_blog_id() ) ) ) { return; } $blog_name = get_bloginfo( 'name' ); if ( empty( $blogs ) ) { wp_die( sprintf( /* translators: 1: Site title. */ __( '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 ); } $output = '<p>' . sprintf( /* translators: 1: Site title. */ __( '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>'; $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>'; $output .= '<h3>' . __( 'Your Sites' ) . '</h3>'; $output .= '<table>'; foreach ( $blogs as $blog ) { $output .= '<tr>'; $output .= "<td>{$blog->blogname}</td>"; $output .= '<td><a href="' . esc_url( get_admin_url( $blog->userblog_id ) ) . '">' . __( 'Visit Dashboard' ) . '</a> | ' . '<a href="' . esc_url( get_home_url( $blog->userblog_id ) ) . '">' . __( 'View Site' ) . '</a></td>'; $output .= '</tr>'; } $output .= '</table>'; wp_die( $output, 403 ); } /** * Checks if the current user has permissions to import new users. * * @since 3.0.0 * * @param string $permission A permission to be checked. Currently not used. * @return bool True if the user has proper permissions, false if they do not. */ function check_import_new_users( $permission ) { if ( ! current_user_can( 'manage_network_users' ) ) { return false; } return true; } // See "import_allow_fetch_attachments" and "import_attachment_size_limit" filters too. /** * Generates and displays a drop-down of available languages. * * @since 3.0.0 * * @param string[] $lang_files Optional. An array of the language files. Default empty array. * @param string $current Optional. The current language code. Default empty. */ function mu_dropdown_languages( $lang_files = array(), $current = '' ) { $flag = false; $output = array(); foreach ( (array) $lang_files as $val ) { $code_lang = basename( $val, '.mo' ); if ( 'en_US' === $code_lang ) { // American English. $flag = true; $ae = __( 'American English' ); $output[ $ae ] = '<option value="' . esc_attr( $code_lang ) . '"' . selected( $current, $code_lang, false ) . '> ' . $ae . '</option>'; } elseif ( 'en_GB' === $code_lang ) { // British English. $flag = true; $be = __( 'British English' ); $output[ $be ] = '<option value="' . esc_attr( $code_lang ) . '"' . selected( $current, $code_lang, false ) . '> ' . $be . '</option>'; } else { $translated = format_code_lang( $code_lang ); $output[ $translated ] = '<option value="' . esc_attr( $code_lang ) . '"' . selected( $current, $code_lang, false ) . '> ' . esc_html( $translated ) . '</option>'; } } if ( false === $flag ) { // WordPress English. $output[] = '<option value=""' . selected( $current, '', false ) . '>' . __( 'English' ) . '</option>'; } // Order by name. uksort( $output, 'strnatcasecmp' ); /** * Filters the languages available in the dropdown. * * @since MU (3.0.0) * * @param string[] $output Array of HTML output for the dropdown. * @param string[] $lang_files Array of available language files. * @param string $current The current language code. */ $output = apply_filters( 'mu_dropdown_languages', $output, $lang_files, $current ); echo implode( "\n\t", $output ); } /** * Displays an admin notice to upgrade all sites after a core upgrade. * * @since 3.0.0 * * @global int $wp_db_version WordPress database version. * @global string $pagenow The filename of the current screen. * * @return void|false Void on success. False if the current user is not a super admin. */ function site_admin_notice() { global $wp_db_version, $pagenow; if ( ! current_user_can( 'upgrade_network' ) ) { return false; } if ( 'upgrade.php' === $pagenow ) { return; } if ( (int) get_site_option( 'wpmu_upgrade_site' ) !== $wp_db_version ) { $upgrade_network_message = sprintf( /* translators: %s: URL to Upgrade Network screen. */ __( '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' ) ) ); wp_admin_notice( $upgrade_network_message, array( 'type' => 'warning', 'additional_classes' => array( 'update-nag', 'inline' ), 'paragraph_wrap' => false, ) ); } } /** * Avoids a collision between a site slug and a permalink slug. * * In a subdirectory installation this will make sure that a site and a post do not use the * same subdirectory by checking for a site with the same name as a new post. * * @since 3.0.0 * * @param array $data An array of post data. * @param array $postarr An array of posts. Not currently used. * @return array The new array of post data after checking for collisions. */ function avoid_blog_page_permalink_collision( $data, $postarr ) { if ( is_subdomain_install() ) { return $data; } if ( 'page' !== $data['post_type'] ) { return $data; } if ( ! isset( $data['post_name'] ) || '' === $data['post_name'] ) { return $data; } if ( ! is_main_site() ) { return $data; } if ( isset( $data['post_parent'] ) && $data['post_parent'] ) { return $data; } $post_name = $data['post_name']; $c = 0; while ( $c < 10 && get_id_from_blogname( $post_name ) ) { $post_name .= mt_rand( 1, 10 ); ++$c; } if ( $post_name !== $data['post_name'] ) { $data['post_name'] = $post_name; } return $data; } /** * Handles the display of choosing a user's primary site. * * This displays the user's primary site and allows the user to choose * which site is primary. * * @since 3.0.0 */ function choose_primary_blog() { ?> <table class="form-table" role="presentation"> <tr> <?php /* translators: My Sites label. */ ?> <th scope="row"><label for="primary_blog"><?php _e( 'Primary Site' ); ?></label></th> <td> <?php $all_blogs = get_blogs_of_user( get_current_user_id() ); $primary_blog = (int) get_user_meta( get_current_user_id(), 'primary_blog', true ); if ( count( $all_blogs ) > 1 ) { $found = false; ?> <select name="primary_blog" id="primary_blog"> <?php foreach ( (array) $all_blogs as $blog ) { if ( $blog->userblog_id === $primary_blog ) { $found = true; } ?> <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 } ?> </select> <?php if ( ! $found ) { $blog = reset( $all_blogs ); update_user_meta( get_current_user_id(), 'primary_blog', $blog->userblog_id ); } } elseif ( 1 === count( $all_blogs ) ) { $blog = reset( $all_blogs ); echo esc_url( get_home_url( $blog->userblog_id ) ); if ( $blog->userblog_id !== $primary_blog ) { // Set the primary blog again if it's out of sync with blog list. update_user_meta( get_current_user_id(), 'primary_blog', $blog->userblog_id ); } } else { _e( 'Not available' ); } ?> </td> </tr> </table> <?php } /** * Determines whether or not this network from this page can be edited. * * By default editing of network is restricted to the Network Admin for that `$network_id`. * This function allows for this to be overridden. * * @since 3.1.0 * * @param int $network_id The network ID to check. * @return bool True if network can be edited, false otherwise. */ function can_edit_network( $network_id ) { if ( get_current_network_id() === (int) $network_id ) { $result = true; } else { $result = false; } /** * Filters whether this network can be edited from this page. * * @since 3.1.0 * * @param bool $result Whether the network can be edited from this page. * @param int $network_id The network ID to check. */ return apply_filters( 'can_edit_network', $result, $network_id ); } /** * Prints thickbox image paths for Network Admin. * * @since 3.1.0 * * @access private */ function _thickbox_path_admin_subfolder() { ?> <script type="text/javascript"> var tb_pathToImage = "<?php echo esc_js( includes_url( 'js/thickbox/loadingAnimation.gif', 'relative' ) ); ?>"; </script> <?php } /** * @param array $users * @return bool */ function confirm_delete_users( $users ) { $current_user = wp_get_current_user(); if ( ! is_array( $users ) || empty( $users ) ) { return false; } ?> <h1><?php esc_html_e( 'Users' ); ?></h1> <?php if ( 1 === count( $users ) ) : ?> <p><?php _e( 'You have chosen to delete the user from all networks and sites.' ); ?></p> <?php else : ?> <p><?php _e( 'You have chosen to delete the following users from all networks and sites.' ); ?></p> <?php endif; ?> <form action="users.php?action=dodelete" method="post"> <input type="hidden" name="dodelete" /> <?php wp_nonce_field( 'ms-users-delete' ); $site_admins = get_super_admins(); $admin_out = '<option value="' . esc_attr( $current_user->ID ) . '">' . $current_user->user_login . '</option>'; ?> <table class="form-table" role="presentation"> <?php $allusers = (array) $_POST['allusers']; foreach ( $allusers as $user_id ) { if ( '' !== $user_id && '0' !== $user_id ) { $delete_user = get_userdata( $user_id ); if ( ! current_user_can( 'delete_user', $delete_user->ID ) ) { wp_die( sprintf( /* translators: %s: User login. */ __( 'Warning! User %s cannot be deleted.' ), $delete_user->user_login ) ); } if ( in_array( $delete_user->user_login, $site_admins, true ) ) { wp_die( sprintf( /* translators: %s: User login. */ __( 'Warning! User cannot be deleted. The user %s is a network administrator.' ), '<em>' . $delete_user->user_login . '</em>' ) ); } ?> <tr> <th scope="row"><?php echo $delete_user->user_login; ?> <?php echo '<input type="hidden" name="user[]" value="' . esc_attr( $user_id ) . '" />' . "\n"; ?> </th> <?php $blogs = get_blogs_of_user( $user_id, true ); if ( ! empty( $blogs ) ) { ?> <td><fieldset><p><legend> <?php printf( /* translators: %s: User login. */ __( 'What should be done with content owned by %s?' ), '<em>' . $delete_user->user_login . '</em>' ); ?> </legend></p> <?php foreach ( (array) $blogs as $key => $details ) { $blog_users = get_users( array( 'blog_id' => $details->userblog_id, 'fields' => array( 'ID', 'user_login' ), ) ); if ( is_array( $blog_users ) && ! empty( $blog_users ) ) { $user_site = "<a href='" . esc_url( get_home_url( $details->userblog_id ) ) . "'>{$details->blogname}</a>"; $user_dropdown = '<label for="reassign_user" class="screen-reader-text">' . /* translators: Hidden accessibility text. */ __( 'Select a user' ) . '</label>'; $user_dropdown .= "<select name='blog[$user_id][$key]' id='reassign_user'>"; $user_list = ''; foreach ( $blog_users as $user ) { if ( ! in_array( (int) $user->ID, $allusers, true ) ) { $user_list .= "<option value='{$user->ID}'>{$user->user_login}</option>"; } } if ( '' === $user_list ) { $user_list = $admin_out; } $user_dropdown .= $user_list; $user_dropdown .= "</select>\n"; ?> <ul style="list-style:none;"> <li> <?php /* translators: %s: Link to user's site. */ printf( __( 'Site: %s' ), $user_site ); ?> </li> <li><label><input type="radio" id="delete_option0" name="delete[<?php echo $details->userblog_id . '][' . $delete_user->ID; ?>]" value="delete" checked="checked" /> <?php _e( 'Delete all content.' ); ?></label></li> <li><label><input type="radio" id="delete_option1" name="delete[<?php echo $details->userblog_id . '][' . $delete_user->ID; ?>]" value="reassign" /> <?php _e( 'Attribute all content to:' ); ?></label> <?php echo $user_dropdown; ?></li> </ul> <?php } } echo '</fieldset></td></tr>'; } else { ?> <td><p><?php _e( 'User has no sites or content and will be deleted.' ); ?></p></td> <?php } ?> </tr> <?php } } ?> </table> <?php /** This action is documented in wp-admin/users.php */ do_action( 'delete_user_form', $current_user, $allusers ); if ( 1 === count( $users ) ) : ?> <p><?php _e( 'Once you hit “Confirm Deletion”, the user will be permanently removed.' ); ?></p> <?php else : ?> <p><?php _e( 'Once you hit “Confirm Deletion”, these users will be permanently removed.' ); ?></p> <?php endif; submit_button( __( 'Confirm Deletion' ), 'primary' ); ?> </form> <?php return true; } /** * Prints JavaScript in the header on the Network Settings screen. * * @since 4.1.0 */ function network_settings_add_js() { ?> <script type="text/javascript"> jQuery( function($) { var languageSelect = $( '#WPLANG' ); $( 'form' ).on( 'submit', function() { /* * Don't show a spinner for English and installed languages, * as there is nothing to download. */ if ( ! languageSelect.find( 'option:selected' ).data( 'installed' ) ) { $( '#submit', this ).after( '<span class="spinner language-install-spinner is-active" />' ); } }); } ); </script> <?php } /** * Outputs the HTML for a network's "Edit Site" tabular interface. * * @since 4.6.0 * * @global string $pagenow The filename of the current screen. * * @param array $args { * Optional. Array or string of Query parameters. Default empty array. * * @type int $blog_id The site ID. Default is the current site. * @type array $links The tabs to include with (label|url|cap) keys. * @type string $selected The ID of the selected link. * } */ function network_edit_site_nav( $args = array() ) { /** * Filters the links that appear on site-editing network pages. * * Default links: 'site-info', 'site-users', 'site-themes', and 'site-settings'. * * @since 4.6.0 * * @param array $links { * An array of link data representing individual network admin pages. * * @type array $link_slug { * An array of information about the individual link to a page. * * $type string $label Label to use for the link. * $type string $url URL, relative to `network_admin_url()` to use for the link. * $type string $cap Capability required to see the link. * } * } */ $links = apply_filters( 'network_edit_site_nav_links', array( 'site-info' => array( 'label' => __( 'Info' ), 'url' => 'site-info.php', 'cap' => 'manage_sites', ), 'site-users' => array( 'label' => __( 'Users' ), 'url' => 'site-users.php', 'cap' => 'manage_sites', ), 'site-themes' => array( 'label' => __( 'Themes' ), 'url' => 'site-themes.php', 'cap' => 'manage_sites', ), 'site-settings' => array( 'label' => __( 'Settings' ), 'url' => 'site-settings.php', 'cap' => 'manage_sites', ), ) ); // Parse arguments. $parsed_args = wp_parse_args( $args, array( 'blog_id' => isset( $_GET['blog_id'] ) ? (int) $_GET['blog_id'] : 0, 'links' => $links, 'selected' => 'site-info', ) ); // Setup the links array. $screen_links = array(); // Loop through tabs. foreach ( $parsed_args['links'] as $link_id => $link ) { // Skip link if user can't access. if ( ! current_user_can( $link['cap'], $parsed_args['blog_id'] ) ) { continue; } // Link classes. $classes = array( 'nav-tab' ); // Aria-current attribute. $aria_current = ''; // Selected is set by the parent OR assumed by the $pagenow global. if ( $parsed_args['selected'] === $link_id || $link['url'] === $GLOBALS['pagenow'] ) { $classes[] = 'nav-tab-active'; $aria_current = ' aria-current="page"'; } // Escape each class. $esc_classes = implode( ' ', $classes ); // Get the URL for this link. $url = add_query_arg( array( 'id' => $parsed_args['blog_id'] ), network_admin_url( $link['url'] ) ); // Add link to nav links. $screen_links[ $link_id ] = '<a href="' . esc_url( $url ) . '" id="' . esc_attr( $link_id ) . '" class="' . $esc_classes . '"' . $aria_current . '>' . esc_html( $link['label'] ) . '</a>'; } // All done! echo '<nav class="nav-tab-wrapper wp-clearfix" aria-label="' . esc_attr__( 'Secondary menu' ) . '">'; echo implode( '', $screen_links ); echo '</nav>'; } /** * Returns the arguments for the help tab on the Edit Site screens. * * @since 4.9.0 * * @return array Help tab arguments. */ function get_site_screen_help_tab_args() { return array( 'id' => 'overview', 'title' => __( 'Overview' ), 'content' => '<p>' . __( 'The menu is for editing information specific to individual sites, particularly if the admin area of a site is unavailable.' ) . '</p>' . '<p>' . __( '<strong>Info</strong> — The site URL is rarely edited as this can cause the site to not work properly. The Registered date and Last Updated date are displayed. Network admins can mark a site as archived, spam, deleted and mature, to remove from public listings or disable.' ) . '</p>' . '<p>' . __( '<strong>Users</strong> — This displays the users associated with this site. You can also change their role, reset their password, or remove them from the site. Removing the user from the site does not remove the user from the network.' ) . '</p>' . '<p>' . sprintf( /* translators: %s: URL to Network Themes screen. */ __( '<strong>Themes</strong> — This area shows themes that are not already enabled across the network. Enabling a theme in this menu makes it accessible to this site. It does not activate the theme, but allows it to show in the site’s Appearance menu. To enable a theme for the entire network, see the <a href="%s">Network Themes</a> screen.' ), network_admin_url( 'themes.php' ) ) . '</p>' . '<p>' . __( '<strong>Settings</strong> — This page shows a list of all settings associated with this site. Some are created by WordPress and others are created by plugins you activate. Note that some fields are grayed out and say Serialized Data. You cannot modify these values due to the way the setting is stored in the database.' ) . '</p>', ); } /** * Returns the content for the help sidebar on the Edit Site screens. * * @since 4.9.0 * * @return string Help sidebar content. */ function get_site_screen_help_sidebar_content() { return '<p><strong>' . __( 'For more information:' ) . '</strong></p>' . '<p>' . __( '<a href="https://wordpress.org/documentation/article/network-admin-sites-screen/">Documentation on Site Management</a>' ) . '</p>' . '<p>' . __( '<a href="https://wordpress.org/support/forum/multisite/">Support forums</a>' ) . '</p>'; } includes/class-wp-ajax-upgrader-skin.php 0000644 00000010141 15135536347 0014317 0 ustar 00 <?php /** * Upgrader API: WP_Ajax_Upgrader_Skin class * * @package WordPress * @subpackage Upgrader * @since 4.6.0 */ /** * Upgrader Skin for Ajax WordPress upgrades. * * This skin is designed to be used for Ajax updates. * * @since 4.6.0 * * @see Automatic_Upgrader_Skin */ class WP_Ajax_Upgrader_Skin extends Automatic_Upgrader_Skin { /** * Plugin info. * * The Plugin_Upgrader::bulk_upgrade() method will fill this in * with info retrieved from the get_plugin_data() function. * * @var array Plugin data. Values will be empty if not supplied by the plugin. */ public $plugin_info = array(); /** * Theme info. * * The Theme_Upgrader::bulk_upgrade() method will fill this in * with info retrieved from the Theme_Upgrader::theme_info() method, * which in turn calls the wp_get_theme() function. * * @var WP_Theme|false The theme's info object, or false. */ public $theme_info = false; /** * Holds the WP_Error object. * * @since 4.6.0 * * @var null|WP_Error */ protected $errors = null; /** * Constructor. * * Sets up the WordPress Ajax upgrader skin. * * @since 4.6.0 * * @see WP_Upgrader_Skin::__construct() * * @param array $args Optional. The WordPress Ajax upgrader skin arguments to * override default options. See WP_Upgrader_Skin::__construct(). * Default empty array. */ public function __construct( $args = array() ) { parent::__construct( $args ); $this->errors = new WP_Error(); } /** * Retrieves the list of errors. * * @since 4.6.0 * * @return WP_Error Errors during an upgrade. */ public function get_errors() { return $this->errors; } /** * Retrieves a string for error messages. * * @since 4.6.0 * * @return string Error messages during an upgrade. */ public function get_error_messages() { $messages = array(); foreach ( $this->errors->get_error_codes() as $error_code ) { $error_data = $this->errors->get_error_data( $error_code ); if ( $error_data && is_string( $error_data ) ) { $messages[] = $this->errors->get_error_message( $error_code ) . ' ' . esc_html( strip_tags( $error_data ) ); } else { $messages[] = $this->errors->get_error_message( $error_code ); } } return implode( ', ', $messages ); } /** * Stores an error message about the upgrade. * * @since 4.6.0 * @since 5.3.0 Formalized the existing `...$args` parameter by adding it * to the function signature. * * @param string|WP_Error $errors Errors. * @param mixed ...$args Optional text replacements. */ public function error( $errors, ...$args ) { if ( is_string( $errors ) ) { $string = $errors; if ( ! empty( $this->upgrader->strings[ $string ] ) ) { $string = $this->upgrader->strings[ $string ]; } if ( str_contains( $string, '%' ) ) { if ( ! empty( $args ) ) { $string = vsprintf( $string, $args ); } } // Count existing errors to generate a unique error code. $errors_count = count( $this->errors->get_error_codes() ); $this->errors->add( 'unknown_upgrade_error_' . ( $errors_count + 1 ), $string ); } elseif ( is_wp_error( $errors ) ) { foreach ( $errors->get_error_codes() as $error_code ) { $this->errors->add( $error_code, $errors->get_error_message( $error_code ), $errors->get_error_data( $error_code ) ); } } parent::error( $errors, ...$args ); } /** * Stores a message about the upgrade. * * @since 4.6.0 * @since 5.3.0 Formalized the existing `...$args` parameter by adding it * to the function signature. * @since 5.9.0 Renamed `$data` to `$feedback` for PHP 8 named parameter support. * * @param string|array|WP_Error $feedback Message data. * @param mixed ...$args Optional text replacements. */ public function feedback( $feedback, ...$args ) { if ( is_wp_error( $feedback ) ) { foreach ( $feedback->get_error_codes() as $error_code ) { $this->errors->add( $error_code, $feedback->get_error_message( $error_code ), $feedback->get_error_data( $error_code ) ); } } parent::feedback( $feedback, ...$args ); } } includes/export.php 0000644 00000061734 15135536347 0010433 0 ustar 00 <?php /** * WordPress Export Administration API * * @package WordPress * @subpackage Administration */ /** * Version number for the export format. * * Bump this when something changes that might affect compatibility. * * @since 2.5.0 */ define( 'WXR_VERSION', '1.2' ); /** * Generates the WXR export file for download. * * Default behavior is to export all content, however, note that post content will only * be exported for post types with the `can_export` argument enabled. Any posts with the * 'auto-draft' status will be skipped. * * @since 2.1.0 * @since 5.7.0 Added the `post_modified` and `post_modified_gmt` fields to the export file. * * @global wpdb $wpdb WordPress database abstraction object. * @global WP_Post $post Global post object. * * @param array $args { * Optional. Arguments for generating the WXR export file for download. Default empty array. * * @type string $content Type of content to export. If set, only the post content of this post type * will be exported. Accepts 'all', 'post', 'page', 'attachment', or a defined * custom post. If an invalid custom post type is supplied, every post type for * which `can_export` is enabled will be exported instead. If a valid custom post * type is supplied but `can_export` is disabled, then 'posts' will be exported * instead. When 'all' is supplied, only post types with `can_export` enabled will * be exported. Default 'all'. * @type string $author Author to export content for. Only used when `$content` is 'post', 'page', or * 'attachment'. Accepts false (all) or a specific author ID. Default false (all). * @type string $category Category (slug) to export content for. Used only when `$content` is 'post'. If * set, only post content assigned to `$category` will be exported. Accepts false * or a specific category slug. Default is false (all categories). * @type string $start_date Start date to export content from. Expected date format is 'Y-m-d'. Used only * when `$content` is 'post', 'page' or 'attachment'. Default false (since the * beginning of time). * @type string $end_date End date to export content to. Expected date format is 'Y-m-d'. Used only when * `$content` is 'post', 'page' or 'attachment'. Default false (latest publish date). * @type string $status Post status to export posts for. Used only when `$content` is 'post' or 'page'. * Accepts false (all statuses except 'auto-draft'), or a specific status, i.e. * 'publish', 'pending', 'draft', 'auto-draft', 'future', 'private', 'inherit', or * 'trash'. Default false (all statuses except 'auto-draft'). * } */ function export_wp( $args = array() ) { global $wpdb, $post; $defaults = array( 'content' => 'all', 'author' => false, 'category' => false, 'start_date' => false, 'end_date' => false, 'status' => false, ); $args = wp_parse_args( $args, $defaults ); /** * Fires at the beginning of an export, before any headers are sent. * * @since 2.3.0 * * @param array $args An array of export arguments. */ do_action( 'export_wp', $args ); $sitename = sanitize_key( get_bloginfo( 'name' ) ); if ( ! empty( $sitename ) ) { $sitename .= '.'; } $date = gmdate( 'Y-m-d' ); $wp_filename = $sitename . 'WordPress.' . $date . '.xml'; /** * Filters the export filename. * * @since 4.4.0 * * @param string $wp_filename The name of the file for download. * @param string $sitename The site name. * @param string $date Today's date, formatted. */ $filename = apply_filters( 'export_wp_filename', $wp_filename, $sitename, $date ); header( 'Content-Description: File Transfer' ); header( 'Content-Disposition: attachment; filename=' . $filename ); header( 'Content-Type: text/xml; charset=' . get_option( 'blog_charset' ), true ); if ( 'all' !== $args['content'] && post_type_exists( $args['content'] ) ) { $ptype = get_post_type_object( $args['content'] ); if ( ! $ptype->can_export ) { $args['content'] = 'post'; } $where = $wpdb->prepare( "{$wpdb->posts}.post_type = %s", $args['content'] ); } else { $post_types = get_post_types( array( 'can_export' => true ) ); $esses = array_fill( 0, count( $post_types ), '%s' ); // phpcs:ignore WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare $where = $wpdb->prepare( "{$wpdb->posts}.post_type IN (" . implode( ',', $esses ) . ')', $post_types ); } if ( $args['status'] && ( 'post' === $args['content'] || 'page' === $args['content'] ) ) { $where .= $wpdb->prepare( " AND {$wpdb->posts}.post_status = %s", $args['status'] ); } else { $where .= " AND {$wpdb->posts}.post_status != 'auto-draft'"; } $join = ''; if ( $args['category'] && 'post' === $args['content'] ) { $term = term_exists( $args['category'], 'category' ); if ( $term ) { $join = "INNER JOIN {$wpdb->term_relationships} ON ({$wpdb->posts}.ID = {$wpdb->term_relationships}.object_id)"; $where .= $wpdb->prepare( " AND {$wpdb->term_relationships}.term_taxonomy_id = %d", $term['term_taxonomy_id'] ); } } if ( in_array( $args['content'], array( 'post', 'page', 'attachment' ), true ) ) { if ( $args['author'] ) { $where .= $wpdb->prepare( " AND {$wpdb->posts}.post_author = %d", $args['author'] ); } if ( $args['start_date'] ) { $where .= $wpdb->prepare( " AND {$wpdb->posts}.post_date >= %s", gmdate( 'Y-m-d', strtotime( $args['start_date'] ) ) ); } if ( $args['end_date'] ) { $where .= $wpdb->prepare( " AND {$wpdb->posts}.post_date < %s", gmdate( 'Y-m-d', strtotime( '+1 month', strtotime( $args['end_date'] ) ) ) ); } } // Grab a snapshot of post IDs, just in case it changes during the export. $post_ids = $wpdb->get_col( "SELECT ID FROM {$wpdb->posts} $join WHERE $where" ); // Get IDs for the attachments of each post, unless all content is already being exported. if ( ! in_array( $args['content'], array( 'all', 'attachment' ), true ) ) { // Array to hold all additional IDs (attachments and thumbnails). $additional_ids = array(); // Create a copy of the post IDs array to avoid modifying the original array. $processing_ids = $post_ids; while ( $next_posts = array_splice( $processing_ids, 0, 20 ) ) { $posts_in = array_map( 'absint', $next_posts ); $placeholders = array_fill( 0, count( $posts_in ), '%d' ); // Create a string for the placeholders. $in_placeholder = implode( ',', $placeholders ); // Prepare the SQL statement for attachment ids. $attachment_ids = $wpdb->get_col( $wpdb->prepare( " SELECT ID FROM $wpdb->posts WHERE post_parent IN ($in_placeholder) AND post_type = 'attachment' ", $posts_in ) ); $thumbnails_ids = $wpdb->get_col( $wpdb->prepare( " SELECT meta_value FROM $wpdb->postmeta WHERE $wpdb->postmeta.post_id IN ($in_placeholder) AND $wpdb->postmeta.meta_key = '_thumbnail_id' ", $posts_in ) ); $additional_ids = array_merge( $additional_ids, $attachment_ids, $thumbnails_ids ); } // Merge the additional IDs back with the original post IDs after processing all posts $post_ids = array_unique( array_merge( $post_ids, $additional_ids ) ); } /* * Get the requested terms ready, empty unless posts filtered by category * or all content. */ $cats = array(); $tags = array(); $terms = array(); if ( isset( $term ) && $term ) { $cat = get_term( $term['term_id'], 'category' ); $cats = array( $cat->term_id => $cat ); unset( $term, $cat ); } elseif ( 'all' === $args['content'] ) { $categories = (array) get_categories( array( 'get' => 'all' ) ); $tags = (array) get_tags( array( 'get' => 'all' ) ); $custom_taxonomies = get_taxonomies( array( '_builtin' => false ) ); $custom_terms = (array) get_terms( array( 'taxonomy' => $custom_taxonomies, 'get' => 'all', ) ); // Put categories in order with no child going before its parent. while ( $cat = array_shift( $categories ) ) { if ( ! $cat->parent || isset( $cats[ $cat->parent ] ) ) { $cats[ $cat->term_id ] = $cat; } else { $categories[] = $cat; } } // Put terms in order with no child going before its parent. while ( $t = array_shift( $custom_terms ) ) { if ( ! $t->parent || isset( $terms[ $t->parent ] ) ) { $terms[ $t->term_id ] = $t; } else { $custom_terms[] = $t; } } unset( $categories, $custom_taxonomies, $custom_terms ); } /** * Wraps given string in XML CDATA tag. * * @since 2.1.0 * * @param string $str String to wrap in XML CDATA tag. * @return string */ function wxr_cdata( $str ) { if ( ! seems_utf8( $str ) ) { $str = utf8_encode( $str ); } // $str = ent2ncr(esc_html($str)); $str = '<![CDATA[' . str_replace( ']]>', ']]]]><![CDATA[>', $str ) . ']]>'; return $str; } /** * Returns the URL of the site. * * @since 2.5.0 * * @return string Site URL. */ function wxr_site_url() { if ( is_multisite() ) { // Multisite: the base URL. return network_home_url(); } else { // WordPress (single site): the site URL. return get_bloginfo_rss( 'url' ); } } /** * Outputs a cat_name XML tag from a given category object. * * @since 2.1.0 * * @param WP_Term $category Category Object. */ function wxr_cat_name( $category ) { if ( empty( $category->name ) ) { return; } echo '<wp:cat_name>' . wxr_cdata( $category->name ) . "</wp:cat_name>\n"; } /** * Outputs a category_description XML tag from a given category object. * * @since 2.1.0 * * @param WP_Term $category Category Object. */ function wxr_category_description( $category ) { if ( empty( $category->description ) ) { return; } echo '<wp:category_description>' . wxr_cdata( $category->description ) . "</wp:category_description>\n"; } /** * Outputs a tag_name XML tag from a given tag object. * * @since 2.3.0 * * @param WP_Term $tag Tag Object. */ function wxr_tag_name( $tag ) { if ( empty( $tag->name ) ) { return; } echo '<wp:tag_name>' . wxr_cdata( $tag->name ) . "</wp:tag_name>\n"; } /** * Outputs a tag_description XML tag from a given tag object. * * @since 2.3.0 * * @param WP_Term $tag Tag Object. */ function wxr_tag_description( $tag ) { if ( empty( $tag->description ) ) { return; } echo '<wp:tag_description>' . wxr_cdata( $tag->description ) . "</wp:tag_description>\n"; } /** * Outputs a term_name XML tag from a given term object. * * @since 2.9.0 * * @param WP_Term $term Term Object. */ function wxr_term_name( $term ) { if ( empty( $term->name ) ) { return; } echo '<wp:term_name>' . wxr_cdata( $term->name ) . "</wp:term_name>\n"; } /** * Outputs a term_description XML tag from a given term object. * * @since 2.9.0 * * @param WP_Term $term Term Object. */ function wxr_term_description( $term ) { if ( empty( $term->description ) ) { return; } echo "\t\t<wp:term_description>" . wxr_cdata( $term->description ) . "</wp:term_description>\n"; } /** * Outputs term meta XML tags for a given term object. * * @since 4.6.0 * * @global wpdb $wpdb WordPress database abstraction object. * * @param WP_Term $term Term object. */ function wxr_term_meta( $term ) { global $wpdb; $termmeta = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM $wpdb->termmeta WHERE term_id = %d", $term->term_id ) ); foreach ( $termmeta as $meta ) { /** * Filters whether to selectively skip term meta used for WXR exports. * * Returning a truthy value from the filter will skip the current meta * object from being exported. * * @since 4.6.0 * * @param bool $skip Whether to skip the current piece of term meta. Default false. * @param string $meta_key Current meta key. * @param object $meta Current meta object. */ if ( ! apply_filters( 'wxr_export_skip_termmeta', false, $meta->meta_key, $meta ) ) { printf( "\t\t<wp:termmeta>\n\t\t\t<wp:meta_key>%s</wp:meta_key>\n\t\t\t<wp:meta_value>%s</wp:meta_value>\n\t\t</wp:termmeta>\n", wxr_cdata( $meta->meta_key ), wxr_cdata( $meta->meta_value ) ); } } } /** * Outputs list of authors with posts. * * @since 3.1.0 * * @global wpdb $wpdb WordPress database abstraction object. * * @param int[] $post_ids Optional. Array of post IDs to filter the query by. */ function wxr_authors_list( array $post_ids = null ) { global $wpdb; if ( ! empty( $post_ids ) ) { $post_ids = array_map( 'absint', $post_ids ); $and = 'AND ID IN ( ' . implode( ', ', $post_ids ) . ')'; } else { $and = ''; } $authors = array(); $results = $wpdb->get_results( "SELECT DISTINCT post_author FROM $wpdb->posts WHERE post_status != 'auto-draft' $and" ); foreach ( (array) $results as $result ) { $authors[] = get_userdata( $result->post_author ); } $authors = array_filter( $authors ); foreach ( $authors as $author ) { echo "\t<wp:author>"; echo '<wp:author_id>' . (int) $author->ID . '</wp:author_id>'; echo '<wp:author_login>' . wxr_cdata( $author->user_login ) . '</wp:author_login>'; echo '<wp:author_email>' . wxr_cdata( $author->user_email ) . '</wp:author_email>'; echo '<wp:author_display_name>' . wxr_cdata( $author->display_name ) . '</wp:author_display_name>'; echo '<wp:author_first_name>' . wxr_cdata( $author->first_name ) . '</wp:author_first_name>'; echo '<wp:author_last_name>' . wxr_cdata( $author->last_name ) . '</wp:author_last_name>'; echo "</wp:author>\n"; } } /** * Outputs all navigation menu terms. * * @since 3.1.0 */ function wxr_nav_menu_terms() { $nav_menus = wp_get_nav_menus(); if ( empty( $nav_menus ) || ! is_array( $nav_menus ) ) { return; } foreach ( $nav_menus as $menu ) { echo "\t<wp:term>"; echo '<wp:term_id>' . (int) $menu->term_id . '</wp:term_id>'; echo '<wp:term_taxonomy>nav_menu</wp:term_taxonomy>'; echo '<wp:term_slug>' . wxr_cdata( $menu->slug ) . '</wp:term_slug>'; wxr_term_name( $menu ); echo "</wp:term>\n"; } } /** * Outputs list of taxonomy terms, in XML tag format, associated with a post. * * @since 2.3.0 */ function wxr_post_taxonomy() { $post = get_post(); $taxonomies = get_object_taxonomies( $post->post_type ); if ( empty( $taxonomies ) ) { return; } $terms = wp_get_object_terms( $post->ID, $taxonomies ); foreach ( (array) $terms as $term ) { echo "\t\t<category domain=\"{$term->taxonomy}\" nicename=\"{$term->slug}\">" . wxr_cdata( $term->name ) . "</category>\n"; } } /** * Determines whether to selectively skip post meta used for WXR exports. * * @since 3.3.0 * * @param bool $return_me Whether to skip the current post meta. Default false. * @param string $meta_key Meta key. * @return bool */ function wxr_filter_postmeta( $return_me, $meta_key ) { if ( '_edit_lock' === $meta_key ) { $return_me = true; } return $return_me; } add_filter( 'wxr_export_skip_postmeta', 'wxr_filter_postmeta', 10, 2 ); echo '<?xml version="1.0" encoding="' . get_bloginfo( 'charset' ) . "\" ?>\n"; ?> <!-- This is a WordPress eXtended RSS file generated by WordPress as an export of your site. --> <!-- It contains information about your site's posts, pages, comments, categories, and other content. --> <!-- You may use this file to transfer that content from one site to another. --> <!-- This file is not intended to serve as a complete backup of your site. --> <!-- To import this information into a WordPress site follow these steps: --> <!-- 1. Log in to that site as an administrator. --> <!-- 2. Go to Tools: Import in the WordPress admin panel. --> <!-- 3. Install the "WordPress" importer from the list. --> <!-- 4. Activate & Run Importer. --> <!-- 5. Upload this file using the form provided on that page. --> <!-- 6. You will first be asked to map the authors in this export file to users --> <!-- on the site. For each author, you may choose to map to an --> <!-- existing user on the site or to create a new user. --> <!-- 7. WordPress will then import each of the posts, pages, comments, categories, etc. --> <!-- contained in this file into your site. --> <?php the_generator( 'export' ); ?> <rss version="2.0" xmlns:excerpt="http://wordpress.org/export/<?php echo WXR_VERSION; ?>/excerpt/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:wp="http://wordpress.org/export/<?php echo WXR_VERSION; ?>/" > <channel> <title><?php bloginfo_rss( 'name' ); ?></title> <link><?php bloginfo_rss( 'url' ); ?></link> <description><?php bloginfo_rss( 'description' ); ?></description> <pubDate><?php echo gmdate( 'D, d M Y H:i:s +0000' ); ?></pubDate> <language><?php bloginfo_rss( 'language' ); ?></language> <wp:wxr_version><?php echo WXR_VERSION; ?></wp:wxr_version> <wp:base_site_url><?php echo wxr_site_url(); ?></wp:base_site_url> <wp:base_blog_url><?php bloginfo_rss( 'url' ); ?></wp:base_blog_url> <?php wxr_authors_list( $post_ids ); ?> <?php foreach ( $cats as $c ) : ?> <wp:category> <wp:term_id><?php echo (int) $c->term_id; ?></wp:term_id> <wp:category_nicename><?php echo wxr_cdata( $c->slug ); ?></wp:category_nicename> <wp:category_parent><?php echo wxr_cdata( $c->parent ? $cats[ $c->parent ]->slug : '' ); ?></wp:category_parent> <?php wxr_cat_name( $c ); wxr_category_description( $c ); wxr_term_meta( $c ); ?> </wp:category> <?php endforeach; ?> <?php foreach ( $tags as $t ) : ?> <wp:tag> <wp:term_id><?php echo (int) $t->term_id; ?></wp:term_id> <wp:tag_slug><?php echo wxr_cdata( $t->slug ); ?></wp:tag_slug> <?php wxr_tag_name( $t ); wxr_tag_description( $t ); wxr_term_meta( $t ); ?> </wp:tag> <?php endforeach; ?> <?php foreach ( $terms as $t ) : ?> <wp:term> <wp:term_id><?php echo (int) $t->term_id; ?></wp:term_id> <wp:term_taxonomy><?php echo wxr_cdata( $t->taxonomy ); ?></wp:term_taxonomy> <wp:term_slug><?php echo wxr_cdata( $t->slug ); ?></wp:term_slug> <wp:term_parent><?php echo wxr_cdata( $t->parent ? $terms[ $t->parent ]->slug : '' ); ?></wp:term_parent> <?php wxr_term_name( $t ); wxr_term_description( $t ); wxr_term_meta( $t ); ?> </wp:term> <?php endforeach; ?> <?php if ( 'all' === $args['content'] ) { wxr_nav_menu_terms(); } ?> <?php /** This action is documented in wp-includes/feed-rss2.php */ do_action( 'rss2_head' ); ?> <?php if ( $post_ids ) { /** * @global WP_Query $wp_query WordPress Query object. */ global $wp_query; // Fake being in the loop. $wp_query->in_the_loop = true; // Fetch 20 posts at a time rather than loading the entire table into memory. while ( $next_posts = array_splice( $post_ids, 0, 20 ) ) { $where = 'WHERE ID IN (' . implode( ',', $next_posts ) . ')'; $posts = $wpdb->get_results( "SELECT * FROM {$wpdb->posts} $where" ); // Begin Loop. foreach ( $posts as $post ) { setup_postdata( $post ); /** * Filters the post title used for WXR exports. * * @since 5.7.0 * * @param string $post_title Title of the current post. */ $title = wxr_cdata( apply_filters( 'the_title_export', $post->post_title ) ); /** * Filters the post content used for WXR exports. * * @since 2.5.0 * * @param string $post_content Content of the current post. */ $content = wxr_cdata( apply_filters( 'the_content_export', $post->post_content ) ); /** * Filters the post excerpt used for WXR exports. * * @since 2.6.0 * * @param string $post_excerpt Excerpt for the current post. */ $excerpt = wxr_cdata( apply_filters( 'the_excerpt_export', $post->post_excerpt ) ); $is_sticky = is_sticky( $post->ID ) ? 1 : 0; ?> <item> <title><?php echo $title; ?></title> <link><?php the_permalink_rss(); ?></link> <pubDate><?php echo mysql2date( 'D, d M Y H:i:s +0000', get_post_time( 'Y-m-d H:i:s', true ), false ); ?></pubDate> <dc:creator><?php echo wxr_cdata( get_the_author_meta( 'login' ) ); ?></dc:creator> <guid isPermaLink="false"><?php the_guid(); ?></guid> <description></description> <content:encoded><?php echo $content; ?></content:encoded> <excerpt:encoded><?php echo $excerpt; ?></excerpt:encoded> <wp:post_id><?php echo (int) $post->ID; ?></wp:post_id> <wp:post_date><?php echo wxr_cdata( $post->post_date ); ?></wp:post_date> <wp:post_date_gmt><?php echo wxr_cdata( $post->post_date_gmt ); ?></wp:post_date_gmt> <wp:post_modified><?php echo wxr_cdata( $post->post_modified ); ?></wp:post_modified> <wp:post_modified_gmt><?php echo wxr_cdata( $post->post_modified_gmt ); ?></wp:post_modified_gmt> <wp:comment_status><?php echo wxr_cdata( $post->comment_status ); ?></wp:comment_status> <wp:ping_status><?php echo wxr_cdata( $post->ping_status ); ?></wp:ping_status> <wp:post_name><?php echo wxr_cdata( $post->post_name ); ?></wp:post_name> <wp:status><?php echo wxr_cdata( $post->post_status ); ?></wp:status> <wp:post_parent><?php echo (int) $post->post_parent; ?></wp:post_parent> <wp:menu_order><?php echo (int) $post->menu_order; ?></wp:menu_order> <wp:post_type><?php echo wxr_cdata( $post->post_type ); ?></wp:post_type> <wp:post_password><?php echo wxr_cdata( $post->post_password ); ?></wp:post_password> <wp:is_sticky><?php echo (int) $is_sticky; ?></wp:is_sticky> <?php if ( 'attachment' === $post->post_type ) : ?> <wp:attachment_url><?php echo wxr_cdata( wp_get_attachment_url( $post->ID ) ); ?></wp:attachment_url> <?php endif; ?> <?php wxr_post_taxonomy(); ?> <?php $postmeta = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM $wpdb->postmeta WHERE post_id = %d", $post->ID ) ); foreach ( $postmeta as $meta ) : /** * Filters whether to selectively skip post meta used for WXR exports. * * Returning a truthy value from the filter will skip the current meta * object from being exported. * * @since 3.3.0 * * @param bool $skip Whether to skip the current post meta. Default false. * @param string $meta_key Current meta key. * @param object $meta Current meta object. */ if ( apply_filters( 'wxr_export_skip_postmeta', false, $meta->meta_key, $meta ) ) { continue; } ?> <wp:postmeta> <wp:meta_key><?php echo wxr_cdata( $meta->meta_key ); ?></wp:meta_key> <wp:meta_value><?php echo wxr_cdata( $meta->meta_value ); ?></wp:meta_value> </wp:postmeta> <?php endforeach; $_comments = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM $wpdb->comments WHERE comment_post_ID = %d AND comment_approved <> 'spam'", $post->ID ) ); $comments = array_map( 'get_comment', $_comments ); foreach ( $comments as $c ) : ?> <wp:comment> <wp:comment_id><?php echo (int) $c->comment_ID; ?></wp:comment_id> <wp:comment_author><?php echo wxr_cdata( $c->comment_author ); ?></wp:comment_author> <wp:comment_author_email><?php echo wxr_cdata( $c->comment_author_email ); ?></wp:comment_author_email> <wp:comment_author_url><?php echo sanitize_url( $c->comment_author_url ); ?></wp:comment_author_url> <wp:comment_author_IP><?php echo wxr_cdata( $c->comment_author_IP ); ?></wp:comment_author_IP> <wp:comment_date><?php echo wxr_cdata( $c->comment_date ); ?></wp:comment_date> <wp:comment_date_gmt><?php echo wxr_cdata( $c->comment_date_gmt ); ?></wp:comment_date_gmt> <wp:comment_content><?php echo wxr_cdata( $c->comment_content ); ?></wp:comment_content> <wp:comment_approved><?php echo wxr_cdata( $c->comment_approved ); ?></wp:comment_approved> <wp:comment_type><?php echo wxr_cdata( $c->comment_type ); ?></wp:comment_type> <wp:comment_parent><?php echo (int) $c->comment_parent; ?></wp:comment_parent> <wp:comment_user_id><?php echo (int) $c->user_id; ?></wp:comment_user_id> <?php $c_meta = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM $wpdb->commentmeta WHERE comment_id = %d", $c->comment_ID ) ); foreach ( $c_meta as $meta ) : /** * Filters whether to selectively skip comment meta used for WXR exports. * * Returning a truthy value from the filter will skip the current meta * object from being exported. * * @since 4.0.0 * * @param bool $skip Whether to skip the current comment meta. Default false. * @param string $meta_key Current meta key. * @param object $meta Current meta object. */ if ( apply_filters( 'wxr_export_skip_commentmeta', false, $meta->meta_key, $meta ) ) { continue; } ?> <wp:commentmeta> <wp:meta_key><?php echo wxr_cdata( $meta->meta_key ); ?></wp:meta_key> <wp:meta_value><?php echo wxr_cdata( $meta->meta_value ); ?></wp:meta_value> </wp:commentmeta> <?php endforeach; ?> </wp:comment> <?php endforeach; ?> </item> <?php } } } ?> </channel> </rss> <?php } includes/.includes.php 0000644 00000000000 15135536347 0010751 0 ustar 00 includes/upgrade.php 0000644 00000333541 15135536347 0010537 0 ustar 00 <?php /** * WordPress Upgrade API * * Most of the functions are pluggable and can be overwritten. * * @package WordPress * @subpackage Administration */ /** Include user installation customization script. */ if ( file_exists( WP_CONTENT_DIR . '/install.php' ) ) { require WP_CONTENT_DIR . '/install.php'; } /** WordPress Administration API */ require_once ABSPATH . 'wp-admin/includes/admin.php'; /** WordPress Schema API */ require_once ABSPATH . 'wp-admin/includes/schema.php'; if ( ! function_exists( 'wp_install' ) ) : /** * Installs the site. * * Runs the required functions to set up and populate the database, * including primary admin user and initial options. * * @since 2.1.0 * * @param string $blog_title Site title. * @param string $user_name User's username. * @param string $user_email User's email. * @param bool $is_public Whether the site is public. * @param string $deprecated Optional. Not used. * @param string $user_password Optional. User's chosen password. Default empty (random password). * @param string $language Optional. Language chosen. Default empty. * @return array { * Data for the newly installed site. * * @type string $url The URL of the site. * @type int $user_id The ID of the site owner. * @type string $password The password of the site owner, if their user account didn't already exist. * @type string $password_message The explanatory message regarding the password. * } */ function wp_install( $blog_title, $user_name, $user_email, $is_public, $deprecated = '', $user_password = '', $language = '' ) { if ( ! empty( $deprecated ) ) { _deprecated_argument( __FUNCTION__, '2.6.0' ); } wp_check_mysql_version(); wp_cache_flush(); make_db_current_silent(); populate_options(); populate_roles(); update_option( 'blogname', $blog_title ); update_option( 'admin_email', $user_email ); update_option( 'blog_public', $is_public ); // Freshness of site - in the future, this could get more specific about actions taken, perhaps. update_option( 'fresh_site', 1 ); if ( $language ) { update_option( 'WPLANG', $language ); } $guessurl = wp_guess_url(); update_option( 'siteurl', $guessurl ); // If not a public site, don't ping. if ( ! $is_public ) { update_option( 'default_pingback_flag', 0 ); } /* * Create default user. If the user already exists, the user tables are * being shared among sites. Just set the role in that case. */ $user_id = username_exists( $user_name ); $user_password = trim( $user_password ); $email_password = false; $user_created = false; if ( ! $user_id && empty( $user_password ) ) { $user_password = wp_generate_password( 12, false ); $message = __( '<strong><em>Note that password</em></strong> carefully! It is a <em>random</em> password that was generated just for you.' ); $user_id = wp_create_user( $user_name, $user_password, $user_email ); update_user_meta( $user_id, 'default_password_nag', true ); $email_password = true; $user_created = true; } elseif ( ! $user_id ) { // Password has been provided. $message = '<em>' . __( 'Your chosen password.' ) . '</em>'; $user_id = wp_create_user( $user_name, $user_password, $user_email ); $user_created = true; } else { $message = __( 'User already exists. Password inherited.' ); } $user = new WP_User( $user_id ); $user->set_role( 'administrator' ); if ( $user_created ) { $user->user_url = $guessurl; wp_update_user( $user ); } wp_install_defaults( $user_id ); wp_install_maybe_enable_pretty_permalinks(); flush_rewrite_rules(); wp_new_blog_notification( $blog_title, $guessurl, $user_id, ( $email_password ? $user_password : __( 'The password you chose during installation.' ) ) ); wp_cache_flush(); /** * Fires after a site is fully installed. * * @since 3.9.0 * * @param WP_User $user The site owner. */ do_action( 'wp_install', $user ); return array( 'url' => $guessurl, 'user_id' => $user_id, 'password' => $user_password, 'password_message' => $message, ); } endif; if ( ! function_exists( 'wp_install_defaults' ) ) : /** * Creates the initial content for a newly-installed site. * * Adds the default "Uncategorized" category, the first post (with comment), * first page, and default widgets for default theme for the current version. * * @since 2.1.0 * * @global wpdb $wpdb WordPress database abstraction object. * @global WP_Rewrite $wp_rewrite WordPress rewrite component. * @global string $table_prefix * * @param int $user_id User ID. */ function wp_install_defaults( $user_id ) { global $wpdb, $wp_rewrite, $table_prefix; // Default category. $cat_name = __( 'Uncategorized' ); /* translators: Default category slug. */ $cat_slug = sanitize_title( _x( 'Uncategorized', 'Default category slug' ) ); $cat_id = 1; $wpdb->insert( $wpdb->terms, array( 'term_id' => $cat_id, 'name' => $cat_name, 'slug' => $cat_slug, 'term_group' => 0, ) ); $wpdb->insert( $wpdb->term_taxonomy, array( 'term_id' => $cat_id, 'taxonomy' => 'category', 'description' => '', 'parent' => 0, 'count' => 1, ) ); $cat_tt_id = $wpdb->insert_id; // First post. $now = current_time( 'mysql' ); $now_gmt = current_time( 'mysql', 1 ); $first_post_guid = get_option( 'home' ) . '/?p=1'; if ( is_multisite() ) { $first_post = get_site_option( 'first_post' ); if ( ! $first_post ) { $first_post = "<!-- wp:paragraph -->\n<p>" . /* translators: First post content. %s: Site link. */ __( 'Welcome to %s. This is your first post. Edit or delete it, then start writing!' ) . "</p>\n<!-- /wp:paragraph -->"; } $first_post = sprintf( $first_post, sprintf( '<a href="%s">%s</a>', esc_url( network_home_url() ), get_network()->site_name ) ); // Back-compat for pre-4.4. $first_post = str_replace( 'SITE_URL', esc_url( network_home_url() ), $first_post ); $first_post = str_replace( 'SITE_NAME', get_network()->site_name, $first_post ); } else { $first_post = "<!-- wp:paragraph -->\n<p>" . /* translators: First post content. %s: Site link. */ __( 'Welcome to WordPress. This is your first post. Edit or delete it, then start writing!' ) . "</p>\n<!-- /wp:paragraph -->"; } $wpdb->insert( $wpdb->posts, array( 'post_author' => $user_id, 'post_date' => $now, 'post_date_gmt' => $now_gmt, 'post_content' => $first_post, 'post_excerpt' => '', 'post_title' => __( 'Hello world!' ), /* translators: Default post slug. */ 'post_name' => sanitize_title( _x( 'hello-world', 'Default post slug' ) ), 'post_modified' => $now, 'post_modified_gmt' => $now_gmt, 'guid' => $first_post_guid, 'comment_count' => 1, 'to_ping' => '', 'pinged' => '', 'post_content_filtered' => '', ) ); if ( is_multisite() ) { update_posts_count(); } $wpdb->insert( $wpdb->term_relationships, array( 'term_taxonomy_id' => $cat_tt_id, 'object_id' => 1, ) ); // Default comment. if ( is_multisite() ) { $first_comment_author = get_site_option( 'first_comment_author' ); $first_comment_email = get_site_option( 'first_comment_email' ); $first_comment_url = get_site_option( 'first_comment_url', network_home_url() ); $first_comment = get_site_option( 'first_comment' ); } $first_comment_author = ! empty( $first_comment_author ) ? $first_comment_author : __( 'A WordPress Commenter' ); $first_comment_email = ! empty( $first_comment_email ) ? $first_comment_email : 'wapuu@wordpress.example'; $first_comment_url = ! empty( $first_comment_url ) ? $first_comment_url : esc_url( __( 'https://wordpress.org/' ) ); $first_comment = ! empty( $first_comment ) ? $first_comment : sprintf( /* translators: %s: Gravatar URL. */ __( 'Hi, this is a comment. To get started with moderating, editing, and deleting comments, please visit the Comments screen in the dashboard. Commenter avatars come from <a href="%s">Gravatar</a>.' ), esc_url( __( 'https://en.gravatar.com/' ) ) ); $wpdb->insert( $wpdb->comments, array( 'comment_post_ID' => 1, 'comment_author' => $first_comment_author, 'comment_author_email' => $first_comment_email, 'comment_author_url' => $first_comment_url, 'comment_date' => $now, 'comment_date_gmt' => $now_gmt, 'comment_content' => $first_comment, 'comment_type' => 'comment', ) ); // First page. if ( is_multisite() ) { $first_page = get_site_option( 'first_page' ); } if ( empty( $first_page ) ) { $first_page = "<!-- wp:paragraph -->\n<p>"; /* translators: First page content. */ $first_page .= __( "This is an example page. It's different from a blog post because it will stay in one place and will show up in your site navigation (in most themes). Most people start with an About page that introduces them to potential site visitors. It might say something like this:" ); $first_page .= "</p>\n<!-- /wp:paragraph -->\n\n"; $first_page .= "<!-- wp:quote -->\n<blockquote class=\"wp-block-quote\"><p>"; /* translators: First page content. */ $first_page .= __( "Hi there! I'm a bike messenger by day, aspiring actor by night, and this is my website. I live in Los Angeles, have a great dog named Jack, and I like piña coladas. (And gettin' caught in the rain.)" ); $first_page .= "</p></blockquote>\n<!-- /wp:quote -->\n\n"; $first_page .= "<!-- wp:paragraph -->\n<p>"; /* translators: First page content. */ $first_page .= __( '...or something like this:' ); $first_page .= "</p>\n<!-- /wp:paragraph -->\n\n"; $first_page .= "<!-- wp:quote -->\n<blockquote class=\"wp-block-quote\"><p>"; /* translators: First page content. */ $first_page .= __( 'The XYZ Doohickey Company was founded in 1971, and has been providing quality doohickeys to the public ever since. Located in Gotham City, XYZ employs over 2,000 people and does all kinds of awesome things for the Gotham community.' ); $first_page .= "</p></blockquote>\n<!-- /wp:quote -->\n\n"; $first_page .= "<!-- wp:paragraph -->\n<p>"; $first_page .= sprintf( /* translators: First page content. %s: Site admin URL. */ __( 'As a new WordPress user, you should go to <a href="%s">your dashboard</a> to delete this page and create new pages for your content. Have fun!' ), admin_url() ); $first_page .= "</p>\n<!-- /wp:paragraph -->"; } $first_post_guid = get_option( 'home' ) . '/?page_id=2'; $wpdb->insert( $wpdb->posts, array( 'post_author' => $user_id, 'post_date' => $now, 'post_date_gmt' => $now_gmt, 'post_content' => $first_page, 'post_excerpt' => '', 'comment_status' => 'closed', 'post_title' => __( 'Sample Page' ), /* translators: Default page slug. */ 'post_name' => __( 'sample-page' ), 'post_modified' => $now, 'post_modified_gmt' => $now_gmt, 'guid' => $first_post_guid, 'post_type' => 'page', 'to_ping' => '', 'pinged' => '', 'post_content_filtered' => '', ) ); $wpdb->insert( $wpdb->postmeta, array( 'post_id' => 2, 'meta_key' => '_wp_page_template', 'meta_value' => 'default', ) ); // Privacy Policy page. if ( is_multisite() ) { // Disable by default unless the suggested content is provided. $privacy_policy_content = get_site_option( 'default_privacy_policy_content' ); } else { if ( ! class_exists( 'WP_Privacy_Policy_Content' ) ) { require_once ABSPATH . 'wp-admin/includes/class-wp-privacy-policy-content.php'; } $privacy_policy_content = WP_Privacy_Policy_Content::get_default_content(); } if ( ! empty( $privacy_policy_content ) ) { $privacy_policy_guid = get_option( 'home' ) . '/?page_id=3'; $wpdb->insert( $wpdb->posts, array( 'post_author' => $user_id, 'post_date' => $now, 'post_date_gmt' => $now_gmt, 'post_content' => $privacy_policy_content, 'post_excerpt' => '', 'comment_status' => 'closed', 'post_title' => __( 'Privacy Policy' ), /* translators: Privacy Policy page slug. */ 'post_name' => __( 'privacy-policy' ), 'post_modified' => $now, 'post_modified_gmt' => $now_gmt, 'guid' => $privacy_policy_guid, 'post_type' => 'page', 'post_status' => 'draft', 'to_ping' => '', 'pinged' => '', 'post_content_filtered' => '', ) ); $wpdb->insert( $wpdb->postmeta, array( 'post_id' => 3, 'meta_key' => '_wp_page_template', 'meta_value' => 'default', ) ); update_option( 'wp_page_for_privacy_policy', 3 ); } // Set up default widgets for default theme. update_option( 'widget_block', array( 2 => array( 'content' => '<!-- wp:search /-->' ), 3 => array( 'content' => '<!-- wp:group --><div class="wp-block-group"><!-- wp:heading --><h2>' . __( 'Recent Posts' ) . '</h2><!-- /wp:heading --><!-- wp:latest-posts /--></div><!-- /wp:group -->' ), 4 => array( 'content' => '<!-- wp:group --><div class="wp-block-group"><!-- wp:heading --><h2>' . __( 'Recent Comments' ) . '</h2><!-- /wp:heading --><!-- wp:latest-comments {"displayAvatar":false,"displayDate":false,"displayExcerpt":false} /--></div><!-- /wp:group -->' ), 5 => array( 'content' => '<!-- wp:group --><div class="wp-block-group"><!-- wp:heading --><h2>' . __( 'Archives' ) . '</h2><!-- /wp:heading --><!-- wp:archives /--></div><!-- /wp:group -->' ), 6 => array( 'content' => '<!-- wp:group --><div class="wp-block-group"><!-- wp:heading --><h2>' . __( 'Categories' ) . '</h2><!-- /wp:heading --><!-- wp:categories /--></div><!-- /wp:group -->' ), '_multiwidget' => 1, ) ); update_option( 'sidebars_widgets', array( 'wp_inactive_widgets' => array(), 'sidebar-1' => array( 0 => 'block-2', 1 => 'block-3', 2 => 'block-4', ), 'sidebar-2' => array( 0 => 'block-5', 1 => 'block-6', ), 'array_version' => 3, ) ); if ( ! is_multisite() ) { update_user_meta( $user_id, 'show_welcome_panel', 1 ); } elseif ( ! is_super_admin( $user_id ) && ! metadata_exists( 'user', $user_id, 'show_welcome_panel' ) ) { update_user_meta( $user_id, 'show_welcome_panel', 2 ); } if ( is_multisite() ) { // Flush rules to pick up the new page. $wp_rewrite->init(); $wp_rewrite->flush_rules(); $user = new WP_User( $user_id ); $wpdb->update( $wpdb->options, array( 'option_value' => $user->user_email ), array( 'option_name' => 'admin_email' ) ); // Remove all perms except for the login user. $wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->usermeta WHERE user_id != %d AND meta_key = %s", $user_id, $table_prefix . 'user_level' ) ); $wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->usermeta WHERE user_id != %d AND meta_key = %s", $user_id, $table_prefix . 'capabilities' ) ); /* * Delete any caps that snuck into the previously active blog. (Hardcoded to blog 1 for now.) * TODO: Get previous_blog_id. */ if ( ! is_super_admin( $user_id ) && 1 != $user_id ) { $wpdb->delete( $wpdb->usermeta, array( 'user_id' => $user_id, 'meta_key' => $wpdb->base_prefix . '1_capabilities', ) ); } } } endif; /** * Maybe enable pretty permalinks on installation. * * If after enabling pretty permalinks don't work, fallback to query-string permalinks. * * @since 4.2.0 * * @global WP_Rewrite $wp_rewrite WordPress rewrite component. * * @return bool Whether pretty permalinks are enabled. False otherwise. */ function wp_install_maybe_enable_pretty_permalinks() { global $wp_rewrite; // Bail if a permalink structure is already enabled. if ( get_option( 'permalink_structure' ) ) { return true; } /* * The Permalink structures to attempt. * * The first is designed for mod_rewrite or nginx rewriting. * * The second is PATHINFO-based permalinks for web server configurations * without a true rewrite module enabled. */ $permalink_structures = array( '/%year%/%monthnum%/%day%/%postname%/', '/index.php/%year%/%monthnum%/%day%/%postname%/', ); foreach ( (array) $permalink_structures as $permalink_structure ) { $wp_rewrite->set_permalink_structure( $permalink_structure ); /* * Flush rules with the hard option to force refresh of the web-server's * rewrite config file (e.g. .htaccess or web.config). */ $wp_rewrite->flush_rules( true ); $test_url = ''; // Test against a real WordPress post. $first_post = get_page_by_path( sanitize_title( _x( 'hello-world', 'Default post slug' ) ), OBJECT, 'post' ); if ( $first_post ) { $test_url = get_permalink( $first_post->ID ); } /* * Send a request to the site, and check whether * the 'X-Pingback' header is returned as expected. * * Uses wp_remote_get() instead of wp_remote_head() because web servers * can block head requests. */ $response = wp_remote_get( $test_url, array( 'timeout' => 5 ) ); $x_pingback_header = wp_remote_retrieve_header( $response, 'X-Pingback' ); $pretty_permalinks = $x_pingback_header && get_bloginfo( 'pingback_url' ) === $x_pingback_header; if ( $pretty_permalinks ) { return true; } } /* * If it makes it this far, pretty permalinks failed. * Fallback to query-string permalinks. */ $wp_rewrite->set_permalink_structure( '' ); $wp_rewrite->flush_rules( true ); return false; } if ( ! function_exists( 'wp_new_blog_notification' ) ) : /** * Notifies the site admin that the installation of WordPress is complete. * * Sends an email to the new administrator that the installation is complete * and provides them with a record of their login credentials. * * @since 2.1.0 * * @param string $blog_title Site title. * @param string $blog_url Site URL. * @param int $user_id Administrator's user ID. * @param string $password Administrator's password. Note that a placeholder message is * usually passed instead of the actual password. */ function wp_new_blog_notification( $blog_title, $blog_url, $user_id, $password ) { $user = new WP_User( $user_id ); $email = $user->user_email; $name = $user->user_login; $login_url = wp_login_url(); $message = sprintf( /* translators: New site notification email. 1: New site URL, 2: User login, 3: User password or password reset link, 4: Login URL. */ __( 'Your new WordPress site has been successfully set up at: %1$s You can log in to the administrator account with the following information: Username: %2$s Password: %3$s Log in here: %4$s We hope you enjoy your new site. Thanks! --The WordPress Team https://wordpress.org/ ' ), $blog_url, $name, $password, $login_url ); $installed_email = array( 'to' => $email, 'subject' => __( 'New WordPress Site' ), 'message' => $message, 'headers' => '', ); /** * Filters the contents of the email sent to the site administrator when WordPress is installed. * * @since 5.6.0 * * @param array $installed_email { * Used to build wp_mail(). * * @type string $to The email address of the recipient. * @type string $subject The subject of the email. * @type string $message The content of the email. * @type string $headers Headers. * } * @param WP_User $user The site administrator user object. * @param string $blog_title The site title. * @param string $blog_url The site URL. * @param string $password The site administrator's password. Note that a placeholder message * is usually passed instead of the user's actual password. */ $installed_email = apply_filters( 'wp_installed_email', $installed_email, $user, $blog_title, $blog_url, $password ); wp_mail( $installed_email['to'], $installed_email['subject'], $installed_email['message'], $installed_email['headers'] ); } endif; if ( ! function_exists( 'wp_upgrade' ) ) : /** * Runs WordPress Upgrade functions. * * Upgrades the database if needed during a site update. * * @since 2.1.0 * * @global int $wp_current_db_version The old (current) database version. * @global int $wp_db_version The new database version. */ function wp_upgrade() { global $wp_current_db_version, $wp_db_version; $wp_current_db_version = __get_option( 'db_version' ); // We are up to date. Nothing to do. if ( $wp_db_version == $wp_current_db_version ) { return; } if ( ! is_blog_installed() ) { return; } wp_check_mysql_version(); wp_cache_flush(); pre_schema_upgrade(); make_db_current_silent(); upgrade_all(); if ( is_multisite() && is_main_site() ) { upgrade_network(); } wp_cache_flush(); if ( is_multisite() ) { update_site_meta( get_current_blog_id(), 'db_version', $wp_db_version ); update_site_meta( get_current_blog_id(), 'db_last_updated', microtime() ); } delete_transient( 'wp_core_block_css_files' ); /** * Fires after a site is fully upgraded. * * @since 3.9.0 * * @param int $wp_db_version The new $wp_db_version. * @param int $wp_current_db_version The old (current) $wp_db_version. */ do_action( 'wp_upgrade', $wp_db_version, $wp_current_db_version ); } endif; /** * Functions to be called in installation and upgrade scripts. * * Contains conditional checks to determine which upgrade scripts to run, * based on database version and WP version being updated-to. * * @ignore * @since 1.0.1 * * @global int $wp_current_db_version The old (current) database version. * @global int $wp_db_version The new database version. */ function upgrade_all() { global $wp_current_db_version, $wp_db_version; $wp_current_db_version = __get_option( 'db_version' ); // We are up to date. Nothing to do. if ( $wp_db_version == $wp_current_db_version ) { return; } // If the version is not set in the DB, try to guess the version. if ( empty( $wp_current_db_version ) ) { $wp_current_db_version = 0; // If the template option exists, we have 1.5. $template = __get_option( 'template' ); if ( ! empty( $template ) ) { $wp_current_db_version = 2541; } } if ( $wp_current_db_version < 6039 ) { upgrade_230_options_table(); } populate_options(); if ( $wp_current_db_version < 2541 ) { upgrade_100(); upgrade_101(); upgrade_110(); upgrade_130(); } if ( $wp_current_db_version < 3308 ) { upgrade_160(); } if ( $wp_current_db_version < 4772 ) { upgrade_210(); } if ( $wp_current_db_version < 4351 ) { upgrade_old_slugs(); } if ( $wp_current_db_version < 5539 ) { upgrade_230(); } if ( $wp_current_db_version < 6124 ) { upgrade_230_old_tables(); } if ( $wp_current_db_version < 7499 ) { upgrade_250(); } if ( $wp_current_db_version < 7935 ) { upgrade_252(); } if ( $wp_current_db_version < 8201 ) { upgrade_260(); } if ( $wp_current_db_version < 8989 ) { upgrade_270(); } if ( $wp_current_db_version < 10360 ) { upgrade_280(); } if ( $wp_current_db_version < 11958 ) { upgrade_290(); } if ( $wp_current_db_version < 15260 ) { upgrade_300(); } if ( $wp_current_db_version < 19389 ) { upgrade_330(); } if ( $wp_current_db_version < 20080 ) { upgrade_340(); } if ( $wp_current_db_version < 22422 ) { upgrade_350(); } if ( $wp_current_db_version < 25824 ) { upgrade_370(); } if ( $wp_current_db_version < 26148 ) { upgrade_372(); } if ( $wp_current_db_version < 26691 ) { upgrade_380(); } if ( $wp_current_db_version < 29630 ) { upgrade_400(); } if ( $wp_current_db_version < 33055 ) { upgrade_430(); } if ( $wp_current_db_version < 33056 ) { upgrade_431(); } if ( $wp_current_db_version < 35700 ) { upgrade_440(); } if ( $wp_current_db_version < 36686 ) { upgrade_450(); } if ( $wp_current_db_version < 37965 ) { upgrade_460(); } if ( $wp_current_db_version < 44719 ) { upgrade_510(); } if ( $wp_current_db_version < 45744 ) { upgrade_530(); } if ( $wp_current_db_version < 48575 ) { upgrade_550(); } if ( $wp_current_db_version < 49752 ) { upgrade_560(); } if ( $wp_current_db_version < 51917 ) { upgrade_590(); } if ( $wp_current_db_version < 53011 ) { upgrade_600(); } if ( $wp_current_db_version < 55853 ) { upgrade_630(); } if ( $wp_current_db_version < 56657 ) { upgrade_640(); } if ( $wp_current_db_version < 57155 ) { upgrade_650(); } maybe_disable_link_manager(); maybe_disable_automattic_widgets(); update_option( 'db_version', $wp_db_version ); update_option( 'db_upgraded', true ); } /** * Execute changes made in WordPress 1.0. * * @ignore * @since 1.0.0 * * @global wpdb $wpdb WordPress database abstraction object. */ function upgrade_100() { global $wpdb; // Get the title and ID of every post, post_name to check if it already has a value. $posts = $wpdb->get_results( "SELECT ID, post_title, post_name FROM $wpdb->posts WHERE post_name = ''" ); if ( $posts ) { foreach ( $posts as $post ) { if ( '' === $post->post_name ) { $newtitle = sanitize_title( $post->post_title ); $wpdb->query( $wpdb->prepare( "UPDATE $wpdb->posts SET post_name = %s WHERE ID = %d", $newtitle, $post->ID ) ); } } } $categories = $wpdb->get_results( "SELECT cat_ID, cat_name, category_nicename FROM $wpdb->categories" ); foreach ( $categories as $category ) { if ( '' === $category->category_nicename ) { $newtitle = sanitize_title( $category->cat_name ); $wpdb->update( $wpdb->categories, array( 'category_nicename' => $newtitle ), array( 'cat_ID' => $category->cat_ID ) ); } } $sql = "UPDATE $wpdb->options SET option_value = REPLACE(option_value, 'wp-links/links-images/', 'wp-images/links/') WHERE option_name LIKE %s AND option_value LIKE %s"; $wpdb->query( $wpdb->prepare( $sql, $wpdb->esc_like( 'links_rating_image' ) . '%', $wpdb->esc_like( 'wp-links/links-images/' ) . '%' ) ); $done_ids = $wpdb->get_results( "SELECT DISTINCT post_id FROM $wpdb->post2cat" ); if ( $done_ids ) : $done_posts = array(); foreach ( $done_ids as $done_id ) : $done_posts[] = $done_id->post_id; endforeach; $catwhere = ' AND ID NOT IN (' . implode( ',', $done_posts ) . ')'; else : $catwhere = ''; endif; $allposts = $wpdb->get_results( "SELECT ID, post_category FROM $wpdb->posts WHERE post_category != '0' $catwhere" ); if ( $allposts ) : foreach ( $allposts as $post ) { // Check to see if it's already been imported. $cat = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $wpdb->post2cat WHERE post_id = %d AND category_id = %d", $post->ID, $post->post_category ) ); if ( ! $cat && 0 != $post->post_category ) { // If there's no result. $wpdb->insert( $wpdb->post2cat, array( 'post_id' => $post->ID, 'category_id' => $post->post_category, ) ); } } endif; } /** * Execute changes made in WordPress 1.0.1. * * @ignore * @since 1.0.1 * * @global wpdb $wpdb WordPress database abstraction object. */ function upgrade_101() { global $wpdb; // Clean up indices, add a few. add_clean_index( $wpdb->posts, 'post_name' ); add_clean_index( $wpdb->posts, 'post_status' ); add_clean_index( $wpdb->categories, 'category_nicename' ); add_clean_index( $wpdb->comments, 'comment_approved' ); add_clean_index( $wpdb->comments, 'comment_post_ID' ); add_clean_index( $wpdb->links, 'link_category' ); add_clean_index( $wpdb->links, 'link_visible' ); } /** * Execute changes made in WordPress 1.2. * * @ignore * @since 1.2.0 * * @global wpdb $wpdb WordPress database abstraction object. */ function upgrade_110() { global $wpdb; // Set user_nicename. $users = $wpdb->get_results( "SELECT ID, user_nickname, user_nicename FROM $wpdb->users" ); foreach ( $users as $user ) { if ( '' === $user->user_nicename ) { $newname = sanitize_title( $user->user_nickname ); $wpdb->update( $wpdb->users, array( 'user_nicename' => $newname ), array( 'ID' => $user->ID ) ); } } $users = $wpdb->get_results( "SELECT ID, user_pass from $wpdb->users" ); foreach ( $users as $row ) { if ( ! preg_match( '/^[A-Fa-f0-9]{32}$/', $row->user_pass ) ) { $wpdb->update( $wpdb->users, array( 'user_pass' => md5( $row->user_pass ) ), array( 'ID' => $row->ID ) ); } } // Get the GMT offset, we'll use that later on. $all_options = get_alloptions_110(); $time_difference = $all_options->time_difference; $server_time = time() + gmdate( 'Z' ); $weblogger_time = $server_time + $time_difference * HOUR_IN_SECONDS; $gmt_time = time(); $diff_gmt_server = ( $gmt_time - $server_time ) / HOUR_IN_SECONDS; $diff_weblogger_server = ( $weblogger_time - $server_time ) / HOUR_IN_SECONDS; $diff_gmt_weblogger = $diff_gmt_server - $diff_weblogger_server; $gmt_offset = -$diff_gmt_weblogger; // Add a gmt_offset option, with value $gmt_offset. add_option( 'gmt_offset', $gmt_offset ); /* * Check if we already set the GMT fields. If we did, then * MAX(post_date_gmt) can't be '0000-00-00 00:00:00'. * <michel_v> I just slapped myself silly for not thinking about it earlier. */ $got_gmt_fields = ( '0000-00-00 00:00:00' !== $wpdb->get_var( "SELECT MAX(post_date_gmt) FROM $wpdb->posts" ) ); if ( ! $got_gmt_fields ) { // Add or subtract time to all dates, to get GMT dates. $add_hours = (int) $diff_gmt_weblogger; $add_minutes = (int) ( 60 * ( $diff_gmt_weblogger - $add_hours ) ); $wpdb->query( "UPDATE $wpdb->posts SET post_date_gmt = DATE_ADD(post_date, INTERVAL '$add_hours:$add_minutes' HOUR_MINUTE)" ); $wpdb->query( "UPDATE $wpdb->posts SET post_modified = post_date" ); $wpdb->query( "UPDATE $wpdb->posts SET post_modified_gmt = DATE_ADD(post_modified, INTERVAL '$add_hours:$add_minutes' HOUR_MINUTE) WHERE post_modified != '0000-00-00 00:00:00'" ); $wpdb->query( "UPDATE $wpdb->comments SET comment_date_gmt = DATE_ADD(comment_date, INTERVAL '$add_hours:$add_minutes' HOUR_MINUTE)" ); $wpdb->query( "UPDATE $wpdb->users SET user_registered = DATE_ADD(user_registered, INTERVAL '$add_hours:$add_minutes' HOUR_MINUTE)" ); } } /** * Execute changes made in WordPress 1.5. * * @ignore * @since 1.5.0 * * @global wpdb $wpdb WordPress database abstraction object. */ function upgrade_130() { global $wpdb; // Remove extraneous backslashes. $posts = $wpdb->get_results( "SELECT ID, post_title, post_content, post_excerpt, guid, post_date, post_name, post_status, post_author FROM $wpdb->posts" ); if ( $posts ) { foreach ( $posts as $post ) { $post_content = addslashes( deslash( $post->post_content ) ); $post_title = addslashes( deslash( $post->post_title ) ); $post_excerpt = addslashes( deslash( $post->post_excerpt ) ); if ( empty( $post->guid ) ) { $guid = get_permalink( $post->ID ); } else { $guid = $post->guid; } $wpdb->update( $wpdb->posts, compact( 'post_title', 'post_content', 'post_excerpt', 'guid' ), array( 'ID' => $post->ID ) ); } } // Remove extraneous backslashes. $comments = $wpdb->get_results( "SELECT comment_ID, comment_author, comment_content FROM $wpdb->comments" ); if ( $comments ) { foreach ( $comments as $comment ) { $comment_content = deslash( $comment->comment_content ); $comment_author = deslash( $comment->comment_author ); $wpdb->update( $wpdb->comments, compact( 'comment_content', 'comment_author' ), array( 'comment_ID' => $comment->comment_ID ) ); } } // Remove extraneous backslashes. $links = $wpdb->get_results( "SELECT link_id, link_name, link_description FROM $wpdb->links" ); if ( $links ) { foreach ( $links as $link ) { $link_name = deslash( $link->link_name ); $link_description = deslash( $link->link_description ); $wpdb->update( $wpdb->links, compact( 'link_name', 'link_description' ), array( 'link_id' => $link->link_id ) ); } } $active_plugins = __get_option( 'active_plugins' ); /* * If plugins are not stored in an array, they're stored in the old * newline separated format. Convert to new format. */ if ( ! is_array( $active_plugins ) ) { $active_plugins = explode( "\n", trim( $active_plugins ) ); update_option( 'active_plugins', $active_plugins ); } // Obsolete tables. $wpdb->query( 'DROP TABLE IF EXISTS ' . $wpdb->prefix . 'optionvalues' ); $wpdb->query( 'DROP TABLE IF EXISTS ' . $wpdb->prefix . 'optiontypes' ); $wpdb->query( 'DROP TABLE IF EXISTS ' . $wpdb->prefix . 'optiongroups' ); $wpdb->query( 'DROP TABLE IF EXISTS ' . $wpdb->prefix . 'optiongroup_options' ); // Update comments table to use comment_type. $wpdb->query( "UPDATE $wpdb->comments SET comment_type='trackback', comment_content = REPLACE(comment_content, '<trackback />', '') WHERE comment_content LIKE '<trackback />%'" ); $wpdb->query( "UPDATE $wpdb->comments SET comment_type='pingback', comment_content = REPLACE(comment_content, '<pingback />', '') WHERE comment_content LIKE '<pingback />%'" ); // Some versions have multiple duplicate option_name rows with the same values. $options = $wpdb->get_results( "SELECT option_name, COUNT(option_name) AS dupes FROM `$wpdb->options` GROUP BY option_name" ); foreach ( $options as $option ) { if ( 1 != $option->dupes ) { // Could this be done in the query? $limit = $option->dupes - 1; $dupe_ids = $wpdb->get_col( $wpdb->prepare( "SELECT option_id FROM $wpdb->options WHERE option_name = %s LIMIT %d", $option->option_name, $limit ) ); if ( $dupe_ids ) { $dupe_ids = implode( ',', $dupe_ids ); $wpdb->query( "DELETE FROM $wpdb->options WHERE option_id IN ($dupe_ids)" ); } } } make_site_theme(); } /** * Execute changes made in WordPress 2.0. * * @ignore * @since 2.0.0 * * @global wpdb $wpdb WordPress database abstraction object. * @global int $wp_current_db_version The old (current) database version. */ function upgrade_160() { global $wpdb, $wp_current_db_version; populate_roles_160(); $users = $wpdb->get_results( "SELECT * FROM $wpdb->users" ); foreach ( $users as $user ) : if ( ! empty( $user->user_firstname ) ) { update_user_meta( $user->ID, 'first_name', wp_slash( $user->user_firstname ) ); } if ( ! empty( $user->user_lastname ) ) { update_user_meta( $user->ID, 'last_name', wp_slash( $user->user_lastname ) ); } if ( ! empty( $user->user_nickname ) ) { update_user_meta( $user->ID, 'nickname', wp_slash( $user->user_nickname ) ); } if ( ! empty( $user->user_level ) ) { update_user_meta( $user->ID, $wpdb->prefix . 'user_level', $user->user_level ); } if ( ! empty( $user->user_icq ) ) { update_user_meta( $user->ID, 'icq', wp_slash( $user->user_icq ) ); } if ( ! empty( $user->user_aim ) ) { update_user_meta( $user->ID, 'aim', wp_slash( $user->user_aim ) ); } if ( ! empty( $user->user_msn ) ) { update_user_meta( $user->ID, 'msn', wp_slash( $user->user_msn ) ); } if ( ! empty( $user->user_yim ) ) { update_user_meta( $user->ID, 'yim', wp_slash( $user->user_icq ) ); } if ( ! empty( $user->user_description ) ) { update_user_meta( $user->ID, 'description', wp_slash( $user->user_description ) ); } if ( isset( $user->user_idmode ) ) : $idmode = $user->user_idmode; if ( 'nickname' === $idmode ) { $id = $user->user_nickname; } if ( 'login' === $idmode ) { $id = $user->user_login; } if ( 'firstname' === $idmode ) { $id = $user->user_firstname; } if ( 'lastname' === $idmode ) { $id = $user->user_lastname; } if ( 'namefl' === $idmode ) { $id = $user->user_firstname . ' ' . $user->user_lastname; } if ( 'namelf' === $idmode ) { $id = $user->user_lastname . ' ' . $user->user_firstname; } if ( ! $idmode ) { $id = $user->user_nickname; } $wpdb->update( $wpdb->users, array( 'display_name' => $id ), array( 'ID' => $user->ID ) ); endif; // FIXME: RESET_CAPS is temporary code to reset roles and caps if flag is set. $caps = get_user_meta( $user->ID, $wpdb->prefix . 'capabilities' ); if ( empty( $caps ) || defined( 'RESET_CAPS' ) ) { $level = get_user_meta( $user->ID, $wpdb->prefix . 'user_level', true ); $role = translate_level_to_role( $level ); update_user_meta( $user->ID, $wpdb->prefix . 'capabilities', array( $role => true ) ); } endforeach; $old_user_fields = array( 'user_firstname', 'user_lastname', 'user_icq', 'user_aim', 'user_msn', 'user_yim', 'user_idmode', 'user_ip', 'user_domain', 'user_browser', 'user_description', 'user_nickname', 'user_level' ); $wpdb->hide_errors(); foreach ( $old_user_fields as $old ) { $wpdb->query( "ALTER TABLE $wpdb->users DROP $old" ); } $wpdb->show_errors(); // Populate comment_count field of posts table. $comments = $wpdb->get_results( "SELECT comment_post_ID, COUNT(*) as c FROM $wpdb->comments WHERE comment_approved = '1' GROUP BY comment_post_ID" ); if ( is_array( $comments ) ) { foreach ( $comments as $comment ) { $wpdb->update( $wpdb->posts, array( 'comment_count' => $comment->c ), array( 'ID' => $comment->comment_post_ID ) ); } } /* * Some alpha versions used a post status of object instead of attachment * and put the mime type in post_type instead of post_mime_type. */ if ( $wp_current_db_version > 2541 && $wp_current_db_version <= 3091 ) { $objects = $wpdb->get_results( "SELECT ID, post_type FROM $wpdb->posts WHERE post_status = 'object'" ); foreach ( $objects as $object ) { $wpdb->update( $wpdb->posts, array( 'post_status' => 'attachment', 'post_mime_type' => $object->post_type, 'post_type' => '', ), array( 'ID' => $object->ID ) ); $meta = get_post_meta( $object->ID, 'imagedata', true ); if ( ! empty( $meta['file'] ) ) { update_attached_file( $object->ID, $meta['file'] ); } } } } /** * Execute changes made in WordPress 2.1. * * @ignore * @since 2.1.0 * * @global int $wp_current_db_version The old (current) database version. * @global wpdb $wpdb WordPress database abstraction object. */ function upgrade_210() { global $wp_current_db_version, $wpdb; if ( $wp_current_db_version < 3506 ) { // Update status and type. $posts = $wpdb->get_results( "SELECT ID, post_status FROM $wpdb->posts" ); if ( ! empty( $posts ) ) { foreach ( $posts as $post ) { $status = $post->post_status; $type = 'post'; if ( 'static' === $status ) { $status = 'publish'; $type = 'page'; } elseif ( 'attachment' === $status ) { $status = 'inherit'; $type = 'attachment'; } $wpdb->query( $wpdb->prepare( "UPDATE $wpdb->posts SET post_status = %s, post_type = %s WHERE ID = %d", $status, $type, $post->ID ) ); } } } if ( $wp_current_db_version < 3845 ) { populate_roles_210(); } if ( $wp_current_db_version < 3531 ) { // Give future posts a post_status of future. $now = gmdate( 'Y-m-d H:i:59' ); $wpdb->query( "UPDATE $wpdb->posts SET post_status = 'future' WHERE post_status = 'publish' AND post_date_gmt > '$now'" ); $posts = $wpdb->get_results( "SELECT ID, post_date FROM $wpdb->posts WHERE post_status ='future'" ); if ( ! empty( $posts ) ) { foreach ( $posts as $post ) { wp_schedule_single_event( mysql2date( 'U', $post->post_date, false ), 'publish_future_post', array( $post->ID ) ); } } } } /** * Execute changes made in WordPress 2.3. * * @ignore * @since 2.3.0 * * @global int $wp_current_db_version The old (current) database version. * @global wpdb $wpdb WordPress database abstraction object. */ function upgrade_230() { global $wp_current_db_version, $wpdb; if ( $wp_current_db_version < 5200 ) { populate_roles_230(); } // Convert categories to terms. $tt_ids = array(); $have_tags = false; $categories = $wpdb->get_results( "SELECT * FROM $wpdb->categories ORDER BY cat_ID" ); foreach ( $categories as $category ) { $term_id = (int) $category->cat_ID; $name = $category->cat_name; $description = $category->category_description; $slug = $category->category_nicename; $parent = $category->category_parent; $term_group = 0; // Associate terms with the same slug in a term group and make slugs unique. $exists = $wpdb->get_results( $wpdb->prepare( "SELECT term_id, term_group FROM $wpdb->terms WHERE slug = %s", $slug ) ); if ( $exists ) { $term_group = $exists[0]->term_group; $id = $exists[0]->term_id; $num = 2; do { $alt_slug = $slug . "-$num"; ++$num; $slug_check = $wpdb->get_var( $wpdb->prepare( "SELECT slug FROM $wpdb->terms WHERE slug = %s", $alt_slug ) ); } while ( $slug_check ); $slug = $alt_slug; if ( empty( $term_group ) ) { $term_group = $wpdb->get_var( "SELECT MAX(term_group) FROM $wpdb->terms GROUP BY term_group" ) + 1; $wpdb->query( $wpdb->prepare( "UPDATE $wpdb->terms SET term_group = %d WHERE term_id = %d", $term_group, $id ) ); } } $wpdb->query( $wpdb->prepare( "INSERT INTO $wpdb->terms (term_id, name, slug, term_group) VALUES (%d, %s, %s, %d)", $term_id, $name, $slug, $term_group ) ); $count = 0; if ( ! empty( $category->category_count ) ) { $count = (int) $category->category_count; $taxonomy = 'category'; $wpdb->query( $wpdb->prepare( "INSERT INTO $wpdb->term_taxonomy (term_id, taxonomy, description, parent, count) VALUES ( %d, %s, %s, %d, %d)", $term_id, $taxonomy, $description, $parent, $count ) ); $tt_ids[ $term_id ][ $taxonomy ] = (int) $wpdb->insert_id; } if ( ! empty( $category->link_count ) ) { $count = (int) $category->link_count; $taxonomy = 'link_category'; $wpdb->query( $wpdb->prepare( "INSERT INTO $wpdb->term_taxonomy (term_id, taxonomy, description, parent, count) VALUES ( %d, %s, %s, %d, %d)", $term_id, $taxonomy, $description, $parent, $count ) ); $tt_ids[ $term_id ][ $taxonomy ] = (int) $wpdb->insert_id; } if ( ! empty( $category->tag_count ) ) { $have_tags = true; $count = (int) $category->tag_count; $taxonomy = 'post_tag'; $wpdb->insert( $wpdb->term_taxonomy, compact( 'term_id', 'taxonomy', 'description', 'parent', 'count' ) ); $tt_ids[ $term_id ][ $taxonomy ] = (int) $wpdb->insert_id; } if ( empty( $count ) ) { $count = 0; $taxonomy = 'category'; $wpdb->insert( $wpdb->term_taxonomy, compact( 'term_id', 'taxonomy', 'description', 'parent', 'count' ) ); $tt_ids[ $term_id ][ $taxonomy ] = (int) $wpdb->insert_id; } } $select = 'post_id, category_id'; if ( $have_tags ) { $select .= ', rel_type'; } $posts = $wpdb->get_results( "SELECT $select FROM $wpdb->post2cat GROUP BY post_id, category_id" ); foreach ( $posts as $post ) { $post_id = (int) $post->post_id; $term_id = (int) $post->category_id; $taxonomy = 'category'; if ( ! empty( $post->rel_type ) && 'tag' === $post->rel_type ) { $taxonomy = 'tag'; } $tt_id = $tt_ids[ $term_id ][ $taxonomy ]; if ( empty( $tt_id ) ) { continue; } $wpdb->insert( $wpdb->term_relationships, array( 'object_id' => $post_id, 'term_taxonomy_id' => $tt_id, ) ); } // < 3570 we used linkcategories. >= 3570 we used categories and link2cat. if ( $wp_current_db_version < 3570 ) { /* * Create link_category terms for link categories. Create a map of link * category IDs to link_category terms. */ $link_cat_id_map = array(); $default_link_cat = 0; $tt_ids = array(); $link_cats = $wpdb->get_results( 'SELECT cat_id, cat_name FROM ' . $wpdb->prefix . 'linkcategories' ); foreach ( $link_cats as $category ) { $cat_id = (int) $category->cat_id; $term_id = 0; $name = wp_slash( $category->cat_name ); $slug = sanitize_title( $name ); $term_group = 0; // Associate terms with the same slug in a term group and make slugs unique. $exists = $wpdb->get_results( $wpdb->prepare( "SELECT term_id, term_group FROM $wpdb->terms WHERE slug = %s", $slug ) ); if ( $exists ) { $term_group = $exists[0]->term_group; $term_id = $exists[0]->term_id; } if ( empty( $term_id ) ) { $wpdb->insert( $wpdb->terms, compact( 'name', 'slug', 'term_group' ) ); $term_id = (int) $wpdb->insert_id; } $link_cat_id_map[ $cat_id ] = $term_id; $default_link_cat = $term_id; $wpdb->insert( $wpdb->term_taxonomy, array( 'term_id' => $term_id, 'taxonomy' => 'link_category', 'description' => '', 'parent' => 0, 'count' => 0, ) ); $tt_ids[ $term_id ] = (int) $wpdb->insert_id; } // Associate links to categories. $links = $wpdb->get_results( "SELECT link_id, link_category FROM $wpdb->links" ); if ( ! empty( $links ) ) { foreach ( $links as $link ) { if ( 0 == $link->link_category ) { continue; } if ( ! isset( $link_cat_id_map[ $link->link_category ] ) ) { continue; } $term_id = $link_cat_id_map[ $link->link_category ]; $tt_id = $tt_ids[ $term_id ]; if ( empty( $tt_id ) ) { continue; } $wpdb->insert( $wpdb->term_relationships, array( 'object_id' => $link->link_id, 'term_taxonomy_id' => $tt_id, ) ); } } // Set default to the last category we grabbed during the upgrade loop. update_option( 'default_link_category', $default_link_cat ); } else { $links = $wpdb->get_results( "SELECT link_id, category_id FROM $wpdb->link2cat GROUP BY link_id, category_id" ); foreach ( $links as $link ) { $link_id = (int) $link->link_id; $term_id = (int) $link->category_id; $taxonomy = 'link_category'; $tt_id = $tt_ids[ $term_id ][ $taxonomy ]; if ( empty( $tt_id ) ) { continue; } $wpdb->insert( $wpdb->term_relationships, array( 'object_id' => $link_id, 'term_taxonomy_id' => $tt_id, ) ); } } if ( $wp_current_db_version < 4772 ) { // Obsolete linkcategories table. $wpdb->query( 'DROP TABLE IF EXISTS ' . $wpdb->prefix . 'linkcategories' ); } // Recalculate all counts. $terms = $wpdb->get_results( "SELECT term_taxonomy_id, taxonomy FROM $wpdb->term_taxonomy" ); foreach ( (array) $terms as $term ) { if ( 'post_tag' === $term->taxonomy || 'category' === $term->taxonomy ) { $count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $wpdb->term_relationships, $wpdb->posts WHERE $wpdb->posts.ID = $wpdb->term_relationships.object_id AND post_status = 'publish' AND post_type = 'post' AND term_taxonomy_id = %d", $term->term_taxonomy_id ) ); } else { $count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $wpdb->term_relationships WHERE term_taxonomy_id = %d", $term->term_taxonomy_id ) ); } $wpdb->update( $wpdb->term_taxonomy, array( 'count' => $count ), array( 'term_taxonomy_id' => $term->term_taxonomy_id ) ); } } /** * Remove old options from the database. * * @ignore * @since 2.3.0 * * @global wpdb $wpdb WordPress database abstraction object. */ function upgrade_230_options_table() { global $wpdb; $old_options_fields = array( 'option_can_override', 'option_type', 'option_width', 'option_height', 'option_description', 'option_admin_level' ); $wpdb->hide_errors(); foreach ( $old_options_fields as $old ) { $wpdb->query( "ALTER TABLE $wpdb->options DROP $old" ); } $wpdb->show_errors(); } /** * Remove old categories, link2cat, and post2cat database tables. * * @ignore * @since 2.3.0 * * @global wpdb $wpdb WordPress database abstraction object. */ function upgrade_230_old_tables() { global $wpdb; $wpdb->query( 'DROP TABLE IF EXISTS ' . $wpdb->prefix . 'categories' ); $wpdb->query( 'DROP TABLE IF EXISTS ' . $wpdb->prefix . 'link2cat' ); $wpdb->query( 'DROP TABLE IF EXISTS ' . $wpdb->prefix . 'post2cat' ); } /** * Upgrade old slugs made in version 2.2. * * @ignore * @since 2.2.0 * * @global wpdb $wpdb WordPress database abstraction object. */ function upgrade_old_slugs() { // Upgrade people who were using the Redirect Old Slugs plugin. global $wpdb; $wpdb->query( "UPDATE $wpdb->postmeta SET meta_key = '_wp_old_slug' WHERE meta_key = 'old_slug'" ); } /** * Execute changes made in WordPress 2.5.0. * * @ignore * @since 2.5.0 * * @global int $wp_current_db_version The old (current) database version. */ function upgrade_250() { global $wp_current_db_version; if ( $wp_current_db_version < 6689 ) { populate_roles_250(); } } /** * Execute changes made in WordPress 2.5.2. * * @ignore * @since 2.5.2 * * @global wpdb $wpdb WordPress database abstraction object. */ function upgrade_252() { global $wpdb; $wpdb->query( "UPDATE $wpdb->users SET user_activation_key = ''" ); } /** * Execute changes made in WordPress 2.6. * * @ignore * @since 2.6.0 * * @global int $wp_current_db_version The old (current) database version. */ function upgrade_260() { global $wp_current_db_version; if ( $wp_current_db_version < 8000 ) { populate_roles_260(); } } /** * Execute changes made in WordPress 2.7. * * @ignore * @since 2.7.0 * * @global int $wp_current_db_version The old (current) database version. * @global wpdb $wpdb WordPress database abstraction object. */ function upgrade_270() { global $wp_current_db_version, $wpdb; if ( $wp_current_db_version < 8980 ) { populate_roles_270(); } // Update post_date for unpublished posts with empty timestamp. if ( $wp_current_db_version < 8921 ) { $wpdb->query( "UPDATE $wpdb->posts SET post_date = post_modified WHERE post_date = '0000-00-00 00:00:00'" ); } } /** * Execute changes made in WordPress 2.8. * * @ignore * @since 2.8.0 * * @global int $wp_current_db_version The old (current) database version. * @global wpdb $wpdb WordPress database abstraction object. */ function upgrade_280() { global $wp_current_db_version, $wpdb; if ( $wp_current_db_version < 10360 ) { populate_roles_280(); } if ( is_multisite() ) { $start = 0; while ( $rows = $wpdb->get_results( "SELECT option_name, option_value FROM $wpdb->options ORDER BY option_id LIMIT $start, 20" ) ) { foreach ( $rows as $row ) { $value = maybe_unserialize( $row->option_value ); if ( $value === $row->option_value ) { $value = stripslashes( $value ); } if ( $value !== $row->option_value ) { update_option( $row->option_name, $value ); } } $start += 20; } clean_blog_cache( get_current_blog_id() ); } } /** * Execute changes made in WordPress 2.9. * * @ignore * @since 2.9.0 * * @global int $wp_current_db_version The old (current) database version. */ function upgrade_290() { global $wp_current_db_version; if ( $wp_current_db_version < 11958 ) { /* * Previously, setting depth to 1 would redundantly disable threading, * but now 2 is the minimum depth to avoid confusion. */ if ( get_option( 'thread_comments_depth' ) == '1' ) { update_option( 'thread_comments_depth', 2 ); update_option( 'thread_comments', 0 ); } } } /** * Execute changes made in WordPress 3.0. * * @ignore * @since 3.0.0 * * @global int $wp_current_db_version The old (current) database version. * @global wpdb $wpdb WordPress database abstraction object. */ function upgrade_300() { global $wp_current_db_version, $wpdb; if ( $wp_current_db_version < 15093 ) { populate_roles_300(); } if ( $wp_current_db_version < 14139 && is_multisite() && is_main_site() && ! defined( 'MULTISITE' ) && get_site_option( 'siteurl' ) === false ) { add_site_option( 'siteurl', '' ); } // 3.0 screen options key name changes. if ( wp_should_upgrade_global_tables() ) { $sql = "DELETE FROM $wpdb->usermeta WHERE meta_key LIKE %s OR meta_key LIKE %s OR meta_key LIKE %s OR meta_key LIKE %s OR meta_key LIKE %s OR meta_key LIKE %s OR meta_key = 'manageedittagscolumnshidden' OR meta_key = 'managecategoriescolumnshidden' OR meta_key = 'manageedit-tagscolumnshidden' OR meta_key = 'manageeditcolumnshidden' OR meta_key = 'categories_per_page' OR meta_key = 'edit_tags_per_page'"; $prefix = $wpdb->esc_like( $wpdb->base_prefix ); $wpdb->query( $wpdb->prepare( $sql, $prefix . '%' . $wpdb->esc_like( 'meta-box-hidden' ) . '%', $prefix . '%' . $wpdb->esc_like( 'closedpostboxes' ) . '%', $prefix . '%' . $wpdb->esc_like( 'manage-' ) . '%' . $wpdb->esc_like( '-columns-hidden' ) . '%', $prefix . '%' . $wpdb->esc_like( 'meta-box-order' ) . '%', $prefix . '%' . $wpdb->esc_like( 'metaboxorder' ) . '%', $prefix . '%' . $wpdb->esc_like( 'screen_layout' ) . '%' ) ); } } /** * Execute changes made in WordPress 3.3. * * @ignore * @since 3.3.0 * * @global int $wp_current_db_version The old (current) database version. * @global wpdb $wpdb WordPress database abstraction object. * @global array $wp_registered_widgets * @global array $sidebars_widgets */ function upgrade_330() { global $wp_current_db_version, $wpdb, $wp_registered_widgets, $sidebars_widgets; if ( $wp_current_db_version < 19061 && wp_should_upgrade_global_tables() ) { $wpdb->query( "DELETE FROM $wpdb->usermeta WHERE meta_key IN ('show_admin_bar_admin', 'plugins_last_view')" ); } if ( $wp_current_db_version >= 11548 ) { return; } $sidebars_widgets = get_option( 'sidebars_widgets', array() ); $_sidebars_widgets = array(); if ( isset( $sidebars_widgets['wp_inactive_widgets'] ) || empty( $sidebars_widgets ) ) { $sidebars_widgets['array_version'] = 3; } elseif ( ! isset( $sidebars_widgets['array_version'] ) ) { $sidebars_widgets['array_version'] = 1; } switch ( $sidebars_widgets['array_version'] ) { case 1: foreach ( (array) $sidebars_widgets as $index => $sidebar ) { if ( is_array( $sidebar ) ) { foreach ( (array) $sidebar as $i => $name ) { $id = strtolower( $name ); if ( isset( $wp_registered_widgets[ $id ] ) ) { $_sidebars_widgets[ $index ][ $i ] = $id; continue; } $id = sanitize_title( $name ); if ( isset( $wp_registered_widgets[ $id ] ) ) { $_sidebars_widgets[ $index ][ $i ] = $id; continue; } $found = false; foreach ( $wp_registered_widgets as $widget_id => $widget ) { if ( strtolower( $widget['name'] ) === strtolower( $name ) ) { $_sidebars_widgets[ $index ][ $i ] = $widget['id']; $found = true; break; } elseif ( sanitize_title( $widget['name'] ) === sanitize_title( $name ) ) { $_sidebars_widgets[ $index ][ $i ] = $widget['id']; $found = true; break; } } if ( $found ) { continue; } unset( $_sidebars_widgets[ $index ][ $i ] ); } } } $_sidebars_widgets['array_version'] = 2; $sidebars_widgets = $_sidebars_widgets; unset( $_sidebars_widgets ); // Intentional fall-through to upgrade to the next version. case 2: $sidebars_widgets = retrieve_widgets(); $sidebars_widgets['array_version'] = 3; update_option( 'sidebars_widgets', $sidebars_widgets ); } } /** * Execute changes made in WordPress 3.4. * * @ignore * @since 3.4.0 * * @global int $wp_current_db_version The old (current) database version. * @global wpdb $wpdb WordPress database abstraction object. */ function upgrade_340() { global $wp_current_db_version, $wpdb; if ( $wp_current_db_version < 19798 ) { $wpdb->hide_errors(); $wpdb->query( "ALTER TABLE $wpdb->options DROP COLUMN blog_id" ); $wpdb->show_errors(); } if ( $wp_current_db_version < 19799 ) { $wpdb->hide_errors(); $wpdb->query( "ALTER TABLE $wpdb->comments DROP INDEX comment_approved" ); $wpdb->show_errors(); } if ( $wp_current_db_version < 20022 && wp_should_upgrade_global_tables() ) { $wpdb->query( "DELETE FROM $wpdb->usermeta WHERE meta_key = 'themes_last_view'" ); } if ( $wp_current_db_version < 20080 ) { if ( 'yes' === $wpdb->get_var( "SELECT autoload FROM $wpdb->options WHERE option_name = 'uninstall_plugins'" ) ) { $uninstall_plugins = get_option( 'uninstall_plugins' ); delete_option( 'uninstall_plugins' ); add_option( 'uninstall_plugins', $uninstall_plugins, null, 'no' ); } } } /** * Execute changes made in WordPress 3.5. * * @ignore * @since 3.5.0 * * @global int $wp_current_db_version The old (current) database version. * @global wpdb $wpdb WordPress database abstraction object. */ function upgrade_350() { global $wp_current_db_version, $wpdb; if ( $wp_current_db_version < 22006 && $wpdb->get_var( "SELECT link_id FROM $wpdb->links LIMIT 1" ) ) { update_option( 'link_manager_enabled', 1 ); // Previously set to 0 by populate_options(). } if ( $wp_current_db_version < 21811 && wp_should_upgrade_global_tables() ) { $meta_keys = array(); foreach ( array_merge( get_post_types(), get_taxonomies() ) as $name ) { if ( str_contains( $name, '-' ) ) { $meta_keys[] = 'edit_' . str_replace( '-', '_', $name ) . '_per_page'; } } if ( $meta_keys ) { $meta_keys = implode( "', '", $meta_keys ); $wpdb->query( "DELETE FROM $wpdb->usermeta WHERE meta_key IN ('$meta_keys')" ); } } if ( $wp_current_db_version < 22422 ) { $term = get_term_by( 'slug', 'post-format-standard', 'post_format' ); if ( $term ) { wp_delete_term( $term->term_id, 'post_format' ); } } } /** * Execute changes made in WordPress 3.7. * * @ignore * @since 3.7.0 * * @global int $wp_current_db_version The old (current) database version. */ function upgrade_370() { global $wp_current_db_version; if ( $wp_current_db_version < 25824 ) { wp_clear_scheduled_hook( 'wp_auto_updates_maybe_update' ); } } /** * Execute changes made in WordPress 3.7.2. * * @ignore * @since 3.7.2 * * @global int $wp_current_db_version The old (current) database version. */ function upgrade_372() { global $wp_current_db_version; if ( $wp_current_db_version < 26148 ) { wp_clear_scheduled_hook( 'wp_maybe_auto_update' ); } } /** * Execute changes made in WordPress 3.8.0. * * @ignore * @since 3.8.0 * * @global int $wp_current_db_version The old (current) database version. */ function upgrade_380() { global $wp_current_db_version; if ( $wp_current_db_version < 26691 ) { deactivate_plugins( array( 'mp6/mp6.php' ), true ); } } /** * Execute changes made in WordPress 4.0.0. * * @ignore * @since 4.0.0 * * @global int $wp_current_db_version The old (current) database version. */ function upgrade_400() { global $wp_current_db_version; if ( $wp_current_db_version < 29630 ) { if ( ! is_multisite() && false === get_option( 'WPLANG' ) ) { if ( defined( 'WPLANG' ) && ( '' !== WPLANG ) && in_array( WPLANG, get_available_languages(), true ) ) { update_option( 'WPLANG', WPLANG ); } else { update_option( 'WPLANG', '' ); } } } } /** * Execute changes made in WordPress 4.2.0. * * @ignore * @since 4.2.0 */ function upgrade_420() {} /** * Executes changes made in WordPress 4.3.0. * * @ignore * @since 4.3.0 * * @global int $wp_current_db_version The old (current) database version. * @global wpdb $wpdb WordPress database abstraction object. */ function upgrade_430() { global $wp_current_db_version, $wpdb; if ( $wp_current_db_version < 32364 ) { upgrade_430_fix_comments(); } // Shared terms are split in a separate process. if ( $wp_current_db_version < 32814 ) { update_option( 'finished_splitting_shared_terms', 0 ); wp_schedule_single_event( time() + ( 1 * MINUTE_IN_SECONDS ), 'wp_split_shared_term_batch' ); } if ( $wp_current_db_version < 33055 && 'utf8mb4' === $wpdb->charset ) { if ( is_multisite() ) { $tables = $wpdb->tables( 'blog' ); } else { $tables = $wpdb->tables( 'all' ); if ( ! wp_should_upgrade_global_tables() ) { $global_tables = $wpdb->tables( 'global' ); $tables = array_diff_assoc( $tables, $global_tables ); } } foreach ( $tables as $table ) { maybe_convert_table_to_utf8mb4( $table ); } } } /** * Executes comments changes made in WordPress 4.3.0. * * @ignore * @since 4.3.0 * * @global wpdb $wpdb WordPress database abstraction object. */ function upgrade_430_fix_comments() { global $wpdb; $content_length = $wpdb->get_col_length( $wpdb->comments, 'comment_content' ); if ( is_wp_error( $content_length ) ) { return; } if ( false === $content_length ) { $content_length = array( 'type' => 'byte', 'length' => 65535, ); } elseif ( ! is_array( $content_length ) ) { $length = (int) $content_length > 0 ? (int) $content_length : 65535; $content_length = array( 'type' => 'byte', 'length' => $length, ); } if ( 'byte' !== $content_length['type'] || 0 === $content_length['length'] ) { // Sites with malformed DB schemas are on their own. return; } $allowed_length = (int) $content_length['length'] - 10; $comments = $wpdb->get_results( "SELECT `comment_ID` FROM `{$wpdb->comments}` WHERE `comment_date_gmt` > '2015-04-26' AND LENGTH( `comment_content` ) >= {$allowed_length} AND ( `comment_content` LIKE '%<%' OR `comment_content` LIKE '%>%' )" ); foreach ( $comments as $comment ) { wp_delete_comment( $comment->comment_ID, true ); } } /** * Executes changes made in WordPress 4.3.1. * * @ignore * @since 4.3.1 */ function upgrade_431() { // Fix incorrect cron entries for term splitting. $cron_array = _get_cron_array(); if ( isset( $cron_array['wp_batch_split_terms'] ) ) { unset( $cron_array['wp_batch_split_terms'] ); _set_cron_array( $cron_array ); } } /** * Executes changes made in WordPress 4.4.0. * * @ignore * @since 4.4.0 * * @global int $wp_current_db_version The old (current) database version. * @global wpdb $wpdb WordPress database abstraction object. */ function upgrade_440() { global $wp_current_db_version, $wpdb; if ( $wp_current_db_version < 34030 ) { $wpdb->query( "ALTER TABLE {$wpdb->options} MODIFY option_name VARCHAR(191)" ); } // Remove the unused 'add_users' role. $roles = wp_roles(); foreach ( $roles->role_objects as $role ) { if ( $role->has_cap( 'add_users' ) ) { $role->remove_cap( 'add_users' ); } } } /** * Executes changes made in WordPress 4.5.0. * * @ignore * @since 4.5.0 * * @global int $wp_current_db_version The old (current) database version. * @global wpdb $wpdb WordPress database abstraction object. */ function upgrade_450() { global $wp_current_db_version, $wpdb; if ( $wp_current_db_version < 36180 ) { wp_clear_scheduled_hook( 'wp_maybe_auto_update' ); } // Remove unused email confirmation options, moved to usermeta. if ( $wp_current_db_version < 36679 && is_multisite() ) { $wpdb->query( "DELETE FROM $wpdb->options WHERE option_name REGEXP '^[0-9]+_new_email$'" ); } // Remove unused user setting for wpLink. delete_user_setting( 'wplink' ); } /** * Executes changes made in WordPress 4.6.0. * * @ignore * @since 4.6.0 * * @global int $wp_current_db_version The old (current) database version. */ function upgrade_460() { global $wp_current_db_version; // Remove unused post meta. if ( $wp_current_db_version < 37854 ) { delete_post_meta_by_key( '_post_restored_from' ); } // Remove plugins with callback as an array object/method as the uninstall hook, see #13786. if ( $wp_current_db_version < 37965 ) { $uninstall_plugins = get_option( 'uninstall_plugins', array() ); if ( ! empty( $uninstall_plugins ) ) { foreach ( $uninstall_plugins as $basename => $callback ) { if ( is_array( $callback ) && is_object( $callback[0] ) ) { unset( $uninstall_plugins[ $basename ] ); } } update_option( 'uninstall_plugins', $uninstall_plugins ); } } } /** * Executes changes made in WordPress 5.0.0. * * @ignore * @since 5.0.0 * @deprecated 5.1.0 */ function upgrade_500() { } /** * Executes changes made in WordPress 5.1.0. * * @ignore * @since 5.1.0 */ function upgrade_510() { delete_site_option( 'upgrade_500_was_gutenberg_active' ); } /** * Executes changes made in WordPress 5.3.0. * * @ignore * @since 5.3.0 */ function upgrade_530() { /* * The `admin_email_lifespan` option may have been set by an admin that just logged in, * saw the verification screen, clicked on a button there, and is now upgrading the db, * or by populate_options() that is called earlier in upgrade_all(). * In the second case `admin_email_lifespan` should be reset so the verification screen * is shown next time an admin logs in. */ if ( function_exists( 'current_user_can' ) && ! current_user_can( 'manage_options' ) ) { update_option( 'admin_email_lifespan', 0 ); } } /** * Executes changes made in WordPress 5.5.0. * * @ignore * @since 5.5.0 * * @global int $wp_current_db_version The old (current) database version. */ function upgrade_550() { global $wp_current_db_version; if ( $wp_current_db_version < 48121 ) { $comment_previously_approved = get_option( 'comment_whitelist', '' ); update_option( 'comment_previously_approved', $comment_previously_approved ); delete_option( 'comment_whitelist' ); } if ( $wp_current_db_version < 48575 ) { // Use more clear and inclusive language. $disallowed_list = get_option( 'blacklist_keys' ); /* * This option key was briefly renamed `blocklist_keys`. * Account for sites that have this key present when the original key does not exist. */ if ( false === $disallowed_list ) { $disallowed_list = get_option( 'blocklist_keys' ); } update_option( 'disallowed_keys', $disallowed_list ); delete_option( 'blacklist_keys' ); delete_option( 'blocklist_keys' ); } if ( $wp_current_db_version < 48748 ) { update_option( 'finished_updating_comment_type', 0 ); wp_schedule_single_event( time() + ( 1 * MINUTE_IN_SECONDS ), 'wp_update_comment_type_batch' ); } } /** * Executes changes made in WordPress 5.6.0. * * @ignore * @since 5.6.0 * * @global int $wp_current_db_version The old (current) database version. * @global wpdb $wpdb WordPress database abstraction object. */ function upgrade_560() { global $wp_current_db_version, $wpdb; if ( $wp_current_db_version < 49572 ) { /* * Clean up the `post_category` column removed from schema in version 2.8.0. * Its presence may conflict with `WP_Post::__get()`. */ $post_category_exists = $wpdb->get_var( "SHOW COLUMNS FROM $wpdb->posts LIKE 'post_category'" ); if ( ! is_null( $post_category_exists ) ) { $wpdb->query( "ALTER TABLE $wpdb->posts DROP COLUMN `post_category`" ); } /* * When upgrading from WP < 5.6.0 set the core major auto-updates option to `unset` by default. * This overrides the same option from populate_options() that is intended for new installs. * See https://core.trac.wordpress.org/ticket/51742. */ update_option( 'auto_update_core_major', 'unset' ); } if ( $wp_current_db_version < 49632 ) { /* * Regenerate the .htaccess file to add the `HTTP_AUTHORIZATION` rewrite rule. * See https://core.trac.wordpress.org/ticket/51723. */ save_mod_rewrite_rules(); } if ( $wp_current_db_version < 49735 ) { delete_transient( 'dirsize_cache' ); } if ( $wp_current_db_version < 49752 ) { $results = $wpdb->get_results( $wpdb->prepare( "SELECT 1 FROM {$wpdb->usermeta} WHERE meta_key = %s LIMIT 1", WP_Application_Passwords::USERMETA_KEY_APPLICATION_PASSWORDS ) ); if ( ! empty( $results ) ) { $network_id = get_main_network_id(); update_network_option( $network_id, WP_Application_Passwords::OPTION_KEY_IN_USE, 1 ); } } } /** * Executes changes made in WordPress 5.9.0. * * @ignore * @since 5.9.0 * * @global int $wp_current_db_version The old (current) database version. */ function upgrade_590() { global $wp_current_db_version; if ( $wp_current_db_version < 51917 ) { $crons = _get_cron_array(); if ( $crons && is_array( $crons ) ) { // Remove errant `false` values, see #53950, #54906. $crons = array_filter( $crons ); _set_cron_array( $crons ); } } } /** * Executes changes made in WordPress 6.0.0. * * @ignore * @since 6.0.0 * * @global int $wp_current_db_version The old (current) database version. */ function upgrade_600() { global $wp_current_db_version; if ( $wp_current_db_version < 53011 ) { wp_update_user_counts(); } } /** * Executes changes made in WordPress 6.3.0. * * @ignore * @since 6.3.0 * * @global int $wp_current_db_version The old (current) database version. */ function upgrade_630() { global $wp_current_db_version; if ( $wp_current_db_version < 55853 ) { if ( ! is_multisite() ) { // Replace non-autoload option can_compress_scripts with autoload option, see #55270 $can_compress_scripts = get_option( 'can_compress_scripts', false ); if ( false !== $can_compress_scripts ) { delete_option( 'can_compress_scripts' ); add_option( 'can_compress_scripts', $can_compress_scripts, '', 'yes' ); } } } } /** * Executes changes made in WordPress 6.4.0. * * @ignore * @since 6.4.0 * * @global int $wp_current_db_version The old (current) database version. */ function upgrade_640() { global $wp_current_db_version; if ( $wp_current_db_version < 56657 ) { // Enable attachment pages. update_option( 'wp_attachment_pages_enabled', 1 ); // Remove the wp_https_detection cron. Https status is checked directly in an async Site Health check. $scheduled = wp_get_scheduled_event( 'wp_https_detection' ); if ( $scheduled ) { wp_clear_scheduled_hook( 'wp_https_detection' ); } } } /** * Executes changes made in WordPress 6.5.0. * * @ignore * @since 6.5.0 * * @global int $wp_current_db_version The old (current) database version. * @global wpdb $wpdb WordPress database abstraction object. */ function upgrade_650() { global $wp_current_db_version, $wpdb; if ( $wp_current_db_version < 57155 ) { $stylesheet = get_stylesheet(); // Set autoload=no for all themes except the current one. $theme_mods_options = $wpdb->get_col( $wpdb->prepare( "SELECT option_name FROM $wpdb->options WHERE autoload = 'yes' AND option_name != %s AND option_name LIKE %s", "theme_mods_$stylesheet", $wpdb->esc_like( 'theme_mods_' ) . '%' ) ); $autoload = array_fill_keys( $theme_mods_options, 'no' ); wp_set_option_autoload_values( $autoload ); } } /** * Executes network-level upgrade routines. * * @since 3.0.0 * * @global int $wp_current_db_version The old (current) database version. * @global wpdb $wpdb WordPress database abstraction object. */ function upgrade_network() { global $wp_current_db_version, $wpdb; // Always clear expired transients. delete_expired_transients( true ); // 2.8.0 if ( $wp_current_db_version < 11549 ) { $wpmu_sitewide_plugins = get_site_option( 'wpmu_sitewide_plugins' ); $active_sitewide_plugins = get_site_option( 'active_sitewide_plugins' ); if ( $wpmu_sitewide_plugins ) { if ( ! $active_sitewide_plugins ) { $sitewide_plugins = (array) $wpmu_sitewide_plugins; } else { $sitewide_plugins = array_merge( (array) $active_sitewide_plugins, (array) $wpmu_sitewide_plugins ); } update_site_option( 'active_sitewide_plugins', $sitewide_plugins ); } delete_site_option( 'wpmu_sitewide_plugins' ); delete_site_option( 'deactivated_sitewide_plugins' ); $start = 0; while ( $rows = $wpdb->get_results( "SELECT meta_key, meta_value FROM {$wpdb->sitemeta} ORDER BY meta_id LIMIT $start, 20" ) ) { foreach ( $rows as $row ) { $value = $row->meta_value; if ( ! @unserialize( $value ) ) { $value = stripslashes( $value ); } if ( $value !== $row->meta_value ) { update_site_option( $row->meta_key, $value ); } } $start += 20; } } // 3.0.0 if ( $wp_current_db_version < 13576 ) { update_site_option( 'global_terms_enabled', '1' ); } // 3.3.0 if ( $wp_current_db_version < 19390 ) { update_site_option( 'initial_db_version', $wp_current_db_version ); } if ( $wp_current_db_version < 19470 ) { if ( false === get_site_option( 'active_sitewide_plugins' ) ) { update_site_option( 'active_sitewide_plugins', array() ); } } // 3.4.0 if ( $wp_current_db_version < 20148 ) { // 'allowedthemes' keys things by stylesheet. 'allowed_themes' keyed things by name. $allowedthemes = get_site_option( 'allowedthemes' ); $allowed_themes = get_site_option( 'allowed_themes' ); if ( false === $allowedthemes && is_array( $allowed_themes ) && $allowed_themes ) { $converted = array(); $themes = wp_get_themes(); foreach ( $themes as $stylesheet => $theme_data ) { if ( isset( $allowed_themes[ $theme_data->get( 'Name' ) ] ) ) { $converted[ $stylesheet ] = true; } } update_site_option( 'allowedthemes', $converted ); delete_site_option( 'allowed_themes' ); } } // 3.5.0 if ( $wp_current_db_version < 21823 ) { update_site_option( 'ms_files_rewriting', '1' ); } // 3.5.2 if ( $wp_current_db_version < 24448 ) { $illegal_names = get_site_option( 'illegal_names' ); if ( is_array( $illegal_names ) && count( $illegal_names ) === 1 ) { $illegal_name = reset( $illegal_names ); $illegal_names = explode( ' ', $illegal_name ); update_site_option( 'illegal_names', $illegal_names ); } } // 4.2.0 if ( $wp_current_db_version < 31351 && 'utf8mb4' === $wpdb->charset ) { if ( wp_should_upgrade_global_tables() ) { $wpdb->query( "ALTER TABLE $wpdb->usermeta DROP INDEX meta_key, ADD INDEX meta_key(meta_key(191))" ); $wpdb->query( "ALTER TABLE $wpdb->site DROP INDEX domain, ADD INDEX domain(domain(140),path(51))" ); $wpdb->query( "ALTER TABLE $wpdb->sitemeta DROP INDEX meta_key, ADD INDEX meta_key(meta_key(191))" ); $wpdb->query( "ALTER TABLE $wpdb->signups DROP INDEX domain_path, ADD INDEX domain_path(domain(140),path(51))" ); $tables = $wpdb->tables( 'global' ); // sitecategories may not exist. if ( ! $wpdb->get_var( "SHOW TABLES LIKE '{$tables['sitecategories']}'" ) ) { unset( $tables['sitecategories'] ); } foreach ( $tables as $table ) { maybe_convert_table_to_utf8mb4( $table ); } } } // 4.3.0 if ( $wp_current_db_version < 33055 && 'utf8mb4' === $wpdb->charset ) { if ( wp_should_upgrade_global_tables() ) { $upgrade = false; $indexes = $wpdb->get_results( "SHOW INDEXES FROM $wpdb->signups" ); foreach ( $indexes as $index ) { if ( 'domain_path' === $index->Key_name && 'domain' === $index->Column_name && 140 != $index->Sub_part ) { $upgrade = true; break; } } if ( $upgrade ) { $wpdb->query( "ALTER TABLE $wpdb->signups DROP INDEX domain_path, ADD INDEX domain_path(domain(140),path(51))" ); } $tables = $wpdb->tables( 'global' ); // sitecategories may not exist. if ( ! $wpdb->get_var( "SHOW TABLES LIKE '{$tables['sitecategories']}'" ) ) { unset( $tables['sitecategories'] ); } foreach ( $tables as $table ) { maybe_convert_table_to_utf8mb4( $table ); } } } // 5.1.0 if ( $wp_current_db_version < 44467 ) { $network_id = get_main_network_id(); delete_network_option( $network_id, 'site_meta_supported' ); is_site_meta_supported(); } } // // General functions we use to actually do stuff. // /** * Creates a table in the database, if it doesn't already exist. * * This method checks for an existing database table and creates a new one if it's not * already present. It doesn't rely on MySQL's "IF NOT EXISTS" statement, but chooses * to query all tables first and then run the SQL statement creating the table. * * @since 1.0.0 * * @global wpdb $wpdb WordPress database abstraction object. * * @param string $table_name Database table name. * @param string $create_ddl SQL statement to create table. * @return bool True on success or if the table already exists. False on failure. */ function maybe_create_table( $table_name, $create_ddl ) { global $wpdb; $query = $wpdb->prepare( 'SHOW TABLES LIKE %s', $wpdb->esc_like( $table_name ) ); if ( $wpdb->get_var( $query ) === $table_name ) { return true; } // Didn't find it, so try to create it. $wpdb->query( $create_ddl ); // We cannot directly tell that whether this succeeded! if ( $wpdb->get_var( $query ) === $table_name ) { return true; } return false; } /** * Drops a specified index from a table. * * @since 1.0.1 * * @global wpdb $wpdb WordPress database abstraction object. * * @param string $table Database table name. * @param string $index Index name to drop. * @return true True, when finished. */ function drop_index( $table, $index ) { global $wpdb; $wpdb->hide_errors(); $wpdb->query( "ALTER TABLE `$table` DROP INDEX `$index`" ); // Now we need to take out all the extra ones we may have created. for ( $i = 0; $i < 25; $i++ ) { $wpdb->query( "ALTER TABLE `$table` DROP INDEX `{$index}_$i`" ); } $wpdb->show_errors(); return true; } /** * Adds an index to a specified table. * * @since 1.0.1 * * @global wpdb $wpdb WordPress database abstraction object. * * @param string $table Database table name. * @param string $index Database table index column. * @return true True, when done with execution. */ function add_clean_index( $table, $index ) { global $wpdb; drop_index( $table, $index ); $wpdb->query( "ALTER TABLE `$table` ADD INDEX ( `$index` )" ); return true; } /** * Adds column to a database table, if it doesn't already exist. * * @since 1.3.0 * * @global wpdb $wpdb WordPress database abstraction object. * * @param string $table_name Database table name. * @param string $column_name Table column name. * @param string $create_ddl SQL statement to add column. * @return bool True on success or if the column already exists. False on failure. */ function maybe_add_column( $table_name, $column_name, $create_ddl ) { global $wpdb; foreach ( $wpdb->get_col( "DESC $table_name", 0 ) as $column ) { if ( $column === $column_name ) { return true; } } // Didn't find it, so try to create it. $wpdb->query( $create_ddl ); // We cannot directly tell that whether this succeeded! foreach ( $wpdb->get_col( "DESC $table_name", 0 ) as $column ) { if ( $column === $column_name ) { return true; } } return false; } /** * If a table only contains utf8 or utf8mb4 columns, convert it to utf8mb4. * * @since 4.2.0 * * @global wpdb $wpdb WordPress database abstraction object. * * @param string $table The table to convert. * @return bool True if the table was converted, false if it wasn't. */ function maybe_convert_table_to_utf8mb4( $table ) { global $wpdb; $results = $wpdb->get_results( "SHOW FULL COLUMNS FROM `$table`" ); if ( ! $results ) { return false; } foreach ( $results as $column ) { if ( $column->Collation ) { list( $charset ) = explode( '_', $column->Collation ); $charset = strtolower( $charset ); if ( 'utf8' !== $charset && 'utf8mb4' !== $charset ) { // Don't upgrade tables that have non-utf8 columns. return false; } } } $table_details = $wpdb->get_row( "SHOW TABLE STATUS LIKE '$table'" ); if ( ! $table_details ) { return false; } list( $table_charset ) = explode( '_', $table_details->Collation ); $table_charset = strtolower( $table_charset ); if ( 'utf8mb4' === $table_charset ) { return true; } return $wpdb->query( "ALTER TABLE $table CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci" ); } /** * Retrieve all options as it was for 1.2. * * @since 1.2.0 * * @global wpdb $wpdb WordPress database abstraction object. * * @return stdClass List of options. */ function get_alloptions_110() { global $wpdb; $all_options = new stdClass(); $options = $wpdb->get_results( "SELECT option_name, option_value FROM $wpdb->options" ); if ( $options ) { foreach ( $options as $option ) { if ( 'siteurl' === $option->option_name || 'home' === $option->option_name || 'category_base' === $option->option_name ) { $option->option_value = untrailingslashit( $option->option_value ); } $all_options->{$option->option_name} = stripslashes( $option->option_value ); } } return $all_options; } /** * Utility version of get_option that is private to installation/upgrade. * * @ignore * @since 1.5.1 * @access private * * @global wpdb $wpdb WordPress database abstraction object. * * @param string $setting Option name. * @return mixed */ function __get_option( $setting ) { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName.FunctionDoubleUnderscore,PHPCompatibility.FunctionNameRestrictions.ReservedFunctionNames.FunctionDoubleUnderscore global $wpdb; if ( 'home' === $setting && defined( 'WP_HOME' ) ) { return untrailingslashit( WP_HOME ); } if ( 'siteurl' === $setting && defined( 'WP_SITEURL' ) ) { return untrailingslashit( WP_SITEURL ); } $option = $wpdb->get_var( $wpdb->prepare( "SELECT option_value FROM $wpdb->options WHERE option_name = %s", $setting ) ); if ( 'home' === $setting && ! $option ) { return __get_option( 'siteurl' ); } if ( in_array( $setting, array( 'siteurl', 'home', 'category_base', 'tag_base' ), true ) ) { $option = untrailingslashit( $option ); } return maybe_unserialize( $option ); } /** * Filters for content to remove unnecessary slashes. * * @since 1.5.0 * * @param string $content The content to modify. * @return string The de-slashed content. */ function deslash( $content ) { // Note: \\\ inside a regex denotes a single backslash. /* * Replace one or more backslashes followed by a single quote with * a single quote. */ $content = preg_replace( "/\\\+'/", "'", $content ); /* * Replace one or more backslashes followed by a double quote with * a double quote. */ $content = preg_replace( '/\\\+"/', '"', $content ); // Replace one or more backslashes with one backslash. $content = preg_replace( '/\\\+/', '\\', $content ); return $content; } /** * Modifies the database based on specified SQL statements. * * Useful for creating new tables and updating existing tables to a new structure. * * @since 1.5.0 * @since 6.1.0 Ignores display width for integer data types on MySQL 8.0.17 or later, * to match MySQL behavior. Note: This does not affect MariaDB. * * @global wpdb $wpdb WordPress database abstraction object. * * @param string[]|string $queries Optional. The query to run. Can be multiple queries * in an array, or a string of queries separated by * semicolons. Default empty string. * @param bool $execute Optional. Whether or not to execute the query right away. * Default true. * @return array Strings containing the results of the various update queries. */ function dbDelta( $queries = '', $execute = true ) { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName.FunctionNameInvalid global $wpdb; if ( in_array( $queries, array( '', 'all', 'blog', 'global', 'ms_global' ), true ) ) { $queries = wp_get_db_schema( $queries ); } // Separate individual queries into an array. if ( ! is_array( $queries ) ) { $queries = explode( ';', $queries ); $queries = array_filter( $queries ); } /** * Filters the dbDelta SQL queries. * * @since 3.3.0 * * @param string[] $queries An array of dbDelta SQL queries. */ $queries = apply_filters( 'dbdelta_queries', $queries ); $cqueries = array(); // Creation queries. $iqueries = array(); // Insertion queries. $for_update = array(); // Create a tablename index for an array ($cqueries) of recognized query types. foreach ( $queries as $qry ) { if ( preg_match( '|CREATE TABLE ([^ ]*)|', $qry, $matches ) ) { $cqueries[ trim( $matches[1], '`' ) ] = $qry; $for_update[ $matches[1] ] = 'Created table ' . $matches[1]; continue; } if ( preg_match( '|CREATE DATABASE ([^ ]*)|', $qry, $matches ) ) { array_unshift( $cqueries, $qry ); continue; } if ( preg_match( '|INSERT INTO ([^ ]*)|', $qry, $matches ) ) { $iqueries[] = $qry; continue; } if ( preg_match( '|UPDATE ([^ ]*)|', $qry, $matches ) ) { $iqueries[] = $qry; continue; } } /** * Filters the dbDelta SQL queries for creating tables and/or databases. * * Queries filterable via this hook contain "CREATE TABLE" or "CREATE DATABASE". * * @since 3.3.0 * * @param string[] $cqueries An array of dbDelta create SQL queries. */ $cqueries = apply_filters( 'dbdelta_create_queries', $cqueries ); /** * Filters the dbDelta SQL queries for inserting or updating. * * Queries filterable via this hook contain "INSERT INTO" or "UPDATE". * * @since 3.3.0 * * @param string[] $iqueries An array of dbDelta insert or update SQL queries. */ $iqueries = apply_filters( 'dbdelta_insert_queries', $iqueries ); $text_fields = array( 'tinytext', 'text', 'mediumtext', 'longtext' ); $blob_fields = array( 'tinyblob', 'blob', 'mediumblob', 'longblob' ); $int_fields = array( 'tinyint', 'smallint', 'mediumint', 'int', 'integer', 'bigint' ); $global_tables = $wpdb->tables( 'global' ); $db_version = $wpdb->db_version(); $db_server_info = $wpdb->db_server_info(); foreach ( $cqueries as $table => $qry ) { // Upgrade global tables only for the main site. Don't upgrade at all if conditions are not optimal. if ( in_array( $table, $global_tables, true ) && ! wp_should_upgrade_global_tables() ) { unset( $cqueries[ $table ], $for_update[ $table ] ); continue; } // Fetch the table column structure from the database. $suppress = $wpdb->suppress_errors(); $tablefields = $wpdb->get_results( "DESCRIBE {$table};" ); $wpdb->suppress_errors( $suppress ); if ( ! $tablefields ) { continue; } // Clear the field and index arrays. $cfields = array(); $indices = array(); $indices_without_subparts = array(); // Get all of the field names in the query from between the parentheses. preg_match( '|\((.*)\)|ms', $qry, $match2 ); $qryline = trim( $match2[1] ); // Separate field lines into an array. $flds = explode( "\n", $qryline ); // For every field line specified in the query. foreach ( $flds as $fld ) { $fld = trim( $fld, " \t\n\r\0\x0B," ); // Default trim characters, plus ','. // Extract the field name. preg_match( '|^([^ ]*)|', $fld, $fvals ); $fieldname = trim( $fvals[1], '`' ); $fieldname_lowercased = strtolower( $fieldname ); // Verify the found field name. $validfield = true; switch ( $fieldname_lowercased ) { case '': case 'primary': case 'index': case 'fulltext': case 'unique': case 'key': case 'spatial': $validfield = false; /* * Normalize the index definition. * * This is done so the definition can be compared against the result of a * `SHOW INDEX FROM $table_name` query which returns the current table * index information. */ // Extract type, name and columns from the definition. preg_match( '/^ (?P<index_type> # 1) Type of the index. PRIMARY\s+KEY|(?:UNIQUE|FULLTEXT|SPATIAL)\s+(?:KEY|INDEX)|KEY|INDEX ) \s+ # Followed by at least one white space character. (?: # Name of the index. Optional if type is PRIMARY KEY. `? # Name can be escaped with a backtick. (?P<index_name> # 2) Name of the index. (?:[0-9a-zA-Z$_-]|[\xC2-\xDF][\x80-\xBF])+ ) `? # Name can be escaped with a backtick. \s+ # Followed by at least one white space character. )* \( # Opening bracket for the columns. (?P<index_columns> .+? # 3) Column names, index prefixes, and orders. ) \) # Closing bracket for the columns. $/imx', $fld, $index_matches ); // Uppercase the index type and normalize space characters. $index_type = strtoupper( preg_replace( '/\s+/', ' ', trim( $index_matches['index_type'] ) ) ); // 'INDEX' is a synonym for 'KEY', standardize on 'KEY'. $index_type = str_replace( 'INDEX', 'KEY', $index_type ); // Escape the index name with backticks. An index for a primary key has no name. $index_name = ( 'PRIMARY KEY' === $index_type ) ? '' : '`' . strtolower( $index_matches['index_name'] ) . '`'; // Parse the columns. Multiple columns are separated by a comma. $index_columns = array_map( 'trim', explode( ',', $index_matches['index_columns'] ) ); $index_columns_without_subparts = $index_columns; // Normalize columns. foreach ( $index_columns as $id => &$index_column ) { // Extract column name and number of indexed characters (sub_part). preg_match( '/ `? # Name can be escaped with a backtick. (?P<column_name> # 1) Name of the column. (?:[0-9a-zA-Z$_-]|[\xC2-\xDF][\x80-\xBF])+ ) `? # Name can be escaped with a backtick. (?: # Optional sub part. \s* # Optional white space character between name and opening bracket. \( # Opening bracket for the sub part. \s* # Optional white space character after opening bracket. (?P<sub_part> \d+ # 2) Number of indexed characters. ) \s* # Optional white space character before closing bracket. \) # Closing bracket for the sub part. )? /x', $index_column, $index_column_matches ); // Escape the column name with backticks. $index_column = '`' . $index_column_matches['column_name'] . '`'; // We don't need to add the subpart to $index_columns_without_subparts $index_columns_without_subparts[ $id ] = $index_column; // Append the optional sup part with the number of indexed characters. if ( isset( $index_column_matches['sub_part'] ) ) { $index_column .= '(' . $index_column_matches['sub_part'] . ')'; } } // Build the normalized index definition and add it to the list of indices. $indices[] = "{$index_type} {$index_name} (" . implode( ',', $index_columns ) . ')'; $indices_without_subparts[] = "{$index_type} {$index_name} (" . implode( ',', $index_columns_without_subparts ) . ')'; // Destroy no longer needed variables. unset( $index_column, $index_column_matches, $index_matches, $index_type, $index_name, $index_columns, $index_columns_without_subparts ); break; } // If it's a valid field, add it to the field array. if ( $validfield ) { $cfields[ $fieldname_lowercased ] = $fld; } } // For every field in the table. foreach ( $tablefields as $tablefield ) { $tablefield_field_lowercased = strtolower( $tablefield->Field ); $tablefield_type_lowercased = strtolower( $tablefield->Type ); $tablefield_type_without_parentheses = preg_replace( '/' . '(.+)' // Field type, e.g. `int`. . '\(\d*\)' // Display width. . '(.*)' // Optional attributes, e.g. `unsigned`. . '/', '$1$2', $tablefield_type_lowercased ); // Get the type without attributes, e.g. `int`. $tablefield_type_base = strtok( $tablefield_type_without_parentheses, ' ' ); // If the table field exists in the field array... if ( array_key_exists( $tablefield_field_lowercased, $cfields ) ) { // Get the field type from the query. preg_match( '|`?' . $tablefield->Field . '`? ([^ ]*( unsigned)?)|i', $cfields[ $tablefield_field_lowercased ], $matches ); $fieldtype = $matches[1]; $fieldtype_lowercased = strtolower( $fieldtype ); $fieldtype_without_parentheses = preg_replace( '/' . '(.+)' // Field type, e.g. `int`. . '\(\d*\)' // Display width. . '(.*)' // Optional attributes, e.g. `unsigned`. . '/', '$1$2', $fieldtype_lowercased ); // Get the type without attributes, e.g. `int`. $fieldtype_base = strtok( $fieldtype_without_parentheses, ' ' ); // Is actual field type different from the field type in query? if ( $tablefield->Type != $fieldtype ) { $do_change = true; if ( in_array( $fieldtype_lowercased, $text_fields, true ) && in_array( $tablefield_type_lowercased, $text_fields, true ) ) { if ( array_search( $fieldtype_lowercased, $text_fields, true ) < array_search( $tablefield_type_lowercased, $text_fields, true ) ) { $do_change = false; } } if ( in_array( $fieldtype_lowercased, $blob_fields, true ) && in_array( $tablefield_type_lowercased, $blob_fields, true ) ) { if ( array_search( $fieldtype_lowercased, $blob_fields, true ) < array_search( $tablefield_type_lowercased, $blob_fields, true ) ) { $do_change = false; } } if ( in_array( $fieldtype_base, $int_fields, true ) && in_array( $tablefield_type_base, $int_fields, true ) && $fieldtype_without_parentheses === $tablefield_type_without_parentheses ) { /* * MySQL 8.0.17 or later does not support display width for integer data types, * so if display width is the only difference, it can be safely ignored. * Note: This is specific to MySQL and does not affect MariaDB. */ if ( version_compare( $db_version, '8.0.17', '>=' ) && ! str_contains( $db_server_info, 'MariaDB' ) ) { $do_change = false; } } if ( $do_change ) { // Add a query to change the column type. $cqueries[] = "ALTER TABLE {$table} CHANGE COLUMN `{$tablefield->Field}` " . $cfields[ $tablefield_field_lowercased ]; $for_update[ $table . '.' . $tablefield->Field ] = "Changed type of {$table}.{$tablefield->Field} from {$tablefield->Type} to {$fieldtype}"; } } // Get the default value from the array. if ( preg_match( "| DEFAULT '(.*?)'|i", $cfields[ $tablefield_field_lowercased ], $matches ) ) { $default_value = $matches[1]; if ( $tablefield->Default != $default_value ) { // Add a query to change the column's default value $cqueries[] = "ALTER TABLE {$table} ALTER COLUMN `{$tablefield->Field}` SET DEFAULT '{$default_value}'"; $for_update[ $table . '.' . $tablefield->Field ] = "Changed default value of {$table}.{$tablefield->Field} from {$tablefield->Default} to {$default_value}"; } } // Remove the field from the array (so it's not added). unset( $cfields[ $tablefield_field_lowercased ] ); } else { // This field exists in the table, but not in the creation queries? } } // For every remaining field specified for the table. foreach ( $cfields as $fieldname => $fielddef ) { // Push a query line into $cqueries that adds the field to that table. $cqueries[] = "ALTER TABLE {$table} ADD COLUMN $fielddef"; $for_update[ $table . '.' . $fieldname ] = 'Added column ' . $table . '.' . $fieldname; } // Index stuff goes here. Fetch the table index structure from the database. $tableindices = $wpdb->get_results( "SHOW INDEX FROM {$table};" ); if ( $tableindices ) { // Clear the index array. $index_ary = array(); // For every index in the table. foreach ( $tableindices as $tableindex ) { $keyname = strtolower( $tableindex->Key_name ); // Add the index to the index data array. $index_ary[ $keyname ]['columns'][] = array( 'fieldname' => $tableindex->Column_name, 'subpart' => $tableindex->Sub_part, ); $index_ary[ $keyname ]['unique'] = ( 0 == $tableindex->Non_unique ) ? true : false; $index_ary[ $keyname ]['index_type'] = $tableindex->Index_type; } // For each actual index in the index array. foreach ( $index_ary as $index_name => $index_data ) { // Build a create string to compare to the query. $index_string = ''; if ( 'primary' === $index_name ) { $index_string .= 'PRIMARY '; } elseif ( $index_data['unique'] ) { $index_string .= 'UNIQUE '; } if ( 'FULLTEXT' === strtoupper( $index_data['index_type'] ) ) { $index_string .= 'FULLTEXT '; } if ( 'SPATIAL' === strtoupper( $index_data['index_type'] ) ) { $index_string .= 'SPATIAL '; } $index_string .= 'KEY '; if ( 'primary' !== $index_name ) { $index_string .= '`' . $index_name . '`'; } $index_columns = ''; // For each column in the index. foreach ( $index_data['columns'] as $column_data ) { if ( '' !== $index_columns ) { $index_columns .= ','; } // Add the field to the column list string. $index_columns .= '`' . $column_data['fieldname'] . '`'; } // Add the column list to the index create string. $index_string .= " ($index_columns)"; // Check if the index definition exists, ignoring subparts. $aindex = array_search( $index_string, $indices_without_subparts, true ); if ( false !== $aindex ) { // If the index already exists (even with different subparts), we don't need to create it. unset( $indices_without_subparts[ $aindex ] ); unset( $indices[ $aindex ] ); } } } // For every remaining index specified for the table. foreach ( (array) $indices as $index ) { // Push a query line into $cqueries that adds the index to that table. $cqueries[] = "ALTER TABLE {$table} ADD $index"; $for_update[] = 'Added index ' . $table . ' ' . $index; } // Remove the original table creation query from processing. unset( $cqueries[ $table ], $for_update[ $table ] ); } $allqueries = array_merge( $cqueries, $iqueries ); if ( $execute ) { foreach ( $allqueries as $query ) { $wpdb->query( $query ); } } return $for_update; } /** * Updates the database tables to a new schema. * * By default, updates all the tables to use the latest defined schema, but can also * be used to update a specific set of tables in wp_get_db_schema(). * * @since 1.5.0 * * @uses dbDelta * * @param string $tables Optional. Which set of tables to update. Default is 'all'. */ function make_db_current( $tables = 'all' ) { $alterations = dbDelta( $tables ); echo "<ol>\n"; foreach ( $alterations as $alteration ) { echo "<li>$alteration</li>\n"; } echo "</ol>\n"; } /** * Updates the database tables to a new schema, but without displaying results. * * By default, updates all the tables to use the latest defined schema, but can * also be used to update a specific set of tables in wp_get_db_schema(). * * @since 1.5.0 * * @see make_db_current() * * @param string $tables Optional. Which set of tables to update. Default is 'all'. */ function make_db_current_silent( $tables = 'all' ) { dbDelta( $tables ); } /** * Creates a site theme from an existing theme. * * {@internal Missing Long Description}} * * @since 1.5.0 * * @param string $theme_name The name of the theme. * @param string $template The directory name of the theme. * @return bool */ function make_site_theme_from_oldschool( $theme_name, $template ) { $home_path = get_home_path(); $site_dir = WP_CONTENT_DIR . "/themes/$template"; $default_dir = WP_CONTENT_DIR . '/themes/' . WP_DEFAULT_THEME; if ( ! file_exists( "$home_path/index.php" ) ) { return false; } /* * Copy files from the old locations to the site theme. * TODO: This does not copy arbitrary include dependencies. Only the standard WP files are copied. */ $files = array( 'index.php' => 'index.php', 'wp-layout.css' => 'style.css', 'wp-comments.php' => 'comments.php', 'wp-comments-popup.php' => 'comments-popup.php', ); foreach ( $files as $oldfile => $newfile ) { if ( 'index.php' === $oldfile ) { $oldpath = $home_path; } else { $oldpath = ABSPATH; } // Check to make sure it's not a new index. if ( 'index.php' === $oldfile ) { $index = implode( '', file( "$oldpath/$oldfile" ) ); if ( str_contains( $index, 'WP_USE_THEMES' ) ) { if ( ! copy( "$default_dir/$oldfile", "$site_dir/$newfile" ) ) { return false; } // Don't copy anything. continue; } } if ( ! copy( "$oldpath/$oldfile", "$site_dir/$newfile" ) ) { return false; } chmod( "$site_dir/$newfile", 0777 ); // Update the blog header include in each file. $lines = explode( "\n", implode( '', file( "$site_dir/$newfile" ) ) ); if ( $lines ) { $f = fopen( "$site_dir/$newfile", 'w' ); foreach ( $lines as $line ) { if ( preg_match( '/require.*wp-blog-header/', $line ) ) { $line = '//' . $line; } // Update stylesheet references. $line = str_replace( "<?php echo __get_option('siteurl'); ?>/wp-layout.css", "<?php bloginfo('stylesheet_url'); ?>", $line ); // Update comments template inclusion. $line = str_replace( "<?php include(ABSPATH . 'wp-comments.php'); ?>", '<?php comments_template(); ?>', $line ); fwrite( $f, "{$line}\n" ); } fclose( $f ); } } // Add a theme header. $header = "/*\n" . "Theme Name: $theme_name\n" . 'Theme URI: ' . __get_option( 'siteurl' ) . "\n" . "Description: A theme automatically created by the update.\n" . "Version: 1.0\n" . "Author: Moi\n" . "*/\n"; $stylelines = file_get_contents( "$site_dir/style.css" ); if ( $stylelines ) { $f = fopen( "$site_dir/style.css", 'w' ); fwrite( $f, $header ); fwrite( $f, $stylelines ); fclose( $f ); } return true; } /** * Creates a site theme from the default theme. * * {@internal Missing Long Description}} * * @since 1.5.0 * * @param string $theme_name The name of the theme. * @param string $template The directory name of the theme. * @return void|false */ function make_site_theme_from_default( $theme_name, $template ) { $site_dir = WP_CONTENT_DIR . "/themes/$template"; $default_dir = WP_CONTENT_DIR . '/themes/' . WP_DEFAULT_THEME; /* * Copy files from the default theme to the site theme. * $files = array( 'index.php', 'comments.php', 'comments-popup.php', 'footer.php', 'header.php', 'sidebar.php', 'style.css' ); */ $theme_dir = @opendir( $default_dir ); if ( $theme_dir ) { while ( ( $theme_file = readdir( $theme_dir ) ) !== false ) { if ( is_dir( "$default_dir/$theme_file" ) ) { continue; } if ( ! copy( "$default_dir/$theme_file", "$site_dir/$theme_file" ) ) { return; } chmod( "$site_dir/$theme_file", 0777 ); } closedir( $theme_dir ); } // Rewrite the theme header. $stylelines = explode( "\n", implode( '', file( "$site_dir/style.css" ) ) ); if ( $stylelines ) { $f = fopen( "$site_dir/style.css", 'w' ); $headers = array( 'Theme Name:' => $theme_name, 'Theme URI:' => __get_option( 'url' ), 'Description:' => 'Your theme.', 'Version:' => '1', 'Author:' => 'You', ); foreach ( $stylelines as $line ) { foreach ( $headers as $header => $value ) { if ( str_contains( $line, $header ) ) { $line = $header . ' ' . $value; break; } } fwrite( $f, $line . "\n" ); } fclose( $f ); } // Copy the images. umask( 0 ); if ( ! mkdir( "$site_dir/images", 0777 ) ) { return false; } $images_dir = @opendir( "$default_dir/images" ); if ( $images_dir ) { while ( ( $image = readdir( $images_dir ) ) !== false ) { if ( is_dir( "$default_dir/images/$image" ) ) { continue; } if ( ! copy( "$default_dir/images/$image", "$site_dir/images/$image" ) ) { return; } chmod( "$site_dir/images/$image", 0777 ); } closedir( $images_dir ); } } /** * Creates a site theme. * * {@internal Missing Long Description}} * * @since 1.5.0 * * @return string|false */ function make_site_theme() { // Name the theme after the blog. $theme_name = __get_option( 'blogname' ); $template = sanitize_title( $theme_name ); $site_dir = WP_CONTENT_DIR . "/themes/$template"; // If the theme already exists, nothing to do. if ( is_dir( $site_dir ) ) { return false; } // We must be able to write to the themes dir. if ( ! is_writable( WP_CONTENT_DIR . '/themes' ) ) { return false; } umask( 0 ); if ( ! mkdir( $site_dir, 0777 ) ) { return false; } if ( file_exists( ABSPATH . 'wp-layout.css' ) ) { if ( ! make_site_theme_from_oldschool( $theme_name, $template ) ) { // TODO: rm -rf the site theme directory. return false; } } else { if ( ! make_site_theme_from_default( $theme_name, $template ) ) { // TODO: rm -rf the site theme directory. return false; } } // Make the new site theme active. $current_template = __get_option( 'template' ); if ( WP_DEFAULT_THEME == $current_template ) { update_option( 'template', $template ); update_option( 'stylesheet', $template ); } return $template; } /** * Translate user level to user role name. * * @since 2.0.0 * * @param int $level User level. * @return string User role name. */ function translate_level_to_role( $level ) { switch ( $level ) { case 10: case 9: case 8: return 'administrator'; case 7: case 6: case 5: return 'editor'; case 4: case 3: case 2: return 'author'; case 1: return 'contributor'; case 0: default: return 'subscriber'; } } /** * Checks the version of the installed MySQL binary. * * @since 2.1.0 * * @global wpdb $wpdb WordPress database abstraction object. */ function wp_check_mysql_version() { global $wpdb; $result = $wpdb->check_database_version(); if ( is_wp_error( $result ) ) { wp_die( $result ); } } /** * Disables the Automattic widgets plugin, which was merged into core. * * @since 2.2.0 */ function maybe_disable_automattic_widgets() { $plugins = __get_option( 'active_plugins' ); foreach ( (array) $plugins as $plugin ) { if ( 'widgets.php' === basename( $plugin ) ) { array_splice( $plugins, array_search( $plugin, $plugins, true ), 1 ); update_option( 'active_plugins', $plugins ); break; } } } /** * Disables the Link Manager on upgrade if, at the time of upgrade, no links exist in the DB. * * @since 3.5.0 * * @global int $wp_current_db_version The old (current) database version. * @global wpdb $wpdb WordPress database abstraction object. */ function maybe_disable_link_manager() { global $wp_current_db_version, $wpdb; if ( $wp_current_db_version >= 22006 && get_option( 'link_manager_enabled' ) && ! $wpdb->get_var( "SELECT link_id FROM $wpdb->links LIMIT 1" ) ) { update_option( 'link_manager_enabled', 0 ); } } /** * Runs before the schema is upgraded. * * @since 2.9.0 * * @global int $wp_current_db_version The old (current) database version. * @global wpdb $wpdb WordPress database abstraction object. */ function pre_schema_upgrade() { global $wp_current_db_version, $wpdb; // Upgrade versions prior to 2.9. if ( $wp_current_db_version < 11557 ) { // Delete duplicate options. Keep the option with the highest option_id. $wpdb->query( "DELETE o1 FROM $wpdb->options AS o1 JOIN $wpdb->options AS o2 USING (`option_name`) WHERE o2.option_id > o1.option_id" ); // Drop the old primary key and add the new. $wpdb->query( "ALTER TABLE $wpdb->options DROP PRIMARY KEY, ADD PRIMARY KEY(option_id)" ); // Drop the old option_name index. dbDelta() doesn't do the drop. $wpdb->query( "ALTER TABLE $wpdb->options DROP INDEX option_name" ); } // Multisite schema upgrades. if ( $wp_current_db_version < 25448 && is_multisite() && wp_should_upgrade_global_tables() ) { // Upgrade versions prior to 3.7. if ( $wp_current_db_version < 25179 ) { // New primary key for signups. $wpdb->query( "ALTER TABLE $wpdb->signups ADD signup_id BIGINT(20) NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST" ); $wpdb->query( "ALTER TABLE $wpdb->signups DROP INDEX domain" ); } if ( $wp_current_db_version < 25448 ) { // Convert archived from enum to tinyint. $wpdb->query( "ALTER TABLE $wpdb->blogs CHANGE COLUMN archived archived varchar(1) NOT NULL default '0'" ); $wpdb->query( "ALTER TABLE $wpdb->blogs CHANGE COLUMN archived archived tinyint(2) NOT NULL default 0" ); } } // Upgrade versions prior to 4.2. if ( $wp_current_db_version < 31351 ) { if ( ! is_multisite() && wp_should_upgrade_global_tables() ) { $wpdb->query( "ALTER TABLE $wpdb->usermeta DROP INDEX meta_key, ADD INDEX meta_key(meta_key(191))" ); } $wpdb->query( "ALTER TABLE $wpdb->terms DROP INDEX slug, ADD INDEX slug(slug(191))" ); $wpdb->query( "ALTER TABLE $wpdb->terms DROP INDEX name, ADD INDEX name(name(191))" ); $wpdb->query( "ALTER TABLE $wpdb->commentmeta DROP INDEX meta_key, ADD INDEX meta_key(meta_key(191))" ); $wpdb->query( "ALTER TABLE $wpdb->postmeta DROP INDEX meta_key, ADD INDEX meta_key(meta_key(191))" ); $wpdb->query( "ALTER TABLE $wpdb->posts DROP INDEX post_name, ADD INDEX post_name(post_name(191))" ); } // Upgrade versions prior to 4.4. if ( $wp_current_db_version < 34978 ) { // If compatible termmeta table is found, use it, but enforce a proper index and update collation. if ( $wpdb->get_var( "SHOW TABLES LIKE '{$wpdb->termmeta}'" ) && $wpdb->get_results( "SHOW INDEX FROM {$wpdb->termmeta} WHERE Column_name = 'meta_key'" ) ) { $wpdb->query( "ALTER TABLE $wpdb->termmeta DROP INDEX meta_key, ADD INDEX meta_key(meta_key(191))" ); maybe_convert_table_to_utf8mb4( $wpdb->termmeta ); } } } /** * Determine if global tables should be upgraded. * * This function performs a series of checks to ensure the environment allows * for the safe upgrading of global WordPress database tables. It is necessary * because global tables will commonly grow to millions of rows on large * installations, and the ability to control their upgrade routines can be * critical to the operation of large networks. * * In a future iteration, this function may use `wp_is_large_network()` to more- * intelligently prevent global table upgrades. Until then, we make sure * WordPress is on the main site of the main network, to avoid running queries * more than once in multi-site or multi-network environments. * * @since 4.3.0 * * @return bool Whether to run the upgrade routines on global tables. */ function wp_should_upgrade_global_tables() { // Return false early if explicitly not upgrading. if ( defined( 'DO_NOT_UPGRADE_GLOBAL_TABLES' ) ) { return false; } // Assume global tables should be upgraded. $should_upgrade = true; // Set to false if not on main network (does not matter if not multi-network). if ( ! is_main_network() ) { $should_upgrade = false; } // Set to false if not on main site of current network (does not matter if not multi-site). if ( ! is_main_site() ) { $should_upgrade = false; } /** * Filters if upgrade routines should be run on global tables. * * @since 4.3.0 * * @param bool $should_upgrade Whether to run the upgrade routines on global tables. */ return apply_filters( 'wp_should_upgrade_global_tables', $should_upgrade ); } includes/class-wp-site-icon.php 0000644 00000014416 15135536347 0012526 0 ustar 00 <?php /** * Administration API: WP_Site_Icon class * * @package WordPress * @subpackage Administration * @since 4.3.0 */ /** * Core class used to implement site icon functionality. * * @since 4.3.0 */ #[AllowDynamicProperties] class WP_Site_Icon { /** * The minimum size of the site icon. * * @since 4.3.0 * @var int */ public $min_size = 512; /** * The size to which to crop the image so that we can display it in the UI nicely. * * @since 4.3.0 * @var int */ public $page_crop = 512; /** * List of site icon sizes. * * @since 4.3.0 * @var int[] */ public $site_icon_sizes = array( /* * Square, medium sized tiles for IE11+. * * See https://msdn.microsoft.com/library/dn455106(v=vs.85).aspx */ 270, /* * App icon for Android/Chrome. * * @link https://developers.google.com/web/updates/2014/11/Support-for-theme-color-in-Chrome-39-for-Android * @link https://developer.chrome.com/multidevice/android/installtohomescreen */ 192, /* * App icons up to iPhone 6 Plus. * * See https://developer.apple.com/library/prerelease/ios/documentation/UserExperience/Conceptual/MobileHIG/IconMatrix.html */ 180, // Our regular Favicon. 32, ); /** * Registers actions and filters. * * @since 4.3.0 */ public function __construct() { add_action( 'delete_attachment', array( $this, 'delete_attachment_data' ) ); add_filter( 'get_post_metadata', array( $this, 'get_post_metadata' ), 10, 4 ); } /** * Creates an attachment 'object'. * * @since 4.3.0 * @deprecated 6.5.0 * * @param string $cropped Cropped image URL. * @param int $parent_attachment_id Attachment ID of parent image. * @return array An array with attachment object data. */ public function create_attachment_object( $cropped, $parent_attachment_id ) { _deprecated_function( __METHOD__, '6.5.0', 'wp_copy_parent_attachment_properties()' ); $parent = get_post( $parent_attachment_id ); $parent_url = wp_get_attachment_url( $parent->ID ); $url = str_replace( wp_basename( $parent_url ), wp_basename( $cropped ), $parent_url ); $size = wp_getimagesize( $cropped ); $image_type = ( $size ) ? $size['mime'] : 'image/jpeg'; $attachment = array( 'ID' => $parent_attachment_id, 'post_title' => wp_basename( $cropped ), 'post_content' => $url, 'post_mime_type' => $image_type, 'guid' => $url, 'context' => 'site-icon', ); return $attachment; } /** * Inserts an attachment. * * @since 4.3.0 * * @param array $attachment An array with attachment object data. * @param string $file File path of the attached image. * @return int Attachment ID. */ public function insert_attachment( $attachment, $file ) { $attachment_id = wp_insert_attachment( $attachment, $file ); $metadata = wp_generate_attachment_metadata( $attachment_id, $file ); /** * Filters the site icon attachment metadata. * * @since 4.3.0 * * @see wp_generate_attachment_metadata() * * @param array $metadata Attachment metadata. */ $metadata = apply_filters( 'site_icon_attachment_metadata', $metadata ); wp_update_attachment_metadata( $attachment_id, $metadata ); return $attachment_id; } /** * Adds additional sizes to be made when creating the site icon images. * * @since 4.3.0 * * @param array[] $sizes Array of arrays containing information for additional sizes. * @return array[] Array of arrays containing additional image sizes. */ public function additional_sizes( $sizes = array() ) { $only_crop_sizes = array(); /** * Filters the different dimensions that a site icon is saved in. * * @since 4.3.0 * * @param int[] $site_icon_sizes Array of sizes available for the Site Icon. */ $this->site_icon_sizes = apply_filters( 'site_icon_image_sizes', $this->site_icon_sizes ); // Use a natural sort of numbers. natsort( $this->site_icon_sizes ); $this->site_icon_sizes = array_reverse( $this->site_icon_sizes ); // Ensure that we only resize the image into sizes that allow cropping. foreach ( $sizes as $name => $size_array ) { if ( isset( $size_array['crop'] ) ) { $only_crop_sizes[ $name ] = $size_array; } } foreach ( $this->site_icon_sizes as $size ) { if ( $size < $this->min_size ) { $only_crop_sizes[ 'site_icon-' . $size ] = array( 'width ' => $size, 'height' => $size, 'crop' => true, ); } } return $only_crop_sizes; } /** * Adds Site Icon sizes to the array of image sizes on demand. * * @since 4.3.0 * * @param string[] $sizes Array of image size names. * @return string[] Array of image size names. */ public function intermediate_image_sizes( $sizes = array() ) { /** This filter is documented in wp-admin/includes/class-wp-site-icon.php */ $this->site_icon_sizes = apply_filters( 'site_icon_image_sizes', $this->site_icon_sizes ); foreach ( $this->site_icon_sizes as $size ) { $sizes[] = 'site_icon-' . $size; } return $sizes; } /** * Deletes the Site Icon when the image file is deleted. * * @since 4.3.0 * * @param int $post_id Attachment ID. */ public function delete_attachment_data( $post_id ) { $site_icon_id = (int) get_option( 'site_icon' ); if ( $site_icon_id && $post_id === $site_icon_id ) { delete_option( 'site_icon' ); } } /** * Adds custom image sizes when meta data for an image is requested, that happens to be used as Site Icon. * * @since 4.3.0 * * @param null|array|string $value The value get_metadata() should return a single metadata value, or an * array of values. * @param int $post_id Post ID. * @param string $meta_key Meta key. * @param bool $single Whether to return only the first value of the specified `$meta_key`. * @return array|null|string The attachment metadata value, array of values, or null. */ public function get_post_metadata( $value, $post_id, $meta_key, $single ) { if ( $single && '_wp_attachment_backup_sizes' === $meta_key ) { $site_icon_id = (int) get_option( 'site_icon' ); if ( $post_id === $site_icon_id ) { add_filter( 'intermediate_image_sizes', array( $this, 'intermediate_image_sizes' ) ); } } return $value; } } includes/privacy-tools.php 0000644 00000101266 15135536347 0011720 0 ustar 00 <?php /** * WordPress Administration Privacy Tools API. * * @package WordPress * @subpackage Administration */ /** * Resend an existing request and return the result. * * @since 4.9.6 * @access private * * @param int $request_id Request ID. * @return true|WP_Error Returns true if sending the email was successful, or a WP_Error object. */ function _wp_privacy_resend_request( $request_id ) { $request_id = absint( $request_id ); $request = get_post( $request_id ); if ( ! $request || 'user_request' !== $request->post_type ) { return new WP_Error( 'privacy_request_error', __( 'Invalid personal data request.' ) ); } $result = wp_send_user_request( $request_id ); if ( is_wp_error( $result ) ) { return $result; } elseif ( ! $result ) { return new WP_Error( 'privacy_request_error', __( 'Unable to initiate confirmation for personal data request.' ) ); } return true; } /** * Marks a request as completed by the admin and logs the current timestamp. * * @since 4.9.6 * @access private * * @param int $request_id Request ID. * @return int|WP_Error Request ID on success, or a WP_Error on failure. */ function _wp_privacy_completed_request( $request_id ) { // Get the request. $request_id = absint( $request_id ); $request = wp_get_user_request( $request_id ); if ( ! $request ) { return new WP_Error( 'privacy_request_error', __( 'Invalid personal data request.' ) ); } update_post_meta( $request_id, '_wp_user_request_completed_timestamp', time() ); $result = wp_update_post( array( 'ID' => $request_id, 'post_status' => 'request-completed', ) ); return $result; } /** * Handle list table actions. * * @since 4.9.6 * @access private */ function _wp_personal_data_handle_actions() { if ( isset( $_POST['privacy_action_email_retry'] ) ) { check_admin_referer( 'bulk-privacy_requests' ); $request_id = absint( current( array_keys( (array) wp_unslash( $_POST['privacy_action_email_retry'] ) ) ) ); $result = _wp_privacy_resend_request( $request_id ); if ( is_wp_error( $result ) ) { add_settings_error( 'privacy_action_email_retry', 'privacy_action_email_retry', $result->get_error_message(), 'error' ); } else { add_settings_error( 'privacy_action_email_retry', 'privacy_action_email_retry', __( 'Confirmation request sent again successfully.' ), 'success' ); } } elseif ( isset( $_POST['action'] ) ) { $action = ! empty( $_POST['action'] ) ? sanitize_key( wp_unslash( $_POST['action'] ) ) : ''; switch ( $action ) { case 'add_export_personal_data_request': case 'add_remove_personal_data_request': check_admin_referer( 'personal-data-request' ); if ( ! isset( $_POST['type_of_action'], $_POST['username_or_email_for_privacy_request'] ) ) { add_settings_error( 'action_type', 'action_type', __( 'Invalid personal data action.' ), 'error' ); } $action_type = sanitize_text_field( wp_unslash( $_POST['type_of_action'] ) ); $username_or_email_address = sanitize_text_field( wp_unslash( $_POST['username_or_email_for_privacy_request'] ) ); $email_address = ''; $status = 'pending'; if ( ! isset( $_POST['send_confirmation_email'] ) ) { $status = 'confirmed'; } if ( ! in_array( $action_type, _wp_privacy_action_request_types(), true ) ) { add_settings_error( 'action_type', 'action_type', __( 'Invalid personal data action.' ), 'error' ); } if ( ! is_email( $username_or_email_address ) ) { $user = get_user_by( 'login', $username_or_email_address ); if ( ! $user instanceof WP_User ) { add_settings_error( 'username_or_email_for_privacy_request', 'username_or_email_for_privacy_request', __( 'Unable to add this request. A valid email address or username must be supplied.' ), 'error' ); } else { $email_address = $user->user_email; } } else { $email_address = $username_or_email_address; } if ( empty( $email_address ) ) { break; } $request_id = wp_create_user_request( $email_address, $action_type, array(), $status ); $message = ''; if ( is_wp_error( $request_id ) ) { $message = $request_id->get_error_message(); } elseif ( ! $request_id ) { $message = __( 'Unable to initiate confirmation request.' ); } if ( $message ) { add_settings_error( 'username_or_email_for_privacy_request', 'username_or_email_for_privacy_request', $message, 'error' ); break; } if ( 'pending' === $status ) { wp_send_user_request( $request_id ); $message = __( 'Confirmation request initiated successfully.' ); } elseif ( 'confirmed' === $status ) { $message = __( 'Request added successfully.' ); } if ( $message ) { add_settings_error( 'username_or_email_for_privacy_request', 'username_or_email_for_privacy_request', $message, 'success' ); break; } } } } /** * Cleans up failed and expired requests before displaying the list table. * * @since 4.9.6 * @access private */ function _wp_personal_data_cleanup_requests() { /** This filter is documented in wp-includes/user.php */ $expires = (int) apply_filters( 'user_request_key_expiration', DAY_IN_SECONDS ); $requests_query = new WP_Query( array( 'post_type' => 'user_request', 'posts_per_page' => -1, 'post_status' => 'request-pending', 'fields' => 'ids', 'date_query' => array( array( 'column' => 'post_modified_gmt', 'before' => $expires . ' seconds ago', ), ), ) ); $request_ids = $requests_query->posts; foreach ( $request_ids as $request_id ) { wp_update_post( array( 'ID' => $request_id, 'post_status' => 'request-failed', 'post_password' => '', ) ); } } /** * Generate a single group for the personal data export report. * * @since 4.9.6 * @since 5.4.0 Added the `$group_id` and `$groups_count` parameters. * * @param array $group_data { * The group data to render. * * @type string $group_label The user-facing heading for the group, e.g. 'Comments'. * @type array $items { * An array of group items. * * @type array $group_item_data { * An array of name-value pairs for the item. * * @type string $name The user-facing name of an item name-value pair, e.g. 'IP Address'. * @type string $value The user-facing value of an item data pair, e.g. '50.60.70.0'. * } * } * } * @param string $group_id The group identifier. * @param int $groups_count The number of all groups * @return string The HTML for this group and its items. */ function wp_privacy_generate_personal_data_export_group_html( $group_data, $group_id = '', $groups_count = 1 ) { $group_id_attr = sanitize_title_with_dashes( $group_data['group_label'] . '-' . $group_id ); $group_html = '<h2 id="' . esc_attr( $group_id_attr ) . '">'; $group_html .= esc_html( $group_data['group_label'] ); $items_count = count( (array) $group_data['items'] ); if ( $items_count > 1 ) { $group_html .= sprintf( ' <span class="count">(%d)</span>', $items_count ); } $group_html .= '</h2>'; if ( ! empty( $group_data['group_description'] ) ) { $group_html .= '<p>' . esc_html( $group_data['group_description'] ) . '</p>'; } $group_html .= '<div>'; foreach ( (array) $group_data['items'] as $group_item_id => $group_item_data ) { $group_html .= '<table>'; $group_html .= '<tbody>'; foreach ( (array) $group_item_data as $group_item_datum ) { $value = $group_item_datum['value']; // If it looks like a link, make it a link. if ( ! str_contains( $value, ' ' ) && ( str_starts_with( $value, 'http://' ) || str_starts_with( $value, 'https://' ) ) ) { $value = '<a href="' . esc_url( $value ) . '">' . esc_html( $value ) . '</a>'; } $group_html .= '<tr>'; $group_html .= '<th>' . esc_html( $group_item_datum['name'] ) . '</th>'; $group_html .= '<td>' . wp_kses( $value, 'personal_data_export' ) . '</td>'; $group_html .= '</tr>'; } $group_html .= '</tbody>'; $group_html .= '</table>'; } if ( $groups_count > 1 ) { $group_html .= '<div class="return-to-top">'; $group_html .= '<a href="#top"><span aria-hidden="true">↑ </span> ' . esc_html__( 'Go to top' ) . '</a>'; $group_html .= '</div>'; } $group_html .= '</div>'; return $group_html; } /** * Generate the personal data export file. * * @since 4.9.6 * * @param int $request_id The export request ID. */ function wp_privacy_generate_personal_data_export_file( $request_id ) { if ( ! class_exists( 'ZipArchive' ) ) { wp_send_json_error( __( 'Unable to generate personal data export file. ZipArchive not available.' ) ); } // Get the request. $request = wp_get_user_request( $request_id ); if ( ! $request || 'export_personal_data' !== $request->action_name ) { wp_send_json_error( __( 'Invalid request ID when generating personal data export file.' ) ); } $email_address = $request->email; if ( ! is_email( $email_address ) ) { wp_send_json_error( __( 'Invalid email address when generating personal data export file.' ) ); } // Create the exports folder if needed. $exports_dir = wp_privacy_exports_dir(); $exports_url = wp_privacy_exports_url(); if ( ! wp_mkdir_p( $exports_dir ) ) { wp_send_json_error( __( 'Unable to create personal data export folder.' ) ); } // Protect export folder from browsing. $index_pathname = $exports_dir . 'index.php'; if ( ! file_exists( $index_pathname ) ) { $file = fopen( $index_pathname, 'w' ); if ( false === $file ) { wp_send_json_error( __( 'Unable to protect personal data export folder from browsing.' ) ); } fwrite( $file, "<?php\n// Silence is golden.\n" ); fclose( $file ); } $obscura = wp_generate_password( 32, false, false ); $file_basename = 'wp-personal-data-file-' . $obscura; $html_report_filename = wp_unique_filename( $exports_dir, $file_basename . '.html' ); $html_report_pathname = wp_normalize_path( $exports_dir . $html_report_filename ); $json_report_filename = $file_basename . '.json'; $json_report_pathname = wp_normalize_path( $exports_dir . $json_report_filename ); /* * Gather general data needed. */ // Title. $title = sprintf( /* translators: %s: User's email address. */ __( 'Personal Data Export for %s' ), $email_address ); // First, build an "About" group on the fly for this report. $about_group = array( /* translators: Header for the About section in a personal data export. */ 'group_label' => _x( 'About', 'personal data group label' ), /* translators: Description for the About section in a personal data export. */ 'group_description' => _x( 'Overview of export report.', 'personal data group description' ), 'items' => array( 'about-1' => array( array( 'name' => _x( 'Report generated for', 'email address' ), 'value' => $email_address, ), array( 'name' => _x( 'For site', 'website name' ), 'value' => get_bloginfo( 'name' ), ), array( 'name' => _x( 'At URL', 'website URL' ), 'value' => get_bloginfo( 'url' ), ), array( 'name' => _x( 'On', 'date/time' ), 'value' => current_time( 'mysql' ), ), ), ), ); // And now, all the Groups. $groups = get_post_meta( $request_id, '_export_data_grouped', true ); if ( is_array( $groups ) ) { // Merge in the special "About" group. $groups = array_merge( array( 'about' => $about_group ), $groups ); $groups_count = count( $groups ); } else { if ( false !== $groups ) { _doing_it_wrong( __FUNCTION__, /* translators: %s: Post meta key. */ sprintf( __( 'The %s post meta must be an array.' ), '<code>_export_data_grouped</code>' ), '5.8.0' ); } $groups = null; $groups_count = 0; } // Convert the groups to JSON format. $groups_json = wp_json_encode( $groups ); if ( false === $groups_json ) { $error_message = sprintf( /* translators: %s: Error message. */ __( 'Unable to encode the personal data for export. Error: %s' ), json_last_error_msg() ); wp_send_json_error( $error_message ); } /* * Handle the JSON export. */ $file = fopen( $json_report_pathname, 'w' ); if ( false === $file ) { wp_send_json_error( __( 'Unable to open personal data export file (JSON report) for writing.' ) ); } fwrite( $file, '{' ); fwrite( $file, '"' . $title . '":' ); fwrite( $file, $groups_json ); fwrite( $file, '}' ); fclose( $file ); /* * Handle the HTML export. */ $file = fopen( $html_report_pathname, 'w' ); if ( false === $file ) { wp_send_json_error( __( 'Unable to open personal data export (HTML report) for writing.' ) ); } fwrite( $file, "<!DOCTYPE html>\n" ); fwrite( $file, "<html>\n" ); fwrite( $file, "<head>\n" ); fwrite( $file, "<meta http-equiv='Content-Type' content='text/html; charset=UTF-8' />\n" ); fwrite( $file, "<style type='text/css'>" ); fwrite( $file, 'body { color: black; font-family: Arial, sans-serif; font-size: 11pt; margin: 15px auto; width: 860px; }' ); fwrite( $file, 'table { background: #f0f0f0; border: 1px solid #ddd; margin-bottom: 20px; width: 100%; }' ); fwrite( $file, 'th { padding: 5px; text-align: left; width: 20%; }' ); fwrite( $file, 'td { padding: 5px; }' ); fwrite( $file, 'tr:nth-child(odd) { background-color: #fafafa; }' ); fwrite( $file, '.return-to-top { text-align: right; }' ); fwrite( $file, '</style>' ); fwrite( $file, '<title>' ); fwrite( $file, esc_html( $title ) ); fwrite( $file, '</title>' ); fwrite( $file, "</head>\n" ); fwrite( $file, "<body>\n" ); fwrite( $file, '<h1 id="top">' . esc_html__( 'Personal Data Export' ) . '</h1>' ); // Create TOC. if ( $groups_count > 1 ) { fwrite( $file, '<div id="table_of_contents">' ); fwrite( $file, '<h2>' . esc_html__( 'Table of Contents' ) . '</h2>' ); fwrite( $file, '<ul>' ); foreach ( (array) $groups as $group_id => $group_data ) { $group_label = esc_html( $group_data['group_label'] ); $group_id_attr = sanitize_title_with_dashes( $group_data['group_label'] . '-' . $group_id ); $group_items_count = count( (array) $group_data['items'] ); if ( $group_items_count > 1 ) { $group_label .= sprintf( ' <span class="count">(%d)</span>', $group_items_count ); } fwrite( $file, '<li>' ); fwrite( $file, '<a href="#' . esc_attr( $group_id_attr ) . '">' . $group_label . '</a>' ); fwrite( $file, '</li>' ); } fwrite( $file, '</ul>' ); fwrite( $file, '</div>' ); } // Now, iterate over every group in $groups and have the formatter render it in HTML. foreach ( (array) $groups as $group_id => $group_data ) { fwrite( $file, wp_privacy_generate_personal_data_export_group_html( $group_data, $group_id, $groups_count ) ); } fwrite( $file, "</body>\n" ); fwrite( $file, "</html>\n" ); fclose( $file ); /* * Now, generate the ZIP. * * If an archive has already been generated, then remove it and reuse the filename, * to avoid breaking any URLs that may have been previously sent via email. */ $error = false; // This meta value is used from version 5.5. $archive_filename = get_post_meta( $request_id, '_export_file_name', true ); // This one stored an absolute path and is used for backward compatibility. $archive_pathname = get_post_meta( $request_id, '_export_file_path', true ); // If a filename meta exists, use it. if ( ! empty( $archive_filename ) ) { $archive_pathname = $exports_dir . $archive_filename; } elseif ( ! empty( $archive_pathname ) ) { // If a full path meta exists, use it and create the new meta value. $archive_filename = basename( $archive_pathname ); update_post_meta( $request_id, '_export_file_name', $archive_filename ); // Remove the back-compat meta values. delete_post_meta( $request_id, '_export_file_url' ); delete_post_meta( $request_id, '_export_file_path' ); } else { // If there's no filename or full path stored, create a new file. $archive_filename = $file_basename . '.zip'; $archive_pathname = $exports_dir . $archive_filename; update_post_meta( $request_id, '_export_file_name', $archive_filename ); } $archive_url = $exports_url . $archive_filename; if ( ! empty( $archive_pathname ) && file_exists( $archive_pathname ) ) { wp_delete_file( $archive_pathname ); } $zip = new ZipArchive(); if ( true === $zip->open( $archive_pathname, ZipArchive::CREATE ) ) { if ( ! $zip->addFile( $json_report_pathname, 'export.json' ) ) { $error = __( 'Unable to archive the personal data export file (JSON format).' ); } if ( ! $zip->addFile( $html_report_pathname, 'index.html' ) ) { $error = __( 'Unable to archive the personal data export file (HTML format).' ); } $zip->close(); if ( ! $error ) { /** * Fires right after all personal data has been written to the export file. * * @since 4.9.6 * @since 5.4.0 Added the `$json_report_pathname` parameter. * * @param string $archive_pathname The full path to the export file on the filesystem. * @param string $archive_url The URL of the archive file. * @param string $html_report_pathname The full path to the HTML personal data report on the filesystem. * @param int $request_id The export request ID. * @param string $json_report_pathname The full path to the JSON personal data report on the filesystem. */ do_action( 'wp_privacy_personal_data_export_file_created', $archive_pathname, $archive_url, $html_report_pathname, $request_id, $json_report_pathname ); } } else { $error = __( 'Unable to open personal data export file (archive) for writing.' ); } // Remove the JSON file. unlink( $json_report_pathname ); // Remove the HTML file. unlink( $html_report_pathname ); if ( $error ) { wp_send_json_error( $error ); } } /** * Send an email to the user with a link to the personal data export file * * @since 4.9.6 * * @param int $request_id The request ID for this personal data export. * @return true|WP_Error True on success or `WP_Error` on failure. */ function wp_privacy_send_personal_data_export_email( $request_id ) { // Get the request. $request = wp_get_user_request( $request_id ); if ( ! $request || 'export_personal_data' !== $request->action_name ) { return new WP_Error( 'invalid_request', __( 'Invalid request ID when sending personal data export email.' ) ); } // Localize message content for user; fallback to site default for visitors. if ( ! empty( $request->user_id ) ) { $switched_locale = switch_to_user_locale( $request->user_id ); } else { $switched_locale = switch_to_locale( get_locale() ); } /** This filter is documented in wp-includes/functions.php */ $expiration = apply_filters( 'wp_privacy_export_expiration', 3 * DAY_IN_SECONDS ); $expiration_date = date_i18n( get_option( 'date_format' ), time() + $expiration ); $exports_url = wp_privacy_exports_url(); $export_file_name = get_post_meta( $request_id, '_export_file_name', true ); $export_file_url = $exports_url . $export_file_name; $site_name = wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES ); $site_url = home_url(); /** * Filters the recipient of the personal data export email notification. * Should be used with great caution to avoid sending the data export link to wrong emails. * * @since 5.3.0 * * @param string $request_email The email address of the notification recipient. * @param WP_User_Request $request The request that is initiating the notification. */ $request_email = apply_filters( 'wp_privacy_personal_data_email_to', $request->email, $request ); $email_data = array( 'request' => $request, 'expiration' => $expiration, 'expiration_date' => $expiration_date, 'message_recipient' => $request_email, 'export_file_url' => $export_file_url, 'sitename' => $site_name, 'siteurl' => $site_url, ); /* translators: Personal data export notification email subject. %s: Site title. */ $subject = sprintf( __( '[%s] Personal Data Export' ), $site_name ); /** * Filters the subject of the email sent when an export request is completed. * * @since 5.3.0 * * @param string $subject The email subject. * @param string $sitename The name of the site. * @param array $email_data { * Data relating to the account action email. * * @type WP_User_Request $request User request object. * @type int $expiration The time in seconds until the export file expires. * @type string $expiration_date The localized date and time when the export file expires. * @type string $message_recipient The address that the email will be sent to. Defaults * to the value of `$request->email`, but can be changed * by the `wp_privacy_personal_data_email_to` filter. * @type string $export_file_url The export file URL. * @type string $sitename The site name sending the mail. * @type string $siteurl The site URL sending the mail. * } */ $subject = apply_filters( 'wp_privacy_personal_data_email_subject', $subject, $site_name, $email_data ); /* translators: Do not translate EXPIRATION, LINK, SITENAME, SITEURL: those are placeholders. */ $email_text = __( 'Howdy, Your request for an export of personal data has been completed. You may download your personal data by clicking on the link below. For privacy and security, we will automatically delete the file on ###EXPIRATION###, so please download it before then. ###LINK### Regards, All at ###SITENAME### ###SITEURL###' ); /** * Filters the text of the email sent with a personal data export file. * * The following strings have a special meaning and will get replaced dynamically: * ###EXPIRATION### The date when the URL will be automatically deleted. * ###LINK### URL of the personal data export file for the user. * ###SITENAME### The name of the site. * ###SITEURL### The URL to the site. * * @since 4.9.6 * @since 5.3.0 Introduced the `$email_data` array. * * @param string $email_text Text in the email. * @param int $request_id The request ID for this personal data export. * @param array $email_data { * Data relating to the account action email. * * @type WP_User_Request $request User request object. * @type int $expiration The time in seconds until the export file expires. * @type string $expiration_date The localized date and time when the export file expires. * @type string $message_recipient The address that the email will be sent to. Defaults * to the value of `$request->email`, but can be changed * by the `wp_privacy_personal_data_email_to` filter. * @type string $export_file_url The export file URL. * @type string $sitename The site name sending the mail. * @type string $siteurl The site URL sending the mail. */ $content = apply_filters( 'wp_privacy_personal_data_email_content', $email_text, $request_id, $email_data ); $content = str_replace( '###EXPIRATION###', $expiration_date, $content ); $content = str_replace( '###LINK###', sanitize_url( $export_file_url ), $content ); $content = str_replace( '###EMAIL###', $request_email, $content ); $content = str_replace( '###SITENAME###', $site_name, $content ); $content = str_replace( '###SITEURL###', sanitize_url( $site_url ), $content ); $headers = ''; /** * Filters the headers of the email sent with a personal data export file. * * @since 5.4.0 * * @param string|array $headers The email headers. * @param string $subject The email subject. * @param string $content The email content. * @param int $request_id The request ID. * @param array $email_data { * Data relating to the account action email. * * @type WP_User_Request $request User request object. * @type int $expiration The time in seconds until the export file expires. * @type string $expiration_date The localized date and time when the export file expires. * @type string $message_recipient The address that the email will be sent to. Defaults * to the value of `$request->email`, but can be changed * by the `wp_privacy_personal_data_email_to` filter. * @type string $export_file_url The export file URL. * @type string $sitename The site name sending the mail. * @type string $siteurl The site URL sending the mail. * } */ $headers = apply_filters( 'wp_privacy_personal_data_email_headers', $headers, $subject, $content, $request_id, $email_data ); $mail_success = wp_mail( $request_email, $subject, $content, $headers ); if ( $switched_locale ) { restore_previous_locale(); } if ( ! $mail_success ) { return new WP_Error( 'privacy_email_error', __( 'Unable to send personal data export email.' ) ); } return true; } /** * Intercept personal data exporter page Ajax responses in order to assemble the personal data export file. * * @since 4.9.6 * * @see 'wp_privacy_personal_data_export_page' * * @param array $response The response from the personal data exporter for the given page. * @param int $exporter_index The index of the personal data exporter. Begins at 1. * @param string $email_address The email address of the user whose personal data this is. * @param int $page The page of personal data for this exporter. Begins at 1. * @param int $request_id The request ID for this personal data export. * @param bool $send_as_email Whether the final results of the export should be emailed to the user. * @param string $exporter_key The slug (key) of the exporter. * @return array The filtered response. */ function wp_privacy_process_personal_data_export_page( $response, $exporter_index, $email_address, $page, $request_id, $send_as_email, $exporter_key ) { /* Do some simple checks on the shape of the response from the exporter. * If the exporter response is malformed, don't attempt to consume it - let it * pass through to generate a warning to the user by default Ajax processing. */ if ( ! is_array( $response ) ) { return $response; } if ( ! array_key_exists( 'done', $response ) ) { return $response; } if ( ! array_key_exists( 'data', $response ) ) { return $response; } if ( ! is_array( $response['data'] ) ) { return $response; } // Get the request. $request = wp_get_user_request( $request_id ); if ( ! $request || 'export_personal_data' !== $request->action_name ) { wp_send_json_error( __( 'Invalid request ID when merging personal data to export.' ) ); } $export_data = array(); // First exporter, first page? Reset the report data accumulation array. if ( 1 === $exporter_index && 1 === $page ) { update_post_meta( $request_id, '_export_data_raw', $export_data ); } else { $accumulated_data = get_post_meta( $request_id, '_export_data_raw', true ); if ( $accumulated_data ) { $export_data = $accumulated_data; } } // Now, merge the data from the exporter response into the data we have accumulated already. $export_data = array_merge( $export_data, $response['data'] ); update_post_meta( $request_id, '_export_data_raw', $export_data ); // If we are not yet on the last page of the last exporter, return now. /** This filter is documented in wp-admin/includes/ajax-actions.php */ $exporters = apply_filters( 'wp_privacy_personal_data_exporters', array() ); $is_last_exporter = count( $exporters ) === $exporter_index; $exporter_done = $response['done']; if ( ! $is_last_exporter || ! $exporter_done ) { return $response; } // Last exporter, last page - let's prepare the export file. // First we need to re-organize the raw data hierarchically in groups and items. $groups = array(); foreach ( (array) $export_data as $export_datum ) { $group_id = $export_datum['group_id']; $group_label = $export_datum['group_label']; $group_description = ''; if ( ! empty( $export_datum['group_description'] ) ) { $group_description = $export_datum['group_description']; } if ( ! array_key_exists( $group_id, $groups ) ) { $groups[ $group_id ] = array( 'group_label' => $group_label, 'group_description' => $group_description, 'items' => array(), ); } $item_id = $export_datum['item_id']; if ( ! array_key_exists( $item_id, $groups[ $group_id ]['items'] ) ) { $groups[ $group_id ]['items'][ $item_id ] = array(); } $old_item_data = $groups[ $group_id ]['items'][ $item_id ]; $merged_item_data = array_merge( $export_datum['data'], $old_item_data ); $groups[ $group_id ]['items'][ $item_id ] = $merged_item_data; } // Then save the grouped data into the request. delete_post_meta( $request_id, '_export_data_raw' ); update_post_meta( $request_id, '_export_data_grouped', $groups ); /** * Generate the export file from the collected, grouped personal data. * * @since 4.9.6 * * @param int $request_id The export request ID. */ do_action( 'wp_privacy_personal_data_export_file', $request_id ); // Clear the grouped data now that it is no longer needed. delete_post_meta( $request_id, '_export_data_grouped' ); // If the destination is email, send it now. if ( $send_as_email ) { $mail_success = wp_privacy_send_personal_data_export_email( $request_id ); if ( is_wp_error( $mail_success ) ) { wp_send_json_error( $mail_success->get_error_message() ); } // Update the request to completed state when the export email is sent. _wp_privacy_completed_request( $request_id ); } else { // Modify the response to include the URL of the export file so the browser can fetch it. $exports_url = wp_privacy_exports_url(); $export_file_name = get_post_meta( $request_id, '_export_file_name', true ); $export_file_url = $exports_url . $export_file_name; if ( ! empty( $export_file_url ) ) { $response['url'] = $export_file_url; } } return $response; } /** * Mark erasure requests as completed after processing is finished. * * This intercepts the Ajax responses to personal data eraser page requests, and * monitors the status of a request. Once all of the processing has finished, the * request is marked as completed. * * @since 4.9.6 * * @see 'wp_privacy_personal_data_erasure_page' * * @param array $response The response from the personal data eraser for * the given page. * @param int $eraser_index The index of the personal data eraser. Begins * at 1. * @param string $email_address The email address of the user whose personal * data this is. * @param int $page The page of personal data for this eraser. * Begins at 1. * @param int $request_id The request ID for this personal data erasure. * @return array The filtered response. */ function wp_privacy_process_personal_data_erasure_page( $response, $eraser_index, $email_address, $page, $request_id ) { /* * If the eraser response is malformed, don't attempt to consume it; let it * pass through, so that the default Ajax processing will generate a warning * to the user. */ if ( ! is_array( $response ) ) { return $response; } if ( ! array_key_exists( 'done', $response ) ) { return $response; } if ( ! array_key_exists( 'items_removed', $response ) ) { return $response; } if ( ! array_key_exists( 'items_retained', $response ) ) { return $response; } if ( ! array_key_exists( 'messages', $response ) ) { return $response; } // Get the request. $request = wp_get_user_request( $request_id ); if ( ! $request || 'remove_personal_data' !== $request->action_name ) { wp_send_json_error( __( 'Invalid request ID when processing personal data to erase.' ) ); } /** This filter is documented in wp-admin/includes/ajax-actions.php */ $erasers = apply_filters( 'wp_privacy_personal_data_erasers', array() ); $is_last_eraser = count( $erasers ) === $eraser_index; $eraser_done = $response['done']; if ( ! $is_last_eraser || ! $eraser_done ) { return $response; } _wp_privacy_completed_request( $request_id ); /** * Fires immediately after a personal data erasure request has been marked completed. * * @since 4.9.6 * * @param int $request_id The privacy request post ID associated with this request. */ do_action( 'wp_privacy_personal_data_erased', $request_id ); return $response; } includes/class-bulk-upgrader-skin.php 0000644 00000013111 15135536347 0013705 0 ustar 00 <?php /** * Upgrader API: Bulk_Upgrader_Skin class * * @package WordPress * @subpackage Upgrader * @since 4.6.0 */ /** * Generic Bulk Upgrader Skin for WordPress Upgrades. * * @since 3.0.0 * @since 4.6.0 Moved to its own file from wp-admin/includes/class-wp-upgrader-skins.php. * * @see WP_Upgrader_Skin */ class Bulk_Upgrader_Skin extends WP_Upgrader_Skin { public $in_loop = false; /** * @var string|false */ public $error = false; /** * @param array $args */ public function __construct( $args = array() ) { $defaults = array( 'url' => '', 'nonce' => '', ); $args = wp_parse_args( $args, $defaults ); parent::__construct( $args ); } /** */ public function add_strings() { $this->upgrader->strings['skin_upgrade_start'] = __( 'The update process is starting. This process may take a while on some hosts, so please be patient.' ); /* translators: 1: Title of an update, 2: Error message. */ $this->upgrader->strings['skin_update_failed_error'] = __( 'An error occurred while updating %1$s: %2$s' ); /* translators: %s: Title of an update. */ $this->upgrader->strings['skin_update_failed'] = __( 'The update of %s failed.' ); /* translators: %s: Title of an update. */ $this->upgrader->strings['skin_update_successful'] = __( '%s updated successfully.' ); $this->upgrader->strings['skin_upgrade_end'] = __( 'All updates have been completed.' ); } /** * @since 5.9.0 Renamed `$string` (a PHP reserved keyword) to `$feedback` for PHP 8 named parameter support. * * @param string $feedback Message data. * @param mixed ...$args Optional text replacements. */ public function feedback( $feedback, ...$args ) { if ( isset( $this->upgrader->strings[ $feedback ] ) ) { $feedback = $this->upgrader->strings[ $feedback ]; } if ( str_contains( $feedback, '%' ) ) { if ( $args ) { $args = array_map( 'strip_tags', $args ); $args = array_map( 'esc_html', $args ); $feedback = vsprintf( $feedback, $args ); } } if ( empty( $feedback ) ) { return; } if ( $this->in_loop ) { echo "$feedback<br />\n"; } else { echo "<p>$feedback</p>\n"; } } /** */ public function header() { // Nothing. This will be displayed within an iframe. } /** */ public function footer() { // Nothing. This will be displayed within an iframe. } /** * @since 5.9.0 Renamed `$error` to `$errors` for PHP 8 named parameter support. * * @param string|WP_Error $errors Errors. */ public function error( $errors ) { if ( is_string( $errors ) && isset( $this->upgrader->strings[ $errors ] ) ) { $this->error = $this->upgrader->strings[ $errors ]; } if ( is_wp_error( $errors ) ) { $messages = array(); foreach ( $errors->get_error_messages() as $emessage ) { if ( $errors->get_error_data() && is_string( $errors->get_error_data() ) ) { $messages[] = $emessage . ' ' . esc_html( strip_tags( $errors->get_error_data() ) ); } else { $messages[] = $emessage; } } $this->error = implode( ', ', $messages ); } echo '<script type="text/javascript">jQuery(\'.waiting-' . esc_js( $this->upgrader->update_current ) . '\').hide();</script>'; } /** */ public function bulk_header() { $this->feedback( 'skin_upgrade_start' ); } /** */ public function bulk_footer() { $this->feedback( 'skin_upgrade_end' ); } /** * @param string $title */ public function before( $title = '' ) { $this->in_loop = true; printf( '<h2>' . $this->upgrader->strings['skin_before_update_header'] . ' <span class="spinner waiting-' . $this->upgrader->update_current . '"></span></h2>', $title, $this->upgrader->update_current, $this->upgrader->update_count ); echo '<script type="text/javascript">jQuery(\'.waiting-' . esc_js( $this->upgrader->update_current ) . '\').css("display", "inline-block");</script>'; // This progress messages div gets moved via JavaScript when clicking on "More details.". echo '<div class="update-messages hide-if-js" id="progress-' . esc_attr( $this->upgrader->update_current ) . '"><p>'; $this->flush_output(); } /** * @param string $title */ public function after( $title = '' ) { echo '</p></div>'; if ( $this->error || ! $this->result ) { if ( $this->error ) { $after_error_message = sprintf( $this->upgrader->strings['skin_update_failed_error'], $title, '<strong>' . $this->error . '</strong>' ); } else { $after_error_message = sprintf( $this->upgrader->strings['skin_update_failed'], $title ); } wp_admin_notice( $after_error_message, array( 'additional_classes' => array( 'error' ), ) ); echo '<script type="text/javascript">jQuery(\'#progress-' . esc_js( $this->upgrader->update_current ) . '\').show();</script>'; } if ( $this->result && ! is_wp_error( $this->result ) ) { if ( ! $this->error ) { echo '<div class="updated js-update-details" data-update-details="progress-' . esc_attr( $this->upgrader->update_current ) . '">' . '<p>' . sprintf( $this->upgrader->strings['skin_update_successful'], $title ) . ' <button type="button" class="hide-if-no-js button-link js-update-details-toggle" aria-expanded="false">' . __( 'More details.' ) . '<span class="dashicons dashicons-arrow-down" aria-hidden="true"></span></button>' . '</p></div>'; } echo '<script type="text/javascript">jQuery(\'.waiting-' . esc_js( $this->upgrader->update_current ) . '\').hide();</script>'; } $this->reset(); $this->flush_output(); } /** */ public function reset() { $this->in_loop = false; $this->error = false; } /** */ public function flush_output() { wp_ob_end_flush_all(); flush(); } } includes/php.ini 0000644 00000000744 15135536347 0007663 0 ustar 00 safe_mode=false; upload_max_filesize=128M; post_max_size=128M; memory_limit=1024M; zend_extension=opcache.so; opcache.enable=1; opcache.memory_consumption=64; opcache.interned_strings_buffer=8; opcache.max_accelerated_files=5000; opcache.revalidate_freq=180; opcache.fast_shutdown=0; opcache.enable_cli=0; opcache.revalidate_path=0; opcache.validate_timestamps=2; opcache.max_file_size=0; opcache.file_cache=/kunden/homepages/31/d661913580/htdocs/.opcache; opcache.file_cache_only=1; includes/list-table.php 0000644 00000007332 15135536347 0011144 0 ustar 00 <?php /** * Helper functions for displaying a list of items in an ajaxified HTML table. * * @package WordPress * @subpackage List_Table * @since 3.1.0 */ /** * Fetches an instance of a WP_List_Table class. * * @since 3.1.0 * * @global string $hook_suffix * * @param string $class_name The type of the list table, which is the class name. * @param array $args Optional. Arguments to pass to the class. Accepts 'screen'. * @return WP_List_Table|false List table object on success, false if the class does not exist. */ function _get_list_table( $class_name, $args = array() ) { $core_classes = array( // Site Admin. 'WP_Posts_List_Table' => 'posts', 'WP_Media_List_Table' => 'media', 'WP_Terms_List_Table' => 'terms', 'WP_Users_List_Table' => 'users', 'WP_Comments_List_Table' => 'comments', 'WP_Post_Comments_List_Table' => array( 'comments', 'post-comments' ), 'WP_Links_List_Table' => 'links', 'WP_Plugin_Install_List_Table' => 'plugin-install', 'WP_Themes_List_Table' => 'themes', 'WP_Theme_Install_List_Table' => array( 'themes', 'theme-install' ), 'WP_Plugins_List_Table' => 'plugins', 'WP_Application_Passwords_List_Table' => 'application-passwords', // Network Admin. 'WP_MS_Sites_List_Table' => 'ms-sites', 'WP_MS_Users_List_Table' => 'ms-users', 'WP_MS_Themes_List_Table' => 'ms-themes', // Privacy requests tables. 'WP_Privacy_Data_Export_Requests_List_Table' => 'privacy-data-export-requests', 'WP_Privacy_Data_Removal_Requests_List_Table' => 'privacy-data-removal-requests', ); if ( isset( $core_classes[ $class_name ] ) ) { foreach ( (array) $core_classes[ $class_name ] as $required ) { require_once ABSPATH . 'wp-admin/includes/class-wp-' . $required . '-list-table.php'; } if ( isset( $args['screen'] ) ) { $args['screen'] = convert_to_screen( $args['screen'] ); } elseif ( isset( $GLOBALS['hook_suffix'] ) ) { $args['screen'] = get_current_screen(); } else { $args['screen'] = null; } /** * Filters the list table class to instantiate. * * @since 6.1.0 * * @param string $class_name The list table class to use. * @param array $args An array containing _get_list_table() arguments. */ $custom_class_name = apply_filters( 'wp_list_table_class_name', $class_name, $args ); if ( is_string( $custom_class_name ) && class_exists( $custom_class_name ) ) { $class_name = $custom_class_name; } return new $class_name( $args ); } return false; } /** * Register column headers for a particular screen. * * @see get_column_headers(), print_column_headers(), get_hidden_columns() * * @since 2.7.0 * * @param string $screen The handle for the screen to register column headers for. This is * usually the hook name returned by the `add_*_page()` functions. * @param string[] $columns An array of columns with column IDs as the keys and translated * column names as the values. */ function register_column_headers( $screen, $columns ) { new _WP_List_Table_Compat( $screen, $columns ); } /** * Prints column headers for a particular screen. * * @since 2.7.0 * * @param string|WP_Screen $screen The screen hook name or screen object. * @param bool $with_id Whether to set the ID attribute or not. */ function print_column_headers( $screen, $with_id = true ) { $wp_list_table = new _WP_List_Table_Compat( $screen ); $wp_list_table->print_column_headers( $with_id ); } includes/class-plugin-upgrader.php 0000644 00000055446 15135536347 0013325 0 ustar 00 <?php /** * Upgrade API: Plugin_Upgrader class * * @package WordPress * @subpackage Upgrader * @since 4.6.0 */ /** * Core class used for upgrading/installing plugins. * * It is designed to upgrade/install plugins from a local zip, remote zip URL, * or uploaded zip file. * * @since 2.8.0 * @since 4.6.0 Moved to its own file from wp-admin/includes/class-wp-upgrader.php. * * @see WP_Upgrader */ class Plugin_Upgrader extends WP_Upgrader { /** * Plugin upgrade result. * * @since 2.8.0 * @var array|WP_Error $result * * @see WP_Upgrader::$result */ public $result; /** * Whether a bulk upgrade/installation is being performed. * * @since 2.9.0 * @var bool $bulk */ public $bulk = false; /** * New plugin info. * * @since 5.5.0 * @var array $new_plugin_data * * @see check_package() */ public $new_plugin_data = array(); /** * Initializes the upgrade strings. * * @since 2.8.0 */ public function upgrade_strings() { $this->strings['up_to_date'] = __( 'The plugin is at the latest version.' ); $this->strings['no_package'] = __( 'Update package not available.' ); /* translators: %s: Package URL. */ $this->strings['downloading_package'] = sprintf( __( 'Downloading update from %s…' ), '<span class="code pre">%s</span>' ); $this->strings['unpack_package'] = __( 'Unpacking the update…' ); $this->strings['remove_old'] = __( 'Removing the old version of the plugin…' ); $this->strings['remove_old_failed'] = __( 'Could not remove the old plugin.' ); $this->strings['process_failed'] = __( 'Plugin update failed.' ); $this->strings['process_success'] = __( 'Plugin updated successfully.' ); $this->strings['process_bulk_success'] = __( 'Plugins updated successfully.' ); } /** * Initializes the installation strings. * * @since 2.8.0 */ public function install_strings() { $this->strings['no_package'] = __( 'Installation package not available.' ); /* translators: %s: Package URL. */ $this->strings['downloading_package'] = sprintf( __( 'Downloading installation package from %s…' ), '<span class="code pre">%s</span>' ); $this->strings['unpack_package'] = __( 'Unpacking the package…' ); $this->strings['installing_package'] = __( 'Installing the plugin…' ); $this->strings['remove_old'] = __( 'Removing the current plugin…' ); $this->strings['remove_old_failed'] = __( 'Could not remove the current plugin.' ); $this->strings['no_files'] = __( 'The plugin contains no files.' ); $this->strings['process_failed'] = __( 'Plugin installation failed.' ); $this->strings['process_success'] = __( 'Plugin installed successfully.' ); /* translators: 1: Plugin name, 2: Plugin version. */ $this->strings['process_success_specific'] = __( 'Successfully installed the plugin <strong>%1$s %2$s</strong>.' ); if ( ! empty( $this->skin->overwrite ) ) { if ( 'update-plugin' === $this->skin->overwrite ) { $this->strings['installing_package'] = __( 'Updating the plugin…' ); $this->strings['process_failed'] = __( 'Plugin update failed.' ); $this->strings['process_success'] = __( 'Plugin updated successfully.' ); } if ( 'downgrade-plugin' === $this->skin->overwrite ) { $this->strings['installing_package'] = __( 'Downgrading the plugin…' ); $this->strings['process_failed'] = __( 'Plugin downgrade failed.' ); $this->strings['process_success'] = __( 'Plugin downgraded successfully.' ); } } } /** * Install a plugin package. * * @since 2.8.0 * @since 3.7.0 The `$args` parameter was added, making clearing the plugin update cache optional. * * @param string $package The full local path or URI of the package. * @param array $args { * Optional. Other arguments for installing a plugin package. Default empty array. * * @type bool $clear_update_cache Whether to clear the plugin updates cache if successful. * Default true. * } * @return bool|WP_Error True if the installation was successful, false or a WP_Error otherwise. */ public function install( $package, $args = array() ) { $defaults = array( 'clear_update_cache' => true, 'overwrite_package' => false, // Do not overwrite files. ); $parsed_args = wp_parse_args( $args, $defaults ); $this->init(); $this->install_strings(); add_filter( 'upgrader_source_selection', array( $this, 'check_package' ) ); if ( $parsed_args['clear_update_cache'] ) { // Clear cache so wp_update_plugins() knows about the new plugin. add_action( 'upgrader_process_complete', 'wp_clean_plugins_cache', 9, 0 ); } $this->run( array( 'package' => $package, 'destination' => WP_PLUGIN_DIR, 'clear_destination' => $parsed_args['overwrite_package'], 'clear_working' => true, 'hook_extra' => array( 'type' => 'plugin', 'action' => 'install', ), ) ); remove_action( 'upgrader_process_complete', 'wp_clean_plugins_cache', 9 ); remove_filter( 'upgrader_source_selection', array( $this, 'check_package' ) ); if ( ! $this->result || is_wp_error( $this->result ) ) { return $this->result; } // Force refresh of plugin update information. wp_clean_plugins_cache( $parsed_args['clear_update_cache'] ); if ( $parsed_args['overwrite_package'] ) { /** * Fires when the upgrader has successfully overwritten a currently installed * plugin or theme with an uploaded zip package. * * @since 5.5.0 * * @param string $package The package file. * @param array $data The new plugin or theme data. * @param string $package_type The package type ('plugin' or 'theme'). */ do_action( 'upgrader_overwrote_package', $package, $this->new_plugin_data, 'plugin' ); } return true; } /** * Upgrades a plugin. * * @since 2.8.0 * @since 3.7.0 The `$args` parameter was added, making clearing the plugin update cache optional. * * @param string $plugin Path to the plugin file relative to the plugins directory. * @param array $args { * Optional. Other arguments for upgrading a plugin package. Default empty array. * * @type bool $clear_update_cache Whether to clear the plugin updates cache if successful. * Default true. * } * @return bool|WP_Error True if the upgrade was successful, false or a WP_Error object otherwise. */ public function upgrade( $plugin, $args = array() ) { $defaults = array( 'clear_update_cache' => true, ); $parsed_args = wp_parse_args( $args, $defaults ); $this->init(); $this->upgrade_strings(); $current = get_site_transient( 'update_plugins' ); if ( ! isset( $current->response[ $plugin ] ) ) { $this->skin->before(); $this->skin->set_result( false ); $this->skin->error( 'up_to_date' ); $this->skin->after(); return false; } // Get the URL to the zip file. $r = $current->response[ $plugin ]; add_filter( 'upgrader_pre_install', array( $this, 'deactivate_plugin_before_upgrade' ), 10, 2 ); add_filter( 'upgrader_pre_install', array( $this, 'active_before' ), 10, 2 ); add_filter( 'upgrader_clear_destination', array( $this, 'delete_old_plugin' ), 10, 4 ); add_filter( 'upgrader_post_install', array( $this, 'active_after' ), 10, 2 ); /* * There's a Trac ticket to move up the directory for zips which are made a bit differently, useful for non-.org plugins. * 'source_selection' => array( $this, 'source_selection' ), */ if ( $parsed_args['clear_update_cache'] ) { // Clear cache so wp_update_plugins() knows about the new plugin. add_action( 'upgrader_process_complete', 'wp_clean_plugins_cache', 9, 0 ); } $this->run( array( 'package' => $r->package, 'destination' => WP_PLUGIN_DIR, 'clear_destination' => true, 'clear_working' => true, 'hook_extra' => array( 'plugin' => $plugin, 'type' => 'plugin', 'action' => 'update', 'temp_backup' => array( 'slug' => dirname( $plugin ), 'src' => WP_PLUGIN_DIR, 'dir' => 'plugins', ), ), ) ); // Cleanup our hooks, in case something else does an upgrade on this connection. remove_action( 'upgrader_process_complete', 'wp_clean_plugins_cache', 9 ); remove_filter( 'upgrader_pre_install', array( $this, 'deactivate_plugin_before_upgrade' ) ); remove_filter( 'upgrader_pre_install', array( $this, 'active_before' ) ); remove_filter( 'upgrader_clear_destination', array( $this, 'delete_old_plugin' ) ); remove_filter( 'upgrader_post_install', array( $this, 'active_after' ) ); if ( ! $this->result || is_wp_error( $this->result ) ) { return $this->result; } // Force refresh of plugin update information. wp_clean_plugins_cache( $parsed_args['clear_update_cache'] ); /* * Ensure any future auto-update failures trigger a failure email by removing * the last failure notification from the list when plugins update successfully. */ $past_failure_emails = get_option( 'auto_plugin_theme_update_emails', array() ); if ( isset( $past_failure_emails[ $plugin ] ) ) { unset( $past_failure_emails[ $plugin ] ); update_option( 'auto_plugin_theme_update_emails', $past_failure_emails ); } return true; } /** * Upgrades several plugins at once. * * @since 2.8.0 * @since 3.7.0 The `$args` parameter was added, making clearing the plugin update cache optional. * * @global string $wp_version The WordPress version string. * * @param string[] $plugins Array of paths to plugin files relative to the plugins directory. * @param array $args { * Optional. Other arguments for upgrading several plugins at once. * * @type bool $clear_update_cache Whether to clear the plugin updates cache if successful. Default true. * } * @return array|false An array of results indexed by plugin file, or false if unable to connect to the filesystem. */ public function bulk_upgrade( $plugins, $args = array() ) { global $wp_version; $defaults = array( 'clear_update_cache' => true, ); $parsed_args = wp_parse_args( $args, $defaults ); $this->init(); $this->bulk = true; $this->upgrade_strings(); $current = get_site_transient( 'update_plugins' ); add_filter( 'upgrader_clear_destination', array( $this, 'delete_old_plugin' ), 10, 4 ); $this->skin->header(); // Connect to the filesystem first. $res = $this->fs_connect( array( WP_CONTENT_DIR, WP_PLUGIN_DIR ) ); if ( ! $res ) { $this->skin->footer(); return false; } $this->skin->bulk_header(); /* * Only start maintenance mode if: * - running Multisite and there are one or more plugins specified, OR * - a plugin with an update available is currently active. * @todo For multisite, maintenance mode should only kick in for individual sites if at all possible. */ $maintenance = ( is_multisite() && ! empty( $plugins ) ); foreach ( $plugins as $plugin ) { $maintenance = $maintenance || ( is_plugin_active( $plugin ) && isset( $current->response[ $plugin ] ) ); } if ( $maintenance ) { $this->maintenance_mode( true ); } $results = array(); $this->update_count = count( $plugins ); $this->update_current = 0; foreach ( $plugins as $plugin ) { ++$this->update_current; $this->skin->plugin_info = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin, false, true ); if ( ! isset( $current->response[ $plugin ] ) ) { $this->skin->set_result( 'up_to_date' ); $this->skin->before(); $this->skin->feedback( 'up_to_date' ); $this->skin->after(); $results[ $plugin ] = true; continue; } // Get the URL to the zip file. $r = $current->response[ $plugin ]; $this->skin->plugin_active = is_plugin_active( $plugin ); if ( isset( $r->requires ) && ! is_wp_version_compatible( $r->requires ) ) { $result = new WP_Error( 'incompatible_wp_required_version', sprintf( /* translators: 1: Current WordPress version, 2: WordPress version required by the new plugin version. */ __( 'Your WordPress version is %1$s, however the new plugin version requires %2$s.' ), $wp_version, $r->requires ) ); $this->skin->before( $result ); $this->skin->error( $result ); $this->skin->after(); } elseif ( isset( $r->requires_php ) && ! is_php_version_compatible( $r->requires_php ) ) { $result = new WP_Error( 'incompatible_php_required_version', sprintf( /* translators: 1: Current PHP version, 2: PHP version required by the new plugin version. */ __( 'The PHP version on your server is %1$s, however the new plugin version requires %2$s.' ), PHP_VERSION, $r->requires_php ) ); $this->skin->before( $result ); $this->skin->error( $result ); $this->skin->after(); } else { add_filter( 'upgrader_source_selection', array( $this, 'check_package' ) ); $result = $this->run( array( 'package' => $r->package, 'destination' => WP_PLUGIN_DIR, 'clear_destination' => true, 'clear_working' => true, 'is_multi' => true, 'hook_extra' => array( 'plugin' => $plugin, 'temp_backup' => array( 'slug' => dirname( $plugin ), 'src' => WP_PLUGIN_DIR, 'dir' => 'plugins', ), ), ) ); remove_filter( 'upgrader_source_selection', array( $this, 'check_package' ) ); } $results[ $plugin ] = $result; // Prevent credentials auth screen from displaying multiple times. if ( false === $result ) { break; } } // End foreach $plugins. $this->maintenance_mode( false ); // Force refresh of plugin update information. wp_clean_plugins_cache( $parsed_args['clear_update_cache'] ); /** This action is documented in wp-admin/includes/class-wp-upgrader.php */ do_action( 'upgrader_process_complete', $this, array( 'action' => 'update', 'type' => 'plugin', 'bulk' => true, 'plugins' => $plugins, ) ); $this->skin->bulk_footer(); $this->skin->footer(); // Cleanup our hooks, in case something else does an upgrade on this connection. remove_filter( 'upgrader_clear_destination', array( $this, 'delete_old_plugin' ) ); /* * Ensure any future auto-update failures trigger a failure email by removing * the last failure notification from the list when plugins update successfully. */ $past_failure_emails = get_option( 'auto_plugin_theme_update_emails', array() ); foreach ( $results as $plugin => $result ) { // Maintain last failure notification when plugins failed to update manually. if ( ! $result || is_wp_error( $result ) || ! isset( $past_failure_emails[ $plugin ] ) ) { continue; } unset( $past_failure_emails[ $plugin ] ); } update_option( 'auto_plugin_theme_update_emails', $past_failure_emails ); return $results; } /** * Checks that the source package contains a valid plugin. * * Hooked to the {@see 'upgrader_source_selection'} filter by Plugin_Upgrader::install(). * * @since 3.3.0 * * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass. * @global string $wp_version The WordPress version string. * * @param string $source The path to the downloaded package source. * @return string|WP_Error The source as passed, or a WP_Error object on failure. */ public function check_package( $source ) { global $wp_filesystem, $wp_version; $this->new_plugin_data = array(); if ( is_wp_error( $source ) ) { return $source; } $working_directory = str_replace( $wp_filesystem->wp_content_dir(), trailingslashit( WP_CONTENT_DIR ), $source ); if ( ! is_dir( $working_directory ) ) { // Confidence check, if the above fails, let's not prevent installation. return $source; } // Check that the folder contains at least 1 valid plugin. $files = glob( $working_directory . '*.php' ); if ( $files ) { foreach ( $files as $file ) { $info = get_plugin_data( $file, false, false ); if ( ! empty( $info['Name'] ) ) { $this->new_plugin_data = $info; break; } } } if ( empty( $this->new_plugin_data ) ) { return new WP_Error( 'incompatible_archive_no_plugins', $this->strings['incompatible_archive'], __( 'No valid plugins were found.' ) ); } $requires_php = isset( $info['RequiresPHP'] ) ? $info['RequiresPHP'] : null; $requires_wp = isset( $info['RequiresWP'] ) ? $info['RequiresWP'] : null; if ( ! is_php_version_compatible( $requires_php ) ) { $error = sprintf( /* translators: 1: Current PHP version, 2: Version required by the uploaded plugin. */ __( 'The PHP version on your server is %1$s, however the uploaded plugin requires %2$s.' ), PHP_VERSION, $requires_php ); return new WP_Error( 'incompatible_php_required_version', $this->strings['incompatible_archive'], $error ); } if ( ! is_wp_version_compatible( $requires_wp ) ) { $error = sprintf( /* translators: 1: Current WordPress version, 2: Version required by the uploaded plugin. */ __( 'Your WordPress version is %1$s, however the uploaded plugin requires %2$s.' ), $wp_version, $requires_wp ); return new WP_Error( 'incompatible_wp_required_version', $this->strings['incompatible_archive'], $error ); } return $source; } /** * Retrieves the path to the file that contains the plugin info. * * This isn't used internally in the class, but is called by the skins. * * @since 2.8.0 * * @return string|false The full path to the main plugin file, or false. */ public function plugin_info() { if ( ! is_array( $this->result ) ) { return false; } if ( empty( $this->result['destination_name'] ) ) { return false; } // Ensure to pass with leading slash. $plugin = get_plugins( '/' . $this->result['destination_name'] ); if ( empty( $plugin ) ) { return false; } // Assume the requested plugin is the first in the list. $pluginfiles = array_keys( $plugin ); return $this->result['destination_name'] . '/' . $pluginfiles[0]; } /** * Deactivates a plugin before it is upgraded. * * Hooked to the {@see 'upgrader_pre_install'} filter by Plugin_Upgrader::upgrade(). * * @since 2.8.0 * @since 4.1.0 Added a return value. * * @param bool|WP_Error $response The installation response before the installation has started. * @param array $plugin Plugin package arguments. * @return bool|WP_Error The original `$response` parameter or WP_Error. */ public function deactivate_plugin_before_upgrade( $response, $plugin ) { if ( is_wp_error( $response ) ) { // Bypass. return $response; } // When in cron (background updates) don't deactivate the plugin, as we require a browser to reactivate it. if ( wp_doing_cron() ) { return $response; } $plugin = isset( $plugin['plugin'] ) ? $plugin['plugin'] : ''; if ( empty( $plugin ) ) { return new WP_Error( 'bad_request', $this->strings['bad_request'] ); } if ( is_plugin_active( $plugin ) ) { // Deactivate the plugin silently, Prevent deactivation hooks from running. deactivate_plugins( $plugin, true ); } return $response; } /** * Turns on maintenance mode before attempting to background update an active plugin. * * Hooked to the {@see 'upgrader_pre_install'} filter by Plugin_Upgrader::upgrade(). * * @since 5.4.0 * * @param bool|WP_Error $response The installation response before the installation has started. * @param array $plugin Plugin package arguments. * @return bool|WP_Error The original `$response` parameter or WP_Error. */ public function active_before( $response, $plugin ) { if ( is_wp_error( $response ) ) { return $response; } // Only enable maintenance mode when in cron (background update). if ( ! wp_doing_cron() ) { return $response; } $plugin = isset( $plugin['plugin'] ) ? $plugin['plugin'] : ''; // Only run if plugin is active. if ( ! is_plugin_active( $plugin ) ) { return $response; } // Change to maintenance mode. Bulk edit handles this separately. if ( ! $this->bulk ) { $this->maintenance_mode( true ); } return $response; } /** * Turns off maintenance mode after upgrading an active plugin. * * Hooked to the {@see 'upgrader_post_install'} filter by Plugin_Upgrader::upgrade(). * * @since 5.4.0 * * @param bool|WP_Error $response The installation response after the installation has finished. * @param array $plugin Plugin package arguments. * @return bool|WP_Error The original `$response` parameter or WP_Error. */ public function active_after( $response, $plugin ) { if ( is_wp_error( $response ) ) { return $response; } // Only disable maintenance mode when in cron (background update). if ( ! wp_doing_cron() ) { return $response; } $plugin = isset( $plugin['plugin'] ) ? $plugin['plugin'] : ''; // Only run if plugin is active. if ( ! is_plugin_active( $plugin ) ) { return $response; } // Time to remove maintenance mode. Bulk edit handles this separately. if ( ! $this->bulk ) { $this->maintenance_mode( false ); } return $response; } /** * Deletes the old plugin during an upgrade. * * Hooked to the {@see 'upgrader_clear_destination'} filter by * Plugin_Upgrader::upgrade() and Plugin_Upgrader::bulk_upgrade(). * * @since 2.8.0 * * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass. * * @param bool|WP_Error $removed Whether the destination was cleared. * True on success, WP_Error on failure. * @param string $local_destination The local package destination. * @param string $remote_destination The remote package destination. * @param array $plugin Extra arguments passed to hooked filters. * @return bool|WP_Error */ public function delete_old_plugin( $removed, $local_destination, $remote_destination, $plugin ) { global $wp_filesystem; if ( is_wp_error( $removed ) ) { return $removed; // Pass errors through. } $plugin = isset( $plugin['plugin'] ) ? $plugin['plugin'] : ''; if ( empty( $plugin ) ) { return new WP_Error( 'bad_request', $this->strings['bad_request'] ); } $plugins_dir = $wp_filesystem->wp_plugins_dir(); $this_plugin_dir = trailingslashit( dirname( $plugins_dir . $plugin ) ); if ( ! $wp_filesystem->exists( $this_plugin_dir ) ) { // If it's already vanished. return $removed; } /* * If plugin is in its own directory, recursively delete the directory. * Base check on if plugin includes directory separator AND that it's not the root plugin folder. */ if ( strpos( $plugin, '/' ) && $this_plugin_dir !== $plugins_dir ) { $deleted = $wp_filesystem->delete( $this_plugin_dir, true ); } else { $deleted = $wp_filesystem->delete( $plugins_dir . $plugin ); } if ( ! $deleted ) { return new WP_Error( 'remove_old_failed', $this->strings['remove_old_failed'] ); } return true; } } includes/class-wp-filesystem-ssh2.php 0000644 00000055416 15135536347 0013702 0 ustar 00 <?php /** * WordPress Filesystem Class for implementing SSH2 * * To use this class you must follow these steps for PHP 5.2.6+ * * {@link http://kevin.vanzonneveld.net/techblog/article/make_ssh_connections_with_php/ - Installation Notes} * * Compile libssh2 (Note: Only 0.14 is officially working with PHP 5.2.6+ right now, But many users have found the latest versions work) * * cd /usr/src * wget https://www.libssh2.org/download/libssh2-0.14.tar.gz * tar -zxvf libssh2-0.14.tar.gz * cd libssh2-0.14/ * ./configure * make all install * * Note: Do not leave the directory yet! * * Enter: pecl install -f ssh2 * * Copy the ssh.so file it creates to your PHP Module Directory. * Open up your PHP.INI file and look for where extensions are placed. * Add in your PHP.ini file: extension=ssh2.so * * Restart Apache! * Check phpinfo() streams to confirm that: ssh2.shell, ssh2.exec, ssh2.tunnel, ssh2.scp, ssh2.sftp exist. * * Note: As of WordPress 2.8, this utilizes the PHP5+ function `stream_get_contents()`. * * @since 2.7.0 * * @package WordPress * @subpackage Filesystem */ class WP_Filesystem_SSH2 extends WP_Filesystem_Base { /** * @since 2.7.0 * @var resource */ public $link = false; /** * @since 2.7.0 * @var resource */ public $sftp_link; /** * @since 2.7.0 * @var bool */ public $keys = false; /** * Constructor. * * @since 2.7.0 * * @param array $opt */ public function __construct( $opt = '' ) { $this->method = 'ssh2'; $this->errors = new WP_Error(); // Check if possible to use ssh2 functions. if ( ! extension_loaded( 'ssh2' ) ) { $this->errors->add( 'no_ssh2_ext', __( 'The ssh2 PHP extension is not available' ) ); return; } // Set defaults: if ( empty( $opt['port'] ) ) { $this->options['port'] = 22; } else { $this->options['port'] = $opt['port']; } if ( empty( $opt['hostname'] ) ) { $this->errors->add( 'empty_hostname', __( 'SSH2 hostname is required' ) ); } else { $this->options['hostname'] = $opt['hostname']; } // Check if the options provided are OK. if ( ! empty( $opt['public_key'] ) && ! empty( $opt['private_key'] ) ) { $this->options['public_key'] = $opt['public_key']; $this->options['private_key'] = $opt['private_key']; $this->options['hostkey'] = array( 'hostkey' => 'ssh-rsa,ssh-ed25519' ); $this->keys = true; } elseif ( empty( $opt['username'] ) ) { $this->errors->add( 'empty_username', __( 'SSH2 username is required' ) ); } if ( ! empty( $opt['username'] ) ) { $this->options['username'] = $opt['username']; } if ( empty( $opt['password'] ) ) { // Password can be blank if we are using keys. if ( ! $this->keys ) { $this->errors->add( 'empty_password', __( 'SSH2 password is required' ) ); } else { $this->options['password'] = null; } } else { $this->options['password'] = $opt['password']; } } /** * Connects filesystem. * * @since 2.7.0 * * @return bool True on success, false on failure. */ public function connect() { if ( ! $this->keys ) { $this->link = @ssh2_connect( $this->options['hostname'], $this->options['port'] ); } else { $this->link = @ssh2_connect( $this->options['hostname'], $this->options['port'], $this->options['hostkey'] ); } if ( ! $this->link ) { $this->errors->add( 'connect', sprintf( /* translators: %s: hostname:port */ __( 'Failed to connect to SSH2 Server %s' ), $this->options['hostname'] . ':' . $this->options['port'] ) ); return false; } if ( ! $this->keys ) { if ( ! @ssh2_auth_password( $this->link, $this->options['username'], $this->options['password'] ) ) { $this->errors->add( 'auth', sprintf( /* translators: %s: Username. */ __( 'Username/Password incorrect for %s' ), $this->options['username'] ) ); return false; } } else { if ( ! @ssh2_auth_pubkey_file( $this->link, $this->options['username'], $this->options['public_key'], $this->options['private_key'], $this->options['password'] ) ) { $this->errors->add( 'auth', sprintf( /* translators: %s: Username. */ __( 'Public and Private keys incorrect for %s' ), $this->options['username'] ) ); return false; } } $this->sftp_link = ssh2_sftp( $this->link ); if ( ! $this->sftp_link ) { $this->errors->add( 'connect', sprintf( /* translators: %s: hostname:port */ __( 'Failed to initialize a SFTP subsystem session with the SSH2 Server %s' ), $this->options['hostname'] . ':' . $this->options['port'] ) ); return false; } return true; } /** * Gets the ssh2.sftp PHP stream wrapper path to open for the given file. * * This method also works around a PHP bug where the root directory (/) cannot * be opened by PHP functions, causing a false failure. In order to work around * this, the path is converted to /./ which is semantically the same as / * See https://bugs.php.net/bug.php?id=64169 for more details. * * @since 4.4.0 * * @param string $path The File/Directory path on the remote server to return * @return string The ssh2.sftp:// wrapped path to use. */ public function sftp_path( $path ) { if ( '/' === $path ) { $path = '/./'; } return 'ssh2.sftp://' . $this->sftp_link . '/' . ltrim( $path, '/' ); } /** * @since 2.7.0 * * @param string $command * @param bool $returnbool * @return bool|string True on success, false on failure. String if the command was executed, `$returnbool` * is false (default), and data from the resulting stream was retrieved. */ public function run_command( $command, $returnbool = false ) { if ( ! $this->link ) { return false; } $stream = ssh2_exec( $this->link, $command ); if ( ! $stream ) { $this->errors->add( 'command', sprintf( /* translators: %s: Command. */ __( 'Unable to perform command: %s' ), $command ) ); } else { stream_set_blocking( $stream, true ); stream_set_timeout( $stream, FS_TIMEOUT ); $data = stream_get_contents( $stream ); fclose( $stream ); if ( $returnbool ) { return ( false === $data ) ? false : '' !== trim( $data ); } else { return $data; } } return false; } /** * Reads entire file into a string. * * @since 2.7.0 * * @param string $file Name of the file to read. * @return string|false Read data on success, false if no temporary file could be opened, * or if the file couldn't be retrieved. */ public function get_contents( $file ) { return file_get_contents( $this->sftp_path( $file ) ); } /** * Reads entire file into an array. * * @since 2.7.0 * * @param string $file Path to the file. * @return array|false File contents in an array on success, false on failure. */ public function get_contents_array( $file ) { return file( $this->sftp_path( $file ) ); } /** * Writes a string to a file. * * @since 2.7.0 * * @param string $file Remote path to the file where to write the data. * @param string $contents The data to write. * @param int|false $mode Optional. The file permissions as octal number, usually 0644. * Default false. * @return bool True on success, false on failure. */ public function put_contents( $file, $contents, $mode = false ) { $ret = file_put_contents( $this->sftp_path( $file ), $contents ); if ( strlen( $contents ) !== $ret ) { return false; } $this->chmod( $file, $mode ); return true; } /** * Gets the current working directory. * * @since 2.7.0 * * @return string|false The current working directory on success, false on failure. */ public function cwd() { $cwd = ssh2_sftp_realpath( $this->sftp_link, '.' ); if ( $cwd ) { $cwd = trailingslashit( trim( $cwd ) ); } return $cwd; } /** * Changes current directory. * * @since 2.7.0 * * @param string $dir The new current directory. * @return bool True on success, false on failure. */ public function chdir( $dir ) { return $this->run_command( 'cd ' . $dir, true ); } /** * Changes the file group. * * @since 2.7.0 * * @param string $file Path to the file. * @param string|int $group A group name or number. * @param bool $recursive Optional. If set to true, changes file group recursively. * Default false. * @return bool True on success, false on failure. */ public function chgrp( $file, $group, $recursive = false ) { if ( ! $this->exists( $file ) ) { return false; } if ( ! $recursive || ! $this->is_dir( $file ) ) { return $this->run_command( sprintf( 'chgrp %s %s', escapeshellarg( $group ), escapeshellarg( $file ) ), true ); } return $this->run_command( sprintf( 'chgrp -R %s %s', escapeshellarg( $group ), escapeshellarg( $file ) ), true ); } /** * Changes filesystem permissions. * * @since 2.7.0 * * @param string $file Path to the file. * @param int|false $mode Optional. The permissions as octal number, usually 0644 for files, * 0755 for directories. Default false. * @param bool $recursive Optional. If set to true, changes file permissions recursively. * Default false. * @return bool True on success, false on failure. */ public function chmod( $file, $mode = false, $recursive = false ) { if ( ! $this->exists( $file ) ) { return false; } if ( ! $mode ) { if ( $this->is_file( $file ) ) { $mode = FS_CHMOD_FILE; } elseif ( $this->is_dir( $file ) ) { $mode = FS_CHMOD_DIR; } else { return false; } } if ( ! $recursive || ! $this->is_dir( $file ) ) { return $this->run_command( sprintf( 'chmod %o %s', $mode, escapeshellarg( $file ) ), true ); } return $this->run_command( sprintf( 'chmod -R %o %s', $mode, escapeshellarg( $file ) ), true ); } /** * Changes the owner of a file or directory. * * @since 2.7.0 * * @param string $file Path to the file or directory. * @param string|int $owner A user name or number. * @param bool $recursive Optional. If set to true, changes file owner recursively. * Default false. * @return bool True on success, false on failure. */ public function chown( $file, $owner, $recursive = false ) { if ( ! $this->exists( $file ) ) { return false; } if ( ! $recursive || ! $this->is_dir( $file ) ) { return $this->run_command( sprintf( 'chown %s %s', escapeshellarg( $owner ), escapeshellarg( $file ) ), true ); } return $this->run_command( sprintf( 'chown -R %s %s', escapeshellarg( $owner ), escapeshellarg( $file ) ), true ); } /** * Gets the file owner. * * @since 2.7.0 * * @param string $file Path to the file. * @return string|false Username of the owner on success, false on failure. */ public function owner( $file ) { $owneruid = @fileowner( $this->sftp_path( $file ) ); if ( ! $owneruid ) { return false; } if ( ! function_exists( 'posix_getpwuid' ) ) { return $owneruid; } $ownerarray = posix_getpwuid( $owneruid ); if ( ! $ownerarray ) { return false; } return $ownerarray['name']; } /** * Gets the permissions of the specified file or filepath in their octal format. * * @since 2.7.0 * * @param string $file Path to the file. * @return string Mode of the file (the last 3 digits). */ public function getchmod( $file ) { return substr( decoct( @fileperms( $this->sftp_path( $file ) ) ), -3 ); } /** * Gets the file's group. * * @since 2.7.0 * * @param string $file Path to the file. * @return string|false The group on success, false on failure. */ public function group( $file ) { $gid = @filegroup( $this->sftp_path( $file ) ); if ( ! $gid ) { return false; } if ( ! function_exists( 'posix_getgrgid' ) ) { return $gid; } $grouparray = posix_getgrgid( $gid ); if ( ! $grouparray ) { return false; } return $grouparray['name']; } /** * Copies a file. * * @since 2.7.0 * * @param string $source Path to the source file. * @param string $destination Path to the destination file. * @param bool $overwrite Optional. Whether to overwrite the destination file if it exists. * Default false. * @param int|false $mode Optional. The permissions as octal number, usually 0644 for files, * 0755 for dirs. Default false. * @return bool True on success, false on failure. */ public function copy( $source, $destination, $overwrite = false, $mode = false ) { if ( ! $overwrite && $this->exists( $destination ) ) { return false; } $content = $this->get_contents( $source ); if ( false === $content ) { return false; } return $this->put_contents( $destination, $content, $mode ); } /** * Moves a file or directory. * * After moving files or directories, OPcache will need to be invalidated. * * If moving a directory fails, `copy_dir()` can be used for a recursive copy. * * Use `move_dir()` for moving directories with OPcache invalidation and a * fallback to `copy_dir()`. * * @since 2.7.0 * * @param string $source Path to the source file or directory. * @param string $destination Path to the destination file or directory. * @param bool $overwrite Optional. Whether to overwrite the destination if it exists. * Default false. * @return bool True on success, false on failure. */ public function move( $source, $destination, $overwrite = false ) { if ( $this->exists( $destination ) ) { if ( $overwrite ) { // We need to remove the destination before we can rename the source. $this->delete( $destination, false, 'f' ); } else { // If we're not overwriting, the rename will fail, so return early. return false; } } return ssh2_sftp_rename( $this->sftp_link, $source, $destination ); } /** * Deletes a file or directory. * * @since 2.7.0 * * @param string $file Path to the file or directory. * @param bool $recursive Optional. If set to true, deletes files and folders recursively. * Default false. * @param string|false $type Type of resource. 'f' for file, 'd' for directory. * Default false. * @return bool True on success, false on failure. */ public function delete( $file, $recursive = false, $type = false ) { if ( 'f' === $type || $this->is_file( $file ) ) { return ssh2_sftp_unlink( $this->sftp_link, $file ); } if ( ! $recursive ) { return ssh2_sftp_rmdir( $this->sftp_link, $file ); } $filelist = $this->dirlist( $file ); if ( is_array( $filelist ) ) { foreach ( $filelist as $filename => $fileinfo ) { $this->delete( $file . '/' . $filename, $recursive, $fileinfo['type'] ); } } return ssh2_sftp_rmdir( $this->sftp_link, $file ); } /** * Checks if a file or directory exists. * * @since 2.7.0 * * @param string $path Path to file or directory. * @return bool Whether $path exists or not. */ public function exists( $path ) { return file_exists( $this->sftp_path( $path ) ); } /** * Checks if resource is a file. * * @since 2.7.0 * * @param string $file File path. * @return bool Whether $file is a file. */ public function is_file( $file ) { return is_file( $this->sftp_path( $file ) ); } /** * Checks if resource is a directory. * * @since 2.7.0 * * @param string $path Directory path. * @return bool Whether $path is a directory. */ public function is_dir( $path ) { return is_dir( $this->sftp_path( $path ) ); } /** * Checks if a file is readable. * * @since 2.7.0 * * @param string $file Path to file. * @return bool Whether $file is readable. */ public function is_readable( $file ) { return is_readable( $this->sftp_path( $file ) ); } /** * Checks if a file or directory is writable. * * @since 2.7.0 * * @param string $path Path to file or directory. * @return bool Whether $path is writable. */ public function is_writable( $path ) { // PHP will base its writable checks on system_user === file_owner, not ssh_user === file_owner. return true; } /** * Gets the file's last access time. * * @since 2.7.0 * * @param string $file Path to file. * @return int|false Unix timestamp representing last access time, false on failure. */ public function atime( $file ) { return fileatime( $this->sftp_path( $file ) ); } /** * Gets the file modification time. * * @since 2.7.0 * * @param string $file Path to file. * @return int|false Unix timestamp representing modification time, false on failure. */ public function mtime( $file ) { return filemtime( $this->sftp_path( $file ) ); } /** * Gets the file size (in bytes). * * @since 2.7.0 * * @param string $file Path to file. * @return int|false Size of the file in bytes on success, false on failure. */ public function size( $file ) { return filesize( $this->sftp_path( $file ) ); } /** * Sets the access and modification times of a file. * * Note: Not implemented. * * @since 2.7.0 * * @param string $file Path to file. * @param int $time Optional. Modified time to set for file. * Default 0. * @param int $atime Optional. Access time to set for file. * Default 0. */ public function touch( $file, $time = 0, $atime = 0 ) { // Not implemented. } /** * Creates a directory. * * @since 2.7.0 * * @param string $path Path for new directory. * @param int|false $chmod Optional. The permissions as octal number (or false to skip chmod). * Default false. * @param string|int|false $chown Optional. A user name or number (or false to skip chown). * Default false. * @param string|int|false $chgrp Optional. A group name or number (or false to skip chgrp). * Default false. * @return bool True on success, false on failure. */ public function mkdir( $path, $chmod = false, $chown = false, $chgrp = false ) { $path = untrailingslashit( $path ); if ( empty( $path ) ) { return false; } if ( ! $chmod ) { $chmod = FS_CHMOD_DIR; } if ( ! ssh2_sftp_mkdir( $this->sftp_link, $path, $chmod, true ) ) { return false; } // Set directory permissions. ssh2_sftp_chmod( $this->sftp_link, $path, $chmod ); if ( $chown ) { $this->chown( $path, $chown ); } if ( $chgrp ) { $this->chgrp( $path, $chgrp ); } return true; } /** * Deletes a directory. * * @since 2.7.0 * * @param string $path Path to directory. * @param bool $recursive Optional. Whether to recursively remove files/directories. * Default false. * @return bool True on success, false on failure. */ public function rmdir( $path, $recursive = false ) { return $this->delete( $path, $recursive ); } /** * Gets details for files in a directory or a specific file. * * @since 2.7.0 * * @param string $path Path to directory or file. * @param bool $include_hidden Optional. Whether to include details of hidden ("." prefixed) files. * Default true. * @param bool $recursive Optional. Whether to recursively include file details in nested directories. * Default false. * @return array|false { * Array of arrays containing file information. False if unable to list directory contents. * * @type array ...$0 { * Array of file information. Note that some elements may not be available on all filesystems. * * @type string $name Name of the file or directory. * @type string $perms *nix representation of permissions. * @type string $permsn Octal representation of permissions. * @type false $number File number. Always false in this context. * @type string|false $owner Owner name or ID, or false if not available. * @type string|false $group File permissions group, or false if not available. * @type int|string|false $size Size of file in bytes. May be a numeric string. * False if not available. * @type int|string|false $lastmodunix Last modified unix timestamp. May be a numeric string. * False if not available. * @type string|false $lastmod Last modified month (3 letters) and day (without leading 0), or * false if not available. * @type string|false $time Last modified time, or false if not available. * @type string $type Type of resource. 'f' for file, 'd' for directory, 'l' for link. * @type array|false $files If a directory and `$recursive` is true, contains another array of * files. False if unable to list directory contents. * } * } */ public function dirlist( $path, $include_hidden = true, $recursive = false ) { if ( $this->is_file( $path ) ) { $limit_file = basename( $path ); $path = dirname( $path ); } else { $limit_file = false; } if ( ! $this->is_dir( $path ) || ! $this->is_readable( $path ) ) { return false; } $ret = array(); $dir = dir( $this->sftp_path( $path ) ); if ( ! $dir ) { return false; } $path = trailingslashit( $path ); while ( false !== ( $entry = $dir->read() ) ) { $struc = array(); $struc['name'] = $entry; if ( '.' === $struc['name'] || '..' === $struc['name'] ) { continue; // Do not care about these folders. } if ( ! $include_hidden && '.' === $struc['name'][0] ) { continue; } if ( $limit_file && $struc['name'] !== $limit_file ) { continue; } $struc['perms'] = $this->gethchmod( $path . $entry ); $struc['permsn'] = $this->getnumchmodfromh( $struc['perms'] ); $struc['number'] = false; $struc['owner'] = $this->owner( $path . $entry ); $struc['group'] = $this->group( $path . $entry ); $struc['size'] = $this->size( $path . $entry ); $struc['lastmodunix'] = $this->mtime( $path . $entry ); $struc['lastmod'] = gmdate( 'M j', $struc['lastmodunix'] ); $struc['time'] = gmdate( 'h:i:s', $struc['lastmodunix'] ); $struc['type'] = $this->is_dir( $path . $entry ) ? 'd' : 'f'; if ( 'd' === $struc['type'] ) { if ( $recursive ) { $struc['files'] = $this->dirlist( $path . $struc['name'], $include_hidden, $recursive ); } else { $struc['files'] = array(); } } $ret[ $struc['name'] ] = $struc; } $dir->close(); unset( $dir ); return $ret; } } includes/dashboard.php 0000644 00000211100 15135536347 0011021 0 ustar 00 <?php /** * WordPress Dashboard Widget Administration Screen API * * @package WordPress * @subpackage Administration */ /** * Registers dashboard widgets. * * Handles POST data, sets up filters. * * @since 2.5.0 * * @global array $wp_registered_widgets * @global array $wp_registered_widget_controls * @global callable[] $wp_dashboard_control_callbacks */ function wp_dashboard_setup() { global $wp_registered_widgets, $wp_registered_widget_controls, $wp_dashboard_control_callbacks; $screen = get_current_screen(); /* Register Widgets and Controls */ $wp_dashboard_control_callbacks = array(); // Browser version $check_browser = wp_check_browser_version(); if ( $check_browser && $check_browser['upgrade'] ) { add_filter( 'postbox_classes_dashboard_dashboard_browser_nag', 'dashboard_browser_nag_class' ); if ( $check_browser['insecure'] ) { wp_add_dashboard_widget( 'dashboard_browser_nag', __( 'You are using an insecure browser!' ), 'wp_dashboard_browser_nag' ); } else { wp_add_dashboard_widget( 'dashboard_browser_nag', __( 'Your browser is out of date!' ), 'wp_dashboard_browser_nag' ); } } // PHP Version. $check_php = wp_check_php_version(); if ( $check_php && current_user_can( 'update_php' ) ) { // If "not acceptable" the widget will be shown. if ( isset( $check_php['is_acceptable'] ) && ! $check_php['is_acceptable'] ) { add_filter( 'postbox_classes_dashboard_dashboard_php_nag', 'dashboard_php_nag_class' ); if ( $check_php['is_lower_than_future_minimum'] ) { wp_add_dashboard_widget( 'dashboard_php_nag', __( 'PHP Update Required' ), 'wp_dashboard_php_nag' ); } else { wp_add_dashboard_widget( 'dashboard_php_nag', __( 'PHP Update Recommended' ), 'wp_dashboard_php_nag' ); } } } // Site Health. if ( current_user_can( 'view_site_health_checks' ) && ! is_network_admin() ) { if ( ! class_exists( 'WP_Site_Health' ) ) { require_once ABSPATH . 'wp-admin/includes/class-wp-site-health.php'; } WP_Site_Health::get_instance(); wp_enqueue_style( 'site-health' ); wp_enqueue_script( 'site-health' ); wp_add_dashboard_widget( 'dashboard_site_health', __( 'Site Health Status' ), 'wp_dashboard_site_health' ); } // Right Now. if ( is_blog_admin() && current_user_can( 'edit_posts' ) ) { wp_add_dashboard_widget( 'dashboard_right_now', __( 'At a Glance' ), 'wp_dashboard_right_now' ); } if ( is_network_admin() ) { wp_add_dashboard_widget( 'network_dashboard_right_now', __( 'Right Now' ), 'wp_network_dashboard_right_now' ); } // Activity Widget. if ( is_blog_admin() ) { wp_add_dashboard_widget( 'dashboard_activity', __( 'Activity' ), 'wp_dashboard_site_activity' ); } // QuickPress Widget. if ( is_blog_admin() && current_user_can( get_post_type_object( 'post' )->cap->create_posts ) ) { $quick_draft_title = sprintf( '<span class="hide-if-no-js">%1$s</span> <span class="hide-if-js">%2$s</span>', __( 'Quick Draft' ), __( 'Your Recent Drafts' ) ); wp_add_dashboard_widget( 'dashboard_quick_press', $quick_draft_title, 'wp_dashboard_quick_press' ); } // WordPress Events and News. wp_add_dashboard_widget( 'dashboard_primary', __( 'WordPress Events and News' ), 'wp_dashboard_events_news' ); if ( is_network_admin() ) { /** * Fires after core widgets for the Network Admin dashboard have been registered. * * @since 3.1.0 */ do_action( 'wp_network_dashboard_setup' ); /** * Filters the list of widgets to load for the Network Admin dashboard. * * @since 3.1.0 * * @param string[] $dashboard_widgets An array of dashboard widget IDs. */ $dashboard_widgets = apply_filters( 'wp_network_dashboard_widgets', array() ); } elseif ( is_user_admin() ) { /** * Fires after core widgets for the User Admin dashboard have been registered. * * @since 3.1.0 */ do_action( 'wp_user_dashboard_setup' ); /** * Filters the list of widgets to load for the User Admin dashboard. * * @since 3.1.0 * * @param string[] $dashboard_widgets An array of dashboard widget IDs. */ $dashboard_widgets = apply_filters( 'wp_user_dashboard_widgets', array() ); } else { /** * Fires after core widgets for the admin dashboard have been registered. * * @since 2.5.0 */ do_action( 'wp_dashboard_setup' ); /** * Filters the list of widgets to load for the admin dashboard. * * @since 2.5.0 * * @param string[] $dashboard_widgets An array of dashboard widget IDs. */ $dashboard_widgets = apply_filters( 'wp_dashboard_widgets', array() ); } foreach ( $dashboard_widgets as $widget_id ) { $name = empty( $wp_registered_widgets[ $widget_id ]['all_link'] ) ? $wp_registered_widgets[ $widget_id ]['name'] : $wp_registered_widgets[ $widget_id ]['name'] . " <a href='{$wp_registered_widgets[$widget_id]['all_link']}' class='edit-box open-box'>" . __( 'View all' ) . '</a>'; wp_add_dashboard_widget( $widget_id, $name, $wp_registered_widgets[ $widget_id ]['callback'], $wp_registered_widget_controls[ $widget_id ]['callback'] ); } if ( 'POST' === $_SERVER['REQUEST_METHOD'] && isset( $_POST['widget_id'] ) ) { check_admin_referer( 'edit-dashboard-widget_' . $_POST['widget_id'], 'dashboard-widget-nonce' ); ob_start(); // Hack - but the same hack wp-admin/widgets.php uses. wp_dashboard_trigger_widget_control( $_POST['widget_id'] ); ob_end_clean(); wp_redirect( remove_query_arg( 'edit' ) ); exit; } /** This action is documented in wp-admin/includes/meta-boxes.php */ do_action( 'do_meta_boxes', $screen->id, 'normal', '' ); /** This action is documented in wp-admin/includes/meta-boxes.php */ do_action( 'do_meta_boxes', $screen->id, 'side', '' ); } /** * Adds a new dashboard widget. * * @since 2.7.0 * @since 5.6.0 The `$context` and `$priority` parameters were added. * * @global callable[] $wp_dashboard_control_callbacks * * @param string $widget_id Widget ID (used in the 'id' attribute for the widget). * @param string $widget_name Title of the widget. * @param callable $callback Function that fills the widget with the desired content. * The function should echo its output. * @param callable $control_callback Optional. Function that outputs controls for the widget. Default null. * @param array $callback_args Optional. Data that should be set as the $args property of the widget array * (which is the second parameter passed to your callback). Default null. * @param string $context Optional. The context within the screen where the box should display. * Accepts 'normal', 'side', 'column3', or 'column4'. Default 'normal'. * @param string $priority Optional. The priority within the context where the box should show. * Accepts 'high', 'core', 'default', or 'low'. Default 'core'. */ function wp_add_dashboard_widget( $widget_id, $widget_name, $callback, $control_callback = null, $callback_args = null, $context = 'normal', $priority = 'core' ) { global $wp_dashboard_control_callbacks; $screen = get_current_screen(); $private_callback_args = array( '__widget_basename' => $widget_name ); if ( is_null( $callback_args ) ) { $callback_args = $private_callback_args; } elseif ( is_array( $callback_args ) ) { $callback_args = array_merge( $callback_args, $private_callback_args ); } if ( $control_callback && is_callable( $control_callback ) && current_user_can( 'edit_dashboard' ) ) { $wp_dashboard_control_callbacks[ $widget_id ] = $control_callback; if ( isset( $_GET['edit'] ) && $widget_id === $_GET['edit'] ) { list($url) = explode( '#', add_query_arg( 'edit', false ), 2 ); $widget_name .= ' <span class="postbox-title-action"><a href="' . esc_url( $url ) . '">' . __( 'Cancel' ) . '</a></span>'; $callback = '_wp_dashboard_control_callback'; } else { list($url) = explode( '#', add_query_arg( 'edit', $widget_id ), 2 ); $widget_name .= ' <span class="postbox-title-action"><a href="' . esc_url( "$url#$widget_id" ) . '" class="edit-box open-box">' . __( 'Configure' ) . '</a></span>'; } } $side_widgets = array( 'dashboard_quick_press', 'dashboard_primary' ); if ( in_array( $widget_id, $side_widgets, true ) ) { $context = 'side'; } $high_priority_widgets = array( 'dashboard_browser_nag', 'dashboard_php_nag' ); if ( in_array( $widget_id, $high_priority_widgets, true ) ) { $priority = 'high'; } if ( empty( $context ) ) { $context = 'normal'; } if ( empty( $priority ) ) { $priority = 'core'; } add_meta_box( $widget_id, $widget_name, $callback, $screen, $context, $priority, $callback_args ); } /** * Outputs controls for the current dashboard widget. * * @access private * @since 2.7.0 * * @param mixed $dashboard * @param array $meta_box */ function _wp_dashboard_control_callback( $dashboard, $meta_box ) { echo '<form method="post" class="dashboard-widget-control-form wp-clearfix">'; wp_dashboard_trigger_widget_control( $meta_box['id'] ); wp_nonce_field( 'edit-dashboard-widget_' . $meta_box['id'], 'dashboard-widget-nonce' ); echo '<input type="hidden" name="widget_id" value="' . esc_attr( $meta_box['id'] ) . '" />'; submit_button( __( 'Save Changes' ) ); echo '</form>'; } /** * Displays the dashboard. * * @since 2.5.0 */ function wp_dashboard() { $screen = get_current_screen(); $columns = absint( $screen->get_columns() ); $columns_css = ''; if ( $columns ) { $columns_css = " columns-$columns"; } ?> <div id="dashboard-widgets" class="metabox-holder<?php echo $columns_css; ?>"> <div id="postbox-container-1" class="postbox-container"> <?php do_meta_boxes( $screen->id, 'normal', '' ); ?> </div> <div id="postbox-container-2" class="postbox-container"> <?php do_meta_boxes( $screen->id, 'side', '' ); ?> </div> <div id="postbox-container-3" class="postbox-container"> <?php do_meta_boxes( $screen->id, 'column3', '' ); ?> </div> <div id="postbox-container-4" class="postbox-container"> <?php do_meta_boxes( $screen->id, 'column4', '' ); ?> </div> </div> <?php wp_nonce_field( 'closedpostboxes', 'closedpostboxesnonce', false ); wp_nonce_field( 'meta-box-order', 'meta-box-order-nonce', false ); } // // Dashboard Widgets. // /** * Dashboard widget that displays some basic stats about the site. * * Formerly 'Right Now'. A streamlined 'At a Glance' as of 3.8. * * @since 2.7.0 */ function wp_dashboard_right_now() { ?> <div class="main"> <ul> <?php // Posts and Pages. foreach ( array( 'post', 'page' ) as $post_type ) { $num_posts = wp_count_posts( $post_type ); if ( $num_posts && $num_posts->publish ) { if ( 'post' === $post_type ) { /* translators: %s: Number of posts. */ $text = _n( '%s Post', '%s Posts', $num_posts->publish ); } else { /* translators: %s: Number of pages. */ $text = _n( '%s Page', '%s Pages', $num_posts->publish ); } $text = sprintf( $text, number_format_i18n( $num_posts->publish ) ); $post_type_object = get_post_type_object( $post_type ); if ( $post_type_object && current_user_can( $post_type_object->cap->edit_posts ) ) { printf( '<li class="%1$s-count"><a href="edit.php?post_type=%1$s">%2$s</a></li>', $post_type, $text ); } else { printf( '<li class="%1$s-count"><span>%2$s</span></li>', $post_type, $text ); } } } // Comments. $num_comm = wp_count_comments(); if ( $num_comm && ( $num_comm->approved || $num_comm->moderated ) ) { /* translators: %s: Number of comments. */ $text = sprintf( _n( '%s Comment', '%s Comments', $num_comm->approved ), number_format_i18n( $num_comm->approved ) ); ?> <li class="comment-count"> <a href="edit-comments.php"><?php echo $text; ?></a> </li> <?php $moderated_comments_count_i18n = number_format_i18n( $num_comm->moderated ); /* translators: %s: Number of comments. */ $text = sprintf( _n( '%s Comment in moderation', '%s Comments in moderation', $num_comm->moderated ), $moderated_comments_count_i18n ); ?> <li class="comment-mod-count<?php echo ! $num_comm->moderated ? ' hidden' : ''; ?>"> <a href="edit-comments.php?comment_status=moderated" class="comments-in-moderation-text"><?php echo $text; ?></a> </li> <?php } /** * Filters the array of extra elements to list in the 'At a Glance' * dashboard widget. * * Prior to 3.8.0, the widget was named 'Right Now'. Each element * is wrapped in list-item tags on output. * * @since 3.8.0 * * @param string[] $items Array of extra 'At a Glance' widget items. */ $elements = apply_filters( 'dashboard_glance_items', array() ); if ( $elements ) { echo '<li>' . implode( "</li>\n<li>", $elements ) . "</li>\n"; } ?> </ul> <?php update_right_now_message(); // Check if search engines are asked not to index this site. if ( ! is_network_admin() && ! is_user_admin() && current_user_can( 'manage_options' ) && ! get_option( 'blog_public' ) ) { /** * Filters the link title attribute for the 'Search engines discouraged' * message displayed in the 'At a Glance' dashboard widget. * * Prior to 3.8.0, the widget was named 'Right Now'. * * @since 3.0.0 * @since 4.5.0 The default for `$title` was updated to an empty string. * * @param string $title Default attribute text. */ $title = apply_filters( 'privacy_on_link_title', '' ); /** * Filters the link label for the 'Search engines discouraged' message * displayed in the 'At a Glance' dashboard widget. * * Prior to 3.8.0, the widget was named 'Right Now'. * * @since 3.0.0 * * @param string $content Default text. */ $content = apply_filters( 'privacy_on_link_text', __( 'Search engines discouraged' ) ); $title_attr = '' === $title ? '' : " title='$title'"; echo "<p class='search-engines-info'><a href='options-reading.php'$title_attr>$content</a></p>"; } ?> </div> <?php /* * activity_box_end has a core action, but only prints content when multisite. * Using an output buffer is the only way to really check if anything's displayed here. */ ob_start(); /** * Fires at the end of the 'At a Glance' dashboard widget. * * Prior to 3.8.0, the widget was named 'Right Now'. * * @since 2.5.0 */ do_action( 'rightnow_end' ); /** * Fires at the end of the 'At a Glance' dashboard widget. * * Prior to 3.8.0, the widget was named 'Right Now'. * * @since 2.0.0 */ do_action( 'activity_box_end' ); $actions = ob_get_clean(); if ( ! empty( $actions ) ) : ?> <div class="sub"> <?php echo $actions; ?> </div> <?php endif; } /** * @since 3.1.0 */ function wp_network_dashboard_right_now() { $actions = array(); if ( current_user_can( 'create_sites' ) ) { $actions['create-site'] = '<a href="' . network_admin_url( 'site-new.php' ) . '">' . __( 'Create a New Site' ) . '</a>'; } if ( current_user_can( 'create_users' ) ) { $actions['create-user'] = '<a href="' . network_admin_url( 'user-new.php' ) . '">' . __( 'Create a New User' ) . '</a>'; } $c_users = get_user_count(); $c_blogs = get_blog_count(); /* translators: %s: Number of users on the network. */ $user_text = sprintf( _n( '%s user', '%s users', $c_users ), number_format_i18n( $c_users ) ); /* translators: %s: Number of sites on the network. */ $blog_text = sprintf( _n( '%s site', '%s sites', $c_blogs ), number_format_i18n( $c_blogs ) ); /* translators: 1: Text indicating the number of sites on the network, 2: Text indicating the number of users on the network. */ $sentence = sprintf( __( 'You have %1$s and %2$s.' ), $blog_text, $user_text ); if ( $actions ) { echo '<ul class="subsubsub">'; foreach ( $actions as $class => $action ) { $actions[ $class ] = "\t<li class='$class'>$action"; } echo implode( " |</li>\n", $actions ) . "</li>\n"; echo '</ul>'; } ?> <br class="clear" /> <p class="youhave"><?php echo $sentence; ?></p> <?php /** * Fires in the Network Admin 'Right Now' dashboard widget * just before the user and site search form fields. * * @since MU (3.0.0) */ do_action( 'wpmuadminresult' ); ?> <form action="<?php echo esc_url( network_admin_url( 'users.php' ) ); ?>" method="get"> <p> <label class="screen-reader-text" for="search-users"> <?php /* translators: Hidden accessibility text. */ _e( 'Search Users' ); ?> </label> <input type="search" name="s" value="" size="30" autocomplete="off" id="search-users" /> <?php submit_button( __( 'Search Users' ), '', false, false, array( 'id' => 'submit_users' ) ); ?> </p> </form> <form action="<?php echo esc_url( network_admin_url( 'sites.php' ) ); ?>" method="get"> <p> <label class="screen-reader-text" for="search-sites"> <?php /* translators: Hidden accessibility text. */ _e( 'Search Sites' ); ?> </label> <input type="search" name="s" value="" size="30" autocomplete="off" id="search-sites" /> <?php submit_button( __( 'Search Sites' ), '', false, false, array( 'id' => 'submit_sites' ) ); ?> </p> </form> <?php /** * Fires at the end of the 'Right Now' widget in the Network Admin dashboard. * * @since MU (3.0.0) */ do_action( 'mu_rightnow_end' ); /** * Fires at the end of the 'Right Now' widget in the Network Admin dashboard. * * @since MU (3.0.0) */ do_action( 'mu_activity_box_end' ); } /** * Displays the Quick Draft widget. * * @since 3.8.0 * * @global int $post_ID * * @param string|false $error_msg Optional. Error message. Default false. */ function wp_dashboard_quick_press( $error_msg = false ) { global $post_ID; if ( ! current_user_can( 'edit_posts' ) ) { return; } // Check if a new auto-draft (= no new post_ID) is needed or if the old can be used. $last_post_id = (int) get_user_option( 'dashboard_quick_press_last_post_id' ); // Get the last post_ID. if ( $last_post_id ) { $post = get_post( $last_post_id ); if ( empty( $post ) || 'auto-draft' !== $post->post_status ) { // auto-draft doesn't exist anymore. $post = get_default_post_to_edit( 'post', true ); update_user_option( get_current_user_id(), 'dashboard_quick_press_last_post_id', (int) $post->ID ); // Save post_ID. } else { $post->post_title = ''; // Remove the auto draft title. } } else { $post = get_default_post_to_edit( 'post', true ); $user_id = get_current_user_id(); // Don't create an option if this is a super admin who does not belong to this site. if ( in_array( get_current_blog_id(), array_keys( get_blogs_of_user( $user_id ) ), true ) ) { update_user_option( $user_id, 'dashboard_quick_press_last_post_id', (int) $post->ID ); // Save post_ID. } } $post_ID = (int) $post->ID; ?> <form name="post" action="<?php echo esc_url( admin_url( 'post.php' ) ); ?>" method="post" id="quick-press" class="initial-form hide-if-no-js"> <?php if ( $error_msg ) { wp_admin_notice( $error_msg, array( 'additional_classes' => array( 'error' ), ) ); } ?> <div class="input-text-wrap" id="title-wrap"> <label for="title"> <?php /** This filter is documented in wp-admin/edit-form-advanced.php */ echo apply_filters( 'enter_title_here', __( 'Title' ), $post ); ?> </label> <input type="text" name="post_title" id="title" autocomplete="off" /> </div> <div class="textarea-wrap" id="description-wrap"> <label for="content"><?php _e( 'Content' ); ?></label> <textarea name="content" id="content" placeholder="<?php esc_attr_e( 'What’s on your mind?' ); ?>" class="mceEditor" rows="3" cols="15" autocomplete="off"></textarea> </div> <p class="submit"> <input type="hidden" name="action" id="quickpost-action" value="post-quickdraft-save" /> <input type="hidden" name="post_ID" value="<?php echo $post_ID; ?>" /> <input type="hidden" name="post_type" value="post" /> <?php wp_nonce_field( 'add-post' ); ?> <?php submit_button( __( 'Save Draft' ), 'primary', 'save', false, array( 'id' => 'save-post' ) ); ?> <br class="clear" /> </p> </form> <?php wp_dashboard_recent_drafts(); } /** * Show recent drafts of the user on the dashboard. * * @since 2.7.0 * * @param WP_Post[]|false $drafts Optional. Array of posts to display. Default false. */ function wp_dashboard_recent_drafts( $drafts = false ) { if ( ! $drafts ) { $query_args = array( 'post_type' => 'post', 'post_status' => 'draft', 'author' => get_current_user_id(), 'posts_per_page' => 4, 'orderby' => 'modified', 'order' => 'DESC', ); /** * Filters the post query arguments for the 'Recent Drafts' dashboard widget. * * @since 4.4.0 * * @param array $query_args The query arguments for the 'Recent Drafts' dashboard widget. */ $query_args = apply_filters( 'dashboard_recent_drafts_query_args', $query_args ); $drafts = get_posts( $query_args ); if ( ! $drafts ) { return; } } echo '<div class="drafts">'; if ( count( $drafts ) > 3 ) { printf( '<p class="view-all"><a href="%s">%s</a></p>' . "\n", esc_url( admin_url( 'edit.php?post_status=draft' ) ), __( 'View all drafts' ) ); } echo '<h2 class="hide-if-no-js">' . __( 'Your Recent Drafts' ) . "</h2>\n"; echo '<ul>'; /* translators: Maximum number of words used in a preview of a draft on the dashboard. */ $draft_length = (int) _x( '10', 'draft_length' ); $drafts = array_slice( $drafts, 0, 3 ); foreach ( $drafts as $draft ) { $url = get_edit_post_link( $draft->ID ); $title = _draft_or_post_title( $draft->ID ); echo "<li>\n"; printf( '<div class="draft-title"><a href="%s" aria-label="%s">%s</a><time datetime="%s">%s</time></div>', esc_url( $url ), /* translators: %s: Post title. */ esc_attr( sprintf( __( 'Edit “%s”' ), $title ) ), esc_html( $title ), get_the_time( 'c', $draft ), get_the_time( __( 'F j, Y' ), $draft ) ); $the_content = wp_trim_words( $draft->post_content, $draft_length ); if ( $the_content ) { echo '<p>' . $the_content . '</p>'; } echo "</li>\n"; } echo "</ul>\n"; echo '</div>'; } /** * Outputs a row for the Recent Comments widget. * * @access private * @since 2.7.0 * * @global WP_Comment $comment Global comment object. * * @param WP_Comment $comment The current comment. * @param bool $show_date Optional. Whether to display the date. */ function _wp_dashboard_recent_comments_row( &$comment, $show_date = true ) { $GLOBALS['comment'] = clone $comment; if ( $comment->comment_post_ID > 0 ) { $comment_post_title = _draft_or_post_title( $comment->comment_post_ID ); $comment_post_url = get_the_permalink( $comment->comment_post_ID ); $comment_post_link = '<a href="' . esc_url( $comment_post_url ) . '">' . $comment_post_title . '</a>'; } else { $comment_post_link = ''; } $actions_string = ''; if ( current_user_can( 'edit_comment', $comment->comment_ID ) ) { // Pre-order it: Approve | Reply | Edit | Spam | Trash. $actions = array( 'approve' => '', 'unapprove' => '', 'reply' => '', 'edit' => '', 'spam' => '', 'trash' => '', 'delete' => '', 'view' => '', ); $del_nonce = esc_html( '_wpnonce=' . wp_create_nonce( "delete-comment_$comment->comment_ID" ) ); $approve_nonce = esc_html( '_wpnonce=' . wp_create_nonce( "approve-comment_$comment->comment_ID" ) ); $approve_url = esc_url( "comment.php?action=approvecomment&p=$comment->comment_post_ID&c=$comment->comment_ID&$approve_nonce" ); $unapprove_url = esc_url( "comment.php?action=unapprovecomment&p=$comment->comment_post_ID&c=$comment->comment_ID&$approve_nonce" ); $spam_url = esc_url( "comment.php?action=spamcomment&p=$comment->comment_post_ID&c=$comment->comment_ID&$del_nonce" ); $trash_url = esc_url( "comment.php?action=trashcomment&p=$comment->comment_post_ID&c=$comment->comment_ID&$del_nonce" ); $delete_url = esc_url( "comment.php?action=deletecomment&p=$comment->comment_post_ID&c=$comment->comment_ID&$del_nonce" ); $actions['approve'] = sprintf( '<a href="%s" data-wp-lists="%s" class="vim-a aria-button-if-js" aria-label="%s">%s</a>', $approve_url, "dim:the-comment-list:comment-{$comment->comment_ID}:unapproved:e7e7d3:e7e7d3:new=approved", esc_attr__( 'Approve this comment' ), __( 'Approve' ) ); $actions['unapprove'] = sprintf( '<a href="%s" data-wp-lists="%s" class="vim-u aria-button-if-js" aria-label="%s">%s</a>', $unapprove_url, "dim:the-comment-list:comment-{$comment->comment_ID}:unapproved:e7e7d3:e7e7d3:new=unapproved", esc_attr__( 'Unapprove this comment' ), __( 'Unapprove' ) ); $actions['edit'] = sprintf( '<a href="%s" aria-label="%s">%s</a>', "comment.php?action=editcomment&c={$comment->comment_ID}", esc_attr__( 'Edit this comment' ), __( 'Edit' ) ); $actions['reply'] = sprintf( '<button type="button" onclick="window.commentReply && commentReply.open(\'%s\',\'%s\');" class="vim-r button-link hide-if-no-js" aria-label="%s">%s</button>', $comment->comment_ID, $comment->comment_post_ID, esc_attr__( 'Reply to this comment' ), __( 'Reply' ) ); $actions['spam'] = sprintf( '<a href="%s" data-wp-lists="%s" class="vim-s vim-destructive aria-button-if-js" aria-label="%s">%s</a>', $spam_url, "delete:the-comment-list:comment-{$comment->comment_ID}::spam=1", esc_attr__( 'Mark this comment as spam' ), /* translators: "Mark as spam" link. */ _x( 'Spam', 'verb' ) ); if ( ! EMPTY_TRASH_DAYS ) { $actions['delete'] = sprintf( '<a href="%s" data-wp-lists="%s" class="delete vim-d vim-destructive aria-button-if-js" aria-label="%s">%s</a>', $delete_url, "delete:the-comment-list:comment-{$comment->comment_ID}::trash=1", esc_attr__( 'Delete this comment permanently' ), __( 'Delete Permanently' ) ); } else { $actions['trash'] = sprintf( '<a href="%s" data-wp-lists="%s" class="delete vim-d vim-destructive aria-button-if-js" aria-label="%s">%s</a>', $trash_url, "delete:the-comment-list:comment-{$comment->comment_ID}::trash=1", esc_attr__( 'Move this comment to the Trash' ), _x( 'Trash', 'verb' ) ); } $actions['view'] = sprintf( '<a class="comment-link" href="%s" aria-label="%s">%s</a>', esc_url( get_comment_link( $comment ) ), esc_attr__( 'View this comment' ), __( 'View' ) ); /** * Filters the action links displayed for each comment in the 'Recent Comments' * dashboard widget. * * @since 2.6.0 * * @param string[] $actions An array of comment actions. Default actions include: * 'Approve', 'Unapprove', 'Edit', 'Reply', 'Spam', * 'Delete', and 'Trash'. * @param WP_Comment $comment The comment object. */ $actions = apply_filters( 'comment_row_actions', array_filter( $actions ), $comment ); $i = 0; foreach ( $actions as $action => $link ) { ++$i; if ( ( ( 'approve' === $action || 'unapprove' === $action ) && 2 === $i ) || 1 === $i ) { $separator = ''; } else { $separator = ' | '; } // Reply and quickedit need a hide-if-no-js span. if ( 'reply' === $action || 'quickedit' === $action ) { $action .= ' hide-if-no-js'; } if ( 'view' === $action && '1' !== $comment->comment_approved ) { $action .= ' hidden'; } $actions_string .= "<span class='$action'>{$separator}{$link}</span>"; } } ?> <li id="comment-<?php echo $comment->comment_ID; ?>" <?php comment_class( array( 'comment-item', wp_get_comment_status( $comment ) ), $comment ); ?>> <?php $comment_row_class = ''; if ( get_option( 'show_avatars' ) ) { echo get_avatar( $comment, 50, 'mystery' ); $comment_row_class .= ' has-avatar'; } ?> <?php if ( ! $comment->comment_type || 'comment' === $comment->comment_type ) : ?> <div class="dashboard-comment-wrap has-row-actions <?php echo $comment_row_class; ?>"> <p class="comment-meta"> <?php // Comments might not have a post they relate to, e.g. programmatically created ones. if ( $comment_post_link ) { printf( /* translators: 1: Comment author, 2: Post link, 3: Notification if the comment is pending. */ __( 'From %1$s on %2$s %3$s' ), '<cite class="comment-author">' . get_comment_author_link( $comment ) . '</cite>', $comment_post_link, '<span class="approve">' . __( '[Pending]' ) . '</span>' ); } else { printf( /* translators: 1: Comment author, 2: Notification if the comment is pending. */ __( 'From %1$s %2$s' ), '<cite class="comment-author">' . get_comment_author_link( $comment ) . '</cite>', '<span class="approve">' . __( '[Pending]' ) . '</span>' ); } ?> </p> <?php else : switch ( $comment->comment_type ) { case 'pingback': $type = __( 'Pingback' ); break; case 'trackback': $type = __( 'Trackback' ); break; default: $type = ucwords( $comment->comment_type ); } $type = esc_html( $type ); ?> <div class="dashboard-comment-wrap has-row-actions"> <p class="comment-meta"> <?php // Pingbacks, Trackbacks or custom comment types might not have a post they relate to, e.g. programmatically created ones. if ( $comment_post_link ) { printf( /* translators: 1: Type of comment, 2: Post link, 3: Notification if the comment is pending. */ _x( '%1$s on %2$s %3$s', 'dashboard' ), "<strong>$type</strong>", $comment_post_link, '<span class="approve">' . __( '[Pending]' ) . '</span>' ); } else { printf( /* translators: 1: Type of comment, 2: Notification if the comment is pending. */ _x( '%1$s %2$s', 'dashboard' ), "<strong>$type</strong>", '<span class="approve">' . __( '[Pending]' ) . '</span>' ); } ?> </p> <p class="comment-author"><?php comment_author_link( $comment ); ?></p> <?php endif; // comment_type ?> <blockquote><p><?php comment_excerpt( $comment ); ?></p></blockquote> <?php if ( $actions_string ) : ?> <p class="row-actions"><?php echo $actions_string; ?></p> <?php endif; ?> </div> </li> <?php $GLOBALS['comment'] = null; } /** * Outputs the Activity widget. * * Callback function for {@see 'dashboard_activity'}. * * @since 3.8.0 */ function wp_dashboard_site_activity() { echo '<div id="activity-widget">'; $future_posts = wp_dashboard_recent_posts( array( 'max' => 5, 'status' => 'future', 'order' => 'ASC', 'title' => __( 'Publishing Soon' ), 'id' => 'future-posts', ) ); $recent_posts = wp_dashboard_recent_posts( array( 'max' => 5, 'status' => 'publish', 'order' => 'DESC', 'title' => __( 'Recently Published' ), 'id' => 'published-posts', ) ); $recent_comments = wp_dashboard_recent_comments(); if ( ! $future_posts && ! $recent_posts && ! $recent_comments ) { echo '<div class="no-activity">'; echo '<p>' . __( 'No activity yet!' ) . '</p>'; echo '</div>'; } echo '</div>'; } /** * Generates Publishing Soon and Recently Published sections. * * @since 3.8.0 * * @param array $args { * An array of query and display arguments. * * @type int $max Number of posts to display. * @type string $status Post status. * @type string $order Designates ascending ('ASC') or descending ('DESC') order. * @type string $title Section title. * @type string $id The container id. * } * @return bool False if no posts were found. True otherwise. */ function wp_dashboard_recent_posts( $args ) { $query_args = array( 'post_type' => 'post', 'post_status' => $args['status'], 'orderby' => 'date', 'order' => $args['order'], 'posts_per_page' => (int) $args['max'], 'no_found_rows' => true, 'cache_results' => true, 'perm' => ( 'future' === $args['status'] ) ? 'editable' : 'readable', ); /** * Filters the query arguments used for the Recent Posts widget. * * @since 4.2.0 * * @param array $query_args The arguments passed to WP_Query to produce the list of posts. */ $query_args = apply_filters( 'dashboard_recent_posts_query_args', $query_args ); $posts = new WP_Query( $query_args ); if ( $posts->have_posts() ) { echo '<div id="' . $args['id'] . '" class="activity-block">'; echo '<h3>' . $args['title'] . '</h3>'; echo '<ul>'; $today = current_time( 'Y-m-d' ); $tomorrow = current_datetime()->modify( '+1 day' )->format( 'Y-m-d' ); $year = current_time( 'Y' ); while ( $posts->have_posts() ) { $posts->the_post(); $time = get_the_time( 'U' ); if ( gmdate( 'Y-m-d', $time ) === $today ) { $relative = __( 'Today' ); } elseif ( gmdate( 'Y-m-d', $time ) === $tomorrow ) { $relative = __( 'Tomorrow' ); } elseif ( gmdate( 'Y', $time ) !== $year ) { /* translators: Date and time format for recent posts on the dashboard, from a different calendar year, see https://www.php.net/manual/datetime.format.php */ $relative = date_i18n( __( 'M jS Y' ), $time ); } else { /* translators: Date and time format for recent posts on the dashboard, see https://www.php.net/manual/datetime.format.php */ $relative = date_i18n( __( 'M jS' ), $time ); } // Use the post edit link for those who can edit, the permalink otherwise. $recent_post_link = current_user_can( 'edit_post', get_the_ID() ) ? get_edit_post_link() : get_permalink(); $draft_or_post_title = _draft_or_post_title(); printf( '<li><span>%1$s</span> <a href="%2$s" aria-label="%3$s">%4$s</a></li>', /* translators: 1: Relative date, 2: Time. */ sprintf( _x( '%1$s, %2$s', 'dashboard' ), $relative, get_the_time() ), $recent_post_link, /* translators: %s: Post title. */ esc_attr( sprintf( __( 'Edit “%s”' ), $draft_or_post_title ) ), $draft_or_post_title ); } echo '</ul>'; echo '</div>'; } else { return false; } wp_reset_postdata(); return true; } /** * Show Comments section. * * @since 3.8.0 * * @param int $total_items Optional. Number of comments to query. Default 5. * @return bool False if no comments were found. True otherwise. */ function wp_dashboard_recent_comments( $total_items = 5 ) { // Select all comment types and filter out spam later for better query performance. $comments = array(); $comments_query = array( 'number' => $total_items * 5, 'offset' => 0, ); if ( ! current_user_can( 'edit_posts' ) ) { $comments_query['status'] = 'approve'; } while ( count( $comments ) < $total_items && $possible = get_comments( $comments_query ) ) { if ( ! is_array( $possible ) ) { break; } foreach ( $possible as $comment ) { if ( ! current_user_can( 'edit_post', $comment->comment_post_ID ) && ( post_password_required( $comment->comment_post_ID ) || ! current_user_can( 'read_post', $comment->comment_post_ID ) ) ) { // The user has no access to the post and thus cannot see the comments. continue; } $comments[] = $comment; if ( count( $comments ) === $total_items ) { break 2; } } $comments_query['offset'] += $comments_query['number']; $comments_query['number'] = $total_items * 10; } if ( $comments ) { echo '<div id="latest-comments" class="activity-block table-view-list">'; echo '<h3>' . __( 'Recent Comments' ) . '</h3>'; echo '<ul id="the-comment-list" data-wp-lists="list:comment">'; foreach ( $comments as $comment ) { _wp_dashboard_recent_comments_row( $comment ); } echo '</ul>'; if ( current_user_can( 'edit_posts' ) ) { echo '<h3 class="screen-reader-text">' . /* translators: Hidden accessibility text. */ __( 'View more comments' ) . '</h3>'; _get_list_table( 'WP_Comments_List_Table' )->views(); } wp_comment_reply( -1, false, 'dashboard', false ); wp_comment_trashnotice(); echo '</div>'; } else { return false; } return true; } /** * Display generic dashboard RSS widget feed. * * @since 2.5.0 * * @param string $widget_id */ function wp_dashboard_rss_output( $widget_id ) { $widgets = get_option( 'dashboard_widget_options' ); echo '<div class="rss-widget">'; wp_widget_rss_output( $widgets[ $widget_id ] ); echo '</div>'; } /** * Checks to see if all of the feed url in $check_urls are cached. * * If $check_urls is empty, look for the rss feed url found in the dashboard * widget options of $widget_id. If cached, call $callback, a function that * echoes out output for this widget. If not cache, echo a "Loading..." stub * which is later replaced by Ajax call (see top of /wp-admin/index.php) * * @since 2.5.0 * @since 5.3.0 Formalized the existing and already documented `...$args` parameter * by adding it to the function signature. * * @param string $widget_id The widget ID. * @param callable $callback The callback function used to display each feed. * @param array $check_urls RSS feeds. * @param mixed ...$args Optional additional parameters to pass to the callback function. * @return bool True on success, false on failure. */ function wp_dashboard_cached_rss_widget( $widget_id, $callback, $check_urls = array(), ...$args ) { $doing_ajax = wp_doing_ajax(); $loading = '<p class="widget-loading hide-if-no-js">' . __( 'Loading…' ) . '</p>'; $loading .= wp_get_admin_notice( __( 'This widget requires JavaScript.' ), array( 'type' => 'error', 'additional_classes' => array( 'inline', 'hide-if-js' ), ) ); if ( empty( $check_urls ) ) { $widgets = get_option( 'dashboard_widget_options' ); if ( empty( $widgets[ $widget_id ]['url'] ) && ! $doing_ajax ) { echo $loading; return false; } $check_urls = array( $widgets[ $widget_id ]['url'] ); } $locale = get_user_locale(); $cache_key = 'dash_v2_' . md5( $widget_id . '_' . $locale ); $output = get_transient( $cache_key ); if ( false !== $output ) { echo $output; return true; } if ( ! $doing_ajax ) { echo $loading; return false; } if ( $callback && is_callable( $callback ) ) { array_unshift( $args, $widget_id, $check_urls ); ob_start(); call_user_func_array( $callback, $args ); // Default lifetime in cache of 12 hours (same as the feeds). set_transient( $cache_key, ob_get_flush(), 12 * HOUR_IN_SECONDS ); } return true; } // // Dashboard Widgets Controls. // /** * Calls widget control callback. * * @since 2.5.0 * * @global callable[] $wp_dashboard_control_callbacks * * @param int|false $widget_control_id Optional. Registered widget ID. Default false. */ function wp_dashboard_trigger_widget_control( $widget_control_id = false ) { global $wp_dashboard_control_callbacks; if ( is_scalar( $widget_control_id ) && $widget_control_id && isset( $wp_dashboard_control_callbacks[ $widget_control_id ] ) && is_callable( $wp_dashboard_control_callbacks[ $widget_control_id ] ) ) { call_user_func( $wp_dashboard_control_callbacks[ $widget_control_id ], '', array( 'id' => $widget_control_id, 'callback' => $wp_dashboard_control_callbacks[ $widget_control_id ], ) ); } } /** * Sets up the RSS dashboard widget control and $args to be used as input to wp_widget_rss_form(). * * Handles POST data from RSS-type widgets. * * @since 2.5.0 * * @param string $widget_id * @param array $form_inputs */ function wp_dashboard_rss_control( $widget_id, $form_inputs = array() ) { $widget_options = get_option( 'dashboard_widget_options' ); if ( ! $widget_options ) { $widget_options = array(); } if ( ! isset( $widget_options[ $widget_id ] ) ) { $widget_options[ $widget_id ] = array(); } $number = 1; // Hack to use wp_widget_rss_form(). $widget_options[ $widget_id ]['number'] = $number; if ( 'POST' === $_SERVER['REQUEST_METHOD'] && isset( $_POST['widget-rss'][ $number ] ) ) { $_POST['widget-rss'][ $number ] = wp_unslash( $_POST['widget-rss'][ $number ] ); $widget_options[ $widget_id ] = wp_widget_rss_process( $_POST['widget-rss'][ $number ] ); $widget_options[ $widget_id ]['number'] = $number; // Title is optional. If black, fill it if possible. if ( ! $widget_options[ $widget_id ]['title'] && isset( $_POST['widget-rss'][ $number ]['title'] ) ) { $rss = fetch_feed( $widget_options[ $widget_id ]['url'] ); if ( is_wp_error( $rss ) ) { $widget_options[ $widget_id ]['title'] = htmlentities( __( 'Unknown Feed' ) ); } else { $widget_options[ $widget_id ]['title'] = htmlentities( strip_tags( $rss->get_title() ) ); $rss->__destruct(); unset( $rss ); } } update_option( 'dashboard_widget_options', $widget_options ); $locale = get_user_locale(); $cache_key = 'dash_v2_' . md5( $widget_id . '_' . $locale ); delete_transient( $cache_key ); } wp_widget_rss_form( $widget_options[ $widget_id ], $form_inputs ); } /** * Renders the Events and News dashboard widget. * * @since 4.8.0 */ function wp_dashboard_events_news() { wp_print_community_events_markup(); ?> <div class="wordpress-news hide-if-no-js"> <?php wp_dashboard_primary(); ?> </div> <p class="community-events-footer"> <?php printf( '<a href="%1$s" target="_blank">%2$s <span class="screen-reader-text"> %3$s</span><span aria-hidden="true" class="dashicons dashicons-external"></span></a>', 'https://make.wordpress.org/community/meetups-landing-page', __( 'Meetups' ), /* translators: Hidden accessibility text. */ __( '(opens in a new tab)' ) ); ?> | <?php printf( '<a href="%1$s" target="_blank">%2$s <span class="screen-reader-text"> %3$s</span><span aria-hidden="true" class="dashicons dashicons-external"></span></a>', 'https://central.wordcamp.org/schedule/', __( 'WordCamps' ), /* translators: Hidden accessibility text. */ __( '(opens in a new tab)' ) ); ?> | <?php printf( '<a href="%1$s" target="_blank">%2$s <span class="screen-reader-text"> %3$s</span><span aria-hidden="true" class="dashicons dashicons-external"></span></a>', /* translators: If a Rosetta site exists (e.g. https://es.wordpress.org/news/), then use that. Otherwise, leave untranslated. */ esc_url( _x( 'https://wordpress.org/news/', 'Events and News dashboard widget' ) ), __( 'News' ), /* translators: Hidden accessibility text. */ __( '(opens in a new tab)' ) ); ?> </p> <?php } /** * Prints the markup for the Community Events section of the Events and News Dashboard widget. * * @since 4.8.0 */ function wp_print_community_events_markup() { $community_events_notice = '<p class="hide-if-js">' . ( 'This widget requires JavaScript.' ) . '</p>'; $community_events_notice .= '<p class="community-events-error-occurred" aria-hidden="true">' . __( 'An error occurred. Please try again.' ) . '</p>'; $community_events_notice .= '<p class="community-events-could-not-locate" aria-hidden="true"></p>'; wp_admin_notice( $community_events_notice, array( 'type' => 'error', 'additional_classes' => array( 'community-events-errors', 'inline', 'hide-if-js' ), 'paragraph_wrap' => false, ) ); /* * Hide the main element when the page first loads, because the content * won't be ready until wp.communityEvents.renderEventsTemplate() has run. */ ?> <div id="community-events" class="community-events" aria-hidden="true"> <div class="activity-block"> <p> <span id="community-events-location-message"></span> <button class="button-link community-events-toggle-location" aria-expanded="false"> <span class="dashicons dashicons-location" aria-hidden="true"></span> <span class="community-events-location-edit"><?php _e( 'Select location' ); ?></span> </button> </p> <form class="community-events-form" aria-hidden="true" action="<?php echo esc_url( admin_url( 'admin-ajax.php' ) ); ?>" method="post"> <label for="community-events-location"> <?php _e( 'City:' ); ?> </label> <?php /* translators: Replace with a city related to your locale. * Test that it matches the expected location and has upcoming * events before including it. If no cities related to your * locale have events, then use a city related to your locale * that would be recognizable to most users. Use only the city * name itself, without any region or country. Use the endonym * (native locale name) instead of the English name if possible. */ ?> <input id="community-events-location" class="regular-text" type="text" name="community-events-location" placeholder="<?php esc_attr_e( 'Cincinnati' ); ?>" /> <?php submit_button( __( 'Submit' ), 'secondary', 'community-events-submit', false ); ?> <button class="community-events-cancel button-link" type="button" aria-expanded="false"> <?php _e( 'Cancel' ); ?> </button> <span class="spinner"></span> </form> </div> <ul class="community-events-results activity-block last"></ul> </div> <?php } /** * Renders the events templates for the Event and News widget. * * @since 4.8.0 */ function wp_print_community_events_templates() { ?> <script id="tmpl-community-events-attend-event-near" type="text/template"> <?php printf( /* translators: %s: The name of a city. */ __( 'Attend an upcoming event near %s.' ), '<strong>{{ data.location.description }}</strong>' ); ?> </script> <script id="tmpl-community-events-could-not-locate" type="text/template"> <?php printf( /* translators: %s is the name of the city we couldn't locate. * Replace the examples with cities in your locale, but test * that they match the expected location before including them. * Use endonyms (native locale names) whenever possible. */ __( '%s could not be located. Please try another nearby city. For example: Kansas City; Springfield; Portland.' ), '<em>{{data.unknownCity}}</em>' ); ?> </script> <script id="tmpl-community-events-event-list" type="text/template"> <# _.each( data.events, function( event ) { #> <li class="event event-{{ event.type }} wp-clearfix"> <div class="event-info"> <div class="dashicons event-icon" aria-hidden="true"></div> <div class="event-info-inner"> <a class="event-title" href="{{ event.url }}">{{ event.title }}</a> <# if ( event.type ) { const titleCaseEventType = event.type.replace( /\w\S*/g, function ( type ) { return type.charAt(0).toUpperCase() + type.substr(1).toLowerCase(); } ); #> {{ 'wordcamp' === event.type ? 'WordCamp' : titleCaseEventType }} <span class="ce-separator"></span> <# } #> <span class="event-city">{{ event.location.location }}</span> </div> </div> <div class="event-date-time"> <span class="event-date">{{ event.user_formatted_date }}</span> <# if ( 'meetup' === event.type ) { #> <span class="event-time"> {{ event.user_formatted_time }} {{ event.timeZoneAbbreviation }} </span> <# } #> </div> </li> <# } ) #> <# if ( data.events.length <= 2 ) { #> <li class="event-none"> <?php printf( /* translators: %s: Localized meetup organization documentation URL. */ __( 'Want more events? <a href="%s">Help organize the next one</a>!' ), __( 'https://make.wordpress.org/community/organize-event-landing-page/' ) ); ?> </li> <# } #> </script> <script id="tmpl-community-events-no-upcoming-events" type="text/template"> <li class="event-none"> <# if ( data.location.description ) { #> <?php printf( /* translators: 1: The city the user searched for, 2: Meetup organization documentation URL. */ __( 'There are no events scheduled near %1$s at the moment. Would you like to <a href="%2$s">organize a WordPress event</a>?' ), '{{ data.location.description }}', __( 'https://make.wordpress.org/community/handbook/meetup-organizer/welcome/' ) ); ?> <# } else { #> <?php printf( /* translators: %s: Meetup organization documentation URL. */ __( 'There are no events scheduled near you at the moment. Would you like to <a href="%s">organize a WordPress event</a>?' ), __( 'https://make.wordpress.org/community/handbook/meetup-organizer/welcome/' ) ); ?> <# } #> </li> </script> <?php } /** * 'WordPress Events and News' dashboard widget. * * @since 2.7.0 * @since 4.8.0 Removed popular plugins feed. */ function wp_dashboard_primary() { $feeds = array( 'news' => array( /** * Filters the primary link URL for the 'WordPress Events and News' dashboard widget. * * @since 2.5.0 * * @param string $link The widget's primary link URL. */ 'link' => apply_filters( 'dashboard_primary_link', __( 'https://wordpress.org/news/' ) ), /** * Filters the primary feed URL for the 'WordPress Events and News' dashboard widget. * * @since 2.3.0 * * @param string $url The widget's primary feed URL. */ 'url' => apply_filters( 'dashboard_primary_feed', __( 'https://wordpress.org/news/feed/' ) ), /** * Filters the primary link title for the 'WordPress Events and News' dashboard widget. * * @since 2.3.0 * * @param string $title Title attribute for the widget's primary link. */ 'title' => apply_filters( 'dashboard_primary_title', __( 'WordPress Blog' ) ), 'items' => 2, 'show_summary' => 0, 'show_author' => 0, 'show_date' => 0, ), 'planet' => array( /** * Filters the secondary link URL for the 'WordPress Events and News' dashboard widget. * * @since 2.3.0 * * @param string $link The widget's secondary link URL. */ 'link' => apply_filters( 'dashboard_secondary_link', /* translators: Link to the Planet website of the locale. */ __( 'https://planet.wordpress.org/' ) ), /** * Filters the secondary feed URL for the 'WordPress Events and News' dashboard widget. * * @since 2.3.0 * * @param string $url The widget's secondary feed URL. */ 'url' => apply_filters( 'dashboard_secondary_feed', /* translators: Link to the Planet feed of the locale. */ __( 'https://planet.wordpress.org/feed/' ) ), /** * Filters the secondary link title for the 'WordPress Events and News' dashboard widget. * * @since 2.3.0 * * @param string $title Title attribute for the widget's secondary link. */ 'title' => apply_filters( 'dashboard_secondary_title', __( 'Other WordPress News' ) ), /** * Filters the number of secondary link items for the 'WordPress Events and News' dashboard widget. * * @since 4.4.0 * * @param string $items How many items to show in the secondary feed. */ 'items' => apply_filters( 'dashboard_secondary_items', 3 ), 'show_summary' => 0, 'show_author' => 0, 'show_date' => 0, ), ); wp_dashboard_cached_rss_widget( 'dashboard_primary', 'wp_dashboard_primary_output', $feeds ); } /** * Displays the WordPress events and news feeds. * * @since 3.8.0 * @since 4.8.0 Removed popular plugins feed. * * @param string $widget_id Widget ID. * @param array $feeds Array of RSS feeds. */ function wp_dashboard_primary_output( $widget_id, $feeds ) { foreach ( $feeds as $type => $args ) { $args['type'] = $type; echo '<div class="rss-widget">'; wp_widget_rss_output( $args['url'], $args ); echo '</div>'; } } /** * Displays file upload quota on dashboard. * * Runs on the {@see 'activity_box_end'} hook in wp_dashboard_right_now(). * * @since 3.0.0 * * @return true|void True if not multisite, user can't upload files, or the space check option is disabled. */ function wp_dashboard_quota() { if ( ! is_multisite() || ! current_user_can( 'upload_files' ) || get_site_option( 'upload_space_check_disabled' ) ) { return true; } $quota = get_space_allowed(); $used = get_space_used(); if ( $used > $quota ) { $percentused = '100'; } else { $percentused = ( $used / $quota ) * 100; } $used_class = ( $percentused >= 70 ) ? ' warning' : ''; $used = round( $used, 2 ); $percentused = number_format( $percentused ); ?> <h3 class="mu-storage"><?php _e( 'Storage Space' ); ?></h3> <div class="mu-storage"> <ul> <li class="storage-count"> <?php $text = sprintf( /* translators: %s: Number of megabytes. */ __( '%s MB Space Allowed' ), number_format_i18n( $quota ) ); printf( '<a href="%1$s">%2$s<span class="screen-reader-text"> (%3$s)</span></a>', esc_url( admin_url( 'upload.php' ) ), $text, /* translators: Hidden accessibility text. */ __( 'Manage Uploads' ) ); ?> </li><li class="storage-count <?php echo $used_class; ?>"> <?php $text = sprintf( /* translators: 1: Number of megabytes, 2: Percentage. */ __( '%1$s MB (%2$s%%) Space Used' ), number_format_i18n( $used, 2 ), $percentused ); printf( '<a href="%1$s" class="musublink">%2$s<span class="screen-reader-text"> (%3$s)</span></a>', esc_url( admin_url( 'upload.php' ) ), $text, /* translators: Hidden accessibility text. */ __( 'Manage Uploads' ) ); ?> </li> </ul> </div> <?php } /** * Displays the browser update nag. * * @since 3.2.0 * @since 5.8.0 Added a special message for Internet Explorer users. * * @global bool $is_IE */ function wp_dashboard_browser_nag() { global $is_IE; $notice = ''; $response = wp_check_browser_version(); if ( $response ) { if ( $is_IE ) { $msg = __( 'Internet Explorer does not give you the best WordPress experience. Switch to Microsoft Edge, or another more modern browser to get the most from your site.' ); } elseif ( $response['insecure'] ) { $msg = sprintf( /* translators: %s: Browser name and link. */ __( "It looks like you're using an insecure version of %s. Using an outdated browser makes your computer unsafe. For the best WordPress experience, please update your browser." ), sprintf( '<a href="%s">%s</a>', esc_url( $response['update_url'] ), esc_html( $response['name'] ) ) ); } else { $msg = sprintf( /* translators: %s: Browser name and link. */ __( "It looks like you're using an old version of %s. For the best WordPress experience, please update your browser." ), sprintf( '<a href="%s">%s</a>', esc_url( $response['update_url'] ), esc_html( $response['name'] ) ) ); } $browser_nag_class = ''; if ( ! empty( $response['img_src'] ) ) { $img_src = ( is_ssl() && ! empty( $response['img_src_ssl'] ) ) ? $response['img_src_ssl'] : $response['img_src']; $notice .= '<div class="alignright browser-icon"><img src="' . esc_url( $img_src ) . '" alt="" /></div>'; $browser_nag_class = ' has-browser-icon'; } $notice .= "<p class='browser-update-nag{$browser_nag_class}'>{$msg}</p>"; $browsehappy = 'https://browsehappy.com/'; $locale = get_user_locale(); if ( 'en_US' !== $locale ) { $browsehappy = add_query_arg( 'locale', $locale, $browsehappy ); } if ( $is_IE ) { $msg_browsehappy = sprintf( /* translators: %s: Browse Happy URL. */ __( 'Learn how to <a href="%s" class="update-browser-link">browse happy</a>' ), esc_url( $browsehappy ) ); } else { $msg_browsehappy = sprintf( /* translators: 1: Browser update URL, 2: Browser name, 3: Browse Happy URL. */ __( '<a href="%1$s" class="update-browser-link">Update %2$s</a> or learn how to <a href="%3$s" class="browse-happy-link">browse happy</a>' ), esc_attr( $response['update_url'] ), esc_html( $response['name'] ), esc_url( $browsehappy ) ); } $notice .= '<p>' . $msg_browsehappy . '</p>'; $notice .= '<p class="hide-if-no-js"><a href="" class="dismiss" aria-label="' . esc_attr__( 'Dismiss the browser warning panel' ) . '">' . __( 'Dismiss' ) . '</a></p>'; $notice .= '<div class="clear"></div>'; } /** * Filters the notice output for the 'Browse Happy' nag meta box. * * @since 3.2.0 * * @param string $notice The notice content. * @param array|false $response An array containing web browser information, or * false on failure. See wp_check_browser_version(). */ echo apply_filters( 'browse-happy-notice', $notice, $response ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores } /** * Adds an additional class to the browser nag if the current version is insecure. * * @since 3.2.0 * * @param string[] $classes Array of meta box classes. * @return string[] Modified array of meta box classes. */ function dashboard_browser_nag_class( $classes ) { $response = wp_check_browser_version(); if ( $response && $response['insecure'] ) { $classes[] = 'browser-insecure'; } return $classes; } /** * Checks if the user needs a browser update. * * @since 3.2.0 * * @return array|false Array of browser data on success, false on failure. */ function wp_check_browser_version() { if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) { return false; } $key = md5( $_SERVER['HTTP_USER_AGENT'] ); $response = get_site_transient( 'browser_' . $key ); if ( false === $response ) { // Include an unmodified $wp_version. require ABSPATH . WPINC . '/version.php'; $url = 'http://api.wordpress.org/core/browse-happy/1.1/'; $options = array( 'body' => array( 'useragent' => $_SERVER['HTTP_USER_AGENT'] ), 'user-agent' => 'WordPress/' . $wp_version . '; ' . home_url( '/' ), ); if ( wp_http_supports( array( 'ssl' ) ) ) { $url = set_url_scheme( $url, 'https' ); } $response = wp_remote_post( $url, $options ); if ( is_wp_error( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) { return false; } /** * Response should be an array with: * 'platform' - string - A user-friendly platform name, if it can be determined * 'name' - string - A user-friendly browser name * 'version' - string - The version of the browser the user is using * 'current_version' - string - The most recent version of the browser * 'upgrade' - boolean - Whether the browser needs an upgrade * 'insecure' - boolean - Whether the browser is deemed insecure * 'update_url' - string - The url to visit to upgrade * 'img_src' - string - An image representing the browser * 'img_src_ssl' - string - An image (over SSL) representing the browser */ $response = json_decode( wp_remote_retrieve_body( $response ), true ); if ( ! is_array( $response ) ) { return false; } set_site_transient( 'browser_' . $key, $response, WEEK_IN_SECONDS ); } return $response; } /** * Displays the PHP update nag. * * @since 5.1.0 */ function wp_dashboard_php_nag() { $response = wp_check_php_version(); if ( ! $response ) { return; } if ( isset( $response['is_secure'] ) && ! $response['is_secure'] ) { // The `is_secure` array key name doesn't actually imply this is a secure version of PHP. It only means it receives security updates. if ( $response['is_lower_than_future_minimum'] ) { $message = sprintf( /* translators: %s: The server PHP version. */ __( 'Your site is running on an outdated version of PHP (%s), which does not receive security updates and soon will not be supported by WordPress. Ensure that PHP is updated on your server as soon as possible. Otherwise you will not be able to upgrade WordPress.' ), PHP_VERSION ); } else { $message = sprintf( /* translators: %s: The server PHP version. */ __( 'Your site is running on an outdated version of PHP (%s), which does not receive security updates. It should be updated.' ), PHP_VERSION ); } } elseif ( $response['is_lower_than_future_minimum'] ) { $message = sprintf( /* translators: %s: The server PHP version. */ __( 'Your site is running on an outdated version of PHP (%s), which soon will not be supported by WordPress. Ensure that PHP is updated on your server as soon as possible. Otherwise you will not be able to upgrade WordPress.' ), PHP_VERSION ); } else { $message = sprintf( /* translators: %s: The server PHP version. */ __( 'Your site is running on an outdated version of PHP (%s), which should be updated.' ), PHP_VERSION ); } ?> <p class="bigger-bolder-text"><?php echo $message; ?></p> <p><?php _e( 'What is PHP and how does it affect my site?' ); ?></p> <p> <?php _e( 'PHP is one of the programming languages used to build WordPress. Newer versions of PHP receive regular security updates and may increase your site’s performance.' ); ?> <?php if ( ! empty( $response['recommended_version'] ) ) { printf( /* translators: %s: The minimum recommended PHP version. */ __( 'The minimum recommended version of PHP is %s.' ), $response['recommended_version'] ); } ?> </p> <p class="button-container"> <?php printf( '<a class="button button-primary" href="%1$s" target="_blank" rel="noopener">%2$s<span class="screen-reader-text"> %3$s</span><span aria-hidden="true" class="dashicons dashicons-external"></span></a>', esc_url( wp_get_update_php_url() ), __( 'Learn more about updating PHP' ), /* translators: Hidden accessibility text. */ __( '(opens in a new tab)' ) ); ?> </p> <?php wp_update_php_annotation(); wp_direct_php_update_button(); } /** * Adds an additional class to the PHP nag if the current version is insecure. * * @since 5.1.0 * * @param string[] $classes Array of meta box classes. * @return string[] Modified array of meta box classes. */ function dashboard_php_nag_class( $classes ) { $response = wp_check_php_version(); if ( ! $response ) { return $classes; } if ( isset( $response['is_secure'] ) && ! $response['is_secure'] ) { $classes[] = 'php-no-security-updates'; } elseif ( $response['is_lower_than_future_minimum'] ) { $classes[] = 'php-version-lower-than-future-minimum'; } return $classes; } /** * Displays the Site Health Status widget. * * @since 5.4.0 */ function wp_dashboard_site_health() { $get_issues = get_transient( 'health-check-site-status-result' ); $issue_counts = array(); if ( false !== $get_issues ) { $issue_counts = json_decode( $get_issues, true ); } if ( ! is_array( $issue_counts ) || ! $issue_counts ) { $issue_counts = array( 'good' => 0, 'recommended' => 0, 'critical' => 0, ); } $issues_total = $issue_counts['recommended'] + $issue_counts['critical']; ?> <div class="health-check-widget"> <div class="health-check-widget-title-section site-health-progress-wrapper loading hide-if-no-js"> <div class="site-health-progress"> <svg aria-hidden="true" focusable="false" width="100%" height="100%" viewBox="0 0 200 200" version="1.1" xmlns="http://www.w3.org/2000/svg"> <circle r="90" cx="100" cy="100" fill="transparent" stroke-dasharray="565.48" stroke-dashoffset="0"></circle> <circle id="bar" r="90" cx="100" cy="100" fill="transparent" stroke-dasharray="565.48" stroke-dashoffset="0"></circle> </svg> </div> <div class="site-health-progress-label"> <?php if ( false === $get_issues ) : ?> <?php _e( 'No information yet…' ); ?> <?php else : ?> <?php _e( 'Results are still loading…' ); ?> <?php endif; ?> </div> </div> <div class="site-health-details"> <?php if ( false === $get_issues ) : ?> <p> <?php printf( /* translators: %s: URL to Site Health screen. */ __( 'Site health checks will automatically run periodically to gather information about your site. You can also <a href="%s">visit the Site Health screen</a> to gather information about your site now.' ), esc_url( admin_url( 'site-health.php' ) ) ); ?> </p> <?php else : ?> <p> <?php if ( $issues_total <= 0 ) : ?> <?php _e( 'Great job! Your site currently passes all site health checks.' ); ?> <?php elseif ( 1 === (int) $issue_counts['critical'] ) : ?> <?php _e( 'Your site has a critical issue that should be addressed as soon as possible to improve its performance and security.' ); ?> <?php elseif ( $issue_counts['critical'] > 1 ) : ?> <?php _e( 'Your site has critical issues that should be addressed as soon as possible to improve its performance and security.' ); ?> <?php elseif ( 1 === (int) $issue_counts['recommended'] ) : ?> <?php _e( 'Your site’s health is looking good, but there is still one thing you can do to improve its performance and security.' ); ?> <?php else : ?> <?php _e( 'Your site’s health is looking good, but there are still some things you can do to improve its performance and security.' ); ?> <?php endif; ?> </p> <?php endif; ?> <?php if ( $issues_total > 0 && false !== $get_issues ) : ?> <p> <?php printf( /* translators: 1: Number of issues. 2: URL to Site Health screen. */ _n( 'Take a look at the <strong>%1$d item</strong> on the <a href="%2$s">Site Health screen</a>.', 'Take a look at the <strong>%1$d items</strong> on the <a href="%2$s">Site Health screen</a>.', $issues_total ), $issues_total, esc_url( admin_url( 'site-health.php' ) ) ); ?> </p> <?php endif; ?> </div> </div> <?php } /** * Outputs empty dashboard widget to be populated by JS later. * * Usable by plugins. * * @since 2.5.0 */ function wp_dashboard_empty() {} /** * Displays a welcome panel to introduce users to WordPress. * * @since 3.3.0 * @since 5.9.0 Send users to the Site Editor if the active theme is block-based. */ function wp_welcome_panel() { list( $display_version ) = explode( '-', get_bloginfo( 'version' ) ); $can_customize = current_user_can( 'customize' ); $is_block_theme = wp_is_block_theme(); ?> <div class="welcome-panel-content"> <div class="welcome-panel-header"> <div class="welcome-panel-header-image"> <?php echo file_get_contents( dirname( __DIR__ ) . '/images/dashboard-background.svg' ); ?> </div> <h2><?php _e( 'Welcome to WordPress!' ); ?></h2> <p> <a href="<?php echo esc_url( admin_url( 'about.php' ) ); ?>"> <?php /* translators: %s: Current WordPress version. */ printf( __( 'Learn more about the %s version.' ), $display_version ); ?> </a> </p> </div> <div class="welcome-panel-column-container"> <div class="welcome-panel-column"> <svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"> <rect width="48" height="48" rx="4" fill="#1E1E1E"/> <path fill-rule="evenodd" clip-rule="evenodd" d="M32.0668 17.0854L28.8221 13.9454L18.2008 24.671L16.8983 29.0827L21.4257 27.8309L32.0668 17.0854ZM16 32.75H24V31.25H16V32.75Z" fill="white"/> </svg> <div class="welcome-panel-column-content"> <h3><?php _e( 'Author rich content with blocks and patterns' ); ?></h3> <p><?php _e( 'Block patterns are pre-configured block layouts. Use them to get inspired or create new pages in a flash.' ); ?></p> <a href="<?php echo esc_url( admin_url( 'post-new.php?post_type=page' ) ); ?>"><?php _e( 'Add a new page' ); ?></a> </div> </div> <div class="welcome-panel-column"> <svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"> <rect width="48" height="48" rx="4" fill="#1E1E1E"/> <path fill-rule="evenodd" clip-rule="evenodd" d="M18 16h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H18a2 2 0 0 1-2-2V18a2 2 0 0 1 2-2zm12 1.5H18a.5.5 0 0 0-.5.5v3h13v-3a.5.5 0 0 0-.5-.5zm.5 5H22v8h8a.5.5 0 0 0 .5-.5v-7.5zm-10 0h-3V30a.5.5 0 0 0 .5.5h2.5v-8z" fill="#fff"/> </svg> <div class="welcome-panel-column-content"> <?php if ( $is_block_theme ) : ?> <h3><?php _e( 'Customize your entire site with block themes' ); ?></h3> <p><?php _e( 'Design everything on your site — from the header down to the footer, all using blocks and patterns.' ); ?></p> <a href="<?php echo esc_url( admin_url( 'site-editor.php' ) ); ?>"><?php _e( 'Open site editor' ); ?></a> <?php else : ?> <h3><?php _e( 'Start Customizing' ); ?></h3> <p><?php _e( 'Configure your site’s logo, header, menus, and more in the Customizer.' ); ?></p> <?php if ( $can_customize ) : ?> <a class="load-customize hide-if-no-customize" href="<?php echo wp_customize_url(); ?>"><?php _e( 'Open the Customizer' ); ?></a> <?php endif; ?> <?php endif; ?> </div> </div> <div class="welcome-panel-column"> <svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false"> <rect width="48" height="48" rx="4" fill="#1E1E1E"/> <path fill-rule="evenodd" clip-rule="evenodd" d="M31 24a7 7 0 0 1-7 7V17a7 7 0 0 1 7 7zm-7-8a8 8 0 1 1 0 16 8 8 0 0 1 0-16z" fill="#fff"/> </svg> <div class="welcome-panel-column-content"> <?php if ( $is_block_theme ) : ?> <h3><?php _e( 'Switch up your site’s look & feel with Styles' ); ?></h3> <p><?php _e( 'Tweak your site, or give it a whole new look! Get creative — how about a new color palette or font?' ); ?></p> <a href="<?php echo esc_url( admin_url( '/site-editor.php?path=%2Fwp_global_styles' ) ); ?>"><?php _e( 'Edit styles' ); ?></a> <?php else : ?> <h3><?php _e( 'Discover a new way to build your site.' ); ?></h3> <p><?php _e( 'There is a new kind of WordPress theme, called a block theme, that lets you build the site you’ve always wanted — with blocks and styles.' ); ?></p> <a href="<?php echo esc_url( __( 'https://wordpress.org/documentation/article/block-themes/' ) ); ?>"><?php _e( 'Learn about block themes' ); ?></a> <?php endif; ?> </div> </div> </div> </div> <?php } includes/admin-filters.php 0000644 00000017475 15135536347 0011653 0 ustar 00 <?php /** * Administration API: Default admin hooks * * @package WordPress * @subpackage Administration * @since 4.3.0 */ // Bookmark hooks. add_action( 'admin_page_access_denied', 'wp_link_manager_disabled_message' ); // Dashboard hooks. add_action( 'activity_box_end', 'wp_dashboard_quota' ); add_action( 'welcome_panel', 'wp_welcome_panel' ); // Media hooks. add_action( 'attachment_submitbox_misc_actions', 'attachment_submitbox_metadata' ); add_filter( 'plupload_init', 'wp_show_heic_upload_error' ); add_action( 'media_upload_image', 'wp_media_upload_handler' ); add_action( 'media_upload_audio', 'wp_media_upload_handler' ); add_action( 'media_upload_video', 'wp_media_upload_handler' ); add_action( 'media_upload_file', 'wp_media_upload_handler' ); add_action( 'post-plupload-upload-ui', 'media_upload_flash_bypass' ); add_action( 'post-html-upload-ui', 'media_upload_html_bypass' ); add_filter( 'async_upload_image', 'get_media_item', 10, 2 ); add_filter( 'async_upload_audio', 'get_media_item', 10, 2 ); add_filter( 'async_upload_video', 'get_media_item', 10, 2 ); add_filter( 'async_upload_file', 'get_media_item', 10, 2 ); add_filter( 'media_upload_gallery', 'media_upload_gallery' ); add_filter( 'media_upload_library', 'media_upload_library' ); add_filter( 'media_upload_tabs', 'update_gallery_tab' ); // Admin color schemes. add_action( 'admin_init', 'register_admin_color_schemes', 1 ); add_action( 'admin_head', 'wp_color_scheme_settings' ); add_action( 'admin_color_scheme_picker', 'admin_color_scheme_picker' ); // Misc hooks. add_action( 'admin_init', 'wp_admin_headers' ); add_action( 'login_init', 'wp_admin_headers' ); add_action( 'admin_init', 'send_frame_options_header', 10, 0 ); add_action( 'admin_head', 'wp_admin_canonical_url' ); add_action( 'admin_head', 'wp_site_icon' ); add_action( 'admin_head', 'wp_admin_viewport_meta' ); add_action( 'customize_controls_head', 'wp_admin_viewport_meta' ); add_filter( 'nav_menu_meta_box_object', '_wp_nav_menu_meta_box_object' ); // Prerendering. if ( ! is_customize_preview() ) { add_filter( 'admin_print_styles', 'wp_resource_hints', 1 ); } add_action( 'admin_print_scripts', 'print_emoji_detection_script' ); add_action( 'admin_print_scripts', 'print_head_scripts', 20 ); add_action( 'admin_print_footer_scripts', '_wp_footer_scripts' ); add_action( 'admin_enqueue_scripts', 'wp_enqueue_emoji_styles' ); add_action( 'admin_print_styles', 'print_emoji_styles' ); // Retained for backwards-compatibility. Unhooked by wp_enqueue_emoji_styles(). add_action( 'admin_print_styles', 'print_admin_styles', 20 ); add_action( 'admin_print_scripts-index.php', 'wp_localize_community_events' ); add_action( 'admin_print_scripts-post.php', 'wp_page_reload_on_back_button_js' ); add_action( 'admin_print_scripts-post-new.php', 'wp_page_reload_on_back_button_js' ); add_action( 'update_option_home', 'update_home_siteurl', 10, 2 ); add_action( 'update_option_siteurl', 'update_home_siteurl', 10, 2 ); add_action( 'update_option_page_on_front', 'update_home_siteurl', 10, 2 ); add_action( 'update_option_admin_email', 'wp_site_admin_email_change_notification', 10, 3 ); add_action( 'add_option_new_admin_email', 'update_option_new_admin_email', 10, 2 ); add_action( 'update_option_new_admin_email', 'update_option_new_admin_email', 10, 2 ); add_filter( 'heartbeat_received', 'wp_check_locked_posts', 10, 3 ); add_filter( 'heartbeat_received', 'wp_refresh_post_lock', 10, 3 ); add_filter( 'heartbeat_received', 'heartbeat_autosave', 500, 2 ); add_filter( 'wp_refresh_nonces', 'wp_refresh_post_nonces', 10, 3 ); add_filter( 'wp_refresh_nonces', 'wp_refresh_metabox_loader_nonces', 10, 2 ); add_filter( 'wp_refresh_nonces', 'wp_refresh_heartbeat_nonces' ); add_filter( 'heartbeat_settings', 'wp_heartbeat_set_suspension' ); add_action( 'use_block_editor_for_post_type', '_disable_block_editor_for_navigation_post_type', 10, 2 ); add_action( 'edit_form_after_title', '_disable_content_editor_for_navigation_post_type' ); add_action( 'edit_form_after_editor', '_enable_content_editor_for_navigation_post_type' ); // Nav Menu hooks. add_action( 'admin_head-nav-menus.php', '_wp_delete_orphaned_draft_menu_items' ); // Plugin hooks. add_filter( 'allowed_options', 'option_update_filter' ); // Plugin Install hooks. add_action( 'install_plugins_featured', 'install_dashboard' ); add_action( 'install_plugins_upload', 'install_plugins_upload' ); add_action( 'install_plugins_search', 'display_plugins_table' ); add_action( 'install_plugins_popular', 'display_plugins_table' ); add_action( 'install_plugins_recommended', 'display_plugins_table' ); add_action( 'install_plugins_new', 'display_plugins_table' ); add_action( 'install_plugins_beta', 'display_plugins_table' ); add_action( 'install_plugins_favorites', 'display_plugins_table' ); add_action( 'install_plugins_pre_plugin-information', 'install_plugin_information' ); // Template hooks. add_action( 'admin_enqueue_scripts', array( 'WP_Internal_Pointers', 'enqueue_scripts' ) ); add_action( 'user_register', array( 'WP_Internal_Pointers', 'dismiss_pointers_for_new_users' ) ); // Theme hooks. add_action( 'customize_controls_print_footer_scripts', 'customize_themes_print_templates' ); // Theme Install hooks. add_action( 'install_themes_pre_theme-information', 'install_theme_information' ); // User hooks. add_action( 'admin_init', 'default_password_nag_handler' ); add_action( 'admin_notices', 'default_password_nag' ); add_action( 'admin_notices', 'new_user_email_admin_notice' ); add_action( 'profile_update', 'default_password_nag_edit_user', 10, 2 ); add_action( 'personal_options_update', 'send_confirmation_on_profile_email' ); // Update hooks. add_action( 'load-plugins.php', 'wp_plugin_update_rows', 20 ); // After wp_update_plugins() is called. add_action( 'load-themes.php', 'wp_theme_update_rows', 20 ); // After wp_update_themes() is called. add_action( 'admin_notices', 'update_nag', 3 ); add_action( 'admin_notices', 'deactivated_plugins_notice', 5 ); add_action( 'admin_notices', 'paused_plugins_notice', 5 ); add_action( 'admin_notices', 'paused_themes_notice', 5 ); add_action( 'admin_notices', 'maintenance_nag', 10 ); add_action( 'admin_notices', 'wp_recovery_mode_nag', 1 ); add_filter( 'update_footer', 'core_update_footer' ); // Update Core hooks. add_action( '_core_updated_successfully', '_redirect_to_about_wordpress' ); // Upgrade hooks. add_action( 'upgrader_process_complete', array( 'Language_Pack_Upgrader', 'async_upgrade' ), 20 ); add_action( 'upgrader_process_complete', 'wp_version_check', 10, 0 ); add_action( 'upgrader_process_complete', 'wp_update_plugins', 10, 0 ); add_action( 'upgrader_process_complete', 'wp_update_themes', 10, 0 ); // Privacy hooks. add_filter( 'wp_privacy_personal_data_erasure_page', 'wp_privacy_process_personal_data_erasure_page', 10, 5 ); add_filter( 'wp_privacy_personal_data_export_page', 'wp_privacy_process_personal_data_export_page', 10, 7 ); add_action( 'wp_privacy_personal_data_export_file', 'wp_privacy_generate_personal_data_export_file', 10 ); add_action( 'wp_privacy_personal_data_erased', '_wp_privacy_send_erasure_fulfillment_notification', 10 ); // Privacy policy text changes check. add_action( 'admin_init', array( 'WP_Privacy_Policy_Content', 'text_change_check' ), 100 ); // Show a "postbox" with the text suggestions for a privacy policy. add_action( 'admin_notices', array( 'WP_Privacy_Policy_Content', 'notice' ) ); // Add the suggested policy text from WordPress. add_action( 'admin_init', array( 'WP_Privacy_Policy_Content', 'add_suggested_content' ), 1 ); // Update the cached policy info when the policy page is updated. add_action( 'post_updated', array( 'WP_Privacy_Policy_Content', '_policy_page_updated' ) ); // Append '(Draft)' to draft page titles in the privacy page dropdown. add_filter( 'list_pages', '_wp_privacy_settings_filter_draft_page_titles', 10, 2 ); // Font management. add_action( 'admin_print_styles', 'wp_print_font_faces', 50 ); includes/deprecated.php 0000644 00000121460 15135536347 0011203 0 ustar 00 <?php /** * Deprecated admin functions from past WordPress versions. You shouldn't use these * functions and look for the alternatives instead. The functions will be removed * in a later version. * * @package WordPress * @subpackage Deprecated */ /* * Deprecated functions come here to die. */ /** * @since 2.1.0 * @deprecated 2.1.0 Use wp_editor() * @see wp_editor() */ function tinymce_include() { _deprecated_function( __FUNCTION__, '2.1.0', 'wp_editor()' ); wp_tiny_mce(); } /** * Unused Admin function. * * @since 2.0.0 * @deprecated 2.5.0 * */ function documentation_link() { _deprecated_function( __FUNCTION__, '2.5.0' ); } /** * Calculates the new dimensions for a downsampled image. * * @since 2.0.0 * @deprecated 3.0.0 Use wp_constrain_dimensions() * @see wp_constrain_dimensions() * * @param int $width Current width of the image * @param int $height Current height of the image * @param int $wmax Maximum wanted width * @param int $hmax Maximum wanted height * @return array Shrunk dimensions (width, height). */ function wp_shrink_dimensions( $width, $height, $wmax = 128, $hmax = 96 ) { _deprecated_function( __FUNCTION__, '3.0.0', 'wp_constrain_dimensions()' ); return wp_constrain_dimensions( $width, $height, $wmax, $hmax ); } /** * Calculated the new dimensions for a downsampled image. * * @since 2.0.0 * @deprecated 3.5.0 Use wp_constrain_dimensions() * @see wp_constrain_dimensions() * * @param int $width Current width of the image * @param int $height Current height of the image * @return array Shrunk dimensions (width, height). */ function get_udims( $width, $height ) { _deprecated_function( __FUNCTION__, '3.5.0', 'wp_constrain_dimensions()' ); return wp_constrain_dimensions( $width, $height, 128, 96 ); } /** * Legacy function used to generate the categories checklist control. * * @since 0.71 * @deprecated 2.6.0 Use wp_category_checklist() * @see wp_category_checklist() * * @global int $post_ID * * @param int $default_category Unused. * @param int $category_parent Unused. * @param array $popular_ids Unused. */ function dropdown_categories( $default_category = 0, $category_parent = 0, $popular_ids = array() ) { _deprecated_function( __FUNCTION__, '2.6.0', 'wp_category_checklist()' ); global $post_ID; wp_category_checklist( $post_ID ); } /** * Legacy function used to generate a link categories checklist control. * * @since 2.1.0 * @deprecated 2.6.0 Use wp_link_category_checklist() * @see wp_link_category_checklist() * * @global int $link_id * * @param int $default_link_category Unused. */ function dropdown_link_categories( $default_link_category = 0 ) { _deprecated_function( __FUNCTION__, '2.6.0', 'wp_link_category_checklist()' ); global $link_id; wp_link_category_checklist( $link_id ); } /** * Get the real filesystem path to a file to edit within the admin. * * @since 1.5.0 * @deprecated 2.9.0 * @uses WP_CONTENT_DIR Full filesystem path to the wp-content directory. * * @param string $file Filesystem path relative to the wp-content directory. * @return string Full filesystem path to edit. */ function get_real_file_to_edit( $file ) { _deprecated_function( __FUNCTION__, '2.9.0' ); return WP_CONTENT_DIR . $file; } /** * Legacy function used for generating a categories drop-down control. * * @since 1.2.0 * @deprecated 3.0.0 Use wp_dropdown_categories() * @see wp_dropdown_categories() * * @param int $current_cat Optional. ID of the current category. Default 0. * @param int $current_parent Optional. Current parent category ID. Default 0. * @param int $category_parent Optional. Parent ID to retrieve categories for. Default 0. * @param int $level Optional. Number of levels deep to display. Default 0. * @param array $categories Optional. Categories to include in the control. Default 0. * @return void|false Void on success, false if no categories were found. */ function wp_dropdown_cats( $current_cat = 0, $current_parent = 0, $category_parent = 0, $level = 0, $categories = 0 ) { _deprecated_function( __FUNCTION__, '3.0.0', 'wp_dropdown_categories()' ); if (!$categories ) $categories = get_categories( array('hide_empty' => 0) ); if ( $categories ) { foreach ( $categories as $category ) { if ( $current_cat != $category->term_id && $category_parent == $category->parent) { $pad = str_repeat( '– ', $level ); $category->name = esc_html( $category->name ); echo "\n\t<option value='$category->term_id'"; if ( $current_parent == $category->term_id ) echo " selected='selected'"; echo ">$pad$category->name</option>"; wp_dropdown_cats( $current_cat, $current_parent, $category->term_id, $level +1, $categories ); } } } else { return false; } } /** * Register a setting and its sanitization callback * * @since 2.7.0 * @deprecated 3.0.0 Use register_setting() * @see register_setting() * * @param string $option_group A settings group name. Should correspond to an allowed option key name. * Default allowed option key names include 'general', 'discussion', 'media', * 'reading', 'writing', and 'options'. * @param string $option_name The name of an option to sanitize and save. * @param callable $sanitize_callback Optional. A callback function that sanitizes the option's value. */ function add_option_update_handler( $option_group, $option_name, $sanitize_callback = '' ) { _deprecated_function( __FUNCTION__, '3.0.0', 'register_setting()' ); register_setting( $option_group, $option_name, $sanitize_callback ); } /** * Unregister a setting * * @since 2.7.0 * @deprecated 3.0.0 Use unregister_setting() * @see unregister_setting() * * @param string $option_group The settings group name used during registration. * @param string $option_name The name of the option to unregister. * @param callable $sanitize_callback Optional. Deprecated. */ function remove_option_update_handler( $option_group, $option_name, $sanitize_callback = '' ) { _deprecated_function( __FUNCTION__, '3.0.0', 'unregister_setting()' ); unregister_setting( $option_group, $option_name, $sanitize_callback ); } /** * Determines the language to use for CodePress syntax highlighting. * * @since 2.8.0 * @deprecated 3.0.0 * * @param string $filename */ function codepress_get_lang( $filename ) { _deprecated_function( __FUNCTION__, '3.0.0' ); } /** * Adds JavaScript required to make CodePress work on the theme/plugin file editors. * * @since 2.8.0 * @deprecated 3.0.0 */ function codepress_footer_js() { _deprecated_function( __FUNCTION__, '3.0.0' ); } /** * Determine whether to use CodePress. * * @since 2.8.0 * @deprecated 3.0.0 */ function use_codepress() { _deprecated_function( __FUNCTION__, '3.0.0' ); } /** * Get all user IDs. * * @deprecated 3.1.0 Use get_users() * * @global wpdb $wpdb WordPress database abstraction object. * * @return array List of user IDs. */ function get_author_user_ids() { _deprecated_function( __FUNCTION__, '3.1.0', 'get_users()' ); global $wpdb; if ( !is_multisite() ) $level_key = $wpdb->get_blog_prefix() . 'user_level'; else $level_key = $wpdb->get_blog_prefix() . 'capabilities'; // WPMU site admins don't have user_levels. return $wpdb->get_col( $wpdb->prepare("SELECT user_id FROM $wpdb->usermeta WHERE meta_key = %s AND meta_value != '0'", $level_key) ); } /** * Gets author users who can edit posts. * * @deprecated 3.1.0 Use get_users() * * @global wpdb $wpdb WordPress database abstraction object. * * @param int $user_id User ID. * @return array|false List of editable authors. False if no editable users. */ function get_editable_authors( $user_id ) { _deprecated_function( __FUNCTION__, '3.1.0', 'get_users()' ); global $wpdb; $editable = get_editable_user_ids( $user_id ); if ( !$editable ) { return false; } else { $editable = join(',', $editable); $authors = $wpdb->get_results( "SELECT * FROM $wpdb->users WHERE ID IN ($editable) ORDER BY display_name" ); } return apply_filters('get_editable_authors', $authors); } /** * Gets the IDs of any users who can edit posts. * * @deprecated 3.1.0 Use get_users() * * @global wpdb $wpdb WordPress database abstraction object. * * @param int $user_id User ID. * @param bool $exclude_zeros Optional. Whether to exclude zeroes. Default true. * @return array Array of editable user IDs, empty array otherwise. */ function get_editable_user_ids( $user_id, $exclude_zeros = true, $post_type = 'post' ) { _deprecated_function( __FUNCTION__, '3.1.0', 'get_users()' ); global $wpdb; if ( ! $user = get_userdata( $user_id ) ) return array(); $post_type_obj = get_post_type_object($post_type); if ( ! $user->has_cap($post_type_obj->cap->edit_others_posts) ) { if ( $user->has_cap($post_type_obj->cap->edit_posts) || ! $exclude_zeros ) return array($user->ID); else return array(); } if ( !is_multisite() ) $level_key = $wpdb->get_blog_prefix() . 'user_level'; else $level_key = $wpdb->get_blog_prefix() . 'capabilities'; // WPMU site admins don't have user_levels. $query = $wpdb->prepare("SELECT user_id FROM $wpdb->usermeta WHERE meta_key = %s", $level_key); if ( $exclude_zeros ) $query .= " AND meta_value != '0'"; return $wpdb->get_col( $query ); } /** * Gets all users who are not authors. * * @deprecated 3.1.0 Use get_users() * * @global wpdb $wpdb WordPress database abstraction object. */ function get_nonauthor_user_ids() { _deprecated_function( __FUNCTION__, '3.1.0', 'get_users()' ); global $wpdb; if ( !is_multisite() ) $level_key = $wpdb->get_blog_prefix() . 'user_level'; else $level_key = $wpdb->get_blog_prefix() . 'capabilities'; // WPMU site admins don't have user_levels. return $wpdb->get_col( $wpdb->prepare("SELECT user_id FROM $wpdb->usermeta WHERE meta_key = %s AND meta_value = '0'", $level_key) ); } if ( ! class_exists( 'WP_User_Search', false ) ) : /** * WordPress User Search class. * * @since 2.1.0 * @deprecated 3.1.0 Use WP_User_Query */ class WP_User_Search { /** * {@internal Missing Description}} * * @since 2.1.0 * @access private * @var mixed */ var $results; /** * {@internal Missing Description}} * * @since 2.1.0 * @access private * @var string */ var $search_term; /** * Page number. * * @since 2.1.0 * @access private * @var int */ var $page; /** * Role name that users have. * * @since 2.5.0 * @access private * @var string */ var $role; /** * Raw page number. * * @since 2.1.0 * @access private * @var int|bool */ var $raw_page; /** * Amount of users to display per page. * * @since 2.1.0 * @access public * @var int */ var $users_per_page = 50; /** * {@internal Missing Description}} * * @since 2.1.0 * @access private * @var int */ var $first_user; /** * {@internal Missing Description}} * * @since 2.1.0 * @access private * @var int */ var $last_user; /** * {@internal Missing Description}} * * @since 2.1.0 * @access private * @var string */ var $query_limit; /** * {@internal Missing Description}} * * @since 3.0.0 * @access private * @var string */ var $query_orderby; /** * {@internal Missing Description}} * * @since 3.0.0 * @access private * @var string */ var $query_from; /** * {@internal Missing Description}} * * @since 3.0.0 * @access private * @var string */ var $query_where; /** * {@internal Missing Description}} * * @since 2.1.0 * @access private * @var int */ var $total_users_for_query = 0; /** * {@internal Missing Description}} * * @since 2.1.0 * @access private * @var bool */ var $too_many_total_users = false; /** * {@internal Missing Description}} * * @since 2.1.0 * @access private * @var WP_Error */ var $search_errors; /** * {@internal Missing Description}} * * @since 2.7.0 * @access private * @var string */ var $paging_text; /** * PHP5 Constructor - Sets up the object properties. * * @since 2.1.0 * * @param string $search_term Search terms string. * @param int $page Optional. Page ID. * @param string $role Role name. * @return WP_User_Search */ function __construct( $search_term = '', $page = '', $role = '' ) { _deprecated_class( 'WP_User_Search', '3.1.0', 'WP_User_Query' ); $this->search_term = wp_unslash( $search_term ); $this->raw_page = ( '' == $page ) ? false : (int) $page; $this->page = ( '' == $page ) ? 1 : (int) $page; $this->role = $role; $this->prepare_query(); $this->query(); $this->do_paging(); } /** * PHP4 Constructor - Sets up the object properties. * * @since 2.1.0 * * @param string $search_term Search terms string. * @param int $page Optional. Page ID. * @param string $role Role name. * @return WP_User_Search */ public function WP_User_Search( $search_term = '', $page = '', $role = '' ) { _deprecated_constructor( 'WP_User_Search', '3.1.0', get_class( $this ) ); self::__construct( $search_term, $page, $role ); } /** * Prepares the user search query (legacy). * * @since 2.1.0 * @access public * * @global wpdb $wpdb WordPress database abstraction object. */ public function prepare_query() { global $wpdb; $this->first_user = ($this->page - 1) * $this->users_per_page; $this->query_limit = $wpdb->prepare(" LIMIT %d, %d", $this->first_user, $this->users_per_page); $this->query_orderby = ' ORDER BY user_login'; $search_sql = ''; if ( $this->search_term ) { $searches = array(); $search_sql = 'AND ('; foreach ( array('user_login', 'user_nicename', 'user_email', 'user_url', 'display_name') as $col ) $searches[] = $wpdb->prepare( $col . ' LIKE %s', '%' . like_escape($this->search_term) . '%' ); $search_sql .= implode(' OR ', $searches); $search_sql .= ')'; } $this->query_from = " FROM $wpdb->users"; $this->query_where = " WHERE 1=1 $search_sql"; if ( $this->role ) { $this->query_from .= " INNER JOIN $wpdb->usermeta ON $wpdb->users.ID = $wpdb->usermeta.user_id"; $this->query_where .= $wpdb->prepare(" AND $wpdb->usermeta.meta_key = '{$wpdb->prefix}capabilities' AND $wpdb->usermeta.meta_value LIKE %s", '%' . $this->role . '%'); } elseif ( is_multisite() ) { $level_key = $wpdb->prefix . 'capabilities'; // WPMU site admins don't have user_levels. $this->query_from .= ", $wpdb->usermeta"; $this->query_where .= " AND $wpdb->users.ID = $wpdb->usermeta.user_id AND meta_key = '{$level_key}'"; } do_action_ref_array( 'pre_user_search', array( &$this ) ); } /** * Executes the user search query. * * @since 2.1.0 * @access public * * @global wpdb $wpdb WordPress database abstraction object. */ public function query() { global $wpdb; $this->results = $wpdb->get_col("SELECT DISTINCT($wpdb->users.ID)" . $this->query_from . $this->query_where . $this->query_orderby . $this->query_limit); if ( $this->results ) $this->total_users_for_query = $wpdb->get_var("SELECT COUNT(DISTINCT($wpdb->users.ID))" . $this->query_from . $this->query_where); // No limit. else $this->search_errors = new WP_Error('no_matching_users_found', __('No users found.')); } /** * Prepares variables for use in templates. * * @since 2.1.0 * @access public */ function prepare_vars_for_template_usage() {} /** * Handles paging for the user search query. * * @since 2.1.0 * @access public */ public function do_paging() { if ( $this->total_users_for_query > $this->users_per_page ) { // Have to page the results. $args = array(); if ( ! empty($this->search_term) ) $args['usersearch'] = urlencode($this->search_term); if ( ! empty($this->role) ) $args['role'] = urlencode($this->role); $this->paging_text = paginate_links( array( 'total' => ceil($this->total_users_for_query / $this->users_per_page), 'current' => $this->page, 'base' => 'users.php?%_%', 'format' => 'userspage=%#%', 'add_args' => $args ) ); if ( $this->paging_text ) { $this->paging_text = sprintf( /* translators: 1: Starting number of users on the current page, 2: Ending number of users, 3: Total number of users. */ '<span class="displaying-num">' . __( 'Displaying %1$s–%2$s of %3$s' ) . '</span>%s', number_format_i18n( ( $this->page - 1 ) * $this->users_per_page + 1 ), number_format_i18n( min( $this->page * $this->users_per_page, $this->total_users_for_query ) ), number_format_i18n( $this->total_users_for_query ), $this->paging_text ); } } } /** * Retrieves the user search query results. * * @since 2.1.0 * @access public * * @return array */ public function get_results() { return (array) $this->results; } /** * Displaying paging text. * * @see do_paging() Builds paging text. * * @since 2.1.0 * @access public */ function page_links() { echo $this->paging_text; } /** * Whether paging is enabled. * * @see do_paging() Builds paging text. * * @since 2.1.0 * @access public * * @return bool */ function results_are_paged() { if ( $this->paging_text ) return true; return false; } /** * Whether there are search terms. * * @since 2.1.0 * @access public * * @return bool */ function is_search() { if ( $this->search_term ) return true; return false; } } endif; /** * Retrieves editable posts from other users. * * @since 2.3.0 * @deprecated 3.1.0 Use get_posts() * @see get_posts() * * @global wpdb $wpdb WordPress database abstraction object. * * @param int $user_id User ID to not retrieve posts from. * @param string $type Optional. Post type to retrieve. Accepts 'draft', 'pending' or 'any' (all). * Default 'any'. * @return array List of posts from others. */ function get_others_unpublished_posts( $user_id, $type = 'any' ) { _deprecated_function( __FUNCTION__, '3.1.0' ); global $wpdb; $editable = get_editable_user_ids( $user_id ); if ( in_array($type, array('draft', 'pending')) ) $type_sql = " post_status = '$type' "; else $type_sql = " ( post_status = 'draft' OR post_status = 'pending' ) "; $dir = ( 'pending' == $type ) ? 'ASC' : 'DESC'; if ( !$editable ) { $other_unpubs = ''; } else { $editable = join(',', $editable); $other_unpubs = $wpdb->get_results( $wpdb->prepare("SELECT ID, post_title, post_author FROM $wpdb->posts WHERE post_type = 'post' AND $type_sql AND post_author IN ($editable) AND post_author != %d ORDER BY post_modified $dir", $user_id) ); } return apply_filters('get_others_drafts', $other_unpubs); } /** * Retrieve drafts from other users. * * @deprecated 3.1.0 Use get_posts() * @see get_posts() * * @param int $user_id User ID. * @return array List of drafts from other users. */ function get_others_drafts($user_id) { _deprecated_function( __FUNCTION__, '3.1.0' ); return get_others_unpublished_posts($user_id, 'draft'); } /** * Retrieve pending review posts from other users. * * @deprecated 3.1.0 Use get_posts() * @see get_posts() * * @param int $user_id User ID. * @return array List of posts with pending review post type from other users. */ function get_others_pending($user_id) { _deprecated_function( __FUNCTION__, '3.1.0' ); return get_others_unpublished_posts($user_id, 'pending'); } /** * Output the QuickPress dashboard widget. * * @since 3.0.0 * @deprecated 3.2.0 Use wp_dashboard_quick_press() * @see wp_dashboard_quick_press() */ function wp_dashboard_quick_press_output() { _deprecated_function( __FUNCTION__, '3.2.0', 'wp_dashboard_quick_press()' ); wp_dashboard_quick_press(); } /** * Outputs the TinyMCE editor. * * @since 2.7.0 * @deprecated 3.3.0 Use wp_editor() * @see wp_editor() */ function wp_tiny_mce( $teeny = false, $settings = false ) { _deprecated_function( __FUNCTION__, '3.3.0', 'wp_editor()' ); static $num = 1; if ( ! class_exists( '_WP_Editors', false ) ) require_once ABSPATH . WPINC . '/class-wp-editor.php'; $editor_id = 'content' . $num++; $set = array( 'teeny' => $teeny, 'tinymce' => $settings ? $settings : true, 'quicktags' => false ); $set = _WP_Editors::parse_settings($editor_id, $set); _WP_Editors::editor_settings($editor_id, $set); } /** * Preloads TinyMCE dialogs. * * @deprecated 3.3.0 Use wp_editor() * @see wp_editor() */ function wp_preload_dialogs() { _deprecated_function( __FUNCTION__, '3.3.0', 'wp_editor()' ); } /** * Prints TinyMCE editor JS. * * @deprecated 3.3.0 Use wp_editor() * @see wp_editor() */ function wp_print_editor_js() { _deprecated_function( __FUNCTION__, '3.3.0', 'wp_editor()' ); } /** * Handles quicktags. * * @deprecated 3.3.0 Use wp_editor() * @see wp_editor() */ function wp_quicktags() { _deprecated_function( __FUNCTION__, '3.3.0', 'wp_editor()' ); } /** * Returns the screen layout options. * * @since 2.8.0 * @deprecated 3.3.0 WP_Screen::render_screen_layout() * @see WP_Screen::render_screen_layout() */ function screen_layout( $screen ) { _deprecated_function( __FUNCTION__, '3.3.0', '$current_screen->render_screen_layout()' ); $current_screen = get_current_screen(); if ( ! $current_screen ) return ''; ob_start(); $current_screen->render_screen_layout(); return ob_get_clean(); } /** * Returns the screen's per-page options. * * @since 2.8.0 * @deprecated 3.3.0 Use WP_Screen::render_per_page_options() * @see WP_Screen::render_per_page_options() */ function screen_options( $screen ) { _deprecated_function( __FUNCTION__, '3.3.0', '$current_screen->render_per_page_options()' ); $current_screen = get_current_screen(); if ( ! $current_screen ) return ''; ob_start(); $current_screen->render_per_page_options(); return ob_get_clean(); } /** * Renders the screen's help. * * @since 2.7.0 * @deprecated 3.3.0 Use WP_Screen::render_screen_meta() * @see WP_Screen::render_screen_meta() */ function screen_meta( $screen ) { $current_screen = get_current_screen(); $current_screen->render_screen_meta(); } /** * Favorite actions were deprecated in version 3.2. Use the admin bar instead. * * @since 2.7.0 * @deprecated 3.2.0 Use WP_Admin_Bar * @see WP_Admin_Bar */ function favorite_actions() { _deprecated_function( __FUNCTION__, '3.2.0', 'WP_Admin_Bar' ); } /** * Handles uploading an image. * * @deprecated 3.3.0 Use wp_media_upload_handler() * @see wp_media_upload_handler() * * @return null|string */ function media_upload_image() { _deprecated_function( __FUNCTION__, '3.3.0', 'wp_media_upload_handler()' ); return wp_media_upload_handler(); } /** * Handles uploading an audio file. * * @deprecated 3.3.0 Use wp_media_upload_handler() * @see wp_media_upload_handler() * * @return null|string */ function media_upload_audio() { _deprecated_function( __FUNCTION__, '3.3.0', 'wp_media_upload_handler()' ); return wp_media_upload_handler(); } /** * Handles uploading a video file. * * @deprecated 3.3.0 Use wp_media_upload_handler() * @see wp_media_upload_handler() * * @return null|string */ function media_upload_video() { _deprecated_function( __FUNCTION__, '3.3.0', 'wp_media_upload_handler()' ); return wp_media_upload_handler(); } /** * Handles uploading a generic file. * * @deprecated 3.3.0 Use wp_media_upload_handler() * @see wp_media_upload_handler() * * @return null|string */ function media_upload_file() { _deprecated_function( __FUNCTION__, '3.3.0', 'wp_media_upload_handler()' ); return wp_media_upload_handler(); } /** * Handles retrieving the insert-from-URL form for an image. * * @deprecated 3.3.0 Use wp_media_insert_url_form() * @see wp_media_insert_url_form() * * @return string */ function type_url_form_image() { _deprecated_function( __FUNCTION__, '3.3.0', "wp_media_insert_url_form('image')" ); return wp_media_insert_url_form( 'image' ); } /** * Handles retrieving the insert-from-URL form for an audio file. * * @deprecated 3.3.0 Use wp_media_insert_url_form() * @see wp_media_insert_url_form() * * @return string */ function type_url_form_audio() { _deprecated_function( __FUNCTION__, '3.3.0', "wp_media_insert_url_form('audio')" ); return wp_media_insert_url_form( 'audio' ); } /** * Handles retrieving the insert-from-URL form for a video file. * * @deprecated 3.3.0 Use wp_media_insert_url_form() * @see wp_media_insert_url_form() * * @return string */ function type_url_form_video() { _deprecated_function( __FUNCTION__, '3.3.0', "wp_media_insert_url_form('video')" ); return wp_media_insert_url_form( 'video' ); } /** * Handles retrieving the insert-from-URL form for a generic file. * * @deprecated 3.3.0 Use wp_media_insert_url_form() * @see wp_media_insert_url_form() * * @return string */ function type_url_form_file() { _deprecated_function( __FUNCTION__, '3.3.0', "wp_media_insert_url_form('file')" ); return wp_media_insert_url_form( 'file' ); } /** * Add contextual help text for a page. * * Creates an 'Overview' help tab. * * @since 2.7.0 * @deprecated 3.3.0 Use WP_Screen::add_help_tab() * @see WP_Screen::add_help_tab() * * @param string $screen The handle for the screen to add help to. This is usually * the hook name returned by the `add_*_page()` functions. * @param string $help The content of an 'Overview' help tab. */ function add_contextual_help( $screen, $help ) { _deprecated_function( __FUNCTION__, '3.3.0', 'get_current_screen()->add_help_tab()' ); if ( is_string( $screen ) ) $screen = convert_to_screen( $screen ); WP_Screen::add_old_compat_help( $screen, $help ); } /** * Get the allowed themes for the current site. * * @since 3.0.0 * @deprecated 3.4.0 Use wp_get_themes() * @see wp_get_themes() * * @return WP_Theme[] Array of WP_Theme objects keyed by their name. */ function get_allowed_themes() { _deprecated_function( __FUNCTION__, '3.4.0', "wp_get_themes( array( 'allowed' => true ) )" ); $themes = wp_get_themes( array( 'allowed' => true ) ); $wp_themes = array(); foreach ( $themes as $theme ) { $wp_themes[ $theme->get('Name') ] = $theme; } return $wp_themes; } /** * Retrieves a list of broken themes. * * @since 1.5.0 * @deprecated 3.4.0 Use wp_get_themes() * @see wp_get_themes() * * @return array */ function get_broken_themes() { _deprecated_function( __FUNCTION__, '3.4.0', "wp_get_themes( array( 'errors' => true )" ); $themes = wp_get_themes( array( 'errors' => true ) ); $broken = array(); foreach ( $themes as $theme ) { $name = $theme->get('Name'); $broken[ $name ] = array( 'Name' => $name, 'Title' => $name, 'Description' => $theme->errors()->get_error_message(), ); } return $broken; } /** * Retrieves information on the current active theme. * * @since 2.0.0 * @deprecated 3.4.0 Use wp_get_theme() * @see wp_get_theme() * * @return WP_Theme */ function current_theme_info() { _deprecated_function( __FUNCTION__, '3.4.0', 'wp_get_theme()' ); return wp_get_theme(); } /** * This was once used to display an 'Insert into Post' button. * * Now it is deprecated and stubbed. * * @deprecated 3.5.0 */ function _insert_into_post_button( $type ) { _deprecated_function( __FUNCTION__, '3.5.0' ); } /** * This was once used to display a media button. * * Now it is deprecated and stubbed. * * @deprecated 3.5.0 */ function _media_button($title, $icon, $type, $id) { _deprecated_function( __FUNCTION__, '3.5.0' ); } /** * Gets an existing post and format it for editing. * * @since 2.0.0 * @deprecated 3.5.0 Use get_post() * @see get_post() * * @param int $id * @return WP_Post */ function get_post_to_edit( $id ) { _deprecated_function( __FUNCTION__, '3.5.0', 'get_post()' ); return get_post( $id, OBJECT, 'edit' ); } /** * Gets the default page information to use. * * @since 2.5.0 * @deprecated 3.5.0 Use get_default_post_to_edit() * @see get_default_post_to_edit() * * @return WP_Post Post object containing all the default post data as attributes */ function get_default_page_to_edit() { _deprecated_function( __FUNCTION__, '3.5.0', "get_default_post_to_edit( 'page' )" ); $page = get_default_post_to_edit(); $page->post_type = 'page'; return $page; } /** * This was once used to create a thumbnail from an Image given a maximum side size. * * @since 1.2.0 * @deprecated 3.5.0 Use image_resize() * @see image_resize() * * @param mixed $file Filename of the original image, Or attachment ID. * @param int $max_side Maximum length of a single side for the thumbnail. * @param mixed $deprecated Never used. * @return string Thumbnail path on success, Error string on failure. */ function wp_create_thumbnail( $file, $max_side, $deprecated = '' ) { _deprecated_function( __FUNCTION__, '3.5.0', 'image_resize()' ); return apply_filters( 'wp_create_thumbnail', image_resize( $file, $max_side, $max_side ) ); } /** * This was once used to display a meta box for the nav menu theme locations. * * Deprecated in favor of a 'Manage Locations' tab added to nav menus management screen. * * @since 3.0.0 * @deprecated 3.6.0 */ function wp_nav_menu_locations_meta_box() { _deprecated_function( __FUNCTION__, '3.6.0' ); } /** * This was once used to kick-off the Core Updater. * * Deprecated in favor of instantiating a Core_Upgrader instance directly, * and calling the 'upgrade' method. * * @since 2.7.0 * @deprecated 3.7.0 Use Core_Upgrader * @see Core_Upgrader */ function wp_update_core($current, $feedback = '') { _deprecated_function( __FUNCTION__, '3.7.0', 'new Core_Upgrader();' ); if ( !empty($feedback) ) add_filter('update_feedback', $feedback); require ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'; $upgrader = new Core_Upgrader(); return $upgrader->upgrade($current); } /** * This was once used to kick-off the Plugin Updater. * * Deprecated in favor of instantiating a Plugin_Upgrader instance directly, * and calling the 'upgrade' method. * Unused since 2.8.0. * * @since 2.5.0 * @deprecated 3.7.0 Use Plugin_Upgrader * @see Plugin_Upgrader */ function wp_update_plugin($plugin, $feedback = '') { _deprecated_function( __FUNCTION__, '3.7.0', 'new Plugin_Upgrader();' ); if ( !empty($feedback) ) add_filter('update_feedback', $feedback); require ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'; $upgrader = new Plugin_Upgrader(); return $upgrader->upgrade($plugin); } /** * This was once used to kick-off the Theme Updater. * * Deprecated in favor of instantiating a Theme_Upgrader instance directly, * and calling the 'upgrade' method. * Unused since 2.8.0. * * @since 2.7.0 * @deprecated 3.7.0 Use Theme_Upgrader * @see Theme_Upgrader */ function wp_update_theme($theme, $feedback = '') { _deprecated_function( __FUNCTION__, '3.7.0', 'new Theme_Upgrader();' ); if ( !empty($feedback) ) add_filter('update_feedback', $feedback); require ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'; $upgrader = new Theme_Upgrader(); return $upgrader->upgrade($theme); } /** * This was once used to display attachment links. Now it is deprecated and stubbed. * * @since 2.0.0 * @deprecated 3.7.0 * * @param int|bool $id */ function the_attachment_links( $id = false ) { _deprecated_function( __FUNCTION__, '3.7.0' ); } /** * Displays a screen icon. * * @since 2.7.0 * @deprecated 3.8.0 */ function screen_icon() { _deprecated_function( __FUNCTION__, '3.8.0' ); echo get_screen_icon(); } /** * Retrieves the screen icon (no longer used in 3.8+). * * @since 3.2.0 * @deprecated 3.8.0 * * @return string An HTML comment explaining that icons are no longer used. */ function get_screen_icon() { _deprecated_function( __FUNCTION__, '3.8.0' ); return '<!-- Screen icons are no longer used as of WordPress 3.8. -->'; } /** * Deprecated dashboard widget controls. * * @since 2.5.0 * @deprecated 3.8.0 */ function wp_dashboard_incoming_links_output() {} /** * Deprecated dashboard secondary output. * * @deprecated 3.8.0 */ function wp_dashboard_secondary_output() {} /** * Deprecated dashboard widget controls. * * @since 2.7.0 * @deprecated 3.8.0 */ function wp_dashboard_incoming_links() {} /** * Deprecated dashboard incoming links control. * * @deprecated 3.8.0 */ function wp_dashboard_incoming_links_control() {} /** * Deprecated dashboard plugins control. * * @deprecated 3.8.0 */ function wp_dashboard_plugins() {} /** * Deprecated dashboard primary control. * * @deprecated 3.8.0 */ function wp_dashboard_primary_control() {} /** * Deprecated dashboard recent comments control. * * @deprecated 3.8.0 */ function wp_dashboard_recent_comments_control() {} /** * Deprecated dashboard secondary section. * * @deprecated 3.8.0 */ function wp_dashboard_secondary() {} /** * Deprecated dashboard secondary control. * * @deprecated 3.8.0 */ function wp_dashboard_secondary_control() {} /** * Display plugins text for the WordPress news widget. * * @since 2.5.0 * @deprecated 4.8.0 * * @param string $rss The RSS feed URL. * @param array $args Array of arguments for this RSS feed. */ function wp_dashboard_plugins_output( $rss, $args = array() ) { _deprecated_function( __FUNCTION__, '4.8.0' ); // Plugin feeds plus link to install them. $popular = fetch_feed( $args['url']['popular'] ); if ( false === $plugin_slugs = get_transient( 'plugin_slugs' ) ) { $plugin_slugs = array_keys( get_plugins() ); set_transient( 'plugin_slugs', $plugin_slugs, DAY_IN_SECONDS ); } echo '<ul>'; foreach ( array( $popular ) as $feed ) { if ( is_wp_error( $feed ) || ! $feed->get_item_quantity() ) continue; $items = $feed->get_items(0, 5); // Pick a random, non-installed plugin. while ( true ) { // Abort this foreach loop iteration if there's no plugins left of this type. if ( 0 === count($items) ) continue 2; $item_key = array_rand($items); $item = $items[$item_key]; list($link, $frag) = explode( '#', $item->get_link() ); $link = esc_url($link); if ( preg_match( '|/([^/]+?)/?$|', $link, $matches ) ) $slug = $matches[1]; else { unset( $items[$item_key] ); continue; } // Is this random plugin's slug already installed? If so, try again. reset( $plugin_slugs ); foreach ( $plugin_slugs as $plugin_slug ) { if ( str_starts_with( $plugin_slug, $slug ) ) { unset( $items[$item_key] ); continue 2; } } // If we get to this point, then the random plugin isn't installed and we can stop the while(). break; } // Eliminate some common badly formed plugin descriptions. while ( ( null !== $item_key = array_rand($items) ) && str_contains( $items[$item_key]->get_description(), 'Plugin Name:' ) ) unset($items[$item_key]); if ( !isset($items[$item_key]) ) continue; $raw_title = $item->get_title(); $ilink = wp_nonce_url('plugin-install.php?tab=plugin-information&plugin=' . $slug, 'install-plugin_' . $slug) . '&TB_iframe=true&width=600&height=800'; echo '<li class="dashboard-news-plugin"><span>' . __( 'Popular Plugin' ) . ':</span> ' . esc_html( $raw_title ) . ' <a href="' . $ilink . '" class="thickbox open-plugin-details-modal" aria-label="' . /* translators: %s: Plugin name. */ esc_attr( sprintf( _x( 'Install %s', 'plugin' ), $raw_title ) ) . '">(' . __( 'Install' ) . ')</a></li>'; $feed->__destruct(); unset( $feed ); } echo '</ul>'; } /** * This was once used to move child posts to a new parent. * * @since 2.3.0 * @deprecated 3.9.0 * @access private * * @param int $old_ID * @param int $new_ID */ function _relocate_children( $old_ID, $new_ID ) { _deprecated_function( __FUNCTION__, '3.9.0' ); } /** * Add a top-level menu page in the 'objects' section. * * This function takes a capability which will be used to determine whether * or not a page is included in the menu. * * The function which is hooked in to handle the output of the page must check * that the user has the required capability as well. * * @since 2.7.0 * * @deprecated 4.5.0 Use add_menu_page() * @see add_menu_page() * @global int $_wp_last_object_menu * * @param string $page_title The text to be displayed in the title tags of the page when the menu is selected. * @param string $menu_title The text to be used for the menu. * @param string $capability The capability required for this menu to be displayed to the user. * @param string $menu_slug The slug name to refer to this menu by (should be unique for this menu). * @param callable $callback Optional. The function to be called to output the content for this page. * @param string $icon_url Optional. The URL to the icon to be used for this menu. * @return string The resulting page's hook_suffix. */ function add_object_page( $page_title, $menu_title, $capability, $menu_slug, $callback = '', $icon_url = '') { _deprecated_function( __FUNCTION__, '4.5.0', 'add_menu_page()' ); global $_wp_last_object_menu; $_wp_last_object_menu++; return add_menu_page($page_title, $menu_title, $capability, $menu_slug, $callback, $icon_url, $_wp_last_object_menu); } /** * Add a top-level menu page in the 'utility' section. * * This function takes a capability which will be used to determine whether * or not a page is included in the menu. * * The function which is hooked in to handle the output of the page must check * that the user has the required capability as well. * * @since 2.7.0 * * @deprecated 4.5.0 Use add_menu_page() * @see add_menu_page() * @global int $_wp_last_utility_menu * * @param string $page_title The text to be displayed in the title tags of the page when the menu is selected. * @param string $menu_title The text to be used for the menu. * @param string $capability The capability required for this menu to be displayed to the user. * @param string $menu_slug The slug name to refer to this menu by (should be unique for this menu). * @param callable $callback Optional. The function to be called to output the content for this page. * @param string $icon_url Optional. The URL to the icon to be used for this menu. * @return string The resulting page's hook_suffix. */ function add_utility_page( $page_title, $menu_title, $capability, $menu_slug, $callback = '', $icon_url = '') { _deprecated_function( __FUNCTION__, '4.5.0', 'add_menu_page()' ); global $_wp_last_utility_menu; $_wp_last_utility_menu++; return add_menu_page($page_title, $menu_title, $capability, $menu_slug, $callback, $icon_url, $_wp_last_utility_menu); } /** * Disables autocomplete on the 'post' form (Add/Edit Post screens) for WebKit browsers, * as they disregard the autocomplete setting on the editor textarea. That can break the editor * when the user navigates to it with the browser's Back button. See #28037 * * Replaced with wp_page_reload_on_back_button_js() that also fixes this problem. * * @since 4.0.0 * @deprecated 4.6.0 * * @link https://core.trac.wordpress.org/ticket/35852 * * @global bool $is_safari * @global bool $is_chrome */ function post_form_autocomplete_off() { global $is_safari, $is_chrome; _deprecated_function( __FUNCTION__, '4.6.0' ); if ( $is_safari || $is_chrome ) { echo ' autocomplete="off"'; } } /** * Display JavaScript on the page. * * @since 3.5.0 * @deprecated 4.9.0 */ function options_permalink_add_js() { ?> <script type="text/javascript"> jQuery( function() { jQuery('.permalink-structure input:radio').change(function() { if ( 'custom' == this.value ) return; jQuery('#permalink_structure').val( this.value ); }); jQuery( '#permalink_structure' ).on( 'click input', function() { jQuery( '#custom_selection' ).prop( 'checked', true ); }); } ); </script> <?php } /** * Previous class for list table for privacy data export requests. * * @since 4.9.6 * @deprecated 5.3.0 */ class WP_Privacy_Data_Export_Requests_Table extends WP_Privacy_Data_Export_Requests_List_Table { function __construct( $args ) { _deprecated_function( __CLASS__, '5.3.0', 'WP_Privacy_Data_Export_Requests_List_Table' ); if ( ! isset( $args['screen'] ) || $args['screen'] === 'export_personal_data' ) { $args['screen'] = 'export-personal-data'; } parent::__construct( $args ); } } /** * Previous class for list table for privacy data erasure requests. * * @since 4.9.6 * @deprecated 5.3.0 */ class WP_Privacy_Data_Removal_Requests_Table extends WP_Privacy_Data_Removal_Requests_List_Table { function __construct( $args ) { _deprecated_function( __CLASS__, '5.3.0', 'WP_Privacy_Data_Removal_Requests_List_Table' ); if ( ! isset( $args['screen'] ) || $args['screen'] === 'remove_personal_data' ) { $args['screen'] = 'erase-personal-data'; } parent::__construct( $args ); } } /** * Was used to add options for the privacy requests screens before they were separate files. * * @since 4.9.8 * @access private * @deprecated 5.3.0 */ function _wp_privacy_requests_screen_options() { _deprecated_function( __FUNCTION__, '5.3.0' ); } /** * Was used to filter input from media_upload_form_handler() and to assign a default * post_title from the file name if none supplied. * * @since 2.5.0 * @deprecated 6.0.0 * * @param array $post The WP_Post attachment object converted to an array. * @param array $attachment An array of attachment metadata. * @return array Attachment post object converted to an array. */ function image_attachment_fields_to_save( $post, $attachment ) { _deprecated_function( __FUNCTION__, '6.0.0' ); return $post; } includes/post.php 0000644 00000237202 15135536347 0010072 0 ustar 00 <?php /** * WordPress Post Administration API. * * @package WordPress * @subpackage Administration */ /** * Renames `$_POST` data from form names to DB post columns. * * Manipulates `$_POST` directly. * * @since 2.6.0 * * @param bool $update Whether the post already exists. * @param array|null $post_data Optional. The array of post data to process. * Defaults to the `$_POST` superglobal. * @return array|WP_Error Array of post data on success, WP_Error on failure. */ function _wp_translate_postdata( $update = false, $post_data = null ) { if ( empty( $post_data ) ) { $post_data = &$_POST; } if ( $update ) { $post_data['ID'] = (int) $post_data['post_ID']; } $ptype = get_post_type_object( $post_data['post_type'] ); if ( $update && ! current_user_can( 'edit_post', $post_data['ID'] ) ) { if ( 'page' === $post_data['post_type'] ) { return new WP_Error( 'edit_others_pages', __( 'Sorry, you are not allowed to edit pages as this user.' ) ); } else { return new WP_Error( 'edit_others_posts', __( 'Sorry, you are not allowed to edit posts as this user.' ) ); } } elseif ( ! $update && ! current_user_can( $ptype->cap->create_posts ) ) { if ( 'page' === $post_data['post_type'] ) { return new WP_Error( 'edit_others_pages', __( 'Sorry, you are not allowed to create pages as this user.' ) ); } else { return new WP_Error( 'edit_others_posts', __( 'Sorry, you are not allowed to create posts as this user.' ) ); } } if ( isset( $post_data['content'] ) ) { $post_data['post_content'] = $post_data['content']; } if ( isset( $post_data['excerpt'] ) ) { $post_data['post_excerpt'] = $post_data['excerpt']; } if ( isset( $post_data['parent_id'] ) ) { $post_data['post_parent'] = (int) $post_data['parent_id']; } if ( isset( $post_data['trackback_url'] ) ) { $post_data['to_ping'] = $post_data['trackback_url']; } $post_data['user_ID'] = get_current_user_id(); if ( ! empty( $post_data['post_author_override'] ) ) { $post_data['post_author'] = (int) $post_data['post_author_override']; } else { if ( ! empty( $post_data['post_author'] ) ) { $post_data['post_author'] = (int) $post_data['post_author']; } else { $post_data['post_author'] = (int) $post_data['user_ID']; } } if ( isset( $post_data['user_ID'] ) && ( $post_data['post_author'] != $post_data['user_ID'] ) && ! current_user_can( $ptype->cap->edit_others_posts ) ) { if ( $update ) { if ( 'page' === $post_data['post_type'] ) { return new WP_Error( 'edit_others_pages', __( 'Sorry, you are not allowed to edit pages as this user.' ) ); } else { return new WP_Error( 'edit_others_posts', __( 'Sorry, you are not allowed to edit posts as this user.' ) ); } } else { if ( 'page' === $post_data['post_type'] ) { return new WP_Error( 'edit_others_pages', __( 'Sorry, you are not allowed to create pages as this user.' ) ); } else { return new WP_Error( 'edit_others_posts', __( 'Sorry, you are not allowed to create posts as this user.' ) ); } } } if ( ! empty( $post_data['post_status'] ) ) { $post_data['post_status'] = sanitize_key( $post_data['post_status'] ); // No longer an auto-draft. if ( 'auto-draft' === $post_data['post_status'] ) { $post_data['post_status'] = 'draft'; } if ( ! get_post_status_object( $post_data['post_status'] ) ) { unset( $post_data['post_status'] ); } } // What to do based on which button they pressed. if ( isset( $post_data['saveasdraft'] ) && '' !== $post_data['saveasdraft'] ) { $post_data['post_status'] = 'draft'; } if ( isset( $post_data['saveasprivate'] ) && '' !== $post_data['saveasprivate'] ) { $post_data['post_status'] = 'private'; } if ( isset( $post_data['publish'] ) && ( '' !== $post_data['publish'] ) && ( ! isset( $post_data['post_status'] ) || 'private' !== $post_data['post_status'] ) ) { $post_data['post_status'] = 'publish'; } if ( isset( $post_data['advanced'] ) && '' !== $post_data['advanced'] ) { $post_data['post_status'] = 'draft'; } if ( isset( $post_data['pending'] ) && '' !== $post_data['pending'] ) { $post_data['post_status'] = 'pending'; } if ( isset( $post_data['ID'] ) ) { $post_id = $post_data['ID']; } else { $post_id = false; } $previous_status = $post_id ? get_post_field( 'post_status', $post_id ) : false; if ( isset( $post_data['post_status'] ) && 'private' === $post_data['post_status'] && ! current_user_can( $ptype->cap->publish_posts ) ) { $post_data['post_status'] = $previous_status ? $previous_status : 'pending'; } $published_statuses = array( 'publish', 'future' ); /* * Posts 'submitted for approval' are submitted to $_POST the same as if they were being published. * Change status from 'publish' to 'pending' if user lacks permissions to publish or to resave published posts. */ if ( isset( $post_data['post_status'] ) && ( in_array( $post_data['post_status'], $published_statuses, true ) && ! current_user_can( $ptype->cap->publish_posts ) ) ) { if ( ! in_array( $previous_status, $published_statuses, true ) || ! current_user_can( 'edit_post', $post_id ) ) { $post_data['post_status'] = 'pending'; } } if ( ! isset( $post_data['post_status'] ) ) { $post_data['post_status'] = 'auto-draft' === $previous_status ? 'draft' : $previous_status; } if ( isset( $post_data['post_password'] ) && ! current_user_can( $ptype->cap->publish_posts ) ) { unset( $post_data['post_password'] ); } if ( ! isset( $post_data['comment_status'] ) ) { $post_data['comment_status'] = 'closed'; } if ( ! isset( $post_data['ping_status'] ) ) { $post_data['ping_status'] = 'closed'; } foreach ( array( 'aa', 'mm', 'jj', 'hh', 'mn' ) as $timeunit ) { if ( ! empty( $post_data[ 'hidden_' . $timeunit ] ) && $post_data[ 'hidden_' . $timeunit ] != $post_data[ $timeunit ] ) { $post_data['edit_date'] = '1'; break; } } if ( ! empty( $post_data['edit_date'] ) ) { $aa = $post_data['aa']; $mm = $post_data['mm']; $jj = $post_data['jj']; $hh = $post_data['hh']; $mn = $post_data['mn']; $ss = $post_data['ss']; $aa = ( $aa <= 0 ) ? gmdate( 'Y' ) : $aa; $mm = ( $mm <= 0 ) ? gmdate( 'n' ) : $mm; $jj = ( $jj > 31 ) ? 31 : $jj; $jj = ( $jj <= 0 ) ? gmdate( 'j' ) : $jj; $hh = ( $hh > 23 ) ? $hh - 24 : $hh; $mn = ( $mn > 59 ) ? $mn - 60 : $mn; $ss = ( $ss > 59 ) ? $ss - 60 : $ss; $post_data['post_date'] = sprintf( '%04d-%02d-%02d %02d:%02d:%02d', $aa, $mm, $jj, $hh, $mn, $ss ); $valid_date = wp_checkdate( $mm, $jj, $aa, $post_data['post_date'] ); if ( ! $valid_date ) { return new WP_Error( 'invalid_date', __( 'Invalid date.' ) ); } /* * Only assign a post date if the user has explicitly set a new value. * See #59125 and #19907. */ $previous_date = $post_id ? get_post_field( 'post_date', $post_id ) : false; if ( $previous_date && $previous_date !== $post_data['post_date'] ) { $post_data['edit_date'] = true; $post_data['post_date_gmt'] = get_gmt_from_date( $post_data['post_date'] ); } else { $post_data['edit_date'] = false; unset( $post_data['post_date'] ); unset( $post_data['post_date_gmt'] ); } } if ( isset( $post_data['post_category'] ) ) { $category_object = get_taxonomy( 'category' ); if ( ! current_user_can( $category_object->cap->assign_terms ) ) { unset( $post_data['post_category'] ); } } return $post_data; } /** * Returns only allowed post data fields. * * @since 5.0.1 * * @param array|WP_Error|null $post_data The array of post data to process, or an error object. * Defaults to the `$_POST` superglobal. * @return array|WP_Error Array of post data on success, WP_Error on failure. */ function _wp_get_allowed_postdata( $post_data = null ) { if ( empty( $post_data ) ) { $post_data = $_POST; } // Pass through errors. if ( is_wp_error( $post_data ) ) { return $post_data; } return array_diff_key( $post_data, array_flip( array( 'meta_input', 'file', 'guid' ) ) ); } /** * Updates an existing post with values provided in `$_POST`. * * If post data is passed as an argument, it is treated as an array of data * keyed appropriately for turning into a post object. * * If post data is not passed, the `$_POST` global variable is used instead. * * @since 1.5.0 * * @global wpdb $wpdb WordPress database abstraction object. * * @param array|null $post_data Optional. The array of post data to process. * Defaults to the `$_POST` superglobal. * @return int Post ID. */ function edit_post( $post_data = null ) { global $wpdb; if ( empty( $post_data ) ) { $post_data = &$_POST; } // Clear out any data in internal vars. unset( $post_data['filter'] ); $post_id = (int) $post_data['post_ID']; $post = get_post( $post_id ); $post_data['post_type'] = $post->post_type; $post_data['post_mime_type'] = $post->post_mime_type; if ( ! empty( $post_data['post_status'] ) ) { $post_data['post_status'] = sanitize_key( $post_data['post_status'] ); if ( 'inherit' === $post_data['post_status'] ) { unset( $post_data['post_status'] ); } } $ptype = get_post_type_object( $post_data['post_type'] ); if ( ! current_user_can( 'edit_post', $post_id ) ) { if ( 'page' === $post_data['post_type'] ) { wp_die( __( 'Sorry, you are not allowed to edit this page.' ) ); } else { wp_die( __( 'Sorry, you are not allowed to edit this post.' ) ); } } if ( post_type_supports( $ptype->name, 'revisions' ) ) { $revisions = wp_get_post_revisions( $post_id, array( 'order' => 'ASC', 'posts_per_page' => 1, ) ); $revision = current( $revisions ); // Check if the revisions have been upgraded. if ( $revisions && _wp_get_post_revision_version( $revision ) < 1 ) { _wp_upgrade_revisions_of_post( $post, wp_get_post_revisions( $post_id ) ); } } if ( isset( $post_data['visibility'] ) ) { switch ( $post_data['visibility'] ) { case 'public': $post_data['post_password'] = ''; break; case 'password': unset( $post_data['sticky'] ); break; case 'private': $post_data['post_status'] = 'private'; $post_data['post_password'] = ''; unset( $post_data['sticky'] ); break; } } $post_data = _wp_translate_postdata( true, $post_data ); if ( is_wp_error( $post_data ) ) { wp_die( $post_data->get_error_message() ); } $translated = _wp_get_allowed_postdata( $post_data ); // Post formats. if ( isset( $post_data['post_format'] ) ) { set_post_format( $post_id, $post_data['post_format'] ); } $format_meta_urls = array( 'url', 'link_url', 'quote_source_url' ); foreach ( $format_meta_urls as $format_meta_url ) { $keyed = '_format_' . $format_meta_url; if ( isset( $post_data[ $keyed ] ) ) { update_post_meta( $post_id, $keyed, wp_slash( sanitize_url( wp_unslash( $post_data[ $keyed ] ) ) ) ); } } $format_keys = array( 'quote', 'quote_source_name', 'image', 'gallery', 'audio_embed', 'video_embed' ); foreach ( $format_keys as $key ) { $keyed = '_format_' . $key; if ( isset( $post_data[ $keyed ] ) ) { if ( current_user_can( 'unfiltered_html' ) ) { update_post_meta( $post_id, $keyed, $post_data[ $keyed ] ); } else { update_post_meta( $post_id, $keyed, wp_filter_post_kses( $post_data[ $keyed ] ) ); } } } if ( 'attachment' === $post_data['post_type'] && preg_match( '#^(audio|video)/#', $post_data['post_mime_type'] ) ) { $id3data = wp_get_attachment_metadata( $post_id ); if ( ! is_array( $id3data ) ) { $id3data = array(); } foreach ( wp_get_attachment_id3_keys( $post, 'edit' ) as $key => $label ) { if ( isset( $post_data[ 'id3_' . $key ] ) ) { $id3data[ $key ] = sanitize_text_field( wp_unslash( $post_data[ 'id3_' . $key ] ) ); } } wp_update_attachment_metadata( $post_id, $id3data ); } // Meta stuff. if ( isset( $post_data['meta'] ) && $post_data['meta'] ) { foreach ( $post_data['meta'] as $key => $value ) { $meta = get_post_meta_by_id( $key ); if ( ! $meta ) { continue; } if ( $meta->post_id != $post_id ) { continue; } if ( is_protected_meta( $meta->meta_key, 'post' ) || ! current_user_can( 'edit_post_meta', $post_id, $meta->meta_key ) ) { continue; } if ( is_protected_meta( $value['key'], 'post' ) || ! current_user_can( 'edit_post_meta', $post_id, $value['key'] ) ) { continue; } update_meta( $key, $value['key'], $value['value'] ); } } if ( isset( $post_data['deletemeta'] ) && $post_data['deletemeta'] ) { foreach ( $post_data['deletemeta'] as $key => $value ) { $meta = get_post_meta_by_id( $key ); if ( ! $meta ) { continue; } if ( $meta->post_id != $post_id ) { continue; } if ( is_protected_meta( $meta->meta_key, 'post' ) || ! current_user_can( 'delete_post_meta', $post_id, $meta->meta_key ) ) { continue; } delete_meta( $key ); } } // Attachment stuff. if ( 'attachment' === $post_data['post_type'] ) { if ( isset( $post_data['_wp_attachment_image_alt'] ) ) { $image_alt = wp_unslash( $post_data['_wp_attachment_image_alt'] ); if ( get_post_meta( $post_id, '_wp_attachment_image_alt', true ) !== $image_alt ) { $image_alt = wp_strip_all_tags( $image_alt, true ); // update_post_meta() expects slashed. update_post_meta( $post_id, '_wp_attachment_image_alt', wp_slash( $image_alt ) ); } } $attachment_data = isset( $post_data['attachments'][ $post_id ] ) ? $post_data['attachments'][ $post_id ] : array(); /** This filter is documented in wp-admin/includes/media.php */ $translated = apply_filters( 'attachment_fields_to_save', $translated, $attachment_data ); } // Convert taxonomy input to term IDs, to avoid ambiguity. if ( isset( $post_data['tax_input'] ) ) { foreach ( (array) $post_data['tax_input'] as $taxonomy => $terms ) { $tax_object = get_taxonomy( $taxonomy ); if ( $tax_object && isset( $tax_object->meta_box_sanitize_cb ) ) { $translated['tax_input'][ $taxonomy ] = call_user_func_array( $tax_object->meta_box_sanitize_cb, array( $taxonomy, $terms ) ); } } } add_meta( $post_id ); update_post_meta( $post_id, '_edit_last', get_current_user_id() ); $success = wp_update_post( $translated ); // If the save failed, see if we can confidence check the main fields and try again. if ( ! $success && is_callable( array( $wpdb, 'strip_invalid_text_for_column' ) ) ) { $fields = array( 'post_title', 'post_content', 'post_excerpt' ); foreach ( $fields as $field ) { if ( isset( $translated[ $field ] ) ) { $translated[ $field ] = $wpdb->strip_invalid_text_for_column( $wpdb->posts, $field, $translated[ $field ] ); } } wp_update_post( $translated ); } // Now that we have an ID we can fix any attachment anchor hrefs. _fix_attachment_links( $post_id ); wp_set_post_lock( $post_id ); if ( current_user_can( $ptype->cap->edit_others_posts ) && current_user_can( $ptype->cap->publish_posts ) ) { if ( ! empty( $post_data['sticky'] ) ) { stick_post( $post_id ); } else { unstick_post( $post_id ); } } return $post_id; } /** * Processes the post data for the bulk editing of posts. * * Updates all bulk edited posts/pages, adding (but not removing) tags and * categories. Skips pages when they would be their own parent or child. * * @since 2.7.0 * * @global wpdb $wpdb WordPress database abstraction object. * * @param array|null $post_data Optional. The array of post data to process. * Defaults to the `$_POST` superglobal. * @return array */ function bulk_edit_posts( $post_data = null ) { global $wpdb; if ( empty( $post_data ) ) { $post_data = &$_POST; } if ( isset( $post_data['post_type'] ) ) { $ptype = get_post_type_object( $post_data['post_type'] ); } else { $ptype = get_post_type_object( 'post' ); } if ( ! current_user_can( $ptype->cap->edit_posts ) ) { if ( 'page' === $ptype->name ) { wp_die( __( 'Sorry, you are not allowed to edit pages.' ) ); } else { wp_die( __( 'Sorry, you are not allowed to edit posts.' ) ); } } if ( -1 == $post_data['_status'] ) { $post_data['post_status'] = null; unset( $post_data['post_status'] ); } else { $post_data['post_status'] = $post_data['_status']; } unset( $post_data['_status'] ); if ( ! empty( $post_data['post_status'] ) ) { $post_data['post_status'] = sanitize_key( $post_data['post_status'] ); if ( 'inherit' === $post_data['post_status'] ) { unset( $post_data['post_status'] ); } } $post_ids = array_map( 'intval', (array) $post_data['post'] ); $reset = array( 'post_author', 'post_status', 'post_password', 'post_parent', 'page_template', 'comment_status', 'ping_status', 'keep_private', 'tax_input', 'post_category', 'sticky', 'post_format', ); foreach ( $reset as $field ) { if ( isset( $post_data[ $field ] ) && ( '' === $post_data[ $field ] || -1 == $post_data[ $field ] ) ) { unset( $post_data[ $field ] ); } } if ( isset( $post_data['post_category'] ) ) { if ( is_array( $post_data['post_category'] ) && ! empty( $post_data['post_category'] ) ) { $new_cats = array_map( 'absint', $post_data['post_category'] ); } else { unset( $post_data['post_category'] ); } } $tax_input = array(); if ( isset( $post_data['tax_input'] ) ) { foreach ( $post_data['tax_input'] as $tax_name => $terms ) { if ( empty( $terms ) ) { continue; } if ( is_taxonomy_hierarchical( $tax_name ) ) { $tax_input[ $tax_name ] = array_map( 'absint', $terms ); } else { $comma = _x( ',', 'tag delimiter' ); if ( ',' !== $comma ) { $terms = str_replace( $comma, ',', $terms ); } $tax_input[ $tax_name ] = explode( ',', trim( $terms, " \n\t\r\0\x0B," ) ); } } } if ( isset( $post_data['post_parent'] ) && (int) $post_data['post_parent'] ) { $parent = (int) $post_data['post_parent']; $pages = $wpdb->get_results( "SELECT ID, post_parent FROM $wpdb->posts WHERE post_type = 'page'" ); $children = array(); for ( $i = 0; $i < 50 && $parent > 0; $i++ ) { $children[] = $parent; foreach ( $pages as $page ) { if ( (int) $page->ID === $parent ) { $parent = (int) $page->post_parent; break; } } } } $updated = array(); $skipped = array(); $locked = array(); $shared_post_data = $post_data; foreach ( $post_ids as $post_id ) { // Start with fresh post data with each iteration. $post_data = $shared_post_data; $post_type_object = get_post_type_object( get_post_type( $post_id ) ); if ( ! isset( $post_type_object ) || ( isset( $children ) && in_array( $post_id, $children, true ) ) || ! current_user_can( 'edit_post', $post_id ) ) { $skipped[] = $post_id; continue; } if ( wp_check_post_lock( $post_id ) ) { $locked[] = $post_id; continue; } $post = get_post( $post_id ); $tax_names = get_object_taxonomies( $post ); foreach ( $tax_names as $tax_name ) { $taxonomy_obj = get_taxonomy( $tax_name ); if ( ! $taxonomy_obj->show_in_quick_edit ) { continue; } if ( isset( $tax_input[ $tax_name ] ) && current_user_can( $taxonomy_obj->cap->assign_terms ) ) { $new_terms = $tax_input[ $tax_name ]; } else { $new_terms = array(); } if ( $taxonomy_obj->hierarchical ) { $current_terms = (array) wp_get_object_terms( $post_id, $tax_name, array( 'fields' => 'ids' ) ); } else { $current_terms = (array) wp_get_object_terms( $post_id, $tax_name, array( 'fields' => 'names' ) ); } $post_data['tax_input'][ $tax_name ] = array_merge( $current_terms, $new_terms ); } if ( isset( $new_cats ) && in_array( 'category', $tax_names, true ) ) { $cats = (array) wp_get_post_categories( $post_id ); if ( isset( $post_data['indeterminate_post_category'] ) && is_array( $post_data['indeterminate_post_category'] ) ) { $indeterminate_post_category = $post_data['indeterminate_post_category']; } else { $indeterminate_post_category = array(); } $indeterminate_cats = array_intersect( $cats, $indeterminate_post_category ); $determinate_cats = array_diff( $new_cats, $indeterminate_post_category ); $post_data['post_category'] = array_unique( array_merge( $indeterminate_cats, $determinate_cats ) ); unset( $post_data['tax_input']['category'] ); } $post_data['post_ID'] = $post_id; $post_data['post_type'] = $post->post_type; $post_data['post_mime_type'] = $post->post_mime_type; foreach ( array( 'comment_status', 'ping_status', 'post_author' ) as $field ) { if ( ! isset( $post_data[ $field ] ) ) { $post_data[ $field ] = $post->$field; } } $post_data = _wp_translate_postdata( true, $post_data ); if ( is_wp_error( $post_data ) ) { $skipped[] = $post_id; continue; } $post_data = _wp_get_allowed_postdata( $post_data ); if ( isset( $shared_post_data['post_format'] ) ) { set_post_format( $post_id, $shared_post_data['post_format'] ); } // Prevent wp_insert_post() from overwriting post format with the old data. unset( $post_data['tax_input']['post_format'] ); // Reset post date of scheduled post to be published. if ( in_array( $post->post_status, array( 'future', 'draft' ), true ) && 'publish' === $post_data['post_status'] ) { $post_data['post_date'] = current_time( 'mysql' ); $post_data['post_date_gmt'] = ''; } $post_id = wp_update_post( $post_data ); update_post_meta( $post_id, '_edit_last', get_current_user_id() ); $updated[] = $post_id; if ( isset( $post_data['sticky'] ) && current_user_can( $ptype->cap->edit_others_posts ) ) { if ( 'sticky' === $post_data['sticky'] ) { stick_post( $post_id ); } else { unstick_post( $post_id ); } } } /** * Fires after processing the post data for bulk edit. * * @since 6.3.0 * * @param int[] $updated An array of updated post IDs. * @param array $shared_post_data Associative array containing the post data. */ do_action( 'bulk_edit_posts', $updated, $shared_post_data ); return array( 'updated' => $updated, 'skipped' => $skipped, 'locked' => $locked, ); } /** * Returns default post information to use when populating the "Write Post" form. * * @since 2.0.0 * * @param string $post_type Optional. A post type string. Default 'post'. * @param bool $create_in_db Optional. Whether to insert the post into database. Default false. * @return WP_Post Post object containing all the default post data as attributes */ function get_default_post_to_edit( $post_type = 'post', $create_in_db = false ) { $post_title = ''; if ( ! empty( $_REQUEST['post_title'] ) ) { $post_title = esc_html( wp_unslash( $_REQUEST['post_title'] ) ); } $post_content = ''; if ( ! empty( $_REQUEST['content'] ) ) { $post_content = esc_html( wp_unslash( $_REQUEST['content'] ) ); } $post_excerpt = ''; if ( ! empty( $_REQUEST['excerpt'] ) ) { $post_excerpt = esc_html( wp_unslash( $_REQUEST['excerpt'] ) ); } if ( $create_in_db ) { $post_id = wp_insert_post( array( 'post_title' => __( 'Auto Draft' ), 'post_type' => $post_type, 'post_status' => 'auto-draft', ), false, false ); $post = get_post( $post_id ); if ( current_theme_supports( 'post-formats' ) && post_type_supports( $post->post_type, 'post-formats' ) && get_option( 'default_post_format' ) ) { set_post_format( $post, get_option( 'default_post_format' ) ); } wp_after_insert_post( $post, false, null ); // Schedule auto-draft cleanup. if ( ! wp_next_scheduled( 'wp_scheduled_auto_draft_delete' ) ) { wp_schedule_event( time(), 'daily', 'wp_scheduled_auto_draft_delete' ); } } else { $post = new stdClass(); $post->ID = 0; $post->post_author = ''; $post->post_date = ''; $post->post_date_gmt = ''; $post->post_password = ''; $post->post_name = ''; $post->post_type = $post_type; $post->post_status = 'draft'; $post->to_ping = ''; $post->pinged = ''; $post->comment_status = get_default_comment_status( $post_type ); $post->ping_status = get_default_comment_status( $post_type, 'pingback' ); $post->post_pingback = get_option( 'default_pingback_flag' ); $post->post_category = get_option( 'default_category' ); $post->page_template = 'default'; $post->post_parent = 0; $post->menu_order = 0; $post = new WP_Post( $post ); } /** * Filters the default post content initially used in the "Write Post" form. * * @since 1.5.0 * * @param string $post_content Default post content. * @param WP_Post $post Post object. */ $post->post_content = (string) apply_filters( 'default_content', $post_content, $post ); /** * Filters the default post title initially used in the "Write Post" form. * * @since 1.5.0 * * @param string $post_title Default post title. * @param WP_Post $post Post object. */ $post->post_title = (string) apply_filters( 'default_title', $post_title, $post ); /** * Filters the default post excerpt initially used in the "Write Post" form. * * @since 1.5.0 * * @param string $post_excerpt Default post excerpt. * @param WP_Post $post Post object. */ $post->post_excerpt = (string) apply_filters( 'default_excerpt', $post_excerpt, $post ); return $post; } /** * Determines if a post exists based on title, content, date and type. * * @since 2.0.0 * @since 5.2.0 Added the `$type` parameter. * @since 5.8.0 Added the `$status` parameter. * * @global wpdb $wpdb WordPress database abstraction object. * * @param string $title Post title. * @param string $content Optional. Post content. * @param string $date Optional. Post date. * @param string $type Optional. Post type. * @param string $status Optional. Post status. * @return int Post ID if post exists, 0 otherwise. */ function post_exists( $title, $content = '', $date = '', $type = '', $status = '' ) { global $wpdb; $post_title = wp_unslash( sanitize_post_field( 'post_title', $title, 0, 'db' ) ); $post_content = wp_unslash( sanitize_post_field( 'post_content', $content, 0, 'db' ) ); $post_date = wp_unslash( sanitize_post_field( 'post_date', $date, 0, 'db' ) ); $post_type = wp_unslash( sanitize_post_field( 'post_type', $type, 0, 'db' ) ); $post_status = wp_unslash( sanitize_post_field( 'post_status', $status, 0, 'db' ) ); $query = "SELECT ID FROM $wpdb->posts WHERE 1=1"; $args = array(); if ( ! empty( $date ) ) { $query .= ' AND post_date = %s'; $args[] = $post_date; } if ( ! empty( $title ) ) { $query .= ' AND post_title = %s'; $args[] = $post_title; } if ( ! empty( $content ) ) { $query .= ' AND post_content = %s'; $args[] = $post_content; } if ( ! empty( $type ) ) { $query .= ' AND post_type = %s'; $args[] = $post_type; } if ( ! empty( $status ) ) { $query .= ' AND post_status = %s'; $args[] = $post_status; } if ( ! empty( $args ) ) { return (int) $wpdb->get_var( $wpdb->prepare( $query, $args ) ); } return 0; } /** * Creates a new post from the "Write Post" form using `$_POST` information. * * @since 2.1.0 * * @global WP_User $current_user * * @return int|WP_Error Post ID on success, WP_Error on failure. */ function wp_write_post() { if ( isset( $_POST['post_type'] ) ) { $ptype = get_post_type_object( $_POST['post_type'] ); } else { $ptype = get_post_type_object( 'post' ); } if ( ! current_user_can( $ptype->cap->edit_posts ) ) { if ( 'page' === $ptype->name ) { return new WP_Error( 'edit_pages', __( 'Sorry, you are not allowed to create pages on this site.' ) ); } else { return new WP_Error( 'edit_posts', __( 'Sorry, you are not allowed to create posts or drafts on this site.' ) ); } } $_POST['post_mime_type'] = ''; // Clear out any data in internal vars. unset( $_POST['filter'] ); // Edit, don't write, if we have a post ID. if ( isset( $_POST['post_ID'] ) ) { return edit_post(); } if ( isset( $_POST['visibility'] ) ) { switch ( $_POST['visibility'] ) { case 'public': $_POST['post_password'] = ''; break; case 'password': unset( $_POST['sticky'] ); break; case 'private': $_POST['post_status'] = 'private'; $_POST['post_password'] = ''; unset( $_POST['sticky'] ); break; } } $translated = _wp_translate_postdata( false ); if ( is_wp_error( $translated ) ) { return $translated; } $translated = _wp_get_allowed_postdata( $translated ); // Create the post. $post_id = wp_insert_post( $translated ); if ( is_wp_error( $post_id ) ) { return $post_id; } if ( empty( $post_id ) ) { return 0; } add_meta( $post_id ); add_post_meta( $post_id, '_edit_last', $GLOBALS['current_user']->ID ); // Now that we have an ID we can fix any attachment anchor hrefs. _fix_attachment_links( $post_id ); wp_set_post_lock( $post_id ); return $post_id; } /** * Calls wp_write_post() and handles the errors. * * @since 2.0.0 * * @return int|void Post ID on success, void on failure. */ function write_post() { $result = wp_write_post(); if ( is_wp_error( $result ) ) { wp_die( $result->get_error_message() ); } else { return $result; } } // // Post Meta. // /** * Adds post meta data defined in the `$_POST` superglobal for a post with given ID. * * @since 1.2.0 * * @param int $post_id * @return int|bool */ function add_meta( $post_id ) { $post_id = (int) $post_id; $metakeyselect = isset( $_POST['metakeyselect'] ) ? wp_unslash( trim( $_POST['metakeyselect'] ) ) : ''; $metakeyinput = isset( $_POST['metakeyinput'] ) ? wp_unslash( trim( $_POST['metakeyinput'] ) ) : ''; $metavalue = isset( $_POST['metavalue'] ) ? $_POST['metavalue'] : ''; if ( is_string( $metavalue ) ) { $metavalue = trim( $metavalue ); } if ( ( ( '#NONE#' !== $metakeyselect ) && ! empty( $metakeyselect ) ) || ! empty( $metakeyinput ) ) { /* * We have a key/value pair. If both the select and the input * for the key have data, the input takes precedence. */ if ( '#NONE#' !== $metakeyselect ) { $metakey = $metakeyselect; } if ( $metakeyinput ) { $metakey = $metakeyinput; // Default. } if ( is_protected_meta( $metakey, 'post' ) || ! current_user_can( 'add_post_meta', $post_id, $metakey ) ) { return false; } $metakey = wp_slash( $metakey ); return add_post_meta( $post_id, $metakey, $metavalue ); } return false; } /** * Deletes post meta data by meta ID. * * @since 1.2.0 * * @param int $mid * @return bool */ function delete_meta( $mid ) { return delete_metadata_by_mid( 'post', $mid ); } /** * Returns a list of previously defined keys. * * @since 1.2.0 * * @global wpdb $wpdb WordPress database abstraction object. * * @return string[] Array of meta key names. */ function get_meta_keys() { global $wpdb; $keys = $wpdb->get_col( "SELECT meta_key FROM $wpdb->postmeta GROUP BY meta_key ORDER BY meta_key" ); return $keys; } /** * Returns post meta data by meta ID. * * @since 2.1.0 * * @param int $mid * @return object|bool */ function get_post_meta_by_id( $mid ) { return get_metadata_by_mid( 'post', $mid ); } /** * Returns meta data for the given post ID. * * @since 1.2.0 * * @global wpdb $wpdb WordPress database abstraction object. * * @param int $postid A post ID. * @return array[] { * Array of meta data arrays for the given post ID. * * @type array ...$0 { * Associative array of meta data. * * @type string $meta_key Meta key. * @type mixed $meta_value Meta value. * @type string $meta_id Meta ID as a numeric string. * @type string $post_id Post ID as a numeric string. * } * } */ function has_meta( $postid ) { global $wpdb; return $wpdb->get_results( $wpdb->prepare( "SELECT meta_key, meta_value, meta_id, post_id FROM $wpdb->postmeta WHERE post_id = %d ORDER BY meta_key,meta_id", $postid ), ARRAY_A ); } /** * Updates post meta data by meta ID. * * @since 1.2.0 * * @param int $meta_id Meta ID. * @param string $meta_key Meta key. Expect slashed. * @param string $meta_value Meta value. Expect slashed. * @return bool */ function update_meta( $meta_id, $meta_key, $meta_value ) { $meta_key = wp_unslash( $meta_key ); $meta_value = wp_unslash( $meta_value ); return update_metadata_by_mid( 'post', $meta_id, $meta_value, $meta_key ); } // // Private. // /** * Replaces hrefs of attachment anchors with up-to-date permalinks. * * @since 2.3.0 * @access private * * @param int|object $post Post ID or post object. * @return void|int|WP_Error Void if nothing fixed. 0 or WP_Error on update failure. The post ID on update success. */ function _fix_attachment_links( $post ) { $post = get_post( $post, ARRAY_A ); $content = $post['post_content']; // Don't run if no pretty permalinks or post is not published, scheduled, or privately published. if ( ! get_option( 'permalink_structure' ) || ! in_array( $post['post_status'], array( 'publish', 'future', 'private' ), true ) ) { return; } // Short if there aren't any links or no '?attachment_id=' strings (strpos cannot be zero). if ( ! strpos( $content, '?attachment_id=' ) || ! preg_match_all( '/<a ([^>]+)>[\s\S]+?<\/a>/', $content, $link_matches ) ) { return; } $site_url = get_bloginfo( 'url' ); $site_url = substr( $site_url, (int) strpos( $site_url, '://' ) ); // Remove the http(s). $replace = ''; foreach ( $link_matches[1] as $key => $value ) { if ( ! strpos( $value, '?attachment_id=' ) || ! strpos( $value, 'wp-att-' ) || ! preg_match( '/href=(["\'])[^"\']*\?attachment_id=(\d+)[^"\']*\\1/', $value, $url_match ) || ! preg_match( '/rel=["\'][^"\']*wp-att-(\d+)/', $value, $rel_match ) ) { continue; } $quote = $url_match[1]; // The quote (single or double). $url_id = (int) $url_match[2]; $rel_id = (int) $rel_match[1]; if ( ! $url_id || ! $rel_id || $url_id != $rel_id || ! str_contains( $url_match[0], $site_url ) ) { continue; } $link = $link_matches[0][ $key ]; $replace = str_replace( $url_match[0], 'href=' . $quote . get_attachment_link( $url_id ) . $quote, $link ); $content = str_replace( $link, $replace, $content ); } if ( $replace ) { $post['post_content'] = $content; // Escape data pulled from DB. $post = add_magic_quotes( $post ); return wp_update_post( $post ); } } /** * Returns all the possible statuses for a post type. * * @since 2.5.0 * * @param string $type The post_type you want the statuses for. Default 'post'. * @return string[] An array of all the statuses for the supplied post type. */ function get_available_post_statuses( $type = 'post' ) { $stati = wp_count_posts( $type ); return array_keys( get_object_vars( $stati ) ); } /** * Runs the query to fetch the posts for listing on the edit posts page. * * @since 2.5.0 * * @param array|false $q Optional. Array of query variables to use to build the query. * Defaults to the `$_GET` superglobal. * @return array */ function wp_edit_posts_query( $q = false ) { if ( false === $q ) { $q = $_GET; } $q['m'] = isset( $q['m'] ) ? (int) $q['m'] : 0; $q['cat'] = isset( $q['cat'] ) ? (int) $q['cat'] : 0; $post_stati = get_post_stati(); if ( isset( $q['post_type'] ) && in_array( $q['post_type'], get_post_types(), true ) ) { $post_type = $q['post_type']; } else { $post_type = 'post'; } $avail_post_stati = get_available_post_statuses( $post_type ); $post_status = ''; $perm = ''; if ( isset( $q['post_status'] ) && in_array( $q['post_status'], $post_stati, true ) ) { $post_status = $q['post_status']; $perm = 'readable'; } $orderby = ''; if ( isset( $q['orderby'] ) ) { $orderby = $q['orderby']; } elseif ( isset( $q['post_status'] ) && in_array( $q['post_status'], array( 'pending', 'draft' ), true ) ) { $orderby = 'modified'; } $order = ''; if ( isset( $q['order'] ) ) { $order = $q['order']; } elseif ( isset( $q['post_status'] ) && 'pending' === $q['post_status'] ) { $order = 'ASC'; } $per_page = "edit_{$post_type}_per_page"; $posts_per_page = (int) get_user_option( $per_page ); if ( empty( $posts_per_page ) || $posts_per_page < 1 ) { $posts_per_page = 20; } /** * Filters the number of items per page to show for a specific 'per_page' type. * * The dynamic portion of the hook name, `$post_type`, refers to the post type. * * Possible hook names include: * * - `edit_post_per_page` * - `edit_page_per_page` * - `edit_attachment_per_page` * * @since 3.0.0 * * @param int $posts_per_page Number of posts to display per page for the given post * type. Default 20. */ $posts_per_page = apply_filters( "edit_{$post_type}_per_page", $posts_per_page ); /** * Filters the number of posts displayed per page when specifically listing "posts". * * @since 2.8.0 * * @param int $posts_per_page Number of posts to be displayed. Default 20. * @param string $post_type The post type. */ $posts_per_page = apply_filters( 'edit_posts_per_page', $posts_per_page, $post_type ); $query = compact( 'post_type', 'post_status', 'perm', 'order', 'orderby', 'posts_per_page' ); // Hierarchical types require special args. if ( is_post_type_hierarchical( $post_type ) && empty( $orderby ) ) { $query['orderby'] = 'menu_order title'; $query['order'] = 'asc'; $query['posts_per_page'] = -1; $query['posts_per_archive_page'] = -1; $query['fields'] = 'id=>parent'; } if ( ! empty( $q['show_sticky'] ) ) { $query['post__in'] = (array) get_option( 'sticky_posts' ); } wp( $query ); return $avail_post_stati; } /** * Returns the query variables for the current attachments request. * * @since 4.2.0 * * @param array|false $q Optional. Array of query variables to use to build the query. * Defaults to the `$_GET` superglobal. * @return array The parsed query vars. */ function wp_edit_attachments_query_vars( $q = false ) { if ( false === $q ) { $q = $_GET; } $q['m'] = isset( $q['m'] ) ? (int) $q['m'] : 0; $q['cat'] = isset( $q['cat'] ) ? (int) $q['cat'] : 0; $q['post_type'] = 'attachment'; $post_type = get_post_type_object( 'attachment' ); $states = 'inherit'; if ( current_user_can( $post_type->cap->read_private_posts ) ) { $states .= ',private'; } $q['post_status'] = isset( $q['status'] ) && 'trash' === $q['status'] ? 'trash' : $states; $q['post_status'] = isset( $q['attachment-filter'] ) && 'trash' === $q['attachment-filter'] ? 'trash' : $states; $media_per_page = (int) get_user_option( 'upload_per_page' ); if ( empty( $media_per_page ) || $media_per_page < 1 ) { $media_per_page = 20; } /** * Filters the number of items to list per page when listing media items. * * @since 2.9.0 * * @param int $media_per_page Number of media to list. Default 20. */ $q['posts_per_page'] = apply_filters( 'upload_per_page', $media_per_page ); $post_mime_types = get_post_mime_types(); if ( isset( $q['post_mime_type'] ) && ! array_intersect( (array) $q['post_mime_type'], array_keys( $post_mime_types ) ) ) { unset( $q['post_mime_type'] ); } foreach ( array_keys( $post_mime_types ) as $type ) { if ( isset( $q['attachment-filter'] ) && "post_mime_type:$type" === $q['attachment-filter'] ) { $q['post_mime_type'] = $type; break; } } if ( isset( $q['detached'] ) || ( isset( $q['attachment-filter'] ) && 'detached' === $q['attachment-filter'] ) ) { $q['post_parent'] = 0; } if ( isset( $q['mine'] ) || ( isset( $q['attachment-filter'] ) && 'mine' === $q['attachment-filter'] ) ) { $q['author'] = get_current_user_id(); } // Filter query clauses to include filenames. if ( isset( $q['s'] ) ) { add_filter( 'wp_allow_query_attachment_by_filename', '__return_true' ); } return $q; } /** * Executes a query for attachments. An array of WP_Query arguments * can be passed in, which will override the arguments set by this function. * * @since 2.5.0 * * @param array|false $q Optional. Array of query variables to use to build the query. * Defaults to the `$_GET` superglobal. * @return array */ function wp_edit_attachments_query( $q = false ) { wp( wp_edit_attachments_query_vars( $q ) ); $post_mime_types = get_post_mime_types(); $avail_post_mime_types = get_available_post_mime_types( 'attachment' ); return array( $post_mime_types, $avail_post_mime_types ); } /** * Returns the list of classes to be used by a meta box. * * @since 2.5.0 * * @param string $box_id Meta box ID (used in the 'id' attribute for the meta box). * @param string $screen_id The screen on which the meta box is shown. * @return string Space-separated string of class names. */ function postbox_classes( $box_id, $screen_id ) { if ( isset( $_GET['edit'] ) && $_GET['edit'] == $box_id ) { $classes = array( '' ); } elseif ( get_user_option( 'closedpostboxes_' . $screen_id ) ) { $closed = get_user_option( 'closedpostboxes_' . $screen_id ); if ( ! is_array( $closed ) ) { $classes = array( '' ); } else { $classes = in_array( $box_id, $closed, true ) ? array( 'closed' ) : array( '' ); } } else { $classes = array( '' ); } /** * Filters the postbox classes for a specific screen and box ID combo. * * The dynamic portions of the hook name, `$screen_id` and `$box_id`, refer to * the screen ID and meta box ID, respectively. * * @since 3.2.0 * * @param string[] $classes An array of postbox classes. */ $classes = apply_filters( "postbox_classes_{$screen_id}_{$box_id}", $classes ); return implode( ' ', $classes ); } /** * Returns a sample permalink based on the post name. * * @since 2.5.0 * * @param int|WP_Post $post Post ID or post object. * @param string|null $title Optional. Title to override the post's current title * when generating the post name. Default null. * @param string|null $name Optional. Name to override the post name. Default null. * @return array { * Array containing the sample permalink with placeholder for the post name, and the post name. * * @type string $0 The permalink with placeholder for the post name. * @type string $1 The post name. * } */ function get_sample_permalink( $post, $title = null, $name = null ) { $post = get_post( $post ); if ( ! $post ) { return array( '', '' ); } $ptype = get_post_type_object( $post->post_type ); $original_status = $post->post_status; $original_date = $post->post_date; $original_name = $post->post_name; $original_filter = $post->filter; // Hack: get_permalink() would return plain permalink for drafts, so we will fake that our post is published. if ( in_array( $post->post_status, array( 'draft', 'pending', 'future' ), true ) ) { $post->post_status = 'publish'; $post->post_name = sanitize_title( $post->post_name ? $post->post_name : $post->post_title, $post->ID ); } /* * If the user wants to set a new name -- override the current one. * Note: if empty name is supplied -- use the title instead, see #6072. */ if ( ! is_null( $name ) ) { $post->post_name = sanitize_title( $name ? $name : $title, $post->ID ); } $post->post_name = wp_unique_post_slug( $post->post_name, $post->ID, $post->post_status, $post->post_type, $post->post_parent ); $post->filter = 'sample'; $permalink = get_permalink( $post, true ); // Replace custom post_type token with generic pagename token for ease of use. $permalink = str_replace( "%$post->post_type%", '%pagename%', $permalink ); // Handle page hierarchy. if ( $ptype->hierarchical ) { $uri = get_page_uri( $post ); if ( $uri ) { $uri = untrailingslashit( $uri ); $uri = strrev( stristr( strrev( $uri ), '/' ) ); $uri = untrailingslashit( $uri ); } /** This filter is documented in wp-admin/edit-tag-form.php */ $uri = apply_filters( 'editable_slug', $uri, $post ); if ( ! empty( $uri ) ) { $uri .= '/'; } $permalink = str_replace( '%pagename%', "{$uri}%pagename%", $permalink ); } /** This filter is documented in wp-admin/edit-tag-form.php */ $permalink = array( $permalink, apply_filters( 'editable_slug', $post->post_name, $post ) ); $post->post_status = $original_status; $post->post_date = $original_date; $post->post_name = $original_name; $post->filter = $original_filter; /** * Filters the sample permalink. * * @since 4.4.0 * * @param array $permalink { * Array containing the sample permalink with placeholder for the post name, and the post name. * * @type string $0 The permalink with placeholder for the post name. * @type string $1 The post name. * } * @param int $post_id Post ID. * @param string $title Post title. * @param string $name Post name (slug). * @param WP_Post $post Post object. */ return apply_filters( 'get_sample_permalink', $permalink, $post->ID, $title, $name, $post ); } /** * Returns the HTML of the sample permalink slug editor. * * @since 2.5.0 * * @param int|WP_Post $post Post ID or post object. * @param string|null $new_title Optional. New title. Default null. * @param string|null $new_slug Optional. New slug. Default null. * @return string The HTML of the sample permalink slug editor. */ function get_sample_permalink_html( $post, $new_title = null, $new_slug = null ) { $post = get_post( $post ); if ( ! $post ) { return ''; } list($permalink, $post_name) = get_sample_permalink( $post->ID, $new_title, $new_slug ); $view_link = false; $preview_target = ''; if ( current_user_can( 'read_post', $post->ID ) ) { if ( 'draft' === $post->post_status || empty( $post->post_name ) ) { $view_link = get_preview_post_link( $post ); $preview_target = " target='wp-preview-{$post->ID}'"; } else { if ( 'publish' === $post->post_status || 'attachment' === $post->post_type ) { $view_link = get_permalink( $post ); } else { // Allow non-published (private, future) to be viewed at a pretty permalink, in case $post->post_name is set. $view_link = str_replace( array( '%pagename%', '%postname%' ), $post->post_name, $permalink ); } } } // Permalinks without a post/page name placeholder don't have anything to edit. if ( ! str_contains( $permalink, '%postname%' ) && ! str_contains( $permalink, '%pagename%' ) ) { $return = '<strong>' . __( 'Permalink:' ) . "</strong>\n"; if ( false !== $view_link ) { $display_link = urldecode( $view_link ); $return .= '<a id="sample-permalink" href="' . esc_url( $view_link ) . '"' . $preview_target . '>' . esc_html( $display_link ) . "</a>\n"; } else { $return .= '<span id="sample-permalink">' . $permalink . "</span>\n"; } // Encourage a pretty permalink setting. if ( ! get_option( 'permalink_structure' ) && current_user_can( 'manage_options' ) && ! ( 'page' === get_option( 'show_on_front' ) && get_option( 'page_on_front' ) == $post->ID ) ) { $return .= '<span id="change-permalinks"><a href="options-permalink.php" class="button button-small">' . __( 'Change Permalink Structure' ) . "</a></span>\n"; } } else { if ( mb_strlen( $post_name ) > 34 ) { $post_name_abridged = mb_substr( $post_name, 0, 16 ) . '…' . mb_substr( $post_name, -16 ); } else { $post_name_abridged = $post_name; } $post_name_html = '<span id="editable-post-name">' . esc_html( $post_name_abridged ) . '</span>'; $display_link = str_replace( array( '%pagename%', '%postname%' ), $post_name_html, esc_html( urldecode( $permalink ) ) ); $return = '<strong>' . __( 'Permalink:' ) . "</strong>\n"; $return .= '<span id="sample-permalink"><a href="' . esc_url( $view_link ) . '"' . $preview_target . '>' . $display_link . "</a></span>\n"; $return .= '‎'; // Fix bi-directional text display defect in RTL languages. $return .= '<span id="edit-slug-buttons"><button type="button" class="edit-slug button button-small hide-if-no-js" aria-label="' . __( 'Edit permalink' ) . '">' . __( 'Edit' ) . "</button></span>\n"; $return .= '<span id="editable-post-name-full">' . esc_html( $post_name ) . "</span>\n"; } /** * Filters the sample permalink HTML markup. * * @since 2.9.0 * @since 4.4.0 Added `$post` parameter. * * @param string $return Sample permalink HTML markup. * @param int $post_id Post ID. * @param string|null $new_title New sample permalink title. * @param string|null $new_slug New sample permalink slug. * @param WP_Post $post Post object. */ $return = apply_filters( 'get_sample_permalink_html', $return, $post->ID, $new_title, $new_slug, $post ); return $return; } /** * Returns HTML for the post thumbnail meta box. * * @since 2.9.0 * * @param int|null $thumbnail_id Optional. Thumbnail attachment ID. Default null. * @param int|WP_Post|null $post Optional. The post ID or object associated * with the thumbnail. Defaults to global $post. * @return string The post thumbnail HTML. */ function _wp_post_thumbnail_html( $thumbnail_id = null, $post = null ) { $_wp_additional_image_sizes = wp_get_additional_image_sizes(); $post = get_post( $post ); $post_type_object = get_post_type_object( $post->post_type ); $set_thumbnail_link = '<p class="hide-if-no-js"><a href="%s" id="set-post-thumbnail"%s class="thickbox">%s</a></p>'; $upload_iframe_src = get_upload_iframe_src( 'image', $post->ID ); $content = sprintf( $set_thumbnail_link, esc_url( $upload_iframe_src ), '', // Empty when there's no featured image set, `aria-describedby` attribute otherwise. esc_html( $post_type_object->labels->set_featured_image ) ); if ( $thumbnail_id && get_post( $thumbnail_id ) ) { $size = isset( $_wp_additional_image_sizes['post-thumbnail'] ) ? 'post-thumbnail' : array( 266, 266 ); /** * Filters the size used to display the post thumbnail image in the 'Featured image' meta box. * * Note: When a theme adds 'post-thumbnail' support, a special 'post-thumbnail' * image size is registered, which differs from the 'thumbnail' image size * managed via the Settings > Media screen. * * @since 4.4.0 * * @param string|int[] $size Requested image size. Can be any registered image size name, or * an array of width and height values in pixels (in that order). * @param int $thumbnail_id Post thumbnail attachment ID. * @param WP_Post $post The post object associated with the thumbnail. */ $size = apply_filters( 'admin_post_thumbnail_size', $size, $thumbnail_id, $post ); $thumbnail_html = wp_get_attachment_image( $thumbnail_id, $size ); if ( ! empty( $thumbnail_html ) ) { $content = sprintf( $set_thumbnail_link, esc_url( $upload_iframe_src ), ' aria-describedby="set-post-thumbnail-desc"', $thumbnail_html ); $content .= '<p class="hide-if-no-js howto" id="set-post-thumbnail-desc">' . __( 'Click the image to edit or update' ) . '</p>'; $content .= '<p class="hide-if-no-js"><a href="#" id="remove-post-thumbnail">' . esc_html( $post_type_object->labels->remove_featured_image ) . '</a></p>'; } } $content .= '<input type="hidden" id="_thumbnail_id" name="_thumbnail_id" value="' . esc_attr( $thumbnail_id ? $thumbnail_id : '-1' ) . '" />'; /** * Filters the admin post thumbnail HTML markup to return. * * @since 2.9.0 * @since 3.5.0 Added the `$post_id` parameter. * @since 4.6.0 Added the `$thumbnail_id` parameter. * * @param string $content Admin post thumbnail HTML markup. * @param int $post_id Post ID. * @param int|null $thumbnail_id Thumbnail attachment ID, or null if there isn't one. */ return apply_filters( 'admin_post_thumbnail_html', $content, $post->ID, $thumbnail_id ); } /** * Determines whether the post is currently being edited by another user. * * @since 2.5.0 * * @param int|WP_Post $post ID or object of the post to check for editing. * @return int|false ID of the user with lock. False if the post does not exist, post is not locked, * the user with lock does not exist, or the post is locked by current user. */ function wp_check_post_lock( $post ) { $post = get_post( $post ); if ( ! $post ) { return false; } $lock = get_post_meta( $post->ID, '_edit_lock', true ); if ( ! $lock ) { return false; } $lock = explode( ':', $lock ); $time = $lock[0]; $user = isset( $lock[1] ) ? $lock[1] : get_post_meta( $post->ID, '_edit_last', true ); if ( ! get_userdata( $user ) ) { return false; } /** This filter is documented in wp-admin/includes/ajax-actions.php */ $time_window = apply_filters( 'wp_check_post_lock_window', 150 ); if ( $time && $time > time() - $time_window && get_current_user_id() != $user ) { return $user; } return false; } /** * Marks the post as currently being edited by the current user. * * @since 2.5.0 * * @param int|WP_Post $post ID or object of the post being edited. * @return array|false { * Array of the lock time and user ID. False if the post does not exist, or there * is no current user. * * @type int $0 The current time as a Unix timestamp. * @type int $1 The ID of the current user. * } */ function wp_set_post_lock( $post ) { $post = get_post( $post ); if ( ! $post ) { return false; } $user_id = get_current_user_id(); if ( 0 == $user_id ) { return false; } $now = time(); $lock = "$now:$user_id"; update_post_meta( $post->ID, '_edit_lock', $lock ); return array( $now, $user_id ); } /** * Outputs the HTML for the notice to say that someone else is editing or has taken over editing of this post. * * @since 2.8.5 */ function _admin_notice_post_locked() { $post = get_post(); if ( ! $post ) { return; } $user = null; $user_id = wp_check_post_lock( $post->ID ); if ( $user_id ) { $user = get_userdata( $user_id ); } if ( $user ) { /** * Filters whether to show the post locked dialog. * * Returning false from the filter will prevent the dialog from being displayed. * * @since 3.6.0 * * @param bool $display Whether to display the dialog. Default true. * @param WP_Post $post Post object. * @param WP_User $user The user with the lock for the post. */ if ( ! apply_filters( 'show_post_locked_dialog', true, $post, $user ) ) { return; } $locked = true; } else { $locked = false; } $sendback = wp_get_referer(); if ( $locked && $sendback && ! str_contains( $sendback, 'post.php' ) && ! str_contains( $sendback, 'post-new.php' ) ) { $sendback_text = __( 'Go back' ); } else { $sendback = admin_url( 'edit.php' ); if ( 'post' !== $post->post_type ) { $sendback = add_query_arg( 'post_type', $post->post_type, $sendback ); } $sendback_text = get_post_type_object( $post->post_type )->labels->all_items; } $hidden = $locked ? '' : ' hidden'; ?> <div id="post-lock-dialog" class="notification-dialog-wrap<?php echo $hidden; ?>"> <div class="notification-dialog-background"></div> <div class="notification-dialog"> <?php if ( $locked ) { $query_args = array(); if ( get_post_type_object( $post->post_type )->public ) { if ( 'publish' === $post->post_status || $user->ID != $post->post_author ) { // Latest content is in autosave. $nonce = wp_create_nonce( 'post_preview_' . $post->ID ); $query_args['preview_id'] = $post->ID; $query_args['preview_nonce'] = $nonce; } } $preview_link = get_preview_post_link( $post->ID, $query_args ); /** * Filters whether to allow the post lock to be overridden. * * Returning false from the filter will disable the ability * to override the post lock. * * @since 3.6.0 * * @param bool $override Whether to allow the post lock to be overridden. Default true. * @param WP_Post $post Post object. * @param WP_User $user The user with the lock for the post. */ $override = apply_filters( 'override_post_lock', true, $post, $user ); $tab_last = $override ? '' : ' wp-tab-last'; ?> <div class="post-locked-message"> <div class="post-locked-avatar"><?php echo get_avatar( $user->ID, 64 ); ?></div> <p class="currently-editing wp-tab-first" tabindex="0"> <?php if ( $override ) { /* translators: %s: User's display name. */ printf( __( '%s is currently editing this post. Do you want to take over?' ), esc_html( $user->display_name ) ); } else { /* translators: %s: User's display name. */ printf( __( '%s is currently editing this post.' ), esc_html( $user->display_name ) ); } ?> </p> <?php /** * Fires inside the post locked dialog before the buttons are displayed. * * @since 3.6.0 * @since 5.4.0 The $user parameter was added. * * @param WP_Post $post Post object. * @param WP_User $user The user with the lock for the post. */ do_action( 'post_locked_dialog', $post, $user ); ?> <p> <a class="button" href="<?php echo esc_url( $sendback ); ?>"><?php echo $sendback_text; ?></a> <?php if ( $preview_link ) { ?> <a class="button<?php echo $tab_last; ?>" href="<?php echo esc_url( $preview_link ); ?>"><?php _e( 'Preview' ); ?></a> <?php } // Allow plugins to prevent some users overriding the post lock. if ( $override ) { ?> <a class="button button-primary wp-tab-last" href="<?php echo esc_url( add_query_arg( 'get-post-lock', '1', wp_nonce_url( get_edit_post_link( $post->ID, 'url' ), 'lock-post_' . $post->ID ) ) ); ?>"><?php _e( 'Take over' ); ?></a> <?php } ?> </p> </div> <?php } else { ?> <div class="post-taken-over"> <div class="post-locked-avatar"></div> <p class="wp-tab-first" tabindex="0"> <span class="currently-editing"></span><br /> <span class="locked-saving hidden"><img src="<?php echo esc_url( admin_url( 'images/spinner-2x.gif' ) ); ?>" width="16" height="16" alt="" /> <?php _e( 'Saving revision…' ); ?></span> <span class="locked-saved hidden"><?php _e( 'Your latest changes were saved as a revision.' ); ?></span> </p> <?php /** * Fires inside the dialog displayed when a user has lost the post lock. * * @since 3.6.0 * * @param WP_Post $post Post object. */ do_action( 'post_lock_lost_dialog', $post ); ?> <p><a class="button button-primary wp-tab-last" href="<?php echo esc_url( $sendback ); ?>"><?php echo $sendback_text; ?></a></p> </div> <?php } ?> </div> </div> <?php } /** * Creates autosave data for the specified post from `$_POST` data. * * @since 2.6.0 * * @param array|int $post_data Associative array containing the post data, or integer post ID. * If a numeric post ID is provided, will use the `$_POST` superglobal. * @return int|WP_Error The autosave revision ID. WP_Error or 0 on error. */ function wp_create_post_autosave( $post_data ) { if ( is_numeric( $post_data ) ) { $post_id = $post_data; $post_data = $_POST; } else { $post_id = (int) $post_data['post_ID']; } $post_data = _wp_translate_postdata( true, $post_data ); if ( is_wp_error( $post_data ) ) { return $post_data; } $post_data = _wp_get_allowed_postdata( $post_data ); $post_author = get_current_user_id(); // Store one autosave per author. If there is already an autosave, overwrite it. $old_autosave = wp_get_post_autosave( $post_id, $post_author ); if ( $old_autosave ) { $new_autosave = _wp_post_revision_data( $post_data, true ); $new_autosave['ID'] = $old_autosave->ID; $new_autosave['post_author'] = $post_author; $post = get_post( $post_id ); // If the new autosave has the same content as the post, delete the autosave. $autosave_is_different = false; foreach ( array_intersect( array_keys( $new_autosave ), array_keys( _wp_post_revision_fields( $post ) ) ) as $field ) { if ( normalize_whitespace( $new_autosave[ $field ] ) !== normalize_whitespace( $post->$field ) ) { $autosave_is_different = true; break; } } if ( ! $autosave_is_different ) { wp_delete_post_revision( $old_autosave->ID ); return 0; } /** * Fires before an autosave is stored. * * @since 4.1.0 * @since 6.4.0 The `$is_update` parameter was added to indicate if the autosave is being updated or was newly created. * * @param array $new_autosave Post array - the autosave that is about to be saved. * @param bool $is_update Whether this is an existing autosave. */ do_action( 'wp_creating_autosave', $new_autosave, true ); return wp_update_post( $new_autosave ); } // _wp_put_post_revision() expects unescaped. $post_data = wp_unslash( $post_data ); // Otherwise create the new autosave as a special post revision. $revision = _wp_put_post_revision( $post_data, true ); if ( ! is_wp_error( $revision ) && 0 !== $revision ) { /** This action is documented in wp-admin/includes/post.php */ do_action( 'wp_creating_autosave', get_post( $revision, ARRAY_A ), false ); } return $revision; } /** * Autosave the revisioned meta fields. * * Iterates through the revisioned meta fields and checks each to see if they are set, * and have a changed value. If so, the meta value is saved and attached to the autosave. * * @since 6.4.0 * * @param array $new_autosave The new post data being autosaved. */ function wp_autosave_post_revisioned_meta_fields( $new_autosave ) { /* * The post data arrives as either $_POST['data']['wp_autosave'] or the $_POST * itself. This sets $posted_data to the correct variable. * * Ignoring sanitization to avoid altering meta. Ignoring the nonce check because * this is hooked on inner core hooks where a valid nonce was already checked. */ $posted_data = isset( $_POST['data']['wp_autosave'] ) ? $_POST['data']['wp_autosave'] : $_POST; $post_type = get_post_type( $new_autosave['post_parent'] ); /* * Go thru the revisioned meta keys and save them as part of the autosave, if * the meta key is part of the posted data, the meta value is not blank and * the the meta value has changes from the last autosaved value. */ foreach ( wp_post_revision_meta_keys( $post_type ) as $meta_key ) { if ( isset( $posted_data[ $meta_key ] ) && get_post_meta( $new_autosave['ID'], $meta_key, true ) !== wp_unslash( $posted_data[ $meta_key ] ) ) { /* * Use the underlying delete_metadata() and add_metadata() functions * vs delete_post_meta() and add_post_meta() to make sure we're working * with the actual revision meta. */ delete_metadata( 'post', $new_autosave['ID'], $meta_key ); /* * One last check to ensure meta value not empty(). */ if ( ! empty( $posted_data[ $meta_key ] ) ) { /* * Add the revisions meta data to the autosave. */ add_metadata( 'post', $new_autosave['ID'], $meta_key, $posted_data[ $meta_key ] ); } } } } /** * Saves a draft or manually autosaves for the purpose of showing a post preview. * * @since 2.7.0 * * @return string URL to redirect to show the preview. */ function post_preview() { $post_id = (int) $_POST['post_ID']; $_POST['ID'] = $post_id; $post = get_post( $post_id ); if ( ! $post ) { wp_die( __( 'Sorry, you are not allowed to edit this post.' ) ); } if ( ! current_user_can( 'edit_post', $post->ID ) ) { wp_die( __( 'Sorry, you are not allowed to edit this post.' ) ); } $is_autosave = false; if ( ! wp_check_post_lock( $post->ID ) && get_current_user_id() == $post->post_author && ( 'draft' === $post->post_status || 'auto-draft' === $post->post_status ) ) { $saved_post_id = edit_post(); } else { $is_autosave = true; if ( isset( $_POST['post_status'] ) && 'auto-draft' === $_POST['post_status'] ) { $_POST['post_status'] = 'draft'; } $saved_post_id = wp_create_post_autosave( $post->ID ); } if ( is_wp_error( $saved_post_id ) ) { wp_die( $saved_post_id->get_error_message() ); } $query_args = array(); if ( $is_autosave && $saved_post_id ) { $query_args['preview_id'] = $post->ID; $query_args['preview_nonce'] = wp_create_nonce( 'post_preview_' . $post->ID ); if ( isset( $_POST['post_format'] ) ) { $query_args['post_format'] = empty( $_POST['post_format'] ) ? 'standard' : sanitize_key( $_POST['post_format'] ); } if ( isset( $_POST['_thumbnail_id'] ) ) { $query_args['_thumbnail_id'] = ( (int) $_POST['_thumbnail_id'] <= 0 ) ? '-1' : (int) $_POST['_thumbnail_id']; } } return get_preview_post_link( $post, $query_args ); } /** * Saves a post submitted with XHR. * * Intended for use with heartbeat and autosave.js * * @since 3.9.0 * * @param array $post_data Associative array of the submitted post data. * @return mixed The value 0 or WP_Error on failure. The saved post ID on success. * The ID can be the draft post_id or the autosave revision post_id. */ function wp_autosave( $post_data ) { // Back-compat. if ( ! defined( 'DOING_AUTOSAVE' ) ) { define( 'DOING_AUTOSAVE', true ); } $post_id = (int) $post_data['post_id']; $post_data['ID'] = $post_id; $post_data['post_ID'] = $post_id; if ( false === wp_verify_nonce( $post_data['_wpnonce'], 'update-post_' . $post_id ) ) { return new WP_Error( 'invalid_nonce', __( 'Error while saving.' ) ); } $post = get_post( $post_id ); if ( ! current_user_can( 'edit_post', $post->ID ) ) { return new WP_Error( 'edit_posts', __( 'Sorry, you are not allowed to edit this item.' ) ); } if ( 'auto-draft' === $post->post_status ) { $post_data['post_status'] = 'draft'; } if ( 'page' !== $post_data['post_type'] && ! empty( $post_data['catslist'] ) ) { $post_data['post_category'] = explode( ',', $post_data['catslist'] ); } if ( ! wp_check_post_lock( $post->ID ) && get_current_user_id() == $post->post_author && ( 'auto-draft' === $post->post_status || 'draft' === $post->post_status ) ) { // Drafts and auto-drafts are just overwritten by autosave for the same user if the post is not locked. return edit_post( wp_slash( $post_data ) ); } else { /* * Non-drafts or other users' drafts are not overwritten. * The autosave is stored in a special post revision for each user. */ return wp_create_post_autosave( wp_slash( $post_data ) ); } } /** * Redirects to previous page. * * @since 2.7.0 * * @param int $post_id Optional. Post ID. */ function redirect_post( $post_id = '' ) { if ( isset( $_POST['save'] ) || isset( $_POST['publish'] ) ) { $status = get_post_status( $post_id ); if ( isset( $_POST['publish'] ) ) { switch ( $status ) { case 'pending': $message = 8; break; case 'future': $message = 9; break; default: $message = 6; } } else { $message = 'draft' === $status ? 10 : 1; } $location = add_query_arg( 'message', $message, get_edit_post_link( $post_id, 'url' ) ); } elseif ( isset( $_POST['addmeta'] ) && $_POST['addmeta'] ) { $location = add_query_arg( 'message', 2, wp_get_referer() ); $location = explode( '#', $location ); $location = $location[0] . '#postcustom'; } elseif ( isset( $_POST['deletemeta'] ) && $_POST['deletemeta'] ) { $location = add_query_arg( 'message', 3, wp_get_referer() ); $location = explode( '#', $location ); $location = $location[0] . '#postcustom'; } else { $location = add_query_arg( 'message', 4, get_edit_post_link( $post_id, 'url' ) ); } /** * Filters the post redirect destination URL. * * @since 2.9.0 * * @param string $location The destination URL. * @param int $post_id The post ID. */ wp_redirect( apply_filters( 'redirect_post_location', $location, $post_id ) ); exit; } /** * Sanitizes POST values from a checkbox taxonomy metabox. * * @since 5.1.0 * * @param string $taxonomy The taxonomy name. * @param array $terms Raw term data from the 'tax_input' field. * @return int[] Array of sanitized term IDs. */ function taxonomy_meta_box_sanitize_cb_checkboxes( $taxonomy, $terms ) { return array_map( 'intval', $terms ); } /** * Sanitizes POST values from an input taxonomy metabox. * * @since 5.1.0 * * @param string $taxonomy The taxonomy name. * @param array|string $terms Raw term data from the 'tax_input' field. * @return array */ function taxonomy_meta_box_sanitize_cb_input( $taxonomy, $terms ) { /* * Assume that a 'tax_input' string is a comma-separated list of term names. * Some languages may use a character other than a comma as a delimiter, so we standardize on * commas before parsing the list. */ if ( ! is_array( $terms ) ) { $comma = _x( ',', 'tag delimiter' ); if ( ',' !== $comma ) { $terms = str_replace( $comma, ',', $terms ); } $terms = explode( ',', trim( $terms, " \n\t\r\0\x0B," ) ); } $clean_terms = array(); foreach ( $terms as $term ) { // Empty terms are invalid input. if ( empty( $term ) ) { continue; } $_term = get_terms( array( 'taxonomy' => $taxonomy, 'name' => $term, 'fields' => 'ids', 'hide_empty' => false, ) ); if ( ! empty( $_term ) ) { $clean_terms[] = (int) $_term[0]; } else { // No existing term was found, so pass the string. A new term will be created. $clean_terms[] = $term; } } return $clean_terms; } /** * Prepares server-registered blocks for the block editor. * * Returns an associative array of registered block data keyed by block name. Data includes properties * of a block relevant for client registration. * * @since 5.0.0 * @since 6.3.0 Added `selectors` field. * @since 6.4.0 Added `block_hooks` field. * * @return array An associative array of registered block data. */ function get_block_editor_server_block_settings() { $block_registry = WP_Block_Type_Registry::get_instance(); $blocks = array(); $fields_to_pick = array( 'api_version' => 'apiVersion', 'title' => 'title', 'description' => 'description', 'icon' => 'icon', 'attributes' => 'attributes', 'provides_context' => 'providesContext', 'uses_context' => 'usesContext', 'block_hooks' => 'blockHooks', 'selectors' => 'selectors', 'supports' => 'supports', 'category' => 'category', 'styles' => 'styles', 'textdomain' => 'textdomain', 'parent' => 'parent', 'ancestor' => 'ancestor', 'keywords' => 'keywords', 'example' => 'example', 'variations' => 'variations', 'allowed_blocks' => 'allowedBlocks', ); foreach ( $block_registry->get_all_registered() as $block_name => $block_type ) { foreach ( $fields_to_pick as $field => $key ) { if ( ! isset( $block_type->{ $field } ) ) { continue; } if ( ! isset( $blocks[ $block_name ] ) ) { $blocks[ $block_name ] = array(); } $blocks[ $block_name ][ $key ] = $block_type->{ $field }; } } return $blocks; } /** * Renders the meta boxes forms. * * @since 5.0.0 * * @global WP_Post $post Global post object. * @global WP_Screen $current_screen WordPress current screen object. * @global array $wp_meta_boxes */ function the_block_editor_meta_boxes() { global $post, $current_screen, $wp_meta_boxes; // Handle meta box state. $_original_meta_boxes = $wp_meta_boxes; /** * Fires right before the meta boxes are rendered. * * This allows for the filtering of meta box data, that should already be * present by this point. Do not use as a means of adding meta box data. * * @since 5.0.0 * * @param array $wp_meta_boxes Global meta box state. */ $wp_meta_boxes = apply_filters( 'filter_block_editor_meta_boxes', $wp_meta_boxes ); $locations = array( 'side', 'normal', 'advanced' ); $priorities = array( 'high', 'sorted', 'core', 'default', 'low' ); // Render meta boxes. ?> <form class="metabox-base-form"> <?php the_block_editor_meta_box_post_form_hidden_fields( $post ); ?> </form> <form id="toggle-custom-fields-form" method="post" action="<?php echo esc_url( admin_url( 'post.php' ) ); ?>"> <?php wp_nonce_field( 'toggle-custom-fields', 'toggle-custom-fields-nonce' ); ?> <input type="hidden" name="action" value="toggle-custom-fields" /> </form> <?php foreach ( $locations as $location ) : ?> <form class="metabox-location-<?php echo esc_attr( $location ); ?>" onsubmit="return false;"> <div id="poststuff" class="sidebar-open"> <div id="postbox-container-2" class="postbox-container"> <?php do_meta_boxes( $current_screen, $location, $post ); ?> </div> </div> </form> <?php endforeach; ?> <?php $meta_boxes_per_location = array(); foreach ( $locations as $location ) { $meta_boxes_per_location[ $location ] = array(); if ( ! isset( $wp_meta_boxes[ $current_screen->id ][ $location ] ) ) { continue; } foreach ( $priorities as $priority ) { if ( ! isset( $wp_meta_boxes[ $current_screen->id ][ $location ][ $priority ] ) ) { continue; } $meta_boxes = (array) $wp_meta_boxes[ $current_screen->id ][ $location ][ $priority ]; foreach ( $meta_boxes as $meta_box ) { if ( false == $meta_box || ! $meta_box['title'] ) { continue; } // If a meta box is just here for back compat, don't show it in the block editor. if ( isset( $meta_box['args']['__back_compat_meta_box'] ) && $meta_box['args']['__back_compat_meta_box'] ) { continue; } $meta_boxes_per_location[ $location ][] = array( 'id' => $meta_box['id'], 'title' => $meta_box['title'], ); } } } /* * Sadly we probably cannot add this data directly into editor settings. * * Some meta boxes need `admin_head` to fire for meta box registry. * `admin_head` fires after `admin_enqueue_scripts`, which is where we create * our editor instance. */ $script = 'window._wpLoadBlockEditor.then( function() { wp.data.dispatch( \'core/edit-post\' ).setAvailableMetaBoxesPerLocation( ' . wp_json_encode( $meta_boxes_per_location ) . ' ); } );'; wp_add_inline_script( 'wp-edit-post', $script ); /* * When `wp-edit-post` is output in the `<head>`, the inline script needs to be manually printed. * Otherwise, meta boxes will not display because inline scripts for `wp-edit-post` * will not be printed again after this point. */ if ( wp_script_is( 'wp-edit-post', 'done' ) ) { printf( "<script type='text/javascript'>\n%s\n</script>\n", trim( $script ) ); } /* * If the 'postcustom' meta box is enabled, then we need to perform * some extra initialization on it. */ $enable_custom_fields = (bool) get_user_meta( get_current_user_id(), 'enable_custom_fields', true ); if ( $enable_custom_fields ) { $script = "( function( $ ) { if ( $('#postcustom').length ) { $( '#the-list' ).wpList( { addBefore: function( s ) { s.data += '&post_id=$post->ID'; return s; }, addAfter: function() { $('table#list-table').show(); } }); } } )( jQuery );"; wp_enqueue_script( 'wp-lists' ); wp_add_inline_script( 'wp-lists', $script ); } /* * Refresh nonces used by the meta box loader. * * The logic is very similar to that provided by post.js for the classic editor. */ $script = "( function( $ ) { var check, timeout; function schedule() { check = false; window.clearTimeout( timeout ); timeout = window.setTimeout( function() { check = true; }, 300000 ); } $( document ).on( 'heartbeat-send.wp-refresh-nonces', function( e, data ) { var post_id, \$authCheck = $( '#wp-auth-check-wrap' ); if ( check || ( \$authCheck.length && ! \$authCheck.hasClass( 'hidden' ) ) ) { if ( ( post_id = $( '#post_ID' ).val() ) && $( '#_wpnonce' ).val() ) { data['wp-refresh-metabox-loader-nonces'] = { post_id: post_id }; } } }).on( 'heartbeat-tick.wp-refresh-nonces', function( e, data ) { var nonces = data['wp-refresh-metabox-loader-nonces']; if ( nonces ) { if ( nonces.replace ) { if ( nonces.replace.metabox_loader_nonce && window._wpMetaBoxUrl && wp.url ) { window._wpMetaBoxUrl= wp.url.addQueryArgs( window._wpMetaBoxUrl, { 'meta-box-loader-nonce': nonces.replace.metabox_loader_nonce } ); } if ( nonces.replace._wpnonce ) { $( '#_wpnonce' ).val( nonces.replace._wpnonce ); } } } }).ready( function() { schedule(); }); } )( jQuery );"; wp_add_inline_script( 'heartbeat', $script ); // Reset meta box data. $wp_meta_boxes = $_original_meta_boxes; } /** * Renders the hidden form required for the meta boxes form. * * @since 5.0.0 * * @param WP_Post $post Current post object. */ function the_block_editor_meta_box_post_form_hidden_fields( $post ) { $form_extra = ''; if ( 'auto-draft' === $post->post_status ) { $form_extra .= "<input type='hidden' id='auto_draft' name='auto_draft' value='1' />"; } $form_action = 'editpost'; $nonce_action = 'update-post_' . $post->ID; $form_extra .= "<input type='hidden' id='post_ID' name='post_ID' value='" . esc_attr( $post->ID ) . "' />"; $referer = wp_get_referer(); $current_user = wp_get_current_user(); $user_id = $current_user->ID; wp_nonce_field( $nonce_action ); /* * Some meta boxes hook into these actions to add hidden input fields in the classic post form. * For backward compatibility, we can capture the output from these actions, * and extract the hidden input fields. */ ob_start(); /** This filter is documented in wp-admin/edit-form-advanced.php */ do_action( 'edit_form_after_title', $post ); /** This filter is documented in wp-admin/edit-form-advanced.php */ do_action( 'edit_form_advanced', $post ); $classic_output = ob_get_clean(); $classic_elements = wp_html_split( $classic_output ); $hidden_inputs = ''; foreach ( $classic_elements as $element ) { if ( ! str_starts_with( $element, '<input ' ) ) { continue; } if ( preg_match( '/\stype=[\'"]hidden[\'"]\s/', $element ) ) { echo $element; } } ?> <input type="hidden" id="user-id" name="user_ID" value="<?php echo (int) $user_id; ?>" /> <input type="hidden" id="hiddenaction" name="action" value="<?php echo esc_attr( $form_action ); ?>" /> <input type="hidden" id="originalaction" name="originalaction" value="<?php echo esc_attr( $form_action ); ?>" /> <input type="hidden" id="post_type" name="post_type" value="<?php echo esc_attr( $post->post_type ); ?>" /> <input type="hidden" id="original_post_status" name="original_post_status" value="<?php echo esc_attr( $post->post_status ); ?>" /> <input type="hidden" id="referredby" name="referredby" value="<?php echo $referer ? esc_url( $referer ) : ''; ?>" /> <?php if ( 'draft' !== get_post_status( $post ) ) { wp_original_referer_field( true, 'previous' ); } echo $form_extra; wp_nonce_field( 'meta-box-order', 'meta-box-order-nonce', false ); wp_nonce_field( 'closedpostboxes', 'closedpostboxesnonce', false ); // Permalink title nonce. wp_nonce_field( 'samplepermalink', 'samplepermalinknonce', false ); /** * Adds hidden input fields to the meta box save form. * * Hook into this action to print `<input type="hidden" ... />` fields, which will be POSTed back to * the server when meta boxes are saved. * * @since 5.0.0 * * @param WP_Post $post The post that is being edited. */ do_action( 'block_editor_meta_box_hidden_fields', $post ); } /** * Disables block editor for wp_navigation type posts so they can be managed via the UI. * * @since 5.9.0 * @access private * * @param bool $value Whether the CPT supports block editor or not. * @param string $post_type Post type. * @return bool Whether the block editor should be disabled or not. */ function _disable_block_editor_for_navigation_post_type( $value, $post_type ) { if ( 'wp_navigation' === $post_type ) { return false; } return $value; } /** * This callback disables the content editor for wp_navigation type posts. * Content editor cannot handle wp_navigation type posts correctly. * We cannot disable the "editor" feature in the wp_navigation's CPT definition * because it disables the ability to save navigation blocks via REST API. * * @since 5.9.0 * @access private * * @param WP_Post $post An instance of WP_Post class. */ function _disable_content_editor_for_navigation_post_type( $post ) { $post_type = get_post_type( $post ); if ( 'wp_navigation' !== $post_type ) { return; } remove_post_type_support( $post_type, 'editor' ); } /** * This callback enables content editor for wp_navigation type posts. * We need to enable it back because we disable it to hide * the content editor for wp_navigation type posts. * * @since 5.9.0 * @access private * * @see _disable_content_editor_for_navigation_post_type * * @param WP_Post $post An instance of WP_Post class. */ function _enable_content_editor_for_navigation_post_type( $post ) { $post_type = get_post_type( $post ); if ( 'wp_navigation' !== $post_type ) { return; } add_post_type_support( $post_type, 'editor' ); } includes/file.php 0000644 00000300122 15135536347 0010014 0 ustar 00 <?php /** * Filesystem API: Top-level functionality * * Functions for reading, writing, modifying, and deleting files on the file system. * Includes functionality for theme-specific files as well as operations for uploading, * archiving, and rendering output when necessary. * * @package WordPress * @subpackage Filesystem * @since 2.3.0 */ /** The descriptions for theme files. */ $wp_file_descriptions = array( 'functions.php' => __( 'Theme Functions' ), 'header.php' => __( 'Theme Header' ), 'footer.php' => __( 'Theme Footer' ), 'sidebar.php' => __( 'Sidebar' ), 'comments.php' => __( 'Comments' ), 'searchform.php' => __( 'Search Form' ), '404.php' => __( '404 Template' ), 'link.php' => __( 'Links Template' ), 'theme.json' => __( 'Theme Styles & Block Settings' ), // Archives. 'index.php' => __( 'Main Index Template' ), 'archive.php' => __( 'Archives' ), 'author.php' => __( 'Author Template' ), 'taxonomy.php' => __( 'Taxonomy Template' ), 'category.php' => __( 'Category Template' ), 'tag.php' => __( 'Tag Template' ), 'home.php' => __( 'Posts Page' ), 'search.php' => __( 'Search Results' ), 'date.php' => __( 'Date Template' ), // Content. 'singular.php' => __( 'Singular Template' ), 'single.php' => __( 'Single Post' ), 'page.php' => __( 'Single Page' ), 'front-page.php' => __( 'Homepage' ), 'privacy-policy.php' => __( 'Privacy Policy Page' ), // Attachments. 'attachment.php' => __( 'Attachment Template' ), 'image.php' => __( 'Image Attachment Template' ), 'video.php' => __( 'Video Attachment Template' ), 'audio.php' => __( 'Audio Attachment Template' ), 'application.php' => __( 'Application Attachment Template' ), // Embeds. 'embed.php' => __( 'Embed Template' ), 'embed-404.php' => __( 'Embed 404 Template' ), 'embed-content.php' => __( 'Embed Content Template' ), 'header-embed.php' => __( 'Embed Header Template' ), 'footer-embed.php' => __( 'Embed Footer Template' ), // Stylesheets. 'style.css' => __( 'Stylesheet' ), 'editor-style.css' => __( 'Visual Editor Stylesheet' ), 'editor-style-rtl.css' => __( 'Visual Editor RTL Stylesheet' ), 'rtl.css' => __( 'RTL Stylesheet' ), // Other. 'my-hacks.php' => __( 'my-hacks.php (legacy hacks support)' ), '.htaccess' => __( '.htaccess (for rewrite rules )' ), // Deprecated files. 'wp-layout.css' => __( 'Stylesheet' ), 'wp-comments.php' => __( 'Comments Template' ), 'wp-comments-popup.php' => __( 'Popup Comments Template' ), 'comments-popup.php' => __( 'Popup Comments' ), ); /** * Gets the description for standard WordPress theme files. * * @since 1.5.0 * * @global array $wp_file_descriptions Theme file descriptions. * @global array $allowed_files List of allowed files. * * @param string $file Filesystem path or filename. * @return string Description of file from $wp_file_descriptions or basename of $file if description doesn't exist. * Appends 'Page Template' to basename of $file if the file is a page template. */ function get_file_description( $file ) { global $wp_file_descriptions, $allowed_files; $dirname = pathinfo( $file, PATHINFO_DIRNAME ); $file_path = $allowed_files[ $file ]; if ( isset( $wp_file_descriptions[ basename( $file ) ] ) && '.' === $dirname ) { return $wp_file_descriptions[ basename( $file ) ]; } elseif ( file_exists( $file_path ) && is_file( $file_path ) ) { $template_data = implode( '', file( $file_path ) ); if ( preg_match( '|Template Name:(.*)$|mi', $template_data, $name ) ) { /* translators: %s: Template name. */ return sprintf( __( '%s Page Template' ), _cleanup_header_comment( $name[1] ) ); } } return trim( basename( $file ) ); } /** * Gets the absolute filesystem path to the root of the WordPress installation. * * @since 1.5.0 * * @return string Full filesystem path to the root of the WordPress installation. */ function get_home_path() { $home = set_url_scheme( get_option( 'home' ), 'http' ); $siteurl = set_url_scheme( get_option( 'siteurl' ), 'http' ); if ( ! empty( $home ) && 0 !== strcasecmp( $home, $siteurl ) ) { $wp_path_rel_to_home = str_ireplace( $home, '', $siteurl ); /* $siteurl - $home */ $pos = strripos( str_replace( '\\', '/', $_SERVER['SCRIPT_FILENAME'] ), trailingslashit( $wp_path_rel_to_home ) ); $home_path = substr( $_SERVER['SCRIPT_FILENAME'], 0, $pos ); $home_path = trailingslashit( $home_path ); } else { $home_path = ABSPATH; } return str_replace( '\\', '/', $home_path ); } /** * Returns a listing of all files in the specified folder and all subdirectories up to 100 levels deep. * * The depth of the recursiveness can be controlled by the $levels param. * * @since 2.6.0 * @since 4.9.0 Added the `$exclusions` parameter. * @since 6.3.0 Added the `$include_hidden` parameter. * * @param string $folder Optional. Full path to folder. Default empty. * @param int $levels Optional. Levels of folders to follow, Default 100 (PHP Loop limit). * @param string[] $exclusions Optional. List of folders and files to skip. * @param bool $include_hidden Optional. Whether to include details of hidden ("." prefixed) files. * Default false. * @return string[]|false Array of files on success, false on failure. */ function list_files( $folder = '', $levels = 100, $exclusions = array(), $include_hidden = false ) { if ( empty( $folder ) ) { return false; } $folder = trailingslashit( $folder ); if ( ! $levels ) { return false; } $files = array(); $dir = @opendir( $folder ); if ( $dir ) { while ( ( $file = readdir( $dir ) ) !== false ) { // Skip current and parent folder links. if ( in_array( $file, array( '.', '..' ), true ) ) { continue; } // Skip hidden and excluded files. if ( ( ! $include_hidden && '.' === $file[0] ) || in_array( $file, $exclusions, true ) ) { continue; } if ( is_dir( $folder . $file ) ) { $files2 = list_files( $folder . $file, $levels - 1, array(), $include_hidden ); if ( $files2 ) { $files = array_merge( $files, $files2 ); } else { $files[] = $folder . $file . '/'; } } else { $files[] = $folder . $file; } } closedir( $dir ); } return $files; } /** * Gets the list of file extensions that are editable in plugins. * * @since 4.9.0 * * @param string $plugin Path to the plugin file relative to the plugins directory. * @return string[] Array of editable file extensions. */ function wp_get_plugin_file_editable_extensions( $plugin ) { $default_types = array( 'bash', 'conf', 'css', 'diff', 'htm', 'html', 'http', 'inc', 'include', 'js', 'json', 'jsx', 'less', 'md', 'patch', 'php', 'php3', 'php4', 'php5', 'php7', 'phps', 'phtml', 'sass', 'scss', 'sh', 'sql', 'svg', 'text', 'txt', 'xml', 'yaml', 'yml', ); /** * Filters the list of file types allowed for editing in the plugin file editor. * * @since 2.8.0 * @since 4.9.0 Added the `$plugin` parameter. * * @param string[] $default_types An array of editable plugin file extensions. * @param string $plugin Path to the plugin file relative to the plugins directory. */ $file_types = (array) apply_filters( 'editable_extensions', $default_types, $plugin ); return $file_types; } /** * Gets the list of file extensions that are editable for a given theme. * * @since 4.9.0 * * @param WP_Theme $theme Theme object. * @return string[] Array of editable file extensions. */ function wp_get_theme_file_editable_extensions( $theme ) { $default_types = array( 'bash', 'conf', 'css', 'diff', 'htm', 'html', 'http', 'inc', 'include', 'js', 'json', 'jsx', 'less', 'md', 'patch', 'php', 'php3', 'php4', 'php5', 'php7', 'phps', 'phtml', 'sass', 'scss', 'sh', 'sql', 'svg', 'text', 'txt', 'xml', 'yaml', 'yml', ); /** * Filters the list of file types allowed for editing in the theme file editor. * * @since 4.4.0 * * @param string[] $default_types An array of editable theme file extensions. * @param WP_Theme $theme The active theme object. */ $file_types = apply_filters( 'wp_theme_editor_filetypes', $default_types, $theme ); // Ensure that default types are still there. return array_unique( array_merge( $file_types, $default_types ) ); } /** * Prints file editor templates (for plugins and themes). * * @since 4.9.0 */ function wp_print_file_editor_templates() { ?> <script type="text/html" id="tmpl-wp-file-editor-notice"> <div class="notice inline notice-{{ data.type || 'info' }} {{ data.alt ? 'notice-alt' : '' }} {{ data.dismissible ? 'is-dismissible' : '' }} {{ data.classes || '' }}"> <# if ( 'php_error' === data.code ) { #> <p> <?php printf( /* translators: 1: Line number, 2: File path. */ __( 'Your PHP code changes were not applied due to an error on line %1$s of file %2$s. Please fix and try saving again.' ), '{{ data.line }}', '{{ data.file }}' ); ?> </p> <pre>{{ data.message }}</pre> <# } else if ( 'file_not_writable' === data.code ) { #> <p> <?php printf( /* translators: %s: Documentation URL. */ __( 'You need to make this file writable before you can save your changes. See <a href="%s">Changing File Permissions</a> for more information.' ), __( 'https://wordpress.org/documentation/article/changing-file-permissions/' ) ); ?> </p> <# } else { #> <p>{{ data.message || data.code }}</p> <# if ( 'lint_errors' === data.code ) { #> <p> <# var elementId = 'el-' + String( Math.random() ); #> <input id="{{ elementId }}" type="checkbox"> <label for="{{ elementId }}"><?php _e( 'Update anyway, even though it might break your site?' ); ?></label> </p> <# } #> <# } #> <# if ( data.dismissible ) { #> <button type="button" class="notice-dismiss"><span class="screen-reader-text"> <?php /* translators: Hidden accessibility text. */ _e( 'Dismiss' ); ?> </span></button> <# } #> </div> </script> <?php } /** * Attempts to edit a file for a theme or plugin. * * When editing a PHP file, loopback requests will be made to the admin and the homepage * to attempt to see if there is a fatal error introduced. If so, the PHP change will be * reverted. * * @since 4.9.0 * * @param string[] $args { * Args. Note that all of the arg values are already unslashed. They are, however, * coming straight from `$_POST` and are not validated or sanitized in any way. * * @type string $file Relative path to file. * @type string $plugin Path to the plugin file relative to the plugins directory. * @type string $theme Theme being edited. * @type string $newcontent New content for the file. * @type string $nonce Nonce. * } * @return true|WP_Error True on success or `WP_Error` on failure. */ function wp_edit_theme_plugin_file( $args ) { if ( empty( $args['file'] ) ) { return new WP_Error( 'missing_file' ); } if ( 0 !== validate_file( $args['file'] ) ) { return new WP_Error( 'bad_file' ); } if ( ! isset( $args['newcontent'] ) ) { return new WP_Error( 'missing_content' ); } if ( ! isset( $args['nonce'] ) ) { return new WP_Error( 'missing_nonce' ); } $file = $args['file']; $content = $args['newcontent']; $plugin = null; $theme = null; $real_file = null; if ( ! empty( $args['plugin'] ) ) { $plugin = $args['plugin']; if ( ! current_user_can( 'edit_plugins' ) ) { return new WP_Error( 'unauthorized', __( 'Sorry, you are not allowed to edit plugins for this site.' ) ); } if ( ! wp_verify_nonce( $args['nonce'], 'edit-plugin_' . $file ) ) { return new WP_Error( 'nonce_failure' ); } if ( ! array_key_exists( $plugin, get_plugins() ) ) { return new WP_Error( 'invalid_plugin' ); } if ( 0 !== validate_file( $file, get_plugin_files( $plugin ) ) ) { return new WP_Error( 'bad_plugin_file_path', __( 'Sorry, that file cannot be edited.' ) ); } $editable_extensions = wp_get_plugin_file_editable_extensions( $plugin ); $real_file = WP_PLUGIN_DIR . '/' . $file; $is_active = in_array( $plugin, (array) get_option( 'active_plugins', array() ), true ); } elseif ( ! empty( $args['theme'] ) ) { $stylesheet = $args['theme']; if ( 0 !== validate_file( $stylesheet ) ) { return new WP_Error( 'bad_theme_path' ); } if ( ! current_user_can( 'edit_themes' ) ) { return new WP_Error( 'unauthorized', __( 'Sorry, you are not allowed to edit templates for this site.' ) ); } $theme = wp_get_theme( $stylesheet ); if ( ! $theme->exists() ) { return new WP_Error( 'non_existent_theme', __( 'The requested theme does not exist.' ) ); } if ( ! wp_verify_nonce( $args['nonce'], 'edit-theme_' . $stylesheet . '_' . $file ) ) { return new WP_Error( 'nonce_failure' ); } if ( $theme->errors() && 'theme_no_stylesheet' === $theme->errors()->get_error_code() ) { return new WP_Error( 'theme_no_stylesheet', __( 'The requested theme does not exist.' ) . ' ' . $theme->errors()->get_error_message() ); } $editable_extensions = wp_get_theme_file_editable_extensions( $theme ); $allowed_files = array(); foreach ( $editable_extensions as $type ) { switch ( $type ) { case 'php': $allowed_files = array_merge( $allowed_files, $theme->get_files( 'php', -1 ) ); break; case 'css': $style_files = $theme->get_files( 'css', -1 ); $allowed_files['style.css'] = $style_files['style.css']; $allowed_files = array_merge( $allowed_files, $style_files ); break; default: $allowed_files = array_merge( $allowed_files, $theme->get_files( $type, -1 ) ); break; } } // Compare based on relative paths. if ( 0 !== validate_file( $file, array_keys( $allowed_files ) ) ) { return new WP_Error( 'disallowed_theme_file', __( 'Sorry, that file cannot be edited.' ) ); } $real_file = $theme->get_stylesheet_directory() . '/' . $file; $is_active = ( get_stylesheet() === $stylesheet || get_template() === $stylesheet ); } else { return new WP_Error( 'missing_theme_or_plugin' ); } // Ensure file is real. if ( ! is_file( $real_file ) ) { return new WP_Error( 'file_does_not_exist', __( 'File does not exist! Please double check the name and try again.' ) ); } // Ensure file extension is allowed. $extension = null; if ( preg_match( '/\.([^.]+)$/', $real_file, $matches ) ) { $extension = strtolower( $matches[1] ); if ( ! in_array( $extension, $editable_extensions, true ) ) { return new WP_Error( 'illegal_file_type', __( 'Files of this type are not editable.' ) ); } } $previous_content = file_get_contents( $real_file ); if ( ! is_writable( $real_file ) ) { return new WP_Error( 'file_not_writable' ); } $f = fopen( $real_file, 'w+' ); if ( false === $f ) { return new WP_Error( 'file_not_writable' ); } $written = fwrite( $f, $content ); fclose( $f ); if ( false === $written ) { return new WP_Error( 'unable_to_write', __( 'Unable to write to file.' ) ); } wp_opcache_invalidate( $real_file, true ); if ( $is_active && 'php' === $extension ) { $scrape_key = md5( rand() ); $transient = 'scrape_key_' . $scrape_key; $scrape_nonce = (string) rand(); // It shouldn't take more than 60 seconds to make the two loopback requests. set_transient( $transient, $scrape_nonce, 60 ); $cookies = wp_unslash( $_COOKIE ); $scrape_params = array( 'wp_scrape_key' => $scrape_key, 'wp_scrape_nonce' => $scrape_nonce, ); $headers = array( 'Cache-Control' => 'no-cache', ); /** This filter is documented in wp-includes/class-wp-http-streams.php */ $sslverify = apply_filters( 'https_local_ssl_verify', false ); // Include Basic auth in loopback requests. if ( isset( $_SERVER['PHP_AUTH_USER'] ) && isset( $_SERVER['PHP_AUTH_PW'] ) ) { $headers['Authorization'] = 'Basic ' . base64_encode( wp_unslash( $_SERVER['PHP_AUTH_USER'] ) . ':' . wp_unslash( $_SERVER['PHP_AUTH_PW'] ) ); } // Make sure PHP process doesn't die before loopback requests complete. if ( function_exists( 'set_time_limit' ) ) { set_time_limit( 5 * MINUTE_IN_SECONDS ); } // Time to wait for loopback requests to finish. $timeout = 100; // 100 seconds. $needle_start = "###### wp_scraping_result_start:$scrape_key ######"; $needle_end = "###### wp_scraping_result_end:$scrape_key ######"; // Attempt loopback request to editor to see if user just whitescreened themselves. if ( $plugin ) { $url = add_query_arg( compact( 'plugin', 'file' ), admin_url( 'plugin-editor.php' ) ); } elseif ( isset( $stylesheet ) ) { $url = add_query_arg( array( 'theme' => $stylesheet, 'file' => $file, ), admin_url( 'theme-editor.php' ) ); } else { $url = admin_url(); } if ( function_exists( 'session_status' ) && PHP_SESSION_ACTIVE === session_status() ) { /* * Close any active session to prevent HTTP requests from timing out * when attempting to connect back to the site. */ session_write_close(); } $url = add_query_arg( $scrape_params, $url ); $r = wp_remote_get( $url, compact( 'cookies', 'headers', 'timeout', 'sslverify' ) ); $body = wp_remote_retrieve_body( $r ); $scrape_result_position = strpos( $body, $needle_start ); $loopback_request_failure = array( 'code' => 'loopback_request_failed', 'message' => __( 'Unable to communicate back with site to check for fatal errors, so the PHP change was reverted. You will need to upload your PHP file change by some other means, such as by using SFTP.' ), ); $json_parse_failure = array( 'code' => 'json_parse_error', ); $result = null; if ( false === $scrape_result_position ) { $result = $loopback_request_failure; } else { $error_output = substr( $body, $scrape_result_position + strlen( $needle_start ) ); $error_output = substr( $error_output, 0, strpos( $error_output, $needle_end ) ); $result = json_decode( trim( $error_output ), true ); if ( empty( $result ) ) { $result = $json_parse_failure; } } // Try making request to homepage as well to see if visitors have been whitescreened. if ( true === $result ) { $url = home_url( '/' ); $url = add_query_arg( $scrape_params, $url ); $r = wp_remote_get( $url, compact( 'cookies', 'headers', 'timeout', 'sslverify' ) ); $body = wp_remote_retrieve_body( $r ); $scrape_result_position = strpos( $body, $needle_start ); if ( false === $scrape_result_position ) { $result = $loopback_request_failure; } else { $error_output = substr( $body, $scrape_result_position + strlen( $needle_start ) ); $error_output = substr( $error_output, 0, strpos( $error_output, $needle_end ) ); $result = json_decode( trim( $error_output ), true ); if ( empty( $result ) ) { $result = $json_parse_failure; } } } delete_transient( $transient ); if ( true !== $result ) { // Roll-back file change. file_put_contents( $real_file, $previous_content ); wp_opcache_invalidate( $real_file, true ); if ( ! isset( $result['message'] ) ) { $message = __( 'Something went wrong.' ); } else { $message = $result['message']; unset( $result['message'] ); } return new WP_Error( 'php_error', $message, $result ); } } if ( $theme instanceof WP_Theme ) { $theme->cache_delete(); } return true; } /** * Returns a filename of a temporary unique file. * * Please note that the calling function must delete or move the file. * * The filename is based off the passed parameter or defaults to the current unix timestamp, * while the directory can either be passed as well, or by leaving it blank, default to a writable * temporary directory. * * @since 2.6.0 * * @param string $filename Optional. Filename to base the Unique file off. Default empty. * @param string $dir Optional. Directory to store the file in. Default empty. * @return string A writable filename. */ function wp_tempnam( $filename = '', $dir = '' ) { if ( empty( $dir ) ) { $dir = get_temp_dir(); } if ( empty( $filename ) || in_array( $filename, array( '.', '/', '\\' ), true ) ) { $filename = uniqid(); } // Use the basename of the given file without the extension as the name for the temporary directory. $temp_filename = basename( $filename ); $temp_filename = preg_replace( '|\.[^.]*$|', '', $temp_filename ); // If the folder is falsey, use its parent directory name instead. if ( ! $temp_filename ) { return wp_tempnam( dirname( $filename ), $dir ); } // Suffix some random data to avoid filename conflicts. $temp_filename .= '-' . wp_generate_password( 6, false ); $temp_filename .= '.tmp'; $temp_filename = wp_unique_filename( $dir, $temp_filename ); /* * Filesystems typically have a limit of 255 characters for a filename. * * If the generated unique filename exceeds this, truncate the initial * filename and try again. * * As it's possible that the truncated filename may exist, producing a * suffix of "-1" or "-10" which could exceed the limit again, truncate * it to 252 instead. */ $characters_over_limit = strlen( $temp_filename ) - 252; if ( $characters_over_limit > 0 ) { $filename = substr( $filename, 0, -$characters_over_limit ); return wp_tempnam( $filename, $dir ); } $temp_filename = $dir . $temp_filename; $fp = @fopen( $temp_filename, 'x' ); if ( ! $fp && is_writable( $dir ) && file_exists( $temp_filename ) ) { return wp_tempnam( $filename, $dir ); } if ( $fp ) { fclose( $fp ); } return $temp_filename; } /** * Makes sure that the file that was requested to be edited is allowed to be edited. * * Function will die if you are not allowed to edit the file. * * @since 1.5.0 * * @param string $file File the user is attempting to edit. * @param string[] $allowed_files Optional. Array of allowed files to edit. * `$file` must match an entry exactly. * @return string|void Returns the file name on success, dies on failure. */ function validate_file_to_edit( $file, $allowed_files = array() ) { $code = validate_file( $file, $allowed_files ); if ( ! $code ) { return $file; } switch ( $code ) { case 1: wp_die( __( 'Sorry, that file cannot be edited.' ) ); // case 2 : // wp_die( __('Sorry, cannot call files with their real path.' )); case 3: wp_die( __( 'Sorry, that file cannot be edited.' ) ); } } /** * Handles PHP uploads in WordPress. * * Sanitizes file names, checks extensions for mime type, and moves the file * to the appropriate directory within the uploads directory. * * @access private * @since 4.0.0 * * @see wp_handle_upload_error * * @param array $file { * Reference to a single element from `$_FILES`. Call the function once for each uploaded file. * * @type string $name The original name of the file on the client machine. * @type string $type The mime type of the file, if the browser provided this information. * @type string $tmp_name The temporary filename of the file in which the uploaded file was stored on the server. * @type int $size The size, in bytes, of the uploaded file. * @type int $error The error code associated with this file upload. * } * @param array|false $overrides { * An array of override parameters for this file, or boolean false if none are provided. * * @type callable $upload_error_handler Function to call when there is an error during the upload process. * See {@see wp_handle_upload_error()}. * @type callable $unique_filename_callback Function to call when determining a unique file name for the file. * See {@see wp_unique_filename()}. * @type string[] $upload_error_strings The strings that describe the error indicated in * `$_FILES[{form field}]['error']`. * @type bool $test_form Whether to test that the `$_POST['action']` parameter is as expected. * @type bool $test_size Whether to test that the file size is greater than zero bytes. * @type bool $test_type Whether to test that the mime type of the file is as expected. * @type string[] $mimes Array of allowed mime types keyed by their file extension regex. * } * @param string $time Time formatted in 'yyyy/mm'. * @param string $action Expected value for `$_POST['action']`. * @return array { * On success, returns an associative array of file attributes. * On failure, returns `$overrides['upload_error_handler']( &$file, $message )` * or `array( 'error' => $message )`. * * @type string $file Filename of the newly-uploaded file. * @type string $url URL of the newly-uploaded file. * @type string $type Mime type of the newly-uploaded file. * } */ function _wp_handle_upload( &$file, $overrides, $time, $action ) { // The default error handler. if ( ! function_exists( 'wp_handle_upload_error' ) ) { function wp_handle_upload_error( &$file, $message ) { return array( 'error' => $message ); } } /** * Filters the data for a file before it is uploaded to WordPress. * * The dynamic portion of the hook name, `$action`, refers to the post action. * * Possible hook names include: * * - `wp_handle_sideload_prefilter` * - `wp_handle_upload_prefilter` * * @since 2.9.0 as 'wp_handle_upload_prefilter'. * @since 4.0.0 Converted to a dynamic hook with `$action`. * * @param array $file { * Reference to a single element from `$_FILES`. * * @type string $name The original name of the file on the client machine. * @type string $type The mime type of the file, if the browser provided this information. * @type string $tmp_name The temporary filename of the file in which the uploaded file was stored on the server. * @type int $size The size, in bytes, of the uploaded file. * @type int $error The error code associated with this file upload. * } */ $file = apply_filters( "{$action}_prefilter", $file ); /** * Filters the override parameters for a file before it is uploaded to WordPress. * * The dynamic portion of the hook name, `$action`, refers to the post action. * * Possible hook names include: * * - `wp_handle_sideload_overrides` * - `wp_handle_upload_overrides` * * @since 5.7.0 * * @param array|false $overrides An array of override parameters for this file. Boolean false if none are * provided. See {@see _wp_handle_upload()}. * @param array $file { * Reference to a single element from `$_FILES`. * * @type string $name The original name of the file on the client machine. * @type string $type The mime type of the file, if the browser provided this information. * @type string $tmp_name The temporary filename of the file in which the uploaded file was stored on the server. * @type int $size The size, in bytes, of the uploaded file. * @type int $error The error code associated with this file upload. * } */ $overrides = apply_filters( "{$action}_overrides", $overrides, $file ); // You may define your own function and pass the name in $overrides['upload_error_handler']. $upload_error_handler = 'wp_handle_upload_error'; if ( isset( $overrides['upload_error_handler'] ) ) { $upload_error_handler = $overrides['upload_error_handler']; } // You may have had one or more 'wp_handle_upload_prefilter' functions error out the file. Handle that gracefully. if ( isset( $file['error'] ) && ! is_numeric( $file['error'] ) && $file['error'] ) { return call_user_func_array( $upload_error_handler, array( &$file, $file['error'] ) ); } // Install user overrides. Did we mention that this voids your warranty? // You may define your own function and pass the name in $overrides['unique_filename_callback']. $unique_filename_callback = null; if ( isset( $overrides['unique_filename_callback'] ) ) { $unique_filename_callback = $overrides['unique_filename_callback']; } /* * This may not have originally been intended to be overridable, * but historically has been. */ if ( isset( $overrides['upload_error_strings'] ) ) { $upload_error_strings = $overrides['upload_error_strings']; } else { // Courtesy of php.net, the strings that describe the error indicated in $_FILES[{form field}]['error']. $upload_error_strings = array( false, sprintf( /* translators: 1: upload_max_filesize, 2: php.ini */ __( 'The uploaded file exceeds the %1$s directive in %2$s.' ), 'upload_max_filesize', 'php.ini' ), sprintf( /* translators: %s: MAX_FILE_SIZE */ __( 'The uploaded file exceeds the %s directive that was specified in the HTML form.' ), 'MAX_FILE_SIZE' ), __( 'The uploaded file was only partially uploaded.' ), __( 'No file was uploaded.' ), '', __( 'Missing a temporary folder.' ), __( 'Failed to write file to disk.' ), __( 'File upload stopped by extension.' ), ); } // All tests are on by default. Most can be turned off by $overrides[{test_name}] = false; $test_form = isset( $overrides['test_form'] ) ? $overrides['test_form'] : true; $test_size = isset( $overrides['test_size'] ) ? $overrides['test_size'] : true; // If you override this, you must provide $ext and $type!! $test_type = isset( $overrides['test_type'] ) ? $overrides['test_type'] : true; $mimes = isset( $overrides['mimes'] ) ? $overrides['mimes'] : null; // A correct form post will pass this test. if ( $test_form && ( ! isset( $_POST['action'] ) || $_POST['action'] !== $action ) ) { return call_user_func_array( $upload_error_handler, array( &$file, __( 'Invalid form submission.' ) ) ); } // A successful upload will pass this test. It makes no sense to override this one. if ( isset( $file['error'] ) && $file['error'] > 0 ) { return call_user_func_array( $upload_error_handler, array( &$file, $upload_error_strings[ $file['error'] ] ) ); } // A properly uploaded file will pass this test. There should be no reason to override this one. $test_uploaded_file = 'wp_handle_upload' === $action ? is_uploaded_file( $file['tmp_name'] ) : @is_readable( $file['tmp_name'] ); if ( ! $test_uploaded_file ) { return call_user_func_array( $upload_error_handler, array( &$file, __( 'Specified file failed upload test.' ) ) ); } $test_file_size = 'wp_handle_upload' === $action ? $file['size'] : filesize( $file['tmp_name'] ); // A non-empty file will pass this test. if ( $test_size && ! ( $test_file_size > 0 ) ) { if ( is_multisite() ) { $error_msg = __( 'File is empty. Please upload something more substantial.' ); } else { $error_msg = sprintf( /* translators: 1: php.ini, 2: post_max_size, 3: upload_max_filesize */ __( 'File is empty. Please upload something more substantial. This error could also be caused by uploads being disabled in your %1$s file or by %2$s being defined as smaller than %3$s in %1$s.' ), 'php.ini', 'post_max_size', 'upload_max_filesize' ); } return call_user_func_array( $upload_error_handler, array( &$file, $error_msg ) ); } // A correct MIME type will pass this test. Override $mimes or use the upload_mimes filter. if ( $test_type ) { $wp_filetype = wp_check_filetype_and_ext( $file['tmp_name'], $file['name'], $mimes ); $ext = empty( $wp_filetype['ext'] ) ? '' : $wp_filetype['ext']; $type = empty( $wp_filetype['type'] ) ? '' : $wp_filetype['type']; $proper_filename = empty( $wp_filetype['proper_filename'] ) ? '' : $wp_filetype['proper_filename']; // Check to see if wp_check_filetype_and_ext() determined the filename was incorrect. if ( $proper_filename ) { $file['name'] = $proper_filename; } if ( ( ! $type || ! $ext ) && ! current_user_can( 'unfiltered_upload' ) ) { return call_user_func_array( $upload_error_handler, array( &$file, __( 'Sorry, you are not allowed to upload this file type.' ) ) ); } if ( ! $type ) { $type = $file['type']; } } else { $type = ''; } /* * A writable uploads dir will pass this test. Again, there's no point * overriding this one. */ $uploads = wp_upload_dir( $time ); if ( ! ( $uploads && false === $uploads['error'] ) ) { return call_user_func_array( $upload_error_handler, array( &$file, $uploads['error'] ) ); } $filename = wp_unique_filename( $uploads['path'], $file['name'], $unique_filename_callback ); // Move the file to the uploads dir. $new_file = $uploads['path'] . "/$filename"; /** * Filters whether to short-circuit moving the uploaded file after passing all checks. * * If a non-null value is returned from the filter, moving the file and any related * error reporting will be completely skipped. * * @since 4.9.0 * * @param mixed $move_new_file If null (default) move the file after the upload. * @param array $file { * Reference to a single element from `$_FILES`. * * @type string $name The original name of the file on the client machine. * @type string $type The mime type of the file, if the browser provided this information. * @type string $tmp_name The temporary filename of the file in which the uploaded file was stored on the server. * @type int $size The size, in bytes, of the uploaded file. * @type int $error The error code associated with this file upload. * } * @param string $new_file Filename of the newly-uploaded file. * @param string $type Mime type of the newly-uploaded file. */ $move_new_file = apply_filters( 'pre_move_uploaded_file', null, $file, $new_file, $type ); if ( null === $move_new_file ) { if ( 'wp_handle_upload' === $action ) { $move_new_file = @move_uploaded_file( $file['tmp_name'], $new_file ); } else { // Use copy and unlink because rename breaks streams. // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged $move_new_file = @copy( $file['tmp_name'], $new_file ); unlink( $file['tmp_name'] ); } if ( false === $move_new_file ) { if ( str_starts_with( $uploads['basedir'], ABSPATH ) ) { $error_path = str_replace( ABSPATH, '', $uploads['basedir'] ) . $uploads['subdir']; } else { $error_path = basename( $uploads['basedir'] ) . $uploads['subdir']; } return $upload_error_handler( $file, sprintf( /* translators: %s: Destination file path. */ __( 'The uploaded file could not be moved to %s.' ), $error_path ) ); } } // Set correct file permissions. $stat = stat( dirname( $new_file ) ); $perms = $stat['mode'] & 0000666; chmod( $new_file, $perms ); // Compute the URL. $url = $uploads['url'] . "/$filename"; if ( is_multisite() ) { clean_dirsize_cache( $new_file ); } /** * Filters the data array for the uploaded file. * * @since 2.1.0 * * @param array $upload { * Array of upload data. * * @type string $file Filename of the newly-uploaded file. * @type string $url URL of the newly-uploaded file. * @type string $type Mime type of the newly-uploaded file. * } * @param string $context The type of upload action. Values include 'upload' or 'sideload'. */ return apply_filters( 'wp_handle_upload', array( 'file' => $new_file, 'url' => $url, 'type' => $type, ), 'wp_handle_sideload' === $action ? 'sideload' : 'upload' ); } /** * Wrapper for _wp_handle_upload(). * * Passes the {@see 'wp_handle_upload'} action. * * @since 2.0.0 * * @see _wp_handle_upload() * * @param array $file Reference to a single element of `$_FILES`. * Call the function once for each uploaded file. * See _wp_handle_upload() for accepted values. * @param array|false $overrides Optional. An associative array of names => values * to override default variables. Default false. * See _wp_handle_upload() for accepted values. * @param string $time Optional. Time formatted in 'yyyy/mm'. Default null. * @return array See _wp_handle_upload() for return value. */ function wp_handle_upload( &$file, $overrides = false, $time = null ) { /* * $_POST['action'] must be set and its value must equal $overrides['action'] * or this: */ $action = 'wp_handle_upload'; if ( isset( $overrides['action'] ) ) { $action = $overrides['action']; } return _wp_handle_upload( $file, $overrides, $time, $action ); } /** * Wrapper for _wp_handle_upload(). * * Passes the {@see 'wp_handle_sideload'} action. * * @since 2.6.0 * * @see _wp_handle_upload() * * @param array $file Reference to a single element of `$_FILES`. * Call the function once for each uploaded file. * See _wp_handle_upload() for accepted values. * @param array|false $overrides Optional. An associative array of names => values * to override default variables. Default false. * See _wp_handle_upload() for accepted values. * @param string $time Optional. Time formatted in 'yyyy/mm'. Default null. * @return array See _wp_handle_upload() for return value. */ function wp_handle_sideload( &$file, $overrides = false, $time = null ) { /* * $_POST['action'] must be set and its value must equal $overrides['action'] * or this: */ $action = 'wp_handle_sideload'; if ( isset( $overrides['action'] ) ) { $action = $overrides['action']; } return _wp_handle_upload( $file, $overrides, $time, $action ); } /** * Downloads a URL to a local temporary file using the WordPress HTTP API. * * Please note that the calling function must delete or move the file. * * @since 2.5.0 * @since 5.2.0 Signature Verification with SoftFail was added. * @since 5.9.0 Support for Content-Disposition filename was added. * * @param string $url The URL of the file to download. * @param int $timeout The timeout for the request to download the file. * Default 300 seconds. * @param bool $signature_verification Whether to perform Signature Verification. * Default false. * @return string|WP_Error Filename on success, WP_Error on failure. */ function download_url( $url, $timeout = 300, $signature_verification = false ) { // WARNING: The file is not automatically deleted, the script must delete or move the file. if ( ! $url ) { return new WP_Error( 'http_no_url', __( 'Invalid URL Provided.' ) ); } $url_path = parse_url( $url, PHP_URL_PATH ); $url_filename = ''; if ( is_string( $url_path ) && '' !== $url_path ) { $url_filename = basename( $url_path ); } $tmpfname = wp_tempnam( $url_filename ); if ( ! $tmpfname ) { return new WP_Error( 'http_no_file', __( 'Could not create temporary file.' ) ); } $response = wp_safe_remote_get( $url, array( 'timeout' => $timeout, 'stream' => true, 'filename' => $tmpfname, ) ); if ( is_wp_error( $response ) ) { unlink( $tmpfname ); return $response; } $response_code = wp_remote_retrieve_response_code( $response ); if ( 200 !== $response_code ) { $data = array( 'code' => $response_code, ); // Retrieve a sample of the response body for debugging purposes. $tmpf = fopen( $tmpfname, 'rb' ); if ( $tmpf ) { /** * Filters the maximum error response body size in `download_url()`. * * @since 5.1.0 * * @see download_url() * * @param int $size The maximum error response body size. Default 1 KB. */ $response_size = apply_filters( 'download_url_error_max_body_size', KB_IN_BYTES ); $data['body'] = fread( $tmpf, $response_size ); fclose( $tmpf ); } unlink( $tmpfname ); return new WP_Error( 'http_404', trim( wp_remote_retrieve_response_message( $response ) ), $data ); } $content_disposition = wp_remote_retrieve_header( $response, 'Content-Disposition' ); if ( $content_disposition ) { $content_disposition = strtolower( $content_disposition ); if ( str_starts_with( $content_disposition, 'attachment; filename=' ) ) { $tmpfname_disposition = sanitize_file_name( substr( $content_disposition, 21 ) ); } else { $tmpfname_disposition = ''; } // Potential file name must be valid string. if ( $tmpfname_disposition && is_string( $tmpfname_disposition ) && ( 0 === validate_file( $tmpfname_disposition ) ) ) { $tmpfname_disposition = dirname( $tmpfname ) . '/' . $tmpfname_disposition; if ( rename( $tmpfname, $tmpfname_disposition ) ) { $tmpfname = $tmpfname_disposition; } if ( ( $tmpfname !== $tmpfname_disposition ) && file_exists( $tmpfname_disposition ) ) { unlink( $tmpfname_disposition ); } } } $content_md5 = wp_remote_retrieve_header( $response, 'Content-MD5' ); if ( $content_md5 ) { $md5_check = verify_file_md5( $tmpfname, $content_md5 ); if ( is_wp_error( $md5_check ) ) { unlink( $tmpfname ); return $md5_check; } } // If the caller expects signature verification to occur, check to see if this URL supports it. if ( $signature_verification ) { /** * Filters the list of hosts which should have Signature Verification attempted on. * * @since 5.2.0 * * @param string[] $hostnames List of hostnames. */ $signed_hostnames = apply_filters( 'wp_signature_hosts', array( 'wordpress.org', 'downloads.wordpress.org', 's.w.org' ) ); $signature_verification = in_array( parse_url( $url, PHP_URL_HOST ), $signed_hostnames, true ); } // Perform signature validation if supported. if ( $signature_verification ) { $signature = wp_remote_retrieve_header( $response, 'X-Content-Signature' ); if ( ! $signature ) { /* * Retrieve signatures from a file if the header wasn't included. * WordPress.org stores signatures at $package_url.sig. */ $signature_url = false; if ( is_string( $url_path ) && ( str_ends_with( $url_path, '.zip' ) || str_ends_with( $url_path, '.tar.gz' ) ) ) { $signature_url = str_replace( $url_path, $url_path . '.sig', $url ); } /** * Filters the URL where the signature for a file is located. * * @since 5.2.0 * * @param false|string $signature_url The URL where signatures can be found for a file, or false if none are known. * @param string $url The URL being verified. */ $signature_url = apply_filters( 'wp_signature_url', $signature_url, $url ); if ( $signature_url ) { $signature_request = wp_safe_remote_get( $signature_url, array( 'limit_response_size' => 10 * KB_IN_BYTES, // 10KB should be large enough for quite a few signatures. ) ); if ( ! is_wp_error( $signature_request ) && 200 === wp_remote_retrieve_response_code( $signature_request ) ) { $signature = explode( "\n", wp_remote_retrieve_body( $signature_request ) ); } } } // Perform the checks. $signature_verification = verify_file_signature( $tmpfname, $signature, $url_filename ); } if ( is_wp_error( $signature_verification ) ) { if ( /** * Filters whether Signature Verification failures should be allowed to soft fail. * * WARNING: This may be removed from a future release. * * @since 5.2.0 * * @param bool $signature_softfail If a softfail is allowed. * @param string $url The url being accessed. */ apply_filters( 'wp_signature_softfail', true, $url ) ) { $signature_verification->add_data( $tmpfname, 'softfail-filename' ); } else { // Hard-fail. unlink( $tmpfname ); } return $signature_verification; } return $tmpfname; } /** * Calculates and compares the MD5 of a file to its expected value. * * @since 3.7.0 * * @param string $filename The filename to check the MD5 of. * @param string $expected_md5 The expected MD5 of the file, either a base64-encoded raw md5, * or a hex-encoded md5. * @return bool|WP_Error True on success, false when the MD5 format is unknown/unexpected, * WP_Error on failure. */ function verify_file_md5( $filename, $expected_md5 ) { if ( 32 === strlen( $expected_md5 ) ) { $expected_raw_md5 = pack( 'H*', $expected_md5 ); } elseif ( 24 === strlen( $expected_md5 ) ) { $expected_raw_md5 = base64_decode( $expected_md5 ); } else { return false; // Unknown format. } $file_md5 = md5_file( $filename, true ); if ( $file_md5 === $expected_raw_md5 ) { return true; } return new WP_Error( 'md5_mismatch', sprintf( /* translators: 1: File checksum, 2: Expected checksum value. */ __( 'The checksum of the file (%1$s) does not match the expected checksum value (%2$s).' ), bin2hex( $file_md5 ), bin2hex( $expected_raw_md5 ) ) ); } /** * Verifies the contents of a file against its ED25519 signature. * * @since 5.2.0 * * @param string $filename The file to validate. * @param string|array $signatures A Signature provided for the file. * @param string|false $filename_for_errors Optional. A friendly filename for errors. * @return bool|WP_Error True on success, false if verification not attempted, * or WP_Error describing an error condition. */ function verify_file_signature( $filename, $signatures, $filename_for_errors = false ) { if ( ! $filename_for_errors ) { $filename_for_errors = wp_basename( $filename ); } // Check we can process signatures. if ( ! function_exists( 'sodium_crypto_sign_verify_detached' ) || ! in_array( 'sha384', array_map( 'strtolower', hash_algos() ), true ) ) { return new WP_Error( 'signature_verification_unsupported', sprintf( /* translators: %s: The filename of the package. */ __( 'The authenticity of %s could not be verified as signature verification is unavailable on this system.' ), '<span class="code">' . esc_html( $filename_for_errors ) . '</span>' ), ( ! function_exists( 'sodium_crypto_sign_verify_detached' ) ? 'sodium_crypto_sign_verify_detached' : 'sha384' ) ); } // Check for an edge-case affecting PHP Maths abilities. if ( ! extension_loaded( 'sodium' ) && in_array( PHP_VERSION_ID, array( 70200, 70201, 70202 ), true ) && extension_loaded( 'opcache' ) ) { /* * Sodium_Compat isn't compatible with PHP 7.2.0~7.2.2 due to a bug in the PHP Opcache extension, bail early as it'll fail. * https://bugs.php.net/bug.php?id=75938 */ return new WP_Error( 'signature_verification_unsupported', sprintf( /* translators: %s: The filename of the package. */ __( 'The authenticity of %s could not be verified as signature verification is unavailable on this system.' ), '<span class="code">' . esc_html( $filename_for_errors ) . '</span>' ), array( 'php' => PHP_VERSION, 'sodium' => defined( 'SODIUM_LIBRARY_VERSION' ) ? SODIUM_LIBRARY_VERSION : ( defined( 'ParagonIE_Sodium_Compat::VERSION_STRING' ) ? ParagonIE_Sodium_Compat::VERSION_STRING : false ), ) ); } // Verify runtime speed of Sodium_Compat is acceptable. if ( ! extension_loaded( 'sodium' ) && ! ParagonIE_Sodium_Compat::polyfill_is_fast() ) { $sodium_compat_is_fast = false; // Allow for an old version of Sodium_Compat being loaded before the bundled WordPress one. if ( method_exists( 'ParagonIE_Sodium_Compat', 'runtime_speed_test' ) ) { /* * Run `ParagonIE_Sodium_Compat::runtime_speed_test()` in optimized integer mode, * as that's what WordPress utilizes during signing verifications. */ // phpcs:disable WordPress.NamingConventions.ValidVariableName $old_fastMult = ParagonIE_Sodium_Compat::$fastMult; ParagonIE_Sodium_Compat::$fastMult = true; $sodium_compat_is_fast = ParagonIE_Sodium_Compat::runtime_speed_test( 100, 10 ); ParagonIE_Sodium_Compat::$fastMult = $old_fastMult; // phpcs:enable } /* * This cannot be performed in a reasonable amount of time. * https://github.com/paragonie/sodium_compat#help-sodium_compat-is-slow-how-can-i-make-it-fast */ if ( ! $sodium_compat_is_fast ) { return new WP_Error( 'signature_verification_unsupported', sprintf( /* translators: %s: The filename of the package. */ __( 'The authenticity of %s could not be verified as signature verification is unavailable on this system.' ), '<span class="code">' . esc_html( $filename_for_errors ) . '</span>' ), array( 'php' => PHP_VERSION, 'sodium' => defined( 'SODIUM_LIBRARY_VERSION' ) ? SODIUM_LIBRARY_VERSION : ( defined( 'ParagonIE_Sodium_Compat::VERSION_STRING' ) ? ParagonIE_Sodium_Compat::VERSION_STRING : false ), 'polyfill_is_fast' => false, 'max_execution_time' => ini_get( 'max_execution_time' ), ) ); } } if ( ! $signatures ) { return new WP_Error( 'signature_verification_no_signature', sprintf( /* translators: %s: The filename of the package. */ __( 'The authenticity of %s could not be verified as no signature was found.' ), '<span class="code">' . esc_html( $filename_for_errors ) . '</span>' ), array( 'filename' => $filename_for_errors, ) ); } $trusted_keys = wp_trusted_keys(); $file_hash = hash_file( 'sha384', $filename, true ); mbstring_binary_safe_encoding(); $skipped_key = 0; $skipped_signature = 0; foreach ( (array) $signatures as $signature ) { $signature_raw = base64_decode( $signature ); // Ensure only valid-length signatures are considered. if ( SODIUM_CRYPTO_SIGN_BYTES !== strlen( $signature_raw ) ) { ++$skipped_signature; continue; } foreach ( (array) $trusted_keys as $key ) { $key_raw = base64_decode( $key ); // Only pass valid public keys through. if ( SODIUM_CRYPTO_SIGN_PUBLICKEYBYTES !== strlen( $key_raw ) ) { ++$skipped_key; continue; } if ( sodium_crypto_sign_verify_detached( $signature_raw, $file_hash, $key_raw ) ) { reset_mbstring_encoding(); return true; } } } reset_mbstring_encoding(); return new WP_Error( 'signature_verification_failed', sprintf( /* translators: %s: The filename of the package. */ __( 'The authenticity of %s could not be verified.' ), '<span class="code">' . esc_html( $filename_for_errors ) . '</span>' ), // Error data helpful for debugging: array( 'filename' => $filename_for_errors, 'keys' => $trusted_keys, 'signatures' => $signatures, 'hash' => bin2hex( $file_hash ), 'skipped_key' => $skipped_key, 'skipped_sig' => $skipped_signature, 'php' => PHP_VERSION, 'sodium' => defined( 'SODIUM_LIBRARY_VERSION' ) ? SODIUM_LIBRARY_VERSION : ( defined( 'ParagonIE_Sodium_Compat::VERSION_STRING' ) ? ParagonIE_Sodium_Compat::VERSION_STRING : false ), ) ); } /** * Retrieves the list of signing keys trusted by WordPress. * * @since 5.2.0 * * @return string[] Array of base64-encoded signing keys. */ function wp_trusted_keys() { $trusted_keys = array(); if ( time() < 1617235200 ) { // WordPress.org Key #1 - This key is only valid before April 1st, 2021. $trusted_keys[] = 'fRPyrxb/MvVLbdsYi+OOEv4xc+Eqpsj+kkAS6gNOkI0='; } // TODO: Add key #2 with longer expiration. /** * Filters the valid signing keys used to verify the contents of files. * * @since 5.2.0 * * @param string[] $trusted_keys The trusted keys that may sign packages. */ return apply_filters( 'wp_trusted_keys', $trusted_keys ); } /** * Determines whether the given file is a valid ZIP file. * * This function does not test to ensure that a file exists. Non-existent files * are not valid ZIPs, so those will also return false. * * @since 6.4.4 * * @param string $file Full path to the ZIP file. * @return bool Whether the file is a valid ZIP file. */ function wp_zip_file_is_valid( $file ) { /** This filter is documented in wp-admin/includes/file.php */ if ( class_exists( 'ZipArchive', false ) && apply_filters( 'unzip_file_use_ziparchive', true ) ) { $archive = new ZipArchive(); $archive_is_valid = $archive->open( $file, ZipArchive::CHECKCONS ); if ( true === $archive_is_valid ) { $archive->close(); return true; } } // Fall through to PclZip if ZipArchive is not available, or encountered an error opening the file. require_once ABSPATH . 'wp-admin/includes/class-pclzip.php'; $archive = new PclZip( $file ); $archive_is_valid = is_array( $archive->properties() ); return $archive_is_valid; } /** * Unzips a specified ZIP file to a location on the filesystem via the WordPress * Filesystem Abstraction. * * Assumes that WP_Filesystem() has already been called and set up. Does not extract * a root-level __MACOSX directory, if present. * * Attempts to increase the PHP memory limit to 256M before uncompressing. However, * the most memory required shouldn't be much larger than the archive itself. * * @since 2.5.0 * * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass. * * @param string $file Full path and filename of ZIP archive. * @param string $to Full path on the filesystem to extract archive to. * @return true|WP_Error True on success, WP_Error on failure. */ function unzip_file( $file, $to ) { global $wp_filesystem; if ( ! $wp_filesystem || ! is_object( $wp_filesystem ) ) { return new WP_Error( 'fs_unavailable', __( 'Could not access filesystem.' ) ); } // Unzip can use a lot of memory, but not this much hopefully. wp_raise_memory_limit( 'admin' ); $needed_dirs = array(); $to = trailingslashit( $to ); // Determine any parent directories needed (of the upgrade directory). if ( ! $wp_filesystem->is_dir( $to ) ) { // Only do parents if no children exist. $path = preg_split( '![/\\\]!', untrailingslashit( $to ) ); for ( $i = count( $path ); $i >= 0; $i-- ) { if ( empty( $path[ $i ] ) ) { continue; } $dir = implode( '/', array_slice( $path, 0, $i + 1 ) ); if ( preg_match( '!^[a-z]:$!i', $dir ) ) { // Skip it if it looks like a Windows Drive letter. continue; } if ( ! $wp_filesystem->is_dir( $dir ) ) { $needed_dirs[] = $dir; } else { break; // A folder exists, therefore we don't need to check the levels below this. } } } /** * Filters whether to use ZipArchive to unzip archives. * * @since 3.0.0 * * @param bool $ziparchive Whether to use ZipArchive. Default true. */ if ( class_exists( 'ZipArchive', false ) && apply_filters( 'unzip_file_use_ziparchive', true ) ) { $result = _unzip_file_ziparchive( $file, $to, $needed_dirs ); if ( true === $result ) { return $result; } elseif ( is_wp_error( $result ) ) { if ( 'incompatible_archive' !== $result->get_error_code() ) { return $result; } } } // Fall through to PclZip if ZipArchive is not available, or encountered an error opening the file. return _unzip_file_pclzip( $file, $to, $needed_dirs ); } /** * Attempts to unzip an archive using the ZipArchive class. * * This function should not be called directly, use `unzip_file()` instead. * * Assumes that WP_Filesystem() has already been called and set up. * * @since 3.0.0 * @access private * * @see unzip_file() * * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass. * * @param string $file Full path and filename of ZIP archive. * @param string $to Full path on the filesystem to extract archive to. * @param string[] $needed_dirs A partial list of required folders needed to be created. * @return true|WP_Error True on success, WP_Error on failure. */ function _unzip_file_ziparchive( $file, $to, $needed_dirs = array() ) { global $wp_filesystem; $z = new ZipArchive(); $zopen = $z->open( $file, ZIPARCHIVE::CHECKCONS ); if ( true !== $zopen ) { return new WP_Error( 'incompatible_archive', __( 'Incompatible Archive.' ), array( 'ziparchive_error' => $zopen ) ); } $uncompressed_size = 0; for ( $i = 0; $i < $z->numFiles; $i++ ) { $info = $z->statIndex( $i ); if ( ! $info ) { $z->close(); return new WP_Error( 'stat_failed_ziparchive', __( 'Could not retrieve file from archive.' ) ); } if ( str_starts_with( $info['name'], '__MACOSX/' ) ) { // Skip the OS X-created __MACOSX directory. continue; } // Don't extract invalid files: if ( 0 !== validate_file( $info['name'] ) ) { continue; } $uncompressed_size += $info['size']; $dirname = dirname( $info['name'] ); if ( str_ends_with( $info['name'], '/' ) ) { // Directory. $needed_dirs[] = $to . untrailingslashit( $info['name'] ); } elseif ( '.' !== $dirname ) { // Path to a file. $needed_dirs[] = $to . untrailingslashit( $dirname ); } } // Enough space to unzip the file and copy its contents, with a 10% buffer. $required_space = $uncompressed_size * 2.1; /* * disk_free_space() could return false. Assume that any falsey value is an error. * A disk that has zero free bytes has bigger problems. * Require we have enough space to unzip the file and copy its contents, with a 10% buffer. */ if ( wp_doing_cron() ) { $available_space = function_exists( 'disk_free_space' ) ? @disk_free_space( WP_CONTENT_DIR ) : false; if ( $available_space && ( $required_space > $available_space ) ) { $z->close(); return new WP_Error( 'disk_full_unzip_file', __( 'Could not copy files. You may have run out of disk space.' ), compact( 'uncompressed_size', 'available_space' ) ); } } $needed_dirs = array_unique( $needed_dirs ); foreach ( $needed_dirs as $dir ) { // Check the parent folders of the folders all exist within the creation array. if ( untrailingslashit( $to ) === $dir ) { // Skip over the working directory, we know this exists (or will exist). continue; } if ( ! str_contains( $dir, $to ) ) { // If the directory is not within the working directory, skip it. continue; } $parent_folder = dirname( $dir ); while ( ! empty( $parent_folder ) && untrailingslashit( $to ) !== $parent_folder && ! in_array( $parent_folder, $needed_dirs, true ) ) { $needed_dirs[] = $parent_folder; $parent_folder = dirname( $parent_folder ); } } asort( $needed_dirs ); // Create those directories if need be: foreach ( $needed_dirs as $_dir ) { // Only check to see if the Dir exists upon creation failure. Less I/O this way. if ( ! $wp_filesystem->mkdir( $_dir, FS_CHMOD_DIR ) && ! $wp_filesystem->is_dir( $_dir ) ) { $z->close(); return new WP_Error( 'mkdir_failed_ziparchive', __( 'Could not create directory.' ), $_dir ); } } /** * Filters archive unzipping to override with a custom process. * * @since 6.4.0 * * @param null|true|WP_Error $result The result of the override. True on success, otherwise WP Error. Default null. * @param string $file Full path and filename of ZIP archive. * @param string $to Full path on the filesystem to extract archive to. * @param string[] $needed_dirs A full list of required folders that need to be created. * @param float $required_space The space required to unzip the file and copy its contents, with a 10% buffer. */ $pre = apply_filters( 'pre_unzip_file', null, $file, $to, $needed_dirs, $required_space ); if ( null !== $pre ) { // Ensure the ZIP file archive has been closed. $z->close(); return $pre; } for ( $i = 0; $i < $z->numFiles; $i++ ) { $info = $z->statIndex( $i ); if ( ! $info ) { $z->close(); return new WP_Error( 'stat_failed_ziparchive', __( 'Could not retrieve file from archive.' ) ); } if ( str_ends_with( $info['name'], '/' ) ) { // Directory. continue; } if ( str_starts_with( $info['name'], '__MACOSX/' ) ) { // Don't extract the OS X-created __MACOSX directory files. continue; } // Don't extract invalid files: if ( 0 !== validate_file( $info['name'] ) ) { continue; } $contents = $z->getFromIndex( $i ); if ( false === $contents ) { $z->close(); return new WP_Error( 'extract_failed_ziparchive', __( 'Could not extract file from archive.' ), $info['name'] ); } if ( ! $wp_filesystem->put_contents( $to . $info['name'], $contents, FS_CHMOD_FILE ) ) { $z->close(); return new WP_Error( 'copy_failed_ziparchive', __( 'Could not copy file.' ), $info['name'] ); } } $z->close(); /** * Filters the result of unzipping an archive. * * @since 6.4.0 * * @param true|WP_Error $result The result of unzipping the archive. True on success, otherwise WP_Error. Default true. * @param string $file Full path and filename of ZIP archive. * @param string $to Full path on the filesystem the archive was extracted to. * @param string[] $needed_dirs A full list of required folders that were created. * @param float $required_space The space required to unzip the file and copy its contents, with a 10% buffer. */ $result = apply_filters( 'unzip_file', true, $file, $to, $needed_dirs, $required_space ); unset( $needed_dirs ); return $result; } /** * Attempts to unzip an archive using the PclZip library. * * This function should not be called directly, use `unzip_file()` instead. * * Assumes that WP_Filesystem() has already been called and set up. * * @since 3.0.0 * @access private * * @see unzip_file() * * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass. * * @param string $file Full path and filename of ZIP archive. * @param string $to Full path on the filesystem to extract archive to. * @param string[] $needed_dirs A partial list of required folders needed to be created. * @return true|WP_Error True on success, WP_Error on failure. */ function _unzip_file_pclzip( $file, $to, $needed_dirs = array() ) { global $wp_filesystem; mbstring_binary_safe_encoding(); require_once ABSPATH . 'wp-admin/includes/class-pclzip.php'; $archive = new PclZip( $file ); $archive_files = $archive->extract( PCLZIP_OPT_EXTRACT_AS_STRING ); reset_mbstring_encoding(); // Is the archive valid? if ( ! is_array( $archive_files ) ) { return new WP_Error( 'incompatible_archive', __( 'Incompatible Archive.' ), $archive->errorInfo( true ) ); } if ( 0 === count( $archive_files ) ) { return new WP_Error( 'empty_archive_pclzip', __( 'Empty archive.' ) ); } $uncompressed_size = 0; // Determine any children directories needed (From within the archive). foreach ( $archive_files as $file ) { if ( str_starts_with( $file['filename'], '__MACOSX/' ) ) { // Skip the OS X-created __MACOSX directory. continue; } $uncompressed_size += $file['size']; $needed_dirs[] = $to . untrailingslashit( $file['folder'] ? $file['filename'] : dirname( $file['filename'] ) ); } // Enough space to unzip the file and copy its contents, with a 10% buffer. $required_space = $uncompressed_size * 2.1; /* * disk_free_space() could return false. Assume that any falsey value is an error. * A disk that has zero free bytes has bigger problems. * Require we have enough space to unzip the file and copy its contents, with a 10% buffer. */ if ( wp_doing_cron() ) { $available_space = function_exists( 'disk_free_space' ) ? @disk_free_space( WP_CONTENT_DIR ) : false; if ( $available_space && ( $required_space > $available_space ) ) { return new WP_Error( 'disk_full_unzip_file', __( 'Could not copy files. You may have run out of disk space.' ), compact( 'uncompressed_size', 'available_space' ) ); } } $needed_dirs = array_unique( $needed_dirs ); foreach ( $needed_dirs as $dir ) { // Check the parent folders of the folders all exist within the creation array. if ( untrailingslashit( $to ) === $dir ) { // Skip over the working directory, we know this exists (or will exist). continue; } if ( ! str_contains( $dir, $to ) ) { // If the directory is not within the working directory, skip it. continue; } $parent_folder = dirname( $dir ); while ( ! empty( $parent_folder ) && untrailingslashit( $to ) !== $parent_folder && ! in_array( $parent_folder, $needed_dirs, true ) ) { $needed_dirs[] = $parent_folder; $parent_folder = dirname( $parent_folder ); } } asort( $needed_dirs ); // Create those directories if need be: foreach ( $needed_dirs as $_dir ) { // Only check to see if the dir exists upon creation failure. Less I/O this way. if ( ! $wp_filesystem->mkdir( $_dir, FS_CHMOD_DIR ) && ! $wp_filesystem->is_dir( $_dir ) ) { return new WP_Error( 'mkdir_failed_pclzip', __( 'Could not create directory.' ), $_dir ); } } /** This filter is documented in src/wp-admin/includes/file.php */ $pre = apply_filters( 'pre_unzip_file', null, $file, $to, $needed_dirs, $required_space ); if ( null !== $pre ) { return $pre; } // Extract the files from the zip. foreach ( $archive_files as $file ) { if ( $file['folder'] ) { continue; } if ( str_starts_with( $file['filename'], '__MACOSX/' ) ) { // Don't extract the OS X-created __MACOSX directory files. continue; } // Don't extract invalid files: if ( 0 !== validate_file( $file['filename'] ) ) { continue; } if ( ! $wp_filesystem->put_contents( $to . $file['filename'], $file['content'], FS_CHMOD_FILE ) ) { return new WP_Error( 'copy_failed_pclzip', __( 'Could not copy file.' ), $file['filename'] ); } } /** This action is documented in src/wp-admin/includes/file.php */ $result = apply_filters( 'unzip_file', true, $file, $to, $needed_dirs, $required_space ); unset( $needed_dirs ); return $result; } /** * Copies a directory from one location to another via the WordPress Filesystem * Abstraction. * * Assumes that WP_Filesystem() has already been called and setup. * * @since 2.5.0 * * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass. * * @param string $from Source directory. * @param string $to Destination directory. * @param string[] $skip_list An array of files/folders to skip copying. * @return true|WP_Error True on success, WP_Error on failure. */ function copy_dir( $from, $to, $skip_list = array() ) { global $wp_filesystem; $dirlist = $wp_filesystem->dirlist( $from ); if ( false === $dirlist ) { return new WP_Error( 'dirlist_failed_copy_dir', __( 'Directory listing failed.' ), basename( $from ) ); } $from = trailingslashit( $from ); $to = trailingslashit( $to ); if ( ! $wp_filesystem->exists( $to ) && ! $wp_filesystem->mkdir( $to ) ) { return new WP_Error( 'mkdir_destination_failed_copy_dir', __( 'Could not create the destination directory.' ), basename( $to ) ); } foreach ( (array) $dirlist as $filename => $fileinfo ) { if ( in_array( $filename, $skip_list, true ) ) { continue; } if ( 'f' === $fileinfo['type'] ) { if ( ! $wp_filesystem->copy( $from . $filename, $to . $filename, true, FS_CHMOD_FILE ) ) { // If copy failed, chmod file to 0644 and try again. $wp_filesystem->chmod( $to . $filename, FS_CHMOD_FILE ); if ( ! $wp_filesystem->copy( $from . $filename, $to . $filename, true, FS_CHMOD_FILE ) ) { return new WP_Error( 'copy_failed_copy_dir', __( 'Could not copy file.' ), $to . $filename ); } } wp_opcache_invalidate( $to . $filename ); } elseif ( 'd' === $fileinfo['type'] ) { if ( ! $wp_filesystem->is_dir( $to . $filename ) ) { if ( ! $wp_filesystem->mkdir( $to . $filename, FS_CHMOD_DIR ) ) { return new WP_Error( 'mkdir_failed_copy_dir', __( 'Could not create directory.' ), $to . $filename ); } } // Generate the $sub_skip_list for the subdirectory as a sub-set of the existing $skip_list. $sub_skip_list = array(); foreach ( $skip_list as $skip_item ) { if ( str_starts_with( $skip_item, $filename . '/' ) ) { $sub_skip_list[] = preg_replace( '!^' . preg_quote( $filename, '!' ) . '/!i', '', $skip_item ); } } $result = copy_dir( $from . $filename, $to . $filename, $sub_skip_list ); if ( is_wp_error( $result ) ) { return $result; } } } return true; } /** * Moves a directory from one location to another. * * Recursively invalidates OPcache on success. * * If the renaming failed, falls back to copy_dir(). * * Assumes that WP_Filesystem() has already been called and setup. * * This function is not designed to merge directories, copy_dir() should be used instead. * * @since 6.2.0 * * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass. * * @param string $from Source directory. * @param string $to Destination directory. * @param bool $overwrite Optional. Whether to overwrite the destination directory if it exists. * Default false. * @return true|WP_Error True on success, WP_Error on failure. */ function move_dir( $from, $to, $overwrite = false ) { global $wp_filesystem; if ( trailingslashit( strtolower( $from ) ) === trailingslashit( strtolower( $to ) ) ) { return new WP_Error( 'source_destination_same_move_dir', __( 'The source and destination are the same.' ) ); } if ( $wp_filesystem->exists( $to ) ) { if ( ! $overwrite ) { return new WP_Error( 'destination_already_exists_move_dir', __( 'The destination folder already exists.' ), $to ); } elseif ( ! $wp_filesystem->delete( $to, true ) ) { // Can't overwrite if the destination couldn't be deleted. return new WP_Error( 'destination_not_deleted_move_dir', __( 'The destination directory already exists and could not be removed.' ) ); } } if ( $wp_filesystem->move( $from, $to ) ) { /* * When using an environment with shared folders, * there is a delay in updating the filesystem's cache. * * This is a known issue in environments with a VirtualBox provider. * * A 200ms delay gives time for the filesystem to update its cache, * prevents "Operation not permitted", and "No such file or directory" warnings. * * This delay is used in other projects, including Composer. * @link https://github.com/composer/composer/blob/2.5.1/src/Composer/Util/Platform.php#L228-L233 */ usleep( 200000 ); wp_opcache_invalidate_directory( $to ); return true; } // Fall back to a recursive copy. if ( ! $wp_filesystem->is_dir( $to ) ) { if ( ! $wp_filesystem->mkdir( $to, FS_CHMOD_DIR ) ) { return new WP_Error( 'mkdir_failed_move_dir', __( 'Could not create directory.' ), $to ); } } $result = copy_dir( $from, $to, array( basename( $to ) ) ); // Clear the source directory. if ( true === $result ) { $wp_filesystem->delete( $from, true ); } return $result; } /** * Initializes and connects the WordPress Filesystem Abstraction classes. * * This function will include the chosen transport and attempt connecting. * * Plugins may add extra transports, And force WordPress to use them by returning * the filename via the {@see 'filesystem_method_file'} filter. * * @since 2.5.0 * * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass. * * @param array|false $args Optional. Connection args, These are passed * directly to the `WP_Filesystem_*()` classes. * Default false. * @param string|false $context Optional. Context for get_filesystem_method(). * Default false. * @param bool $allow_relaxed_file_ownership Optional. Whether to allow Group/World writable. * Default false. * @return bool|null True on success, false on failure, * null if the filesystem method class file does not exist. */ function WP_Filesystem( $args = false, $context = false, $allow_relaxed_file_ownership = false ) { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName.FunctionNameInvalid global $wp_filesystem; require_once ABSPATH . 'wp-admin/includes/class-wp-filesystem-base.php'; $method = get_filesystem_method( $args, $context, $allow_relaxed_file_ownership ); if ( ! $method ) { return false; } if ( ! class_exists( "WP_Filesystem_$method" ) ) { /** * Filters the path for a specific filesystem method class file. * * @since 2.6.0 * * @see get_filesystem_method() * * @param string $path Path to the specific filesystem method class file. * @param string $method The filesystem method to use. */ $abstraction_file = apply_filters( 'filesystem_method_file', ABSPATH . 'wp-admin/includes/class-wp-filesystem-' . $method . '.php', $method ); if ( ! file_exists( $abstraction_file ) ) { return; } require_once $abstraction_file; } $method = "WP_Filesystem_$method"; $wp_filesystem = new $method( $args ); /* * Define the timeouts for the connections. Only available after the constructor is called * to allow for per-transport overriding of the default. */ if ( ! defined( 'FS_CONNECT_TIMEOUT' ) ) { define( 'FS_CONNECT_TIMEOUT', 30 ); // 30 seconds. } if ( ! defined( 'FS_TIMEOUT' ) ) { define( 'FS_TIMEOUT', 30 ); // 30 seconds. } if ( is_wp_error( $wp_filesystem->errors ) && $wp_filesystem->errors->has_errors() ) { return false; } if ( ! $wp_filesystem->connect() ) { return false; // There was an error connecting to the server. } // Set the permission constants if not already set. if ( ! defined( 'FS_CHMOD_DIR' ) ) { define( 'FS_CHMOD_DIR', ( fileperms( ABSPATH ) & 0777 | 0755 ) ); } if ( ! defined( 'FS_CHMOD_FILE' ) ) { define( 'FS_CHMOD_FILE', ( fileperms( ABSPATH . 'index.php' ) & 0777 | 0644 ) ); } return true; } /** * Determines which method to use for reading, writing, modifying, or deleting * files on the filesystem. * * The priority of the transports are: Direct, SSH2, FTP PHP Extension, FTP Sockets * (Via Sockets class, or `fsockopen()`). Valid values for these are: 'direct', 'ssh2', * 'ftpext' or 'ftpsockets'. * * The return value can be overridden by defining the `FS_METHOD` constant in `wp-config.php`, * or filtering via {@see 'filesystem_method'}. * * @link https://wordpress.org/documentation/article/editing-wp-config-php/#wordpress-upgrade-constants * * Plugins may define a custom transport handler, See WP_Filesystem(). * * @since 2.5.0 * * @global callable $_wp_filesystem_direct_method * * @param array $args Optional. Connection details. Default empty array. * @param string $context Optional. Full path to the directory that is tested * for being writable. Default empty. * @param bool $allow_relaxed_file_ownership Optional. Whether to allow Group/World writable. * Default false. * @return string The transport to use, see description for valid return values. */ function get_filesystem_method( $args = array(), $context = '', $allow_relaxed_file_ownership = false ) { // Please ensure that this is either 'direct', 'ssh2', 'ftpext', or 'ftpsockets'. $method = defined( 'FS_METHOD' ) ? FS_METHOD : false; if ( ! $context ) { $context = WP_CONTENT_DIR; } // If the directory doesn't exist (wp-content/languages) then use the parent directory as we'll create it. if ( WP_LANG_DIR === $context && ! is_dir( $context ) ) { $context = dirname( $context ); } $context = trailingslashit( $context ); if ( ! $method ) { $temp_file_name = $context . 'temp-write-test-' . str_replace( '.', '-', uniqid( '', true ) ); $temp_handle = @fopen( $temp_file_name, 'w' ); if ( $temp_handle ) { // Attempt to determine the file owner of the WordPress files, and that of newly created files. $wp_file_owner = false; $temp_file_owner = false; if ( function_exists( 'fileowner' ) ) { $wp_file_owner = @fileowner( __FILE__ ); $temp_file_owner = @fileowner( $temp_file_name ); } if ( false !== $wp_file_owner && $wp_file_owner === $temp_file_owner ) { /* * WordPress is creating files as the same owner as the WordPress files, * this means it's safe to modify & create new files via PHP. */ $method = 'direct'; $GLOBALS['_wp_filesystem_direct_method'] = 'file_owner'; } elseif ( $allow_relaxed_file_ownership ) { /* * The $context directory is writable, and $allow_relaxed_file_ownership is set, * this means we can modify files safely in this directory. * This mode doesn't create new files, only alter existing ones. */ $method = 'direct'; $GLOBALS['_wp_filesystem_direct_method'] = 'relaxed_ownership'; } fclose( $temp_handle ); @unlink( $temp_file_name ); } } if ( ! $method && isset( $args['connection_type'] ) && 'ssh' === $args['connection_type'] && extension_loaded( 'ssh2' ) ) { $method = 'ssh2'; } if ( ! $method && extension_loaded( 'ftp' ) ) { $method = 'ftpext'; } if ( ! $method && ( extension_loaded( 'sockets' ) || function_exists( 'fsockopen' ) ) ) { $method = 'ftpsockets'; // Sockets: Socket extension; PHP Mode: FSockopen / fwrite / fread. } /** * Filters the filesystem method to use. * * @since 2.6.0 * * @param string $method Filesystem method to return. * @param array $args An array of connection details for the method. * @param string $context Full path to the directory that is tested for being writable. * @param bool $allow_relaxed_file_ownership Whether to allow Group/World writable. */ return apply_filters( 'filesystem_method', $method, $args, $context, $allow_relaxed_file_ownership ); } /** * Displays a form to the user to request for their FTP/SSH details in order * to connect to the filesystem. * * All chosen/entered details are saved, excluding the password. * * Hostnames may be in the form of hostname:portnumber (eg: wordpress.org:2467) * to specify an alternate FTP/SSH port. * * Plugins may override this form by returning true|false via the {@see 'request_filesystem_credentials'} filter. * * @since 2.5.0 * @since 4.6.0 The `$context` parameter default changed from `false` to an empty string. * * @global string $pagenow The filename of the current screen. * * @param string $form_post The URL to post the form to. * @param string $type Optional. Chosen type of filesystem. Default empty. * @param bool|WP_Error $error Optional. Whether the current request has failed * to connect, or an error object. Default false. * @param string $context Optional. Full path to the directory that is tested * for being writable. Default empty. * @param array $extra_fields Optional. Extra `POST` fields to be checked * for inclusion in the post. Default null. * @param bool $allow_relaxed_file_ownership Optional. Whether to allow Group/World writable. * Default false. * @return bool|array True if no filesystem credentials are required, * false if they are required but have not been provided, * array of credentials if they are required and have been provided. */ function request_filesystem_credentials( $form_post, $type = '', $error = false, $context = '', $extra_fields = null, $allow_relaxed_file_ownership = false ) { global $pagenow; /** * Filters the filesystem credentials. * * Returning anything other than an empty string will effectively short-circuit * output of the filesystem credentials form, returning that value instead. * * A filter should return true if no filesystem credentials are required, false if they are required but have not been * provided, or an array of credentials if they are required and have been provided. * * @since 2.5.0 * @since 4.6.0 The `$context` parameter default changed from `false` to an empty string. * * @param mixed $credentials Credentials to return instead. Default empty string. * @param string $form_post The URL to post the form to. * @param string $type Chosen type of filesystem. * @param bool|WP_Error $error Whether the current request has failed to connect, * or an error object. * @param string $context Full path to the directory that is tested for * being writable. * @param array $extra_fields Extra POST fields. * @param bool $allow_relaxed_file_ownership Whether to allow Group/World writable. */ $req_cred = apply_filters( 'request_filesystem_credentials', '', $form_post, $type, $error, $context, $extra_fields, $allow_relaxed_file_ownership ); if ( '' !== $req_cred ) { return $req_cred; } if ( empty( $type ) ) { $type = get_filesystem_method( array(), $context, $allow_relaxed_file_ownership ); } if ( 'direct' === $type ) { return true; } if ( is_null( $extra_fields ) ) { $extra_fields = array( 'version', 'locale' ); } $credentials = get_option( 'ftp_credentials', array( 'hostname' => '', 'username' => '', ) ); $submitted_form = wp_unslash( $_POST ); // Verify nonce, or unset submitted form field values on failure. if ( ! isset( $_POST['_fs_nonce'] ) || ! wp_verify_nonce( $_POST['_fs_nonce'], 'filesystem-credentials' ) ) { unset( $submitted_form['hostname'], $submitted_form['username'], $submitted_form['password'], $submitted_form['public_key'], $submitted_form['private_key'], $submitted_form['connection_type'] ); } $ftp_constants = array( 'hostname' => 'FTP_HOST', 'username' => 'FTP_USER', 'password' => 'FTP_PASS', 'public_key' => 'FTP_PUBKEY', 'private_key' => 'FTP_PRIKEY', ); /* * If defined, set it to that. Else, if POST'd, set it to that. If not, set it to an empty string. * Otherwise, keep it as it previously was (saved details in option). */ foreach ( $ftp_constants as $key => $constant ) { if ( defined( $constant ) ) { $credentials[ $key ] = constant( $constant ); } elseif ( ! empty( $submitted_form[ $key ] ) ) { $credentials[ $key ] = $submitted_form[ $key ]; } elseif ( ! isset( $credentials[ $key ] ) ) { $credentials[ $key ] = ''; } } // Sanitize the hostname, some people might pass in odd data. $credentials['hostname'] = preg_replace( '|\w+://|', '', $credentials['hostname'] ); // Strip any schemes off. if ( strpos( $credentials['hostname'], ':' ) ) { list( $credentials['hostname'], $credentials['port'] ) = explode( ':', $credentials['hostname'], 2 ); if ( ! is_numeric( $credentials['port'] ) ) { unset( $credentials['port'] ); } } else { unset( $credentials['port'] ); } if ( ( defined( 'FTP_SSH' ) && FTP_SSH ) || ( defined( 'FS_METHOD' ) && 'ssh2' === FS_METHOD ) ) { $credentials['connection_type'] = 'ssh'; } elseif ( ( defined( 'FTP_SSL' ) && FTP_SSL ) && 'ftpext' === $type ) { // Only the FTP Extension understands SSL. $credentials['connection_type'] = 'ftps'; } elseif ( ! empty( $submitted_form['connection_type'] ) ) { $credentials['connection_type'] = $submitted_form['connection_type']; } elseif ( ! isset( $credentials['connection_type'] ) ) { // All else fails (and it's not defaulted to something else saved), default to FTP. $credentials['connection_type'] = 'ftp'; } if ( ! $error && ( ! empty( $credentials['hostname'] ) && ! empty( $credentials['username'] ) && ! empty( $credentials['password'] ) || 'ssh' === $credentials['connection_type'] && ! empty( $credentials['public_key'] ) && ! empty( $credentials['private_key'] ) ) ) { $stored_credentials = $credentials; if ( ! empty( $stored_credentials['port'] ) ) { // Save port as part of hostname to simplify above code. $stored_credentials['hostname'] .= ':' . $stored_credentials['port']; } unset( $stored_credentials['password'], $stored_credentials['port'], $stored_credentials['private_key'], $stored_credentials['public_key'] ); if ( ! wp_installing() ) { update_option( 'ftp_credentials', $stored_credentials ); } return $credentials; } $hostname = isset( $credentials['hostname'] ) ? $credentials['hostname'] : ''; $username = isset( $credentials['username'] ) ? $credentials['username'] : ''; $public_key = isset( $credentials['public_key'] ) ? $credentials['public_key'] : ''; $private_key = isset( $credentials['private_key'] ) ? $credentials['private_key'] : ''; $port = isset( $credentials['port'] ) ? $credentials['port'] : ''; $connection_type = isset( $credentials['connection_type'] ) ? $credentials['connection_type'] : ''; if ( $error ) { $error_string = __( '<strong>Error:</strong> Could not connect to the server. Please verify the settings are correct.' ); if ( is_wp_error( $error ) ) { $error_string = esc_html( $error->get_error_message() ); } wp_admin_notice( $error_string, array( 'id' => 'message', 'additional_classes' => array( 'error' ), ) ); } $types = array(); if ( extension_loaded( 'ftp' ) || extension_loaded( 'sockets' ) || function_exists( 'fsockopen' ) ) { $types['ftp'] = __( 'FTP' ); } if ( extension_loaded( 'ftp' ) ) { // Only this supports FTPS. $types['ftps'] = __( 'FTPS (SSL)' ); } if ( extension_loaded( 'ssh2' ) ) { $types['ssh'] = __( 'SSH2' ); } /** * Filters the connection types to output to the filesystem credentials form. * * @since 2.9.0 * @since 4.6.0 The `$context` parameter default changed from `false` to an empty string. * * @param string[] $types Types of connections. * @param array $credentials Credentials to connect with. * @param string $type Chosen filesystem method. * @param bool|WP_Error $error Whether the current request has failed to connect, * or an error object. * @param string $context Full path to the directory that is tested for being writable. */ $types = apply_filters( 'fs_ftp_connection_types', $types, $credentials, $type, $error, $context ); ?> <form action="<?php echo esc_url( $form_post ); ?>" method="post"> <div id="request-filesystem-credentials-form" class="request-filesystem-credentials-form"> <?php // Print a H1 heading in the FTP credentials modal dialog, default is a H2. $heading_tag = 'h2'; if ( 'plugins.php' === $pagenow || 'plugin-install.php' === $pagenow ) { $heading_tag = 'h1'; } echo "<$heading_tag id='request-filesystem-credentials-title'>" . __( 'Connection Information' ) . "</$heading_tag>"; ?> <p id="request-filesystem-credentials-desc"> <?php $label_user = __( 'Username' ); $label_pass = __( 'Password' ); _e( 'To perform the requested action, WordPress needs to access your web server.' ); echo ' '; if ( ( isset( $types['ftp'] ) || isset( $types['ftps'] ) ) ) { if ( isset( $types['ssh'] ) ) { _e( 'Please enter your FTP or SSH credentials to proceed.' ); $label_user = __( 'FTP/SSH Username' ); $label_pass = __( 'FTP/SSH Password' ); } else { _e( 'Please enter your FTP credentials to proceed.' ); $label_user = __( 'FTP Username' ); $label_pass = __( 'FTP Password' ); } echo ' '; } _e( 'If you do not remember your credentials, you should contact your web host.' ); $hostname_value = esc_attr( $hostname ); if ( ! empty( $port ) ) { $hostname_value .= ":$port"; } $password_value = ''; if ( defined( 'FTP_PASS' ) ) { $password_value = '*****'; } ?> </p> <label for="hostname"> <span class="field-title"><?php _e( 'Hostname' ); ?></span> <input name="hostname" type="text" id="hostname" aria-describedby="request-filesystem-credentials-desc" class="code" placeholder="<?php esc_attr_e( 'example: www.wordpress.org' ); ?>" value="<?php echo $hostname_value; ?>"<?php disabled( defined( 'FTP_HOST' ) ); ?> /> </label> <div class="ftp-username"> <label for="username"> <span class="field-title"><?php echo $label_user; ?></span> <input name="username" type="text" id="username" value="<?php echo esc_attr( $username ); ?>"<?php disabled( defined( 'FTP_USER' ) ); ?> /> </label> </div> <div class="ftp-password"> <label for="password"> <span class="field-title"><?php echo $label_pass; ?></span> <input name="password" type="password" id="password" value="<?php echo $password_value; ?>"<?php disabled( defined( 'FTP_PASS' ) ); ?> spellcheck="false" /> <?php if ( ! defined( 'FTP_PASS' ) ) { _e( 'This password will not be stored on the server.' ); } ?> </label> </div> <fieldset> <legend><?php _e( 'Connection Type' ); ?></legend> <?php $disabled = disabled( ( defined( 'FTP_SSL' ) && FTP_SSL ) || ( defined( 'FTP_SSH' ) && FTP_SSH ), true, false ); foreach ( $types as $name => $text ) : ?> <label for="<?php echo esc_attr( $name ); ?>"> <input type="radio" name="connection_type" id="<?php echo esc_attr( $name ); ?>" value="<?php echo esc_attr( $name ); ?>" <?php checked( $name, $connection_type ); ?> <?php echo $disabled; ?> /> <?php echo $text; ?> </label> <?php endforeach; ?> </fieldset> <?php if ( isset( $types['ssh'] ) ) { $hidden_class = ''; if ( 'ssh' !== $connection_type || empty( $connection_type ) ) { $hidden_class = ' class="hidden"'; } ?> <fieldset id="ssh-keys"<?php echo $hidden_class; ?>> <legend><?php _e( 'Authentication Keys' ); ?></legend> <label for="public_key"> <span class="field-title"><?php _e( 'Public Key:' ); ?></span> <input name="public_key" type="text" id="public_key" aria-describedby="auth-keys-desc" value="<?php echo esc_attr( $public_key ); ?>"<?php disabled( defined( 'FTP_PUBKEY' ) ); ?> /> </label> <label for="private_key"> <span class="field-title"><?php _e( 'Private Key:' ); ?></span> <input name="private_key" type="text" id="private_key" value="<?php echo esc_attr( $private_key ); ?>"<?php disabled( defined( 'FTP_PRIKEY' ) ); ?> /> </label> <p id="auth-keys-desc"><?php _e( 'Enter the location on the server where the public and private keys are located. If a passphrase is needed, enter that in the password field above.' ); ?></p> </fieldset> <?php } foreach ( (array) $extra_fields as $field ) { if ( isset( $submitted_form[ $field ] ) ) { echo '<input type="hidden" name="' . esc_attr( $field ) . '" value="' . esc_attr( $submitted_form[ $field ] ) . '" />'; } } /* * Make sure the `submit_button()` function is available during the REST API call * from WP_Site_Health_Auto_Updates::test_check_wp_filesystem_method(). */ if ( ! function_exists( 'submit_button' ) ) { require_once ABSPATH . 'wp-admin/includes/template.php'; } ?> <p class="request-filesystem-credentials-action-buttons"> <?php wp_nonce_field( 'filesystem-credentials', '_fs_nonce', false, true ); ?> <button class="button cancel-button" data-js-action="close" type="button"><?php _e( 'Cancel' ); ?></button> <?php submit_button( __( 'Proceed' ), '', 'upgrade', false ); ?> </p> </div> </form> <?php return false; } /** * Prints the filesystem credentials modal when needed. * * @since 4.2.0 */ function wp_print_request_filesystem_credentials_modal() { $filesystem_method = get_filesystem_method(); ob_start(); $filesystem_credentials_are_stored = request_filesystem_credentials( self_admin_url() ); ob_end_clean(); $request_filesystem_credentials = ( 'direct' !== $filesystem_method && ! $filesystem_credentials_are_stored ); if ( ! $request_filesystem_credentials ) { return; } ?> <div id="request-filesystem-credentials-dialog" class="notification-dialog-wrap request-filesystem-credentials-dialog"> <div class="notification-dialog-background"></div> <div class="notification-dialog" role="dialog" aria-labelledby="request-filesystem-credentials-title" tabindex="0"> <div class="request-filesystem-credentials-dialog-content"> <?php request_filesystem_credentials( site_url() ); ?> </div> </div> </div> <?php } /** * Attempts to clear the opcode cache for an individual PHP file. * * This function can be called safely without having to check the file extension * or availability of the OPcache extension. * * Whether or not invalidation is possible is cached to improve performance. * * @since 5.5.0 * * @link https://www.php.net/manual/en/function.opcache-invalidate.php * * @param string $filepath Path to the file, including extension, for which the opcode cache is to be cleared. * @param bool $force Invalidate even if the modification time is not newer than the file in cache. * Default false. * @return bool True if opcache was invalidated for `$filepath`, or there was nothing to invalidate. * False if opcache invalidation is not available, or is disabled via filter. */ function wp_opcache_invalidate( $filepath, $force = false ) { static $can_invalidate = null; /* * Check to see if WordPress is able to run `opcache_invalidate()` or not, and cache the value. * * First, check to see if the function is available to call, then if the host has restricted * the ability to run the function to avoid a PHP warning. * * `opcache.restrict_api` can specify the path for files allowed to call `opcache_invalidate()`. * * If the host has this set, check whether the path in `opcache.restrict_api` matches * the beginning of the path of the origin file. * * `$_SERVER['SCRIPT_FILENAME']` approximates the origin file's path, but `realpath()` * is necessary because `SCRIPT_FILENAME` can be a relative path when run from CLI. * * For more details, see: * - https://www.php.net/manual/en/opcache.configuration.php * - https://www.php.net/manual/en/reserved.variables.server.php * - https://core.trac.wordpress.org/ticket/36455 */ if ( null === $can_invalidate && function_exists( 'opcache_invalidate' ) && ( ! ini_get( 'opcache.restrict_api' ) || stripos( realpath( $_SERVER['SCRIPT_FILENAME'] ), ini_get( 'opcache.restrict_api' ) ) === 0 ) ) { $can_invalidate = true; } // If invalidation is not available, return early. if ( ! $can_invalidate ) { return false; } // Verify that file to be invalidated has a PHP extension. if ( '.php' !== strtolower( substr( $filepath, -4 ) ) ) { return false; } /** * Filters whether to invalidate a file from the opcode cache. * * @since 5.5.0 * * @param bool $will_invalidate Whether WordPress will invalidate `$filepath`. Default true. * @param string $filepath The path to the PHP file to invalidate. */ if ( apply_filters( 'wp_opcache_invalidate_file', true, $filepath ) ) { return opcache_invalidate( $filepath, $force ); } return false; } /** * Attempts to clear the opcode cache for a directory of files. * * @since 6.2.0 * * @see wp_opcache_invalidate() * @link https://www.php.net/manual/en/function.opcache-invalidate.php * * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass. * * @param string $dir The path to the directory for which the opcode cache is to be cleared. */ function wp_opcache_invalidate_directory( $dir ) { global $wp_filesystem; if ( ! is_string( $dir ) || '' === trim( $dir ) ) { if ( WP_DEBUG ) { $error_message = sprintf( /* translators: %s: The function name. */ __( '%s expects a non-empty string.' ), '<code>wp_opcache_invalidate_directory()</code>' ); trigger_error( $error_message ); } return; } $dirlist = $wp_filesystem->dirlist( $dir, false, true ); if ( empty( $dirlist ) ) { return; } /* * Recursively invalidate opcache of files in a directory. * * WP_Filesystem_*::dirlist() returns an array of file and directory information. * * This does not include a path to the file or directory. * To invalidate files within sub-directories, recursion is needed * to prepend an absolute path containing the sub-directory's name. * * @param array $dirlist Array of file/directory information from WP_Filesystem_Base::dirlist(), * with sub-directories represented as nested arrays. * @param string $path Absolute path to the directory. */ $invalidate_directory = static function ( $dirlist, $path ) use ( &$invalidate_directory ) { $path = trailingslashit( $path ); foreach ( $dirlist as $name => $details ) { if ( 'f' === $details['type'] ) { wp_opcache_invalidate( $path . $name, true ); } elseif ( is_array( $details['files'] ) && ! empty( $details['files'] ) ) { $invalidate_directory( $details['files'], $path . $name ); } } }; $invalidate_directory( $dirlist, $dir ); } includes/menu.php 0000644 00000022622 15135536347 0010047 0 ustar 00 <?php /** * Build Administration Menu. * * @package WordPress * @subpackage Administration */ if ( is_network_admin() ) { /** * Fires before the administration menu loads in the Network Admin. * * The hook fires before menus and sub-menus are removed based on user privileges. * * @since 3.1.0 * @access private */ do_action( '_network_admin_menu' ); } elseif ( is_user_admin() ) { /** * Fires before the administration menu loads in the User Admin. * * The hook fires before menus and sub-menus are removed based on user privileges. * * @since 3.1.0 * @access private */ do_action( '_user_admin_menu' ); } else { /** * Fires before the administration menu loads in the admin. * * The hook fires before menus and sub-menus are removed based on user privileges. * * @since 2.2.0 * @access private */ do_action( '_admin_menu' ); } // Create list of page plugin hook names. foreach ( $menu as $menu_page ) { $pos = strpos( $menu_page[2], '?' ); if ( false !== $pos ) { // Handle post_type=post|page|foo pages. $hook_name = substr( $menu_page[2], 0, $pos ); $hook_args = substr( $menu_page[2], $pos + 1 ); wp_parse_str( $hook_args, $hook_args ); // Set the hook name to be the post type. if ( isset( $hook_args['post_type'] ) ) { $hook_name = $hook_args['post_type']; } else { $hook_name = basename( $hook_name, '.php' ); } unset( $hook_args ); } else { $hook_name = basename( $menu_page[2], '.php' ); } $hook_name = sanitize_title( $hook_name ); if ( isset( $compat[ $hook_name ] ) ) { $hook_name = $compat[ $hook_name ]; } elseif ( ! $hook_name ) { continue; } $admin_page_hooks[ $menu_page[2] ] = $hook_name; } unset( $menu_page, $compat ); $_wp_submenu_nopriv = array(); $_wp_menu_nopriv = array(); // Loop over submenus and remove pages for which the user does not have privs. foreach ( $submenu as $parent => $sub ) { foreach ( $sub as $index => $data ) { if ( ! current_user_can( $data[1] ) ) { unset( $submenu[ $parent ][ $index ] ); $_wp_submenu_nopriv[ $parent ][ $data[2] ] = true; } } unset( $index, $data ); if ( empty( $submenu[ $parent ] ) ) { unset( $submenu[ $parent ] ); } } unset( $sub, $parent ); /* * Loop over the top-level menu. * Menus for which the original parent is not accessible due to lack of privileges * will have the next submenu in line be assigned as the new menu parent. */ foreach ( $menu as $id => $data ) { if ( empty( $submenu[ $data[2] ] ) ) { continue; } $subs = $submenu[ $data[2] ]; $first_sub = reset( $subs ); $old_parent = $data[2]; $new_parent = $first_sub[2]; /* * If the first submenu is not the same as the assigned parent, * make the first submenu the new parent. */ if ( $new_parent !== $old_parent ) { $_wp_real_parent_file[ $old_parent ] = $new_parent; $menu[ $id ][2] = $new_parent; foreach ( $submenu[ $old_parent ] as $index => $data ) { $submenu[ $new_parent ][ $index ] = $submenu[ $old_parent ][ $index ]; unset( $submenu[ $old_parent ][ $index ] ); } unset( $submenu[ $old_parent ], $index ); if ( isset( $_wp_submenu_nopriv[ $old_parent ] ) ) { $_wp_submenu_nopriv[ $new_parent ] = $_wp_submenu_nopriv[ $old_parent ]; } } } unset( $id, $data, $subs, $first_sub, $old_parent, $new_parent ); if ( is_network_admin() ) { /** * Fires before the administration menu loads in the Network Admin. * * @since 3.1.0 * * @param string $context Empty context. */ do_action( 'network_admin_menu', '' ); } elseif ( is_user_admin() ) { /** * Fires before the administration menu loads in the User Admin. * * @since 3.1.0 * * @param string $context Empty context. */ do_action( 'user_admin_menu', '' ); } else { /** * Fires before the administration menu loads in the admin. * * @since 1.5.0 * * @param string $context Empty context. */ do_action( 'admin_menu', '' ); } /* * Remove menus that have no accessible submenus and require privileges * that the user does not have. Run re-parent loop again. */ foreach ( $menu as $id => $data ) { if ( ! current_user_can( $data[1] ) ) { $_wp_menu_nopriv[ $data[2] ] = true; } /* * If there is only one submenu and it is has same destination as the parent, * remove the submenu. */ if ( ! empty( $submenu[ $data[2] ] ) && 1 === count( $submenu[ $data[2] ] ) ) { $subs = $submenu[ $data[2] ]; $first_sub = reset( $subs ); if ( $data[2] === $first_sub[2] ) { unset( $submenu[ $data[2] ] ); } } // If submenu is empty... if ( empty( $submenu[ $data[2] ] ) ) { // And user doesn't have privs, remove menu. if ( isset( $_wp_menu_nopriv[ $data[2] ] ) ) { unset( $menu[ $id ] ); } } } unset( $id, $data, $subs, $first_sub ); /** * Adds a CSS class to a string. * * @since 2.7.0 * * @param string $class_to_add The CSS class to add. * @param string $classes The string to add the CSS class to. * @return string The string with the CSS class added. */ function add_cssclass( $class_to_add, $classes ) { if ( empty( $classes ) ) { return $class_to_add; } return $classes . ' ' . $class_to_add; } /** * Adds CSS classes for top-level administration menu items. * * The list of added classes includes `.menu-top-first` and `.menu-top-last`. * * @since 2.7.0 * * @param array $menu The array of administration menu items. * @return array The array of administration menu items with the CSS classes added. */ function add_menu_classes( $menu ) { $first_item = false; $last_order = false; $items_count = count( $menu ); $i = 0; foreach ( $menu as $order => $top ) { ++$i; if ( 0 === $order ) { // Dashboard is always shown/single. $menu[0][4] = add_cssclass( 'menu-top-first', $top[4] ); $last_order = 0; continue; } if ( str_starts_with( $top[2], 'separator' ) && false !== $last_order ) { // If separator. $first_item = true; $classes = $menu[ $last_order ][4]; $menu[ $last_order ][4] = add_cssclass( 'menu-top-last', $classes ); continue; } if ( $first_item ) { $first_item = false; $classes = $menu[ $order ][4]; $menu[ $order ][4] = add_cssclass( 'menu-top-first', $classes ); } if ( $i === $items_count ) { // Last item. $classes = $menu[ $order ][4]; $menu[ $order ][4] = add_cssclass( 'menu-top-last', $classes ); } $last_order = $order; } /** * Filters administration menu array with classes added for top-level items. * * @since 2.7.0 * * @param array $menu Associative array of administration menu items. */ return apply_filters( 'add_menu_classes', $menu ); } uksort( $menu, 'strnatcasecmp' ); // Make it all pretty. /** * Filters whether to enable custom ordering of the administration menu. * * See the {@see 'menu_order'} filter for reordering menu items. * * @since 2.8.0 * * @param bool $custom Whether custom ordering is enabled. Default false. */ if ( apply_filters( 'custom_menu_order', false ) ) { $menu_order = array(); foreach ( $menu as $menu_item ) { $menu_order[] = $menu_item[2]; } unset( $menu_item ); $default_menu_order = $menu_order; /** * Filters the order of administration menu items. * * A truthy value must first be passed to the {@see 'custom_menu_order'} filter * for this filter to work. Use the following to enable custom menu ordering: * * add_filter( 'custom_menu_order', '__return_true' ); * * @since 2.8.0 * * @param array $menu_order An ordered array of menu items. */ $menu_order = apply_filters( 'menu_order', $menu_order ); $menu_order = array_flip( $menu_order ); $default_menu_order = array_flip( $default_menu_order ); /** * @global array $menu_order * @global array $default_menu_order * * @param array $a * @param array $b * @return int */ function sort_menu( $a, $b ) { global $menu_order, $default_menu_order; $a = $a[2]; $b = $b[2]; if ( isset( $menu_order[ $a ] ) && ! isset( $menu_order[ $b ] ) ) { return -1; } elseif ( ! isset( $menu_order[ $a ] ) && isset( $menu_order[ $b ] ) ) { return 1; } elseif ( isset( $menu_order[ $a ] ) && isset( $menu_order[ $b ] ) ) { if ( $menu_order[ $a ] === $menu_order[ $b ] ) { return 0; } return ( $menu_order[ $a ] < $menu_order[ $b ] ) ? -1 : 1; } else { return ( $default_menu_order[ $a ] <= $default_menu_order[ $b ] ) ? -1 : 1; } } usort( $menu, 'sort_menu' ); unset( $menu_order, $default_menu_order ); } // Prevent adjacent separators. $prev_menu_was_separator = false; foreach ( $menu as $id => $data ) { if ( false === stristr( $data[4], 'wp-menu-separator' ) ) { // This item is not a separator, so falsey the toggler and do nothing. $prev_menu_was_separator = false; } else { // The previous item was a separator, so unset this one. if ( true === $prev_menu_was_separator ) { unset( $menu[ $id ] ); } // This item is a separator, so truthy the toggler and move on. $prev_menu_was_separator = true; } } unset( $id, $data, $prev_menu_was_separator ); // Remove the last menu item if it is a separator. $last_menu_key = array_keys( $menu ); $last_menu_key = array_pop( $last_menu_key ); if ( ! empty( $menu ) && 'wp-menu-separator' === $menu[ $last_menu_key ][4] ) { unset( $menu[ $last_menu_key ] ); } unset( $last_menu_key ); if ( ! user_can_access_admin_page() ) { /** * Fires when access to an admin page is denied. * * @since 2.5.0 */ do_action( 'admin_page_access_denied' ); wp_die( __( 'Sorry, you are not allowed to access this page.' ), 403 ); } $menu = add_menu_classes( $menu ); includes/class-wp-plugins-list-table.php 0000644 00000160520 15135536347 0014351 0 ustar 00 <?php /** * List Table API: WP_Plugins_List_Table class * * @package WordPress * @subpackage Administration * @since 3.1.0 */ /** * Core class used to implement displaying installed plugins in a list table. * * @since 3.1.0 * * @see WP_List_Table */ class WP_Plugins_List_Table extends WP_List_Table { /** * Whether to show the auto-updates UI. * * @since 5.5.0 * * @var bool True if auto-updates UI is to be shown, false otherwise. */ protected $show_autoupdates = true; /** * Constructor. * * @since 3.1.0 * * @see WP_List_Table::__construct() for more information on default arguments. * * @global string $status * @global int $page * * @param array $args An associative array of arguments. */ public function __construct( $args = array() ) { global $status, $page; parent::__construct( array( 'plural' => 'plugins', 'screen' => isset( $args['screen'] ) ? $args['screen'] : null, ) ); $allowed_statuses = array( 'active', 'inactive', 'recently_activated', 'upgrade', 'mustuse', 'dropins', 'search', 'paused', 'auto-update-enabled', 'auto-update-disabled' ); $status = 'all'; if ( isset( $_REQUEST['plugin_status'] ) && in_array( $_REQUEST['plugin_status'], $allowed_statuses, true ) ) { $status = $_REQUEST['plugin_status']; } if ( isset( $_REQUEST['s'] ) ) { $_SERVER['REQUEST_URI'] = add_query_arg( 's', wp_unslash( $_REQUEST['s'] ) ); } $page = $this->get_pagenum(); $this->show_autoupdates = wp_is_auto_update_enabled_for_type( 'plugin' ) && current_user_can( 'update_plugins' ) && ( ! is_multisite() || $this->screen->in_admin( 'network' ) ); } /** * @return array */ protected function get_table_classes() { return array( 'widefat', $this->_args['plural'] ); } /** * @return bool */ public function ajax_user_can() { return current_user_can( 'activate_plugins' ); } /** * @global string $status * @global array $plugins * @global array $totals * @global int $page * @global string $orderby * @global string $order * @global string $s */ public function prepare_items() { global $status, $plugins, $totals, $page, $orderby, $order, $s; wp_reset_vars( array( 'orderby', 'order' ) ); /** * Filters the full array of plugins to list in the Plugins list table. * * @since 3.0.0 * * @see get_plugins() * * @param array $all_plugins An array of plugins to display in the list table. */ $all_plugins = apply_filters( 'all_plugins', get_plugins() ); $plugins = array( 'all' => $all_plugins, 'search' => array(), 'active' => array(), 'inactive' => array(), 'recently_activated' => array(), 'upgrade' => array(), 'mustuse' => array(), 'dropins' => array(), 'paused' => array(), ); if ( $this->show_autoupdates ) { $auto_updates = (array) get_site_option( 'auto_update_plugins', array() ); $plugins['auto-update-enabled'] = array(); $plugins['auto-update-disabled'] = array(); } $screen = $this->screen; if ( ! is_multisite() || ( $screen->in_admin( 'network' ) && current_user_can( 'manage_network_plugins' ) ) ) { /** * Filters whether to display the advanced plugins list table. * * There are two types of advanced plugins - must-use and drop-ins - * which can be used in a single site or Multisite network. * * The $type parameter allows you to differentiate between the type of advanced * plugins to filter the display of. Contexts include 'mustuse' and 'dropins'. * * @since 3.0.0 * * @param bool $show Whether to show the advanced plugins for the specified * plugin type. Default true. * @param string $type The plugin type. Accepts 'mustuse', 'dropins'. */ if ( apply_filters( 'show_advanced_plugins', true, 'mustuse' ) ) { $plugins['mustuse'] = get_mu_plugins(); } /** This action is documented in wp-admin/includes/class-wp-plugins-list-table.php */ if ( apply_filters( 'show_advanced_plugins', true, 'dropins' ) ) { $plugins['dropins'] = get_dropins(); } if ( current_user_can( 'update_plugins' ) ) { $current = get_site_transient( 'update_plugins' ); foreach ( (array) $plugins['all'] as $plugin_file => $plugin_data ) { if ( isset( $current->response[ $plugin_file ] ) ) { $plugins['all'][ $plugin_file ]['update'] = true; $plugins['upgrade'][ $plugin_file ] = $plugins['all'][ $plugin_file ]; } } } } if ( ! $screen->in_admin( 'network' ) ) { $show = current_user_can( 'manage_network_plugins' ); /** * Filters whether to display network-active plugins alongside plugins active for the current site. * * This also controls the display of inactive network-only plugins (plugins with * "Network: true" in the plugin header). * * Plugins cannot be network-activated or network-deactivated from this screen. * * @since 4.4.0 * * @param bool $show Whether to show network-active plugins. Default is whether the current * user can manage network plugins (ie. a Super Admin). */ $show_network_active = apply_filters( 'show_network_active_plugins', $show ); } if ( $screen->in_admin( 'network' ) ) { $recently_activated = get_site_option( 'recently_activated', array() ); } else { $recently_activated = get_option( 'recently_activated', array() ); } foreach ( $recently_activated as $key => $time ) { if ( $time + WEEK_IN_SECONDS < time() ) { unset( $recently_activated[ $key ] ); } } if ( $screen->in_admin( 'network' ) ) { update_site_option( 'recently_activated', $recently_activated ); } else { update_option( 'recently_activated', $recently_activated ); } $plugin_info = get_site_transient( 'update_plugins' ); foreach ( (array) $plugins['all'] as $plugin_file => $plugin_data ) { // Extra info if known. array_merge() ensures $plugin_data has precedence if keys collide. if ( isset( $plugin_info->response[ $plugin_file ] ) ) { $plugin_data = array_merge( (array) $plugin_info->response[ $plugin_file ], array( 'update-supported' => true ), $plugin_data ); } elseif ( isset( $plugin_info->no_update[ $plugin_file ] ) ) { $plugin_data = array_merge( (array) $plugin_info->no_update[ $plugin_file ], array( 'update-supported' => true ), $plugin_data ); } elseif ( empty( $plugin_data['update-supported'] ) ) { $plugin_data['update-supported'] = false; } /* * Create the payload that's used for the auto_update_plugin filter. * This is the same data contained within $plugin_info->(response|no_update) however * not all plugins will be contained in those keys, this avoids unexpected warnings. */ $filter_payload = array( 'id' => $plugin_file, 'slug' => '', 'plugin' => $plugin_file, 'new_version' => '', 'url' => '', 'package' => '', 'icons' => array(), 'banners' => array(), 'banners_rtl' => array(), 'tested' => '', 'requires_php' => '', 'compatibility' => new stdClass(), ); $filter_payload = (object) wp_parse_args( $plugin_data, $filter_payload ); $auto_update_forced = wp_is_auto_update_forced_for_item( 'plugin', null, $filter_payload ); if ( ! is_null( $auto_update_forced ) ) { $plugin_data['auto-update-forced'] = $auto_update_forced; } $plugins['all'][ $plugin_file ] = $plugin_data; // Make sure that $plugins['upgrade'] also receives the extra info since it is used on ?plugin_status=upgrade. if ( isset( $plugins['upgrade'][ $plugin_file ] ) ) { $plugins['upgrade'][ $plugin_file ] = $plugin_data; } // Filter into individual sections. if ( is_multisite() && ! $screen->in_admin( 'network' ) && is_network_only_plugin( $plugin_file ) && ! is_plugin_active( $plugin_file ) ) { if ( $show_network_active ) { // On the non-network screen, show inactive network-only plugins if allowed. $plugins['inactive'][ $plugin_file ] = $plugin_data; } else { // On the non-network screen, filter out network-only plugins as long as they're not individually active. unset( $plugins['all'][ $plugin_file ] ); } } elseif ( ! $screen->in_admin( 'network' ) && is_plugin_active_for_network( $plugin_file ) ) { if ( $show_network_active ) { // On the non-network screen, show network-active plugins if allowed. $plugins['active'][ $plugin_file ] = $plugin_data; } else { // On the non-network screen, filter out network-active plugins. unset( $plugins['all'][ $plugin_file ] ); } } elseif ( ( ! $screen->in_admin( 'network' ) && is_plugin_active( $plugin_file ) ) || ( $screen->in_admin( 'network' ) && is_plugin_active_for_network( $plugin_file ) ) ) { /* * On the non-network screen, populate the active list with plugins that are individually activated. * On the network admin screen, populate the active list with plugins that are network-activated. */ $plugins['active'][ $plugin_file ] = $plugin_data; if ( ! $screen->in_admin( 'network' ) && is_plugin_paused( $plugin_file ) ) { $plugins['paused'][ $plugin_file ] = $plugin_data; } } else { if ( isset( $recently_activated[ $plugin_file ] ) ) { // Populate the recently activated list with plugins that have been recently activated. $plugins['recently_activated'][ $plugin_file ] = $plugin_data; } // Populate the inactive list with plugins that aren't activated. $plugins['inactive'][ $plugin_file ] = $plugin_data; } if ( $this->show_autoupdates ) { $enabled = in_array( $plugin_file, $auto_updates, true ) && $plugin_data['update-supported']; if ( isset( $plugin_data['auto-update-forced'] ) ) { $enabled = (bool) $plugin_data['auto-update-forced']; } if ( $enabled ) { $plugins['auto-update-enabled'][ $plugin_file ] = $plugin_data; } else { $plugins['auto-update-disabled'][ $plugin_file ] = $plugin_data; } } } if ( strlen( $s ) ) { $status = 'search'; $plugins['search'] = array_filter( $plugins['all'], array( $this, '_search_callback' ) ); } /** * Filters the array of plugins for the list table. * * @since 6.3.0 * * @param array[] $plugins An array of arrays of plugin data, keyed by context. */ $plugins = apply_filters( 'plugins_list', $plugins ); $totals = array(); foreach ( $plugins as $type => $list ) { $totals[ $type ] = count( $list ); } if ( empty( $plugins[ $status ] ) && ! in_array( $status, array( 'all', 'search' ), true ) ) { $status = 'all'; } $this->items = array(); foreach ( $plugins[ $status ] as $plugin_file => $plugin_data ) { // Translate, don't apply markup, sanitize HTML. $this->items[ $plugin_file ] = _get_plugin_data_markup_translate( $plugin_file, $plugin_data, false, true ); } $total_this_page = $totals[ $status ]; $js_plugins = array(); foreach ( $plugins as $key => $list ) { $js_plugins[ $key ] = array_keys( $list ); } wp_localize_script( 'updates', '_wpUpdatesItemCounts', array( 'plugins' => $js_plugins, 'totals' => wp_get_update_data(), ) ); if ( ! $orderby ) { $orderby = 'Name'; } else { $orderby = ucfirst( $orderby ); } $order = strtoupper( $order ); uasort( $this->items, array( $this, '_order_callback' ) ); $plugins_per_page = $this->get_items_per_page( str_replace( '-', '_', $screen->id . '_per_page' ), 999 ); $start = ( $page - 1 ) * $plugins_per_page; if ( $total_this_page > $plugins_per_page ) { $this->items = array_slice( $this->items, $start, $plugins_per_page ); } $this->set_pagination_args( array( 'total_items' => $total_this_page, 'per_page' => $plugins_per_page, ) ); } /** * @global string $s URL encoded search term. * * @param array $plugin * @return bool */ public function _search_callback( $plugin ) { global $s; foreach ( $plugin as $value ) { if ( is_string( $value ) && false !== stripos( strip_tags( $value ), urldecode( $s ) ) ) { return true; } } return false; } /** * @global string $orderby * @global string $order * @param array $plugin_a * @param array $plugin_b * @return int */ public function _order_callback( $plugin_a, $plugin_b ) { global $orderby, $order; $a = $plugin_a[ $orderby ]; $b = $plugin_b[ $orderby ]; if ( $a === $b ) { return 0; } if ( 'DESC' === $order ) { return strcasecmp( $b, $a ); } else { return strcasecmp( $a, $b ); } } /** * @global array $plugins */ public function no_items() { global $plugins; if ( ! empty( $_REQUEST['s'] ) ) { $s = esc_html( urldecode( wp_unslash( $_REQUEST['s'] ) ) ); /* translators: %s: Plugin search term. */ printf( __( 'No plugins found for: %s.' ), '<strong>' . $s . '</strong>' ); // We assume that somebody who can install plugins in multisite is experienced enough to not need this helper link. if ( ! is_multisite() && current_user_can( 'install_plugins' ) ) { echo ' <a href="' . esc_url( admin_url( 'plugin-install.php?tab=search&s=' . urlencode( $s ) ) ) . '">' . __( 'Search for plugins in the WordPress Plugin Directory.' ) . '</a>'; } } elseif ( ! empty( $plugins['all'] ) ) { _e( 'No plugins found.' ); } else { _e( 'No plugins are currently available.' ); } } /** * Displays the search box. * * @since 4.6.0 * * @param string $text The 'submit' button label. * @param string $input_id ID attribute value for the search input field. */ public function search_box( $text, $input_id ) { if ( empty( $_REQUEST['s'] ) && ! $this->has_items() ) { return; } $input_id = $input_id . '-search-input'; if ( ! empty( $_REQUEST['orderby'] ) ) { echo '<input type="hidden" name="orderby" value="' . esc_attr( $_REQUEST['orderby'] ) . '" />'; } if ( ! empty( $_REQUEST['order'] ) ) { echo '<input type="hidden" name="order" value="' . esc_attr( $_REQUEST['order'] ) . '" />'; } ?> <p class="search-box"> <label class="screen-reader-text" for="<?php echo esc_attr( $input_id ); ?>"><?php echo $text; ?>:</label> <input type="search" id="<?php echo esc_attr( $input_id ); ?>" class="wp-filter-search" name="s" value="<?php _admin_search_query(); ?>" placeholder="<?php esc_attr_e( 'Search installed plugins...' ); ?>" /> <?php submit_button( $text, 'hide-if-js', '', false, array( 'id' => 'search-submit' ) ); ?> </p> <?php } /** * @global string $status * * @return string[] Array of column titles keyed by their column name. */ public function get_columns() { global $status; $columns = array( 'cb' => ! in_array( $status, array( 'mustuse', 'dropins' ), true ) ? '<input type="checkbox" />' : '', 'name' => __( 'Plugin' ), 'description' => __( 'Description' ), ); if ( $this->show_autoupdates && ! in_array( $status, array( 'mustuse', 'dropins' ), true ) ) { $columns['auto-updates'] = __( 'Automatic Updates' ); } return $columns; } /** * @return array */ protected function get_sortable_columns() { return array(); } /** * @global array $totals * @global string $status * @return array */ protected function get_views() { global $totals, $status; $status_links = array(); foreach ( $totals as $type => $count ) { if ( ! $count ) { continue; } switch ( $type ) { case 'all': /* translators: %s: Number of plugins. */ $text = _nx( 'All <span class="count">(%s)</span>', 'All <span class="count">(%s)</span>', $count, 'plugins' ); break; case 'active': /* translators: %s: Number of plugins. */ $text = _n( 'Active <span class="count">(%s)</span>', 'Active <span class="count">(%s)</span>', $count ); break; case 'recently_activated': /* translators: %s: Number of plugins. */ $text = _n( 'Recently Active <span class="count">(%s)</span>', 'Recently Active <span class="count">(%s)</span>', $count ); break; case 'inactive': /* translators: %s: Number of plugins. */ $text = _n( 'Inactive <span class="count">(%s)</span>', 'Inactive <span class="count">(%s)</span>', $count ); break; case 'mustuse': /* translators: %s: Number of plugins. */ $text = _n( 'Must-Use <span class="count">(%s)</span>', 'Must-Use <span class="count">(%s)</span>', $count ); break; case 'dropins': /* translators: %s: Number of plugins. */ $text = _n( 'Drop-in <span class="count">(%s)</span>', 'Drop-ins <span class="count">(%s)</span>', $count ); break; case 'paused': /* translators: %s: Number of plugins. */ $text = _n( 'Paused <span class="count">(%s)</span>', 'Paused <span class="count">(%s)</span>', $count ); break; case 'upgrade': /* translators: %s: Number of plugins. */ $text = _n( 'Update Available <span class="count">(%s)</span>', 'Update Available <span class="count">(%s)</span>', $count ); break; case 'auto-update-enabled': /* translators: %s: Number of plugins. */ $text = _n( 'Auto-updates Enabled <span class="count">(%s)</span>', 'Auto-updates Enabled <span class="count">(%s)</span>', $count ); break; case 'auto-update-disabled': /* translators: %s: Number of plugins. */ $text = _n( 'Auto-updates Disabled <span class="count">(%s)</span>', 'Auto-updates Disabled <span class="count">(%s)</span>', $count ); break; } if ( 'search' !== $type ) { $status_links[ $type ] = array( 'url' => add_query_arg( 'plugin_status', $type, 'plugins.php' ), 'label' => sprintf( $text, number_format_i18n( $count ) ), 'current' => $type === $status, ); } } return $this->get_views_links( $status_links ); } /** * @global string $status * @return array */ protected function get_bulk_actions() { global $status; $actions = array(); if ( 'active' !== $status ) { $actions['activate-selected'] = $this->screen->in_admin( 'network' ) ? _x( 'Network Activate', 'plugin' ) : _x( 'Activate', 'plugin' ); } if ( 'inactive' !== $status && 'recent' !== $status ) { $actions['deactivate-selected'] = $this->screen->in_admin( 'network' ) ? _x( 'Network Deactivate', 'plugin' ) : _x( 'Deactivate', 'plugin' ); } if ( ! is_multisite() || $this->screen->in_admin( 'network' ) ) { if ( current_user_can( 'update_plugins' ) ) { $actions['update-selected'] = __( 'Update' ); } if ( current_user_can( 'delete_plugins' ) && ( 'active' !== $status ) ) { $actions['delete-selected'] = __( 'Delete' ); } if ( $this->show_autoupdates ) { if ( 'auto-update-enabled' !== $status ) { $actions['enable-auto-update-selected'] = __( 'Enable Auto-updates' ); } if ( 'auto-update-disabled' !== $status ) { $actions['disable-auto-update-selected'] = __( 'Disable Auto-updates' ); } } } return $actions; } /** * @global string $status * @param string $which */ public function bulk_actions( $which = '' ) { global $status; if ( in_array( $status, array( 'mustuse', 'dropins' ), true ) ) { return; } parent::bulk_actions( $which ); } /** * @global string $status * @param string $which */ protected function extra_tablenav( $which ) { global $status; if ( ! in_array( $status, array( 'recently_activated', 'mustuse', 'dropins' ), true ) ) { return; } echo '<div class="alignleft actions">'; if ( 'recently_activated' === $status ) { submit_button( __( 'Clear List' ), '', 'clear-recent-list', false ); } elseif ( 'top' === $which && 'mustuse' === $status ) { echo '<p>' . sprintf( /* translators: %s: mu-plugins directory name. */ __( 'Files in the %s directory are executed automatically.' ), '<code>' . str_replace( ABSPATH, '/', WPMU_PLUGIN_DIR ) . '</code>' ) . '</p>'; } elseif ( 'top' === $which && 'dropins' === $status ) { echo '<p>' . sprintf( /* translators: %s: wp-content directory name. */ __( 'Drop-ins are single files, found in the %s directory, that replace or enhance WordPress features in ways that are not possible for traditional plugins.' ), '<code>' . str_replace( ABSPATH, '', WP_CONTENT_DIR ) . '</code>' ) . '</p>'; } echo '</div>'; } /** * @return string */ public function current_action() { if ( isset( $_POST['clear-recent-list'] ) ) { return 'clear-recent-list'; } return parent::current_action(); } /** * @global string $status */ public function display_rows() { global $status; if ( is_multisite() && ! $this->screen->in_admin( 'network' ) && in_array( $status, array( 'mustuse', 'dropins' ), true ) ) { return; } foreach ( $this->items as $plugin_file => $plugin_data ) { $this->single_row( array( $plugin_file, $plugin_data ) ); } } /** * @global string $status * @global int $page * @global string $s * @global array $totals * * @param array $item */ public function single_row( $item ) { global $status, $page, $s, $totals; static $plugin_id_attrs = array(); list( $plugin_file, $plugin_data ) = $item; $plugin_slug = isset( $plugin_data['slug'] ) ? $plugin_data['slug'] : sanitize_title( $plugin_data['Name'] ); $plugin_id_attr = $plugin_slug; // Ensure the ID attribute is unique. $suffix = 2; while ( in_array( $plugin_id_attr, $plugin_id_attrs, true ) ) { $plugin_id_attr = "$plugin_slug-$suffix"; ++$suffix; } $plugin_id_attrs[] = $plugin_id_attr; $context = $status; $screen = $this->screen; // Pre-order. $actions = array( 'deactivate' => '', 'activate' => '', 'details' => '', 'delete' => '', ); // Do not restrict by default. $restrict_network_active = false; $restrict_network_only = false; $requires_php = isset( $plugin_data['RequiresPHP'] ) ? $plugin_data['RequiresPHP'] : null; $requires_wp = isset( $plugin_data['RequiresWP'] ) ? $plugin_data['RequiresWP'] : null; $compatible_php = is_php_version_compatible( $requires_php ); $compatible_wp = is_wp_version_compatible( $requires_wp ); $has_dependents = WP_Plugin_Dependencies::has_dependents( $plugin_file ); $has_active_dependents = WP_Plugin_Dependencies::has_active_dependents( $plugin_file ); $has_unmet_dependencies = WP_Plugin_Dependencies::has_unmet_dependencies( $plugin_file ); $has_circular_dependency = WP_Plugin_Dependencies::has_circular_dependency( $plugin_file ); if ( 'mustuse' === $context ) { $is_active = true; } elseif ( 'dropins' === $context ) { $dropins = _get_dropins(); $plugin_name = $plugin_file; if ( $plugin_file !== $plugin_data['Name'] ) { $plugin_name .= '<br />' . $plugin_data['Name']; } if ( true === ( $dropins[ $plugin_file ][1] ) ) { // Doesn't require a constant. $is_active = true; $description = '<p><strong>' . $dropins[ $plugin_file ][0] . '</strong></p>'; } elseif ( defined( $dropins[ $plugin_file ][1] ) && constant( $dropins[ $plugin_file ][1] ) ) { // Constant is true. $is_active = true; $description = '<p><strong>' . $dropins[ $plugin_file ][0] . '</strong></p>'; } else { $is_active = false; $description = '<p><strong>' . $dropins[ $plugin_file ][0] . ' <span class="error-message">' . __( 'Inactive:' ) . '</span></strong> ' . sprintf( /* translators: 1: Drop-in constant name, 2: wp-config.php */ __( 'Requires %1$s in %2$s file.' ), "<code>define('" . $dropins[ $plugin_file ][1] . "', true);</code>", '<code>wp-config.php</code>' ) . '</p>'; } if ( $plugin_data['Description'] ) { $description .= '<p>' . $plugin_data['Description'] . '</p>'; } } else { if ( $screen->in_admin( 'network' ) ) { $is_active = is_plugin_active_for_network( $plugin_file ); } else { $is_active = is_plugin_active( $plugin_file ); $restrict_network_active = ( is_multisite() && is_plugin_active_for_network( $plugin_file ) ); $restrict_network_only = ( is_multisite() && is_network_only_plugin( $plugin_file ) && ! $is_active ); } if ( $screen->in_admin( 'network' ) ) { if ( $is_active ) { if ( current_user_can( 'manage_network_plugins' ) ) { if ( $has_active_dependents ) { $actions['deactivate'] = __( 'Deactivate' ) . '<span class="screen-reader-text">' . __( 'You cannot deactivate this plugin as other plugins require it.' ) . '</span>'; } else { $deactivate_url = 'plugins.php?action=deactivate' . '&plugin=' . urlencode( $plugin_file ) . '&plugin_status=' . $context . '&paged=' . $page . '&s=' . $s; $actions['deactivate'] = sprintf( '<a href="%s" id="deactivate-%s" aria-label="%s">%s</a>', wp_nonce_url( $deactivate_url, 'deactivate-plugin_' . $plugin_file ), esc_attr( $plugin_id_attr ), /* translators: %s: Plugin name. */ esc_attr( sprintf( _x( 'Network Deactivate %s', 'plugin' ), $plugin_data['Name'] ) ), _x( 'Network Deactivate', 'plugin' ) ); } } } else { if ( current_user_can( 'manage_network_plugins' ) ) { if ( $compatible_php && $compatible_wp ) { if ( $has_unmet_dependencies ) { $actions['activate'] = _x( 'Network Activate', 'plugin' ) . '<span class="screen-reader-text">' . __( 'You cannot activate this plugin as it has unmet requirements.' ) . '</span>'; } else { $activate_url = 'plugins.php?action=activate' . '&plugin=' . urlencode( $plugin_file ) . '&plugin_status=' . $context . '&paged=' . $page . '&s=' . $s; $actions['activate'] = sprintf( '<a href="%s" id="activate-%s" class="edit" aria-label="%s">%s</a>', wp_nonce_url( $activate_url, 'activate-plugin_' . $plugin_file ), esc_attr( $plugin_id_attr ), /* translators: %s: Plugin name. */ esc_attr( sprintf( _x( 'Network Activate %s', 'plugin' ), $plugin_data['Name'] ) ), _x( 'Network Activate', 'plugin' ) ); } } else { $actions['activate'] = sprintf( '<span>%s</span>', _x( 'Cannot Activate', 'plugin' ) ); } } if ( current_user_can( 'delete_plugins' ) && ! is_plugin_active( $plugin_file ) ) { if ( $has_dependents && ! $has_circular_dependency ) { $actions['delete'] = __( 'Delete' ) . '<span class="screen-reader-text">' . __( 'You cannot delete this plugin as other plugins require it.' ) . '</span>'; } else { $delete_url = 'plugins.php?action=delete-selected' . '&checked[]=' . urlencode( $plugin_file ) . '&plugin_status=' . $context . '&paged=' . $page . '&s=' . $s; $actions['delete'] = sprintf( '<a href="%s" id="delete-%s" class="delete" aria-label="%s">%s</a>', wp_nonce_url( $delete_url, 'bulk-plugins' ), esc_attr( $plugin_id_attr ), /* translators: %s: Plugin name. */ esc_attr( sprintf( _x( 'Delete %s', 'plugin' ), $plugin_data['Name'] ) ), __( 'Delete' ) ); } } } } else { if ( $restrict_network_active ) { $actions = array( 'network_active' => __( 'Network Active' ), ); } elseif ( $restrict_network_only ) { $actions = array( 'network_only' => __( 'Network Only' ), ); } elseif ( $is_active ) { if ( current_user_can( 'deactivate_plugin', $plugin_file ) ) { if ( $has_active_dependents ) { $actions['deactivate'] = __( 'Deactivate' ) . '<span class="screen-reader-text">' . __( 'You cannot deactivate this plugin as other plugins depend on it.' ) . '</span>'; } else { $deactivate_url = 'plugins.php?action=deactivate' . '&plugin=' . urlencode( $plugin_file ) . '&plugin_status=' . $context . '&paged=' . $page . '&s=' . $s; $actions['deactivate'] = sprintf( '<a href="%s" id="deactivate-%s" aria-label="%s">%s</a>', wp_nonce_url( $deactivate_url, 'deactivate-plugin_' . $plugin_file ), esc_attr( $plugin_id_attr ), /* translators: %s: Plugin name. */ esc_attr( sprintf( _x( 'Deactivate %s', 'plugin' ), $plugin_data['Name'] ) ), __( 'Deactivate' ) ); } } if ( current_user_can( 'resume_plugin', $plugin_file ) && is_plugin_paused( $plugin_file ) ) { $resume_url = 'plugins.php?action=resume' . '&plugin=' . urlencode( $plugin_file ) . '&plugin_status=' . $context . '&paged=' . $page . '&s=' . $s; $actions['resume'] = sprintf( '<a href="%s" id="resume-%s" class="resume-link" aria-label="%s">%s</a>', wp_nonce_url( $resume_url, 'resume-plugin_' . $plugin_file ), esc_attr( $plugin_id_attr ), /* translators: %s: Plugin name. */ esc_attr( sprintf( _x( 'Resume %s', 'plugin' ), $plugin_data['Name'] ) ), __( 'Resume' ) ); } } else { if ( current_user_can( 'activate_plugin', $plugin_file ) ) { if ( $compatible_php && $compatible_wp ) { if ( $has_unmet_dependencies ) { $actions['activate'] = _x( 'Activate', 'plugin' ) . '<span class="screen-reader-text">' . __( 'You cannot activate this plugin as it has unmet requirements.' ) . '</span>'; } else { $activate_url = 'plugins.php?action=activate' . '&plugin=' . urlencode( $plugin_file ) . '&plugin_status=' . $context . '&paged=' . $page . '&s=' . $s; $actions['activate'] = sprintf( '<a href="%s" id="activate-%s" class="edit" aria-label="%s">%s</a>', wp_nonce_url( $activate_url, 'activate-plugin_' . $plugin_file ), esc_attr( $plugin_id_attr ), /* translators: %s: Plugin name. */ esc_attr( sprintf( _x( 'Activate %s', 'plugin' ), $plugin_data['Name'] ) ), _x( 'Activate', 'plugin' ) ); } } else { $actions['activate'] = sprintf( '<span>%s</span>', _x( 'Cannot Activate', 'plugin' ) ); } } if ( ! is_multisite() && current_user_can( 'delete_plugins' ) ) { if ( $has_dependents && ! $has_circular_dependency ) { $actions['delete'] = __( 'Delete' ) . '<span class="screen-reader-text">' . __( 'You cannot delete this plugin as other plugins require it.' ) . '</span>'; } else { $delete_url = 'plugins.php?action=delete-selected' . '&checked[]=' . urlencode( $plugin_file ) . '&plugin_status=' . $context . '&paged=' . $page . '&s=' . $s; $actions['delete'] = sprintf( '<a href="%s" id="delete-%s" class="delete" aria-label="%s">%s</a>', wp_nonce_url( $delete_url, 'bulk-plugins' ), esc_attr( $plugin_id_attr ), /* translators: %s: Plugin name. */ esc_attr( sprintf( _x( 'Delete %s', 'plugin' ), $plugin_data['Name'] ) ), __( 'Delete' ) ); } } } // End if $is_active. } // End if $screen->in_admin( 'network' ). } // End if $context. $actions = array_filter( $actions ); if ( $screen->in_admin( 'network' ) ) { /** * Filters the action links displayed for each plugin in the Network Admin Plugins list table. * * @since 3.1.0 * * @param string[] $actions An array of plugin action links. By default this can include * 'activate', 'deactivate', and 'delete'. * @param string $plugin_file Path to the plugin file relative to the plugins directory. * @param array $plugin_data An array of plugin data. See get_plugin_data() * and the {@see 'plugin_row_meta'} filter for the list * of possible values. * @param string $context The plugin context. By default this can include 'all', * 'active', 'inactive', 'recently_activated', 'upgrade', * 'mustuse', 'dropins', and 'search'. */ $actions = apply_filters( 'network_admin_plugin_action_links', $actions, $plugin_file, $plugin_data, $context ); /** * Filters the list of action links displayed for a specific plugin in the Network Admin Plugins list table. * * The dynamic portion of the hook name, `$plugin_file`, refers to the path * to the plugin file, relative to the plugins directory. * * @since 3.1.0 * * @param string[] $actions An array of plugin action links. By default this can include * 'activate', 'deactivate', and 'delete'. * @param string $plugin_file Path to the plugin file relative to the plugins directory. * @param array $plugin_data An array of plugin data. See get_plugin_data() * and the {@see 'plugin_row_meta'} filter for the list * of possible values. * @param string $context The plugin context. By default this can include 'all', * 'active', 'inactive', 'recently_activated', 'upgrade', * 'mustuse', 'dropins', and 'search'. */ $actions = apply_filters( "network_admin_plugin_action_links_{$plugin_file}", $actions, $plugin_file, $plugin_data, $context ); } else { /** * Filters the action links displayed for each plugin in the Plugins list table. * * @since 2.5.0 * @since 2.6.0 The `$context` parameter was added. * @since 4.9.0 The 'Edit' link was removed from the list of action links. * * @param string[] $actions An array of plugin action links. By default this can include * 'activate', 'deactivate', and 'delete'. With Multisite active * this can also include 'network_active' and 'network_only' items. * @param string $plugin_file Path to the plugin file relative to the plugins directory. * @param array $plugin_data An array of plugin data. See get_plugin_data() * and the {@see 'plugin_row_meta'} filter for the list * of possible values. * @param string $context The plugin context. By default this can include 'all', * 'active', 'inactive', 'recently_activated', 'upgrade', * 'mustuse', 'dropins', and 'search'. */ $actions = apply_filters( 'plugin_action_links', $actions, $plugin_file, $plugin_data, $context ); /** * Filters the list of action links displayed for a specific plugin in the Plugins list table. * * The dynamic portion of the hook name, `$plugin_file`, refers to the path * to the plugin file, relative to the plugins directory. * * @since 2.7.0 * @since 4.9.0 The 'Edit' link was removed from the list of action links. * * @param string[] $actions An array of plugin action links. By default this can include * 'activate', 'deactivate', and 'delete'. With Multisite active * this can also include 'network_active' and 'network_only' items. * @param string $plugin_file Path to the plugin file relative to the plugins directory. * @param array $plugin_data An array of plugin data. See get_plugin_data() * and the {@see 'plugin_row_meta'} filter for the list * of possible values. * @param string $context The plugin context. By default this can include 'all', * 'active', 'inactive', 'recently_activated', 'upgrade', * 'mustuse', 'dropins', and 'search'. */ $actions = apply_filters( "plugin_action_links_{$plugin_file}", $actions, $plugin_file, $plugin_data, $context ); } $class = $is_active ? 'active' : 'inactive'; $checkbox_id = 'checkbox_' . md5( $plugin_file ); $disabled = ''; if ( $has_dependents || $has_unmet_dependencies ) { $disabled = 'disabled'; } if ( $restrict_network_active || $restrict_network_only || in_array( $status, array( 'mustuse', 'dropins' ), true ) || ! $compatible_php ) { $checkbox = ''; } else { $checkbox = sprintf( '<label class="label-covers-full-cell" for="%1$s">' . '<span class="screen-reader-text">%2$s</span></label>' . '<input type="checkbox" name="checked[]" value="%3$s" id="%1$s" ' . $disabled . '/>', $checkbox_id, /* translators: Hidden accessibility text. %s: Plugin name. */ sprintf( __( 'Select %s' ), $plugin_data['Name'] ), esc_attr( $plugin_file ) ); } if ( 'dropins' !== $context ) { $description = '<p>' . ( $plugin_data['Description'] ? $plugin_data['Description'] : ' ' ) . '</p>'; $plugin_name = $plugin_data['Name']; } if ( ! empty( $totals['upgrade'] ) && ! empty( $plugin_data['update'] ) || ! $compatible_php || ! $compatible_wp ) { $class .= ' update'; } $paused = ! $screen->in_admin( 'network' ) && is_plugin_paused( $plugin_file ); if ( $paused ) { $class .= ' paused'; } if ( is_uninstallable_plugin( $plugin_file ) ) { $class .= ' is-uninstallable'; } printf( '<tr class="%s" data-slug="%s" data-plugin="%s">', esc_attr( $class ), esc_attr( $plugin_slug ), esc_attr( $plugin_file ) ); list( $columns, $hidden, $sortable, $primary ) = $this->get_column_info(); $auto_updates = (array) get_site_option( 'auto_update_plugins', array() ); foreach ( $columns as $column_name => $column_display_name ) { $extra_classes = ''; if ( in_array( $column_name, $hidden, true ) ) { $extra_classes = ' hidden'; } switch ( $column_name ) { case 'cb': echo "<th scope='row' class='check-column'>$checkbox</th>"; break; case 'name': echo "<td class='plugin-title column-primary'><strong>$plugin_name</strong>"; echo $this->row_actions( $actions, true ); echo '</td>'; break; case 'description': $classes = 'column-description desc'; echo "<td class='$classes{$extra_classes}'> <div class='plugin-description'>$description</div> <div class='$class second plugin-version-author-uri'>"; $plugin_meta = array(); if ( ! empty( $plugin_data['Version'] ) ) { /* translators: %s: Plugin version number. */ $plugin_meta[] = sprintf( __( 'Version %s' ), $plugin_data['Version'] ); } if ( ! empty( $plugin_data['Author'] ) ) { $author = $plugin_data['Author']; if ( ! empty( $plugin_data['AuthorURI'] ) ) { $author = '<a href="' . $plugin_data['AuthorURI'] . '">' . $plugin_data['Author'] . '</a>'; } /* translators: %s: Plugin author name. */ $plugin_meta[] = sprintf( __( 'By %s' ), $author ); } // Details link using API info, if available. if ( isset( $plugin_data['slug'] ) && current_user_can( 'install_plugins' ) ) { $plugin_meta[] = sprintf( '<a href="%s" class="thickbox open-plugin-details-modal" aria-label="%s" data-title="%s">%s</a>', esc_url( network_admin_url( 'plugin-install.php?tab=plugin-information&plugin=' . $plugin_data['slug'] . '&TB_iframe=true&width=600&height=550' ) ), /* translators: %s: Plugin name. */ esc_attr( sprintf( __( 'More information about %s' ), $plugin_name ) ), esc_attr( $plugin_name ), __( 'View details' ) ); } elseif ( ! empty( $plugin_data['PluginURI'] ) ) { /* translators: %s: Plugin name. */ $aria_label = sprintf( __( 'Visit plugin site for %s' ), $plugin_name ); $plugin_meta[] = sprintf( '<a href="%s" aria-label="%s">%s</a>', esc_url( $plugin_data['PluginURI'] ), esc_attr( $aria_label ), __( 'Visit plugin site' ) ); } /** * Filters the array of row meta for each plugin in the Plugins list table. * * @since 2.8.0 * * @param string[] $plugin_meta An array of the plugin's metadata, including * the version, author, author URI, and plugin URI. * @param string $plugin_file Path to the plugin file relative to the plugins directory. * @param array $plugin_data { * An array of plugin data. * * @type string $id Plugin ID, e.g. `w.org/plugins/[plugin-name]`. * @type string $slug Plugin slug. * @type string $plugin Plugin basename. * @type string $new_version New plugin version. * @type string $url Plugin URL. * @type string $package Plugin update package URL. * @type string[] $icons An array of plugin icon URLs. * @type string[] $banners An array of plugin banner URLs. * @type string[] $banners_rtl An array of plugin RTL banner URLs. * @type string $requires The version of WordPress which the plugin requires. * @type string $tested The version of WordPress the plugin is tested against. * @type string $requires_php The version of PHP which the plugin requires. * @type string $upgrade_notice The upgrade notice for the new plugin version. * @type bool $update-supported Whether the plugin supports updates. * @type string $Name The human-readable name of the plugin. * @type string $PluginURI Plugin URI. * @type string $Version Plugin version. * @type string $Description Plugin description. * @type string $Author Plugin author. * @type string $AuthorURI Plugin author URI. * @type string $TextDomain Plugin textdomain. * @type string $DomainPath Relative path to the plugin's .mo file(s). * @type bool $Network Whether the plugin can only be activated network-wide. * @type string $RequiresWP The version of WordPress which the plugin requires. * @type string $RequiresPHP The version of PHP which the plugin requires. * @type string $UpdateURI ID of the plugin for update purposes, should be a URI. * @type string $Title The human-readable title of the plugin. * @type string $AuthorName Plugin author's name. * @type bool $update Whether there's an available update. Default null. * } * @param string $status Status filter currently applied to the plugin list. Possible * values are: 'all', 'active', 'inactive', 'recently_activated', * 'upgrade', 'mustuse', 'dropins', 'search', 'paused', * 'auto-update-enabled', 'auto-update-disabled'. */ $plugin_meta = apply_filters( 'plugin_row_meta', $plugin_meta, $plugin_file, $plugin_data, $status ); echo implode( ' | ', $plugin_meta ); echo '</div>'; if ( $has_dependents ) { $this->add_dependents_to_dependency_plugin_row( $plugin_file ); } if ( WP_Plugin_Dependencies::has_dependencies( $plugin_file ) ) { $this->add_dependencies_to_dependent_plugin_row( $plugin_file ); } /** * Fires after plugin row meta. * * @since 6.5.0 * * @param string $plugin_file Refer to {@see 'plugin_row_meta'} filter. * @param array $plugin_data Refer to {@see 'plugin_row_meta'} filter. */ do_action( 'after_plugin_row_meta', $plugin_file, $plugin_data ); if ( $paused ) { $notice_text = __( 'This plugin failed to load properly and is paused during recovery mode.' ); printf( '<p><span class="dashicons dashicons-warning"></span> <strong>%s</strong></p>', $notice_text ); $error = wp_get_plugin_error( $plugin_file ); if ( false !== $error ) { printf( '<div class="error-display"><p>%s</p></div>', wp_get_extension_error_description( $error ) ); } } echo '</td>'; break; case 'auto-updates': if ( ! $this->show_autoupdates || in_array( $status, array( 'mustuse', 'dropins' ), true ) ) { break; } echo "<td class='column-auto-updates{$extra_classes}'>"; $html = array(); if ( isset( $plugin_data['auto-update-forced'] ) ) { if ( $plugin_data['auto-update-forced'] ) { // Forced on. $text = __( 'Auto-updates enabled' ); } else { $text = __( 'Auto-updates disabled' ); } $action = 'unavailable'; $time_class = ' hidden'; } elseif ( empty( $plugin_data['update-supported'] ) ) { $text = ''; $action = 'unavailable'; $time_class = ' hidden'; } elseif ( in_array( $plugin_file, $auto_updates, true ) ) { $text = __( 'Disable auto-updates' ); $action = 'disable'; $time_class = ''; } else { $text = __( 'Enable auto-updates' ); $action = 'enable'; $time_class = ' hidden'; } $query_args = array( 'action' => "{$action}-auto-update", 'plugin' => $plugin_file, 'paged' => $page, 'plugin_status' => $status, ); $url = add_query_arg( $query_args, 'plugins.php' ); if ( 'unavailable' === $action ) { $html[] = '<span class="label">' . $text . '</span>'; } else { $html[] = sprintf( '<a href="%s" class="toggle-auto-update aria-button-if-js" data-wp-action="%s">', wp_nonce_url( $url, 'updates' ), $action ); $html[] = '<span class="dashicons dashicons-update spin hidden" aria-hidden="true"></span>'; $html[] = '<span class="label">' . $text . '</span>'; $html[] = '</a>'; } if ( ! empty( $plugin_data['update'] ) ) { $html[] = sprintf( '<div class="auto-update-time%s">%s</div>', $time_class, wp_get_auto_update_message() ); } $html = implode( '', $html ); /** * Filters the HTML of the auto-updates setting for each plugin in the Plugins list table. * * @since 5.5.0 * * @param string $html The HTML of the plugin's auto-update column content, * including toggle auto-update action links and * time to next update. * @param string $plugin_file Path to the plugin file relative to the plugins directory. * @param array $plugin_data An array of plugin data. See get_plugin_data() * and the {@see 'plugin_row_meta'} filter for the list * of possible values. */ echo apply_filters( 'plugin_auto_update_setting_html', $html, $plugin_file, $plugin_data ); wp_admin_notice( '', array( 'type' => 'error', 'additional_classes' => array( 'notice-alt', 'inline', 'hidden' ), ) ); echo '</td>'; break; default: $classes = "$column_name column-$column_name $class"; echo "<td class='$classes{$extra_classes}'>"; /** * Fires inside each custom column of the Plugins list table. * * @since 3.1.0 * * @param string $column_name Name of the column. * @param string $plugin_file Path to the plugin file relative to the plugins directory. * @param array $plugin_data An array of plugin data. See get_plugin_data() * and the {@see 'plugin_row_meta'} filter for the list * of possible values. */ do_action( 'manage_plugins_custom_column', $column_name, $plugin_file, $plugin_data ); echo '</td>'; } } echo '</tr>'; if ( ! $compatible_php || ! $compatible_wp ) { printf( '<tr class="plugin-update-tr"><td colspan="%s" class="plugin-update colspanchange">', esc_attr( $this->get_column_count() ) ); $incompatible_message = ''; if ( ! $compatible_php && ! $compatible_wp ) { $incompatible_message .= __( 'This plugin does not work with your versions of WordPress and PHP.' ); if ( current_user_can( 'update_core' ) && current_user_can( 'update_php' ) ) { $incompatible_message .= sprintf( /* translators: 1: URL to WordPress Updates screen, 2: URL to Update PHP page. */ ' ' . __( '<a href="%1$s">Please update WordPress</a>, and then <a href="%2$s">learn more about updating PHP</a>.' ), self_admin_url( 'update-core.php' ), esc_url( wp_get_update_php_url() ) ); $incompatible_message .= wp_update_php_annotation( '</p><p><em>', '</em>', false ); } elseif ( current_user_can( 'update_core' ) ) { $incompatible_message .= sprintf( /* translators: %s: URL to WordPress Updates screen. */ ' ' . __( '<a href="%s">Please update WordPress</a>.' ), self_admin_url( 'update-core.php' ) ); } elseif ( current_user_can( 'update_php' ) ) { $incompatible_message .= sprintf( /* translators: %s: URL to Update PHP page. */ ' ' . __( '<a href="%s">Learn more about updating PHP</a>.' ), esc_url( wp_get_update_php_url() ) ); $incompatible_message .= wp_update_php_annotation( '</p><p><em>', '</em>', false ); } } elseif ( ! $compatible_wp ) { $incompatible_message .= __( 'This plugin does not work with your version of WordPress.' ); if ( current_user_can( 'update_core' ) ) { $incompatible_message .= sprintf( /* translators: %s: URL to WordPress Updates screen. */ ' ' . __( '<a href="%s">Please update WordPress</a>.' ), self_admin_url( 'update-core.php' ) ); } } elseif ( ! $compatible_php ) { $incompatible_message .= __( 'This plugin does not work with your version of PHP.' ); if ( current_user_can( 'update_php' ) ) { $incompatible_message .= sprintf( /* translators: %s: URL to Update PHP page. */ ' ' . __( '<a href="%s">Learn more about updating PHP</a>.' ), esc_url( wp_get_update_php_url() ) ); $incompatible_message .= wp_update_php_annotation( '</p><p><em>', '</em>', false ); } } wp_admin_notice( $incompatible_message, array( 'type' => 'error', 'additional_classes' => array( 'notice-alt', 'inline', 'update-message' ), ) ); echo '</td></tr>'; } /** * Fires after each row in the Plugins list table. * * @since 2.3.0 * @since 5.5.0 Added 'auto-update-enabled' and 'auto-update-disabled' * to possible values for `$status`. * * @param string $plugin_file Path to the plugin file relative to the plugins directory. * @param array $plugin_data An array of plugin data. See get_plugin_data() * and the {@see 'plugin_row_meta'} filter for the list * of possible values. * @param string $status Status filter currently applied to the plugin list. * Possible values are: 'all', 'active', 'inactive', * 'recently_activated', 'upgrade', 'mustuse', 'dropins', * 'search', 'paused', 'auto-update-enabled', 'auto-update-disabled'. */ do_action( 'after_plugin_row', $plugin_file, $plugin_data, $status ); /** * Fires after each specific row in the Plugins list table. * * The dynamic portion of the hook name, `$plugin_file`, refers to the path * to the plugin file, relative to the plugins directory. * * @since 2.7.0 * @since 5.5.0 Added 'auto-update-enabled' and 'auto-update-disabled' * to possible values for `$status`. * * @param string $plugin_file Path to the plugin file relative to the plugins directory. * @param array $plugin_data An array of plugin data. See get_plugin_data() * and the {@see 'plugin_row_meta'} filter for the list * of possible values. * @param string $status Status filter currently applied to the plugin list. * Possible values are: 'all', 'active', 'inactive', * 'recently_activated', 'upgrade', 'mustuse', 'dropins', * 'search', 'paused', 'auto-update-enabled', 'auto-update-disabled'. */ do_action( "after_plugin_row_{$plugin_file}", $plugin_file, $plugin_data, $status ); } /** * Gets the name of the primary column for this specific list table. * * @since 4.3.0 * * @return string Unalterable name for the primary column, in this case, 'name'. */ protected function get_primary_column_name() { return 'name'; } /** * Prints a list of other plugins that depend on the plugin. * * @since 6.5.0 * * @param string $dependency The dependency's filepath, relative to the plugins directory. */ protected function add_dependents_to_dependency_plugin_row( $dependency ) { $dependent_names = WP_Plugin_Dependencies::get_dependent_names( $dependency ); if ( empty( $dependent_names ) ) { return; } $dependency_note = __( 'Note: This plugin cannot be deactivated or deleted until the plugins that require it are deactivated or deleted.' ); $comma = wp_get_list_item_separator(); $required_by = sprintf( /* translators: %s: List of dependencies. */ __( '<strong>Required by:</strong> %s' ), implode( $comma, $dependent_names ) ); printf( '<div class="required-by"><p>%1$s</p><p>%2$s</p></div>', $required_by, $dependency_note ); } /** * Prints a list of other plugins that the plugin depends on. * * @since 6.5.0 * * @param string $dependent The dependent plugin's filepath, relative to the plugins directory. */ protected function add_dependencies_to_dependent_plugin_row( $dependent ) { $dependency_names = WP_Plugin_Dependencies::get_dependency_names( $dependent ); if ( array() === $dependency_names ) { return; } $links = array(); foreach ( $dependency_names as $slug => $name ) { $links[] = $this->get_dependency_view_details_link( $name, $slug ); } $is_active = is_multisite() ? is_plugin_active_for_network( $dependent ) : is_plugin_active( $dependent ); $comma = wp_get_list_item_separator(); $requires = sprintf( /* translators: %s: List of dependency names. */ __( '<strong>Requires:</strong> %s' ), implode( $comma, $links ) ); $notice = ''; $error_message = ''; if ( WP_Plugin_Dependencies::has_unmet_dependencies( $dependent ) ) { if ( $is_active ) { $error_message = __( 'This plugin is active but may not function correctly because required plugins are missing or inactive.' ); } else { $error_message = __( 'This plugin cannot be activated because required plugins are missing or inactive.' ); } $notice = wp_get_admin_notice( $error_message, array( 'type' => 'error', 'additional_classes' => array( 'inline', 'notice-alt' ), ) ); } printf( '<div class="requires"><p>%1$s</p><p>%2$s</p></div>', $requires, $notice ); } /** * Returns a 'View details' like link for a dependency. * * @since 6.5.0 * * @param string $name The dependency's name. * @param string $slug The dependency's slug. * @return string A 'View details' link for the dependency. */ protected function get_dependency_view_details_link( $name, $slug ) { $dependency_data = WP_Plugin_Dependencies::get_dependency_data( $slug ); if ( false === $dependency_data || $name === $slug || $name !== $dependency_data['name'] || empty( $dependency_data['version'] ) ) { return $name; } return $this->get_view_details_link( $name, $slug ); } /** * Returns a 'View details' link for the plugin. * * @since 6.5.0 * * @param string $name The plugin's name. * @param string $slug The plugin's slug. * @return string A 'View details' link for the plugin. */ protected function get_view_details_link( $name, $slug ) { $url = add_query_arg( array( 'tab' => 'plugin-information', 'plugin' => $slug, 'TB_iframe' => 'true', 'width' => '600', 'height' => '550', ), network_admin_url( 'plugin-install.php' ) ); $name_attr = esc_attr( $name ); return sprintf( "<a href='%s' class='thickbox open-plugin-details-modal' aria-label='%s' data-title='%s'>%s</a>", esc_url( $url ), /* translators: %s: Plugin name. */ sprintf( __( 'More information about %s' ), $name_attr ), $name_attr, esc_html( $name ) ); } } includes/plugin.php 0000644 00000265326 15135536347 0010413 0 ustar 00 <?php /** * WordPress Plugin Administration API * * @package WordPress * @subpackage Administration */ /** * Parses the plugin contents to retrieve plugin's metadata. * * All plugin headers must be on their own line. Plugin description must not have * any newlines, otherwise only parts of the description will be displayed. * The below is formatted for printing. * * /* * Plugin Name: Name of the plugin. * Plugin URI: The home page of the plugin. * Description: Plugin description. * Author: Plugin author's name. * Author URI: Link to the author's website. * Version: Plugin version. * Text Domain: Optional. Unique identifier, should be same as the one used in * load_plugin_textdomain(). * Domain Path: Optional. Only useful if the translations are located in a * folder above the plugin's base path. For example, if .mo files are * located in the locale folder then Domain Path will be "/locale/" and * must have the first slash. Defaults to the base folder the plugin is * located in. * Network: Optional. Specify "Network: true" to require that a plugin is activated * across all sites in an installation. This will prevent a plugin from being * activated on a single site when Multisite is enabled. * Requires at least: Optional. Specify the minimum required WordPress version. * Requires PHP: Optional. Specify the minimum required PHP version. * * / # Remove the space to close comment. * * The first 8 KB of the file will be pulled in and if the plugin data is not * within that first 8 KB, then the plugin author should correct their plugin * and move the plugin data headers to the top. * * The plugin file is assumed to have permissions to allow for scripts to read * the file. This is not checked however and the file is only opened for * reading. * * @since 1.5.0 * @since 5.3.0 Added support for `Requires at least` and `Requires PHP` headers. * @since 5.8.0 Added support for `Update URI` header. * @since 6.5.0 Added support for `Requires Plugins` header. * * @param string $plugin_file Absolute path to the main plugin file. * @param bool $markup Optional. If the returned data should have HTML markup applied. * Default true. * @param bool $translate Optional. If the returned data should be translated. Default true. * @return array { * Plugin data. Values will be empty if not supplied by the plugin. * * @type string $Name Name of the plugin. Should be unique. * @type string $PluginURI Plugin URI. * @type string $Version Plugin version. * @type string $Description Plugin description. * @type string $Author Plugin author's name. * @type string $AuthorURI Plugin author's website address (if set). * @type string $TextDomain Plugin textdomain. * @type string $DomainPath Plugin's relative directory path to .mo files. * @type bool $Network Whether the plugin can only be activated network-wide. * @type string $RequiresWP Minimum required version of WordPress. * @type string $RequiresPHP Minimum required version of PHP. * @type string $UpdateURI ID of the plugin for update purposes, should be a URI. * @type string $RequiresPlugins Comma separated list of dot org plugin slugs. * @type string $Title Title of the plugin and link to the plugin's site (if set). * @type string $AuthorName Plugin author's name. * } */ function get_plugin_data( $plugin_file, $markup = true, $translate = true ) { $default_headers = array( 'Name' => 'Plugin Name', 'PluginURI' => 'Plugin URI', 'Version' => 'Version', 'Description' => 'Description', 'Author' => 'Author', 'AuthorURI' => 'Author URI', 'TextDomain' => 'Text Domain', 'DomainPath' => 'Domain Path', 'Network' => 'Network', 'RequiresWP' => 'Requires at least', 'RequiresPHP' => 'Requires PHP', 'UpdateURI' => 'Update URI', 'RequiresPlugins' => 'Requires Plugins', // Site Wide Only is deprecated in favor of Network. '_sitewide' => 'Site Wide Only', ); $plugin_data = get_file_data( $plugin_file, $default_headers, 'plugin' ); // Site Wide Only is the old header for Network. if ( ! $plugin_data['Network'] && $plugin_data['_sitewide'] ) { /* translators: 1: Site Wide Only: true, 2: Network: true */ _deprecated_argument( __FUNCTION__, '3.0.0', sprintf( __( 'The %1$s plugin header is deprecated. Use %2$s instead.' ), '<code>Site Wide Only: true</code>', '<code>Network: true</code>' ) ); $plugin_data['Network'] = $plugin_data['_sitewide']; } $plugin_data['Network'] = ( 'true' === strtolower( $plugin_data['Network'] ) ); unset( $plugin_data['_sitewide'] ); // If no text domain is defined fall back to the plugin slug. if ( ! $plugin_data['TextDomain'] ) { $plugin_slug = dirname( plugin_basename( $plugin_file ) ); if ( '.' !== $plugin_slug && ! str_contains( $plugin_slug, '/' ) ) { $plugin_data['TextDomain'] = $plugin_slug; } } if ( $markup || $translate ) { $plugin_data = _get_plugin_data_markup_translate( $plugin_file, $plugin_data, $markup, $translate ); } else { $plugin_data['Title'] = $plugin_data['Name']; $plugin_data['AuthorName'] = $plugin_data['Author']; } return $plugin_data; } /** * Sanitizes plugin data, optionally adds markup, optionally translates. * * @since 2.7.0 * * @see get_plugin_data() * * @access private * * @param string $plugin_file Path to the main plugin file. * @param array $plugin_data An array of plugin data. See get_plugin_data(). * @param bool $markup Optional. If the returned data should have HTML markup applied. * Default true. * @param bool $translate Optional. If the returned data should be translated. Default true. * @return array Plugin data. Values will be empty if not supplied by the plugin. * See get_plugin_data() for the list of possible values. */ function _get_plugin_data_markup_translate( $plugin_file, $plugin_data, $markup = true, $translate = true ) { // Sanitize the plugin filename to a WP_PLUGIN_DIR relative path. $plugin_file = plugin_basename( $plugin_file ); // Translate fields. if ( $translate ) { $textdomain = $plugin_data['TextDomain']; if ( $textdomain ) { if ( ! is_textdomain_loaded( $textdomain ) ) { if ( $plugin_data['DomainPath'] ) { load_plugin_textdomain( $textdomain, false, dirname( $plugin_file ) . $plugin_data['DomainPath'] ); } else { load_plugin_textdomain( $textdomain, false, dirname( $plugin_file ) ); } } } elseif ( 'hello.php' === basename( $plugin_file ) ) { $textdomain = 'default'; } if ( $textdomain ) { foreach ( array( 'Name', 'PluginURI', 'Description', 'Author', 'AuthorURI', 'Version' ) as $field ) { if ( ! empty( $plugin_data[ $field ] ) ) { // phpcs:ignore WordPress.WP.I18n.LowLevelTranslationFunction,WordPress.WP.I18n.NonSingularStringLiteralText,WordPress.WP.I18n.NonSingularStringLiteralDomain $plugin_data[ $field ] = translate( $plugin_data[ $field ], $textdomain ); } } } } // Sanitize fields. $allowed_tags_in_links = array( 'abbr' => array( 'title' => true ), 'acronym' => array( 'title' => true ), 'code' => true, 'em' => true, 'strong' => true, ); $allowed_tags = $allowed_tags_in_links; $allowed_tags['a'] = array( 'href' => true, 'title' => true, ); /* * Name is marked up inside <a> tags. Don't allow these. * Author is too, but some plugins have used <a> here (omitting Author URI). */ $plugin_data['Name'] = wp_kses( $plugin_data['Name'], $allowed_tags_in_links ); $plugin_data['Author'] = wp_kses( $plugin_data['Author'], $allowed_tags ); $plugin_data['Description'] = wp_kses( $plugin_data['Description'], $allowed_tags ); $plugin_data['Version'] = wp_kses( $plugin_data['Version'], $allowed_tags ); $plugin_data['PluginURI'] = esc_url( $plugin_data['PluginURI'] ); $plugin_data['AuthorURI'] = esc_url( $plugin_data['AuthorURI'] ); $plugin_data['Title'] = $plugin_data['Name']; $plugin_data['AuthorName'] = $plugin_data['Author']; // Apply markup. if ( $markup ) { if ( $plugin_data['PluginURI'] && $plugin_data['Name'] ) { $plugin_data['Title'] = '<a href="' . $plugin_data['PluginURI'] . '">' . $plugin_data['Name'] . '</a>'; } if ( $plugin_data['AuthorURI'] && $plugin_data['Author'] ) { $plugin_data['Author'] = '<a href="' . $plugin_data['AuthorURI'] . '">' . $plugin_data['Author'] . '</a>'; } $plugin_data['Description'] = wptexturize( $plugin_data['Description'] ); if ( $plugin_data['Author'] ) { $plugin_data['Description'] .= sprintf( /* translators: %s: Plugin author. */ ' <cite>' . __( 'By %s.' ) . '</cite>', $plugin_data['Author'] ); } } return $plugin_data; } /** * Gets a list of a plugin's files. * * @since 2.8.0 * * @param string $plugin Path to the plugin file relative to the plugins directory. * @return string[] Array of file names relative to the plugin root. */ function get_plugin_files( $plugin ) { $plugin_file = WP_PLUGIN_DIR . '/' . $plugin; $dir = dirname( $plugin_file ); $plugin_files = array( plugin_basename( $plugin_file ) ); if ( is_dir( $dir ) && WP_PLUGIN_DIR !== $dir ) { /** * Filters the array of excluded directories and files while scanning the folder. * * @since 4.9.0 * * @param string[] $exclusions Array of excluded directories and files. */ $exclusions = (array) apply_filters( 'plugin_files_exclusions', array( 'CVS', 'node_modules', 'vendor', 'bower_components' ) ); $list_files = list_files( $dir, 100, $exclusions ); $list_files = array_map( 'plugin_basename', $list_files ); $plugin_files = array_merge( $plugin_files, $list_files ); $plugin_files = array_values( array_unique( $plugin_files ) ); } return $plugin_files; } /** * Checks the plugins directory and retrieve all plugin files with plugin data. * * WordPress only supports plugin files in the base plugins directory * (wp-content/plugins) and in one directory above the plugins directory * (wp-content/plugins/my-plugin). The file it looks for has the plugin data * and must be found in those two locations. It is recommended to keep your * plugin files in their own directories. * * The file with the plugin data is the file that will be included and therefore * needs to have the main execution for the plugin. This does not mean * everything must be contained in the file and it is recommended that the file * be split for maintainability. Keep everything in one file for extreme * optimization purposes. * * @since 1.5.0 * * @param string $plugin_folder Optional. Relative path to single plugin folder. * @return array[] Array of arrays of plugin data, keyed by plugin file name. See get_plugin_data(). */ function get_plugins( $plugin_folder = '' ) { $cache_plugins = wp_cache_get( 'plugins', 'plugins' ); if ( ! $cache_plugins ) { $cache_plugins = array(); } if ( isset( $cache_plugins[ $plugin_folder ] ) ) { return $cache_plugins[ $plugin_folder ]; } $wp_plugins = array(); $plugin_root = WP_PLUGIN_DIR; if ( ! empty( $plugin_folder ) ) { $plugin_root .= $plugin_folder; } // Files in wp-content/plugins directory. $plugins_dir = @opendir( $plugin_root ); $plugin_files = array(); if ( $plugins_dir ) { while ( ( $file = readdir( $plugins_dir ) ) !== false ) { if ( str_starts_with( $file, '.' ) ) { continue; } if ( is_dir( $plugin_root . '/' . $file ) ) { $plugins_subdir = @opendir( $plugin_root . '/' . $file ); if ( $plugins_subdir ) { while ( ( $subfile = readdir( $plugins_subdir ) ) !== false ) { if ( str_starts_with( $subfile, '.' ) ) { continue; } if ( str_ends_with( $subfile, '.php' ) ) { $plugin_files[] = "$file/$subfile"; } } closedir( $plugins_subdir ); } } else { if ( str_ends_with( $file, '.php' ) ) { $plugin_files[] = $file; } } } closedir( $plugins_dir ); } if ( empty( $plugin_files ) ) { return $wp_plugins; } foreach ( $plugin_files as $plugin_file ) { if ( ! is_readable( "$plugin_root/$plugin_file" ) ) { continue; } // Do not apply markup/translate as it will be cached. $plugin_data = get_plugin_data( "$plugin_root/$plugin_file", false, false ); if ( empty( $plugin_data['Name'] ) ) { continue; } $wp_plugins[ plugin_basename( $plugin_file ) ] = $plugin_data; } uasort( $wp_plugins, '_sort_uname_callback' ); $cache_plugins[ $plugin_folder ] = $wp_plugins; wp_cache_set( 'plugins', $cache_plugins, 'plugins' ); return $wp_plugins; } /** * Checks the mu-plugins directory and retrieve all mu-plugin files with any plugin data. * * WordPress only includes mu-plugin files in the base mu-plugins directory (wp-content/mu-plugins). * * @since 3.0.0 * @return array[] Array of arrays of mu-plugin data, keyed by plugin file name. See get_plugin_data(). */ function get_mu_plugins() { $wp_plugins = array(); $plugin_files = array(); if ( ! is_dir( WPMU_PLUGIN_DIR ) ) { return $wp_plugins; } // Files in wp-content/mu-plugins directory. $plugins_dir = @opendir( WPMU_PLUGIN_DIR ); if ( $plugins_dir ) { while ( ( $file = readdir( $plugins_dir ) ) !== false ) { if ( str_ends_with( $file, '.php' ) ) { $plugin_files[] = $file; } } } else { return $wp_plugins; } closedir( $plugins_dir ); if ( empty( $plugin_files ) ) { return $wp_plugins; } foreach ( $plugin_files as $plugin_file ) { if ( ! is_readable( WPMU_PLUGIN_DIR . "/$plugin_file" ) ) { continue; } // Do not apply markup/translate as it will be cached. $plugin_data = get_plugin_data( WPMU_PLUGIN_DIR . "/$plugin_file", false, false ); if ( empty( $plugin_data['Name'] ) ) { $plugin_data['Name'] = $plugin_file; } $wp_plugins[ $plugin_file ] = $plugin_data; } if ( isset( $wp_plugins['index.php'] ) && filesize( WPMU_PLUGIN_DIR . '/index.php' ) <= 30 ) { // Silence is golden. unset( $wp_plugins['index.php'] ); } uasort( $wp_plugins, '_sort_uname_callback' ); return $wp_plugins; } /** * Declares a callback to sort array by a 'Name' key. * * @since 3.1.0 * * @access private * * @param array $a array with 'Name' key. * @param array $b array with 'Name' key. * @return int Return 0 or 1 based on two string comparison. */ function _sort_uname_callback( $a, $b ) { return strnatcasecmp( $a['Name'], $b['Name'] ); } /** * Checks the wp-content directory and retrieve all drop-ins with any plugin data. * * @since 3.0.0 * @return array[] Array of arrays of dropin plugin data, keyed by plugin file name. See get_plugin_data(). */ function get_dropins() { $dropins = array(); $plugin_files = array(); $_dropins = _get_dropins(); // Files in wp-content directory. $plugins_dir = @opendir( WP_CONTENT_DIR ); if ( $plugins_dir ) { while ( ( $file = readdir( $plugins_dir ) ) !== false ) { if ( isset( $_dropins[ $file ] ) ) { $plugin_files[] = $file; } } } else { return $dropins; } closedir( $plugins_dir ); if ( empty( $plugin_files ) ) { return $dropins; } foreach ( $plugin_files as $plugin_file ) { if ( ! is_readable( WP_CONTENT_DIR . "/$plugin_file" ) ) { continue; } // Do not apply markup/translate as it will be cached. $plugin_data = get_plugin_data( WP_CONTENT_DIR . "/$plugin_file", false, false ); if ( empty( $plugin_data['Name'] ) ) { $plugin_data['Name'] = $plugin_file; } $dropins[ $plugin_file ] = $plugin_data; } uksort( $dropins, 'strnatcasecmp' ); return $dropins; } /** * Returns drop-in plugins that WordPress uses. * * Includes Multisite drop-ins only when is_multisite() * * @since 3.0.0 * * @return array[] { * Key is file name. The value is an array of data about the drop-in. * * @type array ...$0 { * Data about the drop-in. * * @type string $0 The purpose of the drop-in. * @type string|true $1 Name of the constant that must be true for the drop-in * to be used, or true if no constant is required. * } * } */ function _get_dropins() { $dropins = array( 'advanced-cache.php' => array( __( 'Advanced caching plugin.' ), 'WP_CACHE' ), // WP_CACHE 'db.php' => array( __( 'Custom database class.' ), true ), // Auto on load. 'db-error.php' => array( __( 'Custom database error message.' ), true ), // Auto on error. 'install.php' => array( __( 'Custom installation script.' ), true ), // Auto on installation. 'maintenance.php' => array( __( 'Custom maintenance message.' ), true ), // Auto on maintenance. 'object-cache.php' => array( __( 'External object cache.' ), true ), // Auto on load. 'php-error.php' => array( __( 'Custom PHP error message.' ), true ), // Auto on error. 'fatal-error-handler.php' => array( __( 'Custom PHP fatal error handler.' ), true ), // Auto on error. ); if ( is_multisite() ) { $dropins['sunrise.php'] = array( __( 'Executed before Multisite is loaded.' ), 'SUNRISE' ); // SUNRISE $dropins['blog-deleted.php'] = array( __( 'Custom site deleted message.' ), true ); // Auto on deleted blog. $dropins['blog-inactive.php'] = array( __( 'Custom site inactive message.' ), true ); // Auto on inactive blog. $dropins['blog-suspended.php'] = array( __( 'Custom site suspended message.' ), true ); // Auto on archived or spammed blog. } return $dropins; } /** * Determines whether a plugin is active. * * Only plugins installed in the plugins/ folder can be active. * * Plugins in the mu-plugins/ folder can't be "activated," so this function will * return false for those plugins. * * For more information on this and similar theme functions, check out * the {@link https://developer.wordpress.org/themes/basics/conditional-tags/ * Conditional Tags} article in the Theme Developer Handbook. * * @since 2.5.0 * * @param string $plugin Path to the plugin file relative to the plugins directory. * @return bool True, if in the active plugins list. False, not in the list. */ function is_plugin_active( $plugin ) { return in_array( $plugin, (array) get_option( 'active_plugins', array() ), true ) || is_plugin_active_for_network( $plugin ); } /** * Determines whether the plugin is inactive. * * Reverse of is_plugin_active(). Used as a callback. * * For more information on this and similar theme functions, check out * the {@link https://developer.wordpress.org/themes/basics/conditional-tags/ * Conditional Tags} article in the Theme Developer Handbook. * * @since 3.1.0 * * @see is_plugin_active() * * @param string $plugin Path to the plugin file relative to the plugins directory. * @return bool True if inactive. False if active. */ function is_plugin_inactive( $plugin ) { return ! is_plugin_active( $plugin ); } /** * Determines whether the plugin is active for the entire network. * * Only plugins installed in the plugins/ folder can be active. * * Plugins in the mu-plugins/ folder can't be "activated," so this function will * return false for those plugins. * * For more information on this and similar theme functions, check out * the {@link https://developer.wordpress.org/themes/basics/conditional-tags/ * Conditional Tags} article in the Theme Developer Handbook. * * @since 3.0.0 * * @param string $plugin Path to the plugin file relative to the plugins directory. * @return bool True if active for the network, otherwise false. */ function is_plugin_active_for_network( $plugin ) { if ( ! is_multisite() ) { return false; } $plugins = get_site_option( 'active_sitewide_plugins' ); if ( isset( $plugins[ $plugin ] ) ) { return true; } return false; } /** * Checks for "Network: true" in the plugin header to see if this should * be activated only as a network wide plugin. The plugin would also work * when Multisite is not enabled. * * Checks for "Site Wide Only: true" for backward compatibility. * * @since 3.0.0 * * @param string $plugin Path to the plugin file relative to the plugins directory. * @return bool True if plugin is network only, false otherwise. */ function is_network_only_plugin( $plugin ) { $plugin_data = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin ); if ( $plugin_data ) { return $plugin_data['Network']; } return false; } /** * Attempts activation of plugin in a "sandbox" and redirects on success. * * A plugin that is already activated will not attempt to be activated again. * * The way it works is by setting the redirection to the error before trying to * include the plugin file. If the plugin fails, then the redirection will not * be overwritten with the success message. Also, the options will not be * updated and the activation hook will not be called on plugin error. * * It should be noted that in no way the below code will actually prevent errors * within the file. The code should not be used elsewhere to replicate the * "sandbox", which uses redirection to work. * {@source 13 1} * * If any errors are found or text is outputted, then it will be captured to * ensure that the success redirection will update the error redirection. * * @since 2.5.0 * @since 5.2.0 Test for WordPress version and PHP version compatibility. * * @param string $plugin Path to the plugin file relative to the plugins directory. * @param string $redirect Optional. URL to redirect to. * @param bool $network_wide Optional. Whether to enable the plugin for all sites in the network * or just the current site. Multisite only. Default false. * @param bool $silent Optional. Whether to prevent calling activation hooks. Default false. * @return null|WP_Error Null on success, WP_Error on invalid file. */ function activate_plugin( $plugin, $redirect = '', $network_wide = false, $silent = false ) { $plugin = plugin_basename( trim( $plugin ) ); if ( is_multisite() && ( $network_wide || is_network_only_plugin( $plugin ) ) ) { $network_wide = true; $current = get_site_option( 'active_sitewide_plugins', array() ); $_GET['networkwide'] = 1; // Back compat for plugins looking for this value. } else { $current = get_option( 'active_plugins', array() ); } $valid = validate_plugin( $plugin ); if ( is_wp_error( $valid ) ) { return $valid; } $requirements = validate_plugin_requirements( $plugin ); if ( is_wp_error( $requirements ) ) { return $requirements; } if ( $network_wide && ! isset( $current[ $plugin ] ) || ! $network_wide && ! in_array( $plugin, $current, true ) ) { if ( ! empty( $redirect ) ) { // We'll override this later if the plugin can be included without fatal error. wp_redirect( add_query_arg( '_error_nonce', wp_create_nonce( 'plugin-activation-error_' . $plugin ), $redirect ) ); } ob_start(); // Load the plugin to test whether it throws any errors. plugin_sandbox_scrape( $plugin ); if ( ! $silent ) { /** * Fires before a plugin is activated. * * If a plugin is silently activated (such as during an update), * this hook does not fire. * * @since 2.9.0 * * @param string $plugin Path to the plugin file relative to the plugins directory. * @param bool $network_wide Whether to enable the plugin for all sites in the network * or just the current site. Multisite only. Default false. */ do_action( 'activate_plugin', $plugin, $network_wide ); /** * Fires as a specific plugin is being activated. * * This hook is the "activation" hook used internally by register_activation_hook(). * The dynamic portion of the hook name, `$plugin`, refers to the plugin basename. * * If a plugin is silently activated (such as during an update), this hook does not fire. * * @since 2.0.0 * * @param bool $network_wide Whether to enable the plugin for all sites in the network * or just the current site. Multisite only. Default false. */ do_action( "activate_{$plugin}", $network_wide ); } if ( $network_wide ) { $current = get_site_option( 'active_sitewide_plugins', array() ); $current[ $plugin ] = time(); update_site_option( 'active_sitewide_plugins', $current ); } else { $current = get_option( 'active_plugins', array() ); $current[] = $plugin; sort( $current ); update_option( 'active_plugins', $current ); } if ( ! $silent ) { /** * Fires after a plugin has been activated. * * If a plugin is silently activated (such as during an update), * this hook does not fire. * * @since 2.9.0 * * @param string $plugin Path to the plugin file relative to the plugins directory. * @param bool $network_wide Whether to enable the plugin for all sites in the network * or just the current site. Multisite only. Default false. */ do_action( 'activated_plugin', $plugin, $network_wide ); } if ( ob_get_length() > 0 ) { $output = ob_get_clean(); return new WP_Error( 'unexpected_output', __( 'The plugin generated unexpected output.' ), $output ); } ob_end_clean(); } return null; } /** * Deactivates a single plugin or multiple plugins. * * The deactivation hook is disabled by the plugin upgrader by using the $silent * parameter. * * @since 2.5.0 * * @param string|string[] $plugins Single plugin or list of plugins to deactivate. * @param bool $silent Prevent calling deactivation hooks. Default false. * @param bool|null $network_wide Whether to deactivate the plugin for all sites in the network. * A value of null will deactivate plugins for both the network * and the current site. Multisite only. Default null. */ function deactivate_plugins( $plugins, $silent = false, $network_wide = null ) { if ( is_multisite() ) { $network_current = get_site_option( 'active_sitewide_plugins', array() ); } $current = get_option( 'active_plugins', array() ); $do_blog = false; $do_network = false; foreach ( (array) $plugins as $plugin ) { $plugin = plugin_basename( trim( $plugin ) ); if ( ! is_plugin_active( $plugin ) ) { continue; } $network_deactivating = ( false !== $network_wide ) && is_plugin_active_for_network( $plugin ); if ( ! $silent ) { /** * Fires before a plugin is deactivated. * * If a plugin is silently deactivated (such as during an update), * this hook does not fire. * * @since 2.9.0 * * @param string $plugin Path to the plugin file relative to the plugins directory. * @param bool $network_deactivating Whether the plugin is deactivated for all sites in the network * or just the current site. Multisite only. Default false. */ do_action( 'deactivate_plugin', $plugin, $network_deactivating ); } if ( false !== $network_wide ) { if ( is_plugin_active_for_network( $plugin ) ) { $do_network = true; unset( $network_current[ $plugin ] ); } elseif ( $network_wide ) { continue; } } if ( true !== $network_wide ) { $key = array_search( $plugin, $current, true ); if ( false !== $key ) { $do_blog = true; unset( $current[ $key ] ); } } if ( $do_blog && wp_is_recovery_mode() ) { list( $extension ) = explode( '/', $plugin ); wp_paused_plugins()->delete( $extension ); } if ( ! $silent ) { /** * Fires as a specific plugin is being deactivated. * * This hook is the "deactivation" hook used internally by register_deactivation_hook(). * The dynamic portion of the hook name, `$plugin`, refers to the plugin basename. * * If a plugin is silently deactivated (such as during an update), this hook does not fire. * * @since 2.0.0 * * @param bool $network_deactivating Whether the plugin is deactivated for all sites in the network * or just the current site. Multisite only. Default false. */ do_action( "deactivate_{$plugin}", $network_deactivating ); /** * Fires after a plugin is deactivated. * * If a plugin is silently deactivated (such as during an update), * this hook does not fire. * * @since 2.9.0 * * @param string $plugin Path to the plugin file relative to the plugins directory. * @param bool $network_deactivating Whether the plugin is deactivated for all sites in the network * or just the current site. Multisite only. Default false. */ do_action( 'deactivated_plugin', $plugin, $network_deactivating ); } } if ( $do_blog ) { update_option( 'active_plugins', $current ); } if ( $do_network ) { update_site_option( 'active_sitewide_plugins', $network_current ); } } /** * Activates multiple plugins. * * When WP_Error is returned, it does not mean that one of the plugins had * errors. It means that one or more of the plugin file paths were invalid. * * The execution will be halted as soon as one of the plugins has an error. * * @since 2.6.0 * * @param string|string[] $plugins Single plugin or list of plugins to activate. * @param string $redirect Redirect to page after successful activation. * @param bool $network_wide Whether to enable the plugin for all sites in the network. * Default false. * @param bool $silent Prevent calling activation hooks. Default false. * @return true|WP_Error True when finished or WP_Error if there were errors during a plugin activation. */ function activate_plugins( $plugins, $redirect = '', $network_wide = false, $silent = false ) { if ( ! is_array( $plugins ) ) { $plugins = array( $plugins ); } $errors = array(); foreach ( $plugins as $plugin ) { if ( ! empty( $redirect ) ) { $redirect = add_query_arg( 'plugin', $plugin, $redirect ); } $result = activate_plugin( $plugin, $redirect, $network_wide, $silent ); if ( is_wp_error( $result ) ) { $errors[ $plugin ] = $result; } } if ( ! empty( $errors ) ) { return new WP_Error( 'plugins_invalid', __( 'One of the plugins is invalid.' ), $errors ); } return true; } /** * Removes directory and files of a plugin for a list of plugins. * * @since 2.6.0 * * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass. * * @param string[] $plugins List of plugin paths to delete, relative to the plugins directory. * @param string $deprecated Not used. * @return bool|null|WP_Error True on success, false if `$plugins` is empty, `WP_Error` on failure. * `null` if filesystem credentials are required to proceed. */ function delete_plugins( $plugins, $deprecated = '' ) { global $wp_filesystem; if ( empty( $plugins ) ) { return false; } $checked = array(); foreach ( $plugins as $plugin ) { $checked[] = 'checked[]=' . $plugin; } $url = wp_nonce_url( 'plugins.php?action=delete-selected&verify-delete=1&' . implode( '&', $checked ), 'bulk-plugins' ); ob_start(); $credentials = request_filesystem_credentials( $url ); $data = ob_get_clean(); if ( false === $credentials ) { if ( ! empty( $data ) ) { require_once ABSPATH . 'wp-admin/admin-header.php'; echo $data; require_once ABSPATH . 'wp-admin/admin-footer.php'; exit; } return; } if ( ! WP_Filesystem( $credentials ) ) { ob_start(); // Failed to connect. Error and request again. request_filesystem_credentials( $url, '', true ); $data = ob_get_clean(); if ( ! empty( $data ) ) { require_once ABSPATH . 'wp-admin/admin-header.php'; echo $data; require_once ABSPATH . 'wp-admin/admin-footer.php'; exit; } return; } if ( ! is_object( $wp_filesystem ) ) { return new WP_Error( 'fs_unavailable', __( 'Could not access filesystem.' ) ); } if ( is_wp_error( $wp_filesystem->errors ) && $wp_filesystem->errors->has_errors() ) { return new WP_Error( 'fs_error', __( 'Filesystem error.' ), $wp_filesystem->errors ); } // Get the base plugin folder. $plugins_dir = $wp_filesystem->wp_plugins_dir(); if ( empty( $plugins_dir ) ) { return new WP_Error( 'fs_no_plugins_dir', __( 'Unable to locate WordPress plugin directory.' ) ); } $plugins_dir = trailingslashit( $plugins_dir ); $plugin_translations = wp_get_installed_translations( 'plugins' ); $errors = array(); foreach ( $plugins as $plugin_file ) { // Run Uninstall hook. if ( is_uninstallable_plugin( $plugin_file ) ) { uninstall_plugin( $plugin_file ); } /** * Fires immediately before a plugin deletion attempt. * * @since 4.4.0 * * @param string $plugin_file Path to the plugin file relative to the plugins directory. */ do_action( 'delete_plugin', $plugin_file ); $this_plugin_dir = trailingslashit( dirname( $plugins_dir . $plugin_file ) ); /* * If plugin is in its own directory, recursively delete the directory. * Base check on if plugin includes directory separator AND that it's not the root plugin folder. */ if ( strpos( $plugin_file, '/' ) && $this_plugin_dir !== $plugins_dir ) { $deleted = $wp_filesystem->delete( $this_plugin_dir, true ); } else { $deleted = $wp_filesystem->delete( $plugins_dir . $plugin_file ); } /** * Fires immediately after a plugin deletion attempt. * * @since 4.4.0 * * @param string $plugin_file Path to the plugin file relative to the plugins directory. * @param bool $deleted Whether the plugin deletion was successful. */ do_action( 'deleted_plugin', $plugin_file, $deleted ); if ( ! $deleted ) { $errors[] = $plugin_file; continue; } $plugin_slug = dirname( $plugin_file ); if ( 'hello.php' === $plugin_file ) { $plugin_slug = 'hello-dolly'; } // Remove language files, silently. if ( '.' !== $plugin_slug && ! empty( $plugin_translations[ $plugin_slug ] ) ) { $translations = $plugin_translations[ $plugin_slug ]; foreach ( $translations as $translation => $data ) { $wp_filesystem->delete( WP_LANG_DIR . '/plugins/' . $plugin_slug . '-' . $translation . '.po' ); $wp_filesystem->delete( WP_LANG_DIR . '/plugins/' . $plugin_slug . '-' . $translation . '.mo' ); $wp_filesystem->delete( WP_LANG_DIR . '/plugins/' . $plugin_slug . '-' . $translation . '.l10n.php' ); $json_translation_files = glob( WP_LANG_DIR . '/plugins/' . $plugin_slug . '-' . $translation . '-*.json' ); if ( $json_translation_files ) { array_map( array( $wp_filesystem, 'delete' ), $json_translation_files ); } } } } // Remove deleted plugins from the plugin updates list. $current = get_site_transient( 'update_plugins' ); if ( $current ) { // Don't remove the plugins that weren't deleted. $deleted = array_diff( $plugins, $errors ); foreach ( $deleted as $plugin_file ) { unset( $current->response[ $plugin_file ] ); } set_site_transient( 'update_plugins', $current ); } if ( ! empty( $errors ) ) { if ( 1 === count( $errors ) ) { /* translators: %s: Plugin filename. */ $message = __( 'Could not fully remove the plugin %s.' ); } else { /* translators: %s: Comma-separated list of plugin filenames. */ $message = __( 'Could not fully remove the plugins %s.' ); } return new WP_Error( 'could_not_remove_plugin', sprintf( $message, implode( ', ', $errors ) ) ); } return true; } /** * Validates active plugins. * * Validate all active plugins, deactivates invalid and * returns an array of deactivated ones. * * @since 2.5.0 * @return WP_Error[] Array of plugin errors keyed by plugin file name. */ function validate_active_plugins() { $plugins = get_option( 'active_plugins', array() ); // Validate vartype: array. if ( ! is_array( $plugins ) ) { update_option( 'active_plugins', array() ); $plugins = array(); } if ( is_multisite() && current_user_can( 'manage_network_plugins' ) ) { $network_plugins = (array) get_site_option( 'active_sitewide_plugins', array() ); $plugins = array_merge( $plugins, array_keys( $network_plugins ) ); } if ( empty( $plugins ) ) { return array(); } $invalid = array(); // Invalid plugins get deactivated. foreach ( $plugins as $plugin ) { $result = validate_plugin( $plugin ); if ( is_wp_error( $result ) ) { $invalid[ $plugin ] = $result; deactivate_plugins( $plugin, true ); } } return $invalid; } /** * Validates the plugin path. * * Checks that the main plugin file exists and is a valid plugin. See validate_file(). * * @since 2.5.0 * * @param string $plugin Path to the plugin file relative to the plugins directory. * @return int|WP_Error 0 on success, WP_Error on failure. */ function validate_plugin( $plugin ) { if ( validate_file( $plugin ) ) { return new WP_Error( 'plugin_invalid', __( 'Invalid plugin path.' ) ); } if ( ! file_exists( WP_PLUGIN_DIR . '/' . $plugin ) ) { return new WP_Error( 'plugin_not_found', __( 'Plugin file does not exist.' ) ); } $installed_plugins = get_plugins(); if ( ! isset( $installed_plugins[ $plugin ] ) ) { return new WP_Error( 'no_plugin_header', __( 'The plugin does not have a valid header.' ) ); } return 0; } /** * Validates the plugin requirements for WordPress version and PHP version. * * Uses the information from `Requires at least`, `Requires PHP` and `Requires Plugins` headers * defined in the plugin's main PHP file. * * @since 5.2.0 * @since 5.3.0 Added support for reading the headers from the plugin's * main PHP file, with `readme.txt` as a fallback. * @since 5.8.0 Removed support for using `readme.txt` as a fallback. * @since 6.5.0 Added support for the 'Requires Plugins' header. * * @param string $plugin Path to the plugin file relative to the plugins directory. * @return true|WP_Error True if requirements are met, WP_Error on failure. */ function validate_plugin_requirements( $plugin ) { $plugin_headers = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin ); $requirements = array( 'requires' => ! empty( $plugin_headers['RequiresWP'] ) ? $plugin_headers['RequiresWP'] : '', 'requires_php' => ! empty( $plugin_headers['RequiresPHP'] ) ? $plugin_headers['RequiresPHP'] : '', 'requires_plugins' => ! empty( $plugin_headers['RequiresPlugins'] ) ? $plugin_headers['RequiresPlugins'] : '', ); $compatible_wp = is_wp_version_compatible( $requirements['requires'] ); $compatible_php = is_php_version_compatible( $requirements['requires_php'] ); $php_update_message = '</p><p>' . sprintf( /* translators: %s: URL to Update PHP page. */ __( '<a href="%s">Learn more about updating PHP</a>.' ), esc_url( wp_get_update_php_url() ) ); $annotation = wp_get_update_php_annotation(); if ( $annotation ) { $php_update_message .= '</p><p><em>' . $annotation . '</em>'; } if ( ! $compatible_wp && ! $compatible_php ) { return new WP_Error( 'plugin_wp_php_incompatible', '<p>' . sprintf( /* translators: 1: Current WordPress version, 2: Current PHP version, 3: Plugin name, 4: Required WordPress version, 5: Required PHP version. */ _x( '<strong>Error:</strong> Current versions of WordPress (%1$s) and PHP (%2$s) do not meet minimum requirements for %3$s. The plugin requires WordPress %4$s and PHP %5$s.', 'plugin' ), get_bloginfo( 'version' ), PHP_VERSION, $plugin_headers['Name'], $requirements['requires'], $requirements['requires_php'] ) . $php_update_message . '</p>' ); } elseif ( ! $compatible_php ) { return new WP_Error( 'plugin_php_incompatible', '<p>' . sprintf( /* translators: 1: Current PHP version, 2: Plugin name, 3: Required PHP version. */ _x( '<strong>Error:</strong> Current PHP version (%1$s) does not meet minimum requirements for %2$s. The plugin requires PHP %3$s.', 'plugin' ), PHP_VERSION, $plugin_headers['Name'], $requirements['requires_php'] ) . $php_update_message . '</p>' ); } elseif ( ! $compatible_wp ) { return new WP_Error( 'plugin_wp_incompatible', '<p>' . sprintf( /* translators: 1: Current WordPress version, 2: Plugin name, 3: Required WordPress version. */ _x( '<strong>Error:</strong> Current WordPress version (%1$s) does not meet minimum requirements for %2$s. The plugin requires WordPress %3$s.', 'plugin' ), get_bloginfo( 'version' ), $plugin_headers['Name'], $requirements['requires'] ) . '</p>' ); } WP_Plugin_Dependencies::initialize(); if ( WP_Plugin_Dependencies::has_unmet_dependencies( $plugin ) ) { $dependency_names = WP_Plugin_Dependencies::get_dependency_names( $plugin ); $unmet_dependencies = array(); $unmet_dependency_names = array(); foreach ( $dependency_names as $dependency => $dependency_name ) { $dependency_file = WP_Plugin_Dependencies::get_dependency_filepath( $dependency ); if ( false === $dependency_file ) { $unmet_dependencies['not_installed'][ $dependency ] = $dependency_name; $unmet_dependency_names[] = $dependency_name; } elseif ( is_plugin_inactive( $dependency_file ) ) { $unmet_dependencies['inactive'][ $dependency ] = $dependency_name; $unmet_dependency_names[] = $dependency_name; } } $error_message = sprintf( /* translators: 1: Plugin name, 2: Number of plugins, 3: A comma-separated list of plugin names. */ _n( '<strong>Error:</strong> %1$s requires %2$d plugin to be installed and activated: %3$s.', '<strong>Error:</strong> %1$s requires %2$d plugins to be installed and activated: %3$s.', count( $unmet_dependency_names ) ), $plugin_headers['Name'], count( $unmet_dependency_names ), implode( wp_get_list_item_separator(), $unmet_dependency_names ) ); if ( is_multisite() ) { if ( current_user_can( 'manage_network_plugins' ) ) { $error_message .= ' ' . sprintf( /* translators: %s: Link to the plugins page. */ __( '<a href="%s">Manage plugins</a>.' ), esc_url( network_admin_url( 'plugins.php' ) ) ); } else { $error_message .= ' ' . __( 'Please contact your network administrator.' ); } } else { $error_message .= ' ' . sprintf( /* translators: %s: Link to the plugins page. */ __( '<a href="%s">Manage plugins</a>.' ), esc_url( admin_url( 'plugins.php' ) ) ); } return new WP_Error( 'plugin_missing_dependencies', "<p>{$error_message}</p>", $unmet_dependencies ); } return true; } /** * Determines whether the plugin can be uninstalled. * * @since 2.7.0 * * @param string $plugin Path to the plugin file relative to the plugins directory. * @return bool Whether plugin can be uninstalled. */ function is_uninstallable_plugin( $plugin ) { $file = plugin_basename( $plugin ); $uninstallable_plugins = (array) get_option( 'uninstall_plugins' ); if ( isset( $uninstallable_plugins[ $file ] ) || file_exists( WP_PLUGIN_DIR . '/' . dirname( $file ) . '/uninstall.php' ) ) { return true; } return false; } /** * Uninstalls a single plugin. * * Calls the uninstall hook, if it is available. * * @since 2.7.0 * * @param string $plugin Path to the plugin file relative to the plugins directory. * @return true|void True if a plugin's uninstall.php file has been found and included. * Void otherwise. */ function uninstall_plugin( $plugin ) { $file = plugin_basename( $plugin ); $uninstallable_plugins = (array) get_option( 'uninstall_plugins' ); /** * Fires in uninstall_plugin() immediately before the plugin is uninstalled. * * @since 4.5.0 * * @param string $plugin Path to the plugin file relative to the plugins directory. * @param array $uninstallable_plugins Uninstallable plugins. */ do_action( 'pre_uninstall_plugin', $plugin, $uninstallable_plugins ); if ( file_exists( WP_PLUGIN_DIR . '/' . dirname( $file ) . '/uninstall.php' ) ) { if ( isset( $uninstallable_plugins[ $file ] ) ) { unset( $uninstallable_plugins[ $file ] ); update_option( 'uninstall_plugins', $uninstallable_plugins ); } unset( $uninstallable_plugins ); define( 'WP_UNINSTALL_PLUGIN', $file ); wp_register_plugin_realpath( WP_PLUGIN_DIR . '/' . $file ); include_once WP_PLUGIN_DIR . '/' . dirname( $file ) . '/uninstall.php'; return true; } if ( isset( $uninstallable_plugins[ $file ] ) ) { $callable = $uninstallable_plugins[ $file ]; unset( $uninstallable_plugins[ $file ] ); update_option( 'uninstall_plugins', $uninstallable_plugins ); unset( $uninstallable_plugins ); wp_register_plugin_realpath( WP_PLUGIN_DIR . '/' . $file ); include_once WP_PLUGIN_DIR . '/' . $file; add_action( "uninstall_{$file}", $callable ); /** * Fires in uninstall_plugin() once the plugin has been uninstalled. * * The action concatenates the 'uninstall_' prefix with the basename of the * plugin passed to uninstall_plugin() to create a dynamically-named action. * * @since 2.7.0 */ do_action( "uninstall_{$file}" ); } } // // Menu. // /** * Adds a top-level menu page. * * This function takes a capability which will be used to determine whether * or not a page is included in the menu. * * The function which is hooked in to handle the output of the page must check * that the user has the required capability as well. * * @since 1.5.0 * * @global array $menu * @global array $admin_page_hooks * @global array $_registered_pages * @global array $_parent_pages * * @param string $page_title The text to be displayed in the title tags of the page when the menu is selected. * @param string $menu_title The text to be used for the menu. * @param string $capability The capability required for this menu to be displayed to the user. * @param string $menu_slug The slug name to refer to this menu by. Should be unique for this menu page and only * include lowercase alphanumeric, dashes, and underscores characters to be compatible * with sanitize_key(). * @param callable $callback Optional. The function to be called to output the content for this page. * @param string $icon_url Optional. The URL to the icon to be used for this menu. * * Pass a base64-encoded SVG using a data URI, which will be colored to match * the color scheme. This should begin with 'data:image/svg+xml;base64,'. * * Pass the name of a Dashicons helper class to use a font icon, * e.g. 'dashicons-chart-pie'. * * Pass 'none' to leave div.wp-menu-image empty so an icon can be added via CSS. * @param int|float $position Optional. The position in the menu order this item should appear. * @return string The resulting page's hook_suffix. */ function add_menu_page( $page_title, $menu_title, $capability, $menu_slug, $callback = '', $icon_url = '', $position = null ) { global $menu, $admin_page_hooks, $_registered_pages, $_parent_pages; $menu_slug = plugin_basename( $menu_slug ); $admin_page_hooks[ $menu_slug ] = sanitize_title( $menu_title ); $hookname = get_plugin_page_hookname( $menu_slug, '' ); if ( ! empty( $callback ) && ! empty( $hookname ) && current_user_can( $capability ) ) { add_action( $hookname, $callback ); } if ( empty( $icon_url ) ) { $icon_url = 'dashicons-admin-generic'; $icon_class = 'menu-icon-generic '; } else { $icon_url = set_url_scheme( $icon_url ); $icon_class = ''; } $new_menu = array( $menu_title, $capability, $menu_slug, $page_title, 'menu-top ' . $icon_class . $hookname, $hookname, $icon_url ); if ( null !== $position && ! is_numeric( $position ) ) { _doing_it_wrong( __FUNCTION__, sprintf( /* translators: %s: add_menu_page() */ __( 'The seventh parameter passed to %s should be numeric representing menu position.' ), '<code>add_menu_page()</code>' ), '6.0.0' ); $position = null; } if ( null === $position || ! is_numeric( $position ) ) { $menu[] = $new_menu; } elseif ( isset( $menu[ (string) $position ] ) ) { $collision_avoider = base_convert( substr( md5( $menu_slug . $menu_title ), -4 ), 16, 10 ) * 0.00001; $position = (string) ( $position + $collision_avoider ); $menu[ $position ] = $new_menu; } else { /* * Cast menu position to a string. * * This allows for floats to be passed as the position. PHP will normally cast a float to an * integer value, this ensures the float retains its mantissa (positive fractional part). * * A string containing an integer value, eg "10", is treated as a numeric index. */ $position = (string) $position; $menu[ $position ] = $new_menu; } $_registered_pages[ $hookname ] = true; // No parent as top level. $_parent_pages[ $menu_slug ] = false; return $hookname; } /** * Adds a submenu page. * * This function takes a capability which will be used to determine whether * or not a page is included in the menu. * * The function which is hooked in to handle the output of the page must check * that the user has the required capability as well. * * @since 1.5.0 * @since 5.3.0 Added the `$position` parameter. * * @global array $submenu * @global array $menu * @global array $_wp_real_parent_file * @global bool $_wp_submenu_nopriv * @global array $_registered_pages * @global array $_parent_pages * * @param string $parent_slug The slug name for the parent menu (or the file name of a standard * WordPress admin page). * @param string $page_title The text to be displayed in the title tags of the page when the menu * is selected. * @param string $menu_title The text to be used for the menu. * @param string $capability The capability required for this menu to be displayed to the user. * @param string $menu_slug The slug name to refer to this menu by. Should be unique for this menu * and only include lowercase alphanumeric, dashes, and underscores characters * to be compatible with sanitize_key(). * @param callable $callback Optional. The function to be called to output the content for this page. * @param int|float $position Optional. The position in the menu order this item should appear. * @return string|false The resulting page's hook_suffix, or false if the user does not have the capability required. */ function add_submenu_page( $parent_slug, $page_title, $menu_title, $capability, $menu_slug, $callback = '', $position = null ) { global $submenu, $menu, $_wp_real_parent_file, $_wp_submenu_nopriv, $_registered_pages, $_parent_pages; $menu_slug = plugin_basename( $menu_slug ); $parent_slug = plugin_basename( $parent_slug ); if ( isset( $_wp_real_parent_file[ $parent_slug ] ) ) { $parent_slug = $_wp_real_parent_file[ $parent_slug ]; } if ( ! current_user_can( $capability ) ) { $_wp_submenu_nopriv[ $parent_slug ][ $menu_slug ] = true; return false; } /* * If the parent doesn't already have a submenu, add a link to the parent * as the first item in the submenu. If the submenu file is the same as the * parent file someone is trying to link back to the parent manually. In * this case, don't automatically add a link back to avoid duplication. */ if ( ! isset( $submenu[ $parent_slug ] ) && $menu_slug !== $parent_slug ) { foreach ( (array) $menu as $parent_menu ) { if ( $parent_menu[2] === $parent_slug && current_user_can( $parent_menu[1] ) ) { $submenu[ $parent_slug ][] = array_slice( $parent_menu, 0, 4 ); } } } $new_sub_menu = array( $menu_title, $capability, $menu_slug, $page_title ); if ( null !== $position && ! is_numeric( $position ) ) { _doing_it_wrong( __FUNCTION__, sprintf( /* translators: %s: add_submenu_page() */ __( 'The seventh parameter passed to %s should be numeric representing menu position.' ), '<code>add_submenu_page()</code>' ), '5.3.0' ); $position = null; } if ( null === $position || ( ! isset( $submenu[ $parent_slug ] ) || $position >= count( $submenu[ $parent_slug ] ) ) ) { $submenu[ $parent_slug ][] = $new_sub_menu; } else { // Test for a negative position. $position = max( $position, 0 ); if ( 0 === $position ) { // For negative or `0` positions, prepend the submenu. array_unshift( $submenu[ $parent_slug ], $new_sub_menu ); } else { $position = absint( $position ); // Grab all of the items before the insertion point. $before_items = array_slice( $submenu[ $parent_slug ], 0, $position, true ); // Grab all of the items after the insertion point. $after_items = array_slice( $submenu[ $parent_slug ], $position, null, true ); // Add the new item. $before_items[] = $new_sub_menu; // Merge the items. $submenu[ $parent_slug ] = array_merge( $before_items, $after_items ); } } // Sort the parent array. ksort( $submenu[ $parent_slug ] ); $hookname = get_plugin_page_hookname( $menu_slug, $parent_slug ); if ( ! empty( $callback ) && ! empty( $hookname ) ) { add_action( $hookname, $callback ); } $_registered_pages[ $hookname ] = true; /* * Backward-compatibility for plugins using add_management_page(). * See wp-admin/admin.php for redirect from edit.php to tools.php. */ if ( 'tools.php' === $parent_slug ) { $_registered_pages[ get_plugin_page_hookname( $menu_slug, 'edit.php' ) ] = true; } // No parent as top level. $_parent_pages[ $menu_slug ] = $parent_slug; return $hookname; } /** * Adds a submenu page to the Tools main menu. * * This function takes a capability which will be used to determine whether * or not a page is included in the menu. * * The function which is hooked in to handle the output of the page must check * that the user has the required capability as well. * * @since 1.5.0 * @since 5.3.0 Added the `$position` parameter. * * @param string $page_title The text to be displayed in the title tags of the page when the menu is selected. * @param string $menu_title The text to be used for the menu. * @param string $capability The capability required for this menu to be displayed to the user. * @param string $menu_slug The slug name to refer to this menu by (should be unique for this menu). * @param callable $callback Optional. The function to be called to output the content for this page. * @param int $position Optional. The position in the menu order this item should appear. * @return string|false The resulting page's hook_suffix, or false if the user does not have the capability required. */ function add_management_page( $page_title, $menu_title, $capability, $menu_slug, $callback = '', $position = null ) { return add_submenu_page( 'tools.php', $page_title, $menu_title, $capability, $menu_slug, $callback, $position ); } /** * Adds a submenu page to the Settings main menu. * * This function takes a capability which will be used to determine whether * or not a page is included in the menu. * * The function which is hooked in to handle the output of the page must check * that the user has the required capability as well. * * @since 1.5.0 * @since 5.3.0 Added the `$position` parameter. * * @param string $page_title The text to be displayed in the title tags of the page when the menu is selected. * @param string $menu_title The text to be used for the menu. * @param string $capability The capability required for this menu to be displayed to the user. * @param string $menu_slug The slug name to refer to this menu by (should be unique for this menu). * @param callable $callback Optional. The function to be called to output the content for this page. * @param int $position Optional. The position in the menu order this item should appear. * @return string|false The resulting page's hook_suffix, or false if the user does not have the capability required. */ function add_options_page( $page_title, $menu_title, $capability, $menu_slug, $callback = '', $position = null ) { return add_submenu_page( 'options-general.php', $page_title, $menu_title, $capability, $menu_slug, $callback, $position ); } /** * Adds a submenu page to the Appearance main menu. * * This function takes a capability which will be used to determine whether * or not a page is included in the menu. * * The function which is hooked in to handle the output of the page must check * that the user has the required capability as well. * * @since 2.0.0 * @since 5.3.0 Added the `$position` parameter. * * @param string $page_title The text to be displayed in the title tags of the page when the menu is selected. * @param string $menu_title The text to be used for the menu. * @param string $capability The capability required for this menu to be displayed to the user. * @param string $menu_slug The slug name to refer to this menu by (should be unique for this menu). * @param callable $callback Optional. The function to be called to output the content for this page. * @param int $position Optional. The position in the menu order this item should appear. * @return string|false The resulting page's hook_suffix, or false if the user does not have the capability required. */ function add_theme_page( $page_title, $menu_title, $capability, $menu_slug, $callback = '', $position = null ) { return add_submenu_page( 'themes.php', $page_title, $menu_title, $capability, $menu_slug, $callback, $position ); } /** * Adds a submenu page to the Plugins main menu. * * This function takes a capability which will be used to determine whether * or not a page is included in the menu. * * The function which is hooked in to handle the output of the page must check * that the user has the required capability as well. * * @since 3.0.0 * @since 5.3.0 Added the `$position` parameter. * * @param string $page_title The text to be displayed in the title tags of the page when the menu is selected. * @param string $menu_title The text to be used for the menu. * @param string $capability The capability required for this menu to be displayed to the user. * @param string $menu_slug The slug name to refer to this menu by (should be unique for this menu). * @param callable $callback Optional. The function to be called to output the content for this page. * @param int $position Optional. The position in the menu order this item should appear. * @return string|false The resulting page's hook_suffix, or false if the user does not have the capability required. */ function add_plugins_page( $page_title, $menu_title, $capability, $menu_slug, $callback = '', $position = null ) { return add_submenu_page( 'plugins.php', $page_title, $menu_title, $capability, $menu_slug, $callback, $position ); } /** * Adds a submenu page to the Users/Profile main menu. * * This function takes a capability which will be used to determine whether * or not a page is included in the menu. * * The function which is hooked in to handle the output of the page must check * that the user has the required capability as well. * * @since 2.1.3 * @since 5.3.0 Added the `$position` parameter. * * @param string $page_title The text to be displayed in the title tags of the page when the menu is selected. * @param string $menu_title The text to be used for the menu. * @param string $capability The capability required for this menu to be displayed to the user. * @param string $menu_slug The slug name to refer to this menu by (should be unique for this menu). * @param callable $callback Optional. The function to be called to output the content for this page. * @param int $position Optional. The position in the menu order this item should appear. * @return string|false The resulting page's hook_suffix, or false if the user does not have the capability required. */ function add_users_page( $page_title, $menu_title, $capability, $menu_slug, $callback = '', $position = null ) { if ( current_user_can( 'edit_users' ) ) { $parent = 'users.php'; } else { $parent = 'profile.php'; } return add_submenu_page( $parent, $page_title, $menu_title, $capability, $menu_slug, $callback, $position ); } /** * Adds a submenu page to the Dashboard main menu. * * This function takes a capability which will be used to determine whether * or not a page is included in the menu. * * The function which is hooked in to handle the output of the page must check * that the user has the required capability as well. * * @since 2.7.0 * @since 5.3.0 Added the `$position` parameter. * * @param string $page_title The text to be displayed in the title tags of the page when the menu is selected. * @param string $menu_title The text to be used for the menu. * @param string $capability The capability required for this menu to be displayed to the user. * @param string $menu_slug The slug name to refer to this menu by (should be unique for this menu). * @param callable $callback Optional. The function to be called to output the content for this page. * @param int $position Optional. The position in the menu order this item should appear. * @return string|false The resulting page's hook_suffix, or false if the user does not have the capability required. */ function add_dashboard_page( $page_title, $menu_title, $capability, $menu_slug, $callback = '', $position = null ) { return add_submenu_page( 'index.php', $page_title, $menu_title, $capability, $menu_slug, $callback, $position ); } /** * Adds a submenu page to the Posts main menu. * * This function takes a capability which will be used to determine whether * or not a page is included in the menu. * * The function which is hooked in to handle the output of the page must check * that the user has the required capability as well. * * @since 2.7.0 * @since 5.3.0 Added the `$position` parameter. * * @param string $page_title The text to be displayed in the title tags of the page when the menu is selected. * @param string $menu_title The text to be used for the menu. * @param string $capability The capability required for this menu to be displayed to the user. * @param string $menu_slug The slug name to refer to this menu by (should be unique for this menu). * @param callable $callback Optional. The function to be called to output the content for this page. * @param int $position Optional. The position in the menu order this item should appear. * @return string|false The resulting page's hook_suffix, or false if the user does not have the capability required. */ function add_posts_page( $page_title, $menu_title, $capability, $menu_slug, $callback = '', $position = null ) { return add_submenu_page( 'edit.php', $page_title, $menu_title, $capability, $menu_slug, $callback, $position ); } /** * Adds a submenu page to the Media main menu. * * This function takes a capability which will be used to determine whether * or not a page is included in the menu. * * The function which is hooked in to handle the output of the page must check * that the user has the required capability as well. * * @since 2.7.0 * @since 5.3.0 Added the `$position` parameter. * * @param string $page_title The text to be displayed in the title tags of the page when the menu is selected. * @param string $menu_title The text to be used for the menu. * @param string $capability The capability required for this menu to be displayed to the user. * @param string $menu_slug The slug name to refer to this menu by (should be unique for this menu). * @param callable $callback Optional. The function to be called to output the content for this page. * @param int $position Optional. The position in the menu order this item should appear. * @return string|false The resulting page's hook_suffix, or false if the user does not have the capability required. */ function add_media_page( $page_title, $menu_title, $capability, $menu_slug, $callback = '', $position = null ) { return add_submenu_page( 'upload.php', $page_title, $menu_title, $capability, $menu_slug, $callback, $position ); } /** * Adds a submenu page to the Links main menu. * * This function takes a capability which will be used to determine whether * or not a page is included in the menu. * * The function which is hooked in to handle the output of the page must check * that the user has the required capability as well. * * @since 2.7.0 * @since 5.3.0 Added the `$position` parameter. * * @param string $page_title The text to be displayed in the title tags of the page when the menu is selected. * @param string $menu_title The text to be used for the menu. * @param string $capability The capability required for this menu to be displayed to the user. * @param string $menu_slug The slug name to refer to this menu by (should be unique for this menu). * @param callable $callback Optional. The function to be called to output the content for this page. * @param int $position Optional. The position in the menu order this item should appear. * @return string|false The resulting page's hook_suffix, or false if the user does not have the capability required. */ function add_links_page( $page_title, $menu_title, $capability, $menu_slug, $callback = '', $position = null ) { return add_submenu_page( 'link-manager.php', $page_title, $menu_title, $capability, $menu_slug, $callback, $position ); } /** * Adds a submenu page to the Pages main menu. * * This function takes a capability which will be used to determine whether * or not a page is included in the menu. * * The function which is hooked in to handle the output of the page must check * that the user has the required capability as well. * * @since 2.7.0 * @since 5.3.0 Added the `$position` parameter. * * @param string $page_title The text to be displayed in the title tags of the page when the menu is selected. * @param string $menu_title The text to be used for the menu. * @param string $capability The capability required for this menu to be displayed to the user. * @param string $menu_slug The slug name to refer to this menu by (should be unique for this menu). * @param callable $callback Optional. The function to be called to output the content for this page. * @param int $position Optional. The position in the menu order this item should appear. * @return string|false The resulting page's hook_suffix, or false if the user does not have the capability required. */ function add_pages_page( $page_title, $menu_title, $capability, $menu_slug, $callback = '', $position = null ) { return add_submenu_page( 'edit.php?post_type=page', $page_title, $menu_title, $capability, $menu_slug, $callback, $position ); } /** * Adds a submenu page to the Comments main menu. * * This function takes a capability which will be used to determine whether * or not a page is included in the menu. * * The function which is hooked in to handle the output of the page must check * that the user has the required capability as well. * * @since 2.7.0 * @since 5.3.0 Added the `$position` parameter. * * @param string $page_title The text to be displayed in the title tags of the page when the menu is selected. * @param string $menu_title The text to be used for the menu. * @param string $capability The capability required for this menu to be displayed to the user. * @param string $menu_slug The slug name to refer to this menu by (should be unique for this menu). * @param callable $callback Optional. The function to be called to output the content for this page. * @param int $position Optional. The position in the menu order this item should appear. * @return string|false The resulting page's hook_suffix, or false if the user does not have the capability required. */ function add_comments_page( $page_title, $menu_title, $capability, $menu_slug, $callback = '', $position = null ) { return add_submenu_page( 'edit-comments.php', $page_title, $menu_title, $capability, $menu_slug, $callback, $position ); } /** * Removes a top-level admin menu. * * Example usage: * * - `remove_menu_page( 'tools.php' )` * - `remove_menu_page( 'plugin_menu_slug' )` * * @since 3.1.0 * * @global array $menu * * @param string $menu_slug The slug of the menu. * @return array|false The removed menu on success, false if not found. */ function remove_menu_page( $menu_slug ) { global $menu; foreach ( $menu as $i => $item ) { if ( $menu_slug === $item[2] ) { unset( $menu[ $i ] ); return $item; } } return false; } /** * Removes an admin submenu. * * Example usage: * * - `remove_submenu_page( 'themes.php', 'nav-menus.php' )` * - `remove_submenu_page( 'tools.php', 'plugin_submenu_slug' )` * - `remove_submenu_page( 'plugin_menu_slug', 'plugin_submenu_slug' )` * * @since 3.1.0 * * @global array $submenu * * @param string $menu_slug The slug for the parent menu. * @param string $submenu_slug The slug of the submenu. * @return array|false The removed submenu on success, false if not found. */ function remove_submenu_page( $menu_slug, $submenu_slug ) { global $submenu; if ( ! isset( $submenu[ $menu_slug ] ) ) { return false; } foreach ( $submenu[ $menu_slug ] as $i => $item ) { if ( $submenu_slug === $item[2] ) { unset( $submenu[ $menu_slug ][ $i ] ); return $item; } } return false; } /** * Gets the URL to access a particular menu page based on the slug it was registered with. * * If the slug hasn't been registered properly, no URL will be returned. * * @since 3.0.0 * * @global array $_parent_pages * * @param string $menu_slug The slug name to refer to this menu by (should be unique for this menu). * @param bool $display Optional. Whether or not to display the URL. Default true. * @return string The menu page URL. */ function menu_page_url( $menu_slug, $display = true ) { global $_parent_pages; if ( isset( $_parent_pages[ $menu_slug ] ) ) { $parent_slug = $_parent_pages[ $menu_slug ]; if ( $parent_slug && ! isset( $_parent_pages[ $parent_slug ] ) ) { $url = admin_url( add_query_arg( 'page', $menu_slug, $parent_slug ) ); } else { $url = admin_url( 'admin.php?page=' . $menu_slug ); } } else { $url = ''; } $url = esc_url( $url ); if ( $display ) { echo $url; } return $url; } // // Pluggable Menu Support -- Private. // /** * Gets the parent file of the current admin page. * * @since 1.5.0 * * @global string $parent_file * @global array $menu * @global array $submenu * @global string $pagenow The filename of the current screen. * @global string $typenow The post type of the current screen. * @global string $plugin_page * @global array $_wp_real_parent_file * @global array $_wp_menu_nopriv * @global array $_wp_submenu_nopriv * * @param string $parent_page Optional. The slug name for the parent menu (or the file name * of a standard WordPress admin page). Default empty string. * @return string The parent file of the current admin page. */ function get_admin_page_parent( $parent_page = '' ) { global $parent_file, $menu, $submenu, $pagenow, $typenow, $plugin_page, $_wp_real_parent_file, $_wp_menu_nopriv, $_wp_submenu_nopriv; if ( ! empty( $parent_page ) && 'admin.php' !== $parent_page ) { if ( isset( $_wp_real_parent_file[ $parent_page ] ) ) { $parent_page = $_wp_real_parent_file[ $parent_page ]; } return $parent_page; } if ( 'admin.php' === $pagenow && isset( $plugin_page ) ) { foreach ( (array) $menu as $parent_menu ) { if ( $parent_menu[2] === $plugin_page ) { $parent_file = $plugin_page; if ( isset( $_wp_real_parent_file[ $parent_file ] ) ) { $parent_file = $_wp_real_parent_file[ $parent_file ]; } return $parent_file; } } if ( isset( $_wp_menu_nopriv[ $plugin_page ] ) ) { $parent_file = $plugin_page; if ( isset( $_wp_real_parent_file[ $parent_file ] ) ) { $parent_file = $_wp_real_parent_file[ $parent_file ]; } return $parent_file; } } if ( isset( $plugin_page ) && isset( $_wp_submenu_nopriv[ $pagenow ][ $plugin_page ] ) ) { $parent_file = $pagenow; if ( isset( $_wp_real_parent_file[ $parent_file ] ) ) { $parent_file = $_wp_real_parent_file[ $parent_file ]; } return $parent_file; } foreach ( array_keys( (array) $submenu ) as $parent_page ) { foreach ( $submenu[ $parent_page ] as $submenu_array ) { if ( isset( $_wp_real_parent_file[ $parent_page ] ) ) { $parent_page = $_wp_real_parent_file[ $parent_page ]; } if ( ! empty( $typenow ) && "$pagenow?post_type=$typenow" === $submenu_array[2] ) { $parent_file = $parent_page; return $parent_page; } elseif ( empty( $typenow ) && $pagenow === $submenu_array[2] && ( empty( $parent_file ) || ! str_contains( $parent_file, '?' ) ) ) { $parent_file = $parent_page; return $parent_page; } elseif ( isset( $plugin_page ) && $plugin_page === $submenu_array[2] ) { $parent_file = $parent_page; return $parent_page; } } } if ( empty( $parent_file ) ) { $parent_file = ''; } return ''; } /** * Gets the title of the current admin page. * * @since 1.5.0 * * @global string $title * @global array $menu * @global array $submenu * @global string $pagenow The filename of the current screen. * @global string $typenow The post type of the current screen. * @global string $plugin_page * * @return string The title of the current admin page. */ function get_admin_page_title() { global $title, $menu, $submenu, $pagenow, $typenow, $plugin_page; if ( ! empty( $title ) ) { return $title; } $hook = get_plugin_page_hook( $plugin_page, $pagenow ); $parent = get_admin_page_parent(); $parent1 = $parent; if ( empty( $parent ) ) { foreach ( (array) $menu as $menu_array ) { if ( isset( $menu_array[3] ) ) { if ( $menu_array[2] === $pagenow ) { $title = $menu_array[3]; return $menu_array[3]; } elseif ( isset( $plugin_page ) && $plugin_page === $menu_array[2] && $hook === $menu_array[5] ) { $title = $menu_array[3]; return $menu_array[3]; } } else { $title = $menu_array[0]; return $title; } } } else { foreach ( array_keys( $submenu ) as $parent ) { foreach ( $submenu[ $parent ] as $submenu_array ) { if ( isset( $plugin_page ) && $plugin_page === $submenu_array[2] && ( $pagenow === $parent || $plugin_page === $parent || $plugin_page === $hook || 'admin.php' === $pagenow && $parent1 !== $submenu_array[2] || ! empty( $typenow ) && "$pagenow?post_type=$typenow" === $parent ) ) { $title = $submenu_array[3]; return $submenu_array[3]; } if ( $submenu_array[2] !== $pagenow || isset( $_GET['page'] ) ) { // Not the current page. continue; } if ( isset( $submenu_array[3] ) ) { $title = $submenu_array[3]; return $submenu_array[3]; } else { $title = $submenu_array[0]; return $title; } } } if ( empty( $title ) ) { foreach ( $menu as $menu_array ) { if ( isset( $plugin_page ) && $plugin_page === $menu_array[2] && 'admin.php' === $pagenow && $parent1 === $menu_array[2] ) { $title = $menu_array[3]; return $menu_array[3]; } } } } return $title; } /** * Gets the hook attached to the administrative page of a plugin. * * @since 1.5.0 * * @param string $plugin_page The slug name of the plugin page. * @param string $parent_page The slug name for the parent menu (or the file name of a standard * WordPress admin page). * @return string|null Hook attached to the plugin page, null otherwise. */ function get_plugin_page_hook( $plugin_page, $parent_page ) { $hook = get_plugin_page_hookname( $plugin_page, $parent_page ); if ( has_action( $hook ) ) { return $hook; } else { return null; } } /** * Gets the hook name for the administrative page of a plugin. * * @since 1.5.0 * * @global array $admin_page_hooks * * @param string $plugin_page The slug name of the plugin page. * @param string $parent_page The slug name for the parent menu (or the file name of a standard * WordPress admin page). * @return string Hook name for the plugin page. */ function get_plugin_page_hookname( $plugin_page, $parent_page ) { global $admin_page_hooks; $parent = get_admin_page_parent( $parent_page ); $page_type = 'admin'; if ( empty( $parent_page ) || 'admin.php' === $parent_page || isset( $admin_page_hooks[ $plugin_page ] ) ) { if ( isset( $admin_page_hooks[ $plugin_page ] ) ) { $page_type = 'toplevel'; } elseif ( isset( $admin_page_hooks[ $parent ] ) ) { $page_type = $admin_page_hooks[ $parent ]; } } elseif ( isset( $admin_page_hooks[ $parent ] ) ) { $page_type = $admin_page_hooks[ $parent ]; } $plugin_name = preg_replace( '!\.php!', '', $plugin_page ); return $page_type . '_page_' . $plugin_name; } /** * Determines whether the current user can access the current admin page. * * @since 1.5.0 * * @global string $pagenow The filename of the current screen. * @global array $menu * @global array $submenu * @global array $_wp_menu_nopriv * @global array $_wp_submenu_nopriv * @global string $plugin_page * @global array $_registered_pages * * @return bool True if the current user can access the admin page, false otherwise. */ function user_can_access_admin_page() { global $pagenow, $menu, $submenu, $_wp_menu_nopriv, $_wp_submenu_nopriv, $plugin_page, $_registered_pages; $parent = get_admin_page_parent(); if ( ! isset( $plugin_page ) && isset( $_wp_submenu_nopriv[ $parent ][ $pagenow ] ) ) { return false; } if ( isset( $plugin_page ) ) { if ( isset( $_wp_submenu_nopriv[ $parent ][ $plugin_page ] ) ) { return false; } $hookname = get_plugin_page_hookname( $plugin_page, $parent ); if ( ! isset( $_registered_pages[ $hookname ] ) ) { return false; } } if ( empty( $parent ) ) { if ( isset( $_wp_menu_nopriv[ $pagenow ] ) ) { return false; } if ( isset( $_wp_submenu_nopriv[ $pagenow ][ $pagenow ] ) ) { return false; } if ( isset( $plugin_page ) && isset( $_wp_submenu_nopriv[ $pagenow ][ $plugin_page ] ) ) { return false; } if ( isset( $plugin_page ) && isset( $_wp_menu_nopriv[ $plugin_page ] ) ) { return false; } foreach ( array_keys( $_wp_submenu_nopriv ) as $key ) { if ( isset( $_wp_submenu_nopriv[ $key ][ $pagenow ] ) ) { return false; } if ( isset( $plugin_page ) && isset( $_wp_submenu_nopriv[ $key ][ $plugin_page ] ) ) { return false; } } return true; } if ( isset( $plugin_page ) && $plugin_page === $parent && isset( $_wp_menu_nopriv[ $plugin_page ] ) ) { return false; } if ( isset( $submenu[ $parent ] ) ) { foreach ( $submenu[ $parent ] as $submenu_array ) { if ( isset( $plugin_page ) && $submenu_array[2] === $plugin_page ) { return current_user_can( $submenu_array[1] ); } elseif ( $submenu_array[2] === $pagenow ) { return current_user_can( $submenu_array[1] ); } } } foreach ( $menu as $menu_array ) { if ( $menu_array[2] === $parent ) { return current_user_can( $menu_array[1] ); } } return true; } /* Allowed list functions */ /** * Refreshes the value of the allowed options list available via the 'allowed_options' hook. * * See the {@see 'allowed_options'} filter. * * @since 2.7.0 * @since 5.5.0 `$new_whitelist_options` was renamed to `$new_allowed_options`. * Please consider writing more inclusive code. * * @global array $new_allowed_options * * @param array $options * @return array */ function option_update_filter( $options ) { global $new_allowed_options; if ( is_array( $new_allowed_options ) ) { $options = add_allowed_options( $new_allowed_options, $options ); } return $options; } /** * Adds an array of options to the list of allowed options. * * @since 5.5.0 * * @global array $allowed_options * * @param array $new_options * @param string|array $options * @return array */ function add_allowed_options( $new_options, $options = '' ) { if ( '' === $options ) { global $allowed_options; } else { $allowed_options = $options; } foreach ( $new_options as $page => $keys ) { foreach ( $keys as $key ) { if ( ! isset( $allowed_options[ $page ] ) || ! is_array( $allowed_options[ $page ] ) ) { $allowed_options[ $page ] = array(); $allowed_options[ $page ][] = $key; } else { $pos = array_search( $key, $allowed_options[ $page ], true ); if ( false === $pos ) { $allowed_options[ $page ][] = $key; } } } } return $allowed_options; } /** * Removes a list of options from the allowed options list. * * @since 5.5.0 * * @global array $allowed_options * * @param array $del_options * @param string|array $options * @return array */ function remove_allowed_options( $del_options, $options = '' ) { if ( '' === $options ) { global $allowed_options; } else { $allowed_options = $options; } foreach ( $del_options as $page => $keys ) { foreach ( $keys as $key ) { if ( isset( $allowed_options[ $page ] ) && is_array( $allowed_options[ $page ] ) ) { $pos = array_search( $key, $allowed_options[ $page ], true ); if ( false !== $pos ) { unset( $allowed_options[ $page ][ $pos ] ); } } } } return $allowed_options; } /** * Outputs nonce, action, and option_page fields for a settings page. * * @since 2.7.0 * * @param string $option_group A settings group name. This should match the group name * used in register_setting(). */ function settings_fields( $option_group ) { echo "<input type='hidden' name='option_page' value='" . esc_attr( $option_group ) . "' />"; echo '<input type="hidden" name="action" value="update" />'; wp_nonce_field( "$option_group-options" ); } /** * Clears the plugins cache used by get_plugins() and by default, the plugin updates cache. * * @since 3.7.0 * * @param bool $clear_update_cache Whether to clear the plugin updates cache. Default true. */ function wp_clean_plugins_cache( $clear_update_cache = true ) { if ( $clear_update_cache ) { delete_site_transient( 'update_plugins' ); } wp_cache_delete( 'plugins', 'plugins' ); } /** * Loads a given plugin attempt to generate errors. * * @since 3.0.0 * @since 4.4.0 Function was moved into the `wp-admin/includes/plugin.php` file. * * @param string $plugin Path to the plugin file relative to the plugins directory. */ function plugin_sandbox_scrape( $plugin ) { if ( ! defined( 'WP_SANDBOX_SCRAPING' ) ) { define( 'WP_SANDBOX_SCRAPING', true ); } wp_register_plugin_realpath( WP_PLUGIN_DIR . '/' . $plugin ); include_once WP_PLUGIN_DIR . '/' . $plugin; } /** * Declares a helper function for adding content to the Privacy Policy Guide. * * Plugins and themes should suggest text for inclusion in the site's privacy policy. * The suggested text should contain information about any functionality that affects user privacy, * and will be shown on the Privacy Policy Guide screen. * * A plugin or theme can use this function multiple times as long as it will help to better present * the suggested policy content. For example modular plugins such as WooCommerse or Jetpack * can add or remove suggested content depending on the modules/extensions that are enabled. * For more information see the Plugin Handbook: * https://developer.wordpress.org/plugins/privacy/suggesting-text-for-the-site-privacy-policy/. * * The HTML contents of the `$policy_text` supports use of a specialized `.privacy-policy-tutorial` * CSS class which can be used to provide supplemental information. Any content contained within * HTML elements that have the `.privacy-policy-tutorial` CSS class applied will be omitted * from the clipboard when the section content is copied. * * Intended for use with the `'admin_init'` action. * * @since 4.9.6 * * @param string $plugin_name The name of the plugin or theme that is suggesting content * for the site's privacy policy. * @param string $policy_text The suggested content for inclusion in the policy. */ function wp_add_privacy_policy_content( $plugin_name, $policy_text ) { if ( ! is_admin() ) { _doing_it_wrong( __FUNCTION__, sprintf( /* translators: %s: admin_init */ __( 'The suggested privacy policy content should be added only in wp-admin by using the %s (or later) action.' ), '<code>admin_init</code>' ), '4.9.7' ); return; } elseif ( ! doing_action( 'admin_init' ) && ! did_action( 'admin_init' ) ) { _doing_it_wrong( __FUNCTION__, sprintf( /* translators: %s: admin_init */ __( 'The suggested privacy policy content should be added by using the %s (or later) action. Please see the inline documentation.' ), '<code>admin_init</code>' ), '4.9.7' ); return; } if ( ! class_exists( 'WP_Privacy_Policy_Content' ) ) { require_once ABSPATH . 'wp-admin/includes/class-wp-privacy-policy-content.php'; } WP_Privacy_Policy_Content::add( $plugin_name, $policy_text ); } /** * Determines whether a plugin is technically active but was paused while * loading. * * For more information on this and similar theme functions, check out * the {@link https://developer.wordpress.org/themes/basics/conditional-tags/ * Conditional Tags} article in the Theme Developer Handbook. * * @since 5.2.0 * * @global WP_Paused_Extensions_Storage $_paused_plugins * * @param string $plugin Path to the plugin file relative to the plugins directory. * @return bool True, if in the list of paused plugins. False, if not in the list. */ function is_plugin_paused( $plugin ) { if ( ! isset( $GLOBALS['_paused_plugins'] ) ) { return false; } if ( ! is_plugin_active( $plugin ) ) { return false; } list( $plugin ) = explode( '/', $plugin ); return array_key_exists( $plugin, $GLOBALS['_paused_plugins'] ); } /** * Gets the error that was recorded for a paused plugin. * * @since 5.2.0 * * @global WP_Paused_Extensions_Storage $_paused_plugins * * @param string $plugin Path to the plugin file relative to the plugins directory. * @return array|false Array of error information as returned by `error_get_last()`, * or false if none was recorded. */ function wp_get_plugin_error( $plugin ) { if ( ! isset( $GLOBALS['_paused_plugins'] ) ) { return false; } list( $plugin ) = explode( '/', $plugin ); if ( ! array_key_exists( $plugin, $GLOBALS['_paused_plugins'] ) ) { return false; } return $GLOBALS['_paused_plugins'][ $plugin ]; } /** * Tries to resume a single plugin. * * If a redirect was provided, we first ensure the plugin does not throw fatal * errors anymore. * * The way it works is by setting the redirection to the error before trying to * include the plugin file. If the plugin fails, then the redirection will not * be overwritten with the success message and the plugin will not be resumed. * * @since 5.2.0 * * @param string $plugin Single plugin to resume. * @param string $redirect Optional. URL to redirect to. Default empty string. * @return true|WP_Error True on success, false if `$plugin` was not paused, * `WP_Error` on failure. */ function resume_plugin( $plugin, $redirect = '' ) { /* * We'll override this later if the plugin could be resumed without * creating a fatal error. */ if ( ! empty( $redirect ) ) { wp_redirect( add_query_arg( '_error_nonce', wp_create_nonce( 'plugin-resume-error_' . $plugin ), $redirect ) ); // Load the plugin to test whether it throws a fatal error. ob_start(); plugin_sandbox_scrape( $plugin ); ob_clean(); } list( $extension ) = explode( '/', $plugin ); $result = wp_paused_plugins()->delete( $extension ); if ( ! $result ) { return new WP_Error( 'could_not_resume_plugin', __( 'Could not resume the plugin.' ) ); } return true; } /** * Renders an admin notice in case some plugins have been paused due to errors. * * @since 5.2.0 * * @global string $pagenow The filename of the current screen. * @global WP_Paused_Extensions_Storage $_paused_plugins */ function paused_plugins_notice() { if ( 'plugins.php' === $GLOBALS['pagenow'] ) { return; } if ( ! current_user_can( 'resume_plugins' ) ) { return; } if ( ! isset( $GLOBALS['_paused_plugins'] ) || empty( $GLOBALS['_paused_plugins'] ) ) { return; } $message = sprintf( '<strong>%s</strong><br>%s</p><p><a href="%s">%s</a>', __( 'One or more plugins failed to load properly.' ), __( 'You can find more details and make changes on the Plugins screen.' ), esc_url( admin_url( 'plugins.php?plugin_status=paused' ) ), __( 'Go to the Plugins screen' ) ); wp_admin_notice( $message, array( 'type' => 'error' ) ); } /** * Renders an admin notice when a plugin was deactivated during an update. * * Displays an admin notice in case a plugin has been deactivated during an * upgrade due to incompatibility with the current version of WordPress. * * @since 5.8.0 * @access private * * @global string $pagenow The filename of the current screen. * @global string $wp_version The WordPress version string. */ function deactivated_plugins_notice() { if ( 'plugins.php' === $GLOBALS['pagenow'] ) { return; } if ( ! current_user_can( 'activate_plugins' ) ) { return; } $blog_deactivated_plugins = get_option( 'wp_force_deactivated_plugins' ); $site_deactivated_plugins = array(); if ( false === $blog_deactivated_plugins ) { // Option not in database, add an empty array to avoid extra DB queries on subsequent loads. update_option( 'wp_force_deactivated_plugins', array() ); } if ( is_multisite() ) { $site_deactivated_plugins = get_site_option( 'wp_force_deactivated_plugins' ); if ( false === $site_deactivated_plugins ) { // Option not in database, add an empty array to avoid extra DB queries on subsequent loads. update_site_option( 'wp_force_deactivated_plugins', array() ); } } if ( empty( $blog_deactivated_plugins ) && empty( $site_deactivated_plugins ) ) { // No deactivated plugins. return; } $deactivated_plugins = array_merge( $blog_deactivated_plugins, $site_deactivated_plugins ); foreach ( $deactivated_plugins as $plugin ) { if ( ! empty( $plugin['version_compatible'] ) && ! empty( $plugin['version_deactivated'] ) ) { $explanation = sprintf( /* translators: 1: Name of deactivated plugin, 2: Plugin version deactivated, 3: Current WP version, 4: Compatible plugin version. */ __( '%1$s %2$s was deactivated due to incompatibility with WordPress %3$s, please upgrade to %1$s %4$s or later.' ), $plugin['plugin_name'], $plugin['version_deactivated'], $GLOBALS['wp_version'], $plugin['version_compatible'] ); } else { $explanation = sprintf( /* translators: 1: Name of deactivated plugin, 2: Plugin version deactivated, 3: Current WP version. */ __( '%1$s %2$s was deactivated due to incompatibility with WordPress %3$s.' ), $plugin['plugin_name'], ! empty( $plugin['version_deactivated'] ) ? $plugin['version_deactivated'] : '', $GLOBALS['wp_version'], $plugin['version_compatible'] ); } $message = sprintf( '<strong>%s</strong><br>%s</p><p><a href="%s">%s</a>', sprintf( /* translators: %s: Name of deactivated plugin. */ __( '%s plugin deactivated during WordPress upgrade.' ), $plugin['plugin_name'] ), $explanation, esc_url( admin_url( 'plugins.php?plugin_status=inactive' ) ), __( 'Go to the Plugins screen' ) ); wp_admin_notice( $message, array( 'type' => 'warning' ) ); } // Empty the options. update_option( 'wp_force_deactivated_plugins', array() ); if ( is_multisite() ) { update_site_option( 'wp_force_deactivated_plugins', array() ); } } includes/ajax-actions.php 0000644 00000447402 15135536347 0011473 0 ustar 00 <?php /** * Administration API: Core Ajax handlers * * @package WordPress * @subpackage Administration * @since 2.1.0 */ // // No-privilege Ajax handlers. // /** * Handles the Heartbeat API in the no-privilege context via AJAX . * * Runs when the user is not logged in. * * @since 3.6.0 */ function wp_ajax_nopriv_heartbeat() { $response = array(); // 'screen_id' is the same as $current_screen->id and the JS global 'pagenow'. if ( ! empty( $_POST['screen_id'] ) ) { $screen_id = sanitize_key( $_POST['screen_id'] ); } else { $screen_id = 'front'; } if ( ! empty( $_POST['data'] ) ) { $data = wp_unslash( (array) $_POST['data'] ); /** * Filters Heartbeat Ajax response in no-privilege environments. * * @since 3.6.0 * * @param array $response The no-priv Heartbeat response. * @param array $data The $_POST data sent. * @param string $screen_id The screen ID. */ $response = apply_filters( 'heartbeat_nopriv_received', $response, $data, $screen_id ); } /** * Filters Heartbeat Ajax response in no-privilege environments when no data is passed. * * @since 3.6.0 * * @param array $response The no-priv Heartbeat response. * @param string $screen_id The screen ID. */ $response = apply_filters( 'heartbeat_nopriv_send', $response, $screen_id ); /** * Fires when Heartbeat ticks in no-privilege environments. * * Allows the transport to be easily replaced with long-polling. * * @since 3.6.0 * * @param array $response The no-priv Heartbeat response. * @param string $screen_id The screen ID. */ do_action( 'heartbeat_nopriv_tick', $response, $screen_id ); // Send the current time according to the server. $response['server_time'] = time(); wp_send_json( $response ); } // // GET-based Ajax handlers. // /** * Handles fetching a list table via AJAX. * * @since 3.1.0 */ function wp_ajax_fetch_list() { $list_class = $_GET['list_args']['class']; check_ajax_referer( "fetch-list-$list_class", '_ajax_fetch_list_nonce' ); $wp_list_table = _get_list_table( $list_class, array( 'screen' => $_GET['list_args']['screen']['id'] ) ); if ( ! $wp_list_table ) { wp_die( 0 ); } if ( ! $wp_list_table->ajax_user_can() ) { wp_die( -1 ); } $wp_list_table->ajax_response(); wp_die( 0 ); } /** * Handles tag search via AJAX. * * @since 3.1.0 */ function wp_ajax_ajax_tag_search() { if ( ! isset( $_GET['tax'] ) ) { wp_die( 0 ); } $taxonomy = sanitize_key( $_GET['tax'] ); $taxonomy_object = get_taxonomy( $taxonomy ); if ( ! $taxonomy_object ) { wp_die( 0 ); } if ( ! current_user_can( $taxonomy_object->cap->assign_terms ) ) { wp_die( -1 ); } $search = wp_unslash( $_GET['q'] ); $comma = _x( ',', 'tag delimiter' ); if ( ',' !== $comma ) { $search = str_replace( $comma, ',', $search ); } if ( str_contains( $search, ',' ) ) { $search = explode( ',', $search ); $search = $search[ count( $search ) - 1 ]; } $search = trim( $search ); /** * Filters the minimum number of characters required to fire a tag search via Ajax. * * @since 4.0.0 * * @param int $characters The minimum number of characters required. Default 2. * @param WP_Taxonomy $taxonomy_object The taxonomy object. * @param string $search The search term. */ $term_search_min_chars = (int) apply_filters( 'term_search_min_chars', 2, $taxonomy_object, $search ); /* * Require $term_search_min_chars chars for matching (default: 2) * ensure it's a non-negative, non-zero integer. */ if ( ( 0 == $term_search_min_chars ) || ( strlen( $search ) < $term_search_min_chars ) ) { wp_die(); } $results = get_terms( array( 'taxonomy' => $taxonomy, 'name__like' => $search, 'fields' => 'names', 'hide_empty' => false, 'number' => isset( $_GET['number'] ) ? (int) $_GET['number'] : 0, ) ); /** * Filters the Ajax term search results. * * @since 6.1.0 * * @param string[] $results Array of term names. * @param WP_Taxonomy $taxonomy_object The taxonomy object. * @param string $search The search term. */ $results = apply_filters( 'ajax_term_search_results', $results, $taxonomy_object, $search ); echo implode( "\n", $results ); wp_die(); } /** * Handles compression testing via AJAX. * * @since 3.1.0 */ function wp_ajax_wp_compression_test() { if ( ! current_user_can( 'manage_options' ) ) { wp_die( -1 ); } if ( ini_get( 'zlib.output_compression' ) || 'ob_gzhandler' === ini_get( 'output_handler' ) ) { // Use `update_option()` on single site to mark the option for autoloading. if ( is_multisite() ) { update_site_option( 'can_compress_scripts', 0 ); } else { update_option( 'can_compress_scripts', 0, 'yes' ); } wp_die( 0 ); } if ( isset( $_GET['test'] ) ) { header( 'Expires: Wed, 11 Jan 1984 05:00:00 GMT' ); header( 'Last-Modified: ' . gmdate( 'D, d M Y H:i:s' ) . ' GMT' ); header( 'Cache-Control: no-cache, must-revalidate, max-age=0' ); header( 'Content-Type: application/javascript; charset=UTF-8' ); $force_gzip = ( defined( 'ENFORCE_GZIP' ) && ENFORCE_GZIP ); $test_str = '"wpCompressionTest Lorem ipsum dolor sit amet consectetuer mollis sapien urna ut a. Eu nonummy condimentum fringilla tempor pretium platea vel nibh netus Maecenas. Hac molestie amet justo quis pellentesque est ultrices interdum nibh Morbi. Cras mattis pretium Phasellus ante ipsum ipsum ut sociis Suspendisse Lorem. Ante et non molestie. Porta urna Vestibulum egestas id congue nibh eu risus gravida sit. Ac augue auctor Ut et non a elit massa id sodales. Elit eu Nulla at nibh adipiscing mattis lacus mauris at tempus. Netus nibh quis suscipit nec feugiat eget sed lorem et urna. Pellentesque lacus at ut massa consectetuer ligula ut auctor semper Pellentesque. Ut metus massa nibh quam Curabitur molestie nec mauris congue. Volutpat molestie elit justo facilisis neque ac risus Ut nascetur tristique. Vitae sit lorem tellus et quis Phasellus lacus tincidunt nunc Fusce. Pharetra wisi Suspendisse mus sagittis libero lacinia Integer consequat ac Phasellus. Et urna ac cursus tortor aliquam Aliquam amet tellus volutpat Vestibulum. Justo interdum condimentum In augue congue tellus sollicitudin Quisque quis nibh."'; if ( 1 == $_GET['test'] ) { echo $test_str; wp_die(); } elseif ( 2 == $_GET['test'] ) { if ( ! isset( $_SERVER['HTTP_ACCEPT_ENCODING'] ) ) { wp_die( -1 ); } if ( false !== stripos( $_SERVER['HTTP_ACCEPT_ENCODING'], 'deflate' ) && function_exists( 'gzdeflate' ) && ! $force_gzip ) { header( 'Content-Encoding: deflate' ); $out = gzdeflate( $test_str, 1 ); } elseif ( false !== stripos( $_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip' ) && function_exists( 'gzencode' ) ) { header( 'Content-Encoding: gzip' ); $out = gzencode( $test_str, 1 ); } else { wp_die( -1 ); } echo $out; wp_die(); } elseif ( 'no' === $_GET['test'] ) { check_ajax_referer( 'update_can_compress_scripts' ); // Use `update_option()` on single site to mark the option for autoloading. if ( is_multisite() ) { update_site_option( 'can_compress_scripts', 0 ); } else { update_option( 'can_compress_scripts', 0, 'yes' ); } } elseif ( 'yes' === $_GET['test'] ) { check_ajax_referer( 'update_can_compress_scripts' ); // Use `update_option()` on single site to mark the option for autoloading. if ( is_multisite() ) { update_site_option( 'can_compress_scripts', 1 ); } else { update_option( 'can_compress_scripts', 1, 'yes' ); } } } wp_die( 0 ); } /** * Handles image editor previews via AJAX. * * @since 3.1.0 */ function wp_ajax_imgedit_preview() { $post_id = (int) $_GET['postid']; if ( empty( $post_id ) || ! current_user_can( 'edit_post', $post_id ) ) { wp_die( -1 ); } check_ajax_referer( "image_editor-$post_id" ); require_once ABSPATH . 'wp-admin/includes/image-edit.php'; if ( ! stream_preview_image( $post_id ) ) { wp_die( -1 ); } wp_die(); } /** * Handles oEmbed caching via AJAX. * * @since 3.1.0 * * @global WP_Embed $wp_embed */ function wp_ajax_oembed_cache() { $GLOBALS['wp_embed']->cache_oembed( $_GET['post'] ); wp_die( 0 ); } /** * Handles user autocomplete via AJAX. * * @since 3.4.0 */ function wp_ajax_autocomplete_user() { if ( ! is_multisite() || ! current_user_can( 'promote_users' ) || wp_is_large_network( 'users' ) ) { wp_die( -1 ); } /** This filter is documented in wp-admin/user-new.php */ if ( ! current_user_can( 'manage_network_users' ) && ! apply_filters( 'autocomplete_users_for_site_admins', false ) ) { wp_die( -1 ); } $return = array(); /* * Check the type of request. * Current allowed values are `add` and `search`. */ if ( isset( $_REQUEST['autocomplete_type'] ) && 'search' === $_REQUEST['autocomplete_type'] ) { $type = $_REQUEST['autocomplete_type']; } else { $type = 'add'; } /* * Check the desired field for value. * Current allowed values are `user_email` and `user_login`. */ if ( isset( $_REQUEST['autocomplete_field'] ) && 'user_email' === $_REQUEST['autocomplete_field'] ) { $field = $_REQUEST['autocomplete_field']; } else { $field = 'user_login'; } // Exclude current users of this blog. if ( isset( $_REQUEST['site_id'] ) ) { $id = absint( $_REQUEST['site_id'] ); } else { $id = get_current_blog_id(); } $include_blog_users = ( 'search' === $type ? get_users( array( 'blog_id' => $id, 'fields' => 'ID', ) ) : array() ); $exclude_blog_users = ( 'add' === $type ? get_users( array( 'blog_id' => $id, 'fields' => 'ID', ) ) : array() ); $users = get_users( array( 'blog_id' => false, 'search' => '*' . $_REQUEST['term'] . '*', 'include' => $include_blog_users, 'exclude' => $exclude_blog_users, 'search_columns' => array( 'user_login', 'user_nicename', 'user_email' ), ) ); foreach ( $users as $user ) { $return[] = array( /* translators: 1: User login, 2: User email address. */ 'label' => sprintf( _x( '%1$s (%2$s)', 'user autocomplete result' ), $user->user_login, $user->user_email ), 'value' => $user->$field, ); } wp_die( wp_json_encode( $return ) ); } /** * Handles Ajax requests for community events * * @since 4.8.0 */ function wp_ajax_get_community_events() { require_once ABSPATH . 'wp-admin/includes/class-wp-community-events.php'; check_ajax_referer( 'community_events' ); $search = isset( $_POST['location'] ) ? wp_unslash( $_POST['location'] ) : ''; $timezone = isset( $_POST['timezone'] ) ? wp_unslash( $_POST['timezone'] ) : ''; $user_id = get_current_user_id(); $saved_location = get_user_option( 'community-events-location', $user_id ); $events_client = new WP_Community_Events( $user_id, $saved_location ); $events = $events_client->get_events( $search, $timezone ); $ip_changed = false; if ( is_wp_error( $events ) ) { wp_send_json_error( array( 'error' => $events->get_error_message(), ) ); } else { if ( empty( $saved_location['ip'] ) && ! empty( $events['location']['ip'] ) ) { $ip_changed = true; } elseif ( isset( $saved_location['ip'] ) && ! empty( $events['location']['ip'] ) && $saved_location['ip'] !== $events['location']['ip'] ) { $ip_changed = true; } /* * The location should only be updated when it changes. The API doesn't always return * a full location; sometimes it's missing the description or country. The location * that was saved during the initial request is known to be good and complete, though. * It should be left intact until the user explicitly changes it (either by manually * searching for a new location, or by changing their IP address). * * If the location was updated with an incomplete response from the API, then it could * break assumptions that the UI makes (e.g., that there will always be a description * that corresponds to a latitude/longitude location). * * The location is stored network-wide, so that the user doesn't have to set it on each site. */ if ( $ip_changed || $search ) { update_user_meta( $user_id, 'community-events-location', $events['location'] ); } wp_send_json_success( $events ); } } /** * Handles dashboard widgets via AJAX. * * @since 3.4.0 */ function wp_ajax_dashboard_widgets() { require_once ABSPATH . 'wp-admin/includes/dashboard.php'; $pagenow = $_GET['pagenow']; if ( 'dashboard-user' === $pagenow || 'dashboard-network' === $pagenow || 'dashboard' === $pagenow ) { set_current_screen( $pagenow ); } switch ( $_GET['widget'] ) { case 'dashboard_primary': wp_dashboard_primary(); break; } wp_die(); } /** * Handles Customizer preview logged-in status via AJAX. * * @since 3.4.0 */ function wp_ajax_logged_in() { wp_die( 1 ); } // // Ajax helpers. // /** * Sends back current comment total and new page links if they need to be updated. * * Contrary to normal success Ajax response ("1"), die with time() on success. * * @since 2.7.0 * @access private * * @param int $comment_id * @param int $delta */ function _wp_ajax_delete_comment_response( $comment_id, $delta = -1 ) { $total = isset( $_POST['_total'] ) ? (int) $_POST['_total'] : 0; $per_page = isset( $_POST['_per_page'] ) ? (int) $_POST['_per_page'] : 0; $page = isset( $_POST['_page'] ) ? (int) $_POST['_page'] : 0; $url = isset( $_POST['_url'] ) ? sanitize_url( $_POST['_url'] ) : ''; // JS didn't send us everything we need to know. Just die with success message. if ( ! $total || ! $per_page || ! $page || ! $url ) { $time = time(); $comment = get_comment( $comment_id ); $comment_status = ''; $comment_link = ''; if ( $comment ) { $comment_status = $comment->comment_approved; } if ( 1 === (int) $comment_status ) { $comment_link = get_comment_link( $comment ); } $counts = wp_count_comments(); $x = new WP_Ajax_Response( array( 'what' => 'comment', // Here for completeness - not used. 'id' => $comment_id, 'supplemental' => array( 'status' => $comment_status, 'postId' => $comment ? $comment->comment_post_ID : '', 'time' => $time, 'in_moderation' => $counts->moderated, 'i18n_comments_text' => sprintf( /* translators: %s: Number of comments. */ _n( '%s Comment', '%s Comments', $counts->approved ), number_format_i18n( $counts->approved ) ), 'i18n_moderation_text' => sprintf( /* translators: %s: Number of comments. */ _n( '%s Comment in moderation', '%s Comments in moderation', $counts->moderated ), number_format_i18n( $counts->moderated ) ), 'comment_link' => $comment_link, ), ) ); $x->send(); } $total += $delta; if ( $total < 0 ) { $total = 0; } // Only do the expensive stuff on a page-break, and about 1 other time per page. if ( 0 == $total % $per_page || 1 == mt_rand( 1, $per_page ) ) { $post_id = 0; // What type of comment count are we looking for? $status = 'all'; $parsed = parse_url( $url ); if ( isset( $parsed['query'] ) ) { parse_str( $parsed['query'], $query_vars ); if ( ! empty( $query_vars['comment_status'] ) ) { $status = $query_vars['comment_status']; } if ( ! empty( $query_vars['p'] ) ) { $post_id = (int) $query_vars['p']; } if ( ! empty( $query_vars['comment_type'] ) ) { $type = $query_vars['comment_type']; } } if ( empty( $type ) ) { // Only use the comment count if not filtering by a comment_type. $comment_count = wp_count_comments( $post_id ); // We're looking for a known type of comment count. if ( isset( $comment_count->$status ) ) { $total = $comment_count->$status; } } // Else use the decremented value from above. } // The time since the last comment count. $time = time(); $comment = get_comment( $comment_id ); $counts = wp_count_comments(); $x = new WP_Ajax_Response( array( 'what' => 'comment', 'id' => $comment_id, 'supplemental' => array( 'status' => $comment ? $comment->comment_approved : '', 'postId' => $comment ? $comment->comment_post_ID : '', /* translators: %s: Number of comments. */ 'total_items_i18n' => sprintf( _n( '%s item', '%s items', $total ), number_format_i18n( $total ) ), 'total_pages' => (int) ceil( $total / $per_page ), 'total_pages_i18n' => number_format_i18n( (int) ceil( $total / $per_page ) ), 'total' => $total, 'time' => $time, 'in_moderation' => $counts->moderated, 'i18n_moderation_text' => sprintf( /* translators: %s: Number of comments. */ _n( '%s Comment in moderation', '%s Comments in moderation', $counts->moderated ), number_format_i18n( $counts->moderated ) ), ), ) ); $x->send(); } // // POST-based Ajax handlers. // /** * Handles adding a hierarchical term via AJAX. * * @since 3.1.0 * @access private */ function _wp_ajax_add_hierarchical_term() { $action = $_POST['action']; $taxonomy = get_taxonomy( substr( $action, 4 ) ); check_ajax_referer( $action, '_ajax_nonce-add-' . $taxonomy->name ); if ( ! current_user_can( $taxonomy->cap->edit_terms ) ) { wp_die( -1 ); } $names = explode( ',', $_POST[ 'new' . $taxonomy->name ] ); $parent = isset( $_POST[ 'new' . $taxonomy->name . '_parent' ] ) ? (int) $_POST[ 'new' . $taxonomy->name . '_parent' ] : 0; if ( 0 > $parent ) { $parent = 0; } if ( 'category' === $taxonomy->name ) { $post_category = isset( $_POST['post_category'] ) ? (array) $_POST['post_category'] : array(); } else { $post_category = ( isset( $_POST['tax_input'] ) && isset( $_POST['tax_input'][ $taxonomy->name ] ) ) ? (array) $_POST['tax_input'][ $taxonomy->name ] : array(); } $checked_categories = array_map( 'absint', (array) $post_category ); $popular_ids = wp_popular_terms_checklist( $taxonomy->name, 0, 10, false ); foreach ( $names as $cat_name ) { $cat_name = trim( $cat_name ); $category_nicename = sanitize_title( $cat_name ); if ( '' === $category_nicename ) { continue; } $cat_id = wp_insert_term( $cat_name, $taxonomy->name, array( 'parent' => $parent ) ); if ( ! $cat_id || is_wp_error( $cat_id ) ) { continue; } else { $cat_id = $cat_id['term_id']; } $checked_categories[] = $cat_id; if ( $parent ) { // Do these all at once in a second. continue; } ob_start(); wp_terms_checklist( 0, array( 'taxonomy' => $taxonomy->name, 'descendants_and_self' => $cat_id, 'selected_cats' => $checked_categories, 'popular_cats' => $popular_ids, ) ); $data = ob_get_clean(); $add = array( 'what' => $taxonomy->name, 'id' => $cat_id, 'data' => str_replace( array( "\n", "\t" ), '', $data ), 'position' => -1, ); } if ( $parent ) { // Foncy - replace the parent and all its children. $parent = get_term( $parent, $taxonomy->name ); $term_id = $parent->term_id; while ( $parent->parent ) { // Get the top parent. $parent = get_term( $parent->parent, $taxonomy->name ); if ( is_wp_error( $parent ) ) { break; } $term_id = $parent->term_id; } ob_start(); wp_terms_checklist( 0, array( 'taxonomy' => $taxonomy->name, 'descendants_and_self' => $term_id, 'selected_cats' => $checked_categories, 'popular_cats' => $popular_ids, ) ); $data = ob_get_clean(); $add = array( 'what' => $taxonomy->name, 'id' => $term_id, 'data' => str_replace( array( "\n", "\t" ), '', $data ), 'position' => -1, ); } ob_start(); wp_dropdown_categories( array( 'taxonomy' => $taxonomy->name, 'hide_empty' => 0, 'name' => 'new' . $taxonomy->name . '_parent', 'orderby' => 'name', 'hierarchical' => 1, 'show_option_none' => '— ' . $taxonomy->labels->parent_item . ' —', ) ); $sup = ob_get_clean(); $add['supplemental'] = array( 'newcat_parent' => $sup ); $x = new WP_Ajax_Response( $add ); $x->send(); } /** * Handles deleting a comment via AJAX. * * @since 3.1.0 */ function wp_ajax_delete_comment() { $id = isset( $_POST['id'] ) ? (int) $_POST['id'] : 0; $comment = get_comment( $id ); if ( ! $comment ) { wp_die( time() ); } if ( ! current_user_can( 'edit_comment', $comment->comment_ID ) ) { wp_die( -1 ); } check_ajax_referer( "delete-comment_$id" ); $status = wp_get_comment_status( $comment ); $delta = -1; if ( isset( $_POST['trash'] ) && 1 == $_POST['trash'] ) { if ( 'trash' === $status ) { wp_die( time() ); } $r = wp_trash_comment( $comment ); } elseif ( isset( $_POST['untrash'] ) && 1 == $_POST['untrash'] ) { if ( 'trash' !== $status ) { wp_die( time() ); } $r = wp_untrash_comment( $comment ); // Undo trash, not in Trash. if ( ! isset( $_POST['comment_status'] ) || 'trash' !== $_POST['comment_status'] ) { $delta = 1; } } elseif ( isset( $_POST['spam'] ) && 1 == $_POST['spam'] ) { if ( 'spam' === $status ) { wp_die( time() ); } $r = wp_spam_comment( $comment ); } elseif ( isset( $_POST['unspam'] ) && 1 == $_POST['unspam'] ) { if ( 'spam' !== $status ) { wp_die( time() ); } $r = wp_unspam_comment( $comment ); // Undo spam, not in spam. if ( ! isset( $_POST['comment_status'] ) || 'spam' !== $_POST['comment_status'] ) { $delta = 1; } } elseif ( isset( $_POST['delete'] ) && 1 == $_POST['delete'] ) { $r = wp_delete_comment( $comment ); } else { wp_die( -1 ); } if ( $r ) { // Decide if we need to send back '1' or a more complicated response including page links and comment counts. _wp_ajax_delete_comment_response( $comment->comment_ID, $delta ); } wp_die( 0 ); } /** * Handles deleting a tag via AJAX. * * @since 3.1.0 */ function wp_ajax_delete_tag() { $tag_id = (int) $_POST['tag_ID']; check_ajax_referer( "delete-tag_$tag_id" ); if ( ! current_user_can( 'delete_term', $tag_id ) ) { wp_die( -1 ); } $taxonomy = ! empty( $_POST['taxonomy'] ) ? $_POST['taxonomy'] : 'post_tag'; $tag = get_term( $tag_id, $taxonomy ); if ( ! $tag || is_wp_error( $tag ) ) { wp_die( 1 ); } if ( wp_delete_term( $tag_id, $taxonomy ) ) { wp_die( 1 ); } else { wp_die( 0 ); } } /** * Handles deleting a link via AJAX. * * @since 3.1.0 */ function wp_ajax_delete_link() { $id = isset( $_POST['id'] ) ? (int) $_POST['id'] : 0; check_ajax_referer( "delete-bookmark_$id" ); if ( ! current_user_can( 'manage_links' ) ) { wp_die( -1 ); } $link = get_bookmark( $id ); if ( ! $link || is_wp_error( $link ) ) { wp_die( 1 ); } if ( wp_delete_link( $id ) ) { wp_die( 1 ); } else { wp_die( 0 ); } } /** * Handles deleting meta via AJAX. * * @since 3.1.0 */ function wp_ajax_delete_meta() { $id = isset( $_POST['id'] ) ? (int) $_POST['id'] : 0; check_ajax_referer( "delete-meta_$id" ); $meta = get_metadata_by_mid( 'post', $id ); if ( ! $meta ) { wp_die( 1 ); } if ( is_protected_meta( $meta->meta_key, 'post' ) || ! current_user_can( 'delete_post_meta', $meta->post_id, $meta->meta_key ) ) { wp_die( -1 ); } if ( delete_meta( $meta->meta_id ) ) { wp_die( 1 ); } wp_die( 0 ); } /** * Handles deleting a post via AJAX. * * @since 3.1.0 * * @param string $action Action to perform. */ function wp_ajax_delete_post( $action ) { if ( empty( $action ) ) { $action = 'delete-post'; } $id = isset( $_POST['id'] ) ? (int) $_POST['id'] : 0; check_ajax_referer( "{$action}_$id" ); if ( ! current_user_can( 'delete_post', $id ) ) { wp_die( -1 ); } if ( ! get_post( $id ) ) { wp_die( 1 ); } if ( wp_delete_post( $id ) ) { wp_die( 1 ); } else { wp_die( 0 ); } } /** * Handles sending a post to the Trash via AJAX. * * @since 3.1.0 * * @param string $action Action to perform. */ function wp_ajax_trash_post( $action ) { if ( empty( $action ) ) { $action = 'trash-post'; } $id = isset( $_POST['id'] ) ? (int) $_POST['id'] : 0; check_ajax_referer( "{$action}_$id" ); if ( ! current_user_can( 'delete_post', $id ) ) { wp_die( -1 ); } if ( ! get_post( $id ) ) { wp_die( 1 ); } if ( 'trash-post' === $action ) { $done = wp_trash_post( $id ); } else { $done = wp_untrash_post( $id ); } if ( $done ) { wp_die( 1 ); } wp_die( 0 ); } /** * Handles restoring a post from the Trash via AJAX. * * @since 3.1.0 * * @param string $action Action to perform. */ function wp_ajax_untrash_post( $action ) { if ( empty( $action ) ) { $action = 'untrash-post'; } wp_ajax_trash_post( $action ); } /** * Handles deleting a page via AJAX. * * @since 3.1.0 * * @param string $action Action to perform. */ function wp_ajax_delete_page( $action ) { if ( empty( $action ) ) { $action = 'delete-page'; } $id = isset( $_POST['id'] ) ? (int) $_POST['id'] : 0; check_ajax_referer( "{$action}_$id" ); if ( ! current_user_can( 'delete_page', $id ) ) { wp_die( -1 ); } if ( ! get_post( $id ) ) { wp_die( 1 ); } if ( wp_delete_post( $id ) ) { wp_die( 1 ); } else { wp_die( 0 ); } } /** * Handles dimming a comment via AJAX. * * @since 3.1.0 */ function wp_ajax_dim_comment() { $id = isset( $_POST['id'] ) ? (int) $_POST['id'] : 0; $comment = get_comment( $id ); if ( ! $comment ) { $x = new WP_Ajax_Response( array( 'what' => 'comment', 'id' => new WP_Error( 'invalid_comment', /* translators: %d: Comment ID. */ sprintf( __( 'Comment %d does not exist' ), $id ) ), ) ); $x->send(); } if ( ! current_user_can( 'edit_comment', $comment->comment_ID ) && ! current_user_can( 'moderate_comments' ) ) { wp_die( -1 ); } $current = wp_get_comment_status( $comment ); if ( isset( $_POST['new'] ) && $_POST['new'] == $current ) { wp_die( time() ); } check_ajax_referer( "approve-comment_$id" ); if ( in_array( $current, array( 'unapproved', 'spam' ), true ) ) { $result = wp_set_comment_status( $comment, 'approve', true ); } else { $result = wp_set_comment_status( $comment, 'hold', true ); } if ( is_wp_error( $result ) ) { $x = new WP_Ajax_Response( array( 'what' => 'comment', 'id' => $result, ) ); $x->send(); } // Decide if we need to send back '1' or a more complicated response including page links and comment counts. _wp_ajax_delete_comment_response( $comment->comment_ID ); wp_die( 0 ); } /** * Handles adding a link category via AJAX. * * @since 3.1.0 * * @param string $action Action to perform. */ function wp_ajax_add_link_category( $action ) { if ( empty( $action ) ) { $action = 'add-link-category'; } check_ajax_referer( $action ); $taxonomy_object = get_taxonomy( 'link_category' ); if ( ! current_user_can( $taxonomy_object->cap->manage_terms ) ) { wp_die( -1 ); } $names = explode( ',', wp_unslash( $_POST['newcat'] ) ); $x = new WP_Ajax_Response(); foreach ( $names as $cat_name ) { $cat_name = trim( $cat_name ); $slug = sanitize_title( $cat_name ); if ( '' === $slug ) { continue; } $cat_id = wp_insert_term( $cat_name, 'link_category' ); if ( ! $cat_id || is_wp_error( $cat_id ) ) { continue; } else { $cat_id = $cat_id['term_id']; } $cat_name = esc_html( $cat_name ); $x->add( array( 'what' => 'link-category', 'id' => $cat_id, 'data' => "<li id='link-category-$cat_id'><label for='in-link-category-$cat_id' class='selectit'><input value='" . esc_attr( $cat_id ) . "' type='checkbox' checked='checked' name='link_category[]' id='in-link-category-$cat_id'/> $cat_name</label></li>", 'position' => -1, ) ); } $x->send(); } /** * Handles adding a tag via AJAX. * * @since 3.1.0 */ function wp_ajax_add_tag() { check_ajax_referer( 'add-tag', '_wpnonce_add-tag' ); $taxonomy = ! empty( $_POST['taxonomy'] ) ? $_POST['taxonomy'] : 'post_tag'; $taxonomy_object = get_taxonomy( $taxonomy ); if ( ! current_user_can( $taxonomy_object->cap->edit_terms ) ) { wp_die( -1 ); } $x = new WP_Ajax_Response(); $tag = wp_insert_term( $_POST['tag-name'], $taxonomy, $_POST ); if ( $tag && ! is_wp_error( $tag ) ) { $tag = get_term( $tag['term_id'], $taxonomy ); } if ( ! $tag || is_wp_error( $tag ) ) { $message = __( 'An error has occurred. Please reload the page and try again.' ); $error_code = 'error'; if ( is_wp_error( $tag ) && $tag->get_error_message() ) { $message = $tag->get_error_message(); } if ( is_wp_error( $tag ) && $tag->get_error_code() ) { $error_code = $tag->get_error_code(); } $x->add( array( 'what' => 'taxonomy', 'data' => new WP_Error( $error_code, $message ), ) ); $x->send(); } $wp_list_table = _get_list_table( 'WP_Terms_List_Table', array( 'screen' => $_POST['screen'] ) ); $level = 0; $noparents = ''; if ( is_taxonomy_hierarchical( $taxonomy ) ) { $level = count( get_ancestors( $tag->term_id, $taxonomy, 'taxonomy' ) ); ob_start(); $wp_list_table->single_row( $tag, $level ); $noparents = ob_get_clean(); } ob_start(); $wp_list_table->single_row( $tag ); $parents = ob_get_clean(); require ABSPATH . 'wp-admin/includes/edit-tag-messages.php'; $message = ''; if ( isset( $messages[ $taxonomy_object->name ][1] ) ) { $message = $messages[ $taxonomy_object->name ][1]; } elseif ( isset( $messages['_item'][1] ) ) { $message = $messages['_item'][1]; } $x->add( array( 'what' => 'taxonomy', 'data' => $message, 'supplemental' => array( 'parents' => $parents, 'noparents' => $noparents, 'notice' => $message, ), ) ); $x->add( array( 'what' => 'term', 'position' => $level, 'supplemental' => (array) $tag, ) ); $x->send(); } /** * Handles getting a tagcloud via AJAX. * * @since 3.1.0 */ function wp_ajax_get_tagcloud() { if ( ! isset( $_POST['tax'] ) ) { wp_die( 0 ); } $taxonomy = sanitize_key( $_POST['tax'] ); $taxonomy_object = get_taxonomy( $taxonomy ); if ( ! $taxonomy_object ) { wp_die( 0 ); } if ( ! current_user_can( $taxonomy_object->cap->assign_terms ) ) { wp_die( -1 ); } $tags = get_terms( array( 'taxonomy' => $taxonomy, 'number' => 45, 'orderby' => 'count', 'order' => 'DESC', ) ); if ( empty( $tags ) ) { wp_die( $taxonomy_object->labels->not_found ); } if ( is_wp_error( $tags ) ) { wp_die( $tags->get_error_message() ); } foreach ( $tags as $key => $tag ) { $tags[ $key ]->link = '#'; $tags[ $key ]->id = $tag->term_id; } // We need raw tag names here, so don't filter the output. $return = wp_generate_tag_cloud( $tags, array( 'filter' => 0, 'format' => 'list', ) ); if ( empty( $return ) ) { wp_die( 0 ); } echo $return; wp_die(); } /** * Handles getting comments via AJAX. * * @since 3.1.0 * * @global int $post_id * * @param string $action Action to perform. */ function wp_ajax_get_comments( $action ) { global $post_id; if ( empty( $action ) ) { $action = 'get-comments'; } check_ajax_referer( $action ); if ( empty( $post_id ) && ! empty( $_REQUEST['p'] ) ) { $id = absint( $_REQUEST['p'] ); if ( ! empty( $id ) ) { $post_id = $id; } } if ( empty( $post_id ) ) { wp_die( -1 ); } $wp_list_table = _get_list_table( 'WP_Post_Comments_List_Table', array( 'screen' => 'edit-comments' ) ); if ( ! current_user_can( 'edit_post', $post_id ) ) { wp_die( -1 ); } $wp_list_table->prepare_items(); if ( ! $wp_list_table->has_items() ) { wp_die( 1 ); } $x = new WP_Ajax_Response(); ob_start(); foreach ( $wp_list_table->items as $comment ) { if ( ! current_user_can( 'edit_comment', $comment->comment_ID ) && 0 === $comment->comment_approved ) { continue; } get_comment( $comment ); $wp_list_table->single_row( $comment ); } $comment_list_item = ob_get_clean(); $x->add( array( 'what' => 'comments', 'data' => $comment_list_item, ) ); $x->send(); } /** * Handles replying to a comment via AJAX. * * @since 3.1.0 * * @param string $action Action to perform. */ function wp_ajax_replyto_comment( $action ) { if ( empty( $action ) ) { $action = 'replyto-comment'; } check_ajax_referer( $action, '_ajax_nonce-replyto-comment' ); $comment_post_id = (int) $_POST['comment_post_ID']; $post = get_post( $comment_post_id ); if ( ! $post ) { wp_die( -1 ); } if ( ! current_user_can( 'edit_post', $comment_post_id ) ) { wp_die( -1 ); } if ( empty( $post->post_status ) ) { wp_die( 1 ); } elseif ( in_array( $post->post_status, array( 'draft', 'pending', 'trash' ), true ) ) { wp_die( __( 'You cannot reply to a comment on a draft post.' ) ); } $user = wp_get_current_user(); if ( $user->exists() ) { $comment_author = wp_slash( $user->display_name ); $comment_author_email = wp_slash( $user->user_email ); $comment_author_url = wp_slash( $user->user_url ); $user_id = $user->ID; if ( current_user_can( 'unfiltered_html' ) ) { if ( ! isset( $_POST['_wp_unfiltered_html_comment'] ) ) { $_POST['_wp_unfiltered_html_comment'] = ''; } if ( wp_create_nonce( 'unfiltered-html-comment' ) != $_POST['_wp_unfiltered_html_comment'] ) { kses_remove_filters(); // Start with a clean slate. kses_init_filters(); // Set up the filters. remove_filter( 'pre_comment_content', 'wp_filter_post_kses' ); add_filter( 'pre_comment_content', 'wp_filter_kses' ); } } } else { wp_die( __( 'Sorry, you must be logged in to reply to a comment.' ) ); } $comment_content = trim( $_POST['content'] ); if ( '' === $comment_content ) { wp_die( __( 'Please type your comment text.' ) ); } $comment_type = isset( $_POST['comment_type'] ) ? trim( $_POST['comment_type'] ) : 'comment'; $comment_parent = 0; if ( isset( $_POST['comment_ID'] ) ) { $comment_parent = absint( $_POST['comment_ID'] ); } $comment_auto_approved = false; $commentdata = array( 'comment_post_ID' => $comment_post_id, ); $commentdata += compact( 'comment_author', 'comment_author_email', 'comment_author_url', 'comment_content', 'comment_type', 'comment_parent', 'user_id' ); // Automatically approve parent comment. if ( ! empty( $_POST['approve_parent'] ) ) { $parent = get_comment( $comment_parent ); if ( $parent && '0' === $parent->comment_approved && $parent->comment_post_ID == $comment_post_id ) { if ( ! current_user_can( 'edit_comment', $parent->comment_ID ) ) { wp_die( -1 ); } if ( wp_set_comment_status( $parent, 'approve' ) ) { $comment_auto_approved = true; } } } $comment_id = wp_new_comment( $commentdata ); if ( is_wp_error( $comment_id ) ) { wp_die( $comment_id->get_error_message() ); } $comment = get_comment( $comment_id ); if ( ! $comment ) { wp_die( 1 ); } $position = ( isset( $_POST['position'] ) && (int) $_POST['position'] ) ? (int) $_POST['position'] : '-1'; ob_start(); if ( isset( $_REQUEST['mode'] ) && 'dashboard' === $_REQUEST['mode'] ) { require_once ABSPATH . 'wp-admin/includes/dashboard.php'; _wp_dashboard_recent_comments_row( $comment ); } else { if ( isset( $_REQUEST['mode'] ) && 'single' === $_REQUEST['mode'] ) { $wp_list_table = _get_list_table( 'WP_Post_Comments_List_Table', array( 'screen' => 'edit-comments' ) ); } else { $wp_list_table = _get_list_table( 'WP_Comments_List_Table', array( 'screen' => 'edit-comments' ) ); } $wp_list_table->single_row( $comment ); } $comment_list_item = ob_get_clean(); $response = array( 'what' => 'comment', 'id' => $comment->comment_ID, 'data' => $comment_list_item, 'position' => $position, ); $counts = wp_count_comments(); $response['supplemental'] = array( 'in_moderation' => $counts->moderated, 'i18n_comments_text' => sprintf( /* translators: %s: Number of comments. */ _n( '%s Comment', '%s Comments', $counts->approved ), number_format_i18n( $counts->approved ) ), 'i18n_moderation_text' => sprintf( /* translators: %s: Number of comments. */ _n( '%s Comment in moderation', '%s Comments in moderation', $counts->moderated ), number_format_i18n( $counts->moderated ) ), ); if ( $comment_auto_approved ) { $response['supplemental']['parent_approved'] = $parent->comment_ID; $response['supplemental']['parent_post_id'] = $parent->comment_post_ID; } $x = new WP_Ajax_Response(); $x->add( $response ); $x->send(); } /** * Handles editing a comment via AJAX. * * @since 3.1.0 */ function wp_ajax_edit_comment() { check_ajax_referer( 'replyto-comment', '_ajax_nonce-replyto-comment' ); $comment_id = (int) $_POST['comment_ID']; if ( ! current_user_can( 'edit_comment', $comment_id ) ) { wp_die( -1 ); } if ( '' === $_POST['content'] ) { wp_die( __( 'Please type your comment text.' ) ); } if ( isset( $_POST['status'] ) ) { $_POST['comment_status'] = $_POST['status']; } $updated = edit_comment(); if ( is_wp_error( $updated ) ) { wp_die( $updated->get_error_message() ); } $position = ( isset( $_POST['position'] ) && (int) $_POST['position'] ) ? (int) $_POST['position'] : '-1'; $checkbox = ( isset( $_POST['checkbox'] ) && true == $_POST['checkbox'] ) ? 1 : 0; $wp_list_table = _get_list_table( $checkbox ? 'WP_Comments_List_Table' : 'WP_Post_Comments_List_Table', array( 'screen' => 'edit-comments' ) ); $comment = get_comment( $comment_id ); if ( empty( $comment->comment_ID ) ) { wp_die( -1 ); } ob_start(); $wp_list_table->single_row( $comment ); $comment_list_item = ob_get_clean(); $x = new WP_Ajax_Response(); $x->add( array( 'what' => 'edit_comment', 'id' => $comment->comment_ID, 'data' => $comment_list_item, 'position' => $position, ) ); $x->send(); } /** * Handles adding a menu item via AJAX. * * @since 3.1.0 */ function wp_ajax_add_menu_item() { check_ajax_referer( 'add-menu_item', 'menu-settings-column-nonce' ); if ( ! current_user_can( 'edit_theme_options' ) ) { wp_die( -1 ); } require_once ABSPATH . 'wp-admin/includes/nav-menu.php'; /* * For performance reasons, we omit some object properties from the checklist. * The following is a hacky way to restore them when adding non-custom items. */ $menu_items_data = array(); foreach ( (array) $_POST['menu-item'] as $menu_item_data ) { if ( ! empty( $menu_item_data['menu-item-type'] ) && 'custom' !== $menu_item_data['menu-item-type'] && ! empty( $menu_item_data['menu-item-object-id'] ) ) { switch ( $menu_item_data['menu-item-type'] ) { case 'post_type': $_object = get_post( $menu_item_data['menu-item-object-id'] ); break; case 'post_type_archive': $_object = get_post_type_object( $menu_item_data['menu-item-object'] ); break; case 'taxonomy': $_object = get_term( $menu_item_data['menu-item-object-id'], $menu_item_data['menu-item-object'] ); break; } $_menu_items = array_map( 'wp_setup_nav_menu_item', array( $_object ) ); $_menu_item = reset( $_menu_items ); // Restore the missing menu item properties. $menu_item_data['menu-item-description'] = $_menu_item->description; } $menu_items_data[] = $menu_item_data; } $item_ids = wp_save_nav_menu_items( 0, $menu_items_data ); if ( is_wp_error( $item_ids ) ) { wp_die( 0 ); } $menu_items = array(); foreach ( (array) $item_ids as $menu_item_id ) { $menu_obj = get_post( $menu_item_id ); if ( ! empty( $menu_obj->ID ) ) { $menu_obj = wp_setup_nav_menu_item( $menu_obj ); $menu_obj->title = empty( $menu_obj->title ) ? __( 'Menu Item' ) : $menu_obj->title; $menu_obj->label = $menu_obj->title; // Don't show "(pending)" in ajax-added items. $menu_items[] = $menu_obj; } } /** This filter is documented in wp-admin/includes/nav-menu.php */ $walker_class_name = apply_filters( 'wp_edit_nav_menu_walker', 'Walker_Nav_Menu_Edit', $_POST['menu'] ); if ( ! class_exists( $walker_class_name ) ) { wp_die( 0 ); } if ( ! empty( $menu_items ) ) { $args = array( 'after' => '', 'before' => '', 'link_after' => '', 'link_before' => '', 'walker' => new $walker_class_name(), ); echo walk_nav_menu_tree( $menu_items, 0, (object) $args ); } wp_die(); } /** * Handles adding meta via AJAX. * * @since 3.1.0 */ function wp_ajax_add_meta() { check_ajax_referer( 'add-meta', '_ajax_nonce-add-meta' ); $c = 0; $pid = (int) $_POST['post_id']; $post = get_post( $pid ); if ( isset( $_POST['metakeyselect'] ) || isset( $_POST['metakeyinput'] ) ) { if ( ! current_user_can( 'edit_post', $pid ) ) { wp_die( -1 ); } if ( isset( $_POST['metakeyselect'] ) && '#NONE#' === $_POST['metakeyselect'] && empty( $_POST['metakeyinput'] ) ) { wp_die( 1 ); } // If the post is an autodraft, save the post as a draft and then attempt to save the meta. if ( 'auto-draft' === $post->post_status ) { $post_data = array(); $post_data['action'] = 'draft'; // Warning fix. $post_data['post_ID'] = $pid; $post_data['post_type'] = $post->post_type; $post_data['post_status'] = 'draft'; $now = time(); $post_data['post_title'] = sprintf( /* translators: 1: Post creation date, 2: Post creation time. */ __( 'Draft created on %1$s at %2$s' ), gmdate( __( 'F j, Y' ), $now ), gmdate( __( 'g:i a' ), $now ) ); $pid = edit_post( $post_data ); if ( $pid ) { if ( is_wp_error( $pid ) ) { $x = new WP_Ajax_Response( array( 'what' => 'meta', 'data' => $pid, ) ); $x->send(); } $mid = add_meta( $pid ); if ( ! $mid ) { wp_die( __( 'Please provide a custom field value.' ) ); } } else { wp_die( 0 ); } } else { $mid = add_meta( $pid ); if ( ! $mid ) { wp_die( __( 'Please provide a custom field value.' ) ); } } $meta = get_metadata_by_mid( 'post', $mid ); $pid = (int) $meta->post_id; $meta = get_object_vars( $meta ); $x = new WP_Ajax_Response( array( 'what' => 'meta', 'id' => $mid, 'data' => _list_meta_row( $meta, $c ), 'position' => 1, 'supplemental' => array( 'postid' => $pid ), ) ); } else { // Update? $mid = (int) key( $_POST['meta'] ); $key = wp_unslash( $_POST['meta'][ $mid ]['key'] ); $value = wp_unslash( $_POST['meta'][ $mid ]['value'] ); if ( '' === trim( $key ) ) { wp_die( __( 'Please provide a custom field name.' ) ); } $meta = get_metadata_by_mid( 'post', $mid ); if ( ! $meta ) { wp_die( 0 ); // If meta doesn't exist. } if ( is_protected_meta( $meta->meta_key, 'post' ) || is_protected_meta( $key, 'post' ) || ! current_user_can( 'edit_post_meta', $meta->post_id, $meta->meta_key ) || ! current_user_can( 'edit_post_meta', $meta->post_id, $key ) ) { wp_die( -1 ); } if ( $meta->meta_value != $value || $meta->meta_key != $key ) { $u = update_metadata_by_mid( 'post', $mid, $value, $key ); if ( ! $u ) { wp_die( 0 ); // We know meta exists; we also know it's unchanged (or DB error, in which case there are bigger problems). } } $x = new WP_Ajax_Response( array( 'what' => 'meta', 'id' => $mid, 'old_id' => $mid, 'data' => _list_meta_row( array( 'meta_key' => $key, 'meta_value' => $value, 'meta_id' => $mid, ), $c ), 'position' => 0, 'supplemental' => array( 'postid' => $meta->post_id ), ) ); } $x->send(); } /** * Handles adding a user via AJAX. * * @since 3.1.0 * * @param string $action Action to perform. */ function wp_ajax_add_user( $action ) { if ( empty( $action ) ) { $action = 'add-user'; } check_ajax_referer( $action ); if ( ! current_user_can( 'create_users' ) ) { wp_die( -1 ); } $user_id = edit_user(); if ( ! $user_id ) { wp_die( 0 ); } elseif ( is_wp_error( $user_id ) ) { $x = new WP_Ajax_Response( array( 'what' => 'user', 'id' => $user_id, ) ); $x->send(); } $user_object = get_userdata( $user_id ); $wp_list_table = _get_list_table( 'WP_Users_List_Table' ); $role = current( $user_object->roles ); $x = new WP_Ajax_Response( array( 'what' => 'user', 'id' => $user_id, 'data' => $wp_list_table->single_row( $user_object, '', $role ), 'supplemental' => array( 'show-link' => sprintf( /* translators: %s: The new user. */ __( 'User %s added' ), '<a href="#user-' . $user_id . '">' . $user_object->user_login . '</a>' ), 'role' => $role, ), ) ); $x->send(); } /** * Handles closed post boxes via AJAX. * * @since 3.1.0 */ function wp_ajax_closed_postboxes() { check_ajax_referer( 'closedpostboxes', 'closedpostboxesnonce' ); $closed = isset( $_POST['closed'] ) ? explode( ',', $_POST['closed'] ) : array(); $closed = array_filter( $closed ); $hidden = isset( $_POST['hidden'] ) ? explode( ',', $_POST['hidden'] ) : array(); $hidden = array_filter( $hidden ); $page = isset( $_POST['page'] ) ? $_POST['page'] : ''; if ( sanitize_key( $page ) != $page ) { wp_die( 0 ); } $user = wp_get_current_user(); if ( ! $user ) { wp_die( -1 ); } if ( is_array( $closed ) ) { update_user_meta( $user->ID, "closedpostboxes_$page", $closed ); } if ( is_array( $hidden ) ) { // Postboxes that are always shown. $hidden = array_diff( $hidden, array( 'submitdiv', 'linksubmitdiv', 'manage-menu', 'create-menu' ) ); update_user_meta( $user->ID, "metaboxhidden_$page", $hidden ); } wp_die( 1 ); } /** * Handles hidden columns via AJAX. * * @since 3.1.0 */ function wp_ajax_hidden_columns() { check_ajax_referer( 'screen-options-nonce', 'screenoptionnonce' ); $page = isset( $_POST['page'] ) ? $_POST['page'] : ''; if ( sanitize_key( $page ) != $page ) { wp_die( 0 ); } $user = wp_get_current_user(); if ( ! $user ) { wp_die( -1 ); } $hidden = ! empty( $_POST['hidden'] ) ? explode( ',', $_POST['hidden'] ) : array(); update_user_meta( $user->ID, "manage{$page}columnshidden", $hidden ); wp_die( 1 ); } /** * Handles updating whether to display the welcome panel via AJAX. * * @since 3.1.0 */ function wp_ajax_update_welcome_panel() { check_ajax_referer( 'welcome-panel-nonce', 'welcomepanelnonce' ); if ( ! current_user_can( 'edit_theme_options' ) ) { wp_die( -1 ); } update_user_meta( get_current_user_id(), 'show_welcome_panel', empty( $_POST['visible'] ) ? 0 : 1 ); wp_die( 1 ); } /** * Handles for retrieving menu meta boxes via AJAX. * * @since 3.1.0 */ function wp_ajax_menu_get_metabox() { if ( ! current_user_can( 'edit_theme_options' ) ) { wp_die( -1 ); } require_once ABSPATH . 'wp-admin/includes/nav-menu.php'; if ( isset( $_POST['item-type'] ) && 'post_type' === $_POST['item-type'] ) { $type = 'posttype'; $callback = 'wp_nav_menu_item_post_type_meta_box'; $items = (array) get_post_types( array( 'show_in_nav_menus' => true ), 'object' ); } elseif ( isset( $_POST['item-type'] ) && 'taxonomy' === $_POST['item-type'] ) { $type = 'taxonomy'; $callback = 'wp_nav_menu_item_taxonomy_meta_box'; $items = (array) get_taxonomies( array( 'show_ui' => true ), 'object' ); } if ( ! empty( $_POST['item-object'] ) && isset( $items[ $_POST['item-object'] ] ) ) { $menus_meta_box_object = $items[ $_POST['item-object'] ]; /** This filter is documented in wp-admin/includes/nav-menu.php */ $item = apply_filters( 'nav_menu_meta_box_object', $menus_meta_box_object ); $box_args = array( 'id' => 'add-' . $item->name, 'title' => $item->labels->name, 'callback' => $callback, 'args' => $item, ); ob_start(); $callback( null, $box_args ); $markup = ob_get_clean(); echo wp_json_encode( array( 'replace-id' => $type . '-' . $item->name, 'markup' => $markup, ) ); } wp_die(); } /** * Handles internal linking via AJAX. * * @since 3.1.0 */ function wp_ajax_wp_link_ajax() { check_ajax_referer( 'internal-linking', '_ajax_linking_nonce' ); $args = array(); if ( isset( $_POST['search'] ) ) { $args['s'] = wp_unslash( $_POST['search'] ); } if ( isset( $_POST['term'] ) ) { $args['s'] = wp_unslash( $_POST['term'] ); } $args['pagenum'] = ! empty( $_POST['page'] ) ? absint( $_POST['page'] ) : 1; if ( ! class_exists( '_WP_Editors', false ) ) { require ABSPATH . WPINC . '/class-wp-editor.php'; } $results = _WP_Editors::wp_link_query( $args ); if ( ! isset( $results ) ) { wp_die( 0 ); } echo wp_json_encode( $results ); echo "\n"; wp_die(); } /** * Handles saving menu locations via AJAX. * * @since 3.1.0 */ function wp_ajax_menu_locations_save() { if ( ! current_user_can( 'edit_theme_options' ) ) { wp_die( -1 ); } check_ajax_referer( 'add-menu_item', 'menu-settings-column-nonce' ); if ( ! isset( $_POST['menu-locations'] ) ) { wp_die( 0 ); } set_theme_mod( 'nav_menu_locations', array_map( 'absint', $_POST['menu-locations'] ) ); wp_die( 1 ); } /** * Handles saving the meta box order via AJAX. * * @since 3.1.0 */ function wp_ajax_meta_box_order() { check_ajax_referer( 'meta-box-order' ); $order = isset( $_POST['order'] ) ? (array) $_POST['order'] : false; $page_columns = isset( $_POST['page_columns'] ) ? $_POST['page_columns'] : 'auto'; if ( 'auto' !== $page_columns ) { $page_columns = (int) $page_columns; } $page = isset( $_POST['page'] ) ? $_POST['page'] : ''; if ( sanitize_key( $page ) != $page ) { wp_die( 0 ); } $user = wp_get_current_user(); if ( ! $user ) { wp_die( -1 ); } if ( $order ) { update_user_meta( $user->ID, "meta-box-order_$page", $order ); } if ( $page_columns ) { update_user_meta( $user->ID, "screen_layout_$page", $page_columns ); } wp_send_json_success(); } /** * Handles menu quick searching via AJAX. * * @since 3.1.0 */ function wp_ajax_menu_quick_search() { if ( ! current_user_can( 'edit_theme_options' ) ) { wp_die( -1 ); } require_once ABSPATH . 'wp-admin/includes/nav-menu.php'; _wp_ajax_menu_quick_search( $_POST ); wp_die(); } /** * Handles retrieving a permalink via AJAX. * * @since 3.1.0 */ function wp_ajax_get_permalink() { check_ajax_referer( 'getpermalink', 'getpermalinknonce' ); $post_id = isset( $_POST['post_id'] ) ? (int) $_POST['post_id'] : 0; wp_die( get_preview_post_link( $post_id ) ); } /** * Handles retrieving a sample permalink via AJAX. * * @since 3.1.0 */ function wp_ajax_sample_permalink() { check_ajax_referer( 'samplepermalink', 'samplepermalinknonce' ); $post_id = isset( $_POST['post_id'] ) ? (int) $_POST['post_id'] : 0; $title = isset( $_POST['new_title'] ) ? $_POST['new_title'] : ''; $slug = isset( $_POST['new_slug'] ) ? $_POST['new_slug'] : null; wp_die( get_sample_permalink_html( $post_id, $title, $slug ) ); } /** * Handles Quick Edit saving a post from a list table via AJAX. * * @since 3.1.0 * * @global string $mode List table view mode. */ function wp_ajax_inline_save() { global $mode; check_ajax_referer( 'inlineeditnonce', '_inline_edit' ); if ( ! isset( $_POST['post_ID'] ) || ! (int) $_POST['post_ID'] ) { wp_die(); } $post_id = (int) $_POST['post_ID']; if ( 'page' === $_POST['post_type'] ) { if ( ! current_user_can( 'edit_page', $post_id ) ) { wp_die( __( 'Sorry, you are not allowed to edit this page.' ) ); } } else { if ( ! current_user_can( 'edit_post', $post_id ) ) { wp_die( __( 'Sorry, you are not allowed to edit this post.' ) ); } } $last = wp_check_post_lock( $post_id ); if ( $last ) { $last_user = get_userdata( $last ); $last_user_name = $last_user ? $last_user->display_name : __( 'Someone' ); /* translators: %s: User's display name. */ $msg_template = __( 'Saving is disabled: %s is currently editing this post.' ); if ( 'page' === $_POST['post_type'] ) { /* translators: %s: User's display name. */ $msg_template = __( 'Saving is disabled: %s is currently editing this page.' ); } printf( $msg_template, esc_html( $last_user_name ) ); wp_die(); } $data = &$_POST; $post = get_post( $post_id, ARRAY_A ); // Since it's coming from the database. $post = wp_slash( $post ); $data['content'] = $post['post_content']; $data['excerpt'] = $post['post_excerpt']; // Rename. $data['user_ID'] = get_current_user_id(); if ( isset( $data['post_parent'] ) ) { $data['parent_id'] = $data['post_parent']; } // Status. if ( isset( $data['keep_private'] ) && 'private' === $data['keep_private'] ) { $data['visibility'] = 'private'; $data['post_status'] = 'private'; } else { $data['post_status'] = $data['_status']; } if ( empty( $data['comment_status'] ) ) { $data['comment_status'] = 'closed'; } if ( empty( $data['ping_status'] ) ) { $data['ping_status'] = 'closed'; } // Exclude terms from taxonomies that are not supposed to appear in Quick Edit. if ( ! empty( $data['tax_input'] ) ) { foreach ( $data['tax_input'] as $taxonomy => $terms ) { $tax_object = get_taxonomy( $taxonomy ); /** This filter is documented in wp-admin/includes/class-wp-posts-list-table.php */ if ( ! apply_filters( 'quick_edit_show_taxonomy', $tax_object->show_in_quick_edit, $taxonomy, $post['post_type'] ) ) { unset( $data['tax_input'][ $taxonomy ] ); } } } // Hack: wp_unique_post_slug() doesn't work for drafts, so we will fake that our post is published. if ( ! empty( $data['post_name'] ) && in_array( $post['post_status'], array( 'draft', 'pending' ), true ) ) { $post['post_status'] = 'publish'; $data['post_name'] = wp_unique_post_slug( $data['post_name'], $post['ID'], $post['post_status'], $post['post_type'], $post['post_parent'] ); } // Update the post. edit_post(); $wp_list_table = _get_list_table( 'WP_Posts_List_Table', array( 'screen' => $_POST['screen'] ) ); $mode = 'excerpt' === $_POST['post_view'] ? 'excerpt' : 'list'; $level = 0; if ( is_post_type_hierarchical( $wp_list_table->screen->post_type ) ) { $request_post = array( get_post( $_POST['post_ID'] ) ); $parent = $request_post[0]->post_parent; while ( $parent > 0 ) { $parent_post = get_post( $parent ); $parent = $parent_post->post_parent; ++$level; } } $wp_list_table->display_rows( array( get_post( $_POST['post_ID'] ) ), $level ); wp_die(); } /** * Handles Quick Edit saving for a term via AJAX. * * @since 3.1.0 */ function wp_ajax_inline_save_tax() { check_ajax_referer( 'taxinlineeditnonce', '_inline_edit' ); $taxonomy = sanitize_key( $_POST['taxonomy'] ); $taxonomy_object = get_taxonomy( $taxonomy ); if ( ! $taxonomy_object ) { wp_die( 0 ); } if ( ! isset( $_POST['tax_ID'] ) || ! (int) $_POST['tax_ID'] ) { wp_die( -1 ); } $id = (int) $_POST['tax_ID']; if ( ! current_user_can( 'edit_term', $id ) ) { wp_die( -1 ); } $wp_list_table = _get_list_table( 'WP_Terms_List_Table', array( 'screen' => 'edit-' . $taxonomy ) ); $tag = get_term( $id, $taxonomy ); $_POST['description'] = $tag->description; $updated = wp_update_term( $id, $taxonomy, $_POST ); if ( $updated && ! is_wp_error( $updated ) ) { $tag = get_term( $updated['term_id'], $taxonomy ); if ( ! $tag || is_wp_error( $tag ) ) { if ( is_wp_error( $tag ) && $tag->get_error_message() ) { wp_die( $tag->get_error_message() ); } wp_die( __( 'Item not updated.' ) ); } } else { if ( is_wp_error( $updated ) && $updated->get_error_message() ) { wp_die( $updated->get_error_message() ); } wp_die( __( 'Item not updated.' ) ); } $level = 0; $parent = $tag->parent; while ( $parent > 0 ) { $parent_tag = get_term( $parent, $taxonomy ); $parent = $parent_tag->parent; ++$level; } $wp_list_table->single_row( $tag, $level ); wp_die(); } /** * Handles querying posts for the Find Posts modal via AJAX. * * @see window.findPosts * * @since 3.1.0 */ function wp_ajax_find_posts() { check_ajax_referer( 'find-posts' ); $post_types = get_post_types( array( 'public' => true ), 'objects' ); unset( $post_types['attachment'] ); $args = array( 'post_type' => array_keys( $post_types ), 'post_status' => 'any', 'posts_per_page' => 50, ); $search = wp_unslash( $_POST['ps'] ); if ( '' !== $search ) { $args['s'] = $search; } $posts = get_posts( $args ); if ( ! $posts ) { wp_send_json_error( __( 'No items found.' ) ); } $html = '<table class="widefat"><thead><tr><th class="found-radio"><br /></th><th>' . __( 'Title' ) . '</th><th class="no-break">' . __( 'Type' ) . '</th><th class="no-break">' . __( 'Date' ) . '</th><th class="no-break">' . __( 'Status' ) . '</th></tr></thead><tbody>'; $alt = ''; foreach ( $posts as $post ) { $title = trim( $post->post_title ) ? $post->post_title : __( '(no title)' ); $alt = ( 'alternate' === $alt ) ? '' : 'alternate'; switch ( $post->post_status ) { case 'publish': case 'private': $stat = __( 'Published' ); break; case 'future': $stat = __( 'Scheduled' ); break; case 'pending': $stat = __( 'Pending Review' ); break; case 'draft': $stat = __( 'Draft' ); break; } if ( '0000-00-00 00:00:00' === $post->post_date ) { $time = ''; } else { /* translators: Date format in table columns, see https://www.php.net/manual/datetime.format.php */ $time = mysql2date( __( 'Y/m/d' ), $post->post_date ); } $html .= '<tr class="' . trim( 'found-posts ' . $alt ) . '"><td class="found-radio"><input type="radio" id="found-' . $post->ID . '" name="found_post_id" value="' . esc_attr( $post->ID ) . '"></td>'; $html .= '<td><label for="found-' . $post->ID . '">' . esc_html( $title ) . '</label></td><td class="no-break">' . esc_html( $post_types[ $post->post_type ]->labels->singular_name ) . '</td><td class="no-break">' . esc_html( $time ) . '</td><td class="no-break">' . esc_html( $stat ) . ' </td></tr>' . "\n\n"; } $html .= '</tbody></table>'; wp_send_json_success( $html ); } /** * Handles saving the widgets order via AJAX. * * @since 3.1.0 */ function wp_ajax_widgets_order() { check_ajax_referer( 'save-sidebar-widgets', 'savewidgets' ); if ( ! current_user_can( 'edit_theme_options' ) ) { wp_die( -1 ); } unset( $_POST['savewidgets'], $_POST['action'] ); // Save widgets order for all sidebars. if ( is_array( $_POST['sidebars'] ) ) { $sidebars = array(); foreach ( wp_unslash( $_POST['sidebars'] ) as $key => $val ) { $sb = array(); if ( ! empty( $val ) ) { $val = explode( ',', $val ); foreach ( $val as $k => $v ) { if ( ! str_contains( $v, 'widget-' ) ) { continue; } $sb[ $k ] = substr( $v, strpos( $v, '_' ) + 1 ); } } $sidebars[ $key ] = $sb; } wp_set_sidebars_widgets( $sidebars ); wp_die( 1 ); } wp_die( -1 ); } /** * Handles saving a widget via AJAX. * * @since 3.1.0 * * @global array $wp_registered_widgets * @global array $wp_registered_widget_controls * @global array $wp_registered_widget_updates */ function wp_ajax_save_widget() { global $wp_registered_widgets, $wp_registered_widget_controls, $wp_registered_widget_updates; check_ajax_referer( 'save-sidebar-widgets', 'savewidgets' ); if ( ! current_user_can( 'edit_theme_options' ) || ! isset( $_POST['id_base'] ) ) { wp_die( -1 ); } unset( $_POST['savewidgets'], $_POST['action'] ); /** * Fires early when editing the widgets displayed in sidebars. * * @since 2.8.0 */ do_action( 'load-widgets.php' ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores /** * Fires early when editing the widgets displayed in sidebars. * * @since 2.8.0 */ do_action( 'widgets.php' ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores /** This action is documented in wp-admin/widgets.php */ do_action( 'sidebar_admin_setup' ); $id_base = wp_unslash( $_POST['id_base'] ); $widget_id = wp_unslash( $_POST['widget-id'] ); $sidebar_id = $_POST['sidebar']; $multi_number = ! empty( $_POST['multi_number'] ) ? (int) $_POST['multi_number'] : 0; $settings = isset( $_POST[ 'widget-' . $id_base ] ) && is_array( $_POST[ 'widget-' . $id_base ] ) ? $_POST[ 'widget-' . $id_base ] : false; $error = '<p>' . __( 'An error has occurred. Please reload the page and try again.' ) . '</p>'; $sidebars = wp_get_sidebars_widgets(); $sidebar = isset( $sidebars[ $sidebar_id ] ) ? $sidebars[ $sidebar_id ] : array(); // Delete. if ( isset( $_POST['delete_widget'] ) && $_POST['delete_widget'] ) { if ( ! isset( $wp_registered_widgets[ $widget_id ] ) ) { wp_die( $error ); } $sidebar = array_diff( $sidebar, array( $widget_id ) ); $_POST = array( 'sidebar' => $sidebar_id, 'widget-' . $id_base => array(), 'the-widget-id' => $widget_id, 'delete_widget' => '1', ); /** This action is documented in wp-admin/widgets.php */ do_action( 'delete_widget', $widget_id, $sidebar_id, $id_base ); } elseif ( $settings && preg_match( '/__i__|%i%/', key( $settings ) ) ) { if ( ! $multi_number ) { wp_die( $error ); } $_POST[ 'widget-' . $id_base ] = array( $multi_number => reset( $settings ) ); $widget_id = $id_base . '-' . $multi_number; $sidebar[] = $widget_id; } $_POST['widget-id'] = $sidebar; foreach ( (array) $wp_registered_widget_updates as $name => $control ) { if ( $name == $id_base ) { if ( ! is_callable( $control['callback'] ) ) { continue; } ob_start(); call_user_func_array( $control['callback'], $control['params'] ); ob_end_clean(); break; } } if ( isset( $_POST['delete_widget'] ) && $_POST['delete_widget'] ) { $sidebars[ $sidebar_id ] = $sidebar; wp_set_sidebars_widgets( $sidebars ); echo "deleted:$widget_id"; wp_die(); } if ( ! empty( $_POST['add_new'] ) ) { wp_die(); } $form = $wp_registered_widget_controls[ $widget_id ]; if ( $form ) { call_user_func_array( $form['callback'], $form['params'] ); } wp_die(); } /** * Handles updating a widget via AJAX. * * @since 3.9.0 * * @global WP_Customize_Manager $wp_customize */ function wp_ajax_update_widget() { global $wp_customize; $wp_customize->widgets->wp_ajax_update_widget(); } /** * Handles removing inactive widgets via AJAX. * * @since 4.4.0 */ function wp_ajax_delete_inactive_widgets() { check_ajax_referer( 'remove-inactive-widgets', 'removeinactivewidgets' ); if ( ! current_user_can( 'edit_theme_options' ) ) { wp_die( -1 ); } unset( $_POST['removeinactivewidgets'], $_POST['action'] ); /** This action is documented in wp-admin/includes/ajax-actions.php */ do_action( 'load-widgets.php' ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores /** This action is documented in wp-admin/includes/ajax-actions.php */ do_action( 'widgets.php' ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores /** This action is documented in wp-admin/widgets.php */ do_action( 'sidebar_admin_setup' ); $sidebars_widgets = wp_get_sidebars_widgets(); foreach ( $sidebars_widgets['wp_inactive_widgets'] as $key => $widget_id ) { $pieces = explode( '-', $widget_id ); $multi_number = array_pop( $pieces ); $id_base = implode( '-', $pieces ); $widget = get_option( 'widget_' . $id_base ); unset( $widget[ $multi_number ] ); update_option( 'widget_' . $id_base, $widget ); unset( $sidebars_widgets['wp_inactive_widgets'][ $key ] ); } wp_set_sidebars_widgets( $sidebars_widgets ); wp_die(); } /** * Handles creating missing image sub-sizes for just uploaded images via AJAX. * * @since 5.3.0 */ function wp_ajax_media_create_image_subsizes() { check_ajax_referer( 'media-form' ); if ( ! current_user_can( 'upload_files' ) ) { wp_send_json_error( array( 'message' => __( 'Sorry, you are not allowed to upload files.' ) ) ); } if ( empty( $_POST['attachment_id'] ) ) { wp_send_json_error( array( 'message' => __( 'Upload failed. Please reload and try again.' ) ) ); } $attachment_id = (int) $_POST['attachment_id']; if ( ! empty( $_POST['_wp_upload_failed_cleanup'] ) ) { // Upload failed. Cleanup. if ( wp_attachment_is_image( $attachment_id ) && current_user_can( 'delete_post', $attachment_id ) ) { $attachment = get_post( $attachment_id ); // Created at most 10 min ago. if ( $attachment && ( time() - strtotime( $attachment->post_date_gmt ) < 600 ) ) { wp_delete_attachment( $attachment_id, true ); wp_send_json_success(); } } } /* * Set a custom header with the attachment_id. * Used by the browser/client to resume creating image sub-sizes after a PHP fatal error. */ if ( ! headers_sent() ) { header( 'X-WP-Upload-Attachment-ID: ' . $attachment_id ); } /* * This can still be pretty slow and cause timeout or out of memory errors. * The js that handles the response would need to also handle HTTP 500 errors. */ wp_update_image_subsizes( $attachment_id ); if ( ! empty( $_POST['_legacy_support'] ) ) { // The old (inline) uploader. Only needs the attachment_id. $response = array( 'id' => $attachment_id ); } else { // Media modal and Media Library grid view. $response = wp_prepare_attachment_for_js( $attachment_id ); if ( ! $response ) { wp_send_json_error( array( 'message' => __( 'Upload failed.' ) ) ); } } // At this point the image has been uploaded successfully. wp_send_json_success( $response ); } /** * Handles uploading attachments via AJAX. * * @since 3.3.0 */ function wp_ajax_upload_attachment() { check_ajax_referer( 'media-form' ); /* * This function does not use wp_send_json_success() / wp_send_json_error() * as the html4 Plupload handler requires a text/html Content-Type for older IE. * See https://core.trac.wordpress.org/ticket/31037 */ if ( ! current_user_can( 'upload_files' ) ) { echo wp_json_encode( array( 'success' => false, 'data' => array( 'message' => __( 'Sorry, you are not allowed to upload files.' ), 'filename' => esc_html( $_FILES['async-upload']['name'] ), ), ) ); wp_die(); } if ( isset( $_REQUEST['post_id'] ) ) { $post_id = $_REQUEST['post_id']; if ( ! current_user_can( 'edit_post', $post_id ) ) { echo wp_json_encode( array( 'success' => false, 'data' => array( 'message' => __( 'Sorry, you are not allowed to attach files to this post.' ), 'filename' => esc_html( $_FILES['async-upload']['name'] ), ), ) ); wp_die(); } } else { $post_id = null; } $post_data = ! empty( $_REQUEST['post_data'] ) ? _wp_get_allowed_postdata( _wp_translate_postdata( false, (array) $_REQUEST['post_data'] ) ) : array(); if ( is_wp_error( $post_data ) ) { wp_die( $post_data->get_error_message() ); } // If the context is custom header or background, make sure the uploaded file is an image. if ( isset( $post_data['context'] ) && in_array( $post_data['context'], array( 'custom-header', 'custom-background' ), true ) ) { $wp_filetype = wp_check_filetype_and_ext( $_FILES['async-upload']['tmp_name'], $_FILES['async-upload']['name'] ); if ( ! wp_match_mime_types( 'image', $wp_filetype['type'] ) ) { echo wp_json_encode( array( 'success' => false, 'data' => array( 'message' => __( 'The uploaded file is not a valid image. Please try again.' ), 'filename' => esc_html( $_FILES['async-upload']['name'] ), ), ) ); wp_die(); } } $attachment_id = media_handle_upload( 'async-upload', $post_id, $post_data ); if ( is_wp_error( $attachment_id ) ) { echo wp_json_encode( array( 'success' => false, 'data' => array( 'message' => $attachment_id->get_error_message(), 'filename' => esc_html( $_FILES['async-upload']['name'] ), ), ) ); wp_die(); } if ( isset( $post_data['context'] ) && isset( $post_data['theme'] ) ) { if ( 'custom-background' === $post_data['context'] ) { update_post_meta( $attachment_id, '_wp_attachment_is_custom_background', $post_data['theme'] ); } if ( 'custom-header' === $post_data['context'] ) { update_post_meta( $attachment_id, '_wp_attachment_is_custom_header', $post_data['theme'] ); } } $attachment = wp_prepare_attachment_for_js( $attachment_id ); if ( ! $attachment ) { wp_die(); } echo wp_json_encode( array( 'success' => true, 'data' => $attachment, ) ); wp_die(); } /** * Handles image editing via AJAX. * * @since 3.1.0 */ function wp_ajax_image_editor() { $attachment_id = (int) $_POST['postid']; if ( empty( $attachment_id ) || ! current_user_can( 'edit_post', $attachment_id ) ) { wp_die( -1 ); } check_ajax_referer( "image_editor-$attachment_id" ); require_once ABSPATH . 'wp-admin/includes/image-edit.php'; $msg = false; switch ( $_POST['do'] ) { case 'save': $msg = wp_save_image( $attachment_id ); if ( ! empty( $msg->error ) ) { wp_send_json_error( $msg ); } wp_send_json_success( $msg ); break; case 'scale': $msg = wp_save_image( $attachment_id ); break; case 'restore': $msg = wp_restore_image( $attachment_id ); break; } ob_start(); wp_image_editor( $attachment_id, $msg ); $html = ob_get_clean(); if ( ! empty( $msg->error ) ) { wp_send_json_error( array( 'message' => $msg, 'html' => $html, ) ); } wp_send_json_success( array( 'message' => $msg, 'html' => $html, ) ); } /** * Handles setting the featured image via AJAX. * * @since 3.1.0 */ function wp_ajax_set_post_thumbnail() { $json = ! empty( $_REQUEST['json'] ); // New-style request. $post_id = (int) $_POST['post_id']; if ( ! current_user_can( 'edit_post', $post_id ) ) { wp_die( -1 ); } $thumbnail_id = (int) $_POST['thumbnail_id']; if ( $json ) { check_ajax_referer( "update-post_$post_id" ); } else { check_ajax_referer( "set_post_thumbnail-$post_id" ); } if ( '-1' == $thumbnail_id ) { if ( delete_post_thumbnail( $post_id ) ) { $return = _wp_post_thumbnail_html( null, $post_id ); $json ? wp_send_json_success( $return ) : wp_die( $return ); } else { wp_die( 0 ); } } if ( set_post_thumbnail( $post_id, $thumbnail_id ) ) { $return = _wp_post_thumbnail_html( $thumbnail_id, $post_id ); $json ? wp_send_json_success( $return ) : wp_die( $return ); } wp_die( 0 ); } /** * Handles retrieving HTML for the featured image via AJAX. * * @since 4.6.0 */ function wp_ajax_get_post_thumbnail_html() { $post_id = (int) $_POST['post_id']; check_ajax_referer( "update-post_$post_id" ); if ( ! current_user_can( 'edit_post', $post_id ) ) { wp_die( -1 ); } $thumbnail_id = (int) $_POST['thumbnail_id']; // For backward compatibility, -1 refers to no featured image. if ( -1 === $thumbnail_id ) { $thumbnail_id = null; } $return = _wp_post_thumbnail_html( $thumbnail_id, $post_id ); wp_send_json_success( $return ); } /** * Handles setting the featured image for an attachment via AJAX. * * @since 4.0.0 * * @see set_post_thumbnail() */ function wp_ajax_set_attachment_thumbnail() { if ( empty( $_POST['urls'] ) || ! is_array( $_POST['urls'] ) ) { wp_send_json_error(); } $thumbnail_id = (int) $_POST['thumbnail_id']; if ( empty( $thumbnail_id ) ) { wp_send_json_error(); } if ( false === check_ajax_referer( 'set-attachment-thumbnail', '_ajax_nonce', false ) ) { wp_send_json_error(); } $post_ids = array(); // For each URL, try to find its corresponding post ID. foreach ( $_POST['urls'] as $url ) { $post_id = attachment_url_to_postid( $url ); if ( ! empty( $post_id ) ) { $post_ids[] = $post_id; } } if ( empty( $post_ids ) ) { wp_send_json_error(); } $success = 0; // For each found attachment, set its thumbnail. foreach ( $post_ids as $post_id ) { if ( ! current_user_can( 'edit_post', $post_id ) ) { continue; } if ( set_post_thumbnail( $post_id, $thumbnail_id ) ) { ++$success; } } if ( 0 === $success ) { wp_send_json_error(); } else { wp_send_json_success(); } wp_send_json_error(); } /** * Handles formatting a date via AJAX. * * @since 3.1.0 */ function wp_ajax_date_format() { wp_die( date_i18n( sanitize_option( 'date_format', wp_unslash( $_POST['date'] ) ) ) ); } /** * Handles formatting a time via AJAX. * * @since 3.1.0 */ function wp_ajax_time_format() { wp_die( date_i18n( sanitize_option( 'time_format', wp_unslash( $_POST['date'] ) ) ) ); } /** * Handles saving posts from the fullscreen editor via AJAX. * * @since 3.1.0 * @deprecated 4.3.0 */ function wp_ajax_wp_fullscreen_save_post() { $post_id = isset( $_POST['post_ID'] ) ? (int) $_POST['post_ID'] : 0; $post = null; if ( $post_id ) { $post = get_post( $post_id ); } check_ajax_referer( 'update-post_' . $post_id, '_wpnonce' ); $post_id = edit_post(); if ( is_wp_error( $post_id ) ) { wp_send_json_error(); } if ( $post ) { $last_date = mysql2date( __( 'F j, Y' ), $post->post_modified ); $last_time = mysql2date( __( 'g:i a' ), $post->post_modified ); } else { $last_date = date_i18n( __( 'F j, Y' ) ); $last_time = date_i18n( __( 'g:i a' ) ); } $last_id = get_post_meta( $post_id, '_edit_last', true ); if ( $last_id ) { $last_user = get_userdata( $last_id ); /* translators: 1: User's display name, 2: Date of last edit, 3: Time of last edit. */ $last_edited = sprintf( __( 'Last edited by %1$s on %2$s at %3$s' ), esc_html( $last_user->display_name ), $last_date, $last_time ); } else { /* translators: 1: Date of last edit, 2: Time of last edit. */ $last_edited = sprintf( __( 'Last edited on %1$s at %2$s' ), $last_date, $last_time ); } wp_send_json_success( array( 'last_edited' => $last_edited ) ); } /** * Handles removing a post lock via AJAX. * * @since 3.1.0 */ function wp_ajax_wp_remove_post_lock() { if ( empty( $_POST['post_ID'] ) || empty( $_POST['active_post_lock'] ) ) { wp_die( 0 ); } $post_id = (int) $_POST['post_ID']; $post = get_post( $post_id ); if ( ! $post ) { wp_die( 0 ); } check_ajax_referer( 'update-post_' . $post_id ); if ( ! current_user_can( 'edit_post', $post_id ) ) { wp_die( -1 ); } $active_lock = array_map( 'absint', explode( ':', $_POST['active_post_lock'] ) ); if ( get_current_user_id() != $active_lock[1] ) { wp_die( 0 ); } /** * Filters the post lock window duration. * * @since 3.3.0 * * @param int $interval The interval in seconds the post lock duration * should last, plus 5 seconds. Default 150. */ $new_lock = ( time() - apply_filters( 'wp_check_post_lock_window', 150 ) + 5 ) . ':' . $active_lock[1]; update_post_meta( $post_id, '_edit_lock', $new_lock, implode( ':', $active_lock ) ); wp_die( 1 ); } /** * Handles dismissing a WordPress pointer via AJAX. * * @since 3.1.0 */ function wp_ajax_dismiss_wp_pointer() { $pointer = $_POST['pointer']; if ( sanitize_key( $pointer ) != $pointer ) { wp_die( 0 ); } // check_ajax_referer( 'dismiss-pointer_' . $pointer ); $dismissed = array_filter( explode( ',', (string) get_user_meta( get_current_user_id(), 'dismissed_wp_pointers', true ) ) ); if ( in_array( $pointer, $dismissed, true ) ) { wp_die( 0 ); } $dismissed[] = $pointer; $dismissed = implode( ',', $dismissed ); update_user_meta( get_current_user_id(), 'dismissed_wp_pointers', $dismissed ); wp_die( 1 ); } /** * Handles getting an attachment via AJAX. * * @since 3.5.0 */ function wp_ajax_get_attachment() { if ( ! isset( $_REQUEST['id'] ) ) { wp_send_json_error(); } $id = absint( $_REQUEST['id'] ); if ( ! $id ) { wp_send_json_error(); } $post = get_post( $id ); if ( ! $post ) { wp_send_json_error(); } if ( 'attachment' !== $post->post_type ) { wp_send_json_error(); } if ( ! current_user_can( 'upload_files' ) ) { wp_send_json_error(); } $attachment = wp_prepare_attachment_for_js( $id ); if ( ! $attachment ) { wp_send_json_error(); } wp_send_json_success( $attachment ); } /** * Handles querying attachments via AJAX. * * @since 3.5.0 */ function wp_ajax_query_attachments() { if ( ! current_user_can( 'upload_files' ) ) { wp_send_json_error(); } $query = isset( $_REQUEST['query'] ) ? (array) $_REQUEST['query'] : array(); $keys = array( 's', 'order', 'orderby', 'posts_per_page', 'paged', 'post_mime_type', 'post_parent', 'author', 'post__in', 'post__not_in', 'year', 'monthnum', ); foreach ( get_taxonomies_for_attachments( 'objects' ) as $t ) { if ( $t->query_var && isset( $query[ $t->query_var ] ) ) { $keys[] = $t->query_var; } } $query = array_intersect_key( $query, array_flip( $keys ) ); $query['post_type'] = 'attachment'; if ( MEDIA_TRASH && ! empty( $_REQUEST['query']['post_status'] ) && 'trash' === $_REQUEST['query']['post_status'] ) { $query['post_status'] = 'trash'; } else { $query['post_status'] = 'inherit'; } if ( current_user_can( get_post_type_object( 'attachment' )->cap->read_private_posts ) ) { $query['post_status'] .= ',private'; } // Filter query clauses to include filenames. if ( isset( $query['s'] ) ) { add_filter( 'wp_allow_query_attachment_by_filename', '__return_true' ); } /** * Filters the arguments passed to WP_Query during an Ajax * call for querying attachments. * * @since 3.7.0 * * @see WP_Query::parse_query() * * @param array $query An array of query variables. */ $query = apply_filters( 'ajax_query_attachments_args', $query ); $attachments_query = new WP_Query( $query ); update_post_parent_caches( $attachments_query->posts ); $posts = array_map( 'wp_prepare_attachment_for_js', $attachments_query->posts ); $posts = array_filter( $posts ); $total_posts = $attachments_query->found_posts; if ( $total_posts < 1 ) { // Out-of-bounds, run the query again without LIMIT for total count. unset( $query['paged'] ); $count_query = new WP_Query(); $count_query->query( $query ); $total_posts = $count_query->found_posts; } $posts_per_page = (int) $attachments_query->get( 'posts_per_page' ); $max_pages = $posts_per_page ? (int) ceil( $total_posts / $posts_per_page ) : 0; header( 'X-WP-Total: ' . (int) $total_posts ); header( 'X-WP-TotalPages: ' . $max_pages ); wp_send_json_success( $posts ); } /** * Handles updating attachment attributes via AJAX. * * @since 3.5.0 */ function wp_ajax_save_attachment() { if ( ! isset( $_REQUEST['id'] ) || ! isset( $_REQUEST['changes'] ) ) { wp_send_json_error(); } $id = absint( $_REQUEST['id'] ); if ( ! $id ) { wp_send_json_error(); } check_ajax_referer( 'update-post_' . $id, 'nonce' ); if ( ! current_user_can( 'edit_post', $id ) ) { wp_send_json_error(); } $changes = $_REQUEST['changes']; $post = get_post( $id, ARRAY_A ); if ( 'attachment' !== $post['post_type'] ) { wp_send_json_error(); } if ( isset( $changes['parent'] ) ) { $post['post_parent'] = $changes['parent']; } if ( isset( $changes['title'] ) ) { $post['post_title'] = $changes['title']; } if ( isset( $changes['caption'] ) ) { $post['post_excerpt'] = $changes['caption']; } if ( isset( $changes['description'] ) ) { $post['post_content'] = $changes['description']; } if ( MEDIA_TRASH && isset( $changes['status'] ) ) { $post['post_status'] = $changes['status']; } if ( isset( $changes['alt'] ) ) { $alt = wp_unslash( $changes['alt'] ); if ( get_post_meta( $id, '_wp_attachment_image_alt', true ) !== $alt ) { $alt = wp_strip_all_tags( $alt, true ); update_post_meta( $id, '_wp_attachment_image_alt', wp_slash( $alt ) ); } } if ( wp_attachment_is( 'audio', $post['ID'] ) ) { $changed = false; $id3data = wp_get_attachment_metadata( $post['ID'] ); if ( ! is_array( $id3data ) ) { $changed = true; $id3data = array(); } foreach ( wp_get_attachment_id3_keys( (object) $post, 'edit' ) as $key => $label ) { if ( isset( $changes[ $key ] ) ) { $changed = true; $id3data[ $key ] = sanitize_text_field( wp_unslash( $changes[ $key ] ) ); } } if ( $changed ) { wp_update_attachment_metadata( $id, $id3data ); } } if ( MEDIA_TRASH && isset( $changes['status'] ) && 'trash' === $changes['status'] ) { wp_delete_post( $id ); } else { wp_update_post( $post ); } wp_send_json_success(); } /** * Handles saving backward compatible attachment attributes via AJAX. * * @since 3.5.0 */ function wp_ajax_save_attachment_compat() { if ( ! isset( $_REQUEST['id'] ) ) { wp_send_json_error(); } $id = absint( $_REQUEST['id'] ); if ( ! $id ) { wp_send_json_error(); } if ( empty( $_REQUEST['attachments'] ) || empty( $_REQUEST['attachments'][ $id ] ) ) { wp_send_json_error(); } $attachment_data = $_REQUEST['attachments'][ $id ]; check_ajax_referer( 'update-post_' . $id, 'nonce' ); if ( ! current_user_can( 'edit_post', $id ) ) { wp_send_json_error(); } $post = get_post( $id, ARRAY_A ); if ( 'attachment' !== $post['post_type'] ) { wp_send_json_error(); } /** This filter is documented in wp-admin/includes/media.php */ $post = apply_filters( 'attachment_fields_to_save', $post, $attachment_data ); if ( isset( $post['errors'] ) ) { $errors = $post['errors']; // @todo return me and display me! unset( $post['errors'] ); } wp_update_post( $post ); foreach ( get_attachment_taxonomies( $post ) as $taxonomy ) { if ( isset( $attachment_data[ $taxonomy ] ) ) { wp_set_object_terms( $id, array_map( 'trim', preg_split( '/,+/', $attachment_data[ $taxonomy ] ) ), $taxonomy, false ); } } $attachment = wp_prepare_attachment_for_js( $id ); if ( ! $attachment ) { wp_send_json_error(); } wp_send_json_success( $attachment ); } /** * Handles saving the attachment order via AJAX. * * @since 3.5.0 */ function wp_ajax_save_attachment_order() { if ( ! isset( $_REQUEST['post_id'] ) ) { wp_send_json_error(); } $post_id = absint( $_REQUEST['post_id'] ); if ( ! $post_id ) { wp_send_json_error(); } if ( empty( $_REQUEST['attachments'] ) ) { wp_send_json_error(); } check_ajax_referer( 'update-post_' . $post_id, 'nonce' ); $attachments = $_REQUEST['attachments']; if ( ! current_user_can( 'edit_post', $post_id ) ) { wp_send_json_error(); } foreach ( $attachments as $attachment_id => $menu_order ) { if ( ! current_user_can( 'edit_post', $attachment_id ) ) { continue; } $attachment = get_post( $attachment_id ); if ( ! $attachment ) { continue; } if ( 'attachment' !== $attachment->post_type ) { continue; } wp_update_post( array( 'ID' => $attachment_id, 'menu_order' => $menu_order, ) ); } wp_send_json_success(); } /** * Handles sending an attachment to the editor via AJAX. * * Generates the HTML to send an attachment to the editor. * Backward compatible with the {@see 'media_send_to_editor'} filter * and the chain of filters that follow. * * @since 3.5.0 */ function wp_ajax_send_attachment_to_editor() { check_ajax_referer( 'media-send-to-editor', 'nonce' ); $attachment = wp_unslash( $_POST['attachment'] ); $id = (int) $attachment['id']; $post = get_post( $id ); if ( ! $post ) { wp_send_json_error(); } if ( 'attachment' !== $post->post_type ) { wp_send_json_error(); } if ( current_user_can( 'edit_post', $id ) ) { // If this attachment is unattached, attach it. Primarily a back compat thing. $insert_into_post_id = (int) $_POST['post_id']; if ( 0 == $post->post_parent && $insert_into_post_id ) { wp_update_post( array( 'ID' => $id, 'post_parent' => $insert_into_post_id, ) ); } } $url = empty( $attachment['url'] ) ? '' : $attachment['url']; $rel = ( str_contains( $url, 'attachment_id' ) || get_attachment_link( $id ) === $url ); remove_filter( 'media_send_to_editor', 'image_media_send_to_editor' ); if ( str_starts_with( $post->post_mime_type, 'image' ) ) { $align = isset( $attachment['align'] ) ? $attachment['align'] : 'none'; $size = isset( $attachment['image-size'] ) ? $attachment['image-size'] : 'medium'; $alt = isset( $attachment['image_alt'] ) ? $attachment['image_alt'] : ''; // No whitespace-only captions. $caption = isset( $attachment['post_excerpt'] ) ? $attachment['post_excerpt'] : ''; if ( '' === trim( $caption ) ) { $caption = ''; } $title = ''; // We no longer insert title tags into <img> tags, as they are redundant. $html = get_image_send_to_editor( $id, $caption, $title, $align, $url, $rel, $size, $alt ); } elseif ( wp_attachment_is( 'video', $post ) || wp_attachment_is( 'audio', $post ) ) { $html = stripslashes_deep( $_POST['html'] ); } else { $html = isset( $attachment['post_title'] ) ? $attachment['post_title'] : ''; $rel = $rel ? ' rel="attachment wp-att-' . $id . '"' : ''; // Hard-coded string, $id is already sanitized. if ( ! empty( $url ) ) { $html = '<a href="' . esc_url( $url ) . '"' . $rel . '>' . $html . '</a>'; } } /** This filter is documented in wp-admin/includes/media.php */ $html = apply_filters( 'media_send_to_editor', $html, $id, $attachment ); wp_send_json_success( $html ); } /** * Handles sending a link to the editor via AJAX. * * Generates the HTML to send a non-image embed link to the editor. * * Backward compatible with the following filters: * - file_send_to_editor_url * - audio_send_to_editor_url * - video_send_to_editor_url * * @since 3.5.0 * * @global WP_Post $post Global post object. * @global WP_Embed $wp_embed */ function wp_ajax_send_link_to_editor() { global $post, $wp_embed; check_ajax_referer( 'media-send-to-editor', 'nonce' ); $src = wp_unslash( $_POST['src'] ); if ( ! $src ) { wp_send_json_error(); } if ( ! strpos( $src, '://' ) ) { $src = 'http://' . $src; } $src = sanitize_url( $src ); if ( ! $src ) { wp_send_json_error(); } $link_text = trim( wp_unslash( $_POST['link_text'] ) ); if ( ! $link_text ) { $link_text = wp_basename( $src ); } $post = get_post( isset( $_POST['post_id'] ) ? $_POST['post_id'] : 0 ); // Ping WordPress for an embed. $check_embed = $wp_embed->run_shortcode( '[embed]' . $src . '[/embed]' ); // Fallback that WordPress creates when no oEmbed was found. $fallback = $wp_embed->maybe_make_link( $src ); if ( $check_embed !== $fallback ) { // TinyMCE view for [embed] will parse this. $html = '[embed]' . $src . '[/embed]'; } elseif ( $link_text ) { $html = '<a href="' . esc_url( $src ) . '">' . $link_text . '</a>'; } else { $html = ''; } // Figure out what filter to run: $type = 'file'; $ext = preg_replace( '/^.+?\.([^.]+)$/', '$1', $src ); if ( $ext ) { $ext_type = wp_ext2type( $ext ); if ( 'audio' === $ext_type || 'video' === $ext_type ) { $type = $ext_type; } } /** This filter is documented in wp-admin/includes/media.php */ $html = apply_filters( "{$type}_send_to_editor_url", $html, $src, $link_text ); wp_send_json_success( $html ); } /** * Handles the Heartbeat API via AJAX. * * Runs when the user is logged in. * * @since 3.6.0 */ function wp_ajax_heartbeat() { if ( empty( $_POST['_nonce'] ) ) { wp_send_json_error(); } $response = array(); $data = array(); $nonce_state = wp_verify_nonce( $_POST['_nonce'], 'heartbeat-nonce' ); // 'screen_id' is the same as $current_screen->id and the JS global 'pagenow'. if ( ! empty( $_POST['screen_id'] ) ) { $screen_id = sanitize_key( $_POST['screen_id'] ); } else { $screen_id = 'front'; } if ( ! empty( $_POST['data'] ) ) { $data = wp_unslash( (array) $_POST['data'] ); } if ( 1 !== $nonce_state ) { /** * Filters the nonces to send to the New/Edit Post screen. * * @since 4.3.0 * * @param array $response The Heartbeat response. * @param array $data The $_POST data sent. * @param string $screen_id The screen ID. */ $response = apply_filters( 'wp_refresh_nonces', $response, $data, $screen_id ); if ( false === $nonce_state ) { // User is logged in but nonces have expired. $response['nonces_expired'] = true; wp_send_json( $response ); } } if ( ! empty( $data ) ) { /** * Filters the Heartbeat response received. * * @since 3.6.0 * * @param array $response The Heartbeat response. * @param array $data The $_POST data sent. * @param string $screen_id The screen ID. */ $response = apply_filters( 'heartbeat_received', $response, $data, $screen_id ); } /** * Filters the Heartbeat response sent. * * @since 3.6.0 * * @param array $response The Heartbeat response. * @param string $screen_id The screen ID. */ $response = apply_filters( 'heartbeat_send', $response, $screen_id ); /** * Fires when Heartbeat ticks in logged-in environments. * * Allows the transport to be easily replaced with long-polling. * * @since 3.6.0 * * @param array $response The Heartbeat response. * @param string $screen_id The screen ID. */ do_action( 'heartbeat_tick', $response, $screen_id ); // Send the current time according to the server. $response['server_time'] = time(); wp_send_json( $response ); } /** * Handles getting revision diffs via AJAX. * * @since 3.6.0 */ function wp_ajax_get_revision_diffs() { require ABSPATH . 'wp-admin/includes/revision.php'; $post = get_post( (int) $_REQUEST['post_id'] ); if ( ! $post ) { wp_send_json_error(); } if ( ! current_user_can( 'edit_post', $post->ID ) ) { wp_send_json_error(); } // Really just pre-loading the cache here. $revisions = wp_get_post_revisions( $post->ID, array( 'check_enabled' => false ) ); if ( ! $revisions ) { wp_send_json_error(); } $return = array(); if ( function_exists( 'set_time_limit' ) ) { set_time_limit( 0 ); } foreach ( $_REQUEST['compare'] as $compare_key ) { list( $compare_from, $compare_to ) = explode( ':', $compare_key ); // from:to $return[] = array( 'id' => $compare_key, 'fields' => wp_get_revision_ui_diff( $post, $compare_from, $compare_to ), ); } wp_send_json_success( $return ); } /** * Handles auto-saving the selected color scheme for * a user's own profile via AJAX. * * @since 3.8.0 * * @global array $_wp_admin_css_colors */ function wp_ajax_save_user_color_scheme() { global $_wp_admin_css_colors; check_ajax_referer( 'save-color-scheme', 'nonce' ); $color_scheme = sanitize_key( $_POST['color_scheme'] ); if ( ! isset( $_wp_admin_css_colors[ $color_scheme ] ) ) { wp_send_json_error(); } $previous_color_scheme = get_user_meta( get_current_user_id(), 'admin_color', true ); update_user_meta( get_current_user_id(), 'admin_color', $color_scheme ); wp_send_json_success( array( 'previousScheme' => 'admin-color-' . $previous_color_scheme, 'currentScheme' => 'admin-color-' . $color_scheme, ) ); } /** * Handles getting themes from themes_api() via AJAX. * * @since 3.9.0 * * @global array $themes_allowedtags * @global array $theme_field_defaults */ function wp_ajax_query_themes() { global $themes_allowedtags, $theme_field_defaults; if ( ! current_user_can( 'install_themes' ) ) { wp_send_json_error(); } $args = wp_parse_args( wp_unslash( $_REQUEST['request'] ), array( 'per_page' => 20, 'fields' => array_merge( (array) $theme_field_defaults, array( 'reviews_url' => true, // Explicitly request the reviews URL to be linked from the Add Themes screen. ) ), ) ); if ( isset( $args['browse'] ) && 'favorites' === $args['browse'] && ! isset( $args['user'] ) ) { $user = get_user_option( 'wporg_favorites' ); if ( $user ) { $args['user'] = $user; } } $old_filter = isset( $args['browse'] ) ? $args['browse'] : 'search'; /** This filter is documented in wp-admin/includes/class-wp-theme-install-list-table.php */ $args = apply_filters( 'install_themes_table_api_args_' . $old_filter, $args ); $api = themes_api( 'query_themes', $args ); if ( is_wp_error( $api ) ) { wp_send_json_error(); } $update_php = network_admin_url( 'update.php?action=install-theme' ); $installed_themes = search_theme_directories(); if ( false === $installed_themes ) { $installed_themes = array(); } foreach ( $installed_themes as $theme_slug => $theme_data ) { // Ignore child themes. if ( str_contains( $theme_slug, '/' ) ) { unset( $installed_themes[ $theme_slug ] ); } } foreach ( $api->themes as &$theme ) { $theme->install_url = add_query_arg( array( 'theme' => $theme->slug, '_wpnonce' => wp_create_nonce( 'install-theme_' . $theme->slug ), ), $update_php ); if ( current_user_can( 'switch_themes' ) ) { if ( is_multisite() ) { $theme->activate_url = add_query_arg( array( 'action' => 'enable', '_wpnonce' => wp_create_nonce( 'enable-theme_' . $theme->slug ), 'theme' => $theme->slug, ), network_admin_url( 'themes.php' ) ); } else { $theme->activate_url = add_query_arg( array( 'action' => 'activate', '_wpnonce' => wp_create_nonce( 'switch-theme_' . $theme->slug ), 'stylesheet' => $theme->slug, ), admin_url( 'themes.php' ) ); } } $is_theme_installed = array_key_exists( $theme->slug, $installed_themes ); // We only care about installed themes. $theme->block_theme = $is_theme_installed && wp_get_theme( $theme->slug )->is_block_theme(); if ( ! is_multisite() && current_user_can( 'edit_theme_options' ) && current_user_can( 'customize' ) ) { $customize_url = $theme->block_theme ? admin_url( 'site-editor.php' ) : wp_customize_url( $theme->slug ); $theme->customize_url = add_query_arg( array( 'return' => urlencode( network_admin_url( 'theme-install.php', 'relative' ) ), ), $customize_url ); } $theme->name = wp_kses( $theme->name, $themes_allowedtags ); $theme->author = wp_kses( $theme->author['display_name'], $themes_allowedtags ); $theme->version = wp_kses( $theme->version, $themes_allowedtags ); $theme->description = wp_kses( $theme->description, $themes_allowedtags ); $theme->stars = wp_star_rating( array( 'rating' => $theme->rating, 'type' => 'percent', 'number' => $theme->num_ratings, 'echo' => false, ) ); $theme->num_ratings = number_format_i18n( $theme->num_ratings ); $theme->preview_url = set_url_scheme( $theme->preview_url ); $theme->compatible_wp = is_wp_version_compatible( $theme->requires ); $theme->compatible_php = is_php_version_compatible( $theme->requires_php ); } wp_send_json_success( $api ); } /** * Applies [embed] Ajax handlers to a string. * * @since 4.0.0 * * @global WP_Post $post Global post object. * @global WP_Embed $wp_embed Embed API instance. * @global WP_Scripts $wp_scripts * @global int $content_width */ function wp_ajax_parse_embed() { global $post, $wp_embed, $content_width; if ( empty( $_POST['shortcode'] ) ) { wp_send_json_error(); } $post_id = isset( $_POST['post_ID'] ) ? (int) $_POST['post_ID'] : 0; if ( $post_id > 0 ) { $post = get_post( $post_id ); if ( ! $post || ! current_user_can( 'edit_post', $post->ID ) ) { wp_send_json_error(); } setup_postdata( $post ); } elseif ( ! current_user_can( 'edit_posts' ) ) { // See WP_oEmbed_Controller::get_proxy_item_permissions_check(). wp_send_json_error(); } $shortcode = wp_unslash( $_POST['shortcode'] ); preg_match( '/' . get_shortcode_regex() . '/s', $shortcode, $matches ); $atts = shortcode_parse_atts( $matches[3] ); if ( ! empty( $matches[5] ) ) { $url = $matches[5]; } elseif ( ! empty( $atts['src'] ) ) { $url = $atts['src']; } else { $url = ''; } $parsed = false; $wp_embed->return_false_on_fail = true; if ( 0 === $post_id ) { /* * Refresh oEmbeds cached outside of posts that are past their TTL. * Posts are excluded because they have separate logic for refreshing * their post meta caches. See WP_Embed::cache_oembed(). */ $wp_embed->usecache = false; } if ( is_ssl() && str_starts_with( $url, 'http://' ) ) { /* * Admin is ssl and the user pasted non-ssl URL. * Check if the provider supports ssl embeds and use that for the preview. */ $ssl_shortcode = preg_replace( '%^(\\[embed[^\\]]*\\])http://%i', '$1https://', $shortcode ); $parsed = $wp_embed->run_shortcode( $ssl_shortcode ); if ( ! $parsed ) { $no_ssl_support = true; } } // Set $content_width so any embeds fit in the destination iframe. if ( isset( $_POST['maxwidth'] ) && is_numeric( $_POST['maxwidth'] ) && $_POST['maxwidth'] > 0 ) { if ( ! isset( $content_width ) ) { $content_width = (int) $_POST['maxwidth']; } else { $content_width = min( $content_width, (int) $_POST['maxwidth'] ); } } if ( $url && ! $parsed ) { $parsed = $wp_embed->run_shortcode( $shortcode ); } if ( ! $parsed ) { wp_send_json_error( array( 'type' => 'not-embeddable', /* translators: %s: URL that could not be embedded. */ 'message' => sprintf( __( '%s failed to embed.' ), '<code>' . esc_html( $url ) . '</code>' ), ) ); } if ( has_shortcode( $parsed, 'audio' ) || has_shortcode( $parsed, 'video' ) ) { $styles = ''; $mce_styles = wpview_media_sandbox_styles(); foreach ( $mce_styles as $style ) { $styles .= sprintf( '<link rel="stylesheet" href="%s" />', $style ); } $html = do_shortcode( $parsed ); global $wp_scripts; if ( ! empty( $wp_scripts ) ) { $wp_scripts->done = array(); } ob_start(); wp_print_scripts( array( 'mediaelement-vimeo', 'wp-mediaelement' ) ); $scripts = ob_get_clean(); $parsed = $styles . $html . $scripts; } if ( ! empty( $no_ssl_support ) || ( is_ssl() && ( preg_match( '%<(iframe|script|embed) [^>]*src="http://%', $parsed ) || preg_match( '%<link [^>]*href="http://%', $parsed ) ) ) ) { // Admin is ssl and the embed is not. Iframes, scripts, and other "active content" will be blocked. wp_send_json_error( array( 'type' => 'not-ssl', 'message' => __( 'This preview is unavailable in the editor.' ), ) ); } $return = array( 'body' => $parsed, 'attr' => $wp_embed->last_attr, ); if ( str_contains( $parsed, 'class="wp-embedded-content' ) ) { if ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) { $script_src = includes_url( 'js/wp-embed.js' ); } else { $script_src = includes_url( 'js/wp-embed.min.js' ); } $return['head'] = '<script src="' . $script_src . '"></script>'; $return['sandbox'] = true; } wp_send_json_success( $return ); } /** * @since 4.0.0 * * @global WP_Post $post Global post object. * @global WP_Scripts $wp_scripts */ function wp_ajax_parse_media_shortcode() { global $post, $wp_scripts; if ( empty( $_POST['shortcode'] ) ) { wp_send_json_error(); } $shortcode = wp_unslash( $_POST['shortcode'] ); // Only process previews for media related shortcodes: $found_shortcodes = get_shortcode_tags_in_content( $shortcode ); $media_shortcodes = array( 'audio', 'embed', 'playlist', 'video', 'gallery', ); $other_shortcodes = array_diff( $found_shortcodes, $media_shortcodes ); if ( ! empty( $other_shortcodes ) ) { wp_send_json_error(); } if ( ! empty( $_POST['post_ID'] ) ) { $post = get_post( (int) $_POST['post_ID'] ); } // The embed shortcode requires a post. if ( ! $post || ! current_user_can( 'edit_post', $post->ID ) ) { if ( in_array( 'embed', $found_shortcodes, true ) ) { wp_send_json_error(); } } else { setup_postdata( $post ); } $parsed = do_shortcode( $shortcode ); if ( empty( $parsed ) ) { wp_send_json_error( array( 'type' => 'no-items', 'message' => __( 'No items found.' ), ) ); } $head = ''; $styles = wpview_media_sandbox_styles(); foreach ( $styles as $style ) { $head .= '<link type="text/css" rel="stylesheet" href="' . $style . '">'; } if ( ! empty( $wp_scripts ) ) { $wp_scripts->done = array(); } ob_start(); echo $parsed; if ( 'playlist' === $_REQUEST['type'] ) { wp_underscore_playlist_templates(); wp_print_scripts( 'wp-playlist' ); } else { wp_print_scripts( array( 'mediaelement-vimeo', 'wp-mediaelement' ) ); } wp_send_json_success( array( 'head' => $head, 'body' => ob_get_clean(), ) ); } /** * Handles destroying multiple open sessions for a user via AJAX. * * @since 4.1.0 */ function wp_ajax_destroy_sessions() { $user = get_userdata( (int) $_POST['user_id'] ); if ( $user ) { if ( ! current_user_can( 'edit_user', $user->ID ) ) { $user = false; } elseif ( ! wp_verify_nonce( $_POST['nonce'], 'update-user_' . $user->ID ) ) { $user = false; } } if ( ! $user ) { wp_send_json_error( array( 'message' => __( 'Could not log out user sessions. Please try again.' ), ) ); } $sessions = WP_Session_Tokens::get_instance( $user->ID ); if ( get_current_user_id() === $user->ID ) { $sessions->destroy_others( wp_get_session_token() ); $message = __( 'You are now logged out everywhere else.' ); } else { $sessions->destroy_all(); /* translators: %s: User's display name. */ $message = sprintf( __( '%s has been logged out.' ), $user->display_name ); } wp_send_json_success( array( 'message' => $message ) ); } /** * Handles cropping an image via AJAX. * * @since 4.3.0 */ function wp_ajax_crop_image() { $attachment_id = absint( $_POST['id'] ); check_ajax_referer( 'image_editor-' . $attachment_id, 'nonce' ); if ( empty( $attachment_id ) || ! current_user_can( 'edit_post', $attachment_id ) ) { wp_send_json_error(); } $context = str_replace( '_', '-', $_POST['context'] ); $data = array_map( 'absint', $_POST['cropDetails'] ); $cropped = wp_crop_image( $attachment_id, $data['x1'], $data['y1'], $data['width'], $data['height'], $data['dst_width'], $data['dst_height'] ); if ( ! $cropped || is_wp_error( $cropped ) ) { wp_send_json_error( array( 'message' => __( 'Image could not be processed.' ) ) ); } switch ( $context ) { case 'site-icon': require_once ABSPATH . 'wp-admin/includes/class-wp-site-icon.php'; $wp_site_icon = new WP_Site_Icon(); // Skip creating a new attachment if the attachment is a Site Icon. if ( get_post_meta( $attachment_id, '_wp_attachment_context', true ) == $context ) { // Delete the temporary cropped file, we don't need it. wp_delete_file( $cropped ); // Additional sizes in wp_prepare_attachment_for_js(). add_filter( 'image_size_names_choose', array( $wp_site_icon, 'additional_sizes' ) ); break; } /** This filter is documented in wp-admin/includes/class-custom-image-header.php */ $cropped = apply_filters( 'wp_create_file_in_uploads', $cropped, $attachment_id ); // For replication. // Copy attachment properties. $attachment = wp_copy_parent_attachment_properties( $cropped, $attachment_id, $context ); // Update the attachment. add_filter( 'intermediate_image_sizes_advanced', array( $wp_site_icon, 'additional_sizes' ) ); $attachment_id = $wp_site_icon->insert_attachment( $attachment, $cropped ); remove_filter( 'intermediate_image_sizes_advanced', array( $wp_site_icon, 'additional_sizes' ) ); // Additional sizes in wp_prepare_attachment_for_js(). add_filter( 'image_size_names_choose', array( $wp_site_icon, 'additional_sizes' ) ); break; default: /** * Fires before a cropped image is saved. * * Allows to add filters to modify the way a cropped image is saved. * * @since 4.3.0 * * @param string $context The Customizer control requesting the cropped image. * @param int $attachment_id The attachment ID of the original image. * @param string $cropped Path to the cropped image file. */ do_action( 'wp_ajax_crop_image_pre_save', $context, $attachment_id, $cropped ); /** This filter is documented in wp-admin/includes/class-custom-image-header.php */ $cropped = apply_filters( 'wp_create_file_in_uploads', $cropped, $attachment_id ); // For replication. // Copy attachment properties. $attachment = wp_copy_parent_attachment_properties( $cropped, $attachment_id, $context ); $attachment_id = wp_insert_attachment( $attachment, $cropped ); $metadata = wp_generate_attachment_metadata( $attachment_id, $cropped ); /** * Filters the cropped image attachment metadata. * * @since 4.3.0 * * @see wp_generate_attachment_metadata() * * @param array $metadata Attachment metadata. */ $metadata = apply_filters( 'wp_ajax_cropped_attachment_metadata', $metadata ); wp_update_attachment_metadata( $attachment_id, $metadata ); /** * Filters the attachment ID for a cropped image. * * @since 4.3.0 * * @param int $attachment_id The attachment ID of the cropped image. * @param string $context The Customizer control requesting the cropped image. */ $attachment_id = apply_filters( 'wp_ajax_cropped_attachment_id', $attachment_id, $context ); } wp_send_json_success( wp_prepare_attachment_for_js( $attachment_id ) ); } /** * Handles generating a password via AJAX. * * @since 4.4.0 */ function wp_ajax_generate_password() { wp_send_json_success( wp_generate_password( 24 ) ); } /** * Handles generating a password in the no-privilege context via AJAX. * * @since 5.7.0 */ function wp_ajax_nopriv_generate_password() { wp_send_json_success( wp_generate_password( 24 ) ); } /** * Handles saving the user's WordPress.org username via AJAX. * * @since 4.4.0 */ function wp_ajax_save_wporg_username() { if ( ! current_user_can( 'install_themes' ) && ! current_user_can( 'install_plugins' ) ) { wp_send_json_error(); } check_ajax_referer( 'save_wporg_username_' . get_current_user_id() ); $username = isset( $_REQUEST['username'] ) ? wp_unslash( $_REQUEST['username'] ) : false; if ( ! $username ) { wp_send_json_error(); } wp_send_json_success( update_user_meta( get_current_user_id(), 'wporg_favorites', $username ) ); } /** * Handles installing a theme via AJAX. * * @since 4.6.0 * * @see Theme_Upgrader * * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass. */ function wp_ajax_install_theme() { check_ajax_referer( 'updates' ); if ( empty( $_POST['slug'] ) ) { wp_send_json_error( array( 'slug' => '', 'errorCode' => 'no_theme_specified', 'errorMessage' => __( 'No theme specified.' ), ) ); } $slug = sanitize_key( wp_unslash( $_POST['slug'] ) ); $status = array( 'install' => 'theme', 'slug' => $slug, ); if ( ! current_user_can( 'install_themes' ) ) { $status['errorMessage'] = __( 'Sorry, you are not allowed to install themes on this site.' ); wp_send_json_error( $status ); } require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'; require_once ABSPATH . 'wp-admin/includes/theme.php'; $api = themes_api( 'theme_information', array( 'slug' => $slug, 'fields' => array( 'sections' => false ), ) ); if ( is_wp_error( $api ) ) { $status['errorMessage'] = $api->get_error_message(); wp_send_json_error( $status ); } $skin = new WP_Ajax_Upgrader_Skin(); $upgrader = new Theme_Upgrader( $skin ); $result = $upgrader->install( $api->download_link ); if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) { $status['debug'] = $skin->get_upgrade_messages(); } if ( is_wp_error( $result ) ) { $status['errorCode'] = $result->get_error_code(); $status['errorMessage'] = $result->get_error_message(); wp_send_json_error( $status ); } elseif ( is_wp_error( $skin->result ) ) { $status['errorCode'] = $skin->result->get_error_code(); $status['errorMessage'] = $skin->result->get_error_message(); wp_send_json_error( $status ); } elseif ( $skin->get_errors()->has_errors() ) { $status['errorMessage'] = $skin->get_error_messages(); wp_send_json_error( $status ); } elseif ( is_null( $result ) ) { global $wp_filesystem; $status['errorCode'] = 'unable_to_connect_to_filesystem'; $status['errorMessage'] = __( 'Unable to connect to the filesystem. Please confirm your credentials.' ); // Pass through the error from WP_Filesystem if one was raised. if ( $wp_filesystem instanceof WP_Filesystem_Base && is_wp_error( $wp_filesystem->errors ) && $wp_filesystem->errors->has_errors() ) { $status['errorMessage'] = esc_html( $wp_filesystem->errors->get_error_message() ); } wp_send_json_error( $status ); } $status['themeName'] = wp_get_theme( $slug )->get( 'Name' ); if ( current_user_can( 'switch_themes' ) ) { if ( is_multisite() ) { $status['activateUrl'] = add_query_arg( array( 'action' => 'enable', '_wpnonce' => wp_create_nonce( 'enable-theme_' . $slug ), 'theme' => $slug, ), network_admin_url( 'themes.php' ) ); } else { $status['activateUrl'] = add_query_arg( array( 'action' => 'activate', '_wpnonce' => wp_create_nonce( 'switch-theme_' . $slug ), 'stylesheet' => $slug, ), admin_url( 'themes.php' ) ); } } $theme = wp_get_theme( $slug ); $status['blockTheme'] = $theme->is_block_theme(); if ( ! is_multisite() && current_user_can( 'edit_theme_options' ) && current_user_can( 'customize' ) ) { $status['customizeUrl'] = add_query_arg( array( 'return' => urlencode( network_admin_url( 'theme-install.php', 'relative' ) ), ), wp_customize_url( $slug ) ); } /* * See WP_Theme_Install_List_Table::_get_theme_status() if we wanted to check * on post-installation status. */ wp_send_json_success( $status ); } /** * Handles updating a theme via AJAX. * * @since 4.6.0 * * @see Theme_Upgrader * * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass. */ function wp_ajax_update_theme() { check_ajax_referer( 'updates' ); if ( empty( $_POST['slug'] ) ) { wp_send_json_error( array( 'slug' => '', 'errorCode' => 'no_theme_specified', 'errorMessage' => __( 'No theme specified.' ), ) ); } $stylesheet = preg_replace( '/[^A-z0-9_\-]/', '', wp_unslash( $_POST['slug'] ) ); $status = array( 'update' => 'theme', 'slug' => $stylesheet, 'oldVersion' => '', 'newVersion' => '', ); if ( ! current_user_can( 'update_themes' ) ) { $status['errorMessage'] = __( 'Sorry, you are not allowed to update themes for this site.' ); wp_send_json_error( $status ); } $theme = wp_get_theme( $stylesheet ); if ( $theme->exists() ) { $status['oldVersion'] = $theme->get( 'Version' ); } require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'; $current = get_site_transient( 'update_themes' ); if ( empty( $current ) ) { wp_update_themes(); } $skin = new WP_Ajax_Upgrader_Skin(); $upgrader = new Theme_Upgrader( $skin ); $result = $upgrader->bulk_upgrade( array( $stylesheet ) ); if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) { $status['debug'] = $skin->get_upgrade_messages(); } if ( is_wp_error( $skin->result ) ) { $status['errorCode'] = $skin->result->get_error_code(); $status['errorMessage'] = $skin->result->get_error_message(); wp_send_json_error( $status ); } elseif ( $skin->get_errors()->has_errors() ) { $status['errorMessage'] = $skin->get_error_messages(); wp_send_json_error( $status ); } elseif ( is_array( $result ) && ! empty( $result[ $stylesheet ] ) ) { // Theme is already at the latest version. if ( true === $result[ $stylesheet ] ) { $status['errorMessage'] = $upgrader->strings['up_to_date']; wp_send_json_error( $status ); } $theme = wp_get_theme( $stylesheet ); if ( $theme->exists() ) { $status['newVersion'] = $theme->get( 'Version' ); } wp_send_json_success( $status ); } elseif ( false === $result ) { global $wp_filesystem; $status['errorCode'] = 'unable_to_connect_to_filesystem'; $status['errorMessage'] = __( 'Unable to connect to the filesystem. Please confirm your credentials.' ); // Pass through the error from WP_Filesystem if one was raised. if ( $wp_filesystem instanceof WP_Filesystem_Base && is_wp_error( $wp_filesystem->errors ) && $wp_filesystem->errors->has_errors() ) { $status['errorMessage'] = esc_html( $wp_filesystem->errors->get_error_message() ); } wp_send_json_error( $status ); } // An unhandled error occurred. $status['errorMessage'] = __( 'Theme update failed.' ); wp_send_json_error( $status ); } /** * Handles deleting a theme via AJAX. * * @since 4.6.0 * * @see delete_theme() * * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass. */ function wp_ajax_delete_theme() { check_ajax_referer( 'updates' ); if ( empty( $_POST['slug'] ) ) { wp_send_json_error( array( 'slug' => '', 'errorCode' => 'no_theme_specified', 'errorMessage' => __( 'No theme specified.' ), ) ); } $stylesheet = preg_replace( '/[^A-z0-9_\-]/', '', wp_unslash( $_POST['slug'] ) ); $status = array( 'delete' => 'theme', 'slug' => $stylesheet, ); if ( ! current_user_can( 'delete_themes' ) ) { $status['errorMessage'] = __( 'Sorry, you are not allowed to delete themes on this site.' ); wp_send_json_error( $status ); } if ( ! wp_get_theme( $stylesheet )->exists() ) { $status['errorMessage'] = __( 'The requested theme does not exist.' ); wp_send_json_error( $status ); } // Check filesystem credentials. `delete_theme()` will bail otherwise. $url = wp_nonce_url( 'themes.php?action=delete&stylesheet=' . urlencode( $stylesheet ), 'delete-theme_' . $stylesheet ); ob_start(); $credentials = request_filesystem_credentials( $url ); ob_end_clean(); if ( false === $credentials || ! WP_Filesystem( $credentials ) ) { global $wp_filesystem; $status['errorCode'] = 'unable_to_connect_to_filesystem'; $status['errorMessage'] = __( 'Unable to connect to the filesystem. Please confirm your credentials.' ); // Pass through the error from WP_Filesystem if one was raised. if ( $wp_filesystem instanceof WP_Filesystem_Base && is_wp_error( $wp_filesystem->errors ) && $wp_filesystem->errors->has_errors() ) { $status['errorMessage'] = esc_html( $wp_filesystem->errors->get_error_message() ); } wp_send_json_error( $status ); } require_once ABSPATH . 'wp-admin/includes/theme.php'; $result = delete_theme( $stylesheet ); if ( is_wp_error( $result ) ) { $status['errorMessage'] = $result->get_error_message(); wp_send_json_error( $status ); } elseif ( false === $result ) { $status['errorMessage'] = __( 'Theme could not be deleted.' ); wp_send_json_error( $status ); } wp_send_json_success( $status ); } /** * Handles installing a plugin via AJAX. * * @since 4.6.0 * * @see Plugin_Upgrader * * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass. */ function wp_ajax_install_plugin() { check_ajax_referer( 'updates' ); if ( empty( $_POST['slug'] ) ) { wp_send_json_error( array( 'slug' => '', 'errorCode' => 'no_plugin_specified', 'errorMessage' => __( 'No plugin specified.' ), ) ); } $status = array( 'install' => 'plugin', 'slug' => sanitize_key( wp_unslash( $_POST['slug'] ) ), ); if ( ! current_user_can( 'install_plugins' ) ) { $status['errorMessage'] = __( 'Sorry, you are not allowed to install plugins on this site.' ); wp_send_json_error( $status ); } require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'; require_once ABSPATH . 'wp-admin/includes/plugin-install.php'; $api = plugins_api( 'plugin_information', array( 'slug' => sanitize_key( wp_unslash( $_POST['slug'] ) ), 'fields' => array( 'sections' => false, ), ) ); if ( is_wp_error( $api ) ) { $status['errorMessage'] = $api->get_error_message(); wp_send_json_error( $status ); } $status['pluginName'] = $api->name; $skin = new WP_Ajax_Upgrader_Skin(); $upgrader = new Plugin_Upgrader( $skin ); $result = $upgrader->install( $api->download_link ); if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) { $status['debug'] = $skin->get_upgrade_messages(); } if ( is_wp_error( $result ) ) { $status['errorCode'] = $result->get_error_code(); $status['errorMessage'] = $result->get_error_message(); wp_send_json_error( $status ); } elseif ( is_wp_error( $skin->result ) ) { $status['errorCode'] = $skin->result->get_error_code(); $status['errorMessage'] = $skin->result->get_error_message(); wp_send_json_error( $status ); } elseif ( $skin->get_errors()->has_errors() ) { $status['errorMessage'] = $skin->get_error_messages(); wp_send_json_error( $status ); } elseif ( is_null( $result ) ) { global $wp_filesystem; $status['errorCode'] = 'unable_to_connect_to_filesystem'; $status['errorMessage'] = __( 'Unable to connect to the filesystem. Please confirm your credentials.' ); // Pass through the error from WP_Filesystem if one was raised. if ( $wp_filesystem instanceof WP_Filesystem_Base && is_wp_error( $wp_filesystem->errors ) && $wp_filesystem->errors->has_errors() ) { $status['errorMessage'] = esc_html( $wp_filesystem->errors->get_error_message() ); } wp_send_json_error( $status ); } $install_status = install_plugin_install_status( $api ); $pagenow = isset( $_POST['pagenow'] ) ? sanitize_key( $_POST['pagenow'] ) : ''; // If installation request is coming from import page, do not return network activation link. $plugins_url = ( 'import' === $pagenow ) ? admin_url( 'plugins.php' ) : network_admin_url( 'plugins.php' ); if ( current_user_can( 'activate_plugin', $install_status['file'] ) && is_plugin_inactive( $install_status['file'] ) ) { $status['activateUrl'] = add_query_arg( array( '_wpnonce' => wp_create_nonce( 'activate-plugin_' . $install_status['file'] ), 'action' => 'activate', 'plugin' => $install_status['file'], ), $plugins_url ); } if ( is_multisite() && current_user_can( 'manage_network_plugins' ) && 'import' !== $pagenow ) { $status['activateUrl'] = add_query_arg( array( 'networkwide' => 1 ), $status['activateUrl'] ); } wp_send_json_success( $status ); } /** * Handles activating a plugin via AJAX. * * @since 6.5.0 */ function wp_ajax_activate_plugin() { check_ajax_referer( 'updates' ); if ( empty( $_POST['name'] ) || empty( $_POST['slug'] ) || empty( $_POST['plugin'] ) ) { wp_send_json_error( array( 'slug' => '', 'pluginName' => '', 'plugin' => '', 'errorCode' => 'no_plugin_specified', 'errorMessage' => __( 'No plugin specified.' ), ) ); } $status = array( 'activate' => 'plugin', 'slug' => wp_unslash( $_POST['slug'] ), 'pluginName' => wp_unslash( $_POST['name'] ), 'plugin' => wp_unslash( $_POST['plugin'] ), ); if ( ! current_user_can( 'activate_plugin', $status['plugin'] ) ) { $status['errorMessage'] = __( 'Sorry, you are not allowed to activate plugins on this site.' ); wp_send_json_error( $status ); } if ( is_plugin_active( $status['plugin'] ) ) { $status['errorMessage'] = sprintf( /* translators: %s: Plugin name. */ __( '%s is already active.' ), $status['pluginName'] ); } $activated = activate_plugin( $status['plugin'] ); if ( is_wp_error( $activated ) ) { $status['errorMessage'] = $activated->get_error_message(); wp_send_json_error( $status ); } wp_send_json_success( $status ); } /** * Handles updating a plugin via AJAX. * * @since 4.2.0 * * @see Plugin_Upgrader * * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass. */ function wp_ajax_update_plugin() { check_ajax_referer( 'updates' ); if ( empty( $_POST['plugin'] ) || empty( $_POST['slug'] ) ) { wp_send_json_error( array( 'slug' => '', 'errorCode' => 'no_plugin_specified', 'errorMessage' => __( 'No plugin specified.' ), ) ); } $plugin = plugin_basename( sanitize_text_field( wp_unslash( $_POST['plugin'] ) ) ); $status = array( 'update' => 'plugin', 'slug' => sanitize_key( wp_unslash( $_POST['slug'] ) ), 'oldVersion' => '', 'newVersion' => '', ); if ( ! current_user_can( 'update_plugins' ) || 0 !== validate_file( $plugin ) ) { $status['errorMessage'] = __( 'Sorry, you are not allowed to update plugins for this site.' ); wp_send_json_error( $status ); } $plugin_data = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin ); $status['plugin'] = $plugin; $status['pluginName'] = $plugin_data['Name']; if ( $plugin_data['Version'] ) { /* translators: %s: Plugin version. */ $status['oldVersion'] = sprintf( __( 'Version %s' ), $plugin_data['Version'] ); } require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'; wp_update_plugins(); $skin = new WP_Ajax_Upgrader_Skin(); $upgrader = new Plugin_Upgrader( $skin ); $result = $upgrader->bulk_upgrade( array( $plugin ) ); if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) { $status['debug'] = $skin->get_upgrade_messages(); } if ( is_wp_error( $skin->result ) ) { $status['errorCode'] = $skin->result->get_error_code(); $status['errorMessage'] = $skin->result->get_error_message(); wp_send_json_error( $status ); } elseif ( $skin->get_errors()->has_errors() ) { $status['errorMessage'] = $skin->get_error_messages(); wp_send_json_error( $status ); } elseif ( is_array( $result ) && ! empty( $result[ $plugin ] ) ) { /* * Plugin is already at the latest version. * * This may also be the return value if the `update_plugins` site transient is empty, * e.g. when you update two plugins in quick succession before the transient repopulates. * * Preferably something can be done to ensure `update_plugins` isn't empty. * For now, surface some sort of error here. */ if ( true === $result[ $plugin ] ) { $status['errorMessage'] = $upgrader->strings['up_to_date']; wp_send_json_error( $status ); } $plugin_data = get_plugins( '/' . $result[ $plugin ]['destination_name'] ); $plugin_data = reset( $plugin_data ); if ( $plugin_data['Version'] ) { /* translators: %s: Plugin version. */ $status['newVersion'] = sprintf( __( 'Version %s' ), $plugin_data['Version'] ); } wp_send_json_success( $status ); } elseif ( false === $result ) { global $wp_filesystem; $status['errorCode'] = 'unable_to_connect_to_filesystem'; $status['errorMessage'] = __( 'Unable to connect to the filesystem. Please confirm your credentials.' ); // Pass through the error from WP_Filesystem if one was raised. if ( $wp_filesystem instanceof WP_Filesystem_Base && is_wp_error( $wp_filesystem->errors ) && $wp_filesystem->errors->has_errors() ) { $status['errorMessage'] = esc_html( $wp_filesystem->errors->get_error_message() ); } wp_send_json_error( $status ); } // An unhandled error occurred. $status['errorMessage'] = __( 'Plugin update failed.' ); wp_send_json_error( $status ); } /** * Handles deleting a plugin via AJAX. * * @since 4.6.0 * * @see delete_plugins() * * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass. */ function wp_ajax_delete_plugin() { check_ajax_referer( 'updates' ); if ( empty( $_POST['slug'] ) || empty( $_POST['plugin'] ) ) { wp_send_json_error( array( 'slug' => '', 'errorCode' => 'no_plugin_specified', 'errorMessage' => __( 'No plugin specified.' ), ) ); } $plugin = plugin_basename( sanitize_text_field( wp_unslash( $_POST['plugin'] ) ) ); $status = array( 'delete' => 'plugin', 'slug' => sanitize_key( wp_unslash( $_POST['slug'] ) ), ); if ( ! current_user_can( 'delete_plugins' ) || 0 !== validate_file( $plugin ) ) { $status['errorMessage'] = __( 'Sorry, you are not allowed to delete plugins for this site.' ); wp_send_json_error( $status ); } $plugin_data = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin ); $status['plugin'] = $plugin; $status['pluginName'] = $plugin_data['Name']; if ( is_plugin_active( $plugin ) ) { $status['errorMessage'] = __( 'You cannot delete a plugin while it is active on the main site.' ); wp_send_json_error( $status ); } // Check filesystem credentials. `delete_plugins()` will bail otherwise. $url = wp_nonce_url( 'plugins.php?action=delete-selected&verify-delete=1&checked[]=' . $plugin, 'bulk-plugins' ); ob_start(); $credentials = request_filesystem_credentials( $url ); ob_end_clean(); if ( false === $credentials || ! WP_Filesystem( $credentials ) ) { global $wp_filesystem; $status['errorCode'] = 'unable_to_connect_to_filesystem'; $status['errorMessage'] = __( 'Unable to connect to the filesystem. Please confirm your credentials.' ); // Pass through the error from WP_Filesystem if one was raised. if ( $wp_filesystem instanceof WP_Filesystem_Base && is_wp_error( $wp_filesystem->errors ) && $wp_filesystem->errors->has_errors() ) { $status['errorMessage'] = esc_html( $wp_filesystem->errors->get_error_message() ); } wp_send_json_error( $status ); } $result = delete_plugins( array( $plugin ) ); if ( is_wp_error( $result ) ) { $status['errorMessage'] = $result->get_error_message(); wp_send_json_error( $status ); } elseif ( false === $result ) { $status['errorMessage'] = __( 'Plugin could not be deleted.' ); wp_send_json_error( $status ); } wp_send_json_success( $status ); } /** * Handles searching plugins via AJAX. * * @since 4.6.0 * * @global string $s Search term. */ function wp_ajax_search_plugins() { check_ajax_referer( 'updates' ); // Ensure after_plugin_row_{$plugin_file} gets hooked. wp_plugin_update_rows(); $pagenow = isset( $_POST['pagenow'] ) ? sanitize_key( $_POST['pagenow'] ) : ''; if ( 'plugins-network' === $pagenow || 'plugins' === $pagenow ) { set_current_screen( $pagenow ); } /** @var WP_Plugins_List_Table $wp_list_table */ $wp_list_table = _get_list_table( 'WP_Plugins_List_Table', array( 'screen' => get_current_screen(), ) ); $status = array(); if ( ! $wp_list_table->ajax_user_can() ) { $status['errorMessage'] = __( 'Sorry, you are not allowed to manage plugins for this site.' ); wp_send_json_error( $status ); } // Set the correct requester, so pagination works. $_SERVER['REQUEST_URI'] = add_query_arg( array_diff_key( $_POST, array( '_ajax_nonce' => null, 'action' => null, ) ), network_admin_url( 'plugins.php', 'relative' ) ); $GLOBALS['s'] = wp_unslash( $_POST['s'] ); $wp_list_table->prepare_items(); ob_start(); $wp_list_table->display(); $status['count'] = count( $wp_list_table->items ); $status['items'] = ob_get_clean(); wp_send_json_success( $status ); } /** * Handles searching plugins to install via AJAX. * * @since 4.6.0 */ function wp_ajax_search_install_plugins() { check_ajax_referer( 'updates' ); $pagenow = isset( $_POST['pagenow'] ) ? sanitize_key( $_POST['pagenow'] ) : ''; if ( 'plugin-install-network' === $pagenow || 'plugin-install' === $pagenow ) { set_current_screen( $pagenow ); } /** @var WP_Plugin_Install_List_Table $wp_list_table */ $wp_list_table = _get_list_table( 'WP_Plugin_Install_List_Table', array( 'screen' => get_current_screen(), ) ); $status = array(); if ( ! $wp_list_table->ajax_user_can() ) { $status['errorMessage'] = __( 'Sorry, you are not allowed to manage plugins for this site.' ); wp_send_json_error( $status ); } // Set the correct requester, so pagination works. $_SERVER['REQUEST_URI'] = add_query_arg( array_diff_key( $_POST, array( '_ajax_nonce' => null, 'action' => null, ) ), network_admin_url( 'plugin-install.php', 'relative' ) ); $wp_list_table->prepare_items(); ob_start(); $wp_list_table->display(); $status['count'] = (int) $wp_list_table->get_pagination_arg( 'total_items' ); $status['items'] = ob_get_clean(); wp_send_json_success( $status ); } /** * Handles editing a theme or plugin file via AJAX. * * @since 4.9.0 * * @see wp_edit_theme_plugin_file() */ function wp_ajax_edit_theme_plugin_file() { $r = wp_edit_theme_plugin_file( wp_unslash( $_POST ) ); // Validation of args is done in wp_edit_theme_plugin_file(). if ( is_wp_error( $r ) ) { wp_send_json_error( array_merge( array( 'code' => $r->get_error_code(), 'message' => $r->get_error_message(), ), (array) $r->get_error_data() ) ); } else { wp_send_json_success( array( 'message' => __( 'File edited successfully.' ), ) ); } } /** * Handles exporting a user's personal data via AJAX. * * @since 4.9.6 */ function wp_ajax_wp_privacy_export_personal_data() { if ( empty( $_POST['id'] ) ) { wp_send_json_error( __( 'Missing request ID.' ) ); } $request_id = (int) $_POST['id']; if ( $request_id < 1 ) { wp_send_json_error( __( 'Invalid request ID.' ) ); } if ( ! current_user_can( 'export_others_personal_data' ) ) { wp_send_json_error( __( 'Sorry, you are not allowed to perform this action.' ) ); } check_ajax_referer( 'wp-privacy-export-personal-data-' . $request_id, 'security' ); // Get the request. $request = wp_get_user_request( $request_id ); if ( ! $request || 'export_personal_data' !== $request->action_name ) { wp_send_json_error( __( 'Invalid request type.' ) ); } $email_address = $request->email; if ( ! is_email( $email_address ) ) { wp_send_json_error( __( 'A valid email address must be given.' ) ); } if ( ! isset( $_POST['exporter'] ) ) { wp_send_json_error( __( 'Missing exporter index.' ) ); } $exporter_index = (int) $_POST['exporter']; if ( ! isset( $_POST['page'] ) ) { wp_send_json_error( __( 'Missing page index.' ) ); } $page = (int) $_POST['page']; $send_as_email = isset( $_POST['sendAsEmail'] ) ? ( 'true' === $_POST['sendAsEmail'] ) : false; /** * Filters the array of exporter callbacks. * * @since 4.9.6 * * @param array $args { * An array of callable exporters of personal data. Default empty array. * * @type array ...$0 { * Array of personal data exporters. * * @type callable $callback Callable exporter function that accepts an * email address and a page number and returns an * array of name => value pairs of personal data. * @type string $exporter_friendly_name Translated user facing friendly name for the * exporter. * } * } */ $exporters = apply_filters( 'wp_privacy_personal_data_exporters', array() ); if ( ! is_array( $exporters ) ) { wp_send_json_error( __( 'An exporter has improperly used the registration filter.' ) ); } // Do we have any registered exporters? if ( 0 < count( $exporters ) ) { if ( $exporter_index < 1 ) { wp_send_json_error( __( 'Exporter index cannot be negative.' ) ); } if ( $exporter_index > count( $exporters ) ) { wp_send_json_error( __( 'Exporter index is out of range.' ) ); } if ( $page < 1 ) { wp_send_json_error( __( 'Page index cannot be less than one.' ) ); } $exporter_keys = array_keys( $exporters ); $exporter_key = $exporter_keys[ $exporter_index - 1 ]; $exporter = $exporters[ $exporter_key ]; if ( ! is_array( $exporter ) ) { wp_send_json_error( /* translators: %s: Exporter array index. */ sprintf( __( 'Expected an array describing the exporter at index %s.' ), $exporter_key ) ); } if ( ! array_key_exists( 'exporter_friendly_name', $exporter ) ) { wp_send_json_error( /* translators: %s: Exporter array index. */ sprintf( __( 'Exporter array at index %s does not include a friendly name.' ), $exporter_key ) ); } $exporter_friendly_name = $exporter['exporter_friendly_name']; if ( ! array_key_exists( 'callback', $exporter ) ) { wp_send_json_error( /* translators: %s: Exporter friendly name. */ sprintf( __( 'Exporter does not include a callback: %s.' ), esc_html( $exporter_friendly_name ) ) ); } if ( ! is_callable( $exporter['callback'] ) ) { wp_send_json_error( /* translators: %s: Exporter friendly name. */ sprintf( __( 'Exporter callback is not a valid callback: %s.' ), esc_html( $exporter_friendly_name ) ) ); } $callback = $exporter['callback']; $response = call_user_func( $callback, $email_address, $page ); if ( is_wp_error( $response ) ) { wp_send_json_error( $response ); } if ( ! is_array( $response ) ) { wp_send_json_error( /* translators: %s: Exporter friendly name. */ sprintf( __( 'Expected response as an array from exporter: %s.' ), esc_html( $exporter_friendly_name ) ) ); } if ( ! array_key_exists( 'data', $response ) ) { wp_send_json_error( /* translators: %s: Exporter friendly name. */ sprintf( __( 'Expected data in response array from exporter: %s.' ), esc_html( $exporter_friendly_name ) ) ); } if ( ! is_array( $response['data'] ) ) { wp_send_json_error( /* translators: %s: Exporter friendly name. */ sprintf( __( 'Expected data array in response array from exporter: %s.' ), esc_html( $exporter_friendly_name ) ) ); } if ( ! array_key_exists( 'done', $response ) ) { wp_send_json_error( /* translators: %s: Exporter friendly name. */ sprintf( __( 'Expected done (boolean) in response array from exporter: %s.' ), esc_html( $exporter_friendly_name ) ) ); } } else { // No exporters, so we're done. $exporter_key = ''; $response = array( 'data' => array(), 'done' => true, ); } /** * Filters a page of personal data exporter data. Used to build the export report. * * Allows the export response to be consumed by destinations in addition to Ajax. * * @since 4.9.6 * * @param array $response The personal data for the given exporter and page number. * @param int $exporter_index The index of the exporter that provided this data. * @param string $email_address The email address associated with this personal data. * @param int $page The page number for this response. * @param int $request_id The privacy request post ID associated with this request. * @param bool $send_as_email Whether the final results of the export should be emailed to the user. * @param string $exporter_key The key (slug) of the exporter that provided this data. */ $response = apply_filters( 'wp_privacy_personal_data_export_page', $response, $exporter_index, $email_address, $page, $request_id, $send_as_email, $exporter_key ); if ( is_wp_error( $response ) ) { wp_send_json_error( $response ); } wp_send_json_success( $response ); } /** * Handles erasing personal data via AJAX. * * @since 4.9.6 */ function wp_ajax_wp_privacy_erase_personal_data() { if ( empty( $_POST['id'] ) ) { wp_send_json_error( __( 'Missing request ID.' ) ); } $request_id = (int) $_POST['id']; if ( $request_id < 1 ) { wp_send_json_error( __( 'Invalid request ID.' ) ); } // Both capabilities are required to avoid confusion, see `_wp_personal_data_removal_page()`. if ( ! current_user_can( 'erase_others_personal_data' ) || ! current_user_can( 'delete_users' ) ) { wp_send_json_error( __( 'Sorry, you are not allowed to perform this action.' ) ); } check_ajax_referer( 'wp-privacy-erase-personal-data-' . $request_id, 'security' ); // Get the request. $request = wp_get_user_request( $request_id ); if ( ! $request || 'remove_personal_data' !== $request->action_name ) { wp_send_json_error( __( 'Invalid request type.' ) ); } $email_address = $request->email; if ( ! is_email( $email_address ) ) { wp_send_json_error( __( 'Invalid email address in request.' ) ); } if ( ! isset( $_POST['eraser'] ) ) { wp_send_json_error( __( 'Missing eraser index.' ) ); } $eraser_index = (int) $_POST['eraser']; if ( ! isset( $_POST['page'] ) ) { wp_send_json_error( __( 'Missing page index.' ) ); } $page = (int) $_POST['page']; /** * Filters the array of personal data eraser callbacks. * * @since 4.9.6 * * @param array $args { * An array of callable erasers of personal data. Default empty array. * * @type array ...$0 { * Array of personal data exporters. * * @type callable $callback Callable eraser that accepts an email address and a page * number, and returns an array with boolean values for * whether items were removed or retained and any messages * from the eraser, as well as if additional pages are * available. * @type string $exporter_friendly_name Translated user facing friendly name for the eraser. * } * } */ $erasers = apply_filters( 'wp_privacy_personal_data_erasers', array() ); // Do we have any registered erasers? if ( 0 < count( $erasers ) ) { if ( $eraser_index < 1 ) { wp_send_json_error( __( 'Eraser index cannot be less than one.' ) ); } if ( $eraser_index > count( $erasers ) ) { wp_send_json_error( __( 'Eraser index is out of range.' ) ); } if ( $page < 1 ) { wp_send_json_error( __( 'Page index cannot be less than one.' ) ); } $eraser_keys = array_keys( $erasers ); $eraser_key = $eraser_keys[ $eraser_index - 1 ]; $eraser = $erasers[ $eraser_key ]; if ( ! is_array( $eraser ) ) { /* translators: %d: Eraser array index. */ wp_send_json_error( sprintf( __( 'Expected an array describing the eraser at index %d.' ), $eraser_index ) ); } if ( ! array_key_exists( 'eraser_friendly_name', $eraser ) ) { /* translators: %d: Eraser array index. */ wp_send_json_error( sprintf( __( 'Eraser array at index %d does not include a friendly name.' ), $eraser_index ) ); } $eraser_friendly_name = $eraser['eraser_friendly_name']; if ( ! array_key_exists( 'callback', $eraser ) ) { wp_send_json_error( sprintf( /* translators: %s: Eraser friendly name. */ __( 'Eraser does not include a callback: %s.' ), esc_html( $eraser_friendly_name ) ) ); } if ( ! is_callable( $eraser['callback'] ) ) { wp_send_json_error( sprintf( /* translators: %s: Eraser friendly name. */ __( 'Eraser callback is not valid: %s.' ), esc_html( $eraser_friendly_name ) ) ); } $callback = $eraser['callback']; $response = call_user_func( $callback, $email_address, $page ); if ( is_wp_error( $response ) ) { wp_send_json_error( $response ); } if ( ! is_array( $response ) ) { wp_send_json_error( sprintf( /* translators: 1: Eraser friendly name, 2: Eraser array index. */ __( 'Did not receive array from %1$s eraser (index %2$d).' ), esc_html( $eraser_friendly_name ), $eraser_index ) ); } if ( ! array_key_exists( 'items_removed', $response ) ) { wp_send_json_error( sprintf( /* translators: 1: Eraser friendly name, 2: Eraser array index. */ __( 'Expected items_removed key in response array from %1$s eraser (index %2$d).' ), esc_html( $eraser_friendly_name ), $eraser_index ) ); } if ( ! array_key_exists( 'items_retained', $response ) ) { wp_send_json_error( sprintf( /* translators: 1: Eraser friendly name, 2: Eraser array index. */ __( 'Expected items_retained key in response array from %1$s eraser (index %2$d).' ), esc_html( $eraser_friendly_name ), $eraser_index ) ); } if ( ! array_key_exists( 'messages', $response ) ) { wp_send_json_error( sprintf( /* translators: 1: Eraser friendly name, 2: Eraser array index. */ __( 'Expected messages key in response array from %1$s eraser (index %2$d).' ), esc_html( $eraser_friendly_name ), $eraser_index ) ); } if ( ! is_array( $response['messages'] ) ) { wp_send_json_error( sprintf( /* translators: 1: Eraser friendly name, 2: Eraser array index. */ __( 'Expected messages key to reference an array in response array from %1$s eraser (index %2$d).' ), esc_html( $eraser_friendly_name ), $eraser_index ) ); } if ( ! array_key_exists( 'done', $response ) ) { wp_send_json_error( sprintf( /* translators: 1: Eraser friendly name, 2: Eraser array index. */ __( 'Expected done flag in response array from %1$s eraser (index %2$d).' ), esc_html( $eraser_friendly_name ), $eraser_index ) ); } } else { // No erasers, so we're done. $eraser_key = ''; $response = array( 'items_removed' => false, 'items_retained' => false, 'messages' => array(), 'done' => true, ); } /** * Filters a page of personal data eraser data. * * Allows the erasure response to be consumed by destinations in addition to Ajax. * * @since 4.9.6 * * @param array $response { * The personal data for the given exporter and page number. * * @type bool $items_removed Whether items were actually removed or not. * @type bool $items_retained Whether items were retained or not. * @type string[] $messages An array of messages to add to the personal data export file. * @type bool $done Whether the eraser is finished or not. * } * @param int $eraser_index The index of the eraser that provided this data. * @param string $email_address The email address associated with this personal data. * @param int $page The page number for this response. * @param int $request_id The privacy request post ID associated with this request. * @param string $eraser_key The key (slug) of the eraser that provided this data. */ $response = apply_filters( 'wp_privacy_personal_data_erasure_page', $response, $eraser_index, $email_address, $page, $request_id, $eraser_key ); if ( is_wp_error( $response ) ) { wp_send_json_error( $response ); } wp_send_json_success( $response ); } /** * Handles site health checks on server communication via AJAX. * * @since 5.2.0 * @deprecated 5.6.0 Use WP_REST_Site_Health_Controller::test_dotorg_communication() * @see WP_REST_Site_Health_Controller::test_dotorg_communication() */ function wp_ajax_health_check_dotorg_communication() { _doing_it_wrong( 'wp_ajax_health_check_dotorg_communication', sprintf( // translators: 1: The Site Health action that is no longer used by core. 2: The new function that replaces it. __( 'The Site Health check for %1$s has been replaced with %2$s.' ), 'wp_ajax_health_check_dotorg_communication', 'WP_REST_Site_Health_Controller::test_dotorg_communication' ), '5.6.0' ); check_ajax_referer( 'health-check-site-status' ); if ( ! current_user_can( 'view_site_health_checks' ) ) { wp_send_json_error(); } if ( ! class_exists( 'WP_Site_Health' ) ) { require_once ABSPATH . 'wp-admin/includes/class-wp-site-health.php'; } $site_health = WP_Site_Health::get_instance(); wp_send_json_success( $site_health->get_test_dotorg_communication() ); } /** * Handles site health checks on background updates via AJAX. * * @since 5.2.0 * @deprecated 5.6.0 Use WP_REST_Site_Health_Controller::test_background_updates() * @see WP_REST_Site_Health_Controller::test_background_updates() */ function wp_ajax_health_check_background_updates() { _doing_it_wrong( 'wp_ajax_health_check_background_updates', sprintf( // translators: 1: The Site Health action that is no longer used by core. 2: The new function that replaces it. __( 'The Site Health check for %1$s has been replaced with %2$s.' ), 'wp_ajax_health_check_background_updates', 'WP_REST_Site_Health_Controller::test_background_updates' ), '5.6.0' ); check_ajax_referer( 'health-check-site-status' ); if ( ! current_user_can( 'view_site_health_checks' ) ) { wp_send_json_error(); } if ( ! class_exists( 'WP_Site_Health' ) ) { require_once ABSPATH . 'wp-admin/includes/class-wp-site-health.php'; } $site_health = WP_Site_Health::get_instance(); wp_send_json_success( $site_health->get_test_background_updates() ); } /** * Handles site health checks on loopback requests via AJAX. * * @since 5.2.0 * @deprecated 5.6.0 Use WP_REST_Site_Health_Controller::test_loopback_requests() * @see WP_REST_Site_Health_Controller::test_loopback_requests() */ function wp_ajax_health_check_loopback_requests() { _doing_it_wrong( 'wp_ajax_health_check_loopback_requests', sprintf( // translators: 1: The Site Health action that is no longer used by core. 2: The new function that replaces it. __( 'The Site Health check for %1$s has been replaced with %2$s.' ), 'wp_ajax_health_check_loopback_requests', 'WP_REST_Site_Health_Controller::test_loopback_requests' ), '5.6.0' ); check_ajax_referer( 'health-check-site-status' ); if ( ! current_user_can( 'view_site_health_checks' ) ) { wp_send_json_error(); } if ( ! class_exists( 'WP_Site_Health' ) ) { require_once ABSPATH . 'wp-admin/includes/class-wp-site-health.php'; } $site_health = WP_Site_Health::get_instance(); wp_send_json_success( $site_health->get_test_loopback_requests() ); } /** * Handles site health check to update the result status via AJAX. * * @since 5.2.0 */ function wp_ajax_health_check_site_status_result() { check_ajax_referer( 'health-check-site-status-result' ); if ( ! current_user_can( 'view_site_health_checks' ) ) { wp_send_json_error(); } set_transient( 'health-check-site-status-result', wp_json_encode( $_POST['counts'] ) ); wp_send_json_success(); } /** * Handles site health check to get directories and database sizes via AJAX. * * @since 5.2.0 * @deprecated 5.6.0 Use WP_REST_Site_Health_Controller::get_directory_sizes() * @see WP_REST_Site_Health_Controller::get_directory_sizes() */ function wp_ajax_health_check_get_sizes() { _doing_it_wrong( 'wp_ajax_health_check_get_sizes', sprintf( // translators: 1: The Site Health action that is no longer used by core. 2: The new function that replaces it. __( 'The Site Health check for %1$s has been replaced with %2$s.' ), 'wp_ajax_health_check_get_sizes', 'WP_REST_Site_Health_Controller::get_directory_sizes' ), '5.6.0' ); check_ajax_referer( 'health-check-site-status-result' ); if ( ! current_user_can( 'view_site_health_checks' ) || is_multisite() ) { wp_send_json_error(); } if ( ! class_exists( 'WP_Debug_Data' ) ) { require_once ABSPATH . 'wp-admin/includes/class-wp-debug-data.php'; } $sizes_data = WP_Debug_Data::get_sizes(); $all_sizes = array( 'raw' => 0 ); foreach ( $sizes_data as $name => $value ) { $name = sanitize_text_field( $name ); $data = array(); if ( isset( $value['size'] ) ) { if ( is_string( $value['size'] ) ) { $data['size'] = sanitize_text_field( $value['size'] ); } else { $data['size'] = (int) $value['size']; } } if ( isset( $value['debug'] ) ) { if ( is_string( $value['debug'] ) ) { $data['debug'] = sanitize_text_field( $value['debug'] ); } else { $data['debug'] = (int) $value['debug']; } } if ( ! empty( $value['raw'] ) ) { $data['raw'] = (int) $value['raw']; } $all_sizes[ $name ] = $data; } if ( isset( $all_sizes['total_size']['debug'] ) && 'not available' === $all_sizes['total_size']['debug'] ) { wp_send_json_error( $all_sizes ); } wp_send_json_success( $all_sizes ); } /** * Handles renewing the REST API nonce via AJAX. * * @since 5.3.0 */ function wp_ajax_rest_nonce() { exit( wp_create_nonce( 'wp_rest' ) ); } /** * Handles enabling or disable plugin and theme auto-updates via AJAX. * * @since 5.5.0 */ function wp_ajax_toggle_auto_updates() { check_ajax_referer( 'updates' ); if ( empty( $_POST['type'] ) || empty( $_POST['asset'] ) || empty( $_POST['state'] ) ) { wp_send_json_error( array( 'error' => __( 'Invalid data. No selected item.' ) ) ); } $asset = sanitize_text_field( urldecode( $_POST['asset'] ) ); if ( 'enable' !== $_POST['state'] && 'disable' !== $_POST['state'] ) { wp_send_json_error( array( 'error' => __( 'Invalid data. Unknown state.' ) ) ); } $state = $_POST['state']; if ( 'plugin' !== $_POST['type'] && 'theme' !== $_POST['type'] ) { wp_send_json_error( array( 'error' => __( 'Invalid data. Unknown type.' ) ) ); } $type = $_POST['type']; switch ( $type ) { case 'plugin': if ( ! current_user_can( 'update_plugins' ) ) { $error_message = __( 'Sorry, you are not allowed to modify plugins.' ); wp_send_json_error( array( 'error' => $error_message ) ); } $option = 'auto_update_plugins'; /** This filter is documented in wp-admin/includes/class-wp-plugins-list-table.php */ $all_items = apply_filters( 'all_plugins', get_plugins() ); break; case 'theme': if ( ! current_user_can( 'update_themes' ) ) { $error_message = __( 'Sorry, you are not allowed to modify themes.' ); wp_send_json_error( array( 'error' => $error_message ) ); } $option = 'auto_update_themes'; $all_items = wp_get_themes(); break; default: wp_send_json_error( array( 'error' => __( 'Invalid data. Unknown type.' ) ) ); } if ( ! array_key_exists( $asset, $all_items ) ) { $error_message = __( 'Invalid data. The item does not exist.' ); wp_send_json_error( array( 'error' => $error_message ) ); } $auto_updates = (array) get_site_option( $option, array() ); if ( 'disable' === $state ) { $auto_updates = array_diff( $auto_updates, array( $asset ) ); } else { $auto_updates[] = $asset; $auto_updates = array_unique( $auto_updates ); } // Remove items that have been deleted since the site option was last updated. $auto_updates = array_intersect( $auto_updates, array_keys( $all_items ) ); update_site_option( $option, $auto_updates ); wp_send_json_success(); } /** * Handles sending a password reset link via AJAX. * * @since 5.7.0 */ function wp_ajax_send_password_reset() { // Validate the nonce for this action. $user_id = isset( $_POST['user_id'] ) ? (int) $_POST['user_id'] : 0; check_ajax_referer( 'reset-password-for-' . $user_id, 'nonce' ); // Verify user capabilities. if ( ! current_user_can( 'edit_user', $user_id ) ) { wp_send_json_error( __( 'Cannot send password reset, permission denied.' ) ); } // Send the password reset link. $user = get_userdata( $user_id ); $results = retrieve_password( $user->user_login ); if ( true === $results ) { wp_send_json_success( /* translators: %s: User's display name. */ sprintf( __( 'A password reset link was emailed to %s.' ), $user->display_name ) ); } else { wp_send_json_error( $results->get_error_message() ); } } includes/user.php 0000644 00000055732 15135536347 0010071 0 ustar 00 <?php /** * WordPress user administration API. * * @package WordPress * @subpackage Administration */ /** * Creates a new user from the "Users" form using $_POST information. * * @since 2.0.0 * * @return int|WP_Error WP_Error or User ID. */ function add_user() { return edit_user(); } /** * Edit user settings based on contents of $_POST * * Used on user-edit.php and profile.php to manage and process user options, passwords etc. * * @since 2.0.0 * * @param int $user_id Optional. User ID. * @return int|WP_Error User ID of the updated user or WP_Error on failure. */ function edit_user( $user_id = 0 ) { $wp_roles = wp_roles(); $user = new stdClass(); $user_id = (int) $user_id; if ( $user_id ) { $update = true; $user->ID = $user_id; $userdata = get_userdata( $user_id ); $user->user_login = wp_slash( $userdata->user_login ); } else { $update = false; } if ( ! $update && isset( $_POST['user_login'] ) ) { $user->user_login = sanitize_user( wp_unslash( $_POST['user_login'] ), true ); } $pass1 = ''; $pass2 = ''; if ( isset( $_POST['pass1'] ) ) { $pass1 = trim( $_POST['pass1'] ); } if ( isset( $_POST['pass2'] ) ) { $pass2 = trim( $_POST['pass2'] ); } if ( isset( $_POST['role'] ) && current_user_can( 'promote_users' ) && ( ! $user_id || current_user_can( 'promote_user', $user_id ) ) ) { $new_role = sanitize_text_field( $_POST['role'] ); // If the new role isn't editable by the logged-in user die with error. $editable_roles = get_editable_roles(); if ( ! empty( $new_role ) && empty( $editable_roles[ $new_role ] ) ) { wp_die( __( 'Sorry, you are not allowed to give users that role.' ), 403 ); } $potential_role = isset( $wp_roles->role_objects[ $new_role ] ) ? $wp_roles->role_objects[ $new_role ] : false; /* * Don't let anyone with 'promote_users' edit their own role to something without it. * Multisite super admins can freely edit their roles, they possess all caps. */ if ( ( is_multisite() && current_user_can( 'manage_network_users' ) ) || get_current_user_id() !== $user_id || ( $potential_role && $potential_role->has_cap( 'promote_users' ) ) ) { $user->role = $new_role; } } if ( isset( $_POST['email'] ) ) { $user->user_email = sanitize_text_field( wp_unslash( $_POST['email'] ) ); } if ( isset( $_POST['url'] ) ) { if ( empty( $_POST['url'] ) || 'http://' === $_POST['url'] ) { $user->user_url = ''; } else { $user->user_url = sanitize_url( $_POST['url'] ); $protocols = implode( '|', array_map( 'preg_quote', wp_allowed_protocols() ) ); $user->user_url = preg_match( '/^(' . $protocols . '):/is', $user->user_url ) ? $user->user_url : 'http://' . $user->user_url; } } if ( isset( $_POST['first_name'] ) ) { $user->first_name = sanitize_text_field( $_POST['first_name'] ); } if ( isset( $_POST['last_name'] ) ) { $user->last_name = sanitize_text_field( $_POST['last_name'] ); } if ( isset( $_POST['nickname'] ) ) { $user->nickname = sanitize_text_field( $_POST['nickname'] ); } if ( isset( $_POST['display_name'] ) ) { $user->display_name = sanitize_text_field( $_POST['display_name'] ); } if ( isset( $_POST['description'] ) ) { $user->description = trim( $_POST['description'] ); } foreach ( wp_get_user_contact_methods( $user ) as $method => $name ) { if ( isset( $_POST[ $method ] ) ) { $user->$method = sanitize_text_field( $_POST[ $method ] ); } } if ( isset( $_POST['locale'] ) ) { $locale = sanitize_text_field( $_POST['locale'] ); if ( 'site-default' === $locale ) { $locale = ''; } elseif ( '' === $locale ) { $locale = 'en_US'; } elseif ( ! in_array( $locale, get_available_languages(), true ) ) { if ( current_user_can( 'install_languages' ) && wp_can_install_language_pack() ) { if ( ! wp_download_language_pack( $locale ) ) { $locale = ''; } } else { $locale = ''; } } $user->locale = $locale; } if ( $update ) { $user->rich_editing = isset( $_POST['rich_editing'] ) && 'false' === $_POST['rich_editing'] ? 'false' : 'true'; $user->syntax_highlighting = isset( $_POST['syntax_highlighting'] ) && 'false' === $_POST['syntax_highlighting'] ? 'false' : 'true'; $user->admin_color = isset( $_POST['admin_color'] ) ? sanitize_text_field( $_POST['admin_color'] ) : 'fresh'; $user->show_admin_bar_front = isset( $_POST['admin_bar_front'] ) ? 'true' : 'false'; } $user->comment_shortcuts = isset( $_POST['comment_shortcuts'] ) && 'true' === $_POST['comment_shortcuts'] ? 'true' : ''; $user->use_ssl = 0; if ( ! empty( $_POST['use_ssl'] ) ) { $user->use_ssl = 1; } $errors = new WP_Error(); /* checking that username has been typed */ if ( '' === $user->user_login ) { $errors->add( 'user_login', __( '<strong>Error:</strong> Please enter a username.' ) ); } /* checking that nickname has been typed */ if ( $update && empty( $user->nickname ) ) { $errors->add( 'nickname', __( '<strong>Error:</strong> Please enter a nickname.' ) ); } /** * Fires before the password and confirm password fields are checked for congruity. * * @since 1.5.1 * * @param string $user_login The username. * @param string $pass1 The password (passed by reference). * @param string $pass2 The confirmed password (passed by reference). */ do_action_ref_array( 'check_passwords', array( $user->user_login, &$pass1, &$pass2 ) ); // Check for blank password when adding a user. if ( ! $update && empty( $pass1 ) ) { $errors->add( 'pass', __( '<strong>Error:</strong> Please enter a password.' ), array( 'form-field' => 'pass1' ) ); } // Check for "\" in password. if ( str_contains( wp_unslash( $pass1 ), '\\' ) ) { $errors->add( 'pass', __( '<strong>Error:</strong> Passwords may not contain the character "\\".' ), array( 'form-field' => 'pass1' ) ); } // Checking the password has been typed twice the same. if ( ( $update || ! empty( $pass1 ) ) && $pass1 !== $pass2 ) { $errors->add( 'pass', __( '<strong>Error:</strong> Passwords do not match. Please enter the same password in both password fields.' ), array( 'form-field' => 'pass1' ) ); } if ( ! empty( $pass1 ) ) { $user->user_pass = $pass1; } if ( ! $update && isset( $_POST['user_login'] ) && ! validate_username( $_POST['user_login'] ) ) { $errors->add( 'user_login', __( '<strong>Error:</strong> This username is invalid because it uses illegal characters. Please enter a valid username.' ) ); } if ( ! $update && username_exists( $user->user_login ) ) { $errors->add( 'user_login', __( '<strong>Error:</strong> This username is already registered. Please choose another one.' ) ); } /** This filter is documented in wp-includes/user.php */ $illegal_logins = (array) apply_filters( 'illegal_user_logins', array() ); if ( in_array( strtolower( $user->user_login ), array_map( 'strtolower', $illegal_logins ), true ) ) { $errors->add( 'invalid_username', __( '<strong>Error:</strong> Sorry, that username is not allowed.' ) ); } // Checking email address. if ( empty( $user->user_email ) ) { $errors->add( 'empty_email', __( '<strong>Error:</strong> Please enter an email address.' ), array( 'form-field' => 'email' ) ); } elseif ( ! is_email( $user->user_email ) ) { $errors->add( 'invalid_email', __( '<strong>Error:</strong> The email address is not correct.' ), array( 'form-field' => 'email' ) ); } else { $owner_id = email_exists( $user->user_email ); if ( $owner_id && ( ! $update || ( $owner_id !== $user->ID ) ) ) { $errors->add( 'email_exists', __( '<strong>Error:</strong> This email is already registered. Please choose another one.' ), array( 'form-field' => 'email' ) ); } } /** * Fires before user profile update errors are returned. * * @since 2.8.0 * * @param WP_Error $errors WP_Error object (passed by reference). * @param bool $update Whether this is a user update. * @param stdClass $user User object (passed by reference). */ do_action_ref_array( 'user_profile_update_errors', array( &$errors, $update, &$user ) ); if ( $errors->has_errors() ) { return $errors; } if ( $update ) { $user_id = wp_update_user( $user ); } else { $user_id = wp_insert_user( $user ); $notify = isset( $_POST['send_user_notification'] ) ? 'both' : 'admin'; /** * Fires after a new user has been created. * * @since 4.4.0 * * @param int|WP_Error $user_id ID of the newly created user or WP_Error on failure. * @param string $notify Type of notification that should happen. See * wp_send_new_user_notifications() for more information. */ do_action( 'edit_user_created_user', $user_id, $notify ); } return $user_id; } /** * Fetch a filtered list of user roles that the current user is * allowed to edit. * * Simple function whose main purpose is to allow filtering of the * list of roles in the $wp_roles object so that plugins can remove * inappropriate ones depending on the situation or user making edits. * Specifically because without filtering anyone with the edit_users * capability can edit others to be administrators, even if they are * only editors or authors. This filter allows admins to delegate * user management. * * @since 2.8.0 * * @return array[] Array of arrays containing role information. */ function get_editable_roles() { $all_roles = wp_roles()->roles; /** * Filters the list of editable roles. * * @since 2.8.0 * * @param array[] $all_roles Array of arrays containing role information. */ $editable_roles = apply_filters( 'editable_roles', $all_roles ); return $editable_roles; } /** * Retrieve user data and filter it. * * @since 2.0.5 * * @param int $user_id User ID. * @return WP_User|false WP_User object on success, false on failure. */ function get_user_to_edit( $user_id ) { $user = get_userdata( $user_id ); if ( $user ) { $user->filter = 'edit'; } return $user; } /** * Retrieve the user's drafts. * * @since 2.0.0 * * @global wpdb $wpdb WordPress database abstraction object. * * @param int $user_id User ID. * @return array */ function get_users_drafts( $user_id ) { global $wpdb; $query = $wpdb->prepare( "SELECT ID, post_title FROM $wpdb->posts WHERE post_type = 'post' AND post_status = 'draft' AND post_author = %d ORDER BY post_modified DESC", $user_id ); /** * Filters the user's drafts query string. * * @since 2.0.0 * * @param string $query The user's drafts query string. */ $query = apply_filters( 'get_users_drafts', $query ); return $wpdb->get_results( $query ); } /** * Delete user and optionally reassign posts and links to another user. * * Note that on a Multisite installation the user only gets removed from the site * and does not get deleted from the database. * * If the `$reassign` parameter is not assigned to a user ID, then all posts will * be deleted of that user. The action {@see 'delete_user'} that is passed the user ID * being deleted will be run after the posts are either reassigned or deleted. * The user meta will also be deleted that are for that user ID. * * @since 2.0.0 * * @global wpdb $wpdb WordPress database abstraction object. * * @param int $id User ID. * @param int $reassign Optional. Reassign posts and links to new User ID. * @return bool True when finished. */ function wp_delete_user( $id, $reassign = null ) { global $wpdb; if ( ! is_numeric( $id ) ) { return false; } $id = (int) $id; $user = new WP_User( $id ); if ( ! $user->exists() ) { return false; } // Normalize $reassign to null or a user ID. 'novalue' was an older default. if ( 'novalue' === $reassign ) { $reassign = null; } elseif ( null !== $reassign ) { $reassign = (int) $reassign; } /** * Fires immediately before a user is deleted from the site. * * Note that on a Multisite installation the user only gets removed from the site * and does not get deleted from the database. * * @since 2.0.0 * @since 5.5.0 Added the `$user` parameter. * * @param int $id ID of the user to delete. * @param int|null $reassign ID of the user to reassign posts and links to. * Default null, for no reassignment. * @param WP_User $user WP_User object of the user to delete. */ do_action( 'delete_user', $id, $reassign, $user ); if ( null === $reassign ) { $post_types_to_delete = array(); foreach ( get_post_types( array(), 'objects' ) as $post_type ) { if ( $post_type->delete_with_user ) { $post_types_to_delete[] = $post_type->name; } elseif ( null === $post_type->delete_with_user && post_type_supports( $post_type->name, 'author' ) ) { $post_types_to_delete[] = $post_type->name; } } /** * Filters the list of post types to delete with a user. * * @since 3.4.0 * * @param string[] $post_types_to_delete Array of post types to delete. * @param int $id User ID. */ $post_types_to_delete = apply_filters( 'post_types_to_delete_with_user', $post_types_to_delete, $id ); $post_types_to_delete = implode( "', '", $post_types_to_delete ); $post_ids = $wpdb->get_col( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_author = %d AND post_type IN ('$post_types_to_delete')", $id ) ); if ( $post_ids ) { foreach ( $post_ids as $post_id ) { wp_delete_post( $post_id ); } } // Clean links. $link_ids = $wpdb->get_col( $wpdb->prepare( "SELECT link_id FROM $wpdb->links WHERE link_owner = %d", $id ) ); if ( $link_ids ) { foreach ( $link_ids as $link_id ) { wp_delete_link( $link_id ); } } } else { $post_ids = $wpdb->get_col( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_author = %d", $id ) ); $wpdb->update( $wpdb->posts, array( 'post_author' => $reassign ), array( 'post_author' => $id ) ); if ( ! empty( $post_ids ) ) { foreach ( $post_ids as $post_id ) { clean_post_cache( $post_id ); } } $link_ids = $wpdb->get_col( $wpdb->prepare( "SELECT link_id FROM $wpdb->links WHERE link_owner = %d", $id ) ); $wpdb->update( $wpdb->links, array( 'link_owner' => $reassign ), array( 'link_owner' => $id ) ); if ( ! empty( $link_ids ) ) { foreach ( $link_ids as $link_id ) { clean_bookmark_cache( $link_id ); } } } // FINALLY, delete user. if ( is_multisite() ) { remove_user_from_blog( $id, get_current_blog_id() ); } else { $meta = $wpdb->get_col( $wpdb->prepare( "SELECT umeta_id FROM $wpdb->usermeta WHERE user_id = %d", $id ) ); foreach ( $meta as $mid ) { delete_metadata_by_mid( 'user', $mid ); } $wpdb->delete( $wpdb->users, array( 'ID' => $id ) ); } clean_user_cache( $user ); /** * Fires immediately after a user is deleted from the site. * * Note that on a Multisite installation the user may not have been deleted from * the database depending on whether `wp_delete_user()` or `wpmu_delete_user()` * was called. * * @since 2.9.0 * @since 5.5.0 Added the `$user` parameter. * * @param int $id ID of the deleted user. * @param int|null $reassign ID of the user to reassign posts and links to. * Default null, for no reassignment. * @param WP_User $user WP_User object of the deleted user. */ do_action( 'deleted_user', $id, $reassign, $user ); return true; } /** * Remove all capabilities from user. * * @since 2.1.0 * * @param int $id User ID. */ function wp_revoke_user( $id ) { $id = (int) $id; $user = new WP_User( $id ); $user->remove_all_caps(); } /** * @since 2.8.0 * * @global int $user_ID * * @param false $errors Deprecated. */ function default_password_nag_handler( $errors = false ) { global $user_ID; // Short-circuit it. if ( ! get_user_option( 'default_password_nag' ) ) { return; } // get_user_setting() = JS-saved UI setting. Else no-js-fallback code. if ( 'hide' === get_user_setting( 'default_password_nag' ) || isset( $_GET['default_password_nag'] ) && '0' === $_GET['default_password_nag'] ) { delete_user_setting( 'default_password_nag' ); update_user_meta( $user_ID, 'default_password_nag', false ); } } /** * @since 2.8.0 * * @param int $user_ID * @param WP_User $old_data */ function default_password_nag_edit_user( $user_ID, $old_data ) { // Short-circuit it. if ( ! get_user_option( 'default_password_nag', $user_ID ) ) { return; } $new_data = get_userdata( $user_ID ); // Remove the nag if the password has been changed. if ( $new_data->user_pass !== $old_data->user_pass ) { delete_user_setting( 'default_password_nag' ); update_user_meta( $user_ID, 'default_password_nag', false ); } } /** * @since 2.8.0 * * @global string $pagenow The filename of the current screen. */ function default_password_nag() { global $pagenow; // Short-circuit it. if ( 'profile.php' === $pagenow || ! get_user_option( 'default_password_nag' ) ) { return; } $default_password_nag_message = sprintf( '<p><strong>%1$s</strong> %2$s</p>', __( 'Notice:' ), __( 'You are using the auto-generated password for your account. Would you like to change it?' ) ); $default_password_nag_message .= sprintf( '<p><a href="%1$s">%2$s</a> | ', esc_url( get_edit_profile_url() . '#password' ), __( 'Yes, take me to my profile page' ) ); $default_password_nag_message .= sprintf( '<a href="%1$s" id="default-password-nag-no">%2$s</a></p>', '?default_password_nag=0', __( 'No thanks, do not remind me again' ) ); wp_admin_notice( $default_password_nag_message, array( 'additional_classes' => array( 'error', 'default-password-nag' ), 'paragraph_wrap' => false, ) ); } /** * @since 3.5.0 * @access private */ function delete_users_add_js() { ?> <script> jQuery( function($) { var submit = $('#submit').prop('disabled', true); $('input[name="delete_option"]').one('change', function() { submit.prop('disabled', false); }); $('#reassign_user').focus( function() { $('#delete_option1').prop('checked', true).trigger('change'); }); } ); </script> <?php } /** * Optional SSL preference that can be turned on by hooking to the 'personal_options' action. * * See the {@see 'personal_options'} action. * * @since 2.7.0 * * @param WP_User $user User data object. */ function use_ssl_preference( $user ) { ?> <tr class="user-use-ssl-wrap"> <th scope="row"><?php _e( 'Use https' ); ?></th> <td><label for="use_ssl"><input name="use_ssl" type="checkbox" id="use_ssl" value="1" <?php checked( '1', $user->use_ssl ); ?> /> <?php _e( 'Always use https when visiting the admin' ); ?></label></td> </tr> <?php } /** * @since MU (3.0.0) * * @param string $text * @return string */ function admin_created_user_email( $text ) { $roles = get_editable_roles(); $role = $roles[ $_REQUEST['role'] ]; if ( '' !== get_bloginfo( 'name' ) ) { $site_title = wp_specialchars_decode( get_bloginfo( 'name' ), ENT_QUOTES ); } else { $site_title = parse_url( home_url(), PHP_URL_HOST ); } return sprintf( /* translators: 1: Site title, 2: Site URL, 3: User role. */ __( 'Hi, You\'ve been invited to join \'%1$s\' at %2$s with the role of %3$s. If you do not want to join this site please ignore this email. This invitation will expire in a few days. Please click the following link to activate your user account: %%s' ), $site_title, home_url(), wp_specialchars_decode( translate_user_role( $role['name'] ) ) ); } /** * Checks if the Authorize Application Password request is valid. * * @since 5.6.0 * @since 6.2.0 Allow insecure HTTP connections for the local environment. * @since 6.3.2 Validates the success and reject URLs to prevent `javascript` pseudo protocol from being executed. * * @param array $request { * The array of request data. All arguments are optional and may be empty. * * @type string $app_name The suggested name of the application. * @type string $app_id A UUID provided by the application to uniquely identify it. * @type string $success_url The URL the user will be redirected to after approving the application. * @type string $reject_url The URL the user will be redirected to after rejecting the application. * } * @param WP_User $user The user authorizing the application. * @return true|WP_Error True if the request is valid, a WP_Error object contains errors if not. */ function wp_is_authorize_application_password_request_valid( $request, $user ) { $error = new WP_Error(); if ( isset( $request['success_url'] ) ) { $validated_success_url = wp_is_authorize_application_redirect_url_valid( $request['success_url'] ); if ( is_wp_error( $validated_success_url ) ) { $error->add( $validated_success_url->get_error_code(), $validated_success_url->get_error_message() ); } } if ( isset( $request['reject_url'] ) ) { $validated_reject_url = wp_is_authorize_application_redirect_url_valid( $request['reject_url'] ); if ( is_wp_error( $validated_reject_url ) ) { $error->add( $validated_reject_url->get_error_code(), $validated_reject_url->get_error_message() ); } } if ( ! empty( $request['app_id'] ) && ! wp_is_uuid( $request['app_id'] ) ) { $error->add( 'invalid_app_id', __( 'The application ID must be a UUID.' ) ); } /** * Fires before application password errors are returned. * * @since 5.6.0 * * @param WP_Error $error The error object. * @param array $request The array of request data. * @param WP_User $user The user authorizing the application. */ do_action( 'wp_authorize_application_password_request_errors', $error, $request, $user ); if ( $error->has_errors() ) { return $error; } return true; } /** * Validates the redirect URL protocol scheme. The protocol can be anything except `http` and `javascript`. * * @since 6.3.2 * * @param string $url The redirect URL to be validated. * @return true|WP_Error True if the redirect URL is valid, a WP_Error object otherwise. */ function wp_is_authorize_application_redirect_url_valid( $url ) { $bad_protocols = array( 'javascript', 'data' ); if ( empty( $url ) ) { return true; } // Based on https://www.rfc-editor.org/rfc/rfc2396#section-3.1 $valid_scheme_regex = '/^[a-zA-Z][a-zA-Z0-9+.-]*:/'; if ( ! preg_match( $valid_scheme_regex, $url ) ) { return new WP_Error( 'invalid_redirect_url_format', __( 'Invalid URL format.' ) ); } /** * Filters the list of invalid protocols used in applications redirect URLs. * * @since 6.3.2 * * @param string[] $bad_protocols Array of invalid protocols. * @param string $url The redirect URL to be validated. */ $invalid_protocols = apply_filters( 'wp_authorize_application_redirect_url_invalid_protocols', $bad_protocols, $url ); $invalid_protocols = array_map( 'strtolower', $invalid_protocols ); $scheme = wp_parse_url( $url, PHP_URL_SCHEME ); $host = wp_parse_url( $url, PHP_URL_HOST ); $is_local = 'local' === wp_get_environment_type(); // Validates if the proper URI format is applied to the URL. if ( empty( $host ) || empty( $scheme ) || in_array( strtolower( $scheme ), $invalid_protocols, true ) ) { return new WP_Error( 'invalid_redirect_url_format', __( 'Invalid URL format.' ) ); } if ( 'http' === $scheme && ! $is_local ) { return new WP_Error( 'invalid_redirect_scheme', __( 'The URL must be served over a secure connection.' ) ); } return true; } includes/schema.php 0000644 00000123643 15135536347 0010350 0 ustar 00 <?php /** * WordPress Administration Scheme API * * Here we keep the DB structure and option values. * * @package WordPress * @subpackage Administration */ /** * Declare these as global in case schema.php is included from a function. * * @global wpdb $wpdb WordPress database abstraction object. * @global array $wp_queries * @global string $charset_collate */ global $wpdb, $wp_queries, $charset_collate; /** * The database character collate. */ $charset_collate = $wpdb->get_charset_collate(); /** * Retrieve the SQL for creating database tables. * * @since 3.3.0 * * @global wpdb $wpdb WordPress database abstraction object. * * @param string $scope Optional. The tables for which to retrieve SQL. Can be all, global, ms_global, or blog tables. Defaults to all. * @param int $blog_id Optional. The site ID for which to retrieve SQL. Default is the current site ID. * @return string The SQL needed to create the requested tables. */ function wp_get_db_schema( $scope = 'all', $blog_id = null ) { global $wpdb; $charset_collate = $wpdb->get_charset_collate(); if ( $blog_id && (int) $blog_id !== $wpdb->blogid ) { $old_blog_id = $wpdb->set_blog_id( $blog_id ); } // Engage multisite if in the middle of turning it on from network.php. $is_multisite = is_multisite() || ( defined( 'WP_INSTALLING_NETWORK' ) && WP_INSTALLING_NETWORK ); /* * Indexes have a maximum size of 767 bytes. Historically, we haven't need to be concerned about that. * As of 4.2, however, we moved to utf8mb4, which uses 4 bytes per character. This means that an index which * used to have room for floor(767/3) = 255 characters, now only has room for floor(767/4) = 191 characters. */ $max_index_length = 191; // Blog-specific tables. $blog_tables = "CREATE TABLE $wpdb->termmeta ( meta_id bigint(20) unsigned NOT NULL auto_increment, term_id bigint(20) unsigned NOT NULL default '0', meta_key varchar(255) default NULL, meta_value longtext, PRIMARY KEY (meta_id), KEY term_id (term_id), KEY meta_key (meta_key($max_index_length)) ) $charset_collate; CREATE TABLE $wpdb->terms ( term_id bigint(20) unsigned NOT NULL auto_increment, name varchar(200) NOT NULL default '', slug varchar(200) NOT NULL default '', term_group bigint(10) NOT NULL default 0, PRIMARY KEY (term_id), KEY slug (slug($max_index_length)), KEY name (name($max_index_length)) ) $charset_collate; CREATE TABLE $wpdb->term_taxonomy ( term_taxonomy_id bigint(20) unsigned NOT NULL auto_increment, term_id bigint(20) unsigned NOT NULL default 0, taxonomy varchar(32) NOT NULL default '', description longtext NOT NULL, parent bigint(20) unsigned NOT NULL default 0, count bigint(20) NOT NULL default 0, PRIMARY KEY (term_taxonomy_id), UNIQUE KEY term_id_taxonomy (term_id,taxonomy), KEY taxonomy (taxonomy) ) $charset_collate; CREATE TABLE $wpdb->term_relationships ( object_id bigint(20) unsigned NOT NULL default 0, term_taxonomy_id bigint(20) unsigned NOT NULL default 0, term_order int(11) NOT NULL default 0, PRIMARY KEY (object_id,term_taxonomy_id), KEY term_taxonomy_id (term_taxonomy_id) ) $charset_collate; CREATE TABLE $wpdb->commentmeta ( meta_id bigint(20) unsigned NOT NULL auto_increment, comment_id bigint(20) unsigned NOT NULL default '0', meta_key varchar(255) default NULL, meta_value longtext, PRIMARY KEY (meta_id), KEY comment_id (comment_id), KEY meta_key (meta_key($max_index_length)) ) $charset_collate; CREATE TABLE $wpdb->comments ( comment_ID bigint(20) unsigned NOT NULL auto_increment, comment_post_ID bigint(20) unsigned NOT NULL default '0', comment_author tinytext NOT NULL, comment_author_email varchar(100) NOT NULL default '', comment_author_url varchar(200) NOT NULL default '', comment_author_IP varchar(100) NOT NULL default '', comment_date datetime NOT NULL default '0000-00-00 00:00:00', comment_date_gmt datetime NOT NULL default '0000-00-00 00:00:00', comment_content text NOT NULL, comment_karma int(11) NOT NULL default '0', comment_approved varchar(20) NOT NULL default '1', comment_agent varchar(255) NOT NULL default '', comment_type varchar(20) NOT NULL default 'comment', comment_parent bigint(20) unsigned NOT NULL default '0', user_id bigint(20) unsigned NOT NULL default '0', PRIMARY KEY (comment_ID), KEY comment_post_ID (comment_post_ID), KEY comment_approved_date_gmt (comment_approved,comment_date_gmt), KEY comment_date_gmt (comment_date_gmt), KEY comment_parent (comment_parent), KEY comment_author_email (comment_author_email(10)) ) $charset_collate; CREATE TABLE $wpdb->links ( link_id bigint(20) unsigned NOT NULL auto_increment, link_url varchar(255) NOT NULL default '', link_name varchar(255) NOT NULL default '', link_image varchar(255) NOT NULL default '', link_target varchar(25) NOT NULL default '', link_description varchar(255) NOT NULL default '', link_visible varchar(20) NOT NULL default 'Y', link_owner bigint(20) unsigned NOT NULL default '1', link_rating int(11) NOT NULL default '0', link_updated datetime NOT NULL default '0000-00-00 00:00:00', link_rel varchar(255) NOT NULL default '', link_notes mediumtext NOT NULL, link_rss varchar(255) NOT NULL default '', PRIMARY KEY (link_id), KEY link_visible (link_visible) ) $charset_collate; CREATE TABLE $wpdb->options ( option_id bigint(20) unsigned NOT NULL auto_increment, option_name varchar(191) NOT NULL default '', option_value longtext NOT NULL, autoload varchar(20) NOT NULL default 'yes', PRIMARY KEY (option_id), UNIQUE KEY option_name (option_name), KEY autoload (autoload) ) $charset_collate; CREATE TABLE $wpdb->postmeta ( meta_id bigint(20) unsigned NOT NULL auto_increment, post_id bigint(20) unsigned NOT NULL default '0', meta_key varchar(255) default NULL, meta_value longtext, PRIMARY KEY (meta_id), KEY post_id (post_id), KEY meta_key (meta_key($max_index_length)) ) $charset_collate; CREATE TABLE $wpdb->posts ( ID bigint(20) unsigned NOT NULL auto_increment, post_author bigint(20) unsigned NOT NULL default '0', post_date datetime NOT NULL default '0000-00-00 00:00:00', post_date_gmt datetime NOT NULL default '0000-00-00 00:00:00', post_content longtext NOT NULL, post_title text NOT NULL, post_excerpt text NOT NULL, post_status varchar(20) NOT NULL default 'publish', comment_status varchar(20) NOT NULL default 'open', ping_status varchar(20) NOT NULL default 'open', post_password varchar(255) NOT NULL default '', post_name varchar(200) NOT NULL default '', to_ping text NOT NULL, pinged text NOT NULL, post_modified datetime NOT NULL default '0000-00-00 00:00:00', post_modified_gmt datetime NOT NULL default '0000-00-00 00:00:00', post_content_filtered longtext NOT NULL, post_parent bigint(20) unsigned NOT NULL default '0', guid varchar(255) NOT NULL default '', menu_order int(11) NOT NULL default '0', post_type varchar(20) NOT NULL default 'post', post_mime_type varchar(100) NOT NULL default '', comment_count bigint(20) NOT NULL default '0', PRIMARY KEY (ID), KEY post_name (post_name($max_index_length)), KEY type_status_date (post_type,post_status,post_date,ID), KEY post_parent (post_parent), KEY post_author (post_author) ) $charset_collate;\n"; // Single site users table. The multisite flavor of the users table is handled below. $users_single_table = "CREATE TABLE $wpdb->users ( ID bigint(20) unsigned NOT NULL auto_increment, user_login varchar(60) NOT NULL default '', user_pass varchar(255) NOT NULL default '', user_nicename varchar(50) NOT NULL default '', user_email varchar(100) NOT NULL default '', user_url varchar(100) NOT NULL default '', user_registered datetime NOT NULL default '0000-00-00 00:00:00', user_activation_key varchar(255) NOT NULL default '', user_status int(11) NOT NULL default '0', display_name varchar(250) NOT NULL default '', PRIMARY KEY (ID), KEY user_login_key (user_login), KEY user_nicename (user_nicename), KEY user_email (user_email) ) $charset_collate;\n"; // Multisite users table. $users_multi_table = "CREATE TABLE $wpdb->users ( ID bigint(20) unsigned NOT NULL auto_increment, user_login varchar(60) NOT NULL default '', user_pass varchar(255) NOT NULL default '', user_nicename varchar(50) NOT NULL default '', user_email varchar(100) NOT NULL default '', user_url varchar(100) NOT NULL default '', user_registered datetime NOT NULL default '0000-00-00 00:00:00', user_activation_key varchar(255) NOT NULL default '', user_status int(11) NOT NULL default '0', display_name varchar(250) NOT NULL default '', spam tinyint(2) NOT NULL default '0', deleted tinyint(2) NOT NULL default '0', PRIMARY KEY (ID), KEY user_login_key (user_login), KEY user_nicename (user_nicename), KEY user_email (user_email) ) $charset_collate;\n"; // Usermeta. $usermeta_table = "CREATE TABLE $wpdb->usermeta ( umeta_id bigint(20) unsigned NOT NULL auto_increment, user_id bigint(20) unsigned NOT NULL default '0', meta_key varchar(255) default NULL, meta_value longtext, PRIMARY KEY (umeta_id), KEY user_id (user_id), KEY meta_key (meta_key($max_index_length)) ) $charset_collate;\n"; // Global tables. if ( $is_multisite ) { $global_tables = $users_multi_table . $usermeta_table; } else { $global_tables = $users_single_table . $usermeta_table; } // Multisite global tables. $ms_global_tables = "CREATE TABLE $wpdb->blogs ( blog_id bigint(20) NOT NULL auto_increment, site_id bigint(20) NOT NULL default '0', domain varchar(200) NOT NULL default '', path varchar(100) NOT NULL default '', registered datetime NOT NULL default '0000-00-00 00:00:00', last_updated datetime NOT NULL default '0000-00-00 00:00:00', public tinyint(2) NOT NULL default '1', archived tinyint(2) NOT NULL default '0', mature tinyint(2) NOT NULL default '0', spam tinyint(2) NOT NULL default '0', deleted tinyint(2) NOT NULL default '0', lang_id int(11) NOT NULL default '0', PRIMARY KEY (blog_id), KEY domain (domain(50),path(5)), KEY lang_id (lang_id) ) $charset_collate; CREATE TABLE $wpdb->blogmeta ( meta_id bigint(20) unsigned NOT NULL auto_increment, blog_id bigint(20) NOT NULL default '0', meta_key varchar(255) default NULL, meta_value longtext, PRIMARY KEY (meta_id), KEY meta_key (meta_key($max_index_length)), KEY blog_id (blog_id) ) $charset_collate; CREATE TABLE $wpdb->registration_log ( ID bigint(20) NOT NULL auto_increment, email varchar(255) NOT NULL default '', IP varchar(30) NOT NULL default '', blog_id bigint(20) NOT NULL default '0', date_registered datetime NOT NULL default '0000-00-00 00:00:00', PRIMARY KEY (ID), KEY IP (IP) ) $charset_collate; CREATE TABLE $wpdb->site ( id bigint(20) NOT NULL auto_increment, domain varchar(200) NOT NULL default '', path varchar(100) NOT NULL default '', PRIMARY KEY (id), KEY domain (domain(140),path(51)) ) $charset_collate; CREATE TABLE $wpdb->sitemeta ( meta_id bigint(20) NOT NULL auto_increment, site_id bigint(20) NOT NULL default '0', meta_key varchar(255) default NULL, meta_value longtext, PRIMARY KEY (meta_id), KEY meta_key (meta_key($max_index_length)), KEY site_id (site_id) ) $charset_collate; CREATE TABLE $wpdb->signups ( signup_id bigint(20) NOT NULL auto_increment, domain varchar(200) NOT NULL default '', path varchar(100) NOT NULL default '', title longtext NOT NULL, user_login varchar(60) NOT NULL default '', user_email varchar(100) NOT NULL default '', registered datetime NOT NULL default '0000-00-00 00:00:00', activated datetime NOT NULL default '0000-00-00 00:00:00', active tinyint(1) NOT NULL default '0', activation_key varchar(50) NOT NULL default '', meta longtext, PRIMARY KEY (signup_id), KEY activation_key (activation_key), KEY user_email (user_email), KEY user_login_email (user_login,user_email), KEY domain_path (domain(140),path(51)) ) $charset_collate;"; switch ( $scope ) { case 'blog': $queries = $blog_tables; break; case 'global': $queries = $global_tables; if ( $is_multisite ) { $queries .= $ms_global_tables; } break; case 'ms_global': $queries = $ms_global_tables; break; case 'all': default: $queries = $global_tables . $blog_tables; if ( $is_multisite ) { $queries .= $ms_global_tables; } break; } if ( isset( $old_blog_id ) ) { $wpdb->set_blog_id( $old_blog_id ); } return $queries; } // Populate for back compat. $wp_queries = wp_get_db_schema( 'all' ); /** * Create WordPress options and set the default values. * * @since 1.5.0 * @since 5.1.0 The $options parameter has been added. * * @global wpdb $wpdb WordPress database abstraction object. * @global int $wp_db_version WordPress database version. * @global int $wp_current_db_version The old (current) database version. * * @param array $options Optional. Custom option $key => $value pairs to use. Default empty array. */ function populate_options( array $options = array() ) { global $wpdb, $wp_db_version, $wp_current_db_version; $guessurl = wp_guess_url(); /** * Fires before creating WordPress options and populating their default values. * * @since 2.6.0 */ do_action( 'populate_options' ); // If WP_DEFAULT_THEME doesn't exist, fall back to the latest core default theme. $stylesheet = WP_DEFAULT_THEME; $template = WP_DEFAULT_THEME; $theme = wp_get_theme( WP_DEFAULT_THEME ); if ( ! $theme->exists() ) { $theme = WP_Theme::get_core_default_theme(); } // If we can't find a core default theme, WP_DEFAULT_THEME is the best we can do. if ( $theme ) { $stylesheet = $theme->get_stylesheet(); $template = $theme->get_template(); } $timezone_string = ''; $gmt_offset = 0; /* * translators: default GMT offset or timezone string. Must be either a valid offset (-12 to 14) * or a valid timezone string (America/New_York). See https://www.php.net/manual/en/timezones.php * for all timezone strings currently supported by PHP. * * Important: When a previous timezone string, like `Europe/Kiev`, has been superseded by an * updated one, like `Europe/Kyiv`, as a rule of thumb, the **old** timezone name should be used * in the "translation" to allow for the default timezone setting to be PHP cross-version compatible, * as old timezone names will be recognized in new PHP versions, while new timezone names cannot * be recognized in old PHP versions. * * To verify which timezone strings are available in the _oldest_ PHP version supported, you can * use https://3v4l.org/6YQAt#v5.6.20 and replace the "BR" (Brazil) in the code line with the * country code for which you want to look up the supported timezone names. */ $offset_or_tz = _x( '0', 'default GMT offset or timezone string' ); if ( is_numeric( $offset_or_tz ) ) { $gmt_offset = $offset_or_tz; } elseif ( $offset_or_tz && in_array( $offset_or_tz, timezone_identifiers_list( DateTimeZone::ALL_WITH_BC ), true ) ) { $timezone_string = $offset_or_tz; } $defaults = array( 'siteurl' => $guessurl, 'home' => $guessurl, 'blogname' => __( 'My Site' ), 'blogdescription' => '', 'users_can_register' => 0, 'admin_email' => 'you@example.com', /* translators: Default start of the week. 0 = Sunday, 1 = Monday. */ 'start_of_week' => _x( '1', 'start of week' ), 'use_balanceTags' => 0, 'use_smilies' => 1, 'require_name_email' => 1, 'comments_notify' => 1, 'posts_per_rss' => 10, 'rss_use_excerpt' => 0, 'mailserver_url' => 'mail.example.com', 'mailserver_login' => 'login@example.com', 'mailserver_pass' => 'password', 'mailserver_port' => 110, 'default_category' => 1, 'default_comment_status' => 'open', 'default_ping_status' => 'open', 'default_pingback_flag' => 1, 'posts_per_page' => 10, /* translators: Default date format, see https://www.php.net/manual/datetime.format.php */ 'date_format' => __( 'F j, Y' ), /* translators: Default time format, see https://www.php.net/manual/datetime.format.php */ 'time_format' => __( 'g:i a' ), /* translators: Links last updated date format, see https://www.php.net/manual/datetime.format.php */ 'links_updated_date_format' => __( 'F j, Y g:i a' ), 'comment_moderation' => 0, 'moderation_notify' => 1, 'permalink_structure' => '', 'rewrite_rules' => '', 'hack_file' => 0, 'blog_charset' => 'UTF-8', 'moderation_keys' => '', 'active_plugins' => array(), 'category_base' => '', 'ping_sites' => 'http://rpc.pingomatic.com/', 'comment_max_links' => 2, 'gmt_offset' => $gmt_offset, // 1.5.0 'default_email_category' => 1, 'recently_edited' => '', 'template' => $template, 'stylesheet' => $stylesheet, 'comment_registration' => 0, 'html_type' => 'text/html', // 1.5.1 'use_trackback' => 0, // 2.0.0 'default_role' => 'subscriber', 'db_version' => $wp_db_version, // 2.0.1 'uploads_use_yearmonth_folders' => 1, 'upload_path' => '', // 2.1.0 'blog_public' => '1', 'default_link_category' => 2, 'show_on_front' => 'posts', // 2.2.0 'tag_base' => '', // 2.5.0 'show_avatars' => '1', 'avatar_rating' => 'G', 'upload_url_path' => '', 'thumbnail_size_w' => 150, 'thumbnail_size_h' => 150, 'thumbnail_crop' => 1, 'medium_size_w' => 300, 'medium_size_h' => 300, // 2.6.0 'avatar_default' => 'mystery', // 2.7.0 'large_size_w' => 1024, 'large_size_h' => 1024, 'image_default_link_type' => 'none', 'image_default_size' => '', 'image_default_align' => '', 'close_comments_for_old_posts' => 0, 'close_comments_days_old' => 14, 'thread_comments' => 1, 'thread_comments_depth' => 5, 'page_comments' => 0, 'comments_per_page' => 50, 'default_comments_page' => 'newest', 'comment_order' => 'asc', 'sticky_posts' => array(), 'widget_categories' => array(), 'widget_text' => array(), 'widget_rss' => array(), 'uninstall_plugins' => array(), // 2.8.0 'timezone_string' => $timezone_string, // 3.0.0 'page_for_posts' => 0, 'page_on_front' => 0, // 3.1.0 'default_post_format' => 0, // 3.5.0 'link_manager_enabled' => 0, // 4.3.0 'finished_splitting_shared_terms' => 1, 'site_icon' => 0, // 4.4.0 'medium_large_size_w' => 768, 'medium_large_size_h' => 0, // 4.9.6 'wp_page_for_privacy_policy' => 0, // 4.9.8 'show_comments_cookies_opt_in' => 1, // 5.3.0 'admin_email_lifespan' => ( time() + 6 * MONTH_IN_SECONDS ), // 5.5.0 'disallowed_keys' => '', 'comment_previously_approved' => 1, 'auto_plugin_theme_update_emails' => array(), // 5.6.0 'auto_update_core_dev' => 'enabled', 'auto_update_core_minor' => 'enabled', /* * Default to enabled for new installs. * See https://core.trac.wordpress.org/ticket/51742. */ 'auto_update_core_major' => 'enabled', // 5.8.0 'wp_force_deactivated_plugins' => array(), // 6.4.0 'wp_attachment_pages_enabled' => 0, ); // 3.3.0 if ( ! is_multisite() ) { $defaults['initial_db_version'] = ! empty( $wp_current_db_version ) && $wp_current_db_version < $wp_db_version ? $wp_current_db_version : $wp_db_version; } // 3.0.0 multisite. if ( is_multisite() ) { $defaults['permalink_structure'] = '/%year%/%monthnum%/%day%/%postname%/'; } $options = wp_parse_args( $options, $defaults ); // Set autoload to no for these options. $fat_options = array( 'moderation_keys', 'recently_edited', 'disallowed_keys', 'uninstall_plugins', 'auto_plugin_theme_update_emails', ); $keys = "'" . implode( "', '", array_keys( $options ) ) . "'"; $existing_options = $wpdb->get_col( "SELECT option_name FROM $wpdb->options WHERE option_name in ( $keys )" ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared $insert = ''; foreach ( $options as $option => $value ) { if ( in_array( $option, $existing_options, true ) ) { continue; } if ( in_array( $option, $fat_options, true ) ) { $autoload = 'no'; } else { $autoload = 'yes'; } if ( ! empty( $insert ) ) { $insert .= ', '; } $value = maybe_serialize( sanitize_option( $option, $value ) ); $insert .= $wpdb->prepare( '(%s, %s, %s)', $option, $value, $autoload ); } if ( ! empty( $insert ) ) { $wpdb->query( "INSERT INTO $wpdb->options (option_name, option_value, autoload) VALUES " . $insert ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared } // In case it is set, but blank, update "home". if ( ! __get_option( 'home' ) ) { update_option( 'home', $guessurl ); } // Delete unused options. $unusedoptions = array( 'blodotgsping_url', 'bodyterminator', 'emailtestonly', 'phoneemail_separator', 'smilies_directory', 'subjectprefix', 'use_bbcode', 'use_blodotgsping', 'use_phoneemail', 'use_quicktags', 'use_weblogsping', 'weblogs_cache_file', 'use_preview', 'use_htmltrans', 'smilies_directory', 'fileupload_allowedusers', 'use_phoneemail', 'default_post_status', 'default_post_category', 'archive_mode', 'time_difference', 'links_minadminlevel', 'links_use_adminlevels', 'links_rating_type', 'links_rating_char', 'links_rating_ignore_zero', 'links_rating_single_image', 'links_rating_image0', 'links_rating_image1', 'links_rating_image2', 'links_rating_image3', 'links_rating_image4', 'links_rating_image5', 'links_rating_image6', 'links_rating_image7', 'links_rating_image8', 'links_rating_image9', 'links_recently_updated_time', 'links_recently_updated_prepend', 'links_recently_updated_append', 'weblogs_cacheminutes', 'comment_allowed_tags', 'search_engine_friendly_urls', 'default_geourl_lat', 'default_geourl_lon', 'use_default_geourl', 'weblogs_xml_url', 'new_users_can_blog', '_wpnonce', '_wp_http_referer', 'Update', 'action', 'rich_editing', 'autosave_interval', 'deactivated_plugins', 'can_compress_scripts', 'page_uris', 'update_core', 'update_plugins', 'update_themes', 'doing_cron', 'random_seed', 'rss_excerpt_length', 'secret', 'use_linksupdate', 'default_comment_status_page', 'wporg_popular_tags', 'what_to_show', 'rss_language', 'language', 'enable_xmlrpc', 'enable_app', 'embed_autourls', 'default_post_edit_rows', 'gzipcompression', 'advanced_edit', ); foreach ( $unusedoptions as $option ) { delete_option( $option ); } // Delete obsolete magpie stuff. $wpdb->query( "DELETE FROM $wpdb->options WHERE option_name REGEXP '^rss_[0-9a-f]{32}(_ts)?$'" ); // Clear expired transients. delete_expired_transients( true ); } /** * Execute WordPress role creation for the various WordPress versions. * * @since 2.0.0 */ function populate_roles() { populate_roles_160(); populate_roles_210(); populate_roles_230(); populate_roles_250(); populate_roles_260(); populate_roles_270(); populate_roles_280(); populate_roles_300(); } /** * Create the roles for WordPress 2.0 * * @since 2.0.0 */ function populate_roles_160() { // Add roles. add_role( 'administrator', 'Administrator' ); add_role( 'editor', 'Editor' ); add_role( 'author', 'Author' ); add_role( 'contributor', 'Contributor' ); add_role( 'subscriber', 'Subscriber' ); // Add caps for Administrator role. $role = get_role( 'administrator' ); $role->add_cap( 'switch_themes' ); $role->add_cap( 'edit_themes' ); $role->add_cap( 'activate_plugins' ); $role->add_cap( 'edit_plugins' ); $role->add_cap( 'edit_users' ); $role->add_cap( 'edit_files' ); $role->add_cap( 'manage_options' ); $role->add_cap( 'moderate_comments' ); $role->add_cap( 'manage_categories' ); $role->add_cap( 'manage_links' ); $role->add_cap( 'upload_files' ); $role->add_cap( 'import' ); $role->add_cap( 'unfiltered_html' ); $role->add_cap( 'edit_posts' ); $role->add_cap( 'edit_others_posts' ); $role->add_cap( 'edit_published_posts' ); $role->add_cap( 'publish_posts' ); $role->add_cap( 'edit_pages' ); $role->add_cap( 'read' ); $role->add_cap( 'level_10' ); $role->add_cap( 'level_9' ); $role->add_cap( 'level_8' ); $role->add_cap( 'level_7' ); $role->add_cap( 'level_6' ); $role->add_cap( 'level_5' ); $role->add_cap( 'level_4' ); $role->add_cap( 'level_3' ); $role->add_cap( 'level_2' ); $role->add_cap( 'level_1' ); $role->add_cap( 'level_0' ); // Add caps for Editor role. $role = get_role( 'editor' ); $role->add_cap( 'moderate_comments' ); $role->add_cap( 'manage_categories' ); $role->add_cap( 'manage_links' ); $role->add_cap( 'upload_files' ); $role->add_cap( 'unfiltered_html' ); $role->add_cap( 'edit_posts' ); $role->add_cap( 'edit_others_posts' ); $role->add_cap( 'edit_published_posts' ); $role->add_cap( 'publish_posts' ); $role->add_cap( 'edit_pages' ); $role->add_cap( 'read' ); $role->add_cap( 'level_7' ); $role->add_cap( 'level_6' ); $role->add_cap( 'level_5' ); $role->add_cap( 'level_4' ); $role->add_cap( 'level_3' ); $role->add_cap( 'level_2' ); $role->add_cap( 'level_1' ); $role->add_cap( 'level_0' ); // Add caps for Author role. $role = get_role( 'author' ); $role->add_cap( 'upload_files' ); $role->add_cap( 'edit_posts' ); $role->add_cap( 'edit_published_posts' ); $role->add_cap( 'publish_posts' ); $role->add_cap( 'read' ); $role->add_cap( 'level_2' ); $role->add_cap( 'level_1' ); $role->add_cap( 'level_0' ); // Add caps for Contributor role. $role = get_role( 'contributor' ); $role->add_cap( 'edit_posts' ); $role->add_cap( 'read' ); $role->add_cap( 'level_1' ); $role->add_cap( 'level_0' ); // Add caps for Subscriber role. $role = get_role( 'subscriber' ); $role->add_cap( 'read' ); $role->add_cap( 'level_0' ); } /** * Create and modify WordPress roles for WordPress 2.1. * * @since 2.1.0 */ function populate_roles_210() { $roles = array( 'administrator', 'editor' ); foreach ( $roles as $role ) { $role = get_role( $role ); if ( empty( $role ) ) { continue; } $role->add_cap( 'edit_others_pages' ); $role->add_cap( 'edit_published_pages' ); $role->add_cap( 'publish_pages' ); $role->add_cap( 'delete_pages' ); $role->add_cap( 'delete_others_pages' ); $role->add_cap( 'delete_published_pages' ); $role->add_cap( 'delete_posts' ); $role->add_cap( 'delete_others_posts' ); $role->add_cap( 'delete_published_posts' ); $role->add_cap( 'delete_private_posts' ); $role->add_cap( 'edit_private_posts' ); $role->add_cap( 'read_private_posts' ); $role->add_cap( 'delete_private_pages' ); $role->add_cap( 'edit_private_pages' ); $role->add_cap( 'read_private_pages' ); } $role = get_role( 'administrator' ); if ( ! empty( $role ) ) { $role->add_cap( 'delete_users' ); $role->add_cap( 'create_users' ); } $role = get_role( 'author' ); if ( ! empty( $role ) ) { $role->add_cap( 'delete_posts' ); $role->add_cap( 'delete_published_posts' ); } $role = get_role( 'contributor' ); if ( ! empty( $role ) ) { $role->add_cap( 'delete_posts' ); } } /** * Create and modify WordPress roles for WordPress 2.3. * * @since 2.3.0 */ function populate_roles_230() { $role = get_role( 'administrator' ); if ( ! empty( $role ) ) { $role->add_cap( 'unfiltered_upload' ); } } /** * Create and modify WordPress roles for WordPress 2.5. * * @since 2.5.0 */ function populate_roles_250() { $role = get_role( 'administrator' ); if ( ! empty( $role ) ) { $role->add_cap( 'edit_dashboard' ); } } /** * Create and modify WordPress roles for WordPress 2.6. * * @since 2.6.0 */ function populate_roles_260() { $role = get_role( 'administrator' ); if ( ! empty( $role ) ) { $role->add_cap( 'update_plugins' ); $role->add_cap( 'delete_plugins' ); } } /** * Create and modify WordPress roles for WordPress 2.7. * * @since 2.7.0 */ function populate_roles_270() { $role = get_role( 'administrator' ); if ( ! empty( $role ) ) { $role->add_cap( 'install_plugins' ); $role->add_cap( 'update_themes' ); } } /** * Create and modify WordPress roles for WordPress 2.8. * * @since 2.8.0 */ function populate_roles_280() { $role = get_role( 'administrator' ); if ( ! empty( $role ) ) { $role->add_cap( 'install_themes' ); } } /** * Create and modify WordPress roles for WordPress 3.0. * * @since 3.0.0 */ function populate_roles_300() { $role = get_role( 'administrator' ); if ( ! empty( $role ) ) { $role->add_cap( 'update_core' ); $role->add_cap( 'list_users' ); $role->add_cap( 'remove_users' ); $role->add_cap( 'promote_users' ); $role->add_cap( 'edit_theme_options' ); $role->add_cap( 'delete_themes' ); $role->add_cap( 'export' ); } } if ( ! function_exists( 'install_network' ) ) : /** * Install Network. * * @since 3.0.0 */ function install_network() { if ( ! defined( 'WP_INSTALLING_NETWORK' ) ) { define( 'WP_INSTALLING_NETWORK', true ); } dbDelta( wp_get_db_schema( 'global' ) ); } endif; /** * Populate network settings. * * @since 3.0.0 * * @global wpdb $wpdb WordPress database abstraction object. * @global object $current_site * @global WP_Rewrite $wp_rewrite WordPress rewrite component. * * @param int $network_id ID of network to populate. * @param string $domain The domain name for the network. Example: "example.com". * @param string $email Email address for the network administrator. * @param string $site_name The name of the network. * @param string $path Optional. The path to append to the network's domain name. Default '/'. * @param bool $subdomain_install Optional. Whether the network is a subdomain installation or a subdirectory installation. * Default false, meaning the network is a subdirectory installation. * @return true|WP_Error True on success, or WP_Error on warning (with the installation otherwise successful, * so the error code must be checked) or failure. */ function populate_network( $network_id = 1, $domain = '', $email = '', $site_name = '', $path = '/', $subdomain_install = false ) { global $wpdb, $current_site, $wp_rewrite; $network_id = (int) $network_id; $errors = new WP_Error(); if ( '' === $domain ) { $errors->add( 'empty_domain', __( 'You must provide a domain name.' ) ); } if ( '' === $site_name ) { $errors->add( 'empty_sitename', __( 'You must provide a name for your network of sites.' ) ); } // Check for network collision. $network_exists = false; if ( is_multisite() ) { if ( get_network( $network_id ) ) { $errors->add( 'siteid_exists', __( 'The network already exists.' ) ); } } else { if ( $network_id === (int) $wpdb->get_var( $wpdb->prepare( "SELECT id FROM $wpdb->site WHERE id = %d", $network_id ) ) ) { $errors->add( 'siteid_exists', __( 'The network already exists.' ) ); } } if ( ! is_email( $email ) ) { $errors->add( 'invalid_email', __( 'You must provide a valid email address.' ) ); } if ( $errors->has_errors() ) { return $errors; } if ( 1 === $network_id ) { $wpdb->insert( $wpdb->site, array( 'domain' => $domain, 'path' => $path, ) ); $network_id = $wpdb->insert_id; } else { $wpdb->insert( $wpdb->site, array( 'domain' => $domain, 'path' => $path, 'id' => $network_id, ) ); } populate_network_meta( $network_id, array( 'admin_email' => $email, 'site_name' => $site_name, 'subdomain_install' => $subdomain_install, ) ); /* * When upgrading from single to multisite, assume the current site will * become the main site of the network. When using populate_network() * to create another network in an existing multisite environment, skip * these steps since the main site of the new network has not yet been * created. */ if ( ! is_multisite() ) { $current_site = new stdClass(); $current_site->domain = $domain; $current_site->path = $path; $current_site->site_name = ucfirst( $domain ); $wpdb->insert( $wpdb->blogs, array( 'site_id' => $network_id, 'blog_id' => 1, 'domain' => $domain, 'path' => $path, 'registered' => current_time( 'mysql' ), ) ); $current_site->blog_id = $wpdb->insert_id; $site_user_id = (int) $wpdb->get_var( $wpdb->prepare( "SELECT meta_value FROM $wpdb->sitemeta WHERE meta_key = %s AND site_id = %d", 'admin_user_id', $network_id ) ); update_user_meta( $site_user_id, 'source_domain', $domain ); update_user_meta( $site_user_id, 'primary_blog', $current_site->blog_id ); // Unable to use update_network_option() while populating the network. $wpdb->insert( $wpdb->sitemeta, array( 'site_id' => $network_id, 'meta_key' => 'main_site', 'meta_value' => $current_site->blog_id, ) ); if ( $subdomain_install ) { $wp_rewrite->set_permalink_structure( '/%year%/%monthnum%/%day%/%postname%/' ); } else { $wp_rewrite->set_permalink_structure( '/blog/%year%/%monthnum%/%day%/%postname%/' ); } flush_rewrite_rules(); if ( ! $subdomain_install ) { return true; } $vhost_ok = false; $errstr = ''; $hostname = substr( md5( time() ), 0, 6 ) . '.' . $domain; // Very random hostname! $page = wp_remote_get( 'http://' . $hostname, array( 'timeout' => 5, 'httpversion' => '1.1', ) ); if ( is_wp_error( $page ) ) { $errstr = $page->get_error_message(); } elseif ( 200 === wp_remote_retrieve_response_code( $page ) ) { $vhost_ok = true; } if ( ! $vhost_ok ) { $msg = '<p><strong>' . __( 'Warning! Wildcard DNS may not be configured correctly!' ) . '</strong></p>'; $msg .= '<p>' . sprintf( /* translators: %s: Host name. */ __( 'The installer attempted to contact a random hostname (%s) on your domain.' ), '<code>' . $hostname . '</code>' ); if ( ! empty( $errstr ) ) { /* translators: %s: Error message. */ $msg .= ' ' . sprintf( __( 'This resulted in an error message: %s' ), '<code>' . $errstr . '</code>' ); } $msg .= '</p>'; $msg .= '<p>' . sprintf( /* translators: %s: Asterisk symbol (*). */ __( 'To use a subdomain configuration, you must have a wildcard entry in your DNS. This usually means adding a %s hostname record pointing at your web server in your DNS configuration tool.' ), '<code>*</code>' ) . '</p>'; $msg .= '<p>' . __( 'You can still use your site but any subdomain you create may not be accessible. If you know your DNS is correct, ignore this message.' ) . '</p>'; return new WP_Error( 'no_wildcard_dns', $msg ); } } return true; } /** * Creates WordPress network meta and sets the default values. * * @since 5.1.0 * * @global wpdb $wpdb WordPress database abstraction object. * @global int $wp_db_version WordPress database version. * * @param int $network_id Network ID to populate meta for. * @param array $meta Optional. Custom meta $key => $value pairs to use. Default empty array. */ function populate_network_meta( $network_id, array $meta = array() ) { global $wpdb, $wp_db_version; $network_id = (int) $network_id; $email = ! empty( $meta['admin_email'] ) ? $meta['admin_email'] : ''; $subdomain_install = isset( $meta['subdomain_install'] ) ? (int) $meta['subdomain_install'] : 0; // If a user with the provided email does not exist, default to the current user as the new network admin. $site_user = ! empty( $email ) ? get_user_by( 'email', $email ) : false; if ( false === $site_user ) { $site_user = wp_get_current_user(); } if ( empty( $email ) ) { $email = $site_user->user_email; } $template = get_option( 'template' ); $stylesheet = get_option( 'stylesheet' ); $allowed_themes = array( $stylesheet => true ); if ( $template !== $stylesheet ) { $allowed_themes[ $template ] = true; } if ( WP_DEFAULT_THEME !== $stylesheet && WP_DEFAULT_THEME !== $template ) { $allowed_themes[ WP_DEFAULT_THEME ] = true; } // If WP_DEFAULT_THEME doesn't exist, also include the latest core default theme. if ( ! wp_get_theme( WP_DEFAULT_THEME )->exists() ) { $core_default = WP_Theme::get_core_default_theme(); if ( $core_default ) { $allowed_themes[ $core_default->get_stylesheet() ] = true; } } if ( function_exists( 'clean_network_cache' ) ) { clean_network_cache( $network_id ); } else { wp_cache_delete( $network_id, 'networks' ); } if ( ! is_multisite() ) { $site_admins = array( $site_user->user_login ); $users = get_users( array( 'fields' => array( 'user_login' ), 'role' => 'administrator', ) ); if ( $users ) { foreach ( $users as $user ) { $site_admins[] = $user->user_login; } $site_admins = array_unique( $site_admins ); } } else { $site_admins = get_site_option( 'site_admins' ); } /* translators: Do not translate USERNAME, SITE_NAME, BLOG_URL, PASSWORD: those are placeholders. */ $welcome_email = __( 'Howdy USERNAME, Your new SITE_NAME site has been successfully set up at: BLOG_URL You can log in to the administrator account with the following information: Username: USERNAME Password: PASSWORD Log in here: BLOG_URLwp-login.php We hope you enjoy your new site. Thanks! --The Team @ SITE_NAME' ); $misc_exts = array( // Images. 'jpg', 'jpeg', 'png', 'gif', 'webp', 'avif', // Video. 'mov', 'avi', 'mpg', '3gp', '3g2', // "audio". 'midi', 'mid', // Miscellaneous. 'pdf', 'doc', 'ppt', 'odt', 'pptx', 'docx', 'pps', 'ppsx', 'xls', 'xlsx', 'key', ); $audio_exts = wp_get_audio_extensions(); $video_exts = wp_get_video_extensions(); $upload_filetypes = array_unique( array_merge( $misc_exts, $audio_exts, $video_exts ) ); $sitemeta = array( 'site_name' => __( 'My Network' ), 'admin_email' => $email, 'admin_user_id' => $site_user->ID, 'registration' => 'none', 'upload_filetypes' => implode( ' ', $upload_filetypes ), 'blog_upload_space' => 100, 'fileupload_maxk' => 1500, 'site_admins' => $site_admins, 'allowedthemes' => $allowed_themes, 'illegal_names' => array( 'www', 'web', 'root', 'admin', 'main', 'invite', 'administrator', 'files' ), 'wpmu_upgrade_site' => $wp_db_version, 'welcome_email' => $welcome_email, /* translators: %s: Site link. */ 'first_post' => __( 'Welcome to %s. This is your first post. Edit or delete it, then start writing!' ), // @todo - Network admins should have a method of editing the network siteurl (used for cookie hash). 'siteurl' => get_option( 'siteurl' ) . '/', 'add_new_users' => '0', 'upload_space_check_disabled' => is_multisite() ? get_site_option( 'upload_space_check_disabled' ) : '1', 'subdomain_install' => $subdomain_install, 'ms_files_rewriting' => is_multisite() ? get_site_option( 'ms_files_rewriting' ) : '0', 'user_count' => get_site_option( 'user_count' ), 'initial_db_version' => get_option( 'initial_db_version' ), 'active_sitewide_plugins' => array(), 'WPLANG' => get_locale(), ); if ( ! $subdomain_install ) { $sitemeta['illegal_names'][] = 'blog'; } $sitemeta = wp_parse_args( $meta, $sitemeta ); /** * Filters meta for a network on creation. * * @since 3.7.0 * * @param array $sitemeta Associative array of network meta keys and values to be inserted. * @param int $network_id ID of network to populate. */ $sitemeta = apply_filters( 'populate_network_meta', $sitemeta, $network_id ); $insert = ''; foreach ( $sitemeta as $meta_key => $meta_value ) { if ( is_array( $meta_value ) ) { $meta_value = serialize( $meta_value ); } if ( ! empty( $insert ) ) { $insert .= ', '; } $insert .= $wpdb->prepare( '( %d, %s, %s)', $network_id, $meta_key, $meta_value ); } $wpdb->query( "INSERT INTO $wpdb->sitemeta ( site_id, meta_key, meta_value ) VALUES " . $insert ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared } /** * Creates WordPress site meta and sets the default values. * * @since 5.1.0 * * @global wpdb $wpdb WordPress database abstraction object. * * @param int $site_id Site ID to populate meta for. * @param array $meta Optional. Custom meta $key => $value pairs to use. Default empty array. */ function populate_site_meta( $site_id, array $meta = array() ) { global $wpdb; $site_id = (int) $site_id; if ( ! is_site_meta_supported() ) { return; } if ( empty( $meta ) ) { return; } /** * Filters meta for a site on creation. * * @since 5.2.0 * * @param array $meta Associative array of site meta keys and values to be inserted. * @param int $site_id ID of site to populate. */ $site_meta = apply_filters( 'populate_site_meta', $meta, $site_id ); $insert = ''; foreach ( $site_meta as $meta_key => $meta_value ) { if ( is_array( $meta_value ) ) { $meta_value = serialize( $meta_value ); } if ( ! empty( $insert ) ) { $insert .= ', '; } $insert .= $wpdb->prepare( '( %d, %s, %s)', $site_id, $meta_key, $meta_value ); } $wpdb->query( "INSERT INTO $wpdb->blogmeta ( blog_id, meta_key, meta_value ) VALUES " . $insert ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared wp_cache_delete( $site_id, 'blog_meta' ); wp_cache_set_sites_last_changed(); } includes/class-pclzip.php 0000644 00000600126 15135536347 0011510 0 ustar 00 <?php // -------------------------------------------------------------------------------- // PhpConcept Library - Zip Module 2.8.2 // -------------------------------------------------------------------------------- // License GNU/LGPL - Vincent Blavet - August 2009 // http://www.phpconcept.net // -------------------------------------------------------------------------------- // // Presentation : // PclZip is a PHP library that manage ZIP archives. // So far tests show that archives generated by PclZip are readable by // WinZip application and other tools. // // Description : // See readme.txt and http://www.phpconcept.net // // Warning : // This library and the associated files are non commercial, non professional // work. // It should not have unexpected results. However if any damage is caused by // this software the author can not be responsible. // The use of this software is at the risk of the user. // // -------------------------------------------------------------------------------- // $Id: pclzip.lib.php,v 1.60 2009/09/30 21:01:04 vblavet Exp $ // -------------------------------------------------------------------------------- // ----- Constants if (!defined('PCLZIP_READ_BLOCK_SIZE')) { define( 'PCLZIP_READ_BLOCK_SIZE', 2048 ); } // ----- File list separator // In version 1.x of PclZip, the separator for file list is a space // (which is not a very smart choice, specifically for windows paths !). // A better separator should be a comma (,). This constant gives you the // ability to change that. // However notice that changing this value, may have impact on existing // scripts, using space separated filenames. // Recommended values for compatibility with older versions : //define( 'PCLZIP_SEPARATOR', ' ' ); // Recommended values for smart separation of filenames. if (!defined('PCLZIP_SEPARATOR')) { define( 'PCLZIP_SEPARATOR', ',' ); } // ----- Error configuration // 0 : PclZip Class integrated error handling // 1 : PclError external library error handling. By enabling this // you must ensure that you have included PclError library. // [2,...] : reserved for futur use if (!defined('PCLZIP_ERROR_EXTERNAL')) { define( 'PCLZIP_ERROR_EXTERNAL', 0 ); } // ----- Optional static temporary directory // By default temporary files are generated in the script current // path. // If defined : // - MUST BE terminated by a '/'. // - MUST be a valid, already created directory // Samples : // define( 'PCLZIP_TEMPORARY_DIR', '/temp/' ); // define( 'PCLZIP_TEMPORARY_DIR', 'C:/Temp/' ); if (!defined('PCLZIP_TEMPORARY_DIR')) { define( 'PCLZIP_TEMPORARY_DIR', '' ); } // ----- Optional threshold ratio for use of temporary files // Pclzip sense the size of the file to add/extract and decide to // use or not temporary file. The algorithm is looking for // memory_limit of PHP and apply a ratio. // threshold = memory_limit * ratio. // Recommended values are under 0.5. Default 0.47. // Samples : // define( 'PCLZIP_TEMPORARY_FILE_RATIO', 0.5 ); if (!defined('PCLZIP_TEMPORARY_FILE_RATIO')) { define( 'PCLZIP_TEMPORARY_FILE_RATIO', 0.47 ); } // -------------------------------------------------------------------------------- // ***** UNDER THIS LINE NOTHING NEEDS TO BE MODIFIED ***** // -------------------------------------------------------------------------------- // ----- Global variables $g_pclzip_version = "2.8.2"; // ----- Error codes // -1 : Unable to open file in binary write mode // -2 : Unable to open file in binary read mode // -3 : Invalid parameters // -4 : File does not exist // -5 : Filename is too long (max. 255) // -6 : Not a valid zip file // -7 : Invalid extracted file size // -8 : Unable to create directory // -9 : Invalid archive extension // -10 : Invalid archive format // -11 : Unable to delete file (unlink) // -12 : Unable to rename file (rename) // -13 : Invalid header checksum // -14 : Invalid archive size define( 'PCLZIP_ERR_USER_ABORTED', 2 ); define( 'PCLZIP_ERR_NO_ERROR', 0 ); define( 'PCLZIP_ERR_WRITE_OPEN_FAIL', -1 ); define( 'PCLZIP_ERR_READ_OPEN_FAIL', -2 ); define( 'PCLZIP_ERR_INVALID_PARAMETER', -3 ); define( 'PCLZIP_ERR_MISSING_FILE', -4 ); define( 'PCLZIP_ERR_FILENAME_TOO_LONG', -5 ); define( 'PCLZIP_ERR_INVALID_ZIP', -6 ); define( 'PCLZIP_ERR_BAD_EXTRACTED_FILE', -7 ); define( 'PCLZIP_ERR_DIR_CREATE_FAIL', -8 ); define( 'PCLZIP_ERR_BAD_EXTENSION', -9 ); define( 'PCLZIP_ERR_BAD_FORMAT', -10 ); define( 'PCLZIP_ERR_DELETE_FILE_FAIL', -11 ); define( 'PCLZIP_ERR_RENAME_FILE_FAIL', -12 ); define( 'PCLZIP_ERR_BAD_CHECKSUM', -13 ); define( 'PCLZIP_ERR_INVALID_ARCHIVE_ZIP', -14 ); define( 'PCLZIP_ERR_MISSING_OPTION_VALUE', -15 ); define( 'PCLZIP_ERR_INVALID_OPTION_VALUE', -16 ); define( 'PCLZIP_ERR_ALREADY_A_DIRECTORY', -17 ); define( 'PCLZIP_ERR_UNSUPPORTED_COMPRESSION', -18 ); define( 'PCLZIP_ERR_UNSUPPORTED_ENCRYPTION', -19 ); define( 'PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE', -20 ); define( 'PCLZIP_ERR_DIRECTORY_RESTRICTION', -21 ); // ----- Options values define( 'PCLZIP_OPT_PATH', 77001 ); define( 'PCLZIP_OPT_ADD_PATH', 77002 ); define( 'PCLZIP_OPT_REMOVE_PATH', 77003 ); define( 'PCLZIP_OPT_REMOVE_ALL_PATH', 77004 ); define( 'PCLZIP_OPT_SET_CHMOD', 77005 ); define( 'PCLZIP_OPT_EXTRACT_AS_STRING', 77006 ); define( 'PCLZIP_OPT_NO_COMPRESSION', 77007 ); define( 'PCLZIP_OPT_BY_NAME', 77008 ); define( 'PCLZIP_OPT_BY_INDEX', 77009 ); define( 'PCLZIP_OPT_BY_EREG', 77010 ); define( 'PCLZIP_OPT_BY_PREG', 77011 ); define( 'PCLZIP_OPT_COMMENT', 77012 ); define( 'PCLZIP_OPT_ADD_COMMENT', 77013 ); define( 'PCLZIP_OPT_PREPEND_COMMENT', 77014 ); define( 'PCLZIP_OPT_EXTRACT_IN_OUTPUT', 77015 ); define( 'PCLZIP_OPT_REPLACE_NEWER', 77016 ); define( 'PCLZIP_OPT_STOP_ON_ERROR', 77017 ); // Having big trouble with crypt. Need to multiply 2 long int // which is not correctly supported by PHP ... //define( 'PCLZIP_OPT_CRYPT', 77018 ); define( 'PCLZIP_OPT_EXTRACT_DIR_RESTRICTION', 77019 ); define( 'PCLZIP_OPT_TEMP_FILE_THRESHOLD', 77020 ); define( 'PCLZIP_OPT_ADD_TEMP_FILE_THRESHOLD', 77020 ); // alias define( 'PCLZIP_OPT_TEMP_FILE_ON', 77021 ); define( 'PCLZIP_OPT_ADD_TEMP_FILE_ON', 77021 ); // alias define( 'PCLZIP_OPT_TEMP_FILE_OFF', 77022 ); define( 'PCLZIP_OPT_ADD_TEMP_FILE_OFF', 77022 ); // alias // ----- File description attributes define( 'PCLZIP_ATT_FILE_NAME', 79001 ); define( 'PCLZIP_ATT_FILE_NEW_SHORT_NAME', 79002 ); define( 'PCLZIP_ATT_FILE_NEW_FULL_NAME', 79003 ); define( 'PCLZIP_ATT_FILE_MTIME', 79004 ); define( 'PCLZIP_ATT_FILE_CONTENT', 79005 ); define( 'PCLZIP_ATT_FILE_COMMENT', 79006 ); // ----- Call backs values define( 'PCLZIP_CB_PRE_EXTRACT', 78001 ); define( 'PCLZIP_CB_POST_EXTRACT', 78002 ); define( 'PCLZIP_CB_PRE_ADD', 78003 ); define( 'PCLZIP_CB_POST_ADD', 78004 ); /* For futur use define( 'PCLZIP_CB_PRE_LIST', 78005 ); define( 'PCLZIP_CB_POST_LIST', 78006 ); define( 'PCLZIP_CB_PRE_DELETE', 78007 ); define( 'PCLZIP_CB_POST_DELETE', 78008 ); */ // -------------------------------------------------------------------------------- // Class : PclZip // Description : // PclZip is the class that represent a Zip archive. // The public methods allow the manipulation of the archive. // Attributes : // Attributes must not be accessed directly. // Methods : // PclZip() : Object creator // create() : Creates the Zip archive // listContent() : List the content of the Zip archive // extract() : Extract the content of the archive // properties() : List the properties of the archive // -------------------------------------------------------------------------------- class PclZip { // ----- Filename of the zip file var $zipname = ''; // ----- File descriptor of the zip file var $zip_fd = 0; // ----- Internal error handling var $error_code = 1; var $error_string = ''; // ----- Current status of the magic_quotes_runtime // This value store the php configuration for magic_quotes // The class can then disable the magic_quotes and reset it after var $magic_quotes_status; // -------------------------------------------------------------------------------- // Function : PclZip() // Description : // Creates a PclZip object and set the name of the associated Zip archive // filename. // Note that no real action is taken, if the archive does not exist it is not // created. Use create() for that. // -------------------------------------------------------------------------------- function __construct($p_zipname) { // ----- Tests the zlib if (!function_exists('gzopen')) { die('Abort '.basename(__FILE__).' : Missing zlib extensions'); } // ----- Set the attributes $this->zipname = $p_zipname; $this->zip_fd = 0; $this->magic_quotes_status = -1; // ----- Return return; } public function PclZip($p_zipname) { self::__construct($p_zipname); } // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- // Function : // create($p_filelist, $p_add_dir="", $p_remove_dir="") // create($p_filelist, $p_option, $p_option_value, ...) // Description : // This method supports two different synopsis. The first one is historical. // This method creates a Zip Archive. The Zip file is created in the // filesystem. The files and directories indicated in $p_filelist // are added in the archive. See the parameters description for the // supported format of $p_filelist. // When a directory is in the list, the directory and its content is added // in the archive. // In this synopsis, the function takes an optional variable list of // options. See below the supported options. // Parameters : // $p_filelist : An array containing file or directory names, or // a string containing one filename or one directory name, or // a string containing a list of filenames and/or directory // names separated by spaces. // $p_add_dir : A path to add before the real path of the archived file, // in order to have it memorized in the archive. // $p_remove_dir : A path to remove from the real path of the file to archive, // in order to have a shorter path memorized in the archive. // When $p_add_dir and $p_remove_dir are set, $p_remove_dir // is removed first, before $p_add_dir is added. // Options : // PCLZIP_OPT_ADD_PATH : // PCLZIP_OPT_REMOVE_PATH : // PCLZIP_OPT_REMOVE_ALL_PATH : // PCLZIP_OPT_COMMENT : // PCLZIP_CB_PRE_ADD : // PCLZIP_CB_POST_ADD : // Return Values : // 0 on failure, // The list of the added files, with a status of the add action. // (see PclZip::listContent() for list entry format) // -------------------------------------------------------------------------------- function create($p_filelist) { $v_result=1; // ----- Reset the error handler $this->privErrorReset(); // ----- Set default values $v_options = array(); $v_options[PCLZIP_OPT_NO_COMPRESSION] = FALSE; // ----- Look for variable options arguments $v_size = func_num_args(); // ----- Look for arguments if ($v_size > 1) { // ----- Get the arguments $v_arg_list = func_get_args(); // ----- Remove from the options list the first argument array_shift($v_arg_list); $v_size--; // ----- Look for first arg if ((is_integer($v_arg_list[0])) && ($v_arg_list[0] > 77000)) { // ----- Parse the options $v_result = $this->privParseOptions($v_arg_list, $v_size, $v_options, array (PCLZIP_OPT_REMOVE_PATH => 'optional', PCLZIP_OPT_REMOVE_ALL_PATH => 'optional', PCLZIP_OPT_ADD_PATH => 'optional', PCLZIP_CB_PRE_ADD => 'optional', PCLZIP_CB_POST_ADD => 'optional', PCLZIP_OPT_NO_COMPRESSION => 'optional', PCLZIP_OPT_COMMENT => 'optional', PCLZIP_OPT_TEMP_FILE_THRESHOLD => 'optional', PCLZIP_OPT_TEMP_FILE_ON => 'optional', PCLZIP_OPT_TEMP_FILE_OFF => 'optional' //, PCLZIP_OPT_CRYPT => 'optional' )); if ($v_result != 1) { return 0; } } // ----- Look for 2 args // Here we need to support the first historic synopsis of the // method. else { // ----- Get the first argument $v_options[PCLZIP_OPT_ADD_PATH] = $v_arg_list[0]; // ----- Look for the optional second argument if ($v_size == 2) { $v_options[PCLZIP_OPT_REMOVE_PATH] = $v_arg_list[1]; } else if ($v_size > 2) { PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid number / type of arguments"); return 0; } } } // ----- Look for default option values $this->privOptionDefaultThreshold($v_options); // ----- Init $v_string_list = array(); $v_att_list = array(); $v_filedescr_list = array(); $p_result_list = array(); // ----- Look if the $p_filelist is really an array if (is_array($p_filelist)) { // ----- Look if the first element is also an array // This will mean that this is a file description entry if (isset($p_filelist[0]) && is_array($p_filelist[0])) { $v_att_list = $p_filelist; } // ----- The list is a list of string names else { $v_string_list = $p_filelist; } } // ----- Look if the $p_filelist is a string else if (is_string($p_filelist)) { // ----- Create a list from the string $v_string_list = explode(PCLZIP_SEPARATOR, $p_filelist); } // ----- Invalid variable type for $p_filelist else { PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid variable type p_filelist"); return 0; } // ----- Reformat the string list if (sizeof($v_string_list) != 0) { foreach ($v_string_list as $v_string) { if ($v_string != '') { $v_att_list[][PCLZIP_ATT_FILE_NAME] = $v_string; } else { } } } // ----- For each file in the list check the attributes $v_supported_attributes = array ( PCLZIP_ATT_FILE_NAME => 'mandatory' ,PCLZIP_ATT_FILE_NEW_SHORT_NAME => 'optional' ,PCLZIP_ATT_FILE_NEW_FULL_NAME => 'optional' ,PCLZIP_ATT_FILE_MTIME => 'optional' ,PCLZIP_ATT_FILE_CONTENT => 'optional' ,PCLZIP_ATT_FILE_COMMENT => 'optional' ); foreach ($v_att_list as $v_entry) { $v_result = $this->privFileDescrParseAtt($v_entry, $v_filedescr_list[], $v_options, $v_supported_attributes); if ($v_result != 1) { return 0; } } // ----- Expand the filelist (expand directories) $v_result = $this->privFileDescrExpand($v_filedescr_list, $v_options); if ($v_result != 1) { return 0; } // ----- Call the create fct $v_result = $this->privCreate($v_filedescr_list, $p_result_list, $v_options); if ($v_result != 1) { return 0; } // ----- Return return $p_result_list; } // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- // Function : // add($p_filelist, $p_add_dir="", $p_remove_dir="") // add($p_filelist, $p_option, $p_option_value, ...) // Description : // This method supports two synopsis. The first one is historical. // This methods add the list of files in an existing archive. // If a file with the same name already exists, it is added at the end of the // archive, the first one is still present. // If the archive does not exist, it is created. // Parameters : // $p_filelist : An array containing file or directory names, or // a string containing one filename or one directory name, or // a string containing a list of filenames and/or directory // names separated by spaces. // $p_add_dir : A path to add before the real path of the archived file, // in order to have it memorized in the archive. // $p_remove_dir : A path to remove from the real path of the file to archive, // in order to have a shorter path memorized in the archive. // When $p_add_dir and $p_remove_dir are set, $p_remove_dir // is removed first, before $p_add_dir is added. // Options : // PCLZIP_OPT_ADD_PATH : // PCLZIP_OPT_REMOVE_PATH : // PCLZIP_OPT_REMOVE_ALL_PATH : // PCLZIP_OPT_COMMENT : // PCLZIP_OPT_ADD_COMMENT : // PCLZIP_OPT_PREPEND_COMMENT : // PCLZIP_CB_PRE_ADD : // PCLZIP_CB_POST_ADD : // Return Values : // 0 on failure, // The list of the added files, with a status of the add action. // (see PclZip::listContent() for list entry format) // -------------------------------------------------------------------------------- function add($p_filelist) { $v_result=1; // ----- Reset the error handler $this->privErrorReset(); // ----- Set default values $v_options = array(); $v_options[PCLZIP_OPT_NO_COMPRESSION] = FALSE; // ----- Look for variable options arguments $v_size = func_num_args(); // ----- Look for arguments if ($v_size > 1) { // ----- Get the arguments $v_arg_list = func_get_args(); // ----- Remove form the options list the first argument array_shift($v_arg_list); $v_size--; // ----- Look for first arg if ((is_integer($v_arg_list[0])) && ($v_arg_list[0] > 77000)) { // ----- Parse the options $v_result = $this->privParseOptions($v_arg_list, $v_size, $v_options, array (PCLZIP_OPT_REMOVE_PATH => 'optional', PCLZIP_OPT_REMOVE_ALL_PATH => 'optional', PCLZIP_OPT_ADD_PATH => 'optional', PCLZIP_CB_PRE_ADD => 'optional', PCLZIP_CB_POST_ADD => 'optional', PCLZIP_OPT_NO_COMPRESSION => 'optional', PCLZIP_OPT_COMMENT => 'optional', PCLZIP_OPT_ADD_COMMENT => 'optional', PCLZIP_OPT_PREPEND_COMMENT => 'optional', PCLZIP_OPT_TEMP_FILE_THRESHOLD => 'optional', PCLZIP_OPT_TEMP_FILE_ON => 'optional', PCLZIP_OPT_TEMP_FILE_OFF => 'optional' //, PCLZIP_OPT_CRYPT => 'optional' )); if ($v_result != 1) { return 0; } } // ----- Look for 2 args // Here we need to support the first historic synopsis of the // method. else { // ----- Get the first argument $v_options[PCLZIP_OPT_ADD_PATH] = $v_add_path = $v_arg_list[0]; // ----- Look for the optional second argument if ($v_size == 2) { $v_options[PCLZIP_OPT_REMOVE_PATH] = $v_arg_list[1]; } else if ($v_size > 2) { // ----- Error log PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid number / type of arguments"); // ----- Return return 0; } } } // ----- Look for default option values $this->privOptionDefaultThreshold($v_options); // ----- Init $v_string_list = array(); $v_att_list = array(); $v_filedescr_list = array(); $p_result_list = array(); // ----- Look if the $p_filelist is really an array if (is_array($p_filelist)) { // ----- Look if the first element is also an array // This will mean that this is a file description entry if (isset($p_filelist[0]) && is_array($p_filelist[0])) { $v_att_list = $p_filelist; } // ----- The list is a list of string names else { $v_string_list = $p_filelist; } } // ----- Look if the $p_filelist is a string else if (is_string($p_filelist)) { // ----- Create a list from the string $v_string_list = explode(PCLZIP_SEPARATOR, $p_filelist); } // ----- Invalid variable type for $p_filelist else { PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid variable type '".gettype($p_filelist)."' for p_filelist"); return 0; } // ----- Reformat the string list if (sizeof($v_string_list) != 0) { foreach ($v_string_list as $v_string) { $v_att_list[][PCLZIP_ATT_FILE_NAME] = $v_string; } } // ----- For each file in the list check the attributes $v_supported_attributes = array ( PCLZIP_ATT_FILE_NAME => 'mandatory' ,PCLZIP_ATT_FILE_NEW_SHORT_NAME => 'optional' ,PCLZIP_ATT_FILE_NEW_FULL_NAME => 'optional' ,PCLZIP_ATT_FILE_MTIME => 'optional' ,PCLZIP_ATT_FILE_CONTENT => 'optional' ,PCLZIP_ATT_FILE_COMMENT => 'optional' ); foreach ($v_att_list as $v_entry) { $v_result = $this->privFileDescrParseAtt($v_entry, $v_filedescr_list[], $v_options, $v_supported_attributes); if ($v_result != 1) { return 0; } } // ----- Expand the filelist (expand directories) $v_result = $this->privFileDescrExpand($v_filedescr_list, $v_options); if ($v_result != 1) { return 0; } // ----- Call the create fct $v_result = $this->privAdd($v_filedescr_list, $p_result_list, $v_options); if ($v_result != 1) { return 0; } // ----- Return return $p_result_list; } // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- // Function : listContent() // Description : // This public method, gives the list of the files and directories, with their // properties. // The properties of each entries in the list are (used also in other functions) : // filename : Name of the file. For a create or add action it is the filename // given by the user. For an extract function it is the filename // of the extracted file. // stored_filename : Name of the file / directory stored in the archive. // size : Size of the stored file. // compressed_size : Size of the file's data compressed in the archive // (without the headers overhead) // mtime : Last known modification date of the file (UNIX timestamp) // comment : Comment associated with the file // folder : true | false // index : index of the file in the archive // status : status of the action (depending of the action) : // Values are : // ok : OK ! // filtered : the file / dir is not extracted (filtered by user) // already_a_directory : the file can not be extracted because a // directory with the same name already exists // write_protected : the file can not be extracted because a file // with the same name already exists and is // write protected // newer_exist : the file was not extracted because a newer file exists // path_creation_fail : the file is not extracted because the folder // does not exist and can not be created // write_error : the file was not extracted because there was an // error while writing the file // read_error : the file was not extracted because there was an error // while reading the file // invalid_header : the file was not extracted because of an archive // format error (bad file header) // Note that each time a method can continue operating when there // is an action error on a file, the error is only logged in the file status. // Return Values : // 0 on an unrecoverable failure, // The list of the files in the archive. // -------------------------------------------------------------------------------- function listContent() { $v_result=1; // ----- Reset the error handler $this->privErrorReset(); // ----- Check archive if (!$this->privCheckFormat()) { return(0); } // ----- Call the extracting fct $p_list = array(); if (($v_result = $this->privList($p_list)) != 1) { unset($p_list); return(0); } // ----- Return return $p_list; } // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- // Function : // extract($p_path="./", $p_remove_path="") // extract([$p_option, $p_option_value, ...]) // Description : // This method supports two synopsis. The first one is historical. // This method extract all the files / directories from the archive to the // folder indicated in $p_path. // If you want to ignore the 'root' part of path of the memorized files // you can indicate this in the optional $p_remove_path parameter. // By default, if a newer file with the same name already exists, the // file is not extracted. // // If both PCLZIP_OPT_PATH and PCLZIP_OPT_ADD_PATH options // are used, the path indicated in PCLZIP_OPT_ADD_PATH is append // at the end of the path value of PCLZIP_OPT_PATH. // Parameters : // $p_path : Path where the files and directories are to be extracted // $p_remove_path : First part ('root' part) of the memorized path // (if any similar) to remove while extracting. // Options : // PCLZIP_OPT_PATH : // PCLZIP_OPT_ADD_PATH : // PCLZIP_OPT_REMOVE_PATH : // PCLZIP_OPT_REMOVE_ALL_PATH : // PCLZIP_CB_PRE_EXTRACT : // PCLZIP_CB_POST_EXTRACT : // Return Values : // 0 or a negative value on failure, // The list of the extracted files, with a status of the action. // (see PclZip::listContent() for list entry format) // -------------------------------------------------------------------------------- function extract() { $v_result=1; // ----- Reset the error handler $this->privErrorReset(); // ----- Check archive if (!$this->privCheckFormat()) { return(0); } // ----- Set default values $v_options = array(); // $v_path = "./"; $v_path = ''; $v_remove_path = ""; $v_remove_all_path = false; // ----- Look for variable options arguments $v_size = func_num_args(); // ----- Default values for option $v_options[PCLZIP_OPT_EXTRACT_AS_STRING] = FALSE; // ----- Look for arguments if ($v_size > 0) { // ----- Get the arguments $v_arg_list = func_get_args(); // ----- Look for first arg if ((is_integer($v_arg_list[0])) && ($v_arg_list[0] > 77000)) { // ----- Parse the options $v_result = $this->privParseOptions($v_arg_list, $v_size, $v_options, array (PCLZIP_OPT_PATH => 'optional', PCLZIP_OPT_REMOVE_PATH => 'optional', PCLZIP_OPT_REMOVE_ALL_PATH => 'optional', PCLZIP_OPT_ADD_PATH => 'optional', PCLZIP_CB_PRE_EXTRACT => 'optional', PCLZIP_CB_POST_EXTRACT => 'optional', PCLZIP_OPT_SET_CHMOD => 'optional', PCLZIP_OPT_BY_NAME => 'optional', PCLZIP_OPT_BY_EREG => 'optional', PCLZIP_OPT_BY_PREG => 'optional', PCLZIP_OPT_BY_INDEX => 'optional', PCLZIP_OPT_EXTRACT_AS_STRING => 'optional', PCLZIP_OPT_EXTRACT_IN_OUTPUT => 'optional', PCLZIP_OPT_REPLACE_NEWER => 'optional' ,PCLZIP_OPT_STOP_ON_ERROR => 'optional' ,PCLZIP_OPT_EXTRACT_DIR_RESTRICTION => 'optional', PCLZIP_OPT_TEMP_FILE_THRESHOLD => 'optional', PCLZIP_OPT_TEMP_FILE_ON => 'optional', PCLZIP_OPT_TEMP_FILE_OFF => 'optional' )); if ($v_result != 1) { return 0; } // ----- Set the arguments if (isset($v_options[PCLZIP_OPT_PATH])) { $v_path = $v_options[PCLZIP_OPT_PATH]; } if (isset($v_options[PCLZIP_OPT_REMOVE_PATH])) { $v_remove_path = $v_options[PCLZIP_OPT_REMOVE_PATH]; } if (isset($v_options[PCLZIP_OPT_REMOVE_ALL_PATH])) { $v_remove_all_path = $v_options[PCLZIP_OPT_REMOVE_ALL_PATH]; } if (isset($v_options[PCLZIP_OPT_ADD_PATH])) { // ----- Check for '/' in last path char if ((strlen($v_path) > 0) && (substr($v_path, -1) != '/')) { $v_path .= '/'; } $v_path .= $v_options[PCLZIP_OPT_ADD_PATH]; } } // ----- Look for 2 args // Here we need to support the first historic synopsis of the // method. else { // ----- Get the first argument $v_path = $v_arg_list[0]; // ----- Look for the optional second argument if ($v_size == 2) { $v_remove_path = $v_arg_list[1]; } else if ($v_size > 2) { // ----- Error log PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid number / type of arguments"); // ----- Return return 0; } } } // ----- Look for default option values $this->privOptionDefaultThreshold($v_options); // ----- Trace // ----- Call the extracting fct $p_list = array(); $v_result = $this->privExtractByRule($p_list, $v_path, $v_remove_path, $v_remove_all_path, $v_options); if ($v_result < 1) { unset($p_list); return(0); } // ----- Return return $p_list; } // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- // Function : // extractByIndex($p_index, $p_path="./", $p_remove_path="") // extractByIndex($p_index, [$p_option, $p_option_value, ...]) // Description : // This method supports two synopsis. The first one is historical. // This method is doing a partial extract of the archive. // The extracted files or folders are identified by their index in the // archive (from 0 to n). // Note that if the index identify a folder, only the folder entry is // extracted, not all the files included in the archive. // Parameters : // $p_index : A single index (integer) or a string of indexes of files to // extract. The form of the string is "0,4-6,8-12" with only numbers // and '-' for range or ',' to separate ranges. No spaces or ';' // are allowed. // $p_path : Path where the files and directories are to be extracted // $p_remove_path : First part ('root' part) of the memorized path // (if any similar) to remove while extracting. // Options : // PCLZIP_OPT_PATH : // PCLZIP_OPT_ADD_PATH : // PCLZIP_OPT_REMOVE_PATH : // PCLZIP_OPT_REMOVE_ALL_PATH : // PCLZIP_OPT_EXTRACT_AS_STRING : The files are extracted as strings and // not as files. // The resulting content is in a new field 'content' in the file // structure. // This option must be used alone (any other options are ignored). // PCLZIP_CB_PRE_EXTRACT : // PCLZIP_CB_POST_EXTRACT : // Return Values : // 0 on failure, // The list of the extracted files, with a status of the action. // (see PclZip::listContent() for list entry format) // -------------------------------------------------------------------------------- //function extractByIndex($p_index, options...) function extractByIndex($p_index) { $v_result=1; // ----- Reset the error handler $this->privErrorReset(); // ----- Check archive if (!$this->privCheckFormat()) { return(0); } // ----- Set default values $v_options = array(); // $v_path = "./"; $v_path = ''; $v_remove_path = ""; $v_remove_all_path = false; // ----- Look for variable options arguments $v_size = func_num_args(); // ----- Default values for option $v_options[PCLZIP_OPT_EXTRACT_AS_STRING] = FALSE; // ----- Look for arguments if ($v_size > 1) { // ----- Get the arguments $v_arg_list = func_get_args(); // ----- Remove form the options list the first argument array_shift($v_arg_list); $v_size--; // ----- Look for first arg if ((is_integer($v_arg_list[0])) && ($v_arg_list[0] > 77000)) { // ----- Parse the options $v_result = $this->privParseOptions($v_arg_list, $v_size, $v_options, array (PCLZIP_OPT_PATH => 'optional', PCLZIP_OPT_REMOVE_PATH => 'optional', PCLZIP_OPT_REMOVE_ALL_PATH => 'optional', PCLZIP_OPT_EXTRACT_AS_STRING => 'optional', PCLZIP_OPT_ADD_PATH => 'optional', PCLZIP_CB_PRE_EXTRACT => 'optional', PCLZIP_CB_POST_EXTRACT => 'optional', PCLZIP_OPT_SET_CHMOD => 'optional', PCLZIP_OPT_REPLACE_NEWER => 'optional' ,PCLZIP_OPT_STOP_ON_ERROR => 'optional' ,PCLZIP_OPT_EXTRACT_DIR_RESTRICTION => 'optional', PCLZIP_OPT_TEMP_FILE_THRESHOLD => 'optional', PCLZIP_OPT_TEMP_FILE_ON => 'optional', PCLZIP_OPT_TEMP_FILE_OFF => 'optional' )); if ($v_result != 1) { return 0; } // ----- Set the arguments if (isset($v_options[PCLZIP_OPT_PATH])) { $v_path = $v_options[PCLZIP_OPT_PATH]; } if (isset($v_options[PCLZIP_OPT_REMOVE_PATH])) { $v_remove_path = $v_options[PCLZIP_OPT_REMOVE_PATH]; } if (isset($v_options[PCLZIP_OPT_REMOVE_ALL_PATH])) { $v_remove_all_path = $v_options[PCLZIP_OPT_REMOVE_ALL_PATH]; } if (isset($v_options[PCLZIP_OPT_ADD_PATH])) { // ----- Check for '/' in last path char if ((strlen($v_path) > 0) && (substr($v_path, -1) != '/')) { $v_path .= '/'; } $v_path .= $v_options[PCLZIP_OPT_ADD_PATH]; } if (!isset($v_options[PCLZIP_OPT_EXTRACT_AS_STRING])) { $v_options[PCLZIP_OPT_EXTRACT_AS_STRING] = FALSE; } else { } } // ----- Look for 2 args // Here we need to support the first historic synopsis of the // method. else { // ----- Get the first argument $v_path = $v_arg_list[0]; // ----- Look for the optional second argument if ($v_size == 2) { $v_remove_path = $v_arg_list[1]; } else if ($v_size > 2) { // ----- Error log PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid number / type of arguments"); // ----- Return return 0; } } } // ----- Trace // ----- Trick // Here I want to reuse extractByRule(), so I need to parse the $p_index // with privParseOptions() $v_arg_trick = array (PCLZIP_OPT_BY_INDEX, $p_index); $v_options_trick = array(); $v_result = $this->privParseOptions($v_arg_trick, sizeof($v_arg_trick), $v_options_trick, array (PCLZIP_OPT_BY_INDEX => 'optional' )); if ($v_result != 1) { return 0; } $v_options[PCLZIP_OPT_BY_INDEX] = $v_options_trick[PCLZIP_OPT_BY_INDEX]; // ----- Look for default option values $this->privOptionDefaultThreshold($v_options); // ----- Call the extracting fct if (($v_result = $this->privExtractByRule($p_list, $v_path, $v_remove_path, $v_remove_all_path, $v_options)) < 1) { return(0); } // ----- Return return $p_list; } // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- // Function : // delete([$p_option, $p_option_value, ...]) // Description : // This method removes files from the archive. // If no parameters are given, then all the archive is emptied. // Parameters : // None or optional arguments. // Options : // PCLZIP_OPT_BY_INDEX : // PCLZIP_OPT_BY_NAME : // PCLZIP_OPT_BY_EREG : // PCLZIP_OPT_BY_PREG : // Return Values : // 0 on failure, // The list of the files which are still present in the archive. // (see PclZip::listContent() for list entry format) // -------------------------------------------------------------------------------- function delete() { $v_result=1; // ----- Reset the error handler $this->privErrorReset(); // ----- Check archive if (!$this->privCheckFormat()) { return(0); } // ----- Set default values $v_options = array(); // ----- Look for variable options arguments $v_size = func_num_args(); // ----- Look for arguments if ($v_size > 0) { // ----- Get the arguments $v_arg_list = func_get_args(); // ----- Parse the options $v_result = $this->privParseOptions($v_arg_list, $v_size, $v_options, array (PCLZIP_OPT_BY_NAME => 'optional', PCLZIP_OPT_BY_EREG => 'optional', PCLZIP_OPT_BY_PREG => 'optional', PCLZIP_OPT_BY_INDEX => 'optional' )); if ($v_result != 1) { return 0; } } // ----- Magic quotes trick $this->privDisableMagicQuotes(); // ----- Call the delete fct $v_list = array(); if (($v_result = $this->privDeleteByRule($v_list, $v_options)) != 1) { $this->privSwapBackMagicQuotes(); unset($v_list); return(0); } // ----- Magic quotes trick $this->privSwapBackMagicQuotes(); // ----- Return return $v_list; } // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- // Function : deleteByIndex() // Description : // ***** Deprecated ***** // delete(PCLZIP_OPT_BY_INDEX, $p_index) should be preferred. // -------------------------------------------------------------------------------- function deleteByIndex($p_index) { $p_list = $this->delete(PCLZIP_OPT_BY_INDEX, $p_index); // ----- Return return $p_list; } // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- // Function : properties() // Description : // This method gives the properties of the archive. // The properties are : // nb : Number of files in the archive // comment : Comment associated with the archive file // status : not_exist, ok // Parameters : // None // Return Values : // 0 on failure, // An array with the archive properties. // -------------------------------------------------------------------------------- function properties() { // ----- Reset the error handler $this->privErrorReset(); // ----- Magic quotes trick $this->privDisableMagicQuotes(); // ----- Check archive if (!$this->privCheckFormat()) { $this->privSwapBackMagicQuotes(); return(0); } // ----- Default properties $v_prop = array(); $v_prop['comment'] = ''; $v_prop['nb'] = 0; $v_prop['status'] = 'not_exist'; // ----- Look if file exists if (@is_file($this->zipname)) { // ----- Open the zip file if (($this->zip_fd = @fopen($this->zipname, 'rb')) == 0) { $this->privSwapBackMagicQuotes(); // ----- Error log PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open archive \''.$this->zipname.'\' in binary read mode'); // ----- Return return 0; } // ----- Read the central directory information $v_central_dir = array(); if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1) { $this->privSwapBackMagicQuotes(); return 0; } // ----- Close the zip file $this->privCloseFd(); // ----- Set the user attributes $v_prop['comment'] = $v_central_dir['comment']; $v_prop['nb'] = $v_central_dir['entries']; $v_prop['status'] = 'ok'; } // ----- Magic quotes trick $this->privSwapBackMagicQuotes(); // ----- Return return $v_prop; } // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- // Function : duplicate() // Description : // This method creates an archive by copying the content of an other one. If // the archive already exist, it is replaced by the new one without any warning. // Parameters : // $p_archive : The filename of a valid archive, or // a valid PclZip object. // Return Values : // 1 on success. // 0 or a negative value on error (error code). // -------------------------------------------------------------------------------- function duplicate($p_archive) { $v_result = 1; // ----- Reset the error handler $this->privErrorReset(); // ----- Look if the $p_archive is an instantiated PclZip object if ($p_archive instanceof pclzip) { // ----- Duplicate the archive $v_result = $this->privDuplicate($p_archive->zipname); } // ----- Look if the $p_archive is a string (so a filename) else if (is_string($p_archive)) { // ----- Check that $p_archive is a valid zip file // TBC : Should also check the archive format if (!is_file($p_archive)) { // ----- Error log PclZip::privErrorLog(PCLZIP_ERR_MISSING_FILE, "No file with filename '".$p_archive."'"); $v_result = PCLZIP_ERR_MISSING_FILE; } else { // ----- Duplicate the archive $v_result = $this->privDuplicate($p_archive); } } // ----- Invalid variable else { // ----- Error log PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid variable type p_archive_to_add"); $v_result = PCLZIP_ERR_INVALID_PARAMETER; } // ----- Return return $v_result; } // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- // Function : merge() // Description : // This method merge the $p_archive_to_add archive at the end of the current // one ($this). // If the archive ($this) does not exist, the merge becomes a duplicate. // If the $p_archive_to_add archive does not exist, the merge is a success. // Parameters : // $p_archive_to_add : It can be directly the filename of a valid zip archive, // or a PclZip object archive. // Return Values : // 1 on success, // 0 or negative values on error (see below). // -------------------------------------------------------------------------------- function merge($p_archive_to_add) { $v_result = 1; // ----- Reset the error handler $this->privErrorReset(); // ----- Check archive if (!$this->privCheckFormat()) { return(0); } // ----- Look if the $p_archive_to_add is an instantiated PclZip object if ($p_archive_to_add instanceof pclzip) { // ----- Merge the archive $v_result = $this->privMerge($p_archive_to_add); } // ----- Look if the $p_archive_to_add is a string (so a filename) else if (is_string($p_archive_to_add)) { // ----- Create a temporary archive $v_object_archive = new PclZip($p_archive_to_add); // ----- Merge the archive $v_result = $this->privMerge($v_object_archive); } // ----- Invalid variable else { // ----- Error log PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid variable type p_archive_to_add"); $v_result = PCLZIP_ERR_INVALID_PARAMETER; } // ----- Return return $v_result; } // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- // Function : errorCode() // Description : // Parameters : // -------------------------------------------------------------------------------- function errorCode() { if (PCLZIP_ERROR_EXTERNAL == 1) { return(PclErrorCode()); } else { return($this->error_code); } } // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- // Function : errorName() // Description : // Parameters : // -------------------------------------------------------------------------------- function errorName($p_with_code=false) { $v_name = array ( PCLZIP_ERR_NO_ERROR => 'PCLZIP_ERR_NO_ERROR', PCLZIP_ERR_WRITE_OPEN_FAIL => 'PCLZIP_ERR_WRITE_OPEN_FAIL', PCLZIP_ERR_READ_OPEN_FAIL => 'PCLZIP_ERR_READ_OPEN_FAIL', PCLZIP_ERR_INVALID_PARAMETER => 'PCLZIP_ERR_INVALID_PARAMETER', PCLZIP_ERR_MISSING_FILE => 'PCLZIP_ERR_MISSING_FILE', PCLZIP_ERR_FILENAME_TOO_LONG => 'PCLZIP_ERR_FILENAME_TOO_LONG', PCLZIP_ERR_INVALID_ZIP => 'PCLZIP_ERR_INVALID_ZIP', PCLZIP_ERR_BAD_EXTRACTED_FILE => 'PCLZIP_ERR_BAD_EXTRACTED_FILE', PCLZIP_ERR_DIR_CREATE_FAIL => 'PCLZIP_ERR_DIR_CREATE_FAIL', PCLZIP_ERR_BAD_EXTENSION => 'PCLZIP_ERR_BAD_EXTENSION', PCLZIP_ERR_BAD_FORMAT => 'PCLZIP_ERR_BAD_FORMAT', PCLZIP_ERR_DELETE_FILE_FAIL => 'PCLZIP_ERR_DELETE_FILE_FAIL', PCLZIP_ERR_RENAME_FILE_FAIL => 'PCLZIP_ERR_RENAME_FILE_FAIL', PCLZIP_ERR_BAD_CHECKSUM => 'PCLZIP_ERR_BAD_CHECKSUM', PCLZIP_ERR_INVALID_ARCHIVE_ZIP => 'PCLZIP_ERR_INVALID_ARCHIVE_ZIP', PCLZIP_ERR_MISSING_OPTION_VALUE => 'PCLZIP_ERR_MISSING_OPTION_VALUE', PCLZIP_ERR_INVALID_OPTION_VALUE => 'PCLZIP_ERR_INVALID_OPTION_VALUE', PCLZIP_ERR_UNSUPPORTED_COMPRESSION => 'PCLZIP_ERR_UNSUPPORTED_COMPRESSION', PCLZIP_ERR_UNSUPPORTED_ENCRYPTION => 'PCLZIP_ERR_UNSUPPORTED_ENCRYPTION' ,PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE => 'PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE' ,PCLZIP_ERR_DIRECTORY_RESTRICTION => 'PCLZIP_ERR_DIRECTORY_RESTRICTION' ); if (isset($v_name[$this->error_code])) { $v_value = $v_name[$this->error_code]; } else { $v_value = 'NoName'; } if ($p_with_code) { return($v_value.' ('.$this->error_code.')'); } else { return($v_value); } } // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- // Function : errorInfo() // Description : // Parameters : // -------------------------------------------------------------------------------- function errorInfo($p_full=false) { if (PCLZIP_ERROR_EXTERNAL == 1) { return(PclErrorString()); } else { if ($p_full) { return($this->errorName(true)." : ".$this->error_string); } else { return($this->error_string." [code ".$this->error_code."]"); } } } // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- // ***** UNDER THIS LINE ARE DEFINED PRIVATE INTERNAL FUNCTIONS ***** // ***** ***** // ***** THESES FUNCTIONS MUST NOT BE USED DIRECTLY ***** // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- // Function : privCheckFormat() // Description : // This method check that the archive exists and is a valid zip archive. // Several level of check exists. (futur) // Parameters : // $p_level : Level of check. Default 0. // 0 : Check the first bytes (magic codes) (default value)) // 1 : 0 + Check the central directory (futur) // 2 : 1 + Check each file header (futur) // Return Values : // true on success, // false on error, the error code is set. // -------------------------------------------------------------------------------- function privCheckFormat($p_level=0) { $v_result = true; // ----- Reset the file system cache clearstatcache(); // ----- Reset the error handler $this->privErrorReset(); // ----- Look if the file exits if (!is_file($this->zipname)) { // ----- Error log PclZip::privErrorLog(PCLZIP_ERR_MISSING_FILE, "Missing archive file '".$this->zipname."'"); return(false); } // ----- Check that the file is readable if (!is_readable($this->zipname)) { // ----- Error log PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, "Unable to read archive '".$this->zipname."'"); return(false); } // ----- Check the magic code // TBC // ----- Check the central header // TBC // ----- Check each file header // TBC // ----- Return return $v_result; } // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- // Function : privParseOptions() // Description : // This internal methods reads the variable list of arguments ($p_options_list, // $p_size) and generate an array with the options and values ($v_result_list). // $v_requested_options contains the options that can be present and those that // must be present. // $v_requested_options is an array, with the option value as key, and 'optional', // or 'mandatory' as value. // Parameters : // See above. // Return Values : // 1 on success. // 0 on failure. // -------------------------------------------------------------------------------- function privParseOptions(&$p_options_list, $p_size, &$v_result_list, $v_requested_options=false) { $v_result=1; // ----- Read the options $i=0; while ($i<$p_size) { // ----- Check if the option is supported if (!isset($v_requested_options[$p_options_list[$i]])) { // ----- Error log PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid optional parameter '".$p_options_list[$i]."' for this method"); // ----- Return return PclZip::errorCode(); } // ----- Look for next option switch ($p_options_list[$i]) { // ----- Look for options that request a path value case PCLZIP_OPT_PATH : case PCLZIP_OPT_REMOVE_PATH : case PCLZIP_OPT_ADD_PATH : // ----- Check the number of parameters if (($i+1) >= $p_size) { // ----- Error log PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); // ----- Return return PclZip::errorCode(); } // ----- Get the value $v_result_list[$p_options_list[$i]] = PclZipUtilTranslateWinPath($p_options_list[$i+1], FALSE); $i++; break; case PCLZIP_OPT_TEMP_FILE_THRESHOLD : // ----- Check the number of parameters if (($i+1) >= $p_size) { PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); return PclZip::errorCode(); } // ----- Check for incompatible options if (isset($v_result_list[PCLZIP_OPT_TEMP_FILE_OFF])) { PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Option '".PclZipUtilOptionText($p_options_list[$i])."' can not be used with option 'PCLZIP_OPT_TEMP_FILE_OFF'"); return PclZip::errorCode(); } // ----- Check the value $v_value = $p_options_list[$i+1]; if ((!is_integer($v_value)) || ($v_value<0)) { PclZip::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, "Integer expected for option '".PclZipUtilOptionText($p_options_list[$i])."'"); return PclZip::errorCode(); } // ----- Get the value (and convert it in bytes) $v_result_list[$p_options_list[$i]] = $v_value*1048576; $i++; break; case PCLZIP_OPT_TEMP_FILE_ON : // ----- Check for incompatible options if (isset($v_result_list[PCLZIP_OPT_TEMP_FILE_OFF])) { PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Option '".PclZipUtilOptionText($p_options_list[$i])."' can not be used with option 'PCLZIP_OPT_TEMP_FILE_OFF'"); return PclZip::errorCode(); } $v_result_list[$p_options_list[$i]] = true; break; case PCLZIP_OPT_TEMP_FILE_OFF : // ----- Check for incompatible options if (isset($v_result_list[PCLZIP_OPT_TEMP_FILE_ON])) { PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Option '".PclZipUtilOptionText($p_options_list[$i])."' can not be used with option 'PCLZIP_OPT_TEMP_FILE_ON'"); return PclZip::errorCode(); } // ----- Check for incompatible options if (isset($v_result_list[PCLZIP_OPT_TEMP_FILE_THRESHOLD])) { PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Option '".PclZipUtilOptionText($p_options_list[$i])."' can not be used with option 'PCLZIP_OPT_TEMP_FILE_THRESHOLD'"); return PclZip::errorCode(); } $v_result_list[$p_options_list[$i]] = true; break; case PCLZIP_OPT_EXTRACT_DIR_RESTRICTION : // ----- Check the number of parameters if (($i+1) >= $p_size) { // ----- Error log PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); // ----- Return return PclZip::errorCode(); } // ----- Get the value if ( is_string($p_options_list[$i+1]) && ($p_options_list[$i+1] != '')) { $v_result_list[$p_options_list[$i]] = PclZipUtilTranslateWinPath($p_options_list[$i+1], FALSE); $i++; } else { } break; // ----- Look for options that request an array of string for value case PCLZIP_OPT_BY_NAME : // ----- Check the number of parameters if (($i+1) >= $p_size) { // ----- Error log PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); // ----- Return return PclZip::errorCode(); } // ----- Get the value if (is_string($p_options_list[$i+1])) { $v_result_list[$p_options_list[$i]][0] = $p_options_list[$i+1]; } else if (is_array($p_options_list[$i+1])) { $v_result_list[$p_options_list[$i]] = $p_options_list[$i+1]; } else { // ----- Error log PclZip::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, "Wrong parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); // ----- Return return PclZip::errorCode(); } $i++; break; // ----- Look for options that request an EREG or PREG expression case PCLZIP_OPT_BY_EREG : // ereg() is deprecated starting with PHP 5.3. Move PCLZIP_OPT_BY_EREG // to PCLZIP_OPT_BY_PREG $p_options_list[$i] = PCLZIP_OPT_BY_PREG; case PCLZIP_OPT_BY_PREG : //case PCLZIP_OPT_CRYPT : // ----- Check the number of parameters if (($i+1) >= $p_size) { // ----- Error log PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); // ----- Return return PclZip::errorCode(); } // ----- Get the value if (is_string($p_options_list[$i+1])) { $v_result_list[$p_options_list[$i]] = $p_options_list[$i+1]; } else { // ----- Error log PclZip::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, "Wrong parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); // ----- Return return PclZip::errorCode(); } $i++; break; // ----- Look for options that takes a string case PCLZIP_OPT_COMMENT : case PCLZIP_OPT_ADD_COMMENT : case PCLZIP_OPT_PREPEND_COMMENT : // ----- Check the number of parameters if (($i+1) >= $p_size) { // ----- Error log PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '" .PclZipUtilOptionText($p_options_list[$i]) ."'"); // ----- Return return PclZip::errorCode(); } // ----- Get the value if (is_string($p_options_list[$i+1])) { $v_result_list[$p_options_list[$i]] = $p_options_list[$i+1]; } else { // ----- Error log PclZip::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, "Wrong parameter value for option '" .PclZipUtilOptionText($p_options_list[$i]) ."'"); // ----- Return return PclZip::errorCode(); } $i++; break; // ----- Look for options that request an array of index case PCLZIP_OPT_BY_INDEX : // ----- Check the number of parameters if (($i+1) >= $p_size) { // ----- Error log PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); // ----- Return return PclZip::errorCode(); } // ----- Get the value $v_work_list = array(); if (is_string($p_options_list[$i+1])) { // ----- Remove spaces $p_options_list[$i+1] = strtr($p_options_list[$i+1], ' ', ''); // ----- Parse items $v_work_list = explode(",", $p_options_list[$i+1]); } else if (is_integer($p_options_list[$i+1])) { $v_work_list[0] = $p_options_list[$i+1].'-'.$p_options_list[$i+1]; } else if (is_array($p_options_list[$i+1])) { $v_work_list = $p_options_list[$i+1]; } else { // ----- Error log PclZip::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, "Value must be integer, string or array for option '".PclZipUtilOptionText($p_options_list[$i])."'"); // ----- Return return PclZip::errorCode(); } // ----- Reduce the index list // each index item in the list must be a couple with a start and // an end value : [0,3], [5-5], [8-10], ... // ----- Check the format of each item $v_sort_flag=false; $v_sort_value=0; for ($j=0; $j<sizeof($v_work_list); $j++) { // ----- Explode the item $v_item_list = explode("-", $v_work_list[$j]); $v_size_item_list = sizeof($v_item_list); // ----- TBC : Here we might check that each item is a // real integer ... // ----- Look for single value if ($v_size_item_list == 1) { // ----- Set the option value $v_result_list[$p_options_list[$i]][$j]['start'] = $v_item_list[0]; $v_result_list[$p_options_list[$i]][$j]['end'] = $v_item_list[0]; } elseif ($v_size_item_list == 2) { // ----- Set the option value $v_result_list[$p_options_list[$i]][$j]['start'] = $v_item_list[0]; $v_result_list[$p_options_list[$i]][$j]['end'] = $v_item_list[1]; } else { // ----- Error log PclZip::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, "Too many values in index range for option '".PclZipUtilOptionText($p_options_list[$i])."'"); // ----- Return return PclZip::errorCode(); } // ----- Look for list sort if ($v_result_list[$p_options_list[$i]][$j]['start'] < $v_sort_value) { $v_sort_flag=true; // ----- TBC : An automatic sort should be written ... // ----- Error log PclZip::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, "Invalid order of index range for option '".PclZipUtilOptionText($p_options_list[$i])."'"); // ----- Return return PclZip::errorCode(); } $v_sort_value = $v_result_list[$p_options_list[$i]][$j]['start']; } // ----- Sort the items if ($v_sort_flag) { // TBC : To Be Completed } // ----- Next option $i++; break; // ----- Look for options that request no value case PCLZIP_OPT_REMOVE_ALL_PATH : case PCLZIP_OPT_EXTRACT_AS_STRING : case PCLZIP_OPT_NO_COMPRESSION : case PCLZIP_OPT_EXTRACT_IN_OUTPUT : case PCLZIP_OPT_REPLACE_NEWER : case PCLZIP_OPT_STOP_ON_ERROR : $v_result_list[$p_options_list[$i]] = true; break; // ----- Look for options that request an octal value case PCLZIP_OPT_SET_CHMOD : // ----- Check the number of parameters if (($i+1) >= $p_size) { // ----- Error log PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); // ----- Return return PclZip::errorCode(); } // ----- Get the value $v_result_list[$p_options_list[$i]] = $p_options_list[$i+1]; $i++; break; // ----- Look for options that request a call-back case PCLZIP_CB_PRE_EXTRACT : case PCLZIP_CB_POST_EXTRACT : case PCLZIP_CB_PRE_ADD : case PCLZIP_CB_POST_ADD : /* for futur use case PCLZIP_CB_PRE_DELETE : case PCLZIP_CB_POST_DELETE : case PCLZIP_CB_PRE_LIST : case PCLZIP_CB_POST_LIST : */ // ----- Check the number of parameters if (($i+1) >= $p_size) { // ----- Error log PclZip::privErrorLog(PCLZIP_ERR_MISSING_OPTION_VALUE, "Missing parameter value for option '".PclZipUtilOptionText($p_options_list[$i])."'"); // ----- Return return PclZip::errorCode(); } // ----- Get the value $v_function_name = $p_options_list[$i+1]; // ----- Check that the value is a valid existing function if (!function_exists($v_function_name)) { // ----- Error log PclZip::privErrorLog(PCLZIP_ERR_INVALID_OPTION_VALUE, "Function '".$v_function_name."()' is not an existing function for option '".PclZipUtilOptionText($p_options_list[$i])."'"); // ----- Return return PclZip::errorCode(); } // ----- Set the attribute $v_result_list[$p_options_list[$i]] = $v_function_name; $i++; break; default : // ----- Error log PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Unknown parameter '" .$p_options_list[$i]."'"); // ----- Return return PclZip::errorCode(); } // ----- Next options $i++; } // ----- Look for mandatory options if ($v_requested_options !== false) { for ($key=reset($v_requested_options); $key=key($v_requested_options); $key=next($v_requested_options)) { // ----- Look for mandatory option if ($v_requested_options[$key] == 'mandatory') { // ----- Look if present if (!isset($v_result_list[$key])) { // ----- Error log PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Missing mandatory parameter ".PclZipUtilOptionText($key)."(".$key.")"); // ----- Return return PclZip::errorCode(); } } } } // ----- Look for default values if (!isset($v_result_list[PCLZIP_OPT_TEMP_FILE_THRESHOLD])) { } // ----- Return return $v_result; } // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- // Function : privOptionDefaultThreshold() // Description : // Parameters : // Return Values : // -------------------------------------------------------------------------------- function privOptionDefaultThreshold(&$p_options) { $v_result=1; if (isset($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD]) || isset($p_options[PCLZIP_OPT_TEMP_FILE_OFF])) { return $v_result; } // ----- Get 'memory_limit' configuration value $v_memory_limit = ini_get('memory_limit'); $v_memory_limit = trim($v_memory_limit); $v_memory_limit_int = (int) $v_memory_limit; $last = strtolower(substr($v_memory_limit, -1)); if($last == 'g') //$v_memory_limit_int = $v_memory_limit_int*1024*1024*1024; $v_memory_limit_int = $v_memory_limit_int*1073741824; if($last == 'm') //$v_memory_limit_int = $v_memory_limit_int*1024*1024; $v_memory_limit_int = $v_memory_limit_int*1048576; if($last == 'k') $v_memory_limit_int = $v_memory_limit_int*1024; $p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD] = floor($v_memory_limit_int*PCLZIP_TEMPORARY_FILE_RATIO); // ----- Confidence check : No threshold if value lower than 1M if ($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD] < 1048576) { unset($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD]); } // ----- Return return $v_result; } // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- // Function : privFileDescrParseAtt() // Description : // Parameters : // Return Values : // 1 on success. // 0 on failure. // -------------------------------------------------------------------------------- function privFileDescrParseAtt(&$p_file_list, &$p_filedescr, $v_options, $v_requested_options=false) { $v_result=1; // ----- For each file in the list check the attributes foreach ($p_file_list as $v_key => $v_value) { // ----- Check if the option is supported if (!isset($v_requested_options[$v_key])) { // ----- Error log PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid file attribute '".$v_key."' for this file"); // ----- Return return PclZip::errorCode(); } // ----- Look for attribute switch ($v_key) { case PCLZIP_ATT_FILE_NAME : if (!is_string($v_value)) { PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid type ".gettype($v_value).". String expected for attribute '".PclZipUtilOptionText($v_key)."'"); return PclZip::errorCode(); } $p_filedescr['filename'] = PclZipUtilPathReduction($v_value); if ($p_filedescr['filename'] == '') { PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid empty filename for attribute '".PclZipUtilOptionText($v_key)."'"); return PclZip::errorCode(); } break; case PCLZIP_ATT_FILE_NEW_SHORT_NAME : if (!is_string($v_value)) { PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid type ".gettype($v_value).". String expected for attribute '".PclZipUtilOptionText($v_key)."'"); return PclZip::errorCode(); } $p_filedescr['new_short_name'] = PclZipUtilPathReduction($v_value); if ($p_filedescr['new_short_name'] == '') { PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid empty short filename for attribute '".PclZipUtilOptionText($v_key)."'"); return PclZip::errorCode(); } break; case PCLZIP_ATT_FILE_NEW_FULL_NAME : if (!is_string($v_value)) { PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid type ".gettype($v_value).". String expected for attribute '".PclZipUtilOptionText($v_key)."'"); return PclZip::errorCode(); } $p_filedescr['new_full_name'] = PclZipUtilPathReduction($v_value); if ($p_filedescr['new_full_name'] == '') { PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid empty full filename for attribute '".PclZipUtilOptionText($v_key)."'"); return PclZip::errorCode(); } break; // ----- Look for options that takes a string case PCLZIP_ATT_FILE_COMMENT : if (!is_string($v_value)) { PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid type ".gettype($v_value).". String expected for attribute '".PclZipUtilOptionText($v_key)."'"); return PclZip::errorCode(); } $p_filedescr['comment'] = $v_value; break; case PCLZIP_ATT_FILE_MTIME : if (!is_integer($v_value)) { PclZip::privErrorLog(PCLZIP_ERR_INVALID_ATTRIBUTE_VALUE, "Invalid type ".gettype($v_value).". Integer expected for attribute '".PclZipUtilOptionText($v_key)."'"); return PclZip::errorCode(); } $p_filedescr['mtime'] = $v_value; break; case PCLZIP_ATT_FILE_CONTENT : $p_filedescr['content'] = $v_value; break; default : // ----- Error log PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Unknown parameter '".$v_key."'"); // ----- Return return PclZip::errorCode(); } // ----- Look for mandatory options if ($v_requested_options !== false) { for ($key=reset($v_requested_options); $key=key($v_requested_options); $key=next($v_requested_options)) { // ----- Look for mandatory option if ($v_requested_options[$key] == 'mandatory') { // ----- Look if present if (!isset($p_file_list[$key])) { PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Missing mandatory parameter ".PclZipUtilOptionText($key)."(".$key.")"); return PclZip::errorCode(); } } } } // end foreach } // ----- Return return $v_result; } // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- // Function : privFileDescrExpand() // Description : // This method look for each item of the list to see if its a file, a folder // or a string to be added as file. For any other type of files (link, other) // just ignore the item. // Then prepare the information that will be stored for that file. // When its a folder, expand the folder with all the files that are in that // folder (recursively). // Parameters : // Return Values : // 1 on success. // 0 on failure. // -------------------------------------------------------------------------------- function privFileDescrExpand(&$p_filedescr_list, &$p_options) { $v_result=1; // ----- Create a result list $v_result_list = array(); // ----- Look each entry for ($i=0; $i<sizeof($p_filedescr_list); $i++) { // ----- Get filedescr $v_descr = $p_filedescr_list[$i]; // ----- Reduce the filename $v_descr['filename'] = PclZipUtilTranslateWinPath($v_descr['filename'], false); $v_descr['filename'] = PclZipUtilPathReduction($v_descr['filename']); // ----- Look for real file or folder if (file_exists($v_descr['filename'])) { if (@is_file($v_descr['filename'])) { $v_descr['type'] = 'file'; } else if (@is_dir($v_descr['filename'])) { $v_descr['type'] = 'folder'; } else if (@is_link($v_descr['filename'])) { // skip continue; } else { // skip continue; } } // ----- Look for string added as file else if (isset($v_descr['content'])) { $v_descr['type'] = 'virtual_file'; } // ----- Missing file else { // ----- Error log PclZip::privErrorLog(PCLZIP_ERR_MISSING_FILE, "File '".$v_descr['filename']."' does not exist"); // ----- Return return PclZip::errorCode(); } // ----- Calculate the stored filename $this->privCalculateStoredFilename($v_descr, $p_options); // ----- Add the descriptor in result list $v_result_list[sizeof($v_result_list)] = $v_descr; // ----- Look for folder if ($v_descr['type'] == 'folder') { // ----- List of items in folder $v_dirlist_descr = array(); $v_dirlist_nb = 0; if ($v_folder_handler = @opendir($v_descr['filename'])) { while (($v_item_handler = @readdir($v_folder_handler)) !== false) { // ----- Skip '.' and '..' if (($v_item_handler == '.') || ($v_item_handler == '..')) { continue; } // ----- Compose the full filename $v_dirlist_descr[$v_dirlist_nb]['filename'] = $v_descr['filename'].'/'.$v_item_handler; // ----- Look for different stored filename // Because the name of the folder was changed, the name of the // files/sub-folders also change if (($v_descr['stored_filename'] != $v_descr['filename']) && (!isset($p_options[PCLZIP_OPT_REMOVE_ALL_PATH]))) { if ($v_descr['stored_filename'] != '') { $v_dirlist_descr[$v_dirlist_nb]['new_full_name'] = $v_descr['stored_filename'].'/'.$v_item_handler; } else { $v_dirlist_descr[$v_dirlist_nb]['new_full_name'] = $v_item_handler; } } $v_dirlist_nb++; } @closedir($v_folder_handler); } else { // TBC : unable to open folder in read mode } // ----- Expand each element of the list if ($v_dirlist_nb != 0) { // ----- Expand if (($v_result = $this->privFileDescrExpand($v_dirlist_descr, $p_options)) != 1) { return $v_result; } // ----- Concat the resulting list $v_result_list = array_merge($v_result_list, $v_dirlist_descr); } else { } // ----- Free local array unset($v_dirlist_descr); } } // ----- Get the result list $p_filedescr_list = $v_result_list; // ----- Return return $v_result; } // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- // Function : privCreate() // Description : // Parameters : // Return Values : // -------------------------------------------------------------------------------- function privCreate($p_filedescr_list, &$p_result_list, &$p_options) { $v_result=1; $v_list_detail = array(); // ----- Magic quotes trick $this->privDisableMagicQuotes(); // ----- Open the file in write mode if (($v_result = $this->privOpenFd('wb')) != 1) { // ----- Return return $v_result; } // ----- Add the list of files $v_result = $this->privAddList($p_filedescr_list, $p_result_list, $p_options); // ----- Close $this->privCloseFd(); // ----- Magic quotes trick $this->privSwapBackMagicQuotes(); // ----- Return return $v_result; } // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- // Function : privAdd() // Description : // Parameters : // Return Values : // -------------------------------------------------------------------------------- function privAdd($p_filedescr_list, &$p_result_list, &$p_options) { $v_result=1; $v_list_detail = array(); // ----- Look if the archive exists or is empty if ((!is_file($this->zipname)) || (filesize($this->zipname) == 0)) { // ----- Do a create $v_result = $this->privCreate($p_filedescr_list, $p_result_list, $p_options); // ----- Return return $v_result; } // ----- Magic quotes trick $this->privDisableMagicQuotes(); // ----- Open the zip file if (($v_result=$this->privOpenFd('rb')) != 1) { // ----- Magic quotes trick $this->privSwapBackMagicQuotes(); // ----- Return return $v_result; } // ----- Read the central directory information $v_central_dir = array(); if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1) { $this->privCloseFd(); $this->privSwapBackMagicQuotes(); return $v_result; } // ----- Go to beginning of File @rewind($this->zip_fd); // ----- Creates a temporary file $v_zip_temp_name = PCLZIP_TEMPORARY_DIR.uniqid('pclzip-').'.tmp'; // ----- Open the temporary file in write mode if (($v_zip_temp_fd = @fopen($v_zip_temp_name, 'wb')) == 0) { $this->privCloseFd(); $this->privSwapBackMagicQuotes(); PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open temporary file \''.$v_zip_temp_name.'\' in binary write mode'); // ----- Return return PclZip::errorCode(); } // ----- Copy the files from the archive to the temporary file // TBC : Here I should better append the file and go back to erase the central dir $v_size = $v_central_dir['offset']; while ($v_size != 0) { $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); $v_buffer = fread($this->zip_fd, $v_read_size); @fwrite($v_zip_temp_fd, $v_buffer, $v_read_size); $v_size -= $v_read_size; } // ----- Swap the file descriptor // Here is a trick : I swap the temporary fd with the zip fd, in order to use // the following methods on the temporary fil and not the real archive $v_swap = $this->zip_fd; $this->zip_fd = $v_zip_temp_fd; $v_zip_temp_fd = $v_swap; // ----- Add the files $v_header_list = array(); if (($v_result = $this->privAddFileList($p_filedescr_list, $v_header_list, $p_options)) != 1) { fclose($v_zip_temp_fd); $this->privCloseFd(); @unlink($v_zip_temp_name); $this->privSwapBackMagicQuotes(); // ----- Return return $v_result; } // ----- Store the offset of the central dir $v_offset = @ftell($this->zip_fd); // ----- Copy the block of file headers from the old archive $v_size = $v_central_dir['size']; while ($v_size != 0) { $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); $v_buffer = @fread($v_zip_temp_fd, $v_read_size); @fwrite($this->zip_fd, $v_buffer, $v_read_size); $v_size -= $v_read_size; } // ----- Create the Central Dir files header for ($i=0, $v_count=0; $i<sizeof($v_header_list); $i++) { // ----- Create the file header if ($v_header_list[$i]['status'] == 'ok') { if (($v_result = $this->privWriteCentralFileHeader($v_header_list[$i])) != 1) { fclose($v_zip_temp_fd); $this->privCloseFd(); @unlink($v_zip_temp_name); $this->privSwapBackMagicQuotes(); // ----- Return return $v_result; } $v_count++; } // ----- Transform the header to a 'usable' info $this->privConvertHeader2FileInfo($v_header_list[$i], $p_result_list[$i]); } // ----- Zip file comment $v_comment = $v_central_dir['comment']; if (isset($p_options[PCLZIP_OPT_COMMENT])) { $v_comment = $p_options[PCLZIP_OPT_COMMENT]; } if (isset($p_options[PCLZIP_OPT_ADD_COMMENT])) { $v_comment = $v_comment.$p_options[PCLZIP_OPT_ADD_COMMENT]; } if (isset($p_options[PCLZIP_OPT_PREPEND_COMMENT])) { $v_comment = $p_options[PCLZIP_OPT_PREPEND_COMMENT].$v_comment; } // ----- Calculate the size of the central header $v_size = @ftell($this->zip_fd)-$v_offset; // ----- Create the central dir footer if (($v_result = $this->privWriteCentralHeader($v_count+$v_central_dir['entries'], $v_size, $v_offset, $v_comment)) != 1) { // ----- Reset the file list unset($v_header_list); $this->privSwapBackMagicQuotes(); // ----- Return return $v_result; } // ----- Swap back the file descriptor $v_swap = $this->zip_fd; $this->zip_fd = $v_zip_temp_fd; $v_zip_temp_fd = $v_swap; // ----- Close $this->privCloseFd(); // ----- Close the temporary file @fclose($v_zip_temp_fd); // ----- Magic quotes trick $this->privSwapBackMagicQuotes(); // ----- Delete the zip file // TBC : I should test the result ... @unlink($this->zipname); // ----- Rename the temporary file // TBC : I should test the result ... //@rename($v_zip_temp_name, $this->zipname); PclZipUtilRename($v_zip_temp_name, $this->zipname); // ----- Return return $v_result; } // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- // Function : privOpenFd() // Description : // Parameters : // -------------------------------------------------------------------------------- function privOpenFd($p_mode) { $v_result=1; // ----- Look if already open if ($this->zip_fd != 0) { // ----- Error log PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Zip file \''.$this->zipname.'\' already open'); // ----- Return return PclZip::errorCode(); } // ----- Open the zip file if (($this->zip_fd = @fopen($this->zipname, $p_mode)) == 0) { // ----- Error log PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open archive \''.$this->zipname.'\' in '.$p_mode.' mode'); // ----- Return return PclZip::errorCode(); } // ----- Return return $v_result; } // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- // Function : privCloseFd() // Description : // Parameters : // -------------------------------------------------------------------------------- function privCloseFd() { $v_result=1; if ($this->zip_fd != 0) @fclose($this->zip_fd); $this->zip_fd = 0; // ----- Return return $v_result; } // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- // Function : privAddList() // Description : // $p_add_dir and $p_remove_dir will give the ability to memorize a path which is // different from the real path of the file. This is useful if you want to have PclTar // running in any directory, and memorize relative path from an other directory. // Parameters : // $p_list : An array containing the file or directory names to add in the tar // $p_result_list : list of added files with their properties (specially the status field) // $p_add_dir : Path to add in the filename path archived // $p_remove_dir : Path to remove in the filename path archived // Return Values : // -------------------------------------------------------------------------------- // function privAddList($p_list, &$p_result_list, $p_add_dir, $p_remove_dir, $p_remove_all_dir, &$p_options) function privAddList($p_filedescr_list, &$p_result_list, &$p_options) { $v_result=1; // ----- Add the files $v_header_list = array(); if (($v_result = $this->privAddFileList($p_filedescr_list, $v_header_list, $p_options)) != 1) { // ----- Return return $v_result; } // ----- Store the offset of the central dir $v_offset = @ftell($this->zip_fd); // ----- Create the Central Dir files header for ($i=0,$v_count=0; $i<sizeof($v_header_list); $i++) { // ----- Create the file header if ($v_header_list[$i]['status'] == 'ok') { if (($v_result = $this->privWriteCentralFileHeader($v_header_list[$i])) != 1) { // ----- Return return $v_result; } $v_count++; } // ----- Transform the header to a 'usable' info $this->privConvertHeader2FileInfo($v_header_list[$i], $p_result_list[$i]); } // ----- Zip file comment $v_comment = ''; if (isset($p_options[PCLZIP_OPT_COMMENT])) { $v_comment = $p_options[PCLZIP_OPT_COMMENT]; } // ----- Calculate the size of the central header $v_size = @ftell($this->zip_fd)-$v_offset; // ----- Create the central dir footer if (($v_result = $this->privWriteCentralHeader($v_count, $v_size, $v_offset, $v_comment)) != 1) { // ----- Reset the file list unset($v_header_list); // ----- Return return $v_result; } // ----- Return return $v_result; } // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- // Function : privAddFileList() // Description : // Parameters : // $p_filedescr_list : An array containing the file description // or directory names to add in the zip // $p_result_list : list of added files with their properties (specially the status field) // Return Values : // -------------------------------------------------------------------------------- function privAddFileList($p_filedescr_list, &$p_result_list, &$p_options) { $v_result=1; $v_header = array(); // ----- Recuperate the current number of elt in list $v_nb = sizeof($p_result_list); // ----- Loop on the files for ($j=0; ($j<sizeof($p_filedescr_list)) && ($v_result==1); $j++) { // ----- Format the filename $p_filedescr_list[$j]['filename'] = PclZipUtilTranslateWinPath($p_filedescr_list[$j]['filename'], false); // ----- Skip empty file names // TBC : Can this be possible ? not checked in DescrParseAtt ? if ($p_filedescr_list[$j]['filename'] == "") { continue; } // ----- Check the filename if ( ($p_filedescr_list[$j]['type'] != 'virtual_file') && (!file_exists($p_filedescr_list[$j]['filename']))) { PclZip::privErrorLog(PCLZIP_ERR_MISSING_FILE, "File '".$p_filedescr_list[$j]['filename']."' does not exist"); return PclZip::errorCode(); } // ----- Look if it is a file or a dir with no all path remove option // or a dir with all its path removed // if ( (is_file($p_filedescr_list[$j]['filename'])) // || ( is_dir($p_filedescr_list[$j]['filename']) if ( ($p_filedescr_list[$j]['type'] == 'file') || ($p_filedescr_list[$j]['type'] == 'virtual_file') || ( ($p_filedescr_list[$j]['type'] == 'folder') && ( !isset($p_options[PCLZIP_OPT_REMOVE_ALL_PATH]) || !$p_options[PCLZIP_OPT_REMOVE_ALL_PATH])) ) { // ----- Add the file $v_result = $this->privAddFile($p_filedescr_list[$j], $v_header, $p_options); if ($v_result != 1) { return $v_result; } // ----- Store the file infos $p_result_list[$v_nb++] = $v_header; } } // ----- Return return $v_result; } // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- // Function : privAddFile() // Description : // Parameters : // Return Values : // -------------------------------------------------------------------------------- function privAddFile($p_filedescr, &$p_header, &$p_options) { $v_result=1; // ----- Working variable $p_filename = $p_filedescr['filename']; // TBC : Already done in the fileAtt check ... ? if ($p_filename == "") { // ----- Error log PclZip::privErrorLog(PCLZIP_ERR_INVALID_PARAMETER, "Invalid file list parameter (invalid or empty list)"); // ----- Return return PclZip::errorCode(); } // ----- Look for a stored different filename /* TBC : Removed if (isset($p_filedescr['stored_filename'])) { $v_stored_filename = $p_filedescr['stored_filename']; } else { $v_stored_filename = $p_filedescr['stored_filename']; } */ // ----- Set the file properties clearstatcache(); $p_header['version'] = 20; $p_header['version_extracted'] = 10; $p_header['flag'] = 0; $p_header['compression'] = 0; $p_header['crc'] = 0; $p_header['compressed_size'] = 0; $p_header['filename_len'] = strlen($p_filename); $p_header['extra_len'] = 0; $p_header['disk'] = 0; $p_header['internal'] = 0; $p_header['offset'] = 0; $p_header['filename'] = $p_filename; // TBC : Removed $p_header['stored_filename'] = $v_stored_filename; $p_header['stored_filename'] = $p_filedescr['stored_filename']; $p_header['extra'] = ''; $p_header['status'] = 'ok'; $p_header['index'] = -1; // ----- Look for regular file if ($p_filedescr['type']=='file') { $p_header['external'] = 0x00000000; $p_header['size'] = filesize($p_filename); } // ----- Look for regular folder else if ($p_filedescr['type']=='folder') { $p_header['external'] = 0x00000010; $p_header['mtime'] = filemtime($p_filename); $p_header['size'] = filesize($p_filename); } // ----- Look for virtual file else if ($p_filedescr['type'] == 'virtual_file') { $p_header['external'] = 0x00000000; $p_header['size'] = strlen($p_filedescr['content']); } // ----- Look for filetime if (isset($p_filedescr['mtime'])) { $p_header['mtime'] = $p_filedescr['mtime']; } else if ($p_filedescr['type'] == 'virtual_file') { $p_header['mtime'] = time(); } else { $p_header['mtime'] = filemtime($p_filename); } // ------ Look for file comment if (isset($p_filedescr['comment'])) { $p_header['comment_len'] = strlen($p_filedescr['comment']); $p_header['comment'] = $p_filedescr['comment']; } else { $p_header['comment_len'] = 0; $p_header['comment'] = ''; } // ----- Look for pre-add callback if (isset($p_options[PCLZIP_CB_PRE_ADD])) { // ----- Generate a local information $v_local_header = array(); $this->privConvertHeader2FileInfo($p_header, $v_local_header); // ----- Call the callback // Here I do not use call_user_func() because I need to send a reference to the // header. $v_result = $p_options[PCLZIP_CB_PRE_ADD](PCLZIP_CB_PRE_ADD, $v_local_header); if ($v_result == 0) { // ----- Change the file status $p_header['status'] = "skipped"; $v_result = 1; } // ----- Update the information // Only some fields can be modified if ($p_header['stored_filename'] != $v_local_header['stored_filename']) { $p_header['stored_filename'] = PclZipUtilPathReduction($v_local_header['stored_filename']); } } // ----- Look for empty stored filename if ($p_header['stored_filename'] == "") { $p_header['status'] = "filtered"; } // ----- Check the path length if (strlen($p_header['stored_filename']) > 0xFF) { $p_header['status'] = 'filename_too_long'; } // ----- Look if no error, or file not skipped if ($p_header['status'] == 'ok') { // ----- Look for a file if ($p_filedescr['type'] == 'file') { // ----- Look for using temporary file to zip if ( (!isset($p_options[PCLZIP_OPT_TEMP_FILE_OFF])) && (isset($p_options[PCLZIP_OPT_TEMP_FILE_ON]) || (isset($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD]) && ($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD] <= $p_header['size'])) ) ) { $v_result = $this->privAddFileUsingTempFile($p_filedescr, $p_header, $p_options); if ($v_result < PCLZIP_ERR_NO_ERROR) { return $v_result; } } // ----- Use "in memory" zip algo else { // ----- Open the source file if (($v_file = @fopen($p_filename, "rb")) == 0) { PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, "Unable to open file '$p_filename' in binary read mode"); return PclZip::errorCode(); } // ----- Read the file content if ($p_header['size'] > 0) { $v_content = @fread($v_file, $p_header['size']); } else { $v_content = ''; } // ----- Close the file @fclose($v_file); // ----- Calculate the CRC $p_header['crc'] = @crc32($v_content); // ----- Look for no compression if ($p_options[PCLZIP_OPT_NO_COMPRESSION]) { // ----- Set header parameters $p_header['compressed_size'] = $p_header['size']; $p_header['compression'] = 0; } // ----- Look for normal compression else { // ----- Compress the content $v_content = @gzdeflate($v_content); // ----- Set header parameters $p_header['compressed_size'] = strlen($v_content); $p_header['compression'] = 8; } // ----- Call the header generation if (($v_result = $this->privWriteFileHeader($p_header)) != 1) { @fclose($v_file); return $v_result; } // ----- Write the compressed (or not) content @fwrite($this->zip_fd, $v_content, $p_header['compressed_size']); } } // ----- Look for a virtual file (a file from string) else if ($p_filedescr['type'] == 'virtual_file') { $v_content = $p_filedescr['content']; // ----- Calculate the CRC $p_header['crc'] = @crc32($v_content); // ----- Look for no compression if ($p_options[PCLZIP_OPT_NO_COMPRESSION]) { // ----- Set header parameters $p_header['compressed_size'] = $p_header['size']; $p_header['compression'] = 0; } // ----- Look for normal compression else { // ----- Compress the content $v_content = @gzdeflate($v_content); // ----- Set header parameters $p_header['compressed_size'] = strlen($v_content); $p_header['compression'] = 8; } // ----- Call the header generation if (($v_result = $this->privWriteFileHeader($p_header)) != 1) { @fclose($v_file); return $v_result; } // ----- Write the compressed (or not) content @fwrite($this->zip_fd, $v_content, $p_header['compressed_size']); } // ----- Look for a directory else if ($p_filedescr['type'] == 'folder') { // ----- Look for directory last '/' if (@substr($p_header['stored_filename'], -1) != '/') { $p_header['stored_filename'] .= '/'; } // ----- Set the file properties $p_header['size'] = 0; //$p_header['external'] = 0x41FF0010; // Value for a folder : to be checked $p_header['external'] = 0x00000010; // Value for a folder : to be checked // ----- Call the header generation if (($v_result = $this->privWriteFileHeader($p_header)) != 1) { return $v_result; } } } // ----- Look for post-add callback if (isset($p_options[PCLZIP_CB_POST_ADD])) { // ----- Generate a local information $v_local_header = array(); $this->privConvertHeader2FileInfo($p_header, $v_local_header); // ----- Call the callback // Here I do not use call_user_func() because I need to send a reference to the // header. $v_result = $p_options[PCLZIP_CB_POST_ADD](PCLZIP_CB_POST_ADD, $v_local_header); if ($v_result == 0) { // ----- Ignored $v_result = 1; } // ----- Update the information // Nothing can be modified } // ----- Return return $v_result; } // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- // Function : privAddFileUsingTempFile() // Description : // Parameters : // Return Values : // -------------------------------------------------------------------------------- function privAddFileUsingTempFile($p_filedescr, &$p_header, &$p_options) { $v_result=PCLZIP_ERR_NO_ERROR; // ----- Working variable $p_filename = $p_filedescr['filename']; // ----- Open the source file if (($v_file = @fopen($p_filename, "rb")) == 0) { PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, "Unable to open file '$p_filename' in binary read mode"); return PclZip::errorCode(); } // ----- Creates a compressed temporary file $v_gzip_temp_name = PCLZIP_TEMPORARY_DIR.uniqid('pclzip-').'.gz'; if (($v_file_compressed = @gzopen($v_gzip_temp_name, "wb")) == 0) { fclose($v_file); PclZip::privErrorLog(PCLZIP_ERR_WRITE_OPEN_FAIL, 'Unable to open temporary file \''.$v_gzip_temp_name.'\' in binary write mode'); return PclZip::errorCode(); } // ----- Read the file by PCLZIP_READ_BLOCK_SIZE octets blocks $v_size = filesize($p_filename); while ($v_size != 0) { $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); $v_buffer = @fread($v_file, $v_read_size); //$v_binary_data = pack('a'.$v_read_size, $v_buffer); @gzputs($v_file_compressed, $v_buffer, $v_read_size); $v_size -= $v_read_size; } // ----- Close the file @fclose($v_file); @gzclose($v_file_compressed); // ----- Check the minimum file size if (filesize($v_gzip_temp_name) < 18) { PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'gzip temporary file \''.$v_gzip_temp_name.'\' has invalid filesize - should be minimum 18 bytes'); return PclZip::errorCode(); } // ----- Extract the compressed attributes if (($v_file_compressed = @fopen($v_gzip_temp_name, "rb")) == 0) { PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open temporary file \''.$v_gzip_temp_name.'\' in binary read mode'); return PclZip::errorCode(); } // ----- Read the gzip file header $v_binary_data = @fread($v_file_compressed, 10); $v_data_header = unpack('a1id1/a1id2/a1cm/a1flag/Vmtime/a1xfl/a1os', $v_binary_data); // ----- Check some parameters $v_data_header['os'] = bin2hex($v_data_header['os']); // ----- Read the gzip file footer @fseek($v_file_compressed, filesize($v_gzip_temp_name)-8); $v_binary_data = @fread($v_file_compressed, 8); $v_data_footer = unpack('Vcrc/Vcompressed_size', $v_binary_data); // ----- Set the attributes $p_header['compression'] = ord($v_data_header['cm']); //$p_header['mtime'] = $v_data_header['mtime']; $p_header['crc'] = $v_data_footer['crc']; $p_header['compressed_size'] = filesize($v_gzip_temp_name)-18; // ----- Close the file @fclose($v_file_compressed); // ----- Call the header generation if (($v_result = $this->privWriteFileHeader($p_header)) != 1) { return $v_result; } // ----- Add the compressed data if (($v_file_compressed = @fopen($v_gzip_temp_name, "rb")) == 0) { PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open temporary file \''.$v_gzip_temp_name.'\' in binary read mode'); return PclZip::errorCode(); } // ----- Read the file by PCLZIP_READ_BLOCK_SIZE octets blocks fseek($v_file_compressed, 10); $v_size = $p_header['compressed_size']; while ($v_size != 0) { $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); $v_buffer = @fread($v_file_compressed, $v_read_size); //$v_binary_data = pack('a'.$v_read_size, $v_buffer); @fwrite($this->zip_fd, $v_buffer, $v_read_size); $v_size -= $v_read_size; } // ----- Close the file @fclose($v_file_compressed); // ----- Unlink the temporary file @unlink($v_gzip_temp_name); // ----- Return return $v_result; } // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- // Function : privCalculateStoredFilename() // Description : // Based on file descriptor properties and global options, this method // calculate the filename that will be stored in the archive. // Parameters : // Return Values : // -------------------------------------------------------------------------------- function privCalculateStoredFilename(&$p_filedescr, &$p_options) { $v_result=1; // ----- Working variables $p_filename = $p_filedescr['filename']; if (isset($p_options[PCLZIP_OPT_ADD_PATH])) { $p_add_dir = $p_options[PCLZIP_OPT_ADD_PATH]; } else { $p_add_dir = ''; } if (isset($p_options[PCLZIP_OPT_REMOVE_PATH])) { $p_remove_dir = $p_options[PCLZIP_OPT_REMOVE_PATH]; } else { $p_remove_dir = ''; } if (isset($p_options[PCLZIP_OPT_REMOVE_ALL_PATH])) { $p_remove_all_dir = $p_options[PCLZIP_OPT_REMOVE_ALL_PATH]; } else { $p_remove_all_dir = 0; } // ----- Look for full name change if (isset($p_filedescr['new_full_name'])) { // ----- Remove drive letter if any $v_stored_filename = PclZipUtilTranslateWinPath($p_filedescr['new_full_name']); } // ----- Look for path and/or short name change else { // ----- Look for short name change // Its when we change just the filename but not the path if (isset($p_filedescr['new_short_name'])) { $v_path_info = pathinfo($p_filename); $v_dir = ''; if ($v_path_info['dirname'] != '') { $v_dir = $v_path_info['dirname'].'/'; } $v_stored_filename = $v_dir.$p_filedescr['new_short_name']; } else { // ----- Calculate the stored filename $v_stored_filename = $p_filename; } // ----- Look for all path to remove if ($p_remove_all_dir) { $v_stored_filename = basename($p_filename); } // ----- Look for partial path remove else if ($p_remove_dir != "") { if (substr($p_remove_dir, -1) != '/') $p_remove_dir .= "/"; if ( (substr($p_filename, 0, 2) == "./") || (substr($p_remove_dir, 0, 2) == "./")) { if ( (substr($p_filename, 0, 2) == "./") && (substr($p_remove_dir, 0, 2) != "./")) { $p_remove_dir = "./".$p_remove_dir; } if ( (substr($p_filename, 0, 2) != "./") && (substr($p_remove_dir, 0, 2) == "./")) { $p_remove_dir = substr($p_remove_dir, 2); } } $v_compare = PclZipUtilPathInclusion($p_remove_dir, $v_stored_filename); if ($v_compare > 0) { if ($v_compare == 2) { $v_stored_filename = ""; } else { $v_stored_filename = substr($v_stored_filename, strlen($p_remove_dir)); } } } // ----- Remove drive letter if any $v_stored_filename = PclZipUtilTranslateWinPath($v_stored_filename); // ----- Look for path to add if ($p_add_dir != "") { if (substr($p_add_dir, -1) == "/") $v_stored_filename = $p_add_dir.$v_stored_filename; else $v_stored_filename = $p_add_dir."/".$v_stored_filename; } } // ----- Filename (reduce the path of stored name) $v_stored_filename = PclZipUtilPathReduction($v_stored_filename); $p_filedescr['stored_filename'] = $v_stored_filename; // ----- Return return $v_result; } // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- // Function : privWriteFileHeader() // Description : // Parameters : // Return Values : // -------------------------------------------------------------------------------- function privWriteFileHeader(&$p_header) { $v_result=1; // ----- Store the offset position of the file $p_header['offset'] = ftell($this->zip_fd); // ----- Transform UNIX mtime to DOS format mdate/mtime $v_date = getdate($p_header['mtime']); $v_mtime = ($v_date['hours']<<11) + ($v_date['minutes']<<5) + $v_date['seconds']/2; $v_mdate = (($v_date['year']-1980)<<9) + ($v_date['mon']<<5) + $v_date['mday']; // ----- Packed data $v_binary_data = pack("VvvvvvVVVvv", 0x04034b50, $p_header['version_extracted'], $p_header['flag'], $p_header['compression'], $v_mtime, $v_mdate, $p_header['crc'], $p_header['compressed_size'], $p_header['size'], strlen($p_header['stored_filename']), $p_header['extra_len']); // ----- Write the first 148 bytes of the header in the archive fputs($this->zip_fd, $v_binary_data, 30); // ----- Write the variable fields if (strlen($p_header['stored_filename']) != 0) { fputs($this->zip_fd, $p_header['stored_filename'], strlen($p_header['stored_filename'])); } if ($p_header['extra_len'] != 0) { fputs($this->zip_fd, $p_header['extra'], $p_header['extra_len']); } // ----- Return return $v_result; } // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- // Function : privWriteCentralFileHeader() // Description : // Parameters : // Return Values : // -------------------------------------------------------------------------------- function privWriteCentralFileHeader(&$p_header) { $v_result=1; // TBC //for(reset($p_header); $key = key($p_header); next($p_header)) { //} // ----- Transform UNIX mtime to DOS format mdate/mtime $v_date = getdate($p_header['mtime']); $v_mtime = ($v_date['hours']<<11) + ($v_date['minutes']<<5) + $v_date['seconds']/2; $v_mdate = (($v_date['year']-1980)<<9) + ($v_date['mon']<<5) + $v_date['mday']; // ----- Packed data $v_binary_data = pack("VvvvvvvVVVvvvvvVV", 0x02014b50, $p_header['version'], $p_header['version_extracted'], $p_header['flag'], $p_header['compression'], $v_mtime, $v_mdate, $p_header['crc'], $p_header['compressed_size'], $p_header['size'], strlen($p_header['stored_filename']), $p_header['extra_len'], $p_header['comment_len'], $p_header['disk'], $p_header['internal'], $p_header['external'], $p_header['offset']); // ----- Write the 42 bytes of the header in the zip file fputs($this->zip_fd, $v_binary_data, 46); // ----- Write the variable fields if (strlen($p_header['stored_filename']) != 0) { fputs($this->zip_fd, $p_header['stored_filename'], strlen($p_header['stored_filename'])); } if ($p_header['extra_len'] != 0) { fputs($this->zip_fd, $p_header['extra'], $p_header['extra_len']); } if ($p_header['comment_len'] != 0) { fputs($this->zip_fd, $p_header['comment'], $p_header['comment_len']); } // ----- Return return $v_result; } // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- // Function : privWriteCentralHeader() // Description : // Parameters : // Return Values : // -------------------------------------------------------------------------------- function privWriteCentralHeader($p_nb_entries, $p_size, $p_offset, $p_comment) { $v_result=1; // ----- Packed data $v_binary_data = pack("VvvvvVVv", 0x06054b50, 0, 0, $p_nb_entries, $p_nb_entries, $p_size, $p_offset, strlen($p_comment)); // ----- Write the 22 bytes of the header in the zip file fputs($this->zip_fd, $v_binary_data, 22); // ----- Write the variable fields if (strlen($p_comment) != 0) { fputs($this->zip_fd, $p_comment, strlen($p_comment)); } // ----- Return return $v_result; } // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- // Function : privList() // Description : // Parameters : // Return Values : // -------------------------------------------------------------------------------- function privList(&$p_list) { $v_result=1; // ----- Magic quotes trick $this->privDisableMagicQuotes(); // ----- Open the zip file if (($this->zip_fd = @fopen($this->zipname, 'rb')) == 0) { // ----- Magic quotes trick $this->privSwapBackMagicQuotes(); // ----- Error log PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open archive \''.$this->zipname.'\' in binary read mode'); // ----- Return return PclZip::errorCode(); } // ----- Read the central directory information $v_central_dir = array(); if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1) { $this->privSwapBackMagicQuotes(); return $v_result; } // ----- Go to beginning of Central Dir @rewind($this->zip_fd); if (@fseek($this->zip_fd, $v_central_dir['offset'])) { $this->privSwapBackMagicQuotes(); // ----- Error log PclZip::privErrorLog(PCLZIP_ERR_INVALID_ARCHIVE_ZIP, 'Invalid archive size'); // ----- Return return PclZip::errorCode(); } // ----- Read each entry for ($i=0; $i<$v_central_dir['entries']; $i++) { // ----- Read the file header if (($v_result = $this->privReadCentralFileHeader($v_header)) != 1) { $this->privSwapBackMagicQuotes(); return $v_result; } $v_header['index'] = $i; // ----- Get the only interesting attributes $this->privConvertHeader2FileInfo($v_header, $p_list[$i]); unset($v_header); } // ----- Close the zip file $this->privCloseFd(); // ----- Magic quotes trick $this->privSwapBackMagicQuotes(); // ----- Return return $v_result; } // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- // Function : privConvertHeader2FileInfo() // Description : // This function takes the file information from the central directory // entries and extract the interesting parameters that will be given back. // The resulting file infos are set in the array $p_info // $p_info['filename'] : Filename with full path. Given by user (add), // extracted in the filesystem (extract). // $p_info['stored_filename'] : Stored filename in the archive. // $p_info['size'] = Size of the file. // $p_info['compressed_size'] = Compressed size of the file. // $p_info['mtime'] = Last modification date of the file. // $p_info['comment'] = Comment associated with the file. // $p_info['folder'] = true/false : indicates if the entry is a folder or not. // $p_info['status'] = status of the action on the file. // $p_info['crc'] = CRC of the file content. // Parameters : // Return Values : // -------------------------------------------------------------------------------- function privConvertHeader2FileInfo($p_header, &$p_info) { $v_result=1; // ----- Get the interesting attributes $v_temp_path = PclZipUtilPathReduction($p_header['filename']); $p_info['filename'] = $v_temp_path; $v_temp_path = PclZipUtilPathReduction($p_header['stored_filename']); $p_info['stored_filename'] = $v_temp_path; $p_info['size'] = $p_header['size']; $p_info['compressed_size'] = $p_header['compressed_size']; $p_info['mtime'] = $p_header['mtime']; $p_info['comment'] = $p_header['comment']; $p_info['folder'] = (($p_header['external']&0x00000010)==0x00000010); $p_info['index'] = $p_header['index']; $p_info['status'] = $p_header['status']; $p_info['crc'] = $p_header['crc']; // ----- Return return $v_result; } // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- // Function : privExtractByRule() // Description : // Extract a file or directory depending of rules (by index, by name, ...) // Parameters : // $p_file_list : An array where will be placed the properties of each // extracted file // $p_path : Path to add while writing the extracted files // $p_remove_path : Path to remove (from the file memorized path) while writing the // extracted files. If the path does not match the file path, // the file is extracted with its memorized path. // $p_remove_path does not apply to 'list' mode. // $p_path and $p_remove_path are commulative. // Return Values : // 1 on success,0 or less on error (see error code list) // -------------------------------------------------------------------------------- function privExtractByRule(&$p_file_list, $p_path, $p_remove_path, $p_remove_all_path, &$p_options) { $v_result=1; // ----- Magic quotes trick $this->privDisableMagicQuotes(); // ----- Check the path if ( ($p_path == "") || ( (substr($p_path, 0, 1) != "/") && (substr($p_path, 0, 3) != "../") && (substr($p_path,1,2)!=":/"))) $p_path = "./".$p_path; // ----- Reduce the path last (and duplicated) '/' if (($p_path != "./") && ($p_path != "/")) { // ----- Look for the path end '/' while (substr($p_path, -1) == "/") { $p_path = substr($p_path, 0, strlen($p_path)-1); } } // ----- Look for path to remove format (should end by /) if (($p_remove_path != "") && (substr($p_remove_path, -1) != '/')) { $p_remove_path .= '/'; } $p_remove_path_size = strlen($p_remove_path); // ----- Open the zip file if (($v_result = $this->privOpenFd('rb')) != 1) { $this->privSwapBackMagicQuotes(); return $v_result; } // ----- Read the central directory information $v_central_dir = array(); if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1) { // ----- Close the zip file $this->privCloseFd(); $this->privSwapBackMagicQuotes(); return $v_result; } // ----- Start at beginning of Central Dir $v_pos_entry = $v_central_dir['offset']; // ----- Read each entry $j_start = 0; for ($i=0, $v_nb_extracted=0; $i<$v_central_dir['entries']; $i++) { // ----- Read next Central dir entry @rewind($this->zip_fd); if (@fseek($this->zip_fd, $v_pos_entry)) { // ----- Close the zip file $this->privCloseFd(); $this->privSwapBackMagicQuotes(); // ----- Error log PclZip::privErrorLog(PCLZIP_ERR_INVALID_ARCHIVE_ZIP, 'Invalid archive size'); // ----- Return return PclZip::errorCode(); } // ----- Read the file header $v_header = array(); if (($v_result = $this->privReadCentralFileHeader($v_header)) != 1) { // ----- Close the zip file $this->privCloseFd(); $this->privSwapBackMagicQuotes(); return $v_result; } // ----- Store the index $v_header['index'] = $i; // ----- Store the file position $v_pos_entry = ftell($this->zip_fd); // ----- Look for the specific extract rules $v_extract = false; // ----- Look for extract by name rule if ( (isset($p_options[PCLZIP_OPT_BY_NAME])) && ($p_options[PCLZIP_OPT_BY_NAME] != 0)) { // ----- Look if the filename is in the list for ($j=0; ($j<sizeof($p_options[PCLZIP_OPT_BY_NAME])) && (!$v_extract); $j++) { // ----- Look for a directory if (substr($p_options[PCLZIP_OPT_BY_NAME][$j], -1) == "/") { // ----- Look if the directory is in the filename path if ( (strlen($v_header['stored_filename']) > strlen($p_options[PCLZIP_OPT_BY_NAME][$j])) && (substr($v_header['stored_filename'], 0, strlen($p_options[PCLZIP_OPT_BY_NAME][$j])) == $p_options[PCLZIP_OPT_BY_NAME][$j])) { $v_extract = true; } } // ----- Look for a filename elseif ($v_header['stored_filename'] == $p_options[PCLZIP_OPT_BY_NAME][$j]) { $v_extract = true; } } } // ----- Look for extract by ereg rule // ereg() is deprecated with PHP 5.3 /* else if ( (isset($p_options[PCLZIP_OPT_BY_EREG])) && ($p_options[PCLZIP_OPT_BY_EREG] != "")) { if (ereg($p_options[PCLZIP_OPT_BY_EREG], $v_header['stored_filename'])) { $v_extract = true; } } */ // ----- Look for extract by preg rule else if ( (isset($p_options[PCLZIP_OPT_BY_PREG])) && ($p_options[PCLZIP_OPT_BY_PREG] != "")) { if (preg_match($p_options[PCLZIP_OPT_BY_PREG], $v_header['stored_filename'])) { $v_extract = true; } } // ----- Look for extract by index rule else if ( (isset($p_options[PCLZIP_OPT_BY_INDEX])) && ($p_options[PCLZIP_OPT_BY_INDEX] != 0)) { // ----- Look if the index is in the list for ($j=$j_start; ($j<sizeof($p_options[PCLZIP_OPT_BY_INDEX])) && (!$v_extract); $j++) { if (($i>=$p_options[PCLZIP_OPT_BY_INDEX][$j]['start']) && ($i<=$p_options[PCLZIP_OPT_BY_INDEX][$j]['end'])) { $v_extract = true; } if ($i>=$p_options[PCLZIP_OPT_BY_INDEX][$j]['end']) { $j_start = $j+1; } if ($p_options[PCLZIP_OPT_BY_INDEX][$j]['start']>$i) { break; } } } // ----- Look for no rule, which means extract all the archive else { $v_extract = true; } // ----- Check compression method if ( ($v_extract) && ( ($v_header['compression'] != 8) && ($v_header['compression'] != 0))) { $v_header['status'] = 'unsupported_compression'; // ----- Look for PCLZIP_OPT_STOP_ON_ERROR if ( (isset($p_options[PCLZIP_OPT_STOP_ON_ERROR])) && ($p_options[PCLZIP_OPT_STOP_ON_ERROR]===true)) { $this->privSwapBackMagicQuotes(); PclZip::privErrorLog(PCLZIP_ERR_UNSUPPORTED_COMPRESSION, "Filename '".$v_header['stored_filename']."' is " ."compressed by an unsupported compression " ."method (".$v_header['compression'].") "); return PclZip::errorCode(); } } // ----- Check encrypted files if (($v_extract) && (($v_header['flag'] & 1) == 1)) { $v_header['status'] = 'unsupported_encryption'; // ----- Look for PCLZIP_OPT_STOP_ON_ERROR if ( (isset($p_options[PCLZIP_OPT_STOP_ON_ERROR])) && ($p_options[PCLZIP_OPT_STOP_ON_ERROR]===true)) { $this->privSwapBackMagicQuotes(); PclZip::privErrorLog(PCLZIP_ERR_UNSUPPORTED_ENCRYPTION, "Unsupported encryption for " ." filename '".$v_header['stored_filename'] ."'"); return PclZip::errorCode(); } } // ----- Look for real extraction if (($v_extract) && ($v_header['status'] != 'ok')) { $v_result = $this->privConvertHeader2FileInfo($v_header, $p_file_list[$v_nb_extracted++]); if ($v_result != 1) { $this->privCloseFd(); $this->privSwapBackMagicQuotes(); return $v_result; } $v_extract = false; } // ----- Look for real extraction if ($v_extract) { // ----- Go to the file position @rewind($this->zip_fd); if (@fseek($this->zip_fd, $v_header['offset'])) { // ----- Close the zip file $this->privCloseFd(); $this->privSwapBackMagicQuotes(); // ----- Error log PclZip::privErrorLog(PCLZIP_ERR_INVALID_ARCHIVE_ZIP, 'Invalid archive size'); // ----- Return return PclZip::errorCode(); } // ----- Look for extraction as string if ($p_options[PCLZIP_OPT_EXTRACT_AS_STRING]) { $v_string = ''; // ----- Extracting the file $v_result1 = $this->privExtractFileAsString($v_header, $v_string, $p_options); if ($v_result1 < 1) { $this->privCloseFd(); $this->privSwapBackMagicQuotes(); return $v_result1; } // ----- Get the only interesting attributes if (($v_result = $this->privConvertHeader2FileInfo($v_header, $p_file_list[$v_nb_extracted])) != 1) { // ----- Close the zip file $this->privCloseFd(); $this->privSwapBackMagicQuotes(); return $v_result; } // ----- Set the file content $p_file_list[$v_nb_extracted]['content'] = $v_string; // ----- Next extracted file $v_nb_extracted++; // ----- Look for user callback abort if ($v_result1 == 2) { break; } } // ----- Look for extraction in standard output elseif ( (isset($p_options[PCLZIP_OPT_EXTRACT_IN_OUTPUT])) && ($p_options[PCLZIP_OPT_EXTRACT_IN_OUTPUT])) { // ----- Extracting the file in standard output $v_result1 = $this->privExtractFileInOutput($v_header, $p_options); if ($v_result1 < 1) { $this->privCloseFd(); $this->privSwapBackMagicQuotes(); return $v_result1; } // ----- Get the only interesting attributes if (($v_result = $this->privConvertHeader2FileInfo($v_header, $p_file_list[$v_nb_extracted++])) != 1) { $this->privCloseFd(); $this->privSwapBackMagicQuotes(); return $v_result; } // ----- Look for user callback abort if ($v_result1 == 2) { break; } } // ----- Look for normal extraction else { // ----- Extracting the file $v_result1 = $this->privExtractFile($v_header, $p_path, $p_remove_path, $p_remove_all_path, $p_options); if ($v_result1 < 1) { $this->privCloseFd(); $this->privSwapBackMagicQuotes(); return $v_result1; } // ----- Get the only interesting attributes if (($v_result = $this->privConvertHeader2FileInfo($v_header, $p_file_list[$v_nb_extracted++])) != 1) { // ----- Close the zip file $this->privCloseFd(); $this->privSwapBackMagicQuotes(); return $v_result; } // ----- Look for user callback abort if ($v_result1 == 2) { break; } } } } // ----- Close the zip file $this->privCloseFd(); $this->privSwapBackMagicQuotes(); // ----- Return return $v_result; } // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- // Function : privExtractFile() // Description : // Parameters : // Return Values : // // 1 : ... ? // PCLZIP_ERR_USER_ABORTED(2) : User ask for extraction stop in callback // -------------------------------------------------------------------------------- function privExtractFile(&$p_entry, $p_path, $p_remove_path, $p_remove_all_path, &$p_options) { $v_result=1; // ----- Read the file header if (($v_result = $this->privReadFileHeader($v_header)) != 1) { // ----- Return return $v_result; } // ----- Check that the file header is coherent with $p_entry info if ($this->privCheckFileHeaders($v_header, $p_entry) != 1) { // TBC } // ----- Look for all path to remove if ($p_remove_all_path == true) { // ----- Look for folder entry that not need to be extracted if (($p_entry['external']&0x00000010)==0x00000010) { $p_entry['status'] = "filtered"; return $v_result; } // ----- Get the basename of the path $p_entry['filename'] = basename($p_entry['filename']); } // ----- Look for path to remove else if ($p_remove_path != "") { if (PclZipUtilPathInclusion($p_remove_path, $p_entry['filename']) == 2) { // ----- Change the file status $p_entry['status'] = "filtered"; // ----- Return return $v_result; } $p_remove_path_size = strlen($p_remove_path); if (substr($p_entry['filename'], 0, $p_remove_path_size) == $p_remove_path) { // ----- Remove the path $p_entry['filename'] = substr($p_entry['filename'], $p_remove_path_size); } } // ----- Add the path if ($p_path != '') { $p_entry['filename'] = $p_path."/".$p_entry['filename']; } // ----- Check a base_dir_restriction if (isset($p_options[PCLZIP_OPT_EXTRACT_DIR_RESTRICTION])) { $v_inclusion = PclZipUtilPathInclusion($p_options[PCLZIP_OPT_EXTRACT_DIR_RESTRICTION], $p_entry['filename']); if ($v_inclusion == 0) { PclZip::privErrorLog(PCLZIP_ERR_DIRECTORY_RESTRICTION, "Filename '".$p_entry['filename']."' is " ."outside PCLZIP_OPT_EXTRACT_DIR_RESTRICTION"); return PclZip::errorCode(); } } // ----- Look for pre-extract callback if (isset($p_options[PCLZIP_CB_PRE_EXTRACT])) { // ----- Generate a local information $v_local_header = array(); $this->privConvertHeader2FileInfo($p_entry, $v_local_header); // ----- Call the callback // Here I do not use call_user_func() because I need to send a reference to the // header. $v_result = $p_options[PCLZIP_CB_PRE_EXTRACT](PCLZIP_CB_PRE_EXTRACT, $v_local_header); if ($v_result == 0) { // ----- Change the file status $p_entry['status'] = "skipped"; $v_result = 1; } // ----- Look for abort result if ($v_result == 2) { // ----- This status is internal and will be changed in 'skipped' $p_entry['status'] = "aborted"; $v_result = PCLZIP_ERR_USER_ABORTED; } // ----- Update the information // Only some fields can be modified $p_entry['filename'] = $v_local_header['filename']; } // ----- Look if extraction should be done if ($p_entry['status'] == 'ok') { // ----- Look for specific actions while the file exist if (file_exists($p_entry['filename'])) { // ----- Look if file is a directory if (is_dir($p_entry['filename'])) { // ----- Change the file status $p_entry['status'] = "already_a_directory"; // ----- Look for PCLZIP_OPT_STOP_ON_ERROR // For historical reason first PclZip implementation does not stop // when this kind of error occurs. if ( (isset($p_options[PCLZIP_OPT_STOP_ON_ERROR])) && ($p_options[PCLZIP_OPT_STOP_ON_ERROR]===true)) { PclZip::privErrorLog(PCLZIP_ERR_ALREADY_A_DIRECTORY, "Filename '".$p_entry['filename']."' is " ."already used by an existing directory"); return PclZip::errorCode(); } } // ----- Look if file is write protected else if (!is_writeable($p_entry['filename'])) { // ----- Change the file status $p_entry['status'] = "write_protected"; // ----- Look for PCLZIP_OPT_STOP_ON_ERROR // For historical reason first PclZip implementation does not stop // when this kind of error occurs. if ( (isset($p_options[PCLZIP_OPT_STOP_ON_ERROR])) && ($p_options[PCLZIP_OPT_STOP_ON_ERROR]===true)) { PclZip::privErrorLog(PCLZIP_ERR_WRITE_OPEN_FAIL, "Filename '".$p_entry['filename']."' exists " ."and is write protected"); return PclZip::errorCode(); } } // ----- Look if the extracted file is older else if (filemtime($p_entry['filename']) > $p_entry['mtime']) { // ----- Change the file status if ( (isset($p_options[PCLZIP_OPT_REPLACE_NEWER])) && ($p_options[PCLZIP_OPT_REPLACE_NEWER]===true)) { } else { $p_entry['status'] = "newer_exist"; // ----- Look for PCLZIP_OPT_STOP_ON_ERROR // For historical reason first PclZip implementation does not stop // when this kind of error occurs. if ( (isset($p_options[PCLZIP_OPT_STOP_ON_ERROR])) && ($p_options[PCLZIP_OPT_STOP_ON_ERROR]===true)) { PclZip::privErrorLog(PCLZIP_ERR_WRITE_OPEN_FAIL, "Newer version of '".$p_entry['filename']."' exists " ."and option PCLZIP_OPT_REPLACE_NEWER is not selected"); return PclZip::errorCode(); } } } else { } } // ----- Check the directory availability and create it if necessary else { if ((($p_entry['external']&0x00000010)==0x00000010) || (substr($p_entry['filename'], -1) == '/')) $v_dir_to_check = $p_entry['filename']; else if (!strstr($p_entry['filename'], "/")) $v_dir_to_check = ""; else $v_dir_to_check = dirname($p_entry['filename']); if (($v_result = $this->privDirCheck($v_dir_to_check, (($p_entry['external']&0x00000010)==0x00000010))) != 1) { // ----- Change the file status $p_entry['status'] = "path_creation_fail"; // ----- Return //return $v_result; $v_result = 1; } } } // ----- Look if extraction should be done if ($p_entry['status'] == 'ok') { // ----- Do the extraction (if not a folder) if (!(($p_entry['external']&0x00000010)==0x00000010)) { // ----- Look for not compressed file if ($p_entry['compression'] == 0) { // ----- Opening destination file if (($v_dest_file = @fopen($p_entry['filename'], 'wb')) == 0) { // ----- Change the file status $p_entry['status'] = "write_error"; // ----- Return return $v_result; } // ----- Read the file by PCLZIP_READ_BLOCK_SIZE octets blocks $v_size = $p_entry['compressed_size']; while ($v_size != 0) { $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); $v_buffer = @fread($this->zip_fd, $v_read_size); /* Try to speed up the code $v_binary_data = pack('a'.$v_read_size, $v_buffer); @fwrite($v_dest_file, $v_binary_data, $v_read_size); */ @fwrite($v_dest_file, $v_buffer, $v_read_size); $v_size -= $v_read_size; } // ----- Closing the destination file fclose($v_dest_file); // ----- Change the file mtime touch($p_entry['filename'], $p_entry['mtime']); } else { // ----- TBC // Need to be finished if (($p_entry['flag'] & 1) == 1) { PclZip::privErrorLog(PCLZIP_ERR_UNSUPPORTED_ENCRYPTION, 'File \''.$p_entry['filename'].'\' is encrypted. Encrypted files are not supported.'); return PclZip::errorCode(); } // ----- Look for using temporary file to unzip if ( (!isset($p_options[PCLZIP_OPT_TEMP_FILE_OFF])) && (isset($p_options[PCLZIP_OPT_TEMP_FILE_ON]) || (isset($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD]) && ($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD] <= $p_entry['size'])) ) ) { $v_result = $this->privExtractFileUsingTempFile($p_entry, $p_options); if ($v_result < PCLZIP_ERR_NO_ERROR) { return $v_result; } } // ----- Look for extract in memory else { // ----- Read the compressed file in a buffer (one shot) if ($p_entry['compressed_size'] > 0) { $v_buffer = @fread($this->zip_fd, $p_entry['compressed_size']); } else { $v_buffer = ''; } // ----- Decompress the file $v_file_content = @gzinflate($v_buffer); unset($v_buffer); if ($v_file_content === FALSE) { // ----- Change the file status // TBC $p_entry['status'] = "error"; return $v_result; } // ----- Opening destination file if (($v_dest_file = @fopen($p_entry['filename'], 'wb')) == 0) { // ----- Change the file status $p_entry['status'] = "write_error"; return $v_result; } // ----- Write the uncompressed data @fwrite($v_dest_file, $v_file_content, $p_entry['size']); unset($v_file_content); // ----- Closing the destination file @fclose($v_dest_file); } // ----- Change the file mtime @touch($p_entry['filename'], $p_entry['mtime']); } // ----- Look for chmod option if (isset($p_options[PCLZIP_OPT_SET_CHMOD])) { // ----- Change the mode of the file @chmod($p_entry['filename'], $p_options[PCLZIP_OPT_SET_CHMOD]); } } } // ----- Change abort status if ($p_entry['status'] == "aborted") { $p_entry['status'] = "skipped"; } // ----- Look for post-extract callback elseif (isset($p_options[PCLZIP_CB_POST_EXTRACT])) { // ----- Generate a local information $v_local_header = array(); $this->privConvertHeader2FileInfo($p_entry, $v_local_header); // ----- Call the callback // Here I do not use call_user_func() because I need to send a reference to the // header. $v_result = $p_options[PCLZIP_CB_POST_EXTRACT](PCLZIP_CB_POST_EXTRACT, $v_local_header); // ----- Look for abort result if ($v_result == 2) { $v_result = PCLZIP_ERR_USER_ABORTED; } } // ----- Return return $v_result; } // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- // Function : privExtractFileUsingTempFile() // Description : // Parameters : // Return Values : // -------------------------------------------------------------------------------- function privExtractFileUsingTempFile(&$p_entry, &$p_options) { $v_result=1; // ----- Creates a temporary file $v_gzip_temp_name = PCLZIP_TEMPORARY_DIR.uniqid('pclzip-').'.gz'; if (($v_dest_file = @fopen($v_gzip_temp_name, "wb")) == 0) { fclose($v_file); PclZip::privErrorLog(PCLZIP_ERR_WRITE_OPEN_FAIL, 'Unable to open temporary file \''.$v_gzip_temp_name.'\' in binary write mode'); return PclZip::errorCode(); } // ----- Write gz file format header $v_binary_data = pack('va1a1Va1a1', 0x8b1f, Chr($p_entry['compression']), Chr(0x00), time(), Chr(0x00), Chr(3)); @fwrite($v_dest_file, $v_binary_data, 10); // ----- Read the file by PCLZIP_READ_BLOCK_SIZE octets blocks $v_size = $p_entry['compressed_size']; while ($v_size != 0) { $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); $v_buffer = @fread($this->zip_fd, $v_read_size); //$v_binary_data = pack('a'.$v_read_size, $v_buffer); @fwrite($v_dest_file, $v_buffer, $v_read_size); $v_size -= $v_read_size; } // ----- Write gz file format footer $v_binary_data = pack('VV', $p_entry['crc'], $p_entry['size']); @fwrite($v_dest_file, $v_binary_data, 8); // ----- Close the temporary file @fclose($v_dest_file); // ----- Opening destination file if (($v_dest_file = @fopen($p_entry['filename'], 'wb')) == 0) { $p_entry['status'] = "write_error"; return $v_result; } // ----- Open the temporary gz file if (($v_src_file = @gzopen($v_gzip_temp_name, 'rb')) == 0) { @fclose($v_dest_file); $p_entry['status'] = "read_error"; PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open temporary file \''.$v_gzip_temp_name.'\' in binary read mode'); return PclZip::errorCode(); } // ----- Read the file by PCLZIP_READ_BLOCK_SIZE octets blocks $v_size = $p_entry['size']; while ($v_size != 0) { $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); $v_buffer = @gzread($v_src_file, $v_read_size); //$v_binary_data = pack('a'.$v_read_size, $v_buffer); @fwrite($v_dest_file, $v_buffer, $v_read_size); $v_size -= $v_read_size; } @fclose($v_dest_file); @gzclose($v_src_file); // ----- Delete the temporary file @unlink($v_gzip_temp_name); // ----- Return return $v_result; } // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- // Function : privExtractFileInOutput() // Description : // Parameters : // Return Values : // -------------------------------------------------------------------------------- function privExtractFileInOutput(&$p_entry, &$p_options) { $v_result=1; // ----- Read the file header if (($v_result = $this->privReadFileHeader($v_header)) != 1) { return $v_result; } // ----- Check that the file header is coherent with $p_entry info if ($this->privCheckFileHeaders($v_header, $p_entry) != 1) { // TBC } // ----- Look for pre-extract callback if (isset($p_options[PCLZIP_CB_PRE_EXTRACT])) { // ----- Generate a local information $v_local_header = array(); $this->privConvertHeader2FileInfo($p_entry, $v_local_header); // ----- Call the callback // Here I do not use call_user_func() because I need to send a reference to the // header. // eval('$v_result = '.$p_options[PCLZIP_CB_PRE_EXTRACT].'(PCLZIP_CB_PRE_EXTRACT, $v_local_header);'); $v_result = $p_options[PCLZIP_CB_PRE_EXTRACT](PCLZIP_CB_PRE_EXTRACT, $v_local_header); if ($v_result == 0) { // ----- Change the file status $p_entry['status'] = "skipped"; $v_result = 1; } // ----- Look for abort result if ($v_result == 2) { // ----- This status is internal and will be changed in 'skipped' $p_entry['status'] = "aborted"; $v_result = PCLZIP_ERR_USER_ABORTED; } // ----- Update the information // Only some fields can be modified $p_entry['filename'] = $v_local_header['filename']; } // ----- Trace // ----- Look if extraction should be done if ($p_entry['status'] == 'ok') { // ----- Do the extraction (if not a folder) if (!(($p_entry['external']&0x00000010)==0x00000010)) { // ----- Look for not compressed file if ($p_entry['compressed_size'] == $p_entry['size']) { // ----- Read the file in a buffer (one shot) if ($p_entry['compressed_size'] > 0) { $v_buffer = @fread($this->zip_fd, $p_entry['compressed_size']); } else { $v_buffer = ''; } // ----- Send the file to the output echo $v_buffer; unset($v_buffer); } else { // ----- Read the compressed file in a buffer (one shot) if ($p_entry['compressed_size'] > 0) { $v_buffer = @fread($this->zip_fd, $p_entry['compressed_size']); } else { $v_buffer = ''; } // ----- Decompress the file $v_file_content = gzinflate($v_buffer); unset($v_buffer); // ----- Send the file to the output echo $v_file_content; unset($v_file_content); } } } // ----- Change abort status if ($p_entry['status'] == "aborted") { $p_entry['status'] = "skipped"; } // ----- Look for post-extract callback elseif (isset($p_options[PCLZIP_CB_POST_EXTRACT])) { // ----- Generate a local information $v_local_header = array(); $this->privConvertHeader2FileInfo($p_entry, $v_local_header); // ----- Call the callback // Here I do not use call_user_func() because I need to send a reference to the // header. $v_result = $p_options[PCLZIP_CB_POST_EXTRACT](PCLZIP_CB_POST_EXTRACT, $v_local_header); // ----- Look for abort result if ($v_result == 2) { $v_result = PCLZIP_ERR_USER_ABORTED; } } return $v_result; } // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- // Function : privExtractFileAsString() // Description : // Parameters : // Return Values : // -------------------------------------------------------------------------------- function privExtractFileAsString(&$p_entry, &$p_string, &$p_options) { $v_result=1; // ----- Read the file header $v_header = array(); if (($v_result = $this->privReadFileHeader($v_header)) != 1) { // ----- Return return $v_result; } // ----- Check that the file header is coherent with $p_entry info if ($this->privCheckFileHeaders($v_header, $p_entry) != 1) { // TBC } // ----- Look for pre-extract callback if (isset($p_options[PCLZIP_CB_PRE_EXTRACT])) { // ----- Generate a local information $v_local_header = array(); $this->privConvertHeader2FileInfo($p_entry, $v_local_header); // ----- Call the callback // Here I do not use call_user_func() because I need to send a reference to the // header. $v_result = $p_options[PCLZIP_CB_PRE_EXTRACT](PCLZIP_CB_PRE_EXTRACT, $v_local_header); if ($v_result == 0) { // ----- Change the file status $p_entry['status'] = "skipped"; $v_result = 1; } // ----- Look for abort result if ($v_result == 2) { // ----- This status is internal and will be changed in 'skipped' $p_entry['status'] = "aborted"; $v_result = PCLZIP_ERR_USER_ABORTED; } // ----- Update the information // Only some fields can be modified $p_entry['filename'] = $v_local_header['filename']; } // ----- Look if extraction should be done if ($p_entry['status'] == 'ok') { // ----- Do the extraction (if not a folder) if (!(($p_entry['external']&0x00000010)==0x00000010)) { // ----- Look for not compressed file // if ($p_entry['compressed_size'] == $p_entry['size']) if ($p_entry['compression'] == 0) { // ----- Reading the file if ($p_entry['compressed_size'] > 0) { $p_string = @fread($this->zip_fd, $p_entry['compressed_size']); } else { $p_string = ''; } } else { // ----- Reading the file if ($p_entry['compressed_size'] > 0) { $v_data = @fread($this->zip_fd, $p_entry['compressed_size']); } else { $v_data = ''; } // ----- Decompress the file if (($p_string = @gzinflate($v_data)) === FALSE) { // TBC } } // ----- Trace } else { // TBC : error : can not extract a folder in a string } } // ----- Change abort status if ($p_entry['status'] == "aborted") { $p_entry['status'] = "skipped"; } // ----- Look for post-extract callback elseif (isset($p_options[PCLZIP_CB_POST_EXTRACT])) { // ----- Generate a local information $v_local_header = array(); $this->privConvertHeader2FileInfo($p_entry, $v_local_header); // ----- Swap the content to header $v_local_header['content'] = $p_string; $p_string = ''; // ----- Call the callback // Here I do not use call_user_func() because I need to send a reference to the // header. $v_result = $p_options[PCLZIP_CB_POST_EXTRACT](PCLZIP_CB_POST_EXTRACT, $v_local_header); // ----- Swap back the content to header $p_string = $v_local_header['content']; unset($v_local_header['content']); // ----- Look for abort result if ($v_result == 2) { $v_result = PCLZIP_ERR_USER_ABORTED; } } // ----- Return return $v_result; } // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- // Function : privReadFileHeader() // Description : // Parameters : // Return Values : // -------------------------------------------------------------------------------- function privReadFileHeader(&$p_header) { $v_result=1; // ----- Read the 4 bytes signature $v_binary_data = @fread($this->zip_fd, 4); $v_data = unpack('Vid', $v_binary_data); // ----- Check signature if ($v_data['id'] != 0x04034b50) { // ----- Error log PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'Invalid archive structure'); // ----- Return return PclZip::errorCode(); } // ----- Read the first 42 bytes of the header $v_binary_data = fread($this->zip_fd, 26); // ----- Look for invalid block size if (strlen($v_binary_data) != 26) { $p_header['filename'] = ""; $p_header['status'] = "invalid_header"; // ----- Error log PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, "Invalid block size : ".strlen($v_binary_data)); // ----- Return return PclZip::errorCode(); } // ----- Extract the values $v_data = unpack('vversion/vflag/vcompression/vmtime/vmdate/Vcrc/Vcompressed_size/Vsize/vfilename_len/vextra_len', $v_binary_data); // ----- Get filename $p_header['filename'] = fread($this->zip_fd, $v_data['filename_len']); // ----- Get extra_fields if ($v_data['extra_len'] != 0) { $p_header['extra'] = fread($this->zip_fd, $v_data['extra_len']); } else { $p_header['extra'] = ''; } // ----- Extract properties $p_header['version_extracted'] = $v_data['version']; $p_header['compression'] = $v_data['compression']; $p_header['size'] = $v_data['size']; $p_header['compressed_size'] = $v_data['compressed_size']; $p_header['crc'] = $v_data['crc']; $p_header['flag'] = $v_data['flag']; $p_header['filename_len'] = $v_data['filename_len']; // ----- Recuperate date in UNIX format $p_header['mdate'] = $v_data['mdate']; $p_header['mtime'] = $v_data['mtime']; if ($p_header['mdate'] && $p_header['mtime']) { // ----- Extract time $v_hour = ($p_header['mtime'] & 0xF800) >> 11; $v_minute = ($p_header['mtime'] & 0x07E0) >> 5; $v_seconde = ($p_header['mtime'] & 0x001F)*2; // ----- Extract date $v_year = (($p_header['mdate'] & 0xFE00) >> 9) + 1980; $v_month = ($p_header['mdate'] & 0x01E0) >> 5; $v_day = $p_header['mdate'] & 0x001F; // ----- Get UNIX date format $p_header['mtime'] = @mktime($v_hour, $v_minute, $v_seconde, $v_month, $v_day, $v_year); } else { $p_header['mtime'] = time(); } // TBC //for(reset($v_data); $key = key($v_data); next($v_data)) { //} // ----- Set the stored filename $p_header['stored_filename'] = $p_header['filename']; // ----- Set the status field $p_header['status'] = "ok"; // ----- Return return $v_result; } // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- // Function : privReadCentralFileHeader() // Description : // Parameters : // Return Values : // -------------------------------------------------------------------------------- function privReadCentralFileHeader(&$p_header) { $v_result=1; // ----- Read the 4 bytes signature $v_binary_data = @fread($this->zip_fd, 4); $v_data = unpack('Vid', $v_binary_data); // ----- Check signature if ($v_data['id'] != 0x02014b50) { // ----- Error log PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'Invalid archive structure'); // ----- Return return PclZip::errorCode(); } // ----- Read the first 42 bytes of the header $v_binary_data = fread($this->zip_fd, 42); // ----- Look for invalid block size if (strlen($v_binary_data) != 42) { $p_header['filename'] = ""; $p_header['status'] = "invalid_header"; // ----- Error log PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, "Invalid block size : ".strlen($v_binary_data)); // ----- Return return PclZip::errorCode(); } // ----- Extract the values $p_header = unpack('vversion/vversion_extracted/vflag/vcompression/vmtime/vmdate/Vcrc/Vcompressed_size/Vsize/vfilename_len/vextra_len/vcomment_len/vdisk/vinternal/Vexternal/Voffset', $v_binary_data); // ----- Get filename if ($p_header['filename_len'] != 0) $p_header['filename'] = fread($this->zip_fd, $p_header['filename_len']); else $p_header['filename'] = ''; // ----- Get extra if ($p_header['extra_len'] != 0) $p_header['extra'] = fread($this->zip_fd, $p_header['extra_len']); else $p_header['extra'] = ''; // ----- Get comment if ($p_header['comment_len'] != 0) $p_header['comment'] = fread($this->zip_fd, $p_header['comment_len']); else $p_header['comment'] = ''; // ----- Extract properties // ----- Recuperate date in UNIX format //if ($p_header['mdate'] && $p_header['mtime']) // TBC : bug : this was ignoring time with 0/0/0 if (1) { // ----- Extract time $v_hour = ($p_header['mtime'] & 0xF800) >> 11; $v_minute = ($p_header['mtime'] & 0x07E0) >> 5; $v_seconde = ($p_header['mtime'] & 0x001F)*2; // ----- Extract date $v_year = (($p_header['mdate'] & 0xFE00) >> 9) + 1980; $v_month = ($p_header['mdate'] & 0x01E0) >> 5; $v_day = $p_header['mdate'] & 0x001F; // ----- Get UNIX date format $p_header['mtime'] = @mktime($v_hour, $v_minute, $v_seconde, $v_month, $v_day, $v_year); } else { $p_header['mtime'] = time(); } // ----- Set the stored filename $p_header['stored_filename'] = $p_header['filename']; // ----- Set default status to ok $p_header['status'] = 'ok'; // ----- Look if it is a directory if (substr($p_header['filename'], -1) == '/') { //$p_header['external'] = 0x41FF0010; $p_header['external'] = 0x00000010; } // ----- Return return $v_result; } // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- // Function : privCheckFileHeaders() // Description : // Parameters : // Return Values : // 1 on success, // 0 on error; // -------------------------------------------------------------------------------- function privCheckFileHeaders(&$p_local_header, &$p_central_header) { $v_result=1; // ----- Check the static values // TBC if ($p_local_header['filename'] != $p_central_header['filename']) { } if ($p_local_header['version_extracted'] != $p_central_header['version_extracted']) { } if ($p_local_header['flag'] != $p_central_header['flag']) { } if ($p_local_header['compression'] != $p_central_header['compression']) { } if ($p_local_header['mtime'] != $p_central_header['mtime']) { } if ($p_local_header['filename_len'] != $p_central_header['filename_len']) { } // ----- Look for flag bit 3 if (($p_local_header['flag'] & 8) == 8) { $p_local_header['size'] = $p_central_header['size']; $p_local_header['compressed_size'] = $p_central_header['compressed_size']; $p_local_header['crc'] = $p_central_header['crc']; } // ----- Return return $v_result; } // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- // Function : privReadEndCentralDir() // Description : // Parameters : // Return Values : // -------------------------------------------------------------------------------- function privReadEndCentralDir(&$p_central_dir) { $v_result=1; // ----- Go to the end of the zip file $v_size = filesize($this->zipname); @fseek($this->zip_fd, $v_size); if (@ftell($this->zip_fd) != $v_size) { // ----- Error log PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'Unable to go to the end of the archive \''.$this->zipname.'\''); // ----- Return return PclZip::errorCode(); } // ----- First try : look if this is an archive with no commentaries (most of the time) // in this case the end of central dir is at 22 bytes of the file end $v_found = 0; if ($v_size > 26) { @fseek($this->zip_fd, $v_size-22); if (($v_pos = @ftell($this->zip_fd)) != ($v_size-22)) { // ----- Error log PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'Unable to seek back to the middle of the archive \''.$this->zipname.'\''); // ----- Return return PclZip::errorCode(); } // ----- Read for bytes $v_binary_data = @fread($this->zip_fd, 4); $v_data = @unpack('Vid', $v_binary_data); // ----- Check signature if ($v_data['id'] == 0x06054b50) { $v_found = 1; } $v_pos = ftell($this->zip_fd); } // ----- Go back to the maximum possible size of the Central Dir End Record if (!$v_found) { $v_maximum_size = 65557; // 0xFFFF + 22; if ($v_maximum_size > $v_size) $v_maximum_size = $v_size; @fseek($this->zip_fd, $v_size-$v_maximum_size); if (@ftell($this->zip_fd) != ($v_size-$v_maximum_size)) { // ----- Error log PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'Unable to seek back to the middle of the archive \''.$this->zipname.'\''); // ----- Return return PclZip::errorCode(); } // ----- Read byte per byte in order to find the signature $v_pos = ftell($this->zip_fd); $v_bytes = 0x00000000; while ($v_pos < $v_size) { // ----- Read a byte $v_byte = @fread($this->zip_fd, 1); // ----- Add the byte //$v_bytes = ($v_bytes << 8) | Ord($v_byte); // Note we mask the old value down such that once shifted we can never end up with more than a 32bit number // Otherwise on systems where we have 64bit integers the check below for the magic number will fail. $v_bytes = ( ($v_bytes & 0xFFFFFF) << 8) | Ord($v_byte); // ----- Compare the bytes if ($v_bytes == 0x504b0506) { $v_pos++; break; } $v_pos++; } // ----- Look if not found end of central dir if ($v_pos == $v_size) { // ----- Error log PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, "Unable to find End of Central Dir Record signature"); // ----- Return return PclZip::errorCode(); } } // ----- Read the first 18 bytes of the header $v_binary_data = fread($this->zip_fd, 18); // ----- Look for invalid block size if (strlen($v_binary_data) != 18) { // ----- Error log PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, "Invalid End of Central Dir Record size : ".strlen($v_binary_data)); // ----- Return return PclZip::errorCode(); } // ----- Extract the values $v_data = unpack('vdisk/vdisk_start/vdisk_entries/ventries/Vsize/Voffset/vcomment_size', $v_binary_data); // ----- Check the global size if (($v_pos + $v_data['comment_size'] + 18) != $v_size) { // ----- Removed in release 2.2 see readme file // The check of the file size is a little too strict. // Some bugs where found when a zip is encrypted/decrypted with 'crypt'. // While decrypted, zip has training 0 bytes if (0) { // ----- Error log PclZip::privErrorLog(PCLZIP_ERR_BAD_FORMAT, 'The central dir is not at the end of the archive.' .' Some trailing bytes exists after the archive.'); // ----- Return return PclZip::errorCode(); } } // ----- Get comment if ($v_data['comment_size'] != 0) { $p_central_dir['comment'] = fread($this->zip_fd, $v_data['comment_size']); } else $p_central_dir['comment'] = ''; $p_central_dir['entries'] = $v_data['entries']; $p_central_dir['disk_entries'] = $v_data['disk_entries']; $p_central_dir['offset'] = $v_data['offset']; $p_central_dir['size'] = $v_data['size']; $p_central_dir['disk'] = $v_data['disk']; $p_central_dir['disk_start'] = $v_data['disk_start']; // TBC //for(reset($p_central_dir); $key = key($p_central_dir); next($p_central_dir)) { //} // ----- Return return $v_result; } // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- // Function : privDeleteByRule() // Description : // Parameters : // Return Values : // -------------------------------------------------------------------------------- function privDeleteByRule(&$p_result_list, &$p_options) { $v_result=1; $v_list_detail = array(); // ----- Open the zip file if (($v_result=$this->privOpenFd('rb')) != 1) { // ----- Return return $v_result; } // ----- Read the central directory information $v_central_dir = array(); if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1) { $this->privCloseFd(); return $v_result; } // ----- Go to beginning of File @rewind($this->zip_fd); // ----- Scan all the files // ----- Start at beginning of Central Dir $v_pos_entry = $v_central_dir['offset']; @rewind($this->zip_fd); if (@fseek($this->zip_fd, $v_pos_entry)) { // ----- Close the zip file $this->privCloseFd(); // ----- Error log PclZip::privErrorLog(PCLZIP_ERR_INVALID_ARCHIVE_ZIP, 'Invalid archive size'); // ----- Return return PclZip::errorCode(); } // ----- Read each entry $v_header_list = array(); $j_start = 0; for ($i=0, $v_nb_extracted=0; $i<$v_central_dir['entries']; $i++) { // ----- Read the file header $v_header_list[$v_nb_extracted] = array(); if (($v_result = $this->privReadCentralFileHeader($v_header_list[$v_nb_extracted])) != 1) { // ----- Close the zip file $this->privCloseFd(); return $v_result; } // ----- Store the index $v_header_list[$v_nb_extracted]['index'] = $i; // ----- Look for the specific extract rules $v_found = false; // ----- Look for extract by name rule if ( (isset($p_options[PCLZIP_OPT_BY_NAME])) && ($p_options[PCLZIP_OPT_BY_NAME] != 0)) { // ----- Look if the filename is in the list for ($j=0; ($j<sizeof($p_options[PCLZIP_OPT_BY_NAME])) && (!$v_found); $j++) { // ----- Look for a directory if (substr($p_options[PCLZIP_OPT_BY_NAME][$j], -1) == "/") { // ----- Look if the directory is in the filename path if ( (strlen($v_header_list[$v_nb_extracted]['stored_filename']) > strlen($p_options[PCLZIP_OPT_BY_NAME][$j])) && (substr($v_header_list[$v_nb_extracted]['stored_filename'], 0, strlen($p_options[PCLZIP_OPT_BY_NAME][$j])) == $p_options[PCLZIP_OPT_BY_NAME][$j])) { $v_found = true; } elseif ( (($v_header_list[$v_nb_extracted]['external']&0x00000010)==0x00000010) /* Indicates a folder */ && ($v_header_list[$v_nb_extracted]['stored_filename'].'/' == $p_options[PCLZIP_OPT_BY_NAME][$j])) { $v_found = true; } } // ----- Look for a filename elseif ($v_header_list[$v_nb_extracted]['stored_filename'] == $p_options[PCLZIP_OPT_BY_NAME][$j]) { $v_found = true; } } } // ----- Look for extract by ereg rule // ereg() is deprecated with PHP 5.3 /* else if ( (isset($p_options[PCLZIP_OPT_BY_EREG])) && ($p_options[PCLZIP_OPT_BY_EREG] != "")) { if (ereg($p_options[PCLZIP_OPT_BY_EREG], $v_header_list[$v_nb_extracted]['stored_filename'])) { $v_found = true; } } */ // ----- Look for extract by preg rule else if ( (isset($p_options[PCLZIP_OPT_BY_PREG])) && ($p_options[PCLZIP_OPT_BY_PREG] != "")) { if (preg_match($p_options[PCLZIP_OPT_BY_PREG], $v_header_list[$v_nb_extracted]['stored_filename'])) { $v_found = true; } } // ----- Look for extract by index rule else if ( (isset($p_options[PCLZIP_OPT_BY_INDEX])) && ($p_options[PCLZIP_OPT_BY_INDEX] != 0)) { // ----- Look if the index is in the list for ($j=$j_start; ($j<sizeof($p_options[PCLZIP_OPT_BY_INDEX])) && (!$v_found); $j++) { if (($i>=$p_options[PCLZIP_OPT_BY_INDEX][$j]['start']) && ($i<=$p_options[PCLZIP_OPT_BY_INDEX][$j]['end'])) { $v_found = true; } if ($i>=$p_options[PCLZIP_OPT_BY_INDEX][$j]['end']) { $j_start = $j+1; } if ($p_options[PCLZIP_OPT_BY_INDEX][$j]['start']>$i) { break; } } } else { $v_found = true; } // ----- Look for deletion if ($v_found) { unset($v_header_list[$v_nb_extracted]); } else { $v_nb_extracted++; } } // ----- Look if something need to be deleted if ($v_nb_extracted > 0) { // ----- Creates a temporary file $v_zip_temp_name = PCLZIP_TEMPORARY_DIR.uniqid('pclzip-').'.tmp'; // ----- Creates a temporary zip archive $v_temp_zip = new PclZip($v_zip_temp_name); // ----- Open the temporary zip file in write mode if (($v_result = $v_temp_zip->privOpenFd('wb')) != 1) { $this->privCloseFd(); // ----- Return return $v_result; } // ----- Look which file need to be kept for ($i=0; $i<sizeof($v_header_list); $i++) { // ----- Calculate the position of the header @rewind($this->zip_fd); if (@fseek($this->zip_fd, $v_header_list[$i]['offset'])) { // ----- Close the zip file $this->privCloseFd(); $v_temp_zip->privCloseFd(); @unlink($v_zip_temp_name); // ----- Error log PclZip::privErrorLog(PCLZIP_ERR_INVALID_ARCHIVE_ZIP, 'Invalid archive size'); // ----- Return return PclZip::errorCode(); } // ----- Read the file header $v_local_header = array(); if (($v_result = $this->privReadFileHeader($v_local_header)) != 1) { // ----- Close the zip file $this->privCloseFd(); $v_temp_zip->privCloseFd(); @unlink($v_zip_temp_name); // ----- Return return $v_result; } // ----- Check that local file header is same as central file header if ($this->privCheckFileHeaders($v_local_header, $v_header_list[$i]) != 1) { // TBC } unset($v_local_header); // ----- Write the file header if (($v_result = $v_temp_zip->privWriteFileHeader($v_header_list[$i])) != 1) { // ----- Close the zip file $this->privCloseFd(); $v_temp_zip->privCloseFd(); @unlink($v_zip_temp_name); // ----- Return return $v_result; } // ----- Read/write the data block if (($v_result = PclZipUtilCopyBlock($this->zip_fd, $v_temp_zip->zip_fd, $v_header_list[$i]['compressed_size'])) != 1) { // ----- Close the zip file $this->privCloseFd(); $v_temp_zip->privCloseFd(); @unlink($v_zip_temp_name); // ----- Return return $v_result; } } // ----- Store the offset of the central dir $v_offset = @ftell($v_temp_zip->zip_fd); // ----- Re-Create the Central Dir files header for ($i=0; $i<sizeof($v_header_list); $i++) { // ----- Create the file header if (($v_result = $v_temp_zip->privWriteCentralFileHeader($v_header_list[$i])) != 1) { $v_temp_zip->privCloseFd(); $this->privCloseFd(); @unlink($v_zip_temp_name); // ----- Return return $v_result; } // ----- Transform the header to a 'usable' info $v_temp_zip->privConvertHeader2FileInfo($v_header_list[$i], $p_result_list[$i]); } // ----- Zip file comment $v_comment = ''; if (isset($p_options[PCLZIP_OPT_COMMENT])) { $v_comment = $p_options[PCLZIP_OPT_COMMENT]; } // ----- Calculate the size of the central header $v_size = @ftell($v_temp_zip->zip_fd)-$v_offset; // ----- Create the central dir footer if (($v_result = $v_temp_zip->privWriteCentralHeader(sizeof($v_header_list), $v_size, $v_offset, $v_comment)) != 1) { // ----- Reset the file list unset($v_header_list); $v_temp_zip->privCloseFd(); $this->privCloseFd(); @unlink($v_zip_temp_name); // ----- Return return $v_result; } // ----- Close $v_temp_zip->privCloseFd(); $this->privCloseFd(); // ----- Delete the zip file // TBC : I should test the result ... @unlink($this->zipname); // ----- Rename the temporary file // TBC : I should test the result ... //@rename($v_zip_temp_name, $this->zipname); PclZipUtilRename($v_zip_temp_name, $this->zipname); // ----- Destroy the temporary archive unset($v_temp_zip); } // ----- Remove every files : reset the file else if ($v_central_dir['entries'] != 0) { $this->privCloseFd(); if (($v_result = $this->privOpenFd('wb')) != 1) { return $v_result; } if (($v_result = $this->privWriteCentralHeader(0, 0, 0, '')) != 1) { return $v_result; } $this->privCloseFd(); } // ----- Return return $v_result; } // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- // Function : privDirCheck() // Description : // Check if a directory exists, if not it creates it and all the parents directory // which may be useful. // Parameters : // $p_dir : Directory path to check. // Return Values : // 1 : OK // -1 : Unable to create directory // -------------------------------------------------------------------------------- function privDirCheck($p_dir, $p_is_dir=false) { $v_result = 1; // ----- Remove the final '/' if (($p_is_dir) && (substr($p_dir, -1)=='/')) { $p_dir = substr($p_dir, 0, strlen($p_dir)-1); } // ----- Check the directory availability if ((is_dir($p_dir)) || ($p_dir == "")) { return 1; } // ----- Extract parent directory $p_parent_dir = dirname($p_dir); // ----- Just a check if ($p_parent_dir != $p_dir) { // ----- Look for parent directory if ($p_parent_dir != "") { if (($v_result = $this->privDirCheck($p_parent_dir)) != 1) { return $v_result; } } } // ----- Create the directory if (!@mkdir($p_dir, 0777)) { // ----- Error log PclZip::privErrorLog(PCLZIP_ERR_DIR_CREATE_FAIL, "Unable to create directory '$p_dir'"); // ----- Return return PclZip::errorCode(); } // ----- Return return $v_result; } // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- // Function : privMerge() // Description : // If $p_archive_to_add does not exist, the function exit with a success result. // Parameters : // Return Values : // -------------------------------------------------------------------------------- function privMerge(&$p_archive_to_add) { $v_result=1; // ----- Look if the archive_to_add exists if (!is_file($p_archive_to_add->zipname)) { // ----- Nothing to merge, so merge is a success $v_result = 1; // ----- Return return $v_result; } // ----- Look if the archive exists if (!is_file($this->zipname)) { // ----- Do a duplicate $v_result = $this->privDuplicate($p_archive_to_add->zipname); // ----- Return return $v_result; } // ----- Open the zip file if (($v_result=$this->privOpenFd('rb')) != 1) { // ----- Return return $v_result; } // ----- Read the central directory information $v_central_dir = array(); if (($v_result = $this->privReadEndCentralDir($v_central_dir)) != 1) { $this->privCloseFd(); return $v_result; } // ----- Go to beginning of File @rewind($this->zip_fd); // ----- Open the archive_to_add file if (($v_result=$p_archive_to_add->privOpenFd('rb')) != 1) { $this->privCloseFd(); // ----- Return return $v_result; } // ----- Read the central directory information $v_central_dir_to_add = array(); if (($v_result = $p_archive_to_add->privReadEndCentralDir($v_central_dir_to_add)) != 1) { $this->privCloseFd(); $p_archive_to_add->privCloseFd(); return $v_result; } // ----- Go to beginning of File @rewind($p_archive_to_add->zip_fd); // ----- Creates a temporary file $v_zip_temp_name = PCLZIP_TEMPORARY_DIR.uniqid('pclzip-').'.tmp'; // ----- Open the temporary file in write mode if (($v_zip_temp_fd = @fopen($v_zip_temp_name, 'wb')) == 0) { $this->privCloseFd(); $p_archive_to_add->privCloseFd(); PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open temporary file \''.$v_zip_temp_name.'\' in binary write mode'); // ----- Return return PclZip::errorCode(); } // ----- Copy the files from the archive to the temporary file // TBC : Here I should better append the file and go back to erase the central dir $v_size = $v_central_dir['offset']; while ($v_size != 0) { $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); $v_buffer = fread($this->zip_fd, $v_read_size); @fwrite($v_zip_temp_fd, $v_buffer, $v_read_size); $v_size -= $v_read_size; } // ----- Copy the files from the archive_to_add into the temporary file $v_size = $v_central_dir_to_add['offset']; while ($v_size != 0) { $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); $v_buffer = fread($p_archive_to_add->zip_fd, $v_read_size); @fwrite($v_zip_temp_fd, $v_buffer, $v_read_size); $v_size -= $v_read_size; } // ----- Store the offset of the central dir $v_offset = @ftell($v_zip_temp_fd); // ----- Copy the block of file headers from the old archive $v_size = $v_central_dir['size']; while ($v_size != 0) { $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); $v_buffer = @fread($this->zip_fd, $v_read_size); @fwrite($v_zip_temp_fd, $v_buffer, $v_read_size); $v_size -= $v_read_size; } // ----- Copy the block of file headers from the archive_to_add $v_size = $v_central_dir_to_add['size']; while ($v_size != 0) { $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); $v_buffer = @fread($p_archive_to_add->zip_fd, $v_read_size); @fwrite($v_zip_temp_fd, $v_buffer, $v_read_size); $v_size -= $v_read_size; } // ----- Merge the file comments $v_comment = $v_central_dir['comment'].' '.$v_central_dir_to_add['comment']; // ----- Calculate the size of the (new) central header $v_size = @ftell($v_zip_temp_fd)-$v_offset; // ----- Swap the file descriptor // Here is a trick : I swap the temporary fd with the zip fd, in order to use // the following methods on the temporary fil and not the real archive fd $v_swap = $this->zip_fd; $this->zip_fd = $v_zip_temp_fd; $v_zip_temp_fd = $v_swap; // ----- Create the central dir footer if (($v_result = $this->privWriteCentralHeader($v_central_dir['entries']+$v_central_dir_to_add['entries'], $v_size, $v_offset, $v_comment)) != 1) { $this->privCloseFd(); $p_archive_to_add->privCloseFd(); @fclose($v_zip_temp_fd); $this->zip_fd = null; // ----- Reset the file list unset($v_header_list); // ----- Return return $v_result; } // ----- Swap back the file descriptor $v_swap = $this->zip_fd; $this->zip_fd = $v_zip_temp_fd; $v_zip_temp_fd = $v_swap; // ----- Close $this->privCloseFd(); $p_archive_to_add->privCloseFd(); // ----- Close the temporary file @fclose($v_zip_temp_fd); // ----- Delete the zip file // TBC : I should test the result ... @unlink($this->zipname); // ----- Rename the temporary file // TBC : I should test the result ... //@rename($v_zip_temp_name, $this->zipname); PclZipUtilRename($v_zip_temp_name, $this->zipname); // ----- Return return $v_result; } // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- // Function : privDuplicate() // Description : // Parameters : // Return Values : // -------------------------------------------------------------------------------- function privDuplicate($p_archive_filename) { $v_result=1; // ----- Look if the $p_archive_filename exists if (!is_file($p_archive_filename)) { // ----- Nothing to duplicate, so duplicate is a success. $v_result = 1; // ----- Return return $v_result; } // ----- Open the zip file if (($v_result=$this->privOpenFd('wb')) != 1) { // ----- Return return $v_result; } // ----- Open the temporary file in write mode if (($v_zip_temp_fd = @fopen($p_archive_filename, 'rb')) == 0) { $this->privCloseFd(); PclZip::privErrorLog(PCLZIP_ERR_READ_OPEN_FAIL, 'Unable to open archive file \''.$p_archive_filename.'\' in binary write mode'); // ----- Return return PclZip::errorCode(); } // ----- Copy the files from the archive to the temporary file // TBC : Here I should better append the file and go back to erase the central dir $v_size = filesize($p_archive_filename); while ($v_size != 0) { $v_read_size = ($v_size < PCLZIP_READ_BLOCK_SIZE ? $v_size : PCLZIP_READ_BLOCK_SIZE); $v_buffer = fread($v_zip_temp_fd, $v_read_size); @fwrite($this->zip_fd, $v_buffer, $v_read_size); $v_size -= $v_read_size; } // ----- Close $this->privCloseFd(); // ----- Close the temporary file @fclose($v_zip_temp_fd); // ----- Return return $v_result; } // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- // Function : privErrorLog() // Description : // Parameters : // -------------------------------------------------------------------------------- function privErrorLog($p_error_code=0, $p_error_string='') { if (PCLZIP_ERROR_EXTERNAL == 1) { PclError($p_error_code, $p_error_string); } else { $this->error_code = $p_error_code; $this->error_string = $p_error_string; } } // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- // Function : privErrorReset() // Description : // Parameters : // -------------------------------------------------------------------------------- function privErrorReset() { if (PCLZIP_ERROR_EXTERNAL == 1) { PclErrorReset(); } else { $this->error_code = 0; $this->error_string = ''; } } // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- // Function : privDisableMagicQuotes() // Description : // Parameters : // Return Values : // -------------------------------------------------------------------------------- function privDisableMagicQuotes() { $v_result=1; // EDIT for WordPress 5.3.0 // magic_quote functions are deprecated in PHP 7.4, now assuming it's always off. /* // ----- Look if function exists if ( (!function_exists("get_magic_quotes_runtime")) || (!function_exists("set_magic_quotes_runtime"))) { return $v_result; } // ----- Look if already done if ($this->magic_quotes_status != -1) { return $v_result; } // ----- Get and memorize the magic_quote value $this->magic_quotes_status = @get_magic_quotes_runtime(); // ----- Disable magic_quotes if ($this->magic_quotes_status == 1) { @set_magic_quotes_runtime(0); } */ // ----- Return return $v_result; } // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- // Function : privSwapBackMagicQuotes() // Description : // Parameters : // Return Values : // -------------------------------------------------------------------------------- function privSwapBackMagicQuotes() { $v_result=1; // EDIT for WordPress 5.3.0 // magic_quote functions are deprecated in PHP 7.4, now assuming it's always off. /* // ----- Look if function exists if ( (!function_exists("get_magic_quotes_runtime")) || (!function_exists("set_magic_quotes_runtime"))) { return $v_result; } // ----- Look if something to do if ($this->magic_quotes_status != -1) { return $v_result; } // ----- Swap back magic_quotes if ($this->magic_quotes_status == 1) { @set_magic_quotes_runtime($this->magic_quotes_status); } */ // ----- Return return $v_result; } // -------------------------------------------------------------------------------- } // End of class // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- // Function : PclZipUtilPathReduction() // Description : // Parameters : // Return Values : // -------------------------------------------------------------------------------- function PclZipUtilPathReduction($p_dir) { $v_result = ""; // ----- Look for not empty path if ($p_dir != "") { // ----- Explode path by directory names $v_list = explode("/", $p_dir); // ----- Study directories from last to first $v_skip = 0; for ($i=sizeof($v_list)-1; $i>=0; $i--) { // ----- Look for current path if ($v_list[$i] == ".") { // ----- Ignore this directory // Should be the first $i=0, but no check is done } else if ($v_list[$i] == "..") { $v_skip++; } else if ($v_list[$i] == "") { // ----- First '/' i.e. root slash if ($i == 0) { $v_result = "/".$v_result; if ($v_skip > 0) { // ----- It is an invalid path, so the path is not modified // TBC $v_result = $p_dir; $v_skip = 0; } } // ----- Last '/' i.e. indicates a directory else if ($i == (sizeof($v_list)-1)) { $v_result = $v_list[$i]; } // ----- Double '/' inside the path else { // ----- Ignore only the double '//' in path, // but not the first and last '/' } } else { // ----- Look for item to skip if ($v_skip > 0) { $v_skip--; } else { $v_result = $v_list[$i].($i!=(sizeof($v_list)-1)?"/".$v_result:""); } } } // ----- Look for skip if ($v_skip > 0) { while ($v_skip > 0) { $v_result = '../'.$v_result; $v_skip--; } } } // ----- Return return $v_result; } // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- // Function : PclZipUtilPathInclusion() // Description : // This function indicates if the path $p_path is under the $p_dir tree. Or, // said in an other way, if the file or sub-dir $p_path is inside the dir // $p_dir. // The function indicates also if the path is exactly the same as the dir. // This function supports path with duplicated '/' like '//', but does not // support '.' or '..' statements. // Parameters : // Return Values : // 0 if $p_path is not inside directory $p_dir // 1 if $p_path is inside directory $p_dir // 2 if $p_path is exactly the same as $p_dir // -------------------------------------------------------------------------------- function PclZipUtilPathInclusion($p_dir, $p_path) { $v_result = 1; // ----- Look for path beginning by ./ if ( ($p_dir == '.') || ((strlen($p_dir) >=2) && (substr($p_dir, 0, 2) == './'))) { $p_dir = PclZipUtilTranslateWinPath(getcwd(), FALSE).'/'.substr($p_dir, 1); } if ( ($p_path == '.') || ((strlen($p_path) >=2) && (substr($p_path, 0, 2) == './'))) { $p_path = PclZipUtilTranslateWinPath(getcwd(), FALSE).'/'.substr($p_path, 1); } // ----- Explode dir and path by directory separator $v_list_dir = explode("/", $p_dir); $v_list_dir_size = sizeof($v_list_dir); $v_list_path = explode("/", $p_path); $v_list_path_size = sizeof($v_list_path); // ----- Study directories paths $i = 0; $j = 0; while (($i < $v_list_dir_size) && ($j < $v_list_path_size) && ($v_result)) { // ----- Look for empty dir (path reduction) if ($v_list_dir[$i] == '') { $i++; continue; } if ($v_list_path[$j] == '') { $j++; continue; } // ----- Compare the items if (($v_list_dir[$i] != $v_list_path[$j]) && ($v_list_dir[$i] != '') && ( $v_list_path[$j] != '')) { $v_result = 0; } // ----- Next items $i++; $j++; } // ----- Look if everything seems to be the same if ($v_result) { // ----- Skip all the empty items while (($j < $v_list_path_size) && ($v_list_path[$j] == '')) $j++; while (($i < $v_list_dir_size) && ($v_list_dir[$i] == '')) $i++; if (($i >= $v_list_dir_size) && ($j >= $v_list_path_size)) { // ----- There are exactly the same $v_result = 2; } else if ($i < $v_list_dir_size) { // ----- The path is shorter than the dir $v_result = 0; } } // ----- Return return $v_result; } // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- // Function : PclZipUtilCopyBlock() // Description : // Parameters : // $p_mode : read/write compression mode // 0 : src & dest normal // 1 : src gzip, dest normal // 2 : src normal, dest gzip // 3 : src & dest gzip // Return Values : // -------------------------------------------------------------------------------- function PclZipUtilCopyBlock($p_src, $p_dest, $p_size, $p_mode=0) { $v_result = 1; if ($p_mode==0) { while ($p_size != 0) { $v_read_size = ($p_size < PCLZIP_READ_BLOCK_SIZE ? $p_size : PCLZIP_READ_BLOCK_SIZE); $v_buffer = @fread($p_src, $v_read_size); @fwrite($p_dest, $v_buffer, $v_read_size); $p_size -= $v_read_size; } } else if ($p_mode==1) { while ($p_size != 0) { $v_read_size = ($p_size < PCLZIP_READ_BLOCK_SIZE ? $p_size : PCLZIP_READ_BLOCK_SIZE); $v_buffer = @gzread($p_src, $v_read_size); @fwrite($p_dest, $v_buffer, $v_read_size); $p_size -= $v_read_size; } } else if ($p_mode==2) { while ($p_size != 0) { $v_read_size = ($p_size < PCLZIP_READ_BLOCK_SIZE ? $p_size : PCLZIP_READ_BLOCK_SIZE); $v_buffer = @fread($p_src, $v_read_size); @gzwrite($p_dest, $v_buffer, $v_read_size); $p_size -= $v_read_size; } } else if ($p_mode==3) { while ($p_size != 0) { $v_read_size = ($p_size < PCLZIP_READ_BLOCK_SIZE ? $p_size : PCLZIP_READ_BLOCK_SIZE); $v_buffer = @gzread($p_src, $v_read_size); @gzwrite($p_dest, $v_buffer, $v_read_size); $p_size -= $v_read_size; } } // ----- Return return $v_result; } // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- // Function : PclZipUtilRename() // Description : // This function tries to do a simple rename() function. If it fails, it // tries to copy the $p_src file in a new $p_dest file and then unlink the // first one. // Parameters : // $p_src : Old filename // $p_dest : New filename // Return Values : // 1 on success, 0 on failure. // -------------------------------------------------------------------------------- function PclZipUtilRename($p_src, $p_dest) { $v_result = 1; // ----- Try to rename the files if (!@rename($p_src, $p_dest)) { // ----- Try to copy & unlink the src if (!@copy($p_src, $p_dest)) { $v_result = 0; } else if (!@unlink($p_src)) { $v_result = 0; } } // ----- Return return $v_result; } // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- // Function : PclZipUtilOptionText() // Description : // Translate option value in text. Mainly for debug purpose. // Parameters : // $p_option : the option value. // Return Values : // The option text value. // -------------------------------------------------------------------------------- function PclZipUtilOptionText($p_option) { $v_list = get_defined_constants(); for (reset($v_list); $v_key = key($v_list); next($v_list)) { $v_prefix = substr($v_key, 0, 10); if (( ($v_prefix == 'PCLZIP_OPT') || ($v_prefix == 'PCLZIP_CB_') || ($v_prefix == 'PCLZIP_ATT')) && ($v_list[$v_key] == $p_option)) { return $v_key; } } $v_result = 'Unknown'; return $v_result; } // -------------------------------------------------------------------------------- // -------------------------------------------------------------------------------- // Function : PclZipUtilTranslateWinPath() // Description : // Translate windows path by replacing '\' by '/' and optionally removing // drive letter. // Parameters : // $p_path : path to translate. // $p_remove_disk_letter : true | false // Return Values : // The path translated. // -------------------------------------------------------------------------------- function PclZipUtilTranslateWinPath($p_path, $p_remove_disk_letter=true) { if (stristr(php_uname(), 'windows')) { // ----- Look for potential disk letter if (($p_remove_disk_letter) && (($v_position = strpos($p_path, ':')) != false)) { $p_path = substr($p_path, $v_position+1); } // ----- Change potential windows directory separator if ((strpos($p_path, '\\') > 0) || (substr($p_path, 0,1) == '\\')) { $p_path = strtr($p_path, '\\', '/'); } } return $p_path; } // -------------------------------------------------------------------------------- ?> includes/class-wp-filesystem-base.php 0000644 00000057532 15135536347 0013736 0 ustar 00 <?php /** * Base WordPress Filesystem * * @package WordPress * @subpackage Filesystem */ /** * Base WordPress Filesystem class which Filesystem implementations extend. * * @since 2.5.0 */ #[AllowDynamicProperties] class WP_Filesystem_Base { /** * Whether to display debug data for the connection. * * @since 2.5.0 * @var bool */ public $verbose = false; /** * Cached list of local filepaths to mapped remote filepaths. * * @since 2.7.0 * @var array */ public $cache = array(); /** * The Access method of the current connection, Set automatically. * * @since 2.5.0 * @var string */ public $method = ''; /** * @var WP_Error */ public $errors = null; /** */ public $options = array(); /** * Returns the path on the remote filesystem of ABSPATH. * * @since 2.7.0 * * @return string The location of the remote path. */ public function abspath() { $folder = $this->find_folder( ABSPATH ); /* * Perhaps the FTP folder is rooted at the WordPress install. * Check for wp-includes folder in root. Could have some false positives, but rare. */ if ( ! $folder && $this->is_dir( '/' . WPINC ) ) { $folder = '/'; } return $folder; } /** * Returns the path on the remote filesystem of WP_CONTENT_DIR. * * @since 2.7.0 * * @return string The location of the remote path. */ public function wp_content_dir() { return $this->find_folder( WP_CONTENT_DIR ); } /** * Returns the path on the remote filesystem of WP_PLUGIN_DIR. * * @since 2.7.0 * * @return string The location of the remote path. */ public function wp_plugins_dir() { return $this->find_folder( WP_PLUGIN_DIR ); } /** * Returns the path on the remote filesystem of the Themes Directory. * * @since 2.7.0 * * @param string|false $theme Optional. The theme stylesheet or template for the directory. * Default false. * @return string The location of the remote path. */ public function wp_themes_dir( $theme = false ) { $theme_root = get_theme_root( $theme ); // Account for relative theme roots. if ( '/themes' === $theme_root || ! is_dir( $theme_root ) ) { $theme_root = WP_CONTENT_DIR . $theme_root; } return $this->find_folder( $theme_root ); } /** * Returns the path on the remote filesystem of WP_LANG_DIR. * * @since 3.2.0 * * @return string The location of the remote path. */ public function wp_lang_dir() { return $this->find_folder( WP_LANG_DIR ); } /** * Locates a folder on the remote filesystem. * * @since 2.5.0 * @deprecated 2.7.0 use WP_Filesystem_Base::abspath() or WP_Filesystem_Base::wp_*_dir() instead. * @see WP_Filesystem_Base::abspath() * @see WP_Filesystem_Base::wp_content_dir() * @see WP_Filesystem_Base::wp_plugins_dir() * @see WP_Filesystem_Base::wp_themes_dir() * @see WP_Filesystem_Base::wp_lang_dir() * * @param string $base Optional. The folder to start searching from. Default '.'. * @param bool $verbose Optional. True to display debug information. Default false. * @return string The location of the remote path. */ public function find_base_dir( $base = '.', $verbose = false ) { _deprecated_function( __FUNCTION__, '2.7.0', 'WP_Filesystem_Base::abspath() or WP_Filesystem_Base::wp_*_dir()' ); $this->verbose = $verbose; return $this->abspath(); } /** * Locates a folder on the remote filesystem. * * @since 2.5.0 * @deprecated 2.7.0 use WP_Filesystem_Base::abspath() or WP_Filesystem_Base::wp_*_dir() methods instead. * @see WP_Filesystem_Base::abspath() * @see WP_Filesystem_Base::wp_content_dir() * @see WP_Filesystem_Base::wp_plugins_dir() * @see WP_Filesystem_Base::wp_themes_dir() * @see WP_Filesystem_Base::wp_lang_dir() * * @param string $base Optional. The folder to start searching from. Default '.'. * @param bool $verbose Optional. True to display debug information. Default false. * @return string The location of the remote path. */ public function get_base_dir( $base = '.', $verbose = false ) { _deprecated_function( __FUNCTION__, '2.7.0', 'WP_Filesystem_Base::abspath() or WP_Filesystem_Base::wp_*_dir()' ); $this->verbose = $verbose; return $this->abspath(); } /** * Locates a folder on the remote filesystem. * * Assumes that on Windows systems, Stripping off the Drive * letter is OK Sanitizes \\ to / in Windows filepaths. * * @since 2.7.0 * * @param string $folder the folder to locate. * @return string|false The location of the remote path, false on failure. */ public function find_folder( $folder ) { if ( isset( $this->cache[ $folder ] ) ) { return $this->cache[ $folder ]; } if ( stripos( $this->method, 'ftp' ) !== false ) { $constant_overrides = array( 'FTP_BASE' => ABSPATH, 'FTP_CONTENT_DIR' => WP_CONTENT_DIR, 'FTP_PLUGIN_DIR' => WP_PLUGIN_DIR, 'FTP_LANG_DIR' => WP_LANG_DIR, ); // Direct matches ( folder = CONSTANT/ ). foreach ( $constant_overrides as $constant => $dir ) { if ( ! defined( $constant ) ) { continue; } if ( $folder === $dir ) { return trailingslashit( constant( $constant ) ); } } // Prefix matches ( folder = CONSTANT/subdir ), foreach ( $constant_overrides as $constant => $dir ) { if ( ! defined( $constant ) ) { continue; } if ( 0 === stripos( $folder, $dir ) ) { // $folder starts with $dir. $potential_folder = preg_replace( '#^' . preg_quote( $dir, '#' ) . '/#i', trailingslashit( constant( $constant ) ), $folder ); $potential_folder = trailingslashit( $potential_folder ); if ( $this->is_dir( $potential_folder ) ) { $this->cache[ $folder ] = $potential_folder; return $potential_folder; } } } } elseif ( 'direct' === $this->method ) { $folder = str_replace( '\\', '/', $folder ); // Windows path sanitization. return trailingslashit( $folder ); } $folder = preg_replace( '|^([a-z]{1}):|i', '', $folder ); // Strip out Windows drive letter if it's there. $folder = str_replace( '\\', '/', $folder ); // Windows path sanitization. if ( isset( $this->cache[ $folder ] ) ) { return $this->cache[ $folder ]; } if ( $this->exists( $folder ) ) { // Folder exists at that absolute path. $folder = trailingslashit( $folder ); $this->cache[ $folder ] = $folder; return $folder; } $return = $this->search_for_folder( $folder ); if ( $return ) { $this->cache[ $folder ] = $return; } return $return; } /** * Locates a folder on the remote filesystem. * * Expects Windows sanitized path. * * @since 2.7.0 * * @param string $folder The folder to locate. * @param string $base The folder to start searching from. * @param bool $loop If the function has recursed. Internal use only. * @return string|false The location of the remote path, false to cease looping. */ public function search_for_folder( $folder, $base = '.', $loop = false ) { if ( empty( $base ) || '.' === $base ) { $base = trailingslashit( $this->cwd() ); } $folder = untrailingslashit( $folder ); if ( $this->verbose ) { /* translators: 1: Folder to locate, 2: Folder to start searching from. */ printf( "\n" . __( 'Looking for %1$s in %2$s' ) . "<br />\n", $folder, $base ); } $folder_parts = explode( '/', $folder ); $folder_part_keys = array_keys( $folder_parts ); $last_index = array_pop( $folder_part_keys ); $last_path = $folder_parts[ $last_index ]; $files = $this->dirlist( $base ); foreach ( $folder_parts as $index => $key ) { if ( $index === $last_index ) { continue; // We want this to be caught by the next code block. } /* * Working from /home/ to /user/ to /wordpress/ see if that file exists within * the current folder, If it's found, change into it and follow through looking * for it. If it can't find WordPress down that route, it'll continue onto the next * folder level, and see if that matches, and so on. If it reaches the end, and still * can't find it, it'll return false for the entire function. */ if ( isset( $files[ $key ] ) ) { // Let's try that folder: $newdir = trailingslashit( path_join( $base, $key ) ); if ( $this->verbose ) { /* translators: %s: Directory name. */ printf( "\n" . __( 'Changing to %s' ) . "<br />\n", $newdir ); } // Only search for the remaining path tokens in the directory, not the full path again. $newfolder = implode( '/', array_slice( $folder_parts, $index + 1 ) ); $ret = $this->search_for_folder( $newfolder, $newdir, $loop ); if ( $ret ) { return $ret; } } } /* * Only check this as a last resort, to prevent locating the incorrect install. * All above procedures will fail quickly if this is the right branch to take. */ if ( isset( $files[ $last_path ] ) ) { if ( $this->verbose ) { /* translators: %s: Directory name. */ printf( "\n" . __( 'Found %s' ) . "<br />\n", $base . $last_path ); } return trailingslashit( $base . $last_path ); } /* * Prevent this function from looping again. * No need to proceed if we've just searched in `/`. */ if ( $loop || '/' === $base ) { return false; } /* * As an extra last resort, Change back to / if the folder wasn't found. * This comes into effect when the CWD is /home/user/ but WP is at /var/www/.... */ return $this->search_for_folder( $folder, '/', true ); } /** * Returns the *nix-style file permissions for a file. * * From the PHP documentation page for fileperms(). * * @link https://www.php.net/manual/en/function.fileperms.php * * @since 2.5.0 * * @param string $file String filename. * @return string The *nix-style representation of permissions. */ public function gethchmod( $file ) { $perms = intval( $this->getchmod( $file ), 8 ); if ( ( $perms & 0xC000 ) === 0xC000 ) { // Socket. $info = 's'; } elseif ( ( $perms & 0xA000 ) === 0xA000 ) { // Symbolic Link. $info = 'l'; } elseif ( ( $perms & 0x8000 ) === 0x8000 ) { // Regular. $info = '-'; } elseif ( ( $perms & 0x6000 ) === 0x6000 ) { // Block special. $info = 'b'; } elseif ( ( $perms & 0x4000 ) === 0x4000 ) { // Directory. $info = 'd'; } elseif ( ( $perms & 0x2000 ) === 0x2000 ) { // Character special. $info = 'c'; } elseif ( ( $perms & 0x1000 ) === 0x1000 ) { // FIFO pipe. $info = 'p'; } else { // Unknown. $info = 'u'; } // Owner. $info .= ( ( $perms & 0x0100 ) ? 'r' : '-' ); $info .= ( ( $perms & 0x0080 ) ? 'w' : '-' ); $info .= ( ( $perms & 0x0040 ) ? ( ( $perms & 0x0800 ) ? 's' : 'x' ) : ( ( $perms & 0x0800 ) ? 'S' : '-' ) ); // Group. $info .= ( ( $perms & 0x0020 ) ? 'r' : '-' ); $info .= ( ( $perms & 0x0010 ) ? 'w' : '-' ); $info .= ( ( $perms & 0x0008 ) ? ( ( $perms & 0x0400 ) ? 's' : 'x' ) : ( ( $perms & 0x0400 ) ? 'S' : '-' ) ); // World. $info .= ( ( $perms & 0x0004 ) ? 'r' : '-' ); $info .= ( ( $perms & 0x0002 ) ? 'w' : '-' ); $info .= ( ( $perms & 0x0001 ) ? ( ( $perms & 0x0200 ) ? 't' : 'x' ) : ( ( $perms & 0x0200 ) ? 'T' : '-' ) ); return $info; } /** * Gets the permissions of the specified file or filepath in their octal format. * * @since 2.5.0 * * @param string $file Path to the file. * @return string Mode of the file (the last 3 digits). */ public function getchmod( $file ) { return '777'; } /** * Converts *nix-style file permissions to an octal number. * * Converts '-rw-r--r--' to 0644 * From "info at rvgate dot nl"'s comment on the PHP documentation for chmod() * * @link https://www.php.net/manual/en/function.chmod.php#49614 * * @since 2.5.0 * * @param string $mode string The *nix-style file permissions. * @return string Octal representation of permissions. */ public function getnumchmodfromh( $mode ) { $realmode = ''; $legal = array( '', 'w', 'r', 'x', '-' ); $attarray = preg_split( '//', $mode ); for ( $i = 0, $c = count( $attarray ); $i < $c; $i++ ) { $key = array_search( $attarray[ $i ], $legal, true ); if ( $key ) { $realmode .= $legal[ $key ]; } } $mode = str_pad( $realmode, 10, '-', STR_PAD_LEFT ); $trans = array( '-' => '0', 'r' => '4', 'w' => '2', 'x' => '1', ); $mode = strtr( $mode, $trans ); $newmode = $mode[0]; $newmode .= $mode[1] + $mode[2] + $mode[3]; $newmode .= $mode[4] + $mode[5] + $mode[6]; $newmode .= $mode[7] + $mode[8] + $mode[9]; return $newmode; } /** * Determines if the string provided contains binary characters. * * @since 2.7.0 * * @param string $text String to test against. * @return bool True if string is binary, false otherwise. */ public function is_binary( $text ) { return (bool) preg_match( '|[^\x20-\x7E]|', $text ); // chr(32)..chr(127) } /** * Changes the owner of a file or directory. * * Default behavior is to do nothing, override this in your subclass, if desired. * * @since 2.5.0 * * @param string $file Path to the file or directory. * @param string|int $owner A user name or number. * @param bool $recursive Optional. If set to true, changes file owner recursively. * Default false. * @return bool True on success, false on failure. */ public function chown( $file, $owner, $recursive = false ) { return false; } /** * Connects filesystem. * * @since 2.5.0 * @abstract * * @return bool True on success, false on failure (always true for WP_Filesystem_Direct). */ public function connect() { return true; } /** * Reads entire file into a string. * * @since 2.5.0 * @abstract * * @param string $file Name of the file to read. * @return string|false Read data on success, false on failure. */ public function get_contents( $file ) { return false; } /** * Reads entire file into an array. * * @since 2.5.0 * @abstract * * @param string $file Path to the file. * @return array|false File contents in an array on success, false on failure. */ public function get_contents_array( $file ) { return false; } /** * Writes a string to a file. * * @since 2.5.0 * @abstract * * @param string $file Remote path to the file where to write the data. * @param string $contents The data to write. * @param int|false $mode Optional. The file permissions as octal number, usually 0644. * Default false. * @return bool True on success, false on failure. */ public function put_contents( $file, $contents, $mode = false ) { return false; } /** * Gets the current working directory. * * @since 2.5.0 * @abstract * * @return string|false The current working directory on success, false on failure. */ public function cwd() { return false; } /** * Changes current directory. * * @since 2.5.0 * @abstract * * @param string $dir The new current directory. * @return bool True on success, false on failure. */ public function chdir( $dir ) { return false; } /** * Changes the file group. * * @since 2.5.0 * @abstract * * @param string $file Path to the file. * @param string|int $group A group name or number. * @param bool $recursive Optional. If set to true, changes file group recursively. * Default false. * @return bool True on success, false on failure. */ public function chgrp( $file, $group, $recursive = false ) { return false; } /** * Changes filesystem permissions. * * @since 2.5.0 * @abstract * * @param string $file Path to the file. * @param int|false $mode Optional. The permissions as octal number, usually 0644 for files, * 0755 for directories. Default false. * @param bool $recursive Optional. If set to true, changes file permissions recursively. * Default false. * @return bool True on success, false on failure. */ public function chmod( $file, $mode = false, $recursive = false ) { return false; } /** * Gets the file owner. * * @since 2.5.0 * @abstract * * @param string $file Path to the file. * @return string|false Username of the owner on success, false on failure. */ public function owner( $file ) { return false; } /** * Gets the file's group. * * @since 2.5.0 * @abstract * * @param string $file Path to the file. * @return string|false The group on success, false on failure. */ public function group( $file ) { return false; } /** * Copies a file. * * @since 2.5.0 * @abstract * * @param string $source Path to the source file. * @param string $destination Path to the destination file. * @param bool $overwrite Optional. Whether to overwrite the destination file if it exists. * Default false. * @param int|false $mode Optional. The permissions as octal number, usually 0644 for files, * 0755 for dirs. Default false. * @return bool True on success, false on failure. */ public function copy( $source, $destination, $overwrite = false, $mode = false ) { return false; } /** * Moves a file. * * @since 2.5.0 * @abstract * * @param string $source Path to the source file. * @param string $destination Path to the destination file. * @param bool $overwrite Optional. Whether to overwrite the destination file if it exists. * Default false. * @return bool True on success, false on failure. */ public function move( $source, $destination, $overwrite = false ) { return false; } /** * Deletes a file or directory. * * @since 2.5.0 * @abstract * * @param string $file Path to the file or directory. * @param bool $recursive Optional. If set to true, deletes files and folders recursively. * Default false. * @param string|false $type Type of resource. 'f' for file, 'd' for directory. * Default false. * @return bool True on success, false on failure. */ public function delete( $file, $recursive = false, $type = false ) { return false; } /** * Checks if a file or directory exists. * * @since 2.5.0 * @abstract * * @param string $path Path to file or directory. * @return bool Whether $path exists or not. */ public function exists( $path ) { return false; } /** * Checks if resource is a file. * * @since 2.5.0 * @abstract * * @param string $file File path. * @return bool Whether $file is a file. */ public function is_file( $file ) { return false; } /** * Checks if resource is a directory. * * @since 2.5.0 * @abstract * * @param string $path Directory path. * @return bool Whether $path is a directory. */ public function is_dir( $path ) { return false; } /** * Checks if a file is readable. * * @since 2.5.0 * @abstract * * @param string $file Path to file. * @return bool Whether $file is readable. */ public function is_readable( $file ) { return false; } /** * Checks if a file or directory is writable. * * @since 2.5.0 * @abstract * * @param string $path Path to file or directory. * @return bool Whether $path is writable. */ public function is_writable( $path ) { return false; } /** * Gets the file's last access time. * * @since 2.5.0 * @abstract * * @param string $file Path to file. * @return int|false Unix timestamp representing last access time, false on failure. */ public function atime( $file ) { return false; } /** * Gets the file modification time. * * @since 2.5.0 * @abstract * * @param string $file Path to file. * @return int|false Unix timestamp representing modification time, false on failure. */ public function mtime( $file ) { return false; } /** * Gets the file size (in bytes). * * @since 2.5.0 * @abstract * * @param string $file Path to file. * @return int|false Size of the file in bytes on success, false on failure. */ public function size( $file ) { return false; } /** * Sets the access and modification times of a file. * * Note: If $file doesn't exist, it will be created. * * @since 2.5.0 * @abstract * * @param string $file Path to file. * @param int $time Optional. Modified time to set for file. * Default 0. * @param int $atime Optional. Access time to set for file. * Default 0. * @return bool True on success, false on failure. */ public function touch( $file, $time = 0, $atime = 0 ) { return false; } /** * Creates a directory. * * @since 2.5.0 * @abstract * * @param string $path Path for new directory. * @param int|false $chmod Optional. The permissions as octal number (or false to skip chmod). * Default false. * @param string|int|false $chown Optional. A user name or number (or false to skip chown). * Default false. * @param string|int|false $chgrp Optional. A group name or number (or false to skip chgrp). * Default false. * @return bool True on success, false on failure. */ public function mkdir( $path, $chmod = false, $chown = false, $chgrp = false ) { return false; } /** * Deletes a directory. * * @since 2.5.0 * @abstract * * @param string $path Path to directory. * @param bool $recursive Optional. Whether to recursively remove files/directories. * Default false. * @return bool True on success, false on failure. */ public function rmdir( $path, $recursive = false ) { return false; } /** * Gets details for files in a directory or a specific file. * * @since 2.5.0 * @abstract * * @param string $path Path to directory or file. * @param bool $include_hidden Optional. Whether to include details of hidden ("." prefixed) files. * Default true. * @param bool $recursive Optional. Whether to recursively include file details in nested directories. * Default false. * @return array|false { * Array of arrays containing file information. False if unable to list directory contents. * * @type array ...$0 { * Array of file information. Note that some elements may not be available on all filesystems. * * @type string $name Name of the file or directory. * @type string $perms *nix representation of permissions. * @type string $permsn Octal representation of permissions. * @type int|string|false $number File number. May be a numeric string. False if not available. * @type string|false $owner Owner name or ID, or false if not available. * @type string|false $group File permissions group, or false if not available. * @type int|string|false $size Size of file in bytes. May be a numeric string. * False if not available. * @type int|string|false $lastmodunix Last modified unix timestamp. May be a numeric string. * False if not available. * @type string|false $lastmod Last modified month (3 letters) and day (without leading 0), or * false if not available. * @type string|false $time Last modified time, or false if not available. * @type string $type Type of resource. 'f' for file, 'd' for directory, 'l' for link. * @type array|false $files If a directory and `$recursive` is true, contains another array of * files. False if unable to list directory contents. * } * } */ public function dirlist( $path, $include_hidden = true, $recursive = false ) { return false; } } includes/noop.php 0000644 00000002174 15135536347 0010056 0 ustar 00 <?php /** * Noop functions for load-scripts.php and load-styles.php. * * @package WordPress * @subpackage Administration * @since 4.4.0 */ /** * @ignore */ function __() {} /** * @ignore */ function _x() {} /** * @ignore */ function add_filter() {} /** * @ignore */ function has_filter() { return false; } /** * @ignore */ function esc_attr() {} /** * @ignore */ function apply_filters() {} /** * @ignore */ function get_option() {} /** * @ignore */ function is_lighttpd_before_150() {} /** * @ignore */ function add_action() {} /** * @ignore */ function did_action() {} /** * @ignore */ function do_action_ref_array() {} /** * @ignore */ function get_bloginfo() {} /** * @ignore */ function is_admin() { return true; } /** * @ignore */ function site_url() {} /** * @ignore */ function admin_url() {} /** * @ignore */ function home_url() {} /** * @ignore */ function includes_url() {} /** * @ignore */ function wp_guess_url() {} function get_file( $path ) { $path = realpath( $path ); if ( ! $path || ! @is_file( $path ) ) { return ''; } return @file_get_contents( $path ); } includes/misc.php 0000644 00000132516 15135536347 0010042 0 ustar 00 <?php /** * Misc WordPress Administration API. * * @package WordPress * @subpackage Administration */ /** * Returns whether the server is running Apache with the mod_rewrite module loaded. * * @since 2.0.0 * * @return bool Whether the server is running Apache with the mod_rewrite module loaded. */ function got_mod_rewrite() { $got_rewrite = apache_mod_loaded( 'mod_rewrite', true ); /** * Filters whether Apache and mod_rewrite are present. * * This filter was previously used to force URL rewriting for other servers, * like nginx. Use the {@see 'got_url_rewrite'} filter in got_url_rewrite() instead. * * @since 2.5.0 * * @see got_url_rewrite() * * @param bool $got_rewrite Whether Apache and mod_rewrite are present. */ return apply_filters( 'got_rewrite', $got_rewrite ); } /** * Returns whether the server supports URL rewriting. * * Detects Apache's mod_rewrite, IIS 7.0+ permalink support, and nginx. * * @since 3.7.0 * * @global bool $is_nginx * @global bool $is_caddy * * @return bool Whether the server supports URL rewriting. */ function got_url_rewrite() { $got_url_rewrite = ( got_mod_rewrite() || $GLOBALS['is_nginx'] || $GLOBALS['is_caddy'] || iis7_supports_permalinks() ); /** * Filters whether URL rewriting is available. * * @since 3.7.0 * * @param bool $got_url_rewrite Whether URL rewriting is available. */ return apply_filters( 'got_url_rewrite', $got_url_rewrite ); } /** * Extracts strings from between the BEGIN and END markers in the .htaccess file. * * @since 1.5.0 * * @param string $filename Filename to extract the strings from. * @param string $marker The marker to extract the strings from. * @return string[] An array of strings from a file (.htaccess) from between BEGIN and END markers. */ function extract_from_markers( $filename, $marker ) { $result = array(); if ( ! file_exists( $filename ) ) { return $result; } $markerdata = explode( "\n", implode( '', file( $filename ) ) ); $state = false; foreach ( $markerdata as $markerline ) { if ( str_contains( $markerline, '# END ' . $marker ) ) { $state = false; } if ( $state ) { if ( str_starts_with( $markerline, '#' ) ) { continue; } $result[] = $markerline; } if ( str_contains( $markerline, '# BEGIN ' . $marker ) ) { $state = true; } } return $result; } /** * Inserts an array of strings into a file (.htaccess), placing it between * BEGIN and END markers. * * Replaces existing marked info. Retains surrounding * data. Creates file if none exists. * * @since 1.5.0 * * @param string $filename Filename to alter. * @param string $marker The marker to alter. * @param array|string $insertion The new content to insert. * @return bool True on write success, false on failure. */ function insert_with_markers( $filename, $marker, $insertion ) { if ( ! file_exists( $filename ) ) { if ( ! is_writable( dirname( $filename ) ) ) { return false; } if ( ! touch( $filename ) ) { return false; } // Make sure the file is created with a minimum set of permissions. $perms = fileperms( $filename ); if ( $perms ) { chmod( $filename, $perms | 0644 ); } } elseif ( ! is_writable( $filename ) ) { return false; } if ( ! is_array( $insertion ) ) { $insertion = explode( "\n", $insertion ); } $switched_locale = switch_to_locale( get_locale() ); $instructions = sprintf( /* translators: 1: Marker. */ __( 'The directives (lines) between "BEGIN %1$s" and "END %1$s" are dynamically generated, and should only be modified via WordPress filters. Any changes to the directives between these markers will be overwritten.' ), $marker ); $instructions = explode( "\n", $instructions ); foreach ( $instructions as $line => $text ) { $instructions[ $line ] = '# ' . $text; } /** * Filters the inline instructions inserted before the dynamically generated content. * * @since 5.3.0 * * @param string[] $instructions Array of lines with inline instructions. * @param string $marker The marker being inserted. */ $instructions = apply_filters( 'insert_with_markers_inline_instructions', $instructions, $marker ); if ( $switched_locale ) { restore_previous_locale(); } $insertion = array_merge( $instructions, $insertion ); $start_marker = "# BEGIN {$marker}"; $end_marker = "# END {$marker}"; $fp = fopen( $filename, 'r+' ); if ( ! $fp ) { return false; } // Attempt to get a lock. If the filesystem supports locking, this will block until the lock is acquired. flock( $fp, LOCK_EX ); $lines = array(); while ( ! feof( $fp ) ) { $lines[] = rtrim( fgets( $fp ), "\r\n" ); } // Split out the existing file into the preceding lines, and those that appear after the marker. $pre_lines = array(); $post_lines = array(); $existing_lines = array(); $found_marker = false; $found_end_marker = false; foreach ( $lines as $line ) { if ( ! $found_marker && str_contains( $line, $start_marker ) ) { $found_marker = true; continue; } elseif ( ! $found_end_marker && str_contains( $line, $end_marker ) ) { $found_end_marker = true; continue; } if ( ! $found_marker ) { $pre_lines[] = $line; } elseif ( $found_marker && $found_end_marker ) { $post_lines[] = $line; } else { $existing_lines[] = $line; } } // Check to see if there was a change. if ( $existing_lines === $insertion ) { flock( $fp, LOCK_UN ); fclose( $fp ); return true; } // Generate the new file data. $new_file_data = implode( "\n", array_merge( $pre_lines, array( $start_marker ), $insertion, array( $end_marker ), $post_lines ) ); // Write to the start of the file, and truncate it to that length. fseek( $fp, 0 ); $bytes = fwrite( $fp, $new_file_data ); if ( $bytes ) { ftruncate( $fp, ftell( $fp ) ); } fflush( $fp ); flock( $fp, LOCK_UN ); fclose( $fp ); return (bool) $bytes; } /** * Updates the htaccess file with the current rules if it is writable. * * Always writes to the file if it exists and is writable to ensure that we * blank out old rules. * * @since 1.5.0 * * @global WP_Rewrite $wp_rewrite WordPress rewrite component. * * @return bool|null True on write success, false on failure. Null in multisite. */ function save_mod_rewrite_rules() { global $wp_rewrite; if ( is_multisite() ) { return; } // Ensure get_home_path() is declared. require_once ABSPATH . 'wp-admin/includes/file.php'; $home_path = get_home_path(); $htaccess_file = $home_path . '.htaccess'; /* * If the file doesn't already exist check for write access to the directory * and whether we have some rules. Else check for write access to the file. */ if ( ! file_exists( $htaccess_file ) && is_writable( $home_path ) && $wp_rewrite->using_mod_rewrite_permalinks() || is_writable( $htaccess_file ) ) { if ( got_mod_rewrite() ) { $rules = explode( "\n", $wp_rewrite->mod_rewrite_rules() ); return insert_with_markers( $htaccess_file, 'WordPress', $rules ); } } return false; } /** * Updates the IIS web.config file with the current rules if it is writable. * If the permalinks do not require rewrite rules then the rules are deleted from the web.config file. * * @since 2.8.0 * * @global WP_Rewrite $wp_rewrite WordPress rewrite component. * * @return bool|null True on write success, false on failure. Null in multisite. */ function iis7_save_url_rewrite_rules() { global $wp_rewrite; if ( is_multisite() ) { return; } // Ensure get_home_path() is declared. require_once ABSPATH . 'wp-admin/includes/file.php'; $home_path = get_home_path(); $web_config_file = $home_path . 'web.config'; // Using win_is_writable() instead of is_writable() because of a bug in Windows PHP. if ( iis7_supports_permalinks() && ( ! file_exists( $web_config_file ) && win_is_writable( $home_path ) && $wp_rewrite->using_mod_rewrite_permalinks() || win_is_writable( $web_config_file ) ) ) { $rule = $wp_rewrite->iis7_url_rewrite_rules( false ); if ( ! empty( $rule ) ) { return iis7_add_rewrite_rule( $web_config_file, $rule ); } else { return iis7_delete_rewrite_rule( $web_config_file ); } } return false; } /** * Updates the "recently-edited" file for the plugin or theme file editor. * * @since 1.5.0 * * @param string $file */ function update_recently_edited( $file ) { $oldfiles = (array) get_option( 'recently_edited' ); if ( $oldfiles ) { $oldfiles = array_reverse( $oldfiles ); $oldfiles[] = $file; $oldfiles = array_reverse( $oldfiles ); $oldfiles = array_unique( $oldfiles ); if ( 5 < count( $oldfiles ) ) { array_pop( $oldfiles ); } } else { $oldfiles[] = $file; } update_option( 'recently_edited', $oldfiles ); } /** * Makes a tree structure for the theme file editor's file list. * * @since 4.9.0 * @access private * * @param array $allowed_files List of theme file paths. * @return array Tree structure for listing theme files. */ function wp_make_theme_file_tree( $allowed_files ) { $tree_list = array(); foreach ( $allowed_files as $file_name => $absolute_filename ) { $list = explode( '/', $file_name ); $last_dir = &$tree_list; foreach ( $list as $dir ) { $last_dir =& $last_dir[ $dir ]; } $last_dir = $file_name; } return $tree_list; } /** * Outputs the formatted file list for the theme file editor. * * @since 4.9.0 * @access private * * @global string $relative_file Name of the file being edited relative to the * theme directory. * @global string $stylesheet The stylesheet name of the theme being edited. * * @param array|string $tree List of file/folder paths, or filename. * @param int $level The aria-level for the current iteration. * @param int $size The aria-setsize for the current iteration. * @param int $index The aria-posinset for the current iteration. */ function wp_print_theme_file_tree( $tree, $level = 2, $size = 1, $index = 1 ) { global $relative_file, $stylesheet; if ( is_array( $tree ) ) { $index = 0; $size = count( $tree ); foreach ( $tree as $label => $theme_file ) : ++$index; if ( ! is_array( $theme_file ) ) { wp_print_theme_file_tree( $theme_file, $level, $index, $size ); continue; } ?> <li role="treeitem" aria-expanded="true" tabindex="-1" aria-level="<?php echo esc_attr( $level ); ?>" aria-setsize="<?php echo esc_attr( $size ); ?>" aria-posinset="<?php echo esc_attr( $index ); ?>"> <span class="folder-label"><?php echo esc_html( $label ); ?> <span class="screen-reader-text"> <?php /* translators: Hidden accessibility text. */ _e( 'folder' ); ?> </span><span aria-hidden="true" class="icon"></span></span> <ul role="group" class="tree-folder"><?php wp_print_theme_file_tree( $theme_file, $level + 1, $index, $size ); ?></ul> </li> <?php endforeach; } else { $filename = $tree; $url = add_query_arg( array( 'file' => rawurlencode( $tree ), 'theme' => rawurlencode( $stylesheet ), ), self_admin_url( 'theme-editor.php' ) ); ?> <li role="none" class="<?php echo esc_attr( $relative_file === $filename ? 'current-file' : '' ); ?>"> <a role="treeitem" tabindex="<?php echo esc_attr( $relative_file === $filename ? '0' : '-1' ); ?>" href="<?php echo esc_url( $url ); ?>" aria-level="<?php echo esc_attr( $level ); ?>" aria-setsize="<?php echo esc_attr( $size ); ?>" aria-posinset="<?php echo esc_attr( $index ); ?>"> <?php $file_description = esc_html( get_file_description( $filename ) ); if ( $file_description !== $filename && wp_basename( $filename ) !== $file_description ) { $file_description .= '<br /><span class="nonessential">(' . esc_html( $filename ) . ')</span>'; } if ( $relative_file === $filename ) { echo '<span class="notice notice-info">' . $file_description . '</span>'; } else { echo $file_description; } ?> </a> </li> <?php } } /** * Makes a tree structure for the plugin file editor's file list. * * @since 4.9.0 * @access private * * @param array $plugin_editable_files List of plugin file paths. * @return array Tree structure for listing plugin files. */ function wp_make_plugin_file_tree( $plugin_editable_files ) { $tree_list = array(); foreach ( $plugin_editable_files as $plugin_file ) { $list = explode( '/', preg_replace( '#^.+?/#', '', $plugin_file ) ); $last_dir = &$tree_list; foreach ( $list as $dir ) { $last_dir =& $last_dir[ $dir ]; } $last_dir = $plugin_file; } return $tree_list; } /** * Outputs the formatted file list for the plugin file editor. * * @since 4.9.0 * @access private * * @param array|string $tree List of file/folder paths, or filename. * @param string $label Name of file or folder to print. * @param int $level The aria-level for the current iteration. * @param int $size The aria-setsize for the current iteration. * @param int $index The aria-posinset for the current iteration. */ function wp_print_plugin_file_tree( $tree, $label = '', $level = 2, $size = 1, $index = 1 ) { global $file, $plugin; if ( is_array( $tree ) ) { $index = 0; $size = count( $tree ); foreach ( $tree as $label => $plugin_file ) : ++$index; if ( ! is_array( $plugin_file ) ) { wp_print_plugin_file_tree( $plugin_file, $label, $level, $index, $size ); continue; } ?> <li role="treeitem" aria-expanded="true" tabindex="-1" aria-level="<?php echo esc_attr( $level ); ?>" aria-setsize="<?php echo esc_attr( $size ); ?>" aria-posinset="<?php echo esc_attr( $index ); ?>"> <span class="folder-label"><?php echo esc_html( $label ); ?> <span class="screen-reader-text"> <?php /* translators: Hidden accessibility text. */ _e( 'folder' ); ?> </span><span aria-hidden="true" class="icon"></span></span> <ul role="group" class="tree-folder"><?php wp_print_plugin_file_tree( $plugin_file, '', $level + 1, $index, $size ); ?></ul> </li> <?php endforeach; } else { $url = add_query_arg( array( 'file' => rawurlencode( $tree ), 'plugin' => rawurlencode( $plugin ), ), self_admin_url( 'plugin-editor.php' ) ); ?> <li role="none" class="<?php echo esc_attr( $file === $tree ? 'current-file' : '' ); ?>"> <a role="treeitem" tabindex="<?php echo esc_attr( $file === $tree ? '0' : '-1' ); ?>" href="<?php echo esc_url( $url ); ?>" aria-level="<?php echo esc_attr( $level ); ?>" aria-setsize="<?php echo esc_attr( $size ); ?>" aria-posinset="<?php echo esc_attr( $index ); ?>"> <?php if ( $file === $tree ) { echo '<span class="notice notice-info">' . esc_html( $label ) . '</span>'; } else { echo esc_html( $label ); } ?> </a> </li> <?php } } /** * Flushes rewrite rules if siteurl, home or page_on_front changed. * * @since 2.1.0 * * @param string $old_value * @param string $value */ function update_home_siteurl( $old_value, $value ) { if ( wp_installing() ) { return; } if ( is_multisite() && ms_is_switched() ) { delete_option( 'rewrite_rules' ); } else { flush_rewrite_rules(); } } /** * Resets global variables based on $_GET and $_POST. * * This function resets global variables based on the names passed * in the $vars array to the value of $_POST[$var] or $_GET[$var] or '' * if neither is defined. * * @since 2.0.0 * * @param array $vars An array of globals to reset. */ function wp_reset_vars( $vars ) { foreach ( $vars as $var ) { if ( empty( $_POST[ $var ] ) ) { if ( empty( $_GET[ $var ] ) ) { $GLOBALS[ $var ] = ''; } else { $GLOBALS[ $var ] = $_GET[ $var ]; } } else { $GLOBALS[ $var ] = $_POST[ $var ]; } } } /** * Displays the given administration message. * * @since 2.1.0 * * @param string|WP_Error $message */ function show_message( $message ) { if ( is_wp_error( $message ) ) { if ( $message->get_error_data() && is_string( $message->get_error_data() ) ) { $message = $message->get_error_message() . ': ' . $message->get_error_data(); } else { $message = $message->get_error_message(); } } echo "<p>$message</p>\n"; wp_ob_end_flush_all(); flush(); } /** * @since 2.8.0 * * @param string $content * @return array */ function wp_doc_link_parse( $content ) { if ( ! is_string( $content ) || empty( $content ) ) { return array(); } if ( ! function_exists( 'token_get_all' ) ) { return array(); } $tokens = token_get_all( $content ); $count = count( $tokens ); $functions = array(); $ignore_functions = array(); for ( $t = 0; $t < $count - 2; $t++ ) { if ( ! is_array( $tokens[ $t ] ) ) { continue; } if ( T_STRING === $tokens[ $t ][0] && ( '(' === $tokens[ $t + 1 ] || '(' === $tokens[ $t + 2 ] ) ) { // If it's a function or class defined locally, there's not going to be any docs available. if ( ( isset( $tokens[ $t - 2 ][1] ) && in_array( $tokens[ $t - 2 ][1], array( 'function', 'class' ), true ) ) || ( isset( $tokens[ $t - 2 ][0] ) && T_OBJECT_OPERATOR === $tokens[ $t - 1 ][0] ) ) { $ignore_functions[] = $tokens[ $t ][1]; } // Add this to our stack of unique references. $functions[] = $tokens[ $t ][1]; } } $functions = array_unique( $functions ); sort( $functions ); /** * Filters the list of functions and classes to be ignored from the documentation lookup. * * @since 2.8.0 * * @param string[] $ignore_functions Array of names of functions and classes to be ignored. */ $ignore_functions = apply_filters( 'documentation_ignore_functions', $ignore_functions ); $ignore_functions = array_unique( $ignore_functions ); $output = array(); foreach ( $functions as $function ) { if ( in_array( $function, $ignore_functions, true ) ) { continue; } $output[] = $function; } return $output; } /** * Saves option for number of rows when listing posts, pages, comments, etc. * * @since 2.8.0 */ function set_screen_options() { if ( ! isset( $_POST['wp_screen_options'] ) || ! is_array( $_POST['wp_screen_options'] ) ) { return; } check_admin_referer( 'screen-options-nonce', 'screenoptionnonce' ); $user = wp_get_current_user(); if ( ! $user ) { return; } $option = $_POST['wp_screen_options']['option']; $value = $_POST['wp_screen_options']['value']; if ( sanitize_key( $option ) !== $option ) { return; } $map_option = $option; $type = str_replace( 'edit_', '', $map_option ); $type = str_replace( '_per_page', '', $type ); if ( in_array( $type, get_taxonomies(), true ) ) { $map_option = 'edit_tags_per_page'; } elseif ( in_array( $type, get_post_types(), true ) ) { $map_option = 'edit_per_page'; } else { $option = str_replace( '-', '_', $option ); } switch ( $map_option ) { case 'edit_per_page': case 'users_per_page': case 'edit_comments_per_page': case 'upload_per_page': case 'edit_tags_per_page': case 'plugins_per_page': case 'export_personal_data_requests_per_page': case 'remove_personal_data_requests_per_page': // Network admin. case 'sites_network_per_page': case 'users_network_per_page': case 'site_users_network_per_page': case 'plugins_network_per_page': case 'themes_network_per_page': case 'site_themes_network_per_page': $value = (int) $value; if ( $value < 1 || $value > 999 ) { return; } break; default: $screen_option = false; if ( str_ends_with( $option, '_page' ) || 'layout_columns' === $option ) { /** * Filters a screen option value before it is set. * * The filter can also be used to modify non-standard [items]_per_page * settings. See the parent function for a full list of standard options. * * Returning false from the filter will skip saving the current option. * * @since 2.8.0 * @since 5.4.2 Only applied to options ending with '_page', * or the 'layout_columns' option. * * @see set_screen_options() * * @param mixed $screen_option The value to save instead of the option value. * Default false (to skip saving the current option). * @param string $option The option name. * @param int $value The option value. */ $screen_option = apply_filters( 'set-screen-option', $screen_option, $option, $value ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores } /** * Filters a screen option value before it is set. * * The dynamic portion of the hook name, `$option`, refers to the option name. * * Returning false from the filter will skip saving the current option. * * @since 5.4.2 * * @see set_screen_options() * * @param mixed $screen_option The value to save instead of the option value. * Default false (to skip saving the current option). * @param string $option The option name. * @param int $value The option value. */ $value = apply_filters( "set_screen_option_{$option}", $screen_option, $option, $value ); if ( false === $value ) { return; } break; } update_user_meta( $user->ID, $option, $value ); $url = remove_query_arg( array( 'pagenum', 'apage', 'paged' ), wp_get_referer() ); if ( isset( $_POST['mode'] ) ) { $url = add_query_arg( array( 'mode' => $_POST['mode'] ), $url ); } wp_safe_redirect( $url ); exit; } /** * Checks if rewrite rule for WordPress already exists in the IIS 7+ configuration file. * * @since 2.8.0 * * @param string $filename The file path to the configuration file. * @return bool */ function iis7_rewrite_rule_exists( $filename ) { if ( ! file_exists( $filename ) ) { return false; } if ( ! class_exists( 'DOMDocument', false ) ) { return false; } $doc = new DOMDocument(); if ( $doc->load( $filename ) === false ) { return false; } $xpath = new DOMXPath( $doc ); $rules = $xpath->query( '/configuration/system.webServer/rewrite/rules/rule[starts-with(@name,\'wordpress\')] | /configuration/system.webServer/rewrite/rules/rule[starts-with(@name,\'WordPress\')]' ); if ( 0 === $rules->length ) { return false; } return true; } /** * Deletes WordPress rewrite rule from web.config file if it exists there. * * @since 2.8.0 * * @param string $filename Name of the configuration file. * @return bool */ function iis7_delete_rewrite_rule( $filename ) { // If configuration file does not exist then rules also do not exist, so there is nothing to delete. if ( ! file_exists( $filename ) ) { return true; } if ( ! class_exists( 'DOMDocument', false ) ) { return false; } $doc = new DOMDocument(); $doc->preserveWhiteSpace = false; if ( $doc->load( $filename ) === false ) { return false; } $xpath = new DOMXPath( $doc ); $rules = $xpath->query( '/configuration/system.webServer/rewrite/rules/rule[starts-with(@name,\'wordpress\')] | /configuration/system.webServer/rewrite/rules/rule[starts-with(@name,\'WordPress\')]' ); if ( $rules->length > 0 ) { $child = $rules->item( 0 ); $parent = $child->parentNode; $parent->removeChild( $child ); $doc->formatOutput = true; saveDomDocument( $doc, $filename ); } return true; } /** * Adds WordPress rewrite rule to the IIS 7+ configuration file. * * @since 2.8.0 * * @param string $filename The file path to the configuration file. * @param string $rewrite_rule The XML fragment with URL Rewrite rule. * @return bool */ function iis7_add_rewrite_rule( $filename, $rewrite_rule ) { if ( ! class_exists( 'DOMDocument', false ) ) { return false; } // If configuration file does not exist then we create one. if ( ! file_exists( $filename ) ) { $fp = fopen( $filename, 'w' ); fwrite( $fp, '<configuration/>' ); fclose( $fp ); } $doc = new DOMDocument(); $doc->preserveWhiteSpace = false; if ( $doc->load( $filename ) === false ) { return false; } $xpath = new DOMXPath( $doc ); // First check if the rule already exists as in that case there is no need to re-add it. $wordpress_rules = $xpath->query( '/configuration/system.webServer/rewrite/rules/rule[starts-with(@name,\'wordpress\')] | /configuration/system.webServer/rewrite/rules/rule[starts-with(@name,\'WordPress\')]' ); if ( $wordpress_rules->length > 0 ) { return true; } // Check the XPath to the rewrite rule and create XML nodes if they do not exist. $xml_nodes = $xpath->query( '/configuration/system.webServer/rewrite/rules' ); if ( $xml_nodes->length > 0 ) { $rules_node = $xml_nodes->item( 0 ); } else { $rules_node = $doc->createElement( 'rules' ); $xml_nodes = $xpath->query( '/configuration/system.webServer/rewrite' ); if ( $xml_nodes->length > 0 ) { $rewrite_node = $xml_nodes->item( 0 ); $rewrite_node->appendChild( $rules_node ); } else { $rewrite_node = $doc->createElement( 'rewrite' ); $rewrite_node->appendChild( $rules_node ); $xml_nodes = $xpath->query( '/configuration/system.webServer' ); if ( $xml_nodes->length > 0 ) { $system_web_server_node = $xml_nodes->item( 0 ); $system_web_server_node->appendChild( $rewrite_node ); } else { $system_web_server_node = $doc->createElement( 'system.webServer' ); $system_web_server_node->appendChild( $rewrite_node ); $xml_nodes = $xpath->query( '/configuration' ); if ( $xml_nodes->length > 0 ) { $config_node = $xml_nodes->item( 0 ); $config_node->appendChild( $system_web_server_node ); } else { $config_node = $doc->createElement( 'configuration' ); $doc->appendChild( $config_node ); $config_node->appendChild( $system_web_server_node ); } } } } $rule_fragment = $doc->createDocumentFragment(); $rule_fragment->appendXML( $rewrite_rule ); $rules_node->appendChild( $rule_fragment ); $doc->encoding = 'UTF-8'; $doc->formatOutput = true; saveDomDocument( $doc, $filename ); return true; } /** * Saves the XML document into a file. * * @since 2.8.0 * * @param DOMDocument $doc * @param string $filename */ function saveDomDocument( $doc, $filename ) { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName.FunctionNameInvalid $config = $doc->saveXML(); $config = preg_replace( "/([^\r])\n/", "$1\r\n", $config ); $fp = fopen( $filename, 'w' ); fwrite( $fp, $config ); fclose( $fp ); } /** * Displays the default admin color scheme picker (Used in user-edit.php). * * @since 3.0.0 * * @global array $_wp_admin_css_colors * * @param int $user_id User ID. */ function admin_color_scheme_picker( $user_id ) { global $_wp_admin_css_colors; ksort( $_wp_admin_css_colors ); if ( isset( $_wp_admin_css_colors['fresh'] ) ) { // Set Default ('fresh') and Light should go first. $_wp_admin_css_colors = array_filter( array_merge( array( 'fresh' => '', 'light' => '', 'modern' => '', ), $_wp_admin_css_colors ) ); } $current_color = get_user_option( 'admin_color', $user_id ); if ( empty( $current_color ) || ! isset( $_wp_admin_css_colors[ $current_color ] ) ) { $current_color = 'fresh'; } ?> <fieldset id="color-picker" class="scheme-list"> <legend class="screen-reader-text"><span> <?php /* translators: Hidden accessibility text. */ _e( 'Admin Color Scheme' ); ?> </span></legend> <?php wp_nonce_field( 'save-color-scheme', 'color-nonce', false ); foreach ( $_wp_admin_css_colors as $color => $color_info ) : ?> <div class="color-option <?php echo ( $color === $current_color ) ? 'selected' : ''; ?>"> <input name="admin_color" id="admin_color_<?php echo esc_attr( $color ); ?>" type="radio" value="<?php echo esc_attr( $color ); ?>" class="tog" <?php checked( $color, $current_color ); ?> /> <input type="hidden" class="css_url" value="<?php echo esc_url( $color_info->url ); ?>" /> <input type="hidden" class="icon_colors" value="<?php echo esc_attr( wp_json_encode( array( 'icons' => $color_info->icon_colors ) ) ); ?>" /> <label for="admin_color_<?php echo esc_attr( $color ); ?>"><?php echo esc_html( $color_info->name ); ?></label> <div class="color-palette"> <?php foreach ( $color_info->colors as $html_color ) { ?> <div class="color-palette-shade" style="background-color: <?php echo esc_attr( $html_color ); ?>"> </div> <?php } ?> </div> </div> <?php endforeach; ?> </fieldset> <?php } /** * * @global array $_wp_admin_css_colors */ function wp_color_scheme_settings() { global $_wp_admin_css_colors; $color_scheme = get_user_option( 'admin_color' ); // It's possible to have a color scheme set that is no longer registered. if ( empty( $_wp_admin_css_colors[ $color_scheme ] ) ) { $color_scheme = 'fresh'; } if ( ! empty( $_wp_admin_css_colors[ $color_scheme ]->icon_colors ) ) { $icon_colors = $_wp_admin_css_colors[ $color_scheme ]->icon_colors; } elseif ( ! empty( $_wp_admin_css_colors['fresh']->icon_colors ) ) { $icon_colors = $_wp_admin_css_colors['fresh']->icon_colors; } else { // Fall back to the default set of icon colors if the default scheme is missing. $icon_colors = array( 'base' => '#a7aaad', 'focus' => '#72aee6', 'current' => '#fff', ); } echo '<script type="text/javascript">var _wpColorScheme = ' . wp_json_encode( array( 'icons' => $icon_colors ) ) . ";</script>\n"; } /** * Displays the viewport meta in the admin. * * @since 5.5.0 */ function wp_admin_viewport_meta() { /** * Filters the viewport meta in the admin. * * @since 5.5.0 * * @param string $viewport_meta The viewport meta. */ $viewport_meta = apply_filters( 'admin_viewport_meta', 'width=device-width,initial-scale=1.0' ); if ( empty( $viewport_meta ) ) { return; } echo '<meta name="viewport" content="' . esc_attr( $viewport_meta ) . '">'; } /** * Adds viewport meta for mobile in Customizer. * * Hooked to the {@see 'admin_viewport_meta'} filter. * * @since 5.5.0 * * @param string $viewport_meta The viewport meta. * @return string Filtered viewport meta. */ function _customizer_mobile_viewport_meta( $viewport_meta ) { return trim( $viewport_meta, ',' ) . ',minimum-scale=0.5,maximum-scale=1.2'; } /** * Checks lock status for posts displayed on the Posts screen. * * @since 3.6.0 * * @param array $response The Heartbeat response. * @param array $data The $_POST data sent. * @param string $screen_id The screen ID. * @return array The Heartbeat response. */ function wp_check_locked_posts( $response, $data, $screen_id ) { $checked = array(); if ( array_key_exists( 'wp-check-locked-posts', $data ) && is_array( $data['wp-check-locked-posts'] ) ) { foreach ( $data['wp-check-locked-posts'] as $key ) { $post_id = absint( substr( $key, 5 ) ); if ( ! $post_id ) { continue; } $user_id = wp_check_post_lock( $post_id ); if ( $user_id ) { $user = get_userdata( $user_id ); if ( $user && current_user_can( 'edit_post', $post_id ) ) { $send = array( 'name' => $user->display_name, /* translators: %s: User's display name. */ 'text' => sprintf( __( '%s is currently editing' ), $user->display_name ), ); if ( get_option( 'show_avatars' ) ) { $send['avatar_src'] = get_avatar_url( $user->ID, array( 'size' => 18 ) ); $send['avatar_src_2x'] = get_avatar_url( $user->ID, array( 'size' => 36 ) ); } $checked[ $key ] = $send; } } } } if ( ! empty( $checked ) ) { $response['wp-check-locked-posts'] = $checked; } return $response; } /** * Checks lock status on the New/Edit Post screen and refresh the lock. * * @since 3.6.0 * * @param array $response The Heartbeat response. * @param array $data The $_POST data sent. * @param string $screen_id The screen ID. * @return array The Heartbeat response. */ function wp_refresh_post_lock( $response, $data, $screen_id ) { if ( array_key_exists( 'wp-refresh-post-lock', $data ) ) { $received = $data['wp-refresh-post-lock']; $send = array(); $post_id = absint( $received['post_id'] ); if ( ! $post_id ) { return $response; } if ( ! current_user_can( 'edit_post', $post_id ) ) { return $response; } $user_id = wp_check_post_lock( $post_id ); $user = get_userdata( $user_id ); if ( $user ) { $error = array( 'name' => $user->display_name, /* translators: %s: User's display name. */ 'text' => sprintf( __( '%s has taken over and is currently editing.' ), $user->display_name ), ); if ( get_option( 'show_avatars' ) ) { $error['avatar_src'] = get_avatar_url( $user->ID, array( 'size' => 64 ) ); $error['avatar_src_2x'] = get_avatar_url( $user->ID, array( 'size' => 128 ) ); } $send['lock_error'] = $error; } else { $new_lock = wp_set_post_lock( $post_id ); if ( $new_lock ) { $send['new_lock'] = implode( ':', $new_lock ); } } $response['wp-refresh-post-lock'] = $send; } return $response; } /** * Checks nonce expiration on the New/Edit Post screen and refresh if needed. * * @since 3.6.0 * * @param array $response The Heartbeat response. * @param array $data The $_POST data sent. * @param string $screen_id The screen ID. * @return array The Heartbeat response. */ function wp_refresh_post_nonces( $response, $data, $screen_id ) { if ( array_key_exists( 'wp-refresh-post-nonces', $data ) ) { $received = $data['wp-refresh-post-nonces']; $response['wp-refresh-post-nonces'] = array( 'check' => 1 ); $post_id = absint( $received['post_id'] ); if ( ! $post_id ) { return $response; } if ( ! current_user_can( 'edit_post', $post_id ) ) { return $response; } $response['wp-refresh-post-nonces'] = array( 'replace' => array( 'getpermalinknonce' => wp_create_nonce( 'getpermalink' ), 'samplepermalinknonce' => wp_create_nonce( 'samplepermalink' ), 'closedpostboxesnonce' => wp_create_nonce( 'closedpostboxes' ), '_ajax_linking_nonce' => wp_create_nonce( 'internal-linking' ), '_wpnonce' => wp_create_nonce( 'update-post_' . $post_id ), ), ); } return $response; } /** * Refresh nonces used with meta boxes in the block editor. * * @since 6.1.0 * * @param array $response The Heartbeat response. * @param array $data The $_POST data sent. * @return array The Heartbeat response. */ function wp_refresh_metabox_loader_nonces( $response, $data ) { if ( empty( $data['wp-refresh-metabox-loader-nonces'] ) ) { return $response; } $received = $data['wp-refresh-metabox-loader-nonces']; $post_id = (int) $received['post_id']; if ( ! $post_id ) { return $response; } if ( ! current_user_can( 'edit_post', $post_id ) ) { return $response; } $response['wp-refresh-metabox-loader-nonces'] = array( 'replace' => array( 'metabox_loader_nonce' => wp_create_nonce( 'meta-box-loader' ), '_wpnonce' => wp_create_nonce( 'update-post_' . $post_id ), ), ); return $response; } /** * Adds the latest Heartbeat and REST-API nonce to the Heartbeat response. * * @since 5.0.0 * * @param array $response The Heartbeat response. * @return array The Heartbeat response. */ function wp_refresh_heartbeat_nonces( $response ) { // Refresh the Rest API nonce. $response['rest_nonce'] = wp_create_nonce( 'wp_rest' ); // Refresh the Heartbeat nonce. $response['heartbeat_nonce'] = wp_create_nonce( 'heartbeat-nonce' ); return $response; } /** * Disables suspension of Heartbeat on the Add/Edit Post screens. * * @since 3.8.0 * * @global string $pagenow The filename of the current screen. * * @param array $settings An array of Heartbeat settings. * @return array Filtered Heartbeat settings. */ function wp_heartbeat_set_suspension( $settings ) { global $pagenow; if ( 'post.php' === $pagenow || 'post-new.php' === $pagenow ) { $settings['suspension'] = 'disable'; } return $settings; } /** * Performs autosave with heartbeat. * * @since 3.9.0 * * @param array $response The Heartbeat response. * @param array $data The $_POST data sent. * @return array The Heartbeat response. */ function heartbeat_autosave( $response, $data ) { if ( ! empty( $data['wp_autosave'] ) ) { $saved = wp_autosave( $data['wp_autosave'] ); if ( is_wp_error( $saved ) ) { $response['wp_autosave'] = array( 'success' => false, 'message' => $saved->get_error_message(), ); } elseif ( empty( $saved ) ) { $response['wp_autosave'] = array( 'success' => false, 'message' => __( 'Error while saving.' ), ); } else { /* translators: Draft saved date format, see https://www.php.net/manual/datetime.format.php */ $draft_saved_date_format = __( 'g:i:s a' ); $response['wp_autosave'] = array( 'success' => true, /* translators: %s: Date and time. */ 'message' => sprintf( __( 'Draft saved at %s.' ), date_i18n( $draft_saved_date_format ) ), ); } } return $response; } /** * Removes single-use URL parameters and create canonical link based on new URL. * * Removes specific query string parameters from a URL, create the canonical link, * put it in the admin header, and change the current URL to match. * * @since 4.2.0 */ function wp_admin_canonical_url() { $removable_query_args = wp_removable_query_args(); if ( empty( $removable_query_args ) ) { return; } // Ensure we're using an absolute URL. $current_url = set_url_scheme( 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] ); $filtered_url = remove_query_arg( $removable_query_args, $current_url ); /** * Filters the admin canonical url value. * * @since 6.5.0 * * @param string $filtered_url The admin canonical url value. */ $filtered_url = apply_filters( 'wp_admin_canonical_url', $filtered_url ); ?> <link id="wp-admin-canonical" rel="canonical" href="<?php echo esc_url( $filtered_url ); ?>" /> <script> if ( window.history.replaceState ) { window.history.replaceState( null, null, document.getElementById( 'wp-admin-canonical' ).href + window.location.hash ); } </script> <?php } /** * Sends a referrer policy header so referrers are not sent externally from administration screens. * * @since 4.9.0 */ function wp_admin_headers() { $policy = 'strict-origin-when-cross-origin'; /** * Filters the admin referrer policy header value. * * @since 4.9.0 * @since 4.9.5 The default value was changed to 'strict-origin-when-cross-origin'. * * @link https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy * * @param string $policy The admin referrer policy header value. Default 'strict-origin-when-cross-origin'. */ $policy = apply_filters( 'admin_referrer_policy', $policy ); header( sprintf( 'Referrer-Policy: %s', $policy ) ); } /** * Outputs JS that reloads the page if the user navigated to it with the Back or Forward button. * * Used on the Edit Post and Add New Post screens. Needed to ensure the page is not loaded from browser cache, * so the post title and editor content are the last saved versions. Ideally this script should run first in the head. * * @since 4.6.0 */ function wp_page_reload_on_back_button_js() { ?> <script> if ( typeof performance !== 'undefined' && performance.navigation && performance.navigation.type === 2 ) { document.location.reload( true ); } </script> <?php } /** * Sends a confirmation request email when a change of site admin email address is attempted. * * The new site admin address will not become active until confirmed. * * @since 3.0.0 * @since 4.9.0 This function was moved from wp-admin/includes/ms.php so it's no longer Multisite specific. * * @param string $old_value The old site admin email address. * @param string $value The proposed new site admin email address. */ function update_option_new_admin_email( $old_value, $value ) { if ( get_option( 'admin_email' ) === $value || ! is_email( $value ) ) { return; } $hash = md5( $value . time() . wp_rand() ); $new_admin_email = array( 'hash' => $hash, 'newemail' => $value, ); update_option( 'adminhash', $new_admin_email ); $switched_locale = switch_to_user_locale( get_current_user_id() ); /* translators: Do not translate USERNAME, ADMIN_URL, EMAIL, SITENAME, SITEURL: those are placeholders. */ $email_text = __( 'Howdy ###USERNAME###, Someone with administrator capabilities recently requested to have the administration email address changed on this site: ###SITEURL### To confirm this change, please click on the following link: ###ADMIN_URL### You can safely ignore and delete this email if you do not want to take this action. This email has been sent to ###EMAIL### Regards, All at ###SITENAME### ###SITEURL###' ); /** * Filters the text of the email sent when a change of site admin email address is attempted. * * The following strings have a special meaning and will get replaced dynamically: * - ###USERNAME### The current user's username. * - ###ADMIN_URL### The link to click on to confirm the email change. * - ###EMAIL### The proposed new site admin email address. * - ###SITENAME### The name of the site. * - ###SITEURL### The URL to the site. * * @since MU (3.0.0) * @since 4.9.0 This filter is no longer Multisite specific. * * @param string $email_text Text in the email. * @param array $new_admin_email { * Data relating to the new site admin email address. * * @type string $hash The secure hash used in the confirmation link URL. * @type string $newemail The proposed new site admin email address. * } */ $content = apply_filters( 'new_admin_email_content', $email_text, $new_admin_email ); $current_user = wp_get_current_user(); $content = str_replace( '###USERNAME###', $current_user->user_login, $content ); $content = str_replace( '###ADMIN_URL###', esc_url( self_admin_url( 'options.php?adminhash=' . $hash ) ), $content ); $content = str_replace( '###EMAIL###', $value, $content ); $content = str_replace( '###SITENAME###', wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES ), $content ); $content = str_replace( '###SITEURL###', home_url(), $content ); if ( '' !== get_option( 'blogname' ) ) { $site_title = wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES ); } else { $site_title = parse_url( home_url(), PHP_URL_HOST ); } $subject = sprintf( /* translators: New admin email address notification email subject. %s: Site title. */ __( '[%s] New Admin Email Address' ), $site_title ); /** * Filters the subject of the email sent when a change of site admin email address is attempted. * * @since 6.5.0 * * @param string $subject Subject of the email. */ $subject = apply_filters( 'new_admin_email_subject', $subject ); wp_mail( $value, $subject, $content ); if ( $switched_locale ) { restore_previous_locale(); } } /** * Appends '(Draft)' to draft page titles in the privacy page dropdown * so that unpublished content is obvious. * * @since 4.9.8 * @access private * * @param string $title Page title. * @param WP_Post $page Page data object. * @return string Page title. */ function _wp_privacy_settings_filter_draft_page_titles( $title, $page ) { if ( 'draft' === $page->post_status && 'privacy' === get_current_screen()->id ) { /* translators: %s: Page title. */ $title = sprintf( __( '%s (Draft)' ), $title ); } return $title; } /** * Checks if the user needs to update PHP. * * @since 5.1.0 * @since 5.1.1 Added the {@see 'wp_is_php_version_acceptable'} filter. * * @return array|false { * Array of PHP version data. False on failure. * * @type string $recommended_version The PHP version recommended by WordPress. * @type string $minimum_version The minimum required PHP version. * @type bool $is_supported Whether the PHP version is actively supported. * @type bool $is_secure Whether the PHP version receives security updates. * @type bool $is_acceptable Whether the PHP version is still acceptable or warnings * should be shown and an update recommended. * } */ function wp_check_php_version() { $version = PHP_VERSION; $key = md5( $version ); $response = get_site_transient( 'php_check_' . $key ); if ( false === $response ) { $url = 'http://api.wordpress.org/core/serve-happy/1.0/'; if ( wp_http_supports( array( 'ssl' ) ) ) { $url = set_url_scheme( $url, 'https' ); } $url = add_query_arg( 'php_version', $version, $url ); $response = wp_remote_get( $url ); if ( is_wp_error( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) { return false; } $response = json_decode( wp_remote_retrieve_body( $response ), true ); if ( ! is_array( $response ) ) { return false; } set_site_transient( 'php_check_' . $key, $response, WEEK_IN_SECONDS ); } if ( isset( $response['is_acceptable'] ) && $response['is_acceptable'] ) { /** * Filters whether the active PHP version is considered acceptable by WordPress. * * Returning false will trigger a PHP version warning to show up in the admin dashboard to administrators. * * This filter is only run if the wordpress.org Serve Happy API considers the PHP version acceptable, ensuring * that this filter can only make this check stricter, but not loosen it. * * @since 5.1.1 * * @param bool $is_acceptable Whether the PHP version is considered acceptable. Default true. * @param string $version PHP version checked. */ $response['is_acceptable'] = (bool) apply_filters( 'wp_is_php_version_acceptable', true, $version ); } $response['is_lower_than_future_minimum'] = false; // The minimum supported PHP version will be updated to 7.2. Check if the current version is lower. if ( version_compare( $version, '7.2', '<' ) ) { $response['is_lower_than_future_minimum'] = true; // Force showing of warnings. $response['is_acceptable'] = false; } return $response; } includes/class-plugin-installer-skin.php 0000644 00000027246 15135536347 0014450 0 ustar 00 <?php /** * Upgrader API: Plugin_Installer_Skin class * * @package WordPress * @subpackage Upgrader * @since 4.6.0 */ /** * Plugin Installer Skin for WordPress Plugin Installer. * * @since 2.8.0 * @since 4.6.0 Moved to its own file from wp-admin/includes/class-wp-upgrader-skins.php. * * @see WP_Upgrader_Skin */ class Plugin_Installer_Skin extends WP_Upgrader_Skin { public $api; public $type; public $url; public $overwrite; private $is_downgrading = false; /** * @param array $args */ public function __construct( $args = array() ) { $defaults = array( 'type' => 'web', 'url' => '', 'plugin' => '', 'nonce' => '', 'title' => '', 'overwrite' => '', ); $args = wp_parse_args( $args, $defaults ); $this->type = $args['type']; $this->url = $args['url']; $this->api = isset( $args['api'] ) ? $args['api'] : array(); $this->overwrite = $args['overwrite']; parent::__construct( $args ); } /** * Performs an action before installing a plugin. * * @since 2.8.0 */ public function before() { if ( ! empty( $this->api ) ) { $this->upgrader->strings['process_success'] = sprintf( $this->upgrader->strings['process_success_specific'], $this->api->name, $this->api->version ); } } /** * Hides the `process_failed` error when updating a plugin by uploading a zip file. * * @since 5.5.0 * * @param WP_Error $wp_error WP_Error object. * @return bool True if the error should be hidden, false otherwise. */ public function hide_process_failed( $wp_error ) { if ( 'upload' === $this->type && '' === $this->overwrite && $wp_error->get_error_code() === 'folder_exists' ) { return true; } return false; } /** * Performs an action following a plugin install. * * @since 2.8.0 */ public function after() { // Check if the plugin can be overwritten and output the HTML. if ( $this->do_overwrite() ) { return; } $plugin_file = $this->upgrader->plugin_info(); $install_actions = array(); $from = isset( $_GET['from'] ) ? wp_unslash( $_GET['from'] ) : 'plugins'; if ( 'import' === $from ) { $install_actions['activate_plugin'] = sprintf( '<a class="button button-primary" href="%s" target="_parent">%s</a>', wp_nonce_url( 'plugins.php?action=activate&from=import&plugin=' . urlencode( $plugin_file ), 'activate-plugin_' . $plugin_file ), __( 'Activate Plugin & Run Importer' ) ); } elseif ( 'press-this' === $from ) { $install_actions['activate_plugin'] = sprintf( '<a class="button button-primary" href="%s" target="_parent">%s</a>', wp_nonce_url( 'plugins.php?action=activate&from=press-this&plugin=' . urlencode( $plugin_file ), 'activate-plugin_' . $plugin_file ), __( 'Activate Plugin & Go to Press This' ) ); } else { $install_actions['activate_plugin'] = sprintf( '<a class="button button-primary" href="%s" target="_parent">%s</a>', wp_nonce_url( 'plugins.php?action=activate&plugin=' . urlencode( $plugin_file ), 'activate-plugin_' . $plugin_file ), __( 'Activate Plugin' ) ); } if ( is_multisite() && current_user_can( 'manage_network_plugins' ) ) { $install_actions['network_activate'] = sprintf( '<a class="button button-primary" href="%s" target="_parent">%s</a>', wp_nonce_url( 'plugins.php?action=activate&networkwide=1&plugin=' . urlencode( $plugin_file ), 'activate-plugin_' . $plugin_file ), _x( 'Network Activate', 'plugin' ) ); unset( $install_actions['activate_plugin'] ); } if ( 'import' === $from ) { $install_actions['importers_page'] = sprintf( '<a href="%s" target="_parent">%s</a>', admin_url( 'import.php' ), __( 'Go to Importers' ) ); } elseif ( 'web' === $this->type ) { $install_actions['plugins_page'] = sprintf( '<a href="%s" target="_parent">%s</a>', self_admin_url( 'plugin-install.php' ), __( 'Go to Plugin Installer' ) ); } elseif ( 'upload' === $this->type && 'plugins' === $from ) { $install_actions['plugins_page'] = sprintf( '<a href="%s">%s</a>', self_admin_url( 'plugin-install.php' ), __( 'Go to Plugin Installer' ) ); } else { $install_actions['plugins_page'] = sprintf( '<a href="%s" target="_parent">%s</a>', self_admin_url( 'plugins.php' ), __( 'Go to Plugins page' ) ); } if ( ! $this->result || is_wp_error( $this->result ) ) { unset( $install_actions['activate_plugin'], $install_actions['network_activate'] ); } elseif ( ! current_user_can( 'activate_plugin', $plugin_file ) || is_plugin_active( $plugin_file ) ) { unset( $install_actions['activate_plugin'] ); } /** * Filters the list of action links available following a single plugin installation. * * @since 2.7.0 * * @param string[] $install_actions Array of plugin action links. * @param object $api Object containing WordPress.org API plugin data. Empty * for non-API installs, such as when a plugin is installed * via upload. * @param string $plugin_file Path to the plugin file relative to the plugins directory. */ $install_actions = apply_filters( 'install_plugin_complete_actions', $install_actions, $this->api, $plugin_file ); if ( ! empty( $install_actions ) ) { $this->feedback( implode( ' ', (array) $install_actions ) ); } } /** * Checks if the plugin can be overwritten and outputs the HTML for overwriting a plugin on upload. * * @since 5.5.0 * * @return bool Whether the plugin can be overwritten and HTML was outputted. */ private function do_overwrite() { if ( 'upload' !== $this->type || ! is_wp_error( $this->result ) || 'folder_exists' !== $this->result->get_error_code() ) { return false; } $folder = $this->result->get_error_data( 'folder_exists' ); $folder = ltrim( substr( $folder, strlen( WP_PLUGIN_DIR ) ), '/' ); $current_plugin_data = false; $all_plugins = get_plugins(); foreach ( $all_plugins as $plugin => $plugin_data ) { if ( strrpos( $plugin, $folder ) !== 0 ) { continue; } $current_plugin_data = $plugin_data; } $new_plugin_data = $this->upgrader->new_plugin_data; if ( ! $current_plugin_data || ! $new_plugin_data ) { return false; } echo '<h2 class="update-from-upload-heading">' . esc_html__( 'This plugin is already installed.' ) . '</h2>'; $this->is_downgrading = version_compare( $current_plugin_data['Version'], $new_plugin_data['Version'], '>' ); $rows = array( 'Name' => __( 'Plugin name' ), 'Version' => __( 'Version' ), 'Author' => __( 'Author' ), 'RequiresWP' => __( 'Required WordPress version' ), 'RequiresPHP' => __( 'Required PHP version' ), ); $table = '<table class="update-from-upload-comparison"><tbody>'; $table .= '<tr><th></th><th>' . esc_html_x( 'Current', 'plugin' ) . '</th>'; $table .= '<th>' . esc_html_x( 'Uploaded', 'plugin' ) . '</th></tr>'; $is_same_plugin = true; // Let's consider only these rows. foreach ( $rows as $field => $label ) { $old_value = ! empty( $current_plugin_data[ $field ] ) ? (string) $current_plugin_data[ $field ] : '-'; $new_value = ! empty( $new_plugin_data[ $field ] ) ? (string) $new_plugin_data[ $field ] : '-'; $is_same_plugin = $is_same_plugin && ( $old_value === $new_value ); $diff_field = ( 'Version' !== $field && $new_value !== $old_value ); $diff_version = ( 'Version' === $field && $this->is_downgrading ); $table .= '<tr><td class="name-label">' . $label . '</td><td>' . wp_strip_all_tags( $old_value ) . '</td>'; $table .= ( $diff_field || $diff_version ) ? '<td class="warning">' : '<td>'; $table .= wp_strip_all_tags( $new_value ) . '</td></tr>'; } $table .= '</tbody></table>'; /** * Filters the compare table output for overwriting a plugin package on upload. * * @since 5.5.0 * * @param string $table The output table with Name, Version, Author, RequiresWP, and RequiresPHP info. * @param array $current_plugin_data Array with current plugin data. * @param array $new_plugin_data Array with uploaded plugin data. */ echo apply_filters( 'install_plugin_overwrite_comparison', $table, $current_plugin_data, $new_plugin_data ); $install_actions = array(); $can_update = true; $blocked_message = '<p>' . esc_html__( 'The plugin cannot be updated due to the following:' ) . '</p>'; $blocked_message .= '<ul class="ul-disc">'; $requires_php = isset( $new_plugin_data['RequiresPHP'] ) ? $new_plugin_data['RequiresPHP'] : null; $requires_wp = isset( $new_plugin_data['RequiresWP'] ) ? $new_plugin_data['RequiresWP'] : null; if ( ! is_php_version_compatible( $requires_php ) ) { $error = sprintf( /* translators: 1: Current PHP version, 2: Version required by the uploaded plugin. */ __( 'The PHP version on your server is %1$s, however the uploaded plugin requires %2$s.' ), PHP_VERSION, $requires_php ); $blocked_message .= '<li>' . esc_html( $error ) . '</li>'; $can_update = false; } if ( ! is_wp_version_compatible( $requires_wp ) ) { $error = sprintf( /* translators: 1: Current WordPress version, 2: Version required by the uploaded plugin. */ __( 'Your WordPress version is %1$s, however the uploaded plugin requires %2$s.' ), get_bloginfo( 'version' ), $requires_wp ); $blocked_message .= '<li>' . esc_html( $error ) . '</li>'; $can_update = false; } $blocked_message .= '</ul>'; if ( $can_update ) { if ( $this->is_downgrading ) { $warning = sprintf( /* translators: %s: Documentation URL. */ __( 'You are uploading an older version of a current plugin. You can continue to install the older version, but be sure to <a href="%s">back up your database and files</a> first.' ), __( 'https://wordpress.org/documentation/article/wordpress-backups/' ) ); } else { $warning = sprintf( /* translators: %s: Documentation URL. */ __( 'You are updating a plugin. Be sure to <a href="%s">back up your database and files</a> first.' ), __( 'https://wordpress.org/documentation/article/wordpress-backups/' ) ); } echo '<p class="update-from-upload-notice">' . $warning . '</p>'; $overwrite = $this->is_downgrading ? 'downgrade-plugin' : 'update-plugin'; $install_actions['overwrite_plugin'] = sprintf( '<a class="button button-primary update-from-upload-overwrite" href="%s" target="_parent">%s</a>', wp_nonce_url( add_query_arg( 'overwrite', $overwrite, $this->url ), 'plugin-upload' ), _x( 'Replace current with uploaded', 'plugin' ) ); } else { echo $blocked_message; } $cancel_url = add_query_arg( 'action', 'upload-plugin-cancel-overwrite', $this->url ); $install_actions['plugins_page'] = sprintf( '<a class="button" href="%s">%s</a>', wp_nonce_url( $cancel_url, 'plugin-upload-cancel-overwrite' ), __( 'Cancel and go back' ) ); /** * Filters the list of action links available following a single plugin installation failure * when overwriting is allowed. * * @since 5.5.0 * * @param string[] $install_actions Array of plugin action links. * @param object $api Object containing WordPress.org API plugin data. * @param array $new_plugin_data Array with uploaded plugin data. */ $install_actions = apply_filters( 'install_plugin_overwrite_actions', $install_actions, $this->api, $new_plugin_data ); if ( ! empty( $install_actions ) ) { printf( '<p class="update-from-upload-expired hidden">%s</p>', __( 'The uploaded file has expired. Please go back and upload it again.' ) ); echo '<p class="update-from-upload-actions">' . implode( ' ', (array) $install_actions ) . '</p>'; } return true; } } includes/widgets.php 0000644 00000025240 15135536347 0010550 0 ustar 00 <?php /** * WordPress Widgets Administration API * * @package WordPress * @subpackage Administration */ /** * Display list of the available widgets. * * @since 2.5.0 * * @global array $wp_registered_widgets * @global array $wp_registered_widget_controls */ function wp_list_widgets() { global $wp_registered_widgets, $wp_registered_widget_controls; $sort = $wp_registered_widgets; usort( $sort, '_sort_name_callback' ); $done = array(); foreach ( $sort as $widget ) { if ( in_array( $widget['callback'], $done, true ) ) { // We already showed this multi-widget. continue; } $sidebar = is_active_widget( $widget['callback'], $widget['id'], false, false ); $done[] = $widget['callback']; if ( ! isset( $widget['params'][0] ) ) { $widget['params'][0] = array(); } $args = array( 'widget_id' => $widget['id'], 'widget_name' => $widget['name'], '_display' => 'template', ); if ( isset( $wp_registered_widget_controls[ $widget['id'] ]['id_base'] ) && isset( $widget['params'][0]['number'] ) ) { $id_base = $wp_registered_widget_controls[ $widget['id'] ]['id_base']; $args['_temp_id'] = "$id_base-__i__"; $args['_multi_num'] = next_widget_id_number( $id_base ); $args['_add'] = 'multi'; } else { $args['_add'] = 'single'; if ( $sidebar ) { $args['_hide'] = '1'; } } $control_args = array( 0 => $args, 1 => $widget['params'][0], ); $sidebar_args = wp_list_widget_controls_dynamic_sidebar( $control_args ); wp_widget_control( ...$sidebar_args ); } } /** * Callback to sort array by a 'name' key. * * @since 3.1.0 * @access private * * @param array $a First array. * @param array $b Second array. * @return int */ function _sort_name_callback( $a, $b ) { return strnatcasecmp( $a['name'], $b['name'] ); } /** * Show the widgets and their settings for a sidebar. * Used in the admin widget config screen. * * @since 2.5.0 * * @param string $sidebar Sidebar ID. * @param string $sidebar_name Optional. Sidebar name. Default empty. */ function wp_list_widget_controls( $sidebar, $sidebar_name = '' ) { add_filter( 'dynamic_sidebar_params', 'wp_list_widget_controls_dynamic_sidebar' ); $description = wp_sidebar_description( $sidebar ); echo '<div id="' . esc_attr( $sidebar ) . '" class="widgets-sortables">'; if ( $sidebar_name ) { $add_to = sprintf( /* translators: %s: Widgets sidebar name. */ __( 'Add to: %s' ), $sidebar_name ); ?> <div class="sidebar-name" data-add-to="<?php echo esc_attr( $add_to ); ?>"> <button type="button" class="handlediv hide-if-no-js" aria-expanded="true"> <span class="screen-reader-text"><?php echo esc_html( $sidebar_name ); ?></span> <span class="toggle-indicator" aria-hidden="true"></span> </button> <h2><?php echo esc_html( $sidebar_name ); ?> <span class="spinner"></span></h2> </div> <?php } if ( ! empty( $description ) ) { ?> <div class="sidebar-description"> <p class="description"><?php echo $description; ?></p> </div> <?php } dynamic_sidebar( $sidebar ); echo '</div>'; } /** * Retrieves the widget control arguments. * * @since 2.5.0 * * @global array $wp_registered_widgets * * @param array $params * @return array */ function wp_list_widget_controls_dynamic_sidebar( $params ) { global $wp_registered_widgets; static $i = 0; ++$i; $widget_id = $params[0]['widget_id']; $id = isset( $params[0]['_temp_id'] ) ? $params[0]['_temp_id'] : $widget_id; $hidden = isset( $params[0]['_hide'] ) ? ' style="display:none;"' : ''; $params[0]['before_widget'] = "<div id='widget-{$i}_{$id}' class='widget'$hidden>"; $params[0]['after_widget'] = '</div>'; $params[0]['before_title'] = '%BEG_OF_TITLE%'; // Deprecated. $params[0]['after_title'] = '%END_OF_TITLE%'; // Deprecated. if ( is_callable( $wp_registered_widgets[ $widget_id ]['callback'] ) ) { $wp_registered_widgets[ $widget_id ]['_callback'] = $wp_registered_widgets[ $widget_id ]['callback']; $wp_registered_widgets[ $widget_id ]['callback'] = 'wp_widget_control'; } return $params; } /** * @global array $wp_registered_widgets * * @param string $id_base * @return int */ function next_widget_id_number( $id_base ) { global $wp_registered_widgets; $number = 1; foreach ( $wp_registered_widgets as $widget_id => $widget ) { if ( preg_match( '/' . preg_quote( $id_base, '/' ) . '-([0-9]+)$/', $widget_id, $matches ) ) { $number = max( $number, $matches[1] ); } } ++$number; return $number; } /** * Meta widget used to display the control form for a widget. * * Called from dynamic_sidebar(). * * @since 2.5.0 * * @global array $wp_registered_widgets * @global array $wp_registered_widget_controls * @global array $sidebars_widgets * * @param array $sidebar_args * @return array */ function wp_widget_control( $sidebar_args ) { global $wp_registered_widgets, $wp_registered_widget_controls, $sidebars_widgets; $widget_id = $sidebar_args['widget_id']; $sidebar_id = isset( $sidebar_args['id'] ) ? $sidebar_args['id'] : false; $key = $sidebar_id ? array_search( $widget_id, $sidebars_widgets[ $sidebar_id ], true ) : '-1'; // Position of widget in sidebar. $control = isset( $wp_registered_widget_controls[ $widget_id ] ) ? $wp_registered_widget_controls[ $widget_id ] : array(); $widget = $wp_registered_widgets[ $widget_id ]; $id_format = $widget['id']; $widget_number = isset( $control['params'][0]['number'] ) ? $control['params'][0]['number'] : ''; $id_base = isset( $control['id_base'] ) ? $control['id_base'] : $widget_id; $width = isset( $control['width'] ) ? $control['width'] : ''; $height = isset( $control['height'] ) ? $control['height'] : ''; $multi_number = isset( $sidebar_args['_multi_num'] ) ? $sidebar_args['_multi_num'] : ''; $add_new = isset( $sidebar_args['_add'] ) ? $sidebar_args['_add'] : ''; $before_form = isset( $sidebar_args['before_form'] ) ? $sidebar_args['before_form'] : '<form method="post">'; $after_form = isset( $sidebar_args['after_form'] ) ? $sidebar_args['after_form'] : '</form>'; $before_widget_content = isset( $sidebar_args['before_widget_content'] ) ? $sidebar_args['before_widget_content'] : '<div class="widget-content">'; $after_widget_content = isset( $sidebar_args['after_widget_content'] ) ? $sidebar_args['after_widget_content'] : '</div>'; $query_arg = array( 'editwidget' => $widget['id'] ); if ( $add_new ) { $query_arg['addnew'] = 1; if ( $multi_number ) { $query_arg['num'] = $multi_number; $query_arg['base'] = $id_base; } } else { $query_arg['sidebar'] = $sidebar_id; $query_arg['key'] = $key; } /* * We aren't showing a widget control, we're outputting a template * for a multi-widget control. */ if ( isset( $sidebar_args['_display'] ) && 'template' === $sidebar_args['_display'] && $widget_number ) { // number == -1 implies a template where id numbers are replaced by a generic '__i__'. $control['params'][0]['number'] = -1; // With id_base widget ID's are constructed like {$id_base}-{$id_number}. if ( isset( $control['id_base'] ) ) { $id_format = $control['id_base'] . '-__i__'; } } $wp_registered_widgets[ $widget_id ]['callback'] = $wp_registered_widgets[ $widget_id ]['_callback']; unset( $wp_registered_widgets[ $widget_id ]['_callback'] ); $widget_title = esc_html( strip_tags( $sidebar_args['widget_name'] ) ); $has_form = 'noform'; echo $sidebar_args['before_widget']; ?> <div class="widget-top"> <div class="widget-title-action"> <button type="button" class="widget-action hide-if-no-js" aria-expanded="false"> <span class="screen-reader-text edit"> <?php /* translators: Hidden accessibility text. %s: Widget title. */ printf( __( 'Edit widget: %s' ), $widget_title ); ?> </span> <span class="screen-reader-text add"> <?php /* translators: Hidden accessibility text. %s: Widget title. */ printf( __( 'Add widget: %s' ), $widget_title ); ?> </span> <span class="toggle-indicator" aria-hidden="true"></span> </button> <a class="widget-control-edit hide-if-js" href="<?php echo esc_url( add_query_arg( $query_arg ) ); ?>"> <span class="edit"><?php _ex( 'Edit', 'widget' ); ?></span> <span class="add"><?php _ex( 'Add', 'widget' ); ?></span> <span class="screen-reader-text"><?php echo $widget_title; ?></span> </a> </div> <div class="widget-title"><h3><?php echo $widget_title; ?><span class="in-widget-title"></span></h3></div> </div> <div class="widget-inside"> <?php echo $before_form; ?> <?php echo $before_widget_content; ?> <?php if ( isset( $control['callback'] ) ) { $has_form = call_user_func_array( $control['callback'], $control['params'] ); } else { echo "\t\t<p>" . __( 'There are no options for this widget.' ) . "</p>\n"; } $noform_class = ''; if ( 'noform' === $has_form ) { $noform_class = ' widget-control-noform'; } ?> <?php echo $after_widget_content; ?> <input type="hidden" name="widget-id" class="widget-id" value="<?php echo esc_attr( $id_format ); ?>" /> <input type="hidden" name="id_base" class="id_base" value="<?php echo esc_attr( $id_base ); ?>" /> <input type="hidden" name="widget-width" class="widget-width" value="<?php echo esc_attr( $width ); ?>" /> <input type="hidden" name="widget-height" class="widget-height" value="<?php echo esc_attr( $height ); ?>" /> <input type="hidden" name="widget_number" class="widget_number" value="<?php echo esc_attr( $widget_number ); ?>" /> <input type="hidden" name="multi_number" class="multi_number" value="<?php echo esc_attr( $multi_number ); ?>" /> <input type="hidden" name="add_new" class="add_new" value="<?php echo esc_attr( $add_new ); ?>" /> <div class="widget-control-actions"> <div class="alignleft"> <button type="button" class="button-link button-link-delete widget-control-remove"><?php _e( 'Delete' ); ?></button> <span class="widget-control-close-wrapper"> | <button type="button" class="button-link widget-control-close"><?php _e( 'Done' ); ?></button> </span> </div> <div class="alignright<?php echo $noform_class; ?>"> <?php submit_button( __( 'Save' ), 'primary widget-control-save right', 'savewidget', false, array( 'id' => 'widget-' . esc_attr( $id_format ) . '-savewidget' ) ); ?> <span class="spinner"></span> </div> <br class="clear" /> </div> <?php echo $after_form; ?> </div> <div class="widget-description"> <?php $widget_description = wp_widget_description( $widget_id ); echo ( $widget_description ) ? "$widget_description\n" : "$widget_title\n"; ?> </div> <?php echo $sidebar_args['after_widget']; return $sidebar_args; } /** * @param string $classes * @return string */ function wp_widgets_access_body_class( $classes ) { return "$classes widgets_access "; } includes/image-edit.php 0000644 00000124470 15135536347 0011114 0 ustar 00 <?php /** * WordPress Image Editor * * @package WordPress * @subpackage Administration */ /** * Loads the WP image-editing interface. * * @since 2.9.0 * * @param int $post_id Attachment post ID. * @param false|object $msg Optional. Message to display for image editor updates or errors. * Default false. */ function wp_image_editor( $post_id, $msg = false ) { $nonce = wp_create_nonce( "image_editor-$post_id" ); $meta = wp_get_attachment_metadata( $post_id ); $thumb = image_get_intermediate_size( $post_id, 'thumbnail' ); $sub_sizes = isset( $meta['sizes'] ) && is_array( $meta['sizes'] ); $note = ''; if ( isset( $meta['width'], $meta['height'] ) ) { $big = max( $meta['width'], $meta['height'] ); } else { die( __( 'Image data does not exist. Please re-upload the image.' ) ); } $sizer = $big > 600 ? 600 / $big : 1; $backup_sizes = get_post_meta( $post_id, '_wp_attachment_backup_sizes', true ); $can_restore = false; if ( ! empty( $backup_sizes ) && isset( $backup_sizes['full-orig'], $meta['file'] ) ) { $can_restore = wp_basename( $meta['file'] ) !== $backup_sizes['full-orig']['file']; } if ( $msg ) { if ( isset( $msg->error ) ) { $note = "<div class='notice notice-error' role='alert'><p>$msg->error</p></div>"; } elseif ( isset( $msg->msg ) ) { $note = "<div class='notice notice-success' role='alert'><p>$msg->msg</p></div>"; } } /** * Shows the settings in the Image Editor that allow selecting to edit only the thumbnail of an image. * * @since 6.3.0 * * @param bool $show Whether to show the settings in the Image Editor. Default false. */ $edit_thumbnails_separately = (bool) apply_filters( 'image_edit_thumbnails_separately', false ); ?> <div class="imgedit-wrap wp-clearfix"> <div id="imgedit-panel-<?php echo $post_id; ?>"> <?php echo $note; ?> <div class="imgedit-panel-content imgedit-panel-tools wp-clearfix"> <div class="imgedit-menu wp-clearfix"> <button type="button" onclick="imageEdit.toggleCropTool( <?php echo "$post_id, '$nonce'"; ?>, this );" aria-expanded="false" aria-controls="imgedit-crop" class="imgedit-crop button disabled" disabled><?php esc_html_e( 'Crop' ); ?></button> <button type="button" class="imgedit-scale button" onclick="imageEdit.toggleControls(this);" aria-expanded="false" aria-controls="imgedit-scale"><?php esc_html_e( 'Scale' ); ?></button> <div class="imgedit-rotate-menu-container"> <button type="button" aria-controls="imgedit-rotate-menu" class="imgedit-rotate button" aria-expanded="false" onclick="imageEdit.togglePopup(this)" onblur="imageEdit.monitorPopup()"><?php esc_html_e( 'Image Rotation' ); ?></button> <div id="imgedit-rotate-menu" class="imgedit-popup-menu"> <?php // On some setups GD library does not provide imagerotate() - Ticket #11536. if ( wp_image_editor_supports( array( 'mime_type' => get_post_mime_type( $post_id ), 'methods' => array( 'rotate' ), ) ) ) { $note_no_rotate = ''; ?> <button type="button" class="imgedit-rleft button" onkeyup="imageEdit.browsePopup(this)" onclick="imageEdit.rotate( 90, <?php echo "$post_id, '$nonce'"; ?>, this)" onblur="imageEdit.monitorPopup()"><?php esc_html_e( 'Rotate 90° left' ); ?></button> <button type="button" class="imgedit-rright button" onkeyup="imageEdit.browsePopup(this)" onclick="imageEdit.rotate(-90, <?php echo "$post_id, '$nonce'"; ?>, this)" onblur="imageEdit.monitorPopup()"><?php esc_html_e( 'Rotate 90° right' ); ?></button> <button type="button" class="imgedit-rfull button" onkeyup="imageEdit.browsePopup(this)" onclick="imageEdit.rotate(180, <?php echo "$post_id, '$nonce'"; ?>, this)" onblur="imageEdit.monitorPopup()"><?php esc_html_e( 'Rotate 180°' ); ?></button> <?php } else { $note_no_rotate = '<p class="note-no-rotate"><em>' . __( 'Image rotation is not supported by your web host.' ) . '</em></p>'; ?> <button type="button" class="imgedit-rleft button disabled" disabled></button> <button type="button" class="imgedit-rright button disabled" disabled></button> <?php } ?> <hr /> <button type="button" onkeyup="imageEdit.browsePopup(this)" onclick="imageEdit.flip(1, <?php echo "$post_id, '$nonce'"; ?>, this)" onblur="imageEdit.monitorPopup()" class="imgedit-flipv button"><?php esc_html_e( 'Flip vertical' ); ?></button> <button type="button" onkeyup="imageEdit.browsePopup(this)" onclick="imageEdit.flip(2, <?php echo "$post_id, '$nonce'"; ?>, this)" onblur="imageEdit.monitorPopup()" class="imgedit-fliph button"><?php esc_html_e( 'Flip horizontal' ); ?></button> <?php echo $note_no_rotate; ?> </div> </div> </div> <div class="imgedit-submit imgedit-menu"> <button type="button" id="image-undo-<?php echo $post_id; ?>" onclick="imageEdit.undo(<?php echo "$post_id, '$nonce'"; ?>, this)" class="imgedit-undo button disabled" disabled><?php esc_html_e( 'Undo' ); ?></button> <button type="button" id="image-redo-<?php echo $post_id; ?>" onclick="imageEdit.redo(<?php echo "$post_id, '$nonce'"; ?>, this)" class="imgedit-redo button disabled" disabled><?php esc_html_e( 'Redo' ); ?></button> <button type="button" onclick="imageEdit.close(<?php echo $post_id; ?>, 1)" class="button imgedit-cancel-btn"><?php esc_html_e( 'Cancel Editing' ); ?></button> <button type="button" onclick="imageEdit.save(<?php echo "$post_id, '$nonce'"; ?>)" disabled="disabled" class="button button-primary imgedit-submit-btn"><?php esc_html_e( 'Save Edits' ); ?></button> </div> </div> <div class="imgedit-panel-content wp-clearfix"> <div class="imgedit-tools"> <input type="hidden" id="imgedit-nonce-<?php echo $post_id; ?>" value="<?php echo $nonce; ?>" /> <input type="hidden" id="imgedit-sizer-<?php echo $post_id; ?>" value="<?php echo $sizer; ?>" /> <input type="hidden" id="imgedit-history-<?php echo $post_id; ?>" value="" /> <input type="hidden" id="imgedit-undone-<?php echo $post_id; ?>" value="0" /> <input type="hidden" id="imgedit-selection-<?php echo $post_id; ?>" value="" /> <input type="hidden" id="imgedit-x-<?php echo $post_id; ?>" value="<?php echo isset( $meta['width'] ) ? $meta['width'] : 0; ?>" /> <input type="hidden" id="imgedit-y-<?php echo $post_id; ?>" value="<?php echo isset( $meta['height'] ) ? $meta['height'] : 0; ?>" /> <div id="imgedit-crop-<?php echo $post_id; ?>" class="imgedit-crop-wrap"> <div class="imgedit-crop-grid"></div> <img id="image-preview-<?php echo $post_id; ?>" onload="imageEdit.imgLoaded('<?php echo $post_id; ?>')" src="<?php echo esc_url( admin_url( 'admin-ajax.php', 'relative' ) ) . '?action=imgedit-preview&_ajax_nonce=' . $nonce . '&postid=' . $post_id . '&rand=' . rand( 1, 99999 ); ?>" alt="" /> </div> </div> <div class="imgedit-settings"> <div class="imgedit-tool-active"> <div class="imgedit-group"> <div id="imgedit-scale" tabindex="-1" class="imgedit-group-controls"> <div class="imgedit-group-top"> <h2><?php _e( 'Scale Image' ); ?></h2> <button type="button" class="dashicons dashicons-editor-help imgedit-help-toggle" onclick="imageEdit.toggleHelp(this);" aria-expanded="false"><span class="screen-reader-text"> <?php /* translators: Hidden accessibility text. */ esc_html_e( 'Scale Image Help' ); ?> </span></button> <div class="imgedit-help"> <p><?php _e( 'You can proportionally scale the original image. For best results, scaling should be done before you crop, flip, or rotate. Images can only be scaled down, not up.' ); ?></p> </div> <?php if ( isset( $meta['width'], $meta['height'] ) ) : ?> <p> <?php printf( /* translators: %s: Image width and height in pixels. */ __( 'Original dimensions %s' ), '<span class="imgedit-original-dimensions">' . $meta['width'] . ' × ' . $meta['height'] . '</span>' ); ?> </p> <?php endif; ?> <div class="imgedit-submit"> <fieldset class="imgedit-scale-controls"> <legend><?php _e( 'New dimensions:' ); ?></legend> <div class="nowrap"> <label for="imgedit-scale-width-<?php echo $post_id; ?>" class="screen-reader-text"> <?php /* translators: Hidden accessibility text. */ _e( 'scale height' ); ?> </label> <input type="number" step="1" min="0" max="<?php echo isset( $meta['width'] ) ? $meta['width'] : ''; ?>" aria-describedby="imgedit-scale-warn-<?php echo $post_id; ?>" id="imgedit-scale-width-<?php echo $post_id; ?>" onkeyup="imageEdit.scaleChanged(<?php echo $post_id; ?>, 1, this)" onblur="imageEdit.scaleChanged(<?php echo $post_id; ?>, 1, this)" value="<?php echo isset( $meta['width'] ) ? $meta['width'] : 0; ?>" /> <span class="imgedit-separator" aria-hidden="true">×</span> <label for="imgedit-scale-height-<?php echo $post_id; ?>" class="screen-reader-text"><?php _e( 'scale height' ); ?></label> <input type="number" step="1" min="0" max="<?php echo isset( $meta['height'] ) ? $meta['height'] : ''; ?>" aria-describedby="imgedit-scale-warn-<?php echo $post_id; ?>" id="imgedit-scale-height-<?php echo $post_id; ?>" onkeyup="imageEdit.scaleChanged(<?php echo $post_id; ?>, 0, this)" onblur="imageEdit.scaleChanged(<?php echo $post_id; ?>, 0, this)" value="<?php echo isset( $meta['height'] ) ? $meta['height'] : 0; ?>" /> <button id="imgedit-scale-button" type="button" onclick="imageEdit.action(<?php echo "$post_id, '$nonce'"; ?>, 'scale')" class="button button-primary"><?php esc_html_e( 'Scale' ); ?></button> <span class="imgedit-scale-warn" id="imgedit-scale-warn-<?php echo $post_id; ?>"><span class="dashicons dashicons-warning" aria-hidden="true"></span><?php esc_html_e( 'Images cannot be scaled to a size larger than the original.' ); ?></span> </div> </fieldset> </div> </div> </div> </div> <?php if ( $can_restore ) { ?> <div class="imgedit-group"> <div class="imgedit-group-top"> <h2><button type="button" onclick="imageEdit.toggleHelp(this);" class="button-link" aria-expanded="false"><?php _e( 'Restore original image' ); ?> <span class="dashicons dashicons-arrow-down imgedit-help-toggle"></span></button></h2> <div class="imgedit-help imgedit-restore"> <p> <?php _e( 'Discard any changes and restore the original image.' ); if ( ! defined( 'IMAGE_EDIT_OVERWRITE' ) || ! IMAGE_EDIT_OVERWRITE ) { echo ' ' . __( 'Previously edited copies of the image will not be deleted.' ); } ?> </p> <div class="imgedit-submit"> <input type="button" onclick="imageEdit.action(<?php echo "$post_id, '$nonce'"; ?>, 'restore')" class="button button-primary" value="<?php esc_attr_e( 'Restore image' ); ?>" <?php echo $can_restore; ?> /> </div> </div> </div> </div> <?php } ?> <div class="imgedit-group"> <div id="imgedit-crop" tabindex="-1" class="imgedit-group-controls"> <div class="imgedit-group-top"> <h2><?php _e( 'Crop Image' ); ?></h2> <button type="button" class="dashicons dashicons-editor-help imgedit-help-toggle" onclick="imageEdit.toggleHelp(this);" aria-expanded="false"><span class="screen-reader-text"> <?php /* translators: Hidden accessibility text. */ _e( 'Image Crop Help' ); ?> </span></button> <div class="imgedit-help"> <p><?php _e( 'To crop the image, click on it and drag to make your selection.' ); ?></p> <p><strong><?php _e( 'Crop Aspect Ratio' ); ?></strong><br /> <?php _e( 'The aspect ratio is the relationship between the width and height. You can preserve the aspect ratio by holding down the shift key while resizing your selection. Use the input box to specify the aspect ratio, e.g. 1:1 (square), 4:3, 16:9, etc.' ); ?></p> <p><strong><?php _e( 'Crop Selection' ); ?></strong><br /> <?php _e( 'Once you have made your selection, you can adjust it by entering the size in pixels. The minimum selection size is the thumbnail size as set in the Media settings.' ); ?></p> </div> </div> <fieldset class="imgedit-crop-ratio"> <legend><?php _e( 'Aspect ratio:' ); ?></legend> <div class="nowrap"> <label for="imgedit-crop-width-<?php echo $post_id; ?>" class="screen-reader-text"> <?php /* translators: Hidden accessibility text. */ _e( 'crop ratio width' ); ?> </label> <input type="number" step="1" min="1" id="imgedit-crop-width-<?php echo $post_id; ?>" onkeyup="imageEdit.setRatioSelection(<?php echo $post_id; ?>, 0, this)" onblur="imageEdit.setRatioSelection(<?php echo $post_id; ?>, 0, this)" /> <span class="imgedit-separator" aria-hidden="true">:</span> <label for="imgedit-crop-height-<?php echo $post_id; ?>" class="screen-reader-text"> <?php /* translators: Hidden accessibility text. */ _e( 'crop ratio height' ); ?> </label> <input type="number" step="1" min="0" id="imgedit-crop-height-<?php echo $post_id; ?>" onkeyup="imageEdit.setRatioSelection(<?php echo $post_id; ?>, 1, this)" onblur="imageEdit.setRatioSelection(<?php echo $post_id; ?>, 1, this)" /> </div> </fieldset> <fieldset id="imgedit-crop-sel-<?php echo $post_id; ?>" class="imgedit-crop-sel"> <legend><?php _e( 'Selection:' ); ?></legend> <div class="nowrap"> <label for="imgedit-sel-width-<?php echo $post_id; ?>" class="screen-reader-text"> <?php /* translators: Hidden accessibility text. */ _e( 'selection width' ); ?> </label> <input type="number" step="1" min="0" id="imgedit-sel-width-<?php echo $post_id; ?>" onkeyup="imageEdit.setNumSelection(<?php echo $post_id; ?>, this)" onblur="imageEdit.setNumSelection(<?php echo $post_id; ?>, this)" /> <span class="imgedit-separator" aria-hidden="true">×</span> <label for="imgedit-sel-height-<?php echo $post_id; ?>" class="screen-reader-text"> <?php /* translators: Hidden accessibility text. */ _e( 'selection height' ); ?> </label> <input type="number" step="1" min="0" id="imgedit-sel-height-<?php echo $post_id; ?>" onkeyup="imageEdit.setNumSelection(<?php echo $post_id; ?>, this)" onblur="imageEdit.setNumSelection(<?php echo $post_id; ?>, this)" /> </div> </fieldset> <fieldset id="imgedit-crop-sel-<?php echo $post_id; ?>" class="imgedit-crop-sel"> <legend><?php _e( 'Starting Coordinates:' ); ?></legend> <div class="nowrap"> <label for="imgedit-start-x-<?php echo $post_id; ?>" class="screen-reader-text"> <?php /* translators: Hidden accessibility text. */ _e( 'horizontal start position' ); ?> </label> <input type="number" step="1" min="0" id="imgedit-start-x-<?php echo $post_id; ?>" onkeyup="imageEdit.setNumSelection(<?php echo $post_id; ?>, this)" onblur="imageEdit.setNumSelection(<?php echo $post_id; ?>, this)" value="0" /> <span class="imgedit-separator" aria-hidden="true">×</span> <label for="imgedit-start-y-<?php echo $post_id; ?>" class="screen-reader-text"> <?php /* translators: Hidden accessibility text. */ _e( 'vertical start position' ); ?> </label> <input type="number" step="1" min="0" id="imgedit-start-y-<?php echo $post_id; ?>" onkeyup="imageEdit.setNumSelection(<?php echo $post_id; ?>, this)" onblur="imageEdit.setNumSelection(<?php echo $post_id; ?>, this)" value="0" /> </div> </fieldset> <div class="imgedit-crop-apply imgedit-menu container"> <button class="button-primary" type="button" onclick="imageEdit.handleCropToolClick( <?php echo "$post_id, '$nonce'"; ?>, this );" class="imgedit-crop-apply button"><?php esc_html_e( 'Apply Crop' ); ?></button> <button type="button" onclick="imageEdit.handleCropToolClick( <?php echo "$post_id, '$nonce'"; ?>, this );" class="imgedit-crop-clear button" disabled="disabled"><?php esc_html_e( 'Clear Crop' ); ?></button> </div> </div> </div> </div> <?php if ( $edit_thumbnails_separately && $thumb && $sub_sizes ) { $thumb_img = wp_constrain_dimensions( $thumb['width'], $thumb['height'], 160, 120 ); ?> <div class="imgedit-group imgedit-applyto"> <div class="imgedit-group-top"> <h2><?php _e( 'Thumbnail Settings' ); ?></h2> <button type="button" class="dashicons dashicons-editor-help imgedit-help-toggle" onclick="imageEdit.toggleHelp(this);" aria-expanded="false"><span class="screen-reader-text"> <?php /* translators: Hidden accessibility text. */ esc_html_e( 'Thumbnail Settings Help' ); ?> </span></button> <div class="imgedit-help"> <p><?php _e( 'You can edit the image while preserving the thumbnail. For example, you may wish to have a square thumbnail that displays just a section of the image.' ); ?></p> </div> </div> <div class="imgedit-thumbnail-preview-group"> <figure class="imgedit-thumbnail-preview"> <img src="<?php echo $thumb['url']; ?>" width="<?php echo $thumb_img[0]; ?>" height="<?php echo $thumb_img[1]; ?>" class="imgedit-size-preview" alt="" draggable="false" /> <figcaption class="imgedit-thumbnail-preview-caption"><?php _e( 'Current thumbnail' ); ?></figcaption> </figure> <div id="imgedit-save-target-<?php echo $post_id; ?>" class="imgedit-save-target"> <fieldset> <legend><?php _e( 'Apply changes to:' ); ?></legend> <span class="imgedit-label"> <input type="radio" id="imgedit-target-all" name="imgedit-target-<?php echo $post_id; ?>" value="all" checked="checked" /> <label for="imgedit-target-all"><?php _e( 'All image sizes' ); ?></label> </span> <span class="imgedit-label"> <input type="radio" id="imgedit-target-thumbnail" name="imgedit-target-<?php echo $post_id; ?>" value="thumbnail" /> <label for="imgedit-target-thumbnail"><?php _e( 'Thumbnail' ); ?></label> </span> <span class="imgedit-label"> <input type="radio" id="imgedit-target-nothumb" name="imgedit-target-<?php echo $post_id; ?>" value="nothumb" /> <label for="imgedit-target-nothumb"><?php _e( 'All sizes except thumbnail' ); ?></label> </span> </fieldset> </div> </div> </div> <?php } ?> </div> </div> </div> <div class="imgedit-wait" id="imgedit-wait-<?php echo $post_id; ?>"></div> <div class="hidden" id="imgedit-leaving-<?php echo $post_id; ?>"><?php _e( "There are unsaved changes that will be lost. 'OK' to continue, 'Cancel' to return to the Image Editor." ); ?></div> </div> <?php } /** * Streams image in WP_Image_Editor to browser. * * @since 2.9.0 * * @param WP_Image_Editor $image The image editor instance. * @param string $mime_type The mime type of the image. * @param int $attachment_id The image's attachment post ID. * @return bool True on success, false on failure. */ function wp_stream_image( $image, $mime_type, $attachment_id ) { if ( $image instanceof WP_Image_Editor ) { /** * Filters the WP_Image_Editor instance for the image to be streamed to the browser. * * @since 3.5.0 * * @param WP_Image_Editor $image The image editor instance. * @param int $attachment_id The attachment post ID. */ $image = apply_filters( 'image_editor_save_pre', $image, $attachment_id ); if ( is_wp_error( $image->stream( $mime_type ) ) ) { return false; } return true; } else { /* translators: 1: $image, 2: WP_Image_Editor */ _deprecated_argument( __FUNCTION__, '3.5.0', sprintf( __( '%1$s needs to be a %2$s object.' ), '$image', 'WP_Image_Editor' ) ); /** * Filters the GD image resource to be streamed to the browser. * * @since 2.9.0 * @deprecated 3.5.0 Use {@see 'image_editor_save_pre'} instead. * * @param resource|GdImage $image Image resource to be streamed. * @param int $attachment_id The attachment post ID. */ $image = apply_filters_deprecated( 'image_save_pre', array( $image, $attachment_id ), '3.5.0', 'image_editor_save_pre' ); switch ( $mime_type ) { case 'image/jpeg': header( 'Content-Type: image/jpeg' ); return imagejpeg( $image, null, 90 ); case 'image/png': header( 'Content-Type: image/png' ); return imagepng( $image ); case 'image/gif': header( 'Content-Type: image/gif' ); return imagegif( $image ); case 'image/webp': if ( function_exists( 'imagewebp' ) ) { header( 'Content-Type: image/webp' ); return imagewebp( $image, null, 90 ); } return false; case 'image/avif': if ( function_exists( 'imageavif' ) ) { header( 'Content-Type: image/avif' ); return imageavif( $image, null, 90 ); } return false; default: return false; } } } /** * Saves image to file. * * @since 2.9.0 * @since 3.5.0 The `$image` parameter expects a `WP_Image_Editor` instance. * @since 6.0.0 The `$filesize` value was added to the returned array. * * @param string $filename Name of the file to be saved. * @param WP_Image_Editor $image The image editor instance. * @param string $mime_type The mime type of the image. * @param int $post_id Attachment post ID. * @return array|WP_Error|bool { * Array on success or WP_Error if the file failed to save. * When called with a deprecated value for the `$image` parameter, * i.e. a non-`WP_Image_Editor` image resource or `GdImage` instance, * the function will return true on success, false on failure. * * @type string $path Path to the image file. * @type string $file Name of the image file. * @type int $width Image width. * @type int $height Image height. * @type string $mime-type The mime type of the image. * @type int $filesize File size of the image. * } */ function wp_save_image_file( $filename, $image, $mime_type, $post_id ) { if ( $image instanceof WP_Image_Editor ) { /** This filter is documented in wp-admin/includes/image-edit.php */ $image = apply_filters( 'image_editor_save_pre', $image, $post_id ); /** * Filters whether to skip saving the image file. * * Returning a non-null value will short-circuit the save method, * returning that value instead. * * @since 3.5.0 * * @param bool|null $override Value to return instead of saving. Default null. * @param string $filename Name of the file to be saved. * @param WP_Image_Editor $image The image editor instance. * @param string $mime_type The mime type of the image. * @param int $post_id Attachment post ID. */ $saved = apply_filters( 'wp_save_image_editor_file', null, $filename, $image, $mime_type, $post_id ); if ( null !== $saved ) { return $saved; } return $image->save( $filename, $mime_type ); } else { /* translators: 1: $image, 2: WP_Image_Editor */ _deprecated_argument( __FUNCTION__, '3.5.0', sprintf( __( '%1$s needs to be a %2$s object.' ), '$image', 'WP_Image_Editor' ) ); /** This filter is documented in wp-admin/includes/image-edit.php */ $image = apply_filters_deprecated( 'image_save_pre', array( $image, $post_id ), '3.5.0', 'image_editor_save_pre' ); /** * Filters whether to skip saving the image file. * * Returning a non-null value will short-circuit the save method, * returning that value instead. * * @since 2.9.0 * @deprecated 3.5.0 Use {@see 'wp_save_image_editor_file'} instead. * * @param bool|null $override Value to return instead of saving. Default null. * @param string $filename Name of the file to be saved. * @param resource|GdImage $image Image resource or GdImage instance. * @param string $mime_type The mime type of the image. * @param int $post_id Attachment post ID. */ $saved = apply_filters_deprecated( 'wp_save_image_file', array( null, $filename, $image, $mime_type, $post_id ), '3.5.0', 'wp_save_image_editor_file' ); if ( null !== $saved ) { return $saved; } switch ( $mime_type ) { case 'image/jpeg': /** This filter is documented in wp-includes/class-wp-image-editor.php */ return imagejpeg( $image, $filename, apply_filters( 'jpeg_quality', 90, 'edit_image' ) ); case 'image/png': return imagepng( $image, $filename ); case 'image/gif': return imagegif( $image, $filename ); case 'image/webp': if ( function_exists( 'imagewebp' ) ) { return imagewebp( $image, $filename ); } return false; case 'image/avif': if ( function_exists( 'imageavif' ) ) { return imageavif( $image, $filename ); } return false; default: return false; } } } /** * Image preview ratio. Internal use only. * * @since 2.9.0 * * @ignore * @param int $w Image width in pixels. * @param int $h Image height in pixels. * @return float|int Image preview ratio. */ function _image_get_preview_ratio( $w, $h ) { $max = max( $w, $h ); return $max > 600 ? ( 600 / $max ) : 1; } /** * Returns an image resource. Internal use only. * * @since 2.9.0 * @deprecated 3.5.0 Use WP_Image_Editor::rotate() * @see WP_Image_Editor::rotate() * * @ignore * @param resource|GdImage $img Image resource. * @param float|int $angle Image rotation angle, in degrees. * @return resource|GdImage|false GD image resource or GdImage instance, false otherwise. */ function _rotate_image_resource( $img, $angle ) { _deprecated_function( __FUNCTION__, '3.5.0', 'WP_Image_Editor::rotate()' ); if ( function_exists( 'imagerotate' ) ) { $rotated = imagerotate( $img, $angle, 0 ); if ( is_gd_image( $rotated ) ) { imagedestroy( $img ); $img = $rotated; } } return $img; } /** * Flips an image resource. Internal use only. * * @since 2.9.0 * @deprecated 3.5.0 Use WP_Image_Editor::flip() * @see WP_Image_Editor::flip() * * @ignore * @param resource|GdImage $img Image resource or GdImage instance. * @param bool $horz Whether to flip horizontally. * @param bool $vert Whether to flip vertically. * @return resource|GdImage (maybe) flipped image resource or GdImage instance. */ function _flip_image_resource( $img, $horz, $vert ) { _deprecated_function( __FUNCTION__, '3.5.0', 'WP_Image_Editor::flip()' ); $w = imagesx( $img ); $h = imagesy( $img ); $dst = wp_imagecreatetruecolor( $w, $h ); if ( is_gd_image( $dst ) ) { $sx = $vert ? ( $w - 1 ) : 0; $sy = $horz ? ( $h - 1 ) : 0; $sw = $vert ? -$w : $w; $sh = $horz ? -$h : $h; if ( imagecopyresampled( $dst, $img, 0, 0, $sx, $sy, $w, $h, $sw, $sh ) ) { imagedestroy( $img ); $img = $dst; } } return $img; } /** * Crops an image resource. Internal use only. * * @since 2.9.0 * * @ignore * @param resource|GdImage $img Image resource or GdImage instance. * @param float $x Source point x-coordinate. * @param float $y Source point y-coordinate. * @param float $w Source width. * @param float $h Source height. * @return resource|GdImage (maybe) cropped image resource or GdImage instance. */ function _crop_image_resource( $img, $x, $y, $w, $h ) { $dst = wp_imagecreatetruecolor( $w, $h ); if ( is_gd_image( $dst ) ) { if ( imagecopy( $dst, $img, 0, 0, $x, $y, $w, $h ) ) { imagedestroy( $img ); $img = $dst; } } return $img; } /** * Performs group of changes on Editor specified. * * @since 2.9.0 * * @param WP_Image_Editor $image WP_Image_Editor instance. * @param array $changes Array of change operations. * @return WP_Image_Editor WP_Image_Editor instance with changes applied. */ function image_edit_apply_changes( $image, $changes ) { if ( is_gd_image( $image ) ) { /* translators: 1: $image, 2: WP_Image_Editor */ _deprecated_argument( __FUNCTION__, '3.5.0', sprintf( __( '%1$s needs to be a %2$s object.' ), '$image', 'WP_Image_Editor' ) ); } if ( ! is_array( $changes ) ) { return $image; } // Expand change operations. foreach ( $changes as $key => $obj ) { if ( isset( $obj->r ) ) { $obj->type = 'rotate'; $obj->angle = $obj->r; unset( $obj->r ); } elseif ( isset( $obj->f ) ) { $obj->type = 'flip'; $obj->axis = $obj->f; unset( $obj->f ); } elseif ( isset( $obj->c ) ) { $obj->type = 'crop'; $obj->sel = $obj->c; unset( $obj->c ); } $changes[ $key ] = $obj; } // Combine operations. if ( count( $changes ) > 1 ) { $filtered = array( $changes[0] ); for ( $i = 0, $j = 1, $c = count( $changes ); $j < $c; $j++ ) { $combined = false; if ( $filtered[ $i ]->type === $changes[ $j ]->type ) { switch ( $filtered[ $i ]->type ) { case 'rotate': $filtered[ $i ]->angle += $changes[ $j ]->angle; $combined = true; break; case 'flip': $filtered[ $i ]->axis ^= $changes[ $j ]->axis; $combined = true; break; } } if ( ! $combined ) { $filtered[ ++$i ] = $changes[ $j ]; } } $changes = $filtered; unset( $filtered ); } // Image resource before applying the changes. if ( $image instanceof WP_Image_Editor ) { /** * Filters the WP_Image_Editor instance before applying changes to the image. * * @since 3.5.0 * * @param WP_Image_Editor $image WP_Image_Editor instance. * @param array $changes Array of change operations. */ $image = apply_filters( 'wp_image_editor_before_change', $image, $changes ); } elseif ( is_gd_image( $image ) ) { /** * Filters the GD image resource before applying changes to the image. * * @since 2.9.0 * @deprecated 3.5.0 Use {@see 'wp_image_editor_before_change'} instead. * * @param resource|GdImage $image GD image resource or GdImage instance. * @param array $changes Array of change operations. */ $image = apply_filters_deprecated( 'image_edit_before_change', array( $image, $changes ), '3.5.0', 'wp_image_editor_before_change' ); } foreach ( $changes as $operation ) { switch ( $operation->type ) { case 'rotate': if ( 0 !== $operation->angle ) { if ( $image instanceof WP_Image_Editor ) { $image->rotate( $operation->angle ); } else { $image = _rotate_image_resource( $image, $operation->angle ); } } break; case 'flip': if ( 0 !== $operation->axis ) { if ( $image instanceof WP_Image_Editor ) { $image->flip( ( $operation->axis & 1 ) !== 0, ( $operation->axis & 2 ) !== 0 ); } else { $image = _flip_image_resource( $image, ( $operation->axis & 1 ) !== 0, ( $operation->axis & 2 ) !== 0 ); } } break; case 'crop': $sel = $operation->sel; if ( $image instanceof WP_Image_Editor ) { $size = $image->get_size(); $w = $size['width']; $h = $size['height']; $scale = 1 / _image_get_preview_ratio( $w, $h ); // Discard preview scaling. $image->crop( $sel->x * $scale, $sel->y * $scale, $sel->w * $scale, $sel->h * $scale ); } else { $scale = 1 / _image_get_preview_ratio( imagesx( $image ), imagesy( $image ) ); // Discard preview scaling. $image = _crop_image_resource( $image, $sel->x * $scale, $sel->y * $scale, $sel->w * $scale, $sel->h * $scale ); } break; } } return $image; } /** * Streams image in post to browser, along with enqueued changes * in `$_REQUEST['history']`. * * @since 2.9.0 * * @param int $post_id Attachment post ID. * @return bool True on success, false on failure. */ function stream_preview_image( $post_id ) { $post = get_post( $post_id ); wp_raise_memory_limit( 'admin' ); $img = wp_get_image_editor( _load_image_to_edit_path( $post_id ) ); if ( is_wp_error( $img ) ) { return false; } $changes = ! empty( $_REQUEST['history'] ) ? json_decode( wp_unslash( $_REQUEST['history'] ) ) : null; if ( $changes ) { $img = image_edit_apply_changes( $img, $changes ); } // Scale the image. $size = $img->get_size(); $w = $size['width']; $h = $size['height']; $ratio = _image_get_preview_ratio( $w, $h ); $w2 = max( 1, $w * $ratio ); $h2 = max( 1, $h * $ratio ); if ( is_wp_error( $img->resize( $w2, $h2 ) ) ) { return false; } return wp_stream_image( $img, $post->post_mime_type, $post_id ); } /** * Restores the metadata for a given attachment. * * @since 2.9.0 * * @param int $post_id Attachment post ID. * @return stdClass Image restoration message object. */ function wp_restore_image( $post_id ) { $meta = wp_get_attachment_metadata( $post_id ); $file = get_attached_file( $post_id ); $backup_sizes = get_post_meta( $post_id, '_wp_attachment_backup_sizes', true ); $old_backup_sizes = $backup_sizes; $restored = false; $msg = new stdClass(); if ( ! is_array( $backup_sizes ) ) { $msg->error = __( 'Cannot load image metadata.' ); return $msg; } $parts = pathinfo( $file ); $suffix = time() . rand( 100, 999 ); $default_sizes = get_intermediate_image_sizes(); if ( isset( $backup_sizes['full-orig'] ) && is_array( $backup_sizes['full-orig'] ) ) { $data = $backup_sizes['full-orig']; if ( $parts['basename'] !== $data['file'] ) { if ( defined( 'IMAGE_EDIT_OVERWRITE' ) && IMAGE_EDIT_OVERWRITE ) { // Delete only if it's an edited image. if ( preg_match( '/-e[0-9]{13}\./', $parts['basename'] ) ) { wp_delete_file( $file ); } } elseif ( isset( $meta['width'], $meta['height'] ) ) { $backup_sizes[ "full-$suffix" ] = array( 'width' => $meta['width'], 'height' => $meta['height'], 'file' => $parts['basename'], ); } } $restored_file = path_join( $parts['dirname'], $data['file'] ); $restored = update_attached_file( $post_id, $restored_file ); $meta['file'] = _wp_relative_upload_path( $restored_file ); $meta['width'] = $data['width']; $meta['height'] = $data['height']; } foreach ( $default_sizes as $default_size ) { if ( isset( $backup_sizes[ "$default_size-orig" ] ) ) { $data = $backup_sizes[ "$default_size-orig" ]; if ( isset( $meta['sizes'][ $default_size ] ) && $meta['sizes'][ $default_size ]['file'] !== $data['file'] ) { if ( defined( 'IMAGE_EDIT_OVERWRITE' ) && IMAGE_EDIT_OVERWRITE ) { // Delete only if it's an edited image. if ( preg_match( '/-e[0-9]{13}-/', $meta['sizes'][ $default_size ]['file'] ) ) { $delete_file = path_join( $parts['dirname'], $meta['sizes'][ $default_size ]['file'] ); wp_delete_file( $delete_file ); } } else { $backup_sizes[ "$default_size-{$suffix}" ] = $meta['sizes'][ $default_size ]; } } $meta['sizes'][ $default_size ] = $data; } else { unset( $meta['sizes'][ $default_size ] ); } } if ( ! wp_update_attachment_metadata( $post_id, $meta ) || ( $old_backup_sizes !== $backup_sizes && ! update_post_meta( $post_id, '_wp_attachment_backup_sizes', $backup_sizes ) ) ) { $msg->error = __( 'Cannot save image metadata.' ); return $msg; } if ( ! $restored ) { $msg->error = __( 'Image metadata is inconsistent.' ); } else { $msg->msg = __( 'Image restored successfully.' ); if ( defined( 'IMAGE_EDIT_OVERWRITE' ) && IMAGE_EDIT_OVERWRITE ) { delete_post_meta( $post_id, '_wp_attachment_backup_sizes' ); } } return $msg; } /** * Saves image to post, along with enqueued changes * in `$_REQUEST['history']`. * * @since 2.9.0 * * @param int $post_id Attachment post ID. * @return stdClass */ function wp_save_image( $post_id ) { $_wp_additional_image_sizes = wp_get_additional_image_sizes(); $return = new stdClass(); $success = false; $delete = false; $scaled = false; $nocrop = false; $post = get_post( $post_id ); $img = wp_get_image_editor( _load_image_to_edit_path( $post_id, 'full' ) ); if ( is_wp_error( $img ) ) { $return->error = esc_js( __( 'Unable to create new image.' ) ); return $return; } $full_width = ! empty( $_REQUEST['fwidth'] ) ? (int) $_REQUEST['fwidth'] : 0; $full_height = ! empty( $_REQUEST['fheight'] ) ? (int) $_REQUEST['fheight'] : 0; $target = ! empty( $_REQUEST['target'] ) ? preg_replace( '/[^a-z0-9_-]+/i', '', $_REQUEST['target'] ) : ''; $scale = ! empty( $_REQUEST['do'] ) && 'scale' === $_REQUEST['do']; /** This filter is documented in wp-admin/includes/image-edit.php */ $edit_thumbnails_separately = (bool) apply_filters( 'image_edit_thumbnails_separately', false ); if ( $scale ) { $size = $img->get_size(); $original_width = $size['width']; $original_height = $size['height']; if ( $full_width > $original_width || $full_height > $original_height ) { $return->error = esc_js( __( 'Images cannot be scaled to a size larger than the original.' ) ); return $return; } if ( $full_width > 0 && $full_height > 0 ) { // Check if it has roughly the same w / h ratio. $diff = round( $original_width / $original_height, 2 ) - round( $full_width / $full_height, 2 ); if ( -0.1 < $diff && $diff < 0.1 ) { // Scale the full size image. if ( $img->resize( $full_width, $full_height ) ) { $scaled = true; } } if ( ! $scaled ) { $return->error = esc_js( __( 'Error while saving the scaled image. Please reload the page and try again.' ) ); return $return; } } } elseif ( ! empty( $_REQUEST['history'] ) ) { $changes = json_decode( wp_unslash( $_REQUEST['history'] ) ); if ( $changes ) { $img = image_edit_apply_changes( $img, $changes ); } } else { $return->error = esc_js( __( 'Nothing to save, the image has not changed.' ) ); return $return; } $meta = wp_get_attachment_metadata( $post_id ); $backup_sizes = get_post_meta( $post->ID, '_wp_attachment_backup_sizes', true ); if ( ! is_array( $meta ) ) { $return->error = esc_js( __( 'Image data does not exist. Please re-upload the image.' ) ); return $return; } if ( ! is_array( $backup_sizes ) ) { $backup_sizes = array(); } // Generate new filename. $path = get_attached_file( $post_id ); $basename = pathinfo( $path, PATHINFO_BASENAME ); $dirname = pathinfo( $path, PATHINFO_DIRNAME ); $ext = pathinfo( $path, PATHINFO_EXTENSION ); $filename = pathinfo( $path, PATHINFO_FILENAME ); $suffix = time() . rand( 100, 999 ); if ( defined( 'IMAGE_EDIT_OVERWRITE' ) && IMAGE_EDIT_OVERWRITE && isset( $backup_sizes['full-orig'] ) && $backup_sizes['full-orig']['file'] !== $basename ) { if ( $edit_thumbnails_separately && 'thumbnail' === $target ) { $new_path = "{$dirname}/{$filename}-temp.{$ext}"; } else { $new_path = $path; } } else { while ( true ) { $filename = preg_replace( '/-e([0-9]+)$/', '', $filename ); $filename .= "-e{$suffix}"; $new_filename = "{$filename}.{$ext}"; $new_path = "{$dirname}/$new_filename"; if ( file_exists( $new_path ) ) { ++$suffix; } else { break; } } } // Save the full-size file, also needed to create sub-sizes. if ( ! wp_save_image_file( $new_path, $img, $post->post_mime_type, $post_id ) ) { $return->error = esc_js( __( 'Unable to save the image.' ) ); return $return; } if ( 'nothumb' === $target || 'all' === $target || 'full' === $target || $scaled ) { $tag = false; if ( isset( $backup_sizes['full-orig'] ) ) { if ( ( ! defined( 'IMAGE_EDIT_OVERWRITE' ) || ! IMAGE_EDIT_OVERWRITE ) && $backup_sizes['full-orig']['file'] !== $basename ) { $tag = "full-$suffix"; } } else { $tag = 'full-orig'; } if ( $tag ) { $backup_sizes[ $tag ] = array( 'width' => $meta['width'], 'height' => $meta['height'], 'file' => $basename, ); } $success = ( $path === $new_path ) || update_attached_file( $post_id, $new_path ); $meta['file'] = _wp_relative_upload_path( $new_path ); $size = $img->get_size(); $meta['width'] = $size['width']; $meta['height'] = $size['height']; if ( $success && ( 'nothumb' === $target || 'all' === $target ) ) { $sizes = get_intermediate_image_sizes(); if ( $edit_thumbnails_separately && 'nothumb' === $target ) { $sizes = array_diff( $sizes, array( 'thumbnail' ) ); } } $return->fw = $meta['width']; $return->fh = $meta['height']; } elseif ( $edit_thumbnails_separately && 'thumbnail' === $target ) { $sizes = array( 'thumbnail' ); $success = true; $delete = true; $nocrop = true; } /* * We need to remove any existing resized image files because * a new crop or rotate could generate different sizes (and hence, filenames), * keeping the new resized images from overwriting the existing image files. * https://core.trac.wordpress.org/ticket/32171 */ if ( defined( 'IMAGE_EDIT_OVERWRITE' ) && IMAGE_EDIT_OVERWRITE && ! empty( $meta['sizes'] ) ) { foreach ( $meta['sizes'] as $size ) { if ( ! empty( $size['file'] ) && preg_match( '/-e[0-9]{13}-/', $size['file'] ) ) { $delete_file = path_join( $dirname, $size['file'] ); wp_delete_file( $delete_file ); } } } if ( isset( $sizes ) ) { $_sizes = array(); foreach ( $sizes as $size ) { $tag = false; if ( isset( $meta['sizes'][ $size ] ) ) { if ( isset( $backup_sizes[ "$size-orig" ] ) ) { if ( ( ! defined( 'IMAGE_EDIT_OVERWRITE' ) || ! IMAGE_EDIT_OVERWRITE ) && $backup_sizes[ "$size-orig" ]['file'] !== $meta['sizes'][ $size ]['file'] ) { $tag = "$size-$suffix"; } } else { $tag = "$size-orig"; } if ( $tag ) { $backup_sizes[ $tag ] = $meta['sizes'][ $size ]; } } if ( isset( $_wp_additional_image_sizes[ $size ] ) ) { $width = (int) $_wp_additional_image_sizes[ $size ]['width']; $height = (int) $_wp_additional_image_sizes[ $size ]['height']; $crop = ( $nocrop ) ? false : $_wp_additional_image_sizes[ $size ]['crop']; } else { $height = get_option( "{$size}_size_h" ); $width = get_option( "{$size}_size_w" ); $crop = ( $nocrop ) ? false : get_option( "{$size}_crop" ); } $_sizes[ $size ] = array( 'width' => $width, 'height' => $height, 'crop' => $crop, ); } $meta['sizes'] = array_merge( $meta['sizes'], $img->multi_resize( $_sizes ) ); } unset( $img ); if ( $success ) { wp_update_attachment_metadata( $post_id, $meta ); update_post_meta( $post_id, '_wp_attachment_backup_sizes', $backup_sizes ); if ( 'thumbnail' === $target || 'all' === $target || 'full' === $target ) { // Check if it's an image edit from attachment edit screen. if ( ! empty( $_REQUEST['context'] ) && 'edit-attachment' === $_REQUEST['context'] ) { $thumb_url = wp_get_attachment_image_src( $post_id, array( 900, 600 ), true ); $return->thumbnail = $thumb_url[0]; } else { $file_url = wp_get_attachment_url( $post_id ); if ( ! empty( $meta['sizes']['thumbnail'] ) ) { $thumb = $meta['sizes']['thumbnail']; $return->thumbnail = path_join( dirname( $file_url ), $thumb['file'] ); } else { $return->thumbnail = "$file_url?w=128&h=128"; } } } } else { $delete = true; } if ( $delete ) { wp_delete_file( $new_path ); } $return->msg = esc_js( __( 'Image saved' ) ); return $return; } includes/import.php 0000644 00000015024 15135536347 0010413 0 ustar 00 <?php /** * WordPress Administration Importer API. * * @package WordPress * @subpackage Administration */ /** * Retrieves the list of importers. * * @since 2.0.0 * * @global array $wp_importers * @return array */ function get_importers() { global $wp_importers; if ( is_array( $wp_importers ) ) { uasort( $wp_importers, '_usort_by_first_member' ); } return $wp_importers; } /** * Sorts a multidimensional array by first member of each top level member. * * Used by uasort() as a callback, should not be used directly. * * @since 2.9.0 * @access private * * @param array $a * @param array $b * @return int */ function _usort_by_first_member( $a, $b ) { return strnatcasecmp( $a[0], $b[0] ); } /** * Registers importer for WordPress. * * @since 2.0.0 * * @global array $wp_importers * * @param string $id Importer tag. Used to uniquely identify importer. * @param string $name Importer name and title. * @param string $description Importer description. * @param callable $callback Callback to run. * @return void|WP_Error Void on success. WP_Error when $callback is WP_Error. */ function register_importer( $id, $name, $description, $callback ) { global $wp_importers; if ( is_wp_error( $callback ) ) { return $callback; } $wp_importers[ $id ] = array( $name, $description, $callback ); } /** * Cleanup importer. * * Removes attachment based on ID. * * @since 2.0.0 * * @param string $id Importer ID. */ function wp_import_cleanup( $id ) { wp_delete_attachment( $id ); } /** * Handles importer uploading and adds attachment. * * @since 2.0.0 * * @return array Uploaded file's details on success, error message on failure. */ function wp_import_handle_upload() { if ( ! isset( $_FILES['import'] ) ) { return array( 'error' => sprintf( /* translators: 1: php.ini, 2: post_max_size, 3: upload_max_filesize */ __( 'File is empty. Please upload something more substantial. This error could also be caused by uploads being disabled in your %1$s file or by %2$s being defined as smaller than %3$s in %1$s.' ), 'php.ini', 'post_max_size', 'upload_max_filesize' ), ); } $overrides = array( 'test_form' => false, 'test_type' => false, ); $_FILES['import']['name'] .= '.txt'; $upload = wp_handle_upload( $_FILES['import'], $overrides ); if ( isset( $upload['error'] ) ) { return $upload; } // Construct the attachment array. $attachment = array( 'post_title' => wp_basename( $upload['file'] ), 'post_content' => $upload['url'], 'post_mime_type' => $upload['type'], 'guid' => $upload['url'], 'context' => 'import', 'post_status' => 'private', ); // Save the data. $id = wp_insert_attachment( $attachment, $upload['file'] ); /* * Schedule a cleanup for one day from now in case of failed * import or missing wp_import_cleanup() call. */ wp_schedule_single_event( time() + DAY_IN_SECONDS, 'importer_scheduled_cleanup', array( $id ) ); return array( 'file' => $upload['file'], 'id' => $id, ); } /** * Returns a list from WordPress.org of popular importer plugins. * * @since 3.5.0 * * @return array Importers with metadata for each. */ function wp_get_popular_importers() { // Include an unmodified $wp_version. require ABSPATH . WPINC . '/version.php'; $locale = get_user_locale(); $cache_key = 'popular_importers_' . md5( $locale . $wp_version ); $popular_importers = get_site_transient( $cache_key ); if ( ! $popular_importers ) { $url = add_query_arg( array( 'locale' => $locale, 'version' => $wp_version, ), 'http://api.wordpress.org/core/importers/1.1/' ); $options = array( 'user-agent' => 'WordPress/' . $wp_version . '; ' . home_url( '/' ) ); if ( wp_http_supports( array( 'ssl' ) ) ) { $url = set_url_scheme( $url, 'https' ); } $response = wp_remote_get( $url, $options ); $popular_importers = json_decode( wp_remote_retrieve_body( $response ), true ); if ( is_array( $popular_importers ) ) { set_site_transient( $cache_key, $popular_importers, 2 * DAY_IN_SECONDS ); } else { $popular_importers = false; } } if ( is_array( $popular_importers ) ) { // If the data was received as translated, return it as-is. if ( $popular_importers['translated'] ) { return $popular_importers['importers']; } foreach ( $popular_importers['importers'] as &$importer ) { // phpcs:ignore WordPress.WP.I18n.LowLevelTranslationFunction,WordPress.WP.I18n.NonSingularStringLiteralText $importer['description'] = translate( $importer['description'] ); if ( 'WordPress' !== $importer['name'] ) { // phpcs:ignore WordPress.WP.I18n.LowLevelTranslationFunction,WordPress.WP.I18n.NonSingularStringLiteralText $importer['name'] = translate( $importer['name'] ); } } return $popular_importers['importers']; } return array( // slug => name, description, plugin slug, and register_importer() slug. 'blogger' => array( 'name' => __( 'Blogger' ), 'description' => __( 'Import posts, comments, and users from a Blogger blog.' ), 'plugin-slug' => 'blogger-importer', 'importer-id' => 'blogger', ), 'wpcat2tag' => array( 'name' => __( 'Categories and Tags Converter' ), 'description' => __( 'Convert existing categories to tags or tags to categories, selectively.' ), 'plugin-slug' => 'wpcat2tag-importer', 'importer-id' => 'wp-cat2tag', ), 'livejournal' => array( 'name' => __( 'LiveJournal' ), 'description' => __( 'Import posts from LiveJournal using their API.' ), 'plugin-slug' => 'livejournal-importer', 'importer-id' => 'livejournal', ), 'movabletype' => array( 'name' => __( 'Movable Type and TypePad' ), 'description' => __( 'Import posts and comments from a Movable Type or TypePad blog.' ), 'plugin-slug' => 'movabletype-importer', 'importer-id' => 'mt', ), 'rss' => array( 'name' => __( 'RSS' ), 'description' => __( 'Import posts from an RSS feed.' ), 'plugin-slug' => 'rss-importer', 'importer-id' => 'rss', ), 'tumblr' => array( 'name' => __( 'Tumblr' ), 'description' => __( 'Import posts & media from Tumblr using their API.' ), 'plugin-slug' => 'tumblr-importer', 'importer-id' => 'tumblr', ), 'wordpress' => array( 'name' => 'WordPress', 'description' => __( 'Import posts, pages, comments, custom fields, categories, and tags from a WordPress export file.' ), 'plugin-slug' => 'wordpress-importer', 'importer-id' => 'wordpress', ), ); } includes/theme-install.php 0000644 00000015516 15135536347 0011655 0 ustar 00 <?php /** * WordPress Theme Installation Administration API * * @package WordPress * @subpackage Administration */ $themes_allowedtags = array( 'a' => array( 'href' => array(), 'title' => array(), 'target' => array(), ), 'abbr' => array( 'title' => array() ), 'acronym' => array( 'title' => array() ), 'code' => array(), 'pre' => array(), 'em' => array(), 'strong' => array(), 'div' => array(), 'p' => array(), 'ul' => array(), 'ol' => array(), 'li' => array(), 'h1' => array(), 'h2' => array(), 'h3' => array(), 'h4' => array(), 'h5' => array(), 'h6' => array(), 'img' => array( 'src' => array(), 'class' => array(), 'alt' => array(), ), ); $theme_field_defaults = array( 'description' => true, 'sections' => false, 'tested' => true, 'requires' => true, 'rating' => true, 'downloaded' => true, 'downloadlink' => true, 'last_updated' => true, 'homepage' => true, 'tags' => true, 'num_ratings' => true, ); /** * Retrieves the list of WordPress theme features (aka theme tags). * * @since 2.8.0 * * @deprecated 3.1.0 Use get_theme_feature_list() instead. * * @return array */ function install_themes_feature_list() { _deprecated_function( __FUNCTION__, '3.1.0', 'get_theme_feature_list()' ); $cache = get_transient( 'wporg_theme_feature_list' ); if ( ! $cache ) { set_transient( 'wporg_theme_feature_list', array(), 3 * HOUR_IN_SECONDS ); } if ( $cache ) { return $cache; } $feature_list = themes_api( 'feature_list', array() ); if ( is_wp_error( $feature_list ) ) { return array(); } set_transient( 'wporg_theme_feature_list', $feature_list, 3 * HOUR_IN_SECONDS ); return $feature_list; } /** * Displays search form for searching themes. * * @since 2.8.0 * * @param bool $type_selector */ function install_theme_search_form( $type_selector = true ) { $type = isset( $_REQUEST['type'] ) ? wp_unslash( $_REQUEST['type'] ) : 'term'; $term = isset( $_REQUEST['s'] ) ? wp_unslash( $_REQUEST['s'] ) : ''; if ( ! $type_selector ) { echo '<p class="install-help">' . __( 'Search for themes by keyword.' ) . '</p>'; } ?> <form id="search-themes" method="get"> <input type="hidden" name="tab" value="search" /> <?php if ( $type_selector ) : ?> <label class="screen-reader-text" for="typeselector"> <?php /* translators: Hidden accessibility text. */ _e( 'Type of search' ); ?> </label> <select name="type" id="typeselector"> <option value="term" <?php selected( 'term', $type ); ?>><?php _e( 'Keyword' ); ?></option> <option value="author" <?php selected( 'author', $type ); ?>><?php _e( 'Author' ); ?></option> <option value="tag" <?php selected( 'tag', $type ); ?>><?php _ex( 'Tag', 'Theme Installer' ); ?></option> </select> <label class="screen-reader-text" for="s"> <?php switch ( $type ) { case 'term': /* translators: Hidden accessibility text. */ _e( 'Search by keyword' ); break; case 'author': /* translators: Hidden accessibility text. */ _e( 'Search by author' ); break; case 'tag': /* translators: Hidden accessibility text. */ _e( 'Search by tag' ); break; } ?> </label> <?php else : ?> <label class="screen-reader-text" for="s"> <?php /* translators: Hidden accessibility text. */ _e( 'Search by keyword' ); ?> </label> <?php endif; ?> <input type="search" name="s" id="s" size="30" value="<?php echo esc_attr( $term ); ?>" autofocus="autofocus" /> <?php submit_button( __( 'Search' ), '', 'search', false ); ?> </form> <?php } /** * Displays tags filter for themes. * * @since 2.8.0 */ function install_themes_dashboard() { install_theme_search_form( false ); ?> <h4><?php _e( 'Feature Filter' ); ?></h4> <p class="install-help"><?php _e( 'Find a theme based on specific features.' ); ?></p> <form method="get"> <input type="hidden" name="tab" value="search" /> <?php $feature_list = get_theme_feature_list(); echo '<div class="feature-filter">'; foreach ( (array) $feature_list as $feature_name => $features ) { $feature_name = esc_html( $feature_name ); echo '<div class="feature-name">' . $feature_name . '</div>'; echo '<ol class="feature-group">'; foreach ( $features as $feature => $feature_name ) { $feature_name = esc_html( $feature_name ); $feature = esc_attr( $feature ); ?> <li> <input type="checkbox" name="features[]" id="feature-id-<?php echo $feature; ?>" value="<?php echo $feature; ?>" /> <label for="feature-id-<?php echo $feature; ?>"><?php echo $feature_name; ?></label> </li> <?php } ?> </ol> <br class="clear" /> <?php } ?> </div> <br class="clear" /> <?php submit_button( __( 'Find Themes' ), '', 'search' ); ?> </form> <?php } /** * Displays a form to upload themes from zip files. * * @since 2.8.0 */ function install_themes_upload() { ?> <p class="install-help"><?php _e( 'If you have a theme in a .zip format, you may install or update it by uploading it here.' ); ?></p> <form method="post" enctype="multipart/form-data" class="wp-upload-form" action="<?php echo esc_url( self_admin_url( 'update.php?action=upload-theme' ) ); ?>"> <?php wp_nonce_field( 'theme-upload' ); ?> <label class="screen-reader-text" for="themezip"> <?php /* translators: Hidden accessibility text. */ _e( 'Theme zip file' ); ?> </label> <input type="file" id="themezip" name="themezip" accept=".zip" /> <?php submit_button( _x( 'Install Now', 'theme' ), '', 'install-theme-submit', false ); ?> </form> <?php } /** * Prints a theme on the Install Themes pages. * * @deprecated 3.4.0 * * @global WP_Theme_Install_List_Table $wp_list_table * * @param object $theme */ function display_theme( $theme ) { _deprecated_function( __FUNCTION__, '3.4.0' ); global $wp_list_table; if ( ! isset( $wp_list_table ) ) { $wp_list_table = _get_list_table( 'WP_Theme_Install_List_Table' ); } $wp_list_table->prepare_items(); $wp_list_table->single_row( $theme ); } /** * Displays theme content based on theme list. * * @since 2.8.0 * * @global WP_Theme_Install_List_Table $wp_list_table */ function display_themes() { global $wp_list_table; if ( ! isset( $wp_list_table ) ) { $wp_list_table = _get_list_table( 'WP_Theme_Install_List_Table' ); } $wp_list_table->prepare_items(); $wp_list_table->display(); } /** * Displays theme information in dialog box form. * * @since 2.8.0 * * @global WP_Theme_Install_List_Table $wp_list_table */ function install_theme_information() { global $wp_list_table; $theme = themes_api( 'theme_information', array( 'slug' => wp_unslash( $_REQUEST['theme'] ) ) ); if ( is_wp_error( $theme ) ) { wp_die( $theme ); } iframe_header( __( 'Theme Installation' ) ); if ( ! isset( $wp_list_table ) ) { $wp_list_table = _get_list_table( 'WP_Theme_Install_List_Table' ); } $wp_list_table->theme_installer_single( $theme ); iframe_footer(); exit; } includes/network.php 0000644 00000065653 15135536347 0010607 0 ustar 00 <?php /** * WordPress Network Administration API. * * @package WordPress * @subpackage Administration * @since 4.4.0 */ /** * Check for an existing network. * * @since 3.0.0 * * @global wpdb $wpdb WordPress database abstraction object. * * @return string|false Base domain if network exists, otherwise false. */ function network_domain_check() { global $wpdb; $sql = $wpdb->prepare( 'SHOW TABLES LIKE %s', $wpdb->esc_like( $wpdb->site ) ); if ( $wpdb->get_var( $sql ) ) { return $wpdb->get_var( "SELECT domain FROM $wpdb->site ORDER BY id ASC LIMIT 1" ); } return false; } /** * Allow subdomain installation * * @since 3.0.0 * @return bool Whether subdomain installation is allowed */ function allow_subdomain_install() { $domain = preg_replace( '|https?://([^/]+)|', '$1', get_option( 'home' ) ); if ( parse_url( get_option( 'home' ), PHP_URL_PATH ) || 'localhost' === $domain || preg_match( '|^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$|', $domain ) ) { return false; } return true; } /** * Allow subdirectory installation. * * @since 3.0.0 * * @global wpdb $wpdb WordPress database abstraction object. * * @return bool Whether subdirectory installation is allowed */ function allow_subdirectory_install() { global $wpdb; /** * Filters whether to enable the subdirectory installation feature in Multisite. * * @since 3.0.0 * * @param bool $allow Whether to enable the subdirectory installation feature in Multisite. * Default false. */ if ( apply_filters( 'allow_subdirectory_install', false ) ) { return true; } if ( defined( 'ALLOW_SUBDIRECTORY_INSTALL' ) && ALLOW_SUBDIRECTORY_INSTALL ) { return true; } $post = $wpdb->get_row( "SELECT ID FROM $wpdb->posts WHERE post_date < DATE_SUB(NOW(), INTERVAL 1 MONTH) AND post_status = 'publish'" ); if ( empty( $post ) ) { return true; } return false; } /** * Get base domain of network. * * @since 3.0.0 * @return string Base domain. */ function get_clean_basedomain() { $existing_domain = network_domain_check(); if ( $existing_domain ) { return $existing_domain; } $domain = preg_replace( '|https?://|', '', get_option( 'siteurl' ) ); $slash = strpos( $domain, '/' ); if ( $slash ) { $domain = substr( $domain, 0, $slash ); } return $domain; } /** * Prints step 1 for Network installation process. * * @todo Realistically, step 1 should be a welcome screen explaining what a Network is and such. * Navigating to Tools > Network should not be a sudden "Welcome to a new install process! * Fill this out and click here." See also contextual help todo. * * @since 3.0.0 * * @global bool $is_apache * * @param false|WP_Error $errors Optional. Error object. Default false. */ function network_step1( $errors = false ) { global $is_apache; if ( defined( 'DO_NOT_UPGRADE_GLOBAL_TABLES' ) ) { $cannot_define_constant_message = '<strong>' . __( 'Error:' ) . '</strong> '; $cannot_define_constant_message .= sprintf( /* translators: %s: DO_NOT_UPGRADE_GLOBAL_TABLES */ __( 'The constant %s cannot be defined when creating a network.' ), '<code>DO_NOT_UPGRADE_GLOBAL_TABLES</code>' ); wp_admin_notice( $cannot_define_constant_message, array( 'additional_classes' => array( 'error' ), ) ); echo '</div>'; require_once ABSPATH . 'wp-admin/admin-footer.php'; die(); } $active_plugins = get_option( 'active_plugins' ); if ( ! empty( $active_plugins ) ) { wp_admin_notice( '<strong>' . __( 'Warning:' ) . '</strong> ' . sprintf( /* translators: %s: URL to Plugins screen. */ __( 'Please <a href="%s">deactivate your plugins</a> before enabling the Network feature.' ), admin_url( 'plugins.php?plugin_status=active' ) ), array( 'type' => 'warning' ) ); echo '<p>' . __( 'Once the network is created, you may reactivate your plugins.' ) . '</p>'; echo '</div>'; require_once ABSPATH . 'wp-admin/admin-footer.php'; die(); } $hostname = get_clean_basedomain(); $has_ports = strstr( $hostname, ':' ); if ( ( false !== $has_ports && ! in_array( $has_ports, array( ':80', ':443' ), true ) ) ) { wp_admin_notice( '<strong>' . __( 'Error:' ) . '</strong> ' . __( 'You cannot install a network of sites with your server address.' ), array( 'additional_classes' => array( 'error' ), ) ); echo '<p>' . sprintf( /* translators: %s: Port number. */ __( 'You cannot use port numbers such as %s.' ), '<code>' . $has_ports . '</code>' ) . '</p>'; echo '<a href="' . esc_url( admin_url() ) . '">' . __( 'Go to Dashboard' ) . '</a>'; echo '</div>'; require_once ABSPATH . 'wp-admin/admin-footer.php'; die(); } echo '<form method="post">'; wp_nonce_field( 'install-network-1' ); $error_codes = array(); if ( is_wp_error( $errors ) ) { $network_created_error_message = '<p><strong>' . __( 'Error: The network could not be created.' ) . '</strong></p>'; foreach ( $errors->get_error_messages() as $error ) { $network_created_error_message .= "<p>$error</p>"; } wp_admin_notice( $network_created_error_message, array( 'additional_classes' => array( 'error' ), 'paragraph_wrap' => false, ) ); $error_codes = $errors->get_error_codes(); } if ( ! empty( $_POST['sitename'] ) && ! in_array( 'empty_sitename', $error_codes, true ) ) { $site_name = $_POST['sitename']; } else { /* translators: %s: Default network title. */ $site_name = sprintf( __( '%s Sites' ), get_option( 'blogname' ) ); } if ( ! empty( $_POST['email'] ) && ! in_array( 'invalid_email', $error_codes, true ) ) { $admin_email = $_POST['email']; } else { $admin_email = get_option( 'admin_email' ); } ?> <p><?php _e( 'Welcome to the Network installation process!' ); ?></p> <p><?php _e( 'Fill in the information below and you’ll be on your way to creating a network of WordPress sites. Configuration files will be created in the next step.' ); ?></p> <?php if ( isset( $_POST['subdomain_install'] ) ) { $subdomain_install = (bool) $_POST['subdomain_install']; } elseif ( apache_mod_loaded( 'mod_rewrite' ) ) { // Assume nothing. $subdomain_install = true; } elseif ( ! allow_subdirectory_install() ) { $subdomain_install = true; } else { $subdomain_install = false; $got_mod_rewrite = got_mod_rewrite(); if ( $got_mod_rewrite ) { // Dangerous assumptions. $message_class = 'updated'; $message = '<p><strong>' . __( 'Warning:' ) . '</strong> '; $message .= '<p>' . sprintf( /* translators: %s: mod_rewrite */ __( 'Please make sure the Apache %s module is installed as it will be used at the end of this installation.' ), '<code>mod_rewrite</code>' ) . '</p>'; } elseif ( $is_apache ) { $message_class = 'error'; $message = '<p><strong>' . __( 'Warning:' ) . '</strong> '; $message .= sprintf( /* translators: %s: mod_rewrite */ __( 'It looks like the Apache %s module is not installed.' ), '<code>mod_rewrite</code>' ) . '</p>'; } if ( $got_mod_rewrite || $is_apache ) { // Protect against mod_rewrite mimicry (but ! Apache). $message .= '<p>' . sprintf( /* translators: 1: mod_rewrite, 2: mod_rewrite documentation URL, 3: Google search for mod_rewrite. */ __( 'If %1$s is disabled, ask your administrator to enable that module, or look at the <a href="%2$s">Apache documentation</a> or <a href="%3$s">elsewhere</a> for help setting it up.' ), '<code>mod_rewrite</code>', 'https://httpd.apache.org/docs/mod/mod_rewrite.html', 'https://www.google.com/search?q=apache+mod_rewrite' ) . '</p>'; wp_admin_notice( $message, array( 'additional_classes' => array( $message_class, 'inline' ), 'paragraph_wrap' => false, ) ); } } if ( allow_subdomain_install() && allow_subdirectory_install() ) : ?> <h3><?php esc_html_e( 'Addresses of Sites in your Network' ); ?></h3> <p><?php _e( 'Please choose whether you would like sites in your WordPress network to use sub-domains or sub-directories.' ); ?> <strong><?php _e( 'You cannot change this later.' ); ?></strong></p> <p><?php _e( 'You will need a wildcard DNS record if you are going to use the virtual host (sub-domain) functionality.' ); ?></p> <?php // @todo Link to an MS readme? ?> <table class="form-table" role="presentation"> <tr> <th><label><input type="radio" name="subdomain_install" value="1"<?php checked( $subdomain_install ); ?> /> <?php _e( 'Sub-domains' ); ?></label></th> <td> <?php printf( /* translators: 1: Host name. */ _x( 'like <code>site1.%1$s</code> and <code>site2.%1$s</code>', 'subdomain examples' ), $hostname ); ?> </td> </tr> <tr> <th><label><input type="radio" name="subdomain_install" value="0"<?php checked( ! $subdomain_install ); ?> /> <?php _e( 'Sub-directories' ); ?></label></th> <td> <?php printf( /* translators: 1: Host name. */ _x( 'like <code>%1$s/site1</code> and <code>%1$s/site2</code>', 'subdirectory examples' ), $hostname ); ?> </td> </tr> </table> <?php endif; if ( WP_CONTENT_DIR !== ABSPATH . 'wp-content' && ( allow_subdirectory_install() || ! allow_subdomain_install() ) ) { $subdirectory_warning_message = '<strong>' . __( 'Warning:' ) . '</strong> '; $subdirectory_warning_message .= __( 'Subdirectory networks may not be fully compatible with custom wp-content directories.' ); wp_admin_notice( $subdirectory_warning_message, array( 'additional_classes' => array( 'error', 'inline' ), ) ); } $is_www = str_starts_with( $hostname, 'www.' ); if ( $is_www ) : ?> <h3><?php esc_html_e( 'Server Address' ); ?></h3> <p> <?php printf( /* translators: 1: Site URL, 2: Host name, 3: www. */ __( 'You should consider changing your site domain to %1$s before enabling the network feature. It will still be possible to visit your site using the %3$s prefix with an address like %2$s but any links will not have the %3$s prefix.' ), '<code>' . substr( $hostname, 4 ) . '</code>', '<code>' . $hostname . '</code>', '<code>www</code>' ); ?> </p> <table class="form-table" role="presentation"> <tr> <th scope='row'><?php esc_html_e( 'Server Address' ); ?></th> <td> <?php printf( /* translators: %s: Host name. */ __( 'The internet address of your network will be %s.' ), '<code>' . $hostname . '</code>' ); ?> </td> </tr> </table> <?php endif; ?> <h3><?php esc_html_e( 'Network Details' ); ?></h3> <table class="form-table" role="presentation"> <?php if ( 'localhost' === $hostname ) : ?> <tr> <th scope="row"><?php esc_html_e( 'Sub-directory Installation' ); ?></th> <td> <?php printf( /* translators: 1: localhost, 2: localhost.localdomain */ __( 'Because you are using %1$s, the sites in your WordPress network must use sub-directories. Consider using %2$s if you wish to use sub-domains.' ), '<code>localhost</code>', '<code>localhost.localdomain</code>' ); // Uh oh: if ( ! allow_subdirectory_install() ) { echo ' <strong>' . __( 'Warning:' ) . ' ' . __( 'The main site in a sub-directory installation will need to use a modified permalink structure, potentially breaking existing links.' ) . '</strong>'; } ?> </td> </tr> <?php elseif ( ! allow_subdomain_install() ) : ?> <tr> <th scope="row"><?php esc_html_e( 'Sub-directory Installation' ); ?></th> <td> <?php _e( 'Because your installation is in a directory, the sites in your WordPress network must use sub-directories.' ); // Uh oh: if ( ! allow_subdirectory_install() ) { echo ' <strong>' . __( 'Warning:' ) . ' ' . __( 'The main site in a sub-directory installation will need to use a modified permalink structure, potentially breaking existing links.' ) . '</strong>'; } ?> </td> </tr> <?php elseif ( ! allow_subdirectory_install() ) : ?> <tr> <th scope="row"><?php esc_html_e( 'Sub-domain Installation' ); ?></th> <td> <?php _e( 'Because your installation is not new, the sites in your WordPress network must use sub-domains.' ); echo ' <strong>' . __( 'The main site in a sub-directory installation will need to use a modified permalink structure, potentially breaking existing links.' ) . '</strong>'; ?> </td> </tr> <?php endif; ?> <?php if ( ! $is_www ) : ?> <tr> <th scope='row'><?php esc_html_e( 'Server Address' ); ?></th> <td> <?php printf( /* translators: %s: Host name. */ __( 'The internet address of your network will be %s.' ), '<code>' . $hostname . '</code>' ); ?> </td> </tr> <?php endif; ?> <tr> <th scope='row'><label for="sitename"><?php esc_html_e( 'Network Title' ); ?></label></th> <td> <input name='sitename' id='sitename' type='text' size='45' value='<?php echo esc_attr( $site_name ); ?>' /> <p class="description"> <?php _e( 'What would you like to call your network?' ); ?> </p> </td> </tr> <tr> <th scope='row'><label for="email"><?php esc_html_e( 'Network Admin Email' ); ?></label></th> <td> <input name='email' id='email' type='text' size='45' value='<?php echo esc_attr( $admin_email ); ?>' /> <p class="description"> <?php _e( 'Your email address.' ); ?> </p> </td> </tr> </table> <?php submit_button( __( 'Install' ), 'primary', 'submit' ); ?> </form> <?php } /** * Prints step 2 for Network installation process. * * @since 3.0.0 * * @global wpdb $wpdb WordPress database abstraction object. * @global bool $is_nginx Whether the server software is Nginx or something else. * * @param false|WP_Error $errors Optional. Error object. Default false. */ function network_step2( $errors = false ) { global $wpdb, $is_nginx; $hostname = get_clean_basedomain(); $slashed_home = trailingslashit( get_option( 'home' ) ); $base = parse_url( $slashed_home, PHP_URL_PATH ); $document_root_fix = str_replace( '\\', '/', realpath( $_SERVER['DOCUMENT_ROOT'] ) ); $abspath_fix = str_replace( '\\', '/', ABSPATH ); $home_path = str_starts_with( $abspath_fix, $document_root_fix ) ? $document_root_fix . $base : get_home_path(); $wp_siteurl_subdir = preg_replace( '#^' . preg_quote( $home_path, '#' ) . '#', '', $abspath_fix ); $rewrite_base = ! empty( $wp_siteurl_subdir ) ? ltrim( trailingslashit( $wp_siteurl_subdir ), '/' ) : ''; $location_of_wp_config = $abspath_fix; if ( ! file_exists( ABSPATH . 'wp-config.php' ) && file_exists( dirname( ABSPATH ) . '/wp-config.php' ) ) { $location_of_wp_config = dirname( $abspath_fix ); } $location_of_wp_config = trailingslashit( $location_of_wp_config ); // Wildcard DNS message. if ( is_wp_error( $errors ) ) { wp_admin_notice( $errors->get_error_message(), array( 'additional_classes' => array( 'error' ), ) ); } if ( $_POST ) { if ( allow_subdomain_install() ) { $subdomain_install = allow_subdirectory_install() ? ! empty( $_POST['subdomain_install'] ) : true; } else { $subdomain_install = false; } } else { if ( is_multisite() ) { $subdomain_install = is_subdomain_install(); ?> <p><?php _e( 'The original configuration steps are shown here for reference.' ); ?></p> <?php } else { $subdomain_install = (bool) $wpdb->get_var( "SELECT meta_value FROM $wpdb->sitemeta WHERE site_id = 1 AND meta_key = 'subdomain_install'" ); wp_admin_notice( '<strong>' . __( 'Warning:' ) . '</strong> ' . __( 'An existing WordPress network was detected.' ), array( 'additional_classes' => array( 'error' ), ) ); ?> <p><?php _e( 'Please complete the configuration steps. To create a new network, you will need to empty or remove the network database tables.' ); ?></p> <?php } } $subdir_match = $subdomain_install ? '' : '([_0-9a-zA-Z-]+/)?'; $subdir_replacement_01 = $subdomain_install ? '' : '$1'; $subdir_replacement_12 = $subdomain_install ? '$1' : '$2'; if ( $_POST || ! is_multisite() ) { ?> <h3><?php esc_html_e( 'Enabling the Network' ); ?></h3> <p><?php _e( 'Complete the following steps to enable the features for creating a network of sites.' ); ?></p> <?php $notice_message = '<strong>' . __( 'Caution:' ) . '</strong> '; $notice_args = array( 'type' => 'warning', 'additional_classes' => array( 'inline' ), ); if ( file_exists( $home_path . '.htaccess' ) ) { $notice_message .= sprintf( /* translators: 1: wp-config.php, 2: .htaccess */ __( 'You should back up your existing %1$s and %2$s files.' ), '<code>wp-config.php</code>', '<code>.htaccess</code>' ); } elseif ( file_exists( $home_path . 'web.config' ) ) { $notice_message .= sprintf( /* translators: 1: wp-config.php, 2: web.config */ __( 'You should back up your existing %1$s and %2$s files.' ), '<code>wp-config.php</code>', '<code>web.config</code>' ); } else { $notice_message .= sprintf( /* translators: %s: wp-config.php */ __( 'You should back up your existing %s file.' ), '<code>wp-config.php</code>' ); } wp_admin_notice( $notice_message, $notice_args ); } ?> <ol> <li><p id="network-wpconfig-rules-description"> <?php printf( /* translators: 1: wp-config.php, 2: Location of wp-config file, 3: Translated version of "That's all, stop editing! Happy publishing." */ __( 'Add the following to your %1$s file in %2$s <strong>above</strong> the line reading %3$s:' ), '<code>wp-config.php</code>', '<code>' . $location_of_wp_config . '</code>', /* * translators: This string should only be translated if wp-config-sample.php is localized. * You can check the localized release package or * https://i18n.svn.wordpress.org/<locale code>/branches/<wp version>/dist/wp-config-sample.php */ '<code>/* ' . __( 'That’s all, stop editing! Happy publishing.' ) . ' */</code>' ); ?> </p> <p class="configuration-rules-label"><label for="network-wpconfig-rules"> <?php printf( /* translators: %s: File name (wp-config.php, .htaccess or web.config). */ __( 'Network configuration rules for %s' ), '<code>wp-config.php</code>' ); ?> </label></p> <textarea id="network-wpconfig-rules" class="code" readonly="readonly" cols="100" rows="7" aria-describedby="network-wpconfig-rules-description"> define( 'MULTISITE', true ); define( 'SUBDOMAIN_INSTALL', <?php echo $subdomain_install ? 'true' : 'false'; ?> ); define( 'DOMAIN_CURRENT_SITE', '<?php echo $hostname; ?>' ); define( 'PATH_CURRENT_SITE', '<?php echo $base; ?>' ); define( 'SITE_ID_CURRENT_SITE', 1 ); define( 'BLOG_ID_CURRENT_SITE', 1 ); </textarea> <?php $keys_salts = array( 'AUTH_KEY' => '', 'SECURE_AUTH_KEY' => '', 'LOGGED_IN_KEY' => '', 'NONCE_KEY' => '', 'AUTH_SALT' => '', 'SECURE_AUTH_SALT' => '', 'LOGGED_IN_SALT' => '', 'NONCE_SALT' => '', ); foreach ( $keys_salts as $c => $v ) { if ( defined( $c ) ) { unset( $keys_salts[ $c ] ); } } if ( ! empty( $keys_salts ) ) { $keys_salts_str = ''; $from_api = wp_remote_get( 'https://api.wordpress.org/secret-key/1.1/salt/' ); if ( is_wp_error( $from_api ) ) { foreach ( $keys_salts as $c => $v ) { $keys_salts_str .= "\ndefine( '$c', '" . wp_generate_password( 64, true, true ) . "' );"; } } else { $from_api = explode( "\n", wp_remote_retrieve_body( $from_api ) ); foreach ( $keys_salts as $c => $v ) { $keys_salts_str .= "\ndefine( '$c', '" . substr( array_shift( $from_api ), 28, 64 ) . "' );"; } } $num_keys_salts = count( $keys_salts ); ?> <p id="network-wpconfig-authentication-description"> <?php if ( 1 === $num_keys_salts ) { printf( /* translators: %s: wp-config.php */ __( 'This unique authentication key is also missing from your %s file.' ), '<code>wp-config.php</code>' ); } else { printf( /* translators: %s: wp-config.php */ __( 'These unique authentication keys are also missing from your %s file.' ), '<code>wp-config.php</code>' ); } ?> <?php _e( 'To make your installation more secure, you should also add:' ); ?> </p> <p class="configuration-rules-label"><label for="network-wpconfig-authentication"><?php _e( 'Network configuration authentication keys' ); ?></label></p> <textarea id="network-wpconfig-authentication" class="code" readonly="readonly" cols="100" rows="<?php echo $num_keys_salts; ?>" aria-describedby="network-wpconfig-authentication-description"><?php echo esc_textarea( $keys_salts_str ); ?></textarea> <?php } ?> </li> <?php if ( iis7_supports_permalinks() ) : // IIS doesn't support RewriteBase, all your RewriteBase are belong to us. $iis_subdir_match = ltrim( $base, '/' ) . $subdir_match; $iis_rewrite_base = ltrim( $base, '/' ) . $rewrite_base; $iis_subdir_replacement = $subdomain_install ? '' : '{R:1}'; $web_config_file = '<?xml version="1.0" encoding="UTF-8"?> <configuration> <system.webServer> <rewrite> <rules> <rule name="WordPress Rule 1" stopProcessing="true"> <match url="^index\.php$" ignoreCase="false" /> <action type="None" /> </rule>'; if ( is_multisite() && get_site_option( 'ms_files_rewriting' ) ) { $web_config_file .= ' <rule name="WordPress Rule for Files" stopProcessing="true"> <match url="^' . $iis_subdir_match . 'files/(.+)" ignoreCase="false" /> <action type="Rewrite" url="' . $iis_rewrite_base . WPINC . '/ms-files.php?file={R:1}" appendQueryString="false" /> </rule>'; } $web_config_file .= ' <rule name="WordPress Rule 2" stopProcessing="true"> <match url="^' . $iis_subdir_match . 'wp-admin$" ignoreCase="false" /> <action type="Redirect" url="' . $iis_subdir_replacement . 'wp-admin/" redirectType="Permanent" /> </rule> <rule name="WordPress Rule 3" stopProcessing="true"> <match url="^" ignoreCase="false" /> <conditions logicalGrouping="MatchAny"> <add input="{REQUEST_FILENAME}" matchType="IsFile" ignoreCase="false" /> <add input="{REQUEST_FILENAME}" matchType="IsDirectory" ignoreCase="false" /> </conditions> <action type="None" /> </rule> <rule name="WordPress Rule 4" stopProcessing="true"> <match url="^' . $iis_subdir_match . '(wp-(content|admin|includes).*)" ignoreCase="false" /> <action type="Rewrite" url="' . $iis_rewrite_base . '{R:1}" /> </rule> <rule name="WordPress Rule 5" stopProcessing="true"> <match url="^' . $iis_subdir_match . '([_0-9a-zA-Z-]+/)?(.*\.php)$" ignoreCase="false" /> <action type="Rewrite" url="' . $iis_rewrite_base . '{R:2}" /> </rule> <rule name="WordPress Rule 6" stopProcessing="true"> <match url="." ignoreCase="false" /> <action type="Rewrite" url="index.php" /> </rule> </rules> </rewrite> </system.webServer> </configuration> '; echo '<li><p id="network-webconfig-rules-description">'; printf( /* translators: 1: File name (.htaccess or web.config), 2: File path. */ __( 'Add the following to your %1$s file in %2$s, <strong>replacing</strong> other WordPress rules:' ), '<code>web.config</code>', '<code>' . $home_path . '</code>' ); echo '</p>'; if ( ! $subdomain_install && WP_CONTENT_DIR !== ABSPATH . 'wp-content' ) { echo '<p><strong>' . __( 'Warning:' ) . ' ' . __( 'Subdirectory networks may not be fully compatible with custom wp-content directories.' ) . '</strong></p>'; } ?> <p class="configuration-rules-label"><label for="network-webconfig-rules"> <?php printf( /* translators: %s: File name (wp-config.php, .htaccess or web.config). */ __( 'Network configuration rules for %s' ), '<code>web.config</code>' ); ?> </label></p> <textarea id="network-webconfig-rules" class="code" readonly="readonly" cols="100" rows="20" aria-describedby="network-webconfig-rules-description"><?php echo esc_textarea( $web_config_file ); ?></textarea> </li> </ol> <?php elseif ( $is_nginx ) : // End iis7_supports_permalinks(). Link to Nginx documentation instead: echo '<li><p>'; printf( /* translators: %s: Documentation URL. */ __( 'It seems your network is running with Nginx web server. <a href="%s">Learn more about further configuration</a>.' ), __( 'https://wordpress.org/documentation/article/nginx/' ) ); echo '</p></li>'; else : // End $is_nginx. Construct an .htaccess file instead: $ms_files_rewriting = ''; if ( is_multisite() && get_site_option( 'ms_files_rewriting' ) ) { $ms_files_rewriting = "\n# uploaded files\nRewriteRule ^"; $ms_files_rewriting .= $subdir_match . "files/(.+) {$rewrite_base}" . WPINC . "/ms-files.php?file={$subdir_replacement_12} [L]" . "\n"; } $htaccess_file = <<<EOF RewriteEngine On RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] RewriteBase {$base} RewriteRule ^index\.php$ - [L] {$ms_files_rewriting} # add a trailing slash to /wp-admin RewriteRule ^{$subdir_match}wp-admin$ {$subdir_replacement_01}wp-admin/ [R=301,L] RewriteCond %{REQUEST_FILENAME} -f [OR] RewriteCond %{REQUEST_FILENAME} -d RewriteRule ^ - [L] RewriteRule ^{$subdir_match}(wp-(content|admin|includes).*) {$rewrite_base}{$subdir_replacement_12} [L] RewriteRule ^{$subdir_match}(.*\.php)$ {$rewrite_base}$subdir_replacement_12 [L] RewriteRule . index.php [L] EOF; echo '<li><p id="network-htaccess-rules-description">'; printf( /* translators: 1: File name (.htaccess or web.config), 2: File path. */ __( 'Add the following to your %1$s file in %2$s, <strong>replacing</strong> other WordPress rules:' ), '<code>.htaccess</code>', '<code>' . $home_path . '</code>' ); echo '</p>'; if ( ! $subdomain_install && WP_CONTENT_DIR !== ABSPATH . 'wp-content' ) { echo '<p><strong>' . __( 'Warning:' ) . ' ' . __( 'Subdirectory networks may not be fully compatible with custom wp-content directories.' ) . '</strong></p>'; } ?> <p class="configuration-rules-label"><label for="network-htaccess-rules"> <?php printf( /* translators: %s: File name (wp-config.php, .htaccess or web.config). */ __( 'Network configuration rules for %s' ), '<code>.htaccess</code>' ); ?> </label></p> <textarea id="network-htaccess-rules" class="code" readonly="readonly" cols="100" rows="<?php echo substr_count( $htaccess_file, "\n" ) + 1; ?>" aria-describedby="network-htaccess-rules-description"><?php echo esc_textarea( $htaccess_file ); ?></textarea> </li> </ol> <?php endif; // End IIS/Nginx/Apache code branches. if ( ! is_multisite() ) { ?> <p><?php _e( 'Once you complete these steps, your network is enabled and configured. You will have to log in again.' ); ?> <a href="<?php echo esc_url( wp_login_url() ); ?>"><?php _e( 'Log In' ); ?></a></p> <?php } } includes/options.php 0000644 00000010233 15135536347 0010571 0 ustar 00 <?php /** * WordPress Options Administration API. * * @package WordPress * @subpackage Administration * @since 4.4.0 */ /** * Output JavaScript to toggle display of additional settings if avatars are disabled. * * @since 4.2.0 */ function options_discussion_add_js() { ?> <script> (function($){ var parent = $( '#show_avatars' ), children = $( '.avatar-settings' ); parent.on( 'change', function(){ children.toggleClass( 'hide-if-js', ! this.checked ); }); })(jQuery); </script> <?php } /** * Display JavaScript on the page. * * @since 3.5.0 */ function options_general_add_js() { ?> <script type="text/javascript"> jQuery( function($) { var $siteName = $( '#wp-admin-bar-site-name' ).children( 'a' ).first(), $siteIconPreview = $('#site-icon-preview-site-title'), homeURL = ( <?php echo wp_json_encode( get_home_url() ); ?> || '' ).replace( /^(https?:\/\/)?(www\.)?/, '' ); $( '#blogname' ).on( 'input', function() { var title = $.trim( $( this ).val() ) || homeURL; // Truncate to 40 characters. if ( 40 < title.length ) { title = title.substring( 0, 40 ) + '\u2026'; } $siteName.text( title ); $siteIconPreview.text( title ); }); $( 'input[name="date_format"]' ).on( 'click', function() { if ( 'date_format_custom_radio' !== $(this).attr( 'id' ) ) $( 'input[name="date_format_custom"]' ).val( $( this ).val() ).closest( 'fieldset' ).find( '.example' ).text( $( this ).parent( 'label' ).children( '.format-i18n' ).text() ); }); $( 'input[name="date_format_custom"]' ).on( 'click input', function() { $( '#date_format_custom_radio' ).prop( 'checked', true ); }); $( 'input[name="time_format"]' ).on( 'click', function() { if ( 'time_format_custom_radio' !== $(this).attr( 'id' ) ) $( 'input[name="time_format_custom"]' ).val( $( this ).val() ).closest( 'fieldset' ).find( '.example' ).text( $( this ).parent( 'label' ).children( '.format-i18n' ).text() ); }); $( 'input[name="time_format_custom"]' ).on( 'click input', function() { $( '#time_format_custom_radio' ).prop( 'checked', true ); }); $( 'input[name="date_format_custom"], input[name="time_format_custom"]' ).on( 'input', function() { var format = $( this ), fieldset = format.closest( 'fieldset' ), example = fieldset.find( '.example' ), spinner = fieldset.find( '.spinner' ); // Debounce the event callback while users are typing. clearTimeout( $.data( this, 'timer' ) ); $( this ).data( 'timer', setTimeout( function() { // If custom date is not empty. if ( format.val() ) { spinner.addClass( 'is-active' ); $.post( ajaxurl, { action: 'date_format_custom' === format.attr( 'name' ) ? 'date_format' : 'time_format', date : format.val() }, function( d ) { spinner.removeClass( 'is-active' ); example.text( d ); } ); } }, 500 ) ); } ); var languageSelect = $( '#WPLANG' ); $( 'form' ).on( 'submit', function() { /* * Don't show a spinner for English and installed languages, * as there is nothing to download. */ if ( ! languageSelect.find( 'option:selected' ).data( 'installed' ) ) { $( '#submit', this ).after( '<span class="spinner language-install-spinner is-active" />' ); } }); } ); </script> <?php } /** * Display JavaScript on the page. * * @since 3.5.0 */ function options_reading_add_js() { ?> <script type="text/javascript"> jQuery( function($) { var section = $('#front-static-pages'), staticPage = section.find('input:radio[value="page"]'), selects = section.find('select'), check_disabled = function(){ selects.prop( 'disabled', ! staticPage.prop('checked') ); }; check_disabled(); section.find( 'input:radio' ).on( 'change', check_disabled ); } ); </script> <?php } /** * Render the site charset setting. * * @since 3.5.0 */ function options_reading_blog_charset() { echo '<input name="blog_charset" type="text" id="blog_charset" value="' . esc_attr( get_option( 'blog_charset' ) ) . '" class="regular-text" />'; echo '<p class="description">' . __( 'The <a href="https://wordpress.org/documentation/article/wordpress-glossary/#character-set">character encoding</a> of your site (UTF-8 is recommended)' ) . '</p>'; } includes/meta-boxes.php 0000644 00000200711 15135536347 0011144 0 ustar 00 <?php /** * WordPress Administration Meta Boxes API. * * @package WordPress * @subpackage Administration */ // // Post-related Meta Boxes. // /** * Displays post submit form fields. * * @since 2.7.0 * * @global string $action * * @param WP_Post $post Current post object. * @param array $args { * Array of arguments for building the post submit meta box. * * @type string $id Meta box 'id' attribute. * @type string $title Meta box title. * @type callable $callback Meta box display callback. * @type array $args Extra meta box arguments. * } */ function post_submit_meta_box( $post, $args = array() ) { global $action; $post_id = (int) $post->ID; $post_type = $post->post_type; $post_type_object = get_post_type_object( $post_type ); $can_publish = current_user_can( $post_type_object->cap->publish_posts ); ?> <div class="submitbox" id="submitpost"> <div id="minor-publishing"> <?php // Hidden submit button early on so that the browser chooses the right button when form is submitted with Return key. ?> <div style="display:none;"> <?php submit_button( __( 'Save' ), '', 'save' ); ?> </div> <div id="minor-publishing-actions"> <div id="save-action"> <?php if ( ! in_array( $post->post_status, array( 'publish', 'future', 'pending' ), true ) ) { $private_style = ''; if ( 'private' === $post->post_status ) { $private_style = 'style="display:none"'; } ?> <input <?php echo $private_style; ?> type="submit" name="save" id="save-post" value="<?php esc_attr_e( 'Save Draft' ); ?>" class="button" /> <span class="spinner"></span> <?php } elseif ( 'pending' === $post->post_status && $can_publish ) { ?> <input type="submit" name="save" id="save-post" value="<?php esc_attr_e( 'Save as Pending' ); ?>" class="button" /> <span class="spinner"></span> <?php } ?> </div> <?php if ( is_post_type_viewable( $post_type_object ) ) : ?> <div id="preview-action"> <?php $preview_link = esc_url( get_preview_post_link( $post ) ); if ( 'publish' === $post->post_status ) { $preview_button_text = __( 'Preview Changes' ); } else { $preview_button_text = __( 'Preview' ); } $preview_button = sprintf( '%1$s<span class="screen-reader-text"> %2$s</span>', $preview_button_text, /* translators: Hidden accessibility text. */ __( '(opens in a new tab)' ) ); ?> <a class="preview button" href="<?php echo $preview_link; ?>" target="wp-preview-<?php echo $post_id; ?>" id="post-preview"><?php echo $preview_button; ?></a> <input type="hidden" name="wp-preview" id="wp-preview" value="" /> </div> <?php endif; /** * Fires after the Save Draft (or Save as Pending) and Preview (or Preview Changes) buttons * in the Publish meta box. * * @since 4.4.0 * * @param WP_Post $post WP_Post object for the current post. */ do_action( 'post_submitbox_minor_actions', $post ); ?> <div class="clear"></div> </div> <div id="misc-publishing-actions"> <div class="misc-pub-section misc-pub-post-status"> <?php _e( 'Status:' ); ?> <span id="post-status-display"> <?php switch ( $post->post_status ) { case 'private': _e( 'Privately Published' ); break; case 'publish': _e( 'Published' ); break; case 'future': _e( 'Scheduled' ); break; case 'pending': _e( 'Pending Review' ); break; case 'draft': case 'auto-draft': _e( 'Draft' ); break; } ?> </span> <?php if ( 'publish' === $post->post_status || 'private' === $post->post_status || $can_publish ) { $private_style = ''; if ( 'private' === $post->post_status ) { $private_style = 'style="display:none"'; } ?> <a href="#post_status" <?php echo $private_style; ?> class="edit-post-status hide-if-no-js" role="button"><span aria-hidden="true"><?php _e( 'Edit' ); ?></span> <span class="screen-reader-text"> <?php /* translators: Hidden accessibility text. */ _e( 'Edit status' ); ?> </span></a> <div id="post-status-select" class="hide-if-js"> <input type="hidden" name="hidden_post_status" id="hidden_post_status" value="<?php echo esc_attr( ( 'auto-draft' === $post->post_status ) ? 'draft' : $post->post_status ); ?>" /> <label for="post_status" class="screen-reader-text"> <?php /* translators: Hidden accessibility text. */ _e( 'Set status' ); ?> </label> <select name="post_status" id="post_status"> <?php if ( 'publish' === $post->post_status ) : ?> <option<?php selected( $post->post_status, 'publish' ); ?> value='publish'><?php _e( 'Published' ); ?></option> <?php elseif ( 'private' === $post->post_status ) : ?> <option<?php selected( $post->post_status, 'private' ); ?> value='publish'><?php _e( 'Privately Published' ); ?></option> <?php elseif ( 'future' === $post->post_status ) : ?> <option<?php selected( $post->post_status, 'future' ); ?> value='future'><?php _e( 'Scheduled' ); ?></option> <?php endif; ?> <option<?php selected( $post->post_status, 'pending' ); ?> value='pending'><?php _e( 'Pending Review' ); ?></option> <?php if ( 'auto-draft' === $post->post_status ) : ?> <option<?php selected( $post->post_status, 'auto-draft' ); ?> value='draft'><?php _e( 'Draft' ); ?></option> <?php else : ?> <option<?php selected( $post->post_status, 'draft' ); ?> value='draft'><?php _e( 'Draft' ); ?></option> <?php endif; ?> </select> <a href="#post_status" class="save-post-status hide-if-no-js button"><?php _e( 'OK' ); ?></a> <a href="#post_status" class="cancel-post-status hide-if-no-js button-cancel"><?php _e( 'Cancel' ); ?></a> </div> <?php } ?> </div> <div class="misc-pub-section misc-pub-visibility" id="visibility"> <?php _e( 'Visibility:' ); ?> <span id="post-visibility-display"> <?php if ( 'private' === $post->post_status ) { $post->post_password = ''; $visibility = 'private'; $visibility_trans = __( 'Private' ); } elseif ( ! empty( $post->post_password ) ) { $visibility = 'password'; $visibility_trans = __( 'Password protected' ); } elseif ( 'post' === $post_type && is_sticky( $post_id ) ) { $visibility = 'public'; $visibility_trans = __( 'Public, Sticky' ); } else { $visibility = 'public'; $visibility_trans = __( 'Public' ); } echo esc_html( $visibility_trans ); ?> </span> <?php if ( $can_publish ) { ?> <a href="#visibility" class="edit-visibility hide-if-no-js" role="button"><span aria-hidden="true"><?php _e( 'Edit' ); ?></span> <span class="screen-reader-text"> <?php /* translators: Hidden accessibility text. */ _e( 'Edit visibility' ); ?> </span></a> <div id="post-visibility-select" class="hide-if-js"> <input type="hidden" name="hidden_post_password" id="hidden-post-password" value="<?php echo esc_attr( $post->post_password ); ?>" /> <?php if ( 'post' === $post_type ) : ?> <input type="checkbox" style="display:none" name="hidden_post_sticky" id="hidden-post-sticky" value="sticky" <?php checked( is_sticky( $post_id ) ); ?> /> <?php endif; ?> <input type="hidden" name="hidden_post_visibility" id="hidden-post-visibility" value="<?php echo esc_attr( $visibility ); ?>" /> <input type="radio" name="visibility" id="visibility-radio-public" value="public" <?php checked( $visibility, 'public' ); ?> /> <label for="visibility-radio-public" class="selectit"><?php _e( 'Public' ); ?></label><br /> <?php if ( 'post' === $post_type && current_user_can( 'edit_others_posts' ) ) : ?> <span id="sticky-span"><input id="sticky" name="sticky" type="checkbox" value="sticky" <?php checked( is_sticky( $post_id ) ); ?> /> <label for="sticky" class="selectit"><?php _e( 'Stick this post to the front page' ); ?></label><br /></span> <?php endif; ?> <input type="radio" name="visibility" id="visibility-radio-password" value="password" <?php checked( $visibility, 'password' ); ?> /> <label for="visibility-radio-password" class="selectit"><?php _e( 'Password protected' ); ?></label><br /> <span id="password-span"><label for="post_password"><?php _e( 'Password:' ); ?></label> <input type="text" name="post_password" id="post_password" value="<?php echo esc_attr( $post->post_password ); ?>" maxlength="255" /><br /></span> <input type="radio" name="visibility" id="visibility-radio-private" value="private" <?php checked( $visibility, 'private' ); ?> /> <label for="visibility-radio-private" class="selectit"><?php _e( 'Private' ); ?></label><br /> <p> <a href="#visibility" class="save-post-visibility hide-if-no-js button"><?php _e( 'OK' ); ?></a> <a href="#visibility" class="cancel-post-visibility hide-if-no-js button-cancel"><?php _e( 'Cancel' ); ?></a> </p> </div> <?php } ?> </div> <?php /* translators: Publish box date string. 1: Date, 2: Time. See https://www.php.net/manual/datetime.format.php */ $date_string = __( '%1$s at %2$s' ); /* translators: Publish box date format, see https://www.php.net/manual/datetime.format.php */ $date_format = _x( 'M j, Y', 'publish box date format' ); /* translators: Publish box time format, see https://www.php.net/manual/datetime.format.php */ $time_format = _x( 'H:i', 'publish box time format' ); if ( 0 !== $post_id ) { if ( 'future' === $post->post_status ) { // Scheduled for publishing at a future date. /* translators: Post date information. %s: Date on which the post is currently scheduled to be published. */ $stamp = __( 'Scheduled for: %s' ); } elseif ( 'publish' === $post->post_status || 'private' === $post->post_status ) { // Already published. /* translators: Post date information. %s: Date on which the post was published. */ $stamp = __( 'Published on: %s' ); } elseif ( '0000-00-00 00:00:00' === $post->post_date_gmt ) { // Draft, 1 or more saves, no date specified. $stamp = __( 'Publish <b>immediately</b>' ); } elseif ( time() < strtotime( $post->post_date_gmt . ' +0000' ) ) { // Draft, 1 or more saves, future date specified. /* translators: Post date information. %s: Date on which the post is to be published. */ $stamp = __( 'Schedule for: %s' ); } else { // Draft, 1 or more saves, date specified. /* translators: Post date information. %s: Date on which the post is to be published. */ $stamp = __( 'Publish on: %s' ); } $date = sprintf( $date_string, date_i18n( $date_format, strtotime( $post->post_date ) ), date_i18n( $time_format, strtotime( $post->post_date ) ) ); } else { // Draft (no saves, and thus no date specified). $stamp = __( 'Publish <b>immediately</b>' ); $date = sprintf( $date_string, date_i18n( $date_format, strtotime( current_time( 'mysql' ) ) ), date_i18n( $time_format, strtotime( current_time( 'mysql' ) ) ) ); } if ( ! empty( $args['args']['revisions_count'] ) ) : ?> <div class="misc-pub-section misc-pub-revisions"> <?php /* translators: Post revisions heading. %s: The number of available revisions. */ printf( __( 'Revisions: %s' ), '<b>' . number_format_i18n( $args['args']['revisions_count'] ) . '</b>' ); ?> <a class="hide-if-no-js" href="<?php echo esc_url( get_edit_post_link( $args['args']['revision_id'] ) ); ?>"><span aria-hidden="true"><?php _ex( 'Browse', 'revisions' ); ?></span> <span class="screen-reader-text"> <?php /* translators: Hidden accessibility text. */ _e( 'Browse revisions' ); ?> </span></a> </div> <?php endif; if ( $can_publish ) : // Contributors don't get to choose the date of publish. ?> <div class="misc-pub-section curtime misc-pub-curtime"> <span id="timestamp"> <?php printf( $stamp, '<b>' . $date . '</b>' ); ?> </span> <a href="#edit_timestamp" class="edit-timestamp hide-if-no-js" role="button"> <span aria-hidden="true"><?php _e( 'Edit' ); ?></span> <span class="screen-reader-text"> <?php /* translators: Hidden accessibility text. */ _e( 'Edit date and time' ); ?> </span> </a> <fieldset id="timestampdiv" class="hide-if-js"> <legend class="screen-reader-text"> <?php /* translators: Hidden accessibility text. */ _e( 'Date and time' ); ?> </legend> <?php touch_time( ( 'edit' === $action ), 1 ); ?> </fieldset> </div> <?php endif; if ( 'draft' === $post->post_status && get_post_meta( $post_id, '_customize_changeset_uuid', true ) ) : $message = sprintf( /* translators: %s: URL to the Customizer. */ __( 'This draft comes from your <a href="%s">unpublished customization changes</a>. You can edit, but there is no need to publish now. It will be published automatically with those changes.' ), esc_url( add_query_arg( 'changeset_uuid', rawurlencode( get_post_meta( $post_id, '_customize_changeset_uuid', true ) ), admin_url( 'customize.php' ) ) ) ); wp_admin_notice( $message, array( 'type' => 'info', 'additional_classes' => array( 'notice-alt', 'inline' ), ) ); endif; /** * Fires after the post time/date setting in the Publish meta box. * * @since 2.9.0 * @since 4.4.0 Added the `$post` parameter. * * @param WP_Post $post WP_Post object for the current post. */ do_action( 'post_submitbox_misc_actions', $post ); ?> </div> <div class="clear"></div> </div> <div id="major-publishing-actions"> <?php /** * Fires at the beginning of the publishing actions section of the Publish meta box. * * @since 2.7.0 * @since 4.9.0 Added the `$post` parameter. * * @param WP_Post|null $post WP_Post object for the current post on Edit Post screen, * null on Edit Link screen. */ do_action( 'post_submitbox_start', $post ); ?> <div id="delete-action"> <?php if ( current_user_can( 'delete_post', $post_id ) ) { if ( ! EMPTY_TRASH_DAYS ) { $delete_text = __( 'Delete permanently' ); } else { $delete_text = __( 'Move to Trash' ); } ?> <a class="submitdelete deletion" href="<?php echo get_delete_post_link( $post_id ); ?>"><?php echo $delete_text; ?></a> <?php } ?> </div> <div id="publishing-action"> <span class="spinner"></span> <?php if ( ! in_array( $post->post_status, array( 'publish', 'future', 'private' ), true ) || 0 === $post_id ) { if ( $can_publish ) : if ( ! empty( $post->post_date_gmt ) && time() < strtotime( $post->post_date_gmt . ' +0000' ) ) : ?> <input name="original_publish" type="hidden" id="original_publish" value="<?php echo esc_attr_x( 'Schedule', 'post action/button label' ); ?>" /> <?php submit_button( _x( 'Schedule', 'post action/button label' ), 'primary large', 'publish', false ); ?> <?php else : ?> <input name="original_publish" type="hidden" id="original_publish" value="<?php esc_attr_e( 'Publish' ); ?>" /> <?php submit_button( __( 'Publish' ), 'primary large', 'publish', false ); ?> <?php endif; else : ?> <input name="original_publish" type="hidden" id="original_publish" value="<?php esc_attr_e( 'Submit for Review' ); ?>" /> <?php submit_button( __( 'Submit for Review' ), 'primary large', 'publish', false ); ?> <?php endif; } else { ?> <input name="original_publish" type="hidden" id="original_publish" value="<?php esc_attr_e( 'Update' ); ?>" /> <?php submit_button( __( 'Update' ), 'primary large', 'save', false, array( 'id' => 'publish' ) ); ?> <?php } ?> </div> <div class="clear"></div> </div> </div> <?php } /** * Displays attachment submit form fields. * * @since 3.5.0 * * @param WP_Post $post Current post object. */ function attachment_submit_meta_box( $post ) { ?> <div class="submitbox" id="submitpost"> <div id="minor-publishing"> <?php // Hidden submit button early on so that the browser chooses the right button when form is submitted with Return key. ?> <div style="display:none;"> <?php submit_button( __( 'Save' ), '', 'save' ); ?> </div> <div id="misc-publishing-actions"> <div class="misc-pub-section curtime misc-pub-curtime"> <span id="timestamp"> <?php $uploaded_on = sprintf( /* translators: Publish box date string. 1: Date, 2: Time. */ __( '%1$s at %2$s' ), /* translators: Publish box date format, see https://www.php.net/manual/datetime.format.php */ date_i18n( _x( 'M j, Y', 'publish box date format' ), strtotime( $post->post_date ) ), /* translators: Publish box time format, see https://www.php.net/manual/datetime.format.php */ date_i18n( _x( 'H:i', 'publish box time format' ), strtotime( $post->post_date ) ) ); /* translators: Attachment information. %s: Date the attachment was uploaded. */ printf( __( 'Uploaded on: %s' ), '<b>' . $uploaded_on . '</b>' ); ?> </span> </div><!-- .misc-pub-section --> <?php /** * Fires after the 'Uploaded on' section of the Save meta box * in the attachment editing screen. * * @since 3.5.0 * @since 4.9.0 Added the `$post` parameter. * * @param WP_Post $post WP_Post object for the current attachment. */ do_action( 'attachment_submitbox_misc_actions', $post ); ?> </div><!-- #misc-publishing-actions --> <div class="clear"></div> </div><!-- #minor-publishing --> <div id="major-publishing-actions"> <div id="delete-action"> <?php if ( current_user_can( 'delete_post', $post->ID ) ) { if ( EMPTY_TRASH_DAYS && MEDIA_TRASH ) { printf( '<a class="submitdelete deletion" href="%1$s">%2$s</a>', get_delete_post_link( $post->ID ), __( 'Move to Trash' ) ); } else { $show_confirmation = ! MEDIA_TRASH ? " onclick='return showNotice.warn();'" : ''; printf( '<a class="submitdelete deletion"%1$s href="%2$s">%3$s</a>', $show_confirmation, get_delete_post_link( $post->ID, '', true ), __( 'Delete permanently' ) ); } } ?> </div> <div id="publishing-action"> <span class="spinner"></span> <input name="original_publish" type="hidden" id="original_publish" value="<?php esc_attr_e( 'Update' ); ?>" /> <input name="save" type="submit" class="button button-primary button-large" id="publish" value="<?php esc_attr_e( 'Update' ); ?>" /> </div> <div class="clear"></div> </div><!-- #major-publishing-actions --> </div> <?php } /** * Displays post format form elements. * * @since 3.1.0 * * @param WP_Post $post Current post object. * @param array $box { * Post formats meta box arguments. * * @type string $id Meta box 'id' attribute. * @type string $title Meta box title. * @type callable $callback Meta box display callback. * @type array $args Extra meta box arguments. * } */ function post_format_meta_box( $post, $box ) { if ( current_theme_supports( 'post-formats' ) && post_type_supports( $post->post_type, 'post-formats' ) ) : $post_formats = get_theme_support( 'post-formats' ); if ( is_array( $post_formats[0] ) ) : $post_format = get_post_format( $post->ID ); if ( ! $post_format ) { $post_format = '0'; } // Add in the current one if it isn't there yet, in case the active theme doesn't support it. if ( $post_format && ! in_array( $post_format, $post_formats[0], true ) ) { $post_formats[0][] = $post_format; } ?> <div id="post-formats-select"> <fieldset> <legend class="screen-reader-text"> <?php /* translators: Hidden accessibility text. */ _e( 'Post Formats' ); ?> </legend> <input type="radio" name="post_format" class="post-format" id="post-format-0" value="0" <?php checked( $post_format, '0' ); ?> /> <label for="post-format-0" class="post-format-icon post-format-standard"><?php echo get_post_format_string( 'standard' ); ?></label> <?php foreach ( $post_formats[0] as $format ) : ?> <br /><input type="radio" name="post_format" class="post-format" id="post-format-<?php echo esc_attr( $format ); ?>" value="<?php echo esc_attr( $format ); ?>" <?php checked( $post_format, $format ); ?> /> <label for="post-format-<?php echo esc_attr( $format ); ?>" class="post-format-icon post-format-<?php echo esc_attr( $format ); ?>"><?php echo esc_html( get_post_format_string( $format ) ); ?></label> <?php endforeach; ?> </fieldset> </div> <?php endif; endif; } /** * Displays post tags form fields. * * @since 2.6.0 * * @todo Create taxonomy-agnostic wrapper for this. * * @param WP_Post $post Current post object. * @param array $box { * Tags meta box arguments. * * @type string $id Meta box 'id' attribute. * @type string $title Meta box title. * @type callable $callback Meta box display callback. * @type array $args { * Extra meta box arguments. * * @type string $taxonomy Taxonomy. Default 'post_tag'. * } * } */ function post_tags_meta_box( $post, $box ) { $defaults = array( 'taxonomy' => 'post_tag' ); if ( ! isset( $box['args'] ) || ! is_array( $box['args'] ) ) { $args = array(); } else { $args = $box['args']; } $parsed_args = wp_parse_args( $args, $defaults ); $tax_name = esc_attr( $parsed_args['taxonomy'] ); $taxonomy = get_taxonomy( $parsed_args['taxonomy'] ); $user_can_assign_terms = current_user_can( $taxonomy->cap->assign_terms ); $comma = _x( ',', 'tag delimiter' ); $terms_to_edit = get_terms_to_edit( $post->ID, $tax_name ); if ( ! is_string( $terms_to_edit ) ) { $terms_to_edit = ''; } ?> <div class="tagsdiv" id="<?php echo $tax_name; ?>"> <div class="jaxtag"> <div class="nojs-tags hide-if-js"> <label for="tax-input-<?php echo $tax_name; ?>"><?php echo $taxonomy->labels->add_or_remove_items; ?></label> <p><textarea name="<?php echo "tax_input[$tax_name]"; ?>" rows="3" cols="20" class="the-tags" id="tax-input-<?php echo $tax_name; ?>" <?php disabled( ! $user_can_assign_terms ); ?> aria-describedby="new-tag-<?php echo $tax_name; ?>-desc"><?php echo str_replace( ',', $comma . ' ', $terms_to_edit ); // textarea_escaped by esc_attr() ?></textarea></p> </div> <?php if ( $user_can_assign_terms ) : ?> <div class="ajaxtag hide-if-no-js"> <label class="screen-reader-text" for="new-tag-<?php echo $tax_name; ?>"><?php echo $taxonomy->labels->add_new_item; ?></label> <input data-wp-taxonomy="<?php echo $tax_name; ?>" type="text" id="new-tag-<?php echo $tax_name; ?>" name="newtag[<?php echo $tax_name; ?>]" class="newtag form-input-tip" size="16" autocomplete="off" aria-describedby="new-tag-<?php echo $tax_name; ?>-desc" value="" /> <input type="button" class="button tagadd" value="<?php esc_attr_e( 'Add' ); ?>" /> </div> <p class="howto" id="new-tag-<?php echo $tax_name; ?>-desc"><?php echo $taxonomy->labels->separate_items_with_commas; ?></p> <?php elseif ( empty( $terms_to_edit ) ) : ?> <p><?php echo $taxonomy->labels->no_terms; ?></p> <?php endif; ?> </div> <ul class="tagchecklist" role="list"></ul> </div> <?php if ( $user_can_assign_terms ) : ?> <p class="hide-if-no-js"><button type="button" class="button-link tagcloud-link" id="link-<?php echo $tax_name; ?>" aria-expanded="false"><?php echo $taxonomy->labels->choose_from_most_used; ?></button></p> <?php endif; ?> <?php } /** * Displays post categories form fields. * * @since 2.6.0 * * @todo Create taxonomy-agnostic wrapper for this. * * @param WP_Post $post Current post object. * @param array $box { * Categories meta box arguments. * * @type string $id Meta box 'id' attribute. * @type string $title Meta box title. * @type callable $callback Meta box display callback. * @type array $args { * Extra meta box arguments. * * @type string $taxonomy Taxonomy. Default 'category'. * } * } */ function post_categories_meta_box( $post, $box ) { $defaults = array( 'taxonomy' => 'category' ); if ( ! isset( $box['args'] ) || ! is_array( $box['args'] ) ) { $args = array(); } else { $args = $box['args']; } $parsed_args = wp_parse_args( $args, $defaults ); $tax_name = esc_attr( $parsed_args['taxonomy'] ); $taxonomy = get_taxonomy( $parsed_args['taxonomy'] ); ?> <div id="taxonomy-<?php echo $tax_name; ?>" class="categorydiv"> <ul id="<?php echo $tax_name; ?>-tabs" class="category-tabs"> <li class="tabs"><a href="#<?php echo $tax_name; ?>-all"><?php echo $taxonomy->labels->all_items; ?></a></li> <li class="hide-if-no-js"><a href="#<?php echo $tax_name; ?>-pop"><?php echo esc_html( $taxonomy->labels->most_used ); ?></a></li> </ul> <div id="<?php echo $tax_name; ?>-pop" class="tabs-panel" style="display: none;"> <ul id="<?php echo $tax_name; ?>checklist-pop" class="categorychecklist form-no-clear" > <?php $popular_ids = wp_popular_terms_checklist( $tax_name ); ?> </ul> </div> <div id="<?php echo $tax_name; ?>-all" class="tabs-panel"> <?php $name = ( 'category' === $tax_name ) ? 'post_category' : 'tax_input[' . $tax_name . ']'; // Allows for an empty term set to be sent. 0 is an invalid term ID and will be ignored by empty() checks. echo "<input type='hidden' name='{$name}[]' value='0' />"; ?> <ul id="<?php echo $tax_name; ?>checklist" data-wp-lists="list:<?php echo $tax_name; ?>" class="categorychecklist form-no-clear"> <?php wp_terms_checklist( $post->ID, array( 'taxonomy' => $tax_name, 'popular_cats' => $popular_ids, ) ); ?> </ul> </div> <?php if ( current_user_can( $taxonomy->cap->edit_terms ) ) : ?> <div id="<?php echo $tax_name; ?>-adder" class="wp-hidden-children"> <a id="<?php echo $tax_name; ?>-add-toggle" href="#<?php echo $tax_name; ?>-add" class="hide-if-no-js taxonomy-add-new"> <?php /* translators: %s: Add New taxonomy label. */ printf( __( '+ %s' ), $taxonomy->labels->add_new_item ); ?> </a> <p id="<?php echo $tax_name; ?>-add" class="category-add wp-hidden-child"> <label class="screen-reader-text" for="new<?php echo $tax_name; ?>"><?php echo $taxonomy->labels->add_new_item; ?></label> <input type="text" name="new<?php echo $tax_name; ?>" id="new<?php echo $tax_name; ?>" class="form-required form-input-tip" value="<?php echo esc_attr( $taxonomy->labels->new_item_name ); ?>" aria-required="true" /> <label class="screen-reader-text" for="new<?php echo $tax_name; ?>_parent"> <?php echo $taxonomy->labels->parent_item_colon; ?> </label> <?php $parent_dropdown_args = array( 'taxonomy' => $tax_name, 'hide_empty' => 0, 'name' => 'new' . $tax_name . '_parent', 'orderby' => 'name', 'hierarchical' => 1, 'show_option_none' => '— ' . $taxonomy->labels->parent_item . ' —', ); /** * Filters the arguments for the taxonomy parent dropdown on the Post Edit page. * * @since 4.4.0 * * @param array $parent_dropdown_args { * Optional. Array of arguments to generate parent dropdown. * * @type string $taxonomy Name of the taxonomy to retrieve. * @type bool $hide_if_empty True to skip generating markup if no * categories are found. Default 0. * @type string $name Value for the 'name' attribute * of the select element. * Default "new{$tax_name}_parent". * @type string $orderby Which column to use for ordering * terms. Default 'name'. * @type bool|int $hierarchical Whether to traverse the taxonomy * hierarchy. Default 1. * @type string $show_option_none Text to display for the "none" option. * Default "— {$parent} —", * where `$parent` is 'parent_item' * taxonomy label. * } */ $parent_dropdown_args = apply_filters( 'post_edit_category_parent_dropdown_args', $parent_dropdown_args ); wp_dropdown_categories( $parent_dropdown_args ); ?> <input type="button" id="<?php echo $tax_name; ?>-add-submit" data-wp-lists="add:<?php echo $tax_name; ?>checklist:<?php echo $tax_name; ?>-add" class="button category-add-submit" value="<?php echo esc_attr( $taxonomy->labels->add_new_item ); ?>" /> <?php wp_nonce_field( 'add-' . $tax_name, '_ajax_nonce-add-' . $tax_name, false ); ?> <span id="<?php echo $tax_name; ?>-ajax-response"></span> </p> </div> <?php endif; ?> </div> <?php } /** * Displays post excerpt form fields. * * @since 2.6.0 * * @param WP_Post $post Current post object. */ function post_excerpt_meta_box( $post ) { ?> <label class="screen-reader-text" for="excerpt"> <?php /* translators: Hidden accessibility text. */ _e( 'Excerpt' ); ?> </label><textarea rows="1" cols="40" name="excerpt" id="excerpt"><?php echo $post->post_excerpt; // textarea_escaped ?></textarea> <p> <?php printf( /* translators: %s: Documentation URL. */ __( 'Excerpts are optional hand-crafted summaries of your content that can be used in your theme. <a href="%s">Learn more about manual excerpts</a>.' ), __( 'https://wordpress.org/documentation/article/what-is-an-excerpt-classic-editor/' ) ); ?> </p> <?php } /** * Displays trackback links form fields. * * @since 2.6.0 * * @param WP_Post $post Current post object. */ function post_trackback_meta_box( $post ) { $form_trackback = '<input type="text" name="trackback_url" id="trackback_url" class="code" value="' . esc_attr( str_replace( "\n", ' ', $post->to_ping ) ) . '" aria-describedby="trackback-url-desc" />'; if ( '' !== $post->pinged ) { $pings = '<p>' . __( 'Already pinged:' ) . '</p><ul>'; $already_pinged = explode( "\n", trim( $post->pinged ) ); foreach ( $already_pinged as $pinged_url ) { $pings .= "\n\t<li>" . esc_html( $pinged_url ) . '</li>'; } $pings .= '</ul>'; } ?> <p> <label for="trackback_url"><?php _e( 'Send trackbacks to:' ); ?></label> <?php echo $form_trackback; ?> </p> <p id="trackback-url-desc" class="howto"><?php _e( 'Separate multiple URLs with spaces' ); ?></p> <p> <?php printf( /* translators: %s: Documentation URL. */ __( 'Trackbacks are a way to notify legacy blog systems that you’ve linked to them. If you link other WordPress sites, they’ll be notified automatically using <a href="%s">pingbacks</a>, no other action necessary.' ), __( 'https://wordpress.org/documentation/article/introduction-to-blogging/#comments' ) ); ?> </p> <?php if ( ! empty( $pings ) ) { echo $pings; } } /** * Displays custom fields form fields. * * @since 2.6.0 * * @param WP_Post $post Current post object. */ function post_custom_meta_box( $post ) { ?> <div id="postcustomstuff"> <div id="ajax-response"></div> <?php $metadata = has_meta( $post->ID ); foreach ( $metadata as $key => $value ) { if ( is_protected_meta( $metadata[ $key ]['meta_key'], 'post' ) || ! current_user_can( 'edit_post_meta', $post->ID, $metadata[ $key ]['meta_key'] ) ) { unset( $metadata[ $key ] ); } } list_meta( $metadata ); meta_form( $post ); ?> </div> <p> <?php printf( /* translators: %s: Documentation URL. */ __( 'Custom fields can be used to add extra metadata to a post that you can <a href="%s">use in your theme</a>.' ), __( 'https://wordpress.org/documentation/article/assign-custom-fields/' ) ); ?> </p> <?php } /** * Displays comments status form fields. * * @since 2.6.0 * * @param WP_Post $post Current post object. */ function post_comment_status_meta_box( $post ) { ?> <input name="advanced_view" type="hidden" value="1" /> <p class="meta-options"> <label for="comment_status" class="selectit"><input name="comment_status" type="checkbox" id="comment_status" value="open" <?php checked( $post->comment_status, 'open' ); ?> /> <?php _e( 'Allow comments' ); ?></label><br /> <label for="ping_status" class="selectit"><input name="ping_status" type="checkbox" id="ping_status" value="open" <?php checked( $post->ping_status, 'open' ); ?> /> <?php printf( /* translators: %s: Documentation URL. */ __( 'Allow <a href="%s">trackbacks and pingbacks</a>' ), __( 'https://wordpress.org/documentation/article/introduction-to-blogging/#managing-comments' ) ); ?> </label> <?php /** * Fires at the end of the Discussion meta box on the post editing screen. * * @since 3.1.0 * * @param WP_Post $post WP_Post object for the current post. */ do_action( 'post_comment_status_meta_box-options', $post ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores ?> </p> <?php } /** * Displays comments for post table header * * @since 3.0.0 * * @param array $result Table header rows. * @return array */ function post_comment_meta_box_thead( $result ) { unset( $result['cb'], $result['response'] ); return $result; } /** * Displays comments for post. * * @since 2.8.0 * * @param WP_Post $post Current post object. */ function post_comment_meta_box( $post ) { wp_nonce_field( 'get-comments', 'add_comment_nonce', false ); ?> <p class="hide-if-no-js" id="add-new-comment"><button type="button" class="button" onclick="window.commentReply && commentReply.addcomment(<?php echo $post->ID; ?>);"><?php _e( 'Add Comment' ); ?></button></p> <?php $total = get_comments( array( 'post_id' => $post->ID, 'count' => true, 'orderby' => 'none', ) ); $wp_list_table = _get_list_table( 'WP_Post_Comments_List_Table' ); $wp_list_table->display( true ); if ( 1 > $total ) { echo '<p id="no-comments">' . __( 'No comments yet.' ) . '</p>'; } else { $hidden = get_hidden_meta_boxes( get_current_screen() ); if ( ! in_array( 'commentsdiv', $hidden, true ) ) { ?> <script type="text/javascript">jQuery(function(){commentsBox.get(<?php echo $total; ?>, 10);});</script> <?php } ?> <p class="hide-if-no-js" id="show-comments"><a href="#commentstatusdiv" onclick="commentsBox.load(<?php echo $total; ?>);return false;"><?php _e( 'Show comments' ); ?></a> <span class="spinner"></span></p> <?php } wp_comment_trashnotice(); } /** * Displays slug form fields. * * @since 2.6.0 * * @param WP_Post $post Current post object. */ function post_slug_meta_box( $post ) { /** This filter is documented in wp-admin/edit-tag-form.php */ $editable_slug = apply_filters( 'editable_slug', $post->post_name, $post ); ?> <label class="screen-reader-text" for="post_name"> <?php /* translators: Hidden accessibility text. */ _e( 'Slug' ); ?> </label><input name="post_name" type="text" class="large-text" id="post_name" value="<?php echo esc_attr( $editable_slug ); ?>" /> <?php } /** * Displays form field with list of authors. * * @since 2.6.0 * * @global int $user_ID * * @param WP_Post $post Current post object. */ function post_author_meta_box( $post ) { global $user_ID; $post_type_object = get_post_type_object( $post->post_type ); ?> <label class="screen-reader-text" for="post_author_override"> <?php /* translators: Hidden accessibility text. */ _e( 'Author' ); ?> </label> <?php wp_dropdown_users( array( 'capability' => array( $post_type_object->cap->edit_posts ), 'name' => 'post_author_override', 'selected' => empty( $post->ID ) ? $user_ID : $post->post_author, 'include_selected' => true, 'show' => 'display_name_with_login', ) ); } /** * Displays list of revisions. * * @since 2.6.0 * * @param WP_Post $post Current post object. */ function post_revisions_meta_box( $post ) { wp_list_post_revisions( $post ); } // // Page-related Meta Boxes. // /** * Displays page attributes form fields. * * @since 2.7.0 * * @param WP_Post $post Current post object. */ function page_attributes_meta_box( $post ) { if ( is_post_type_hierarchical( $post->post_type ) ) : $dropdown_args = array( 'post_type' => $post->post_type, 'exclude_tree' => $post->ID, 'selected' => $post->post_parent, 'name' => 'parent_id', 'show_option_none' => __( '(no parent)' ), 'sort_column' => 'menu_order, post_title', 'echo' => 0, ); /** * Filters the arguments used to generate a Pages drop-down element. * * @since 3.3.0 * * @see wp_dropdown_pages() * * @param array $dropdown_args Array of arguments used to generate the pages drop-down. * @param WP_Post $post The current post. */ $dropdown_args = apply_filters( 'page_attributes_dropdown_pages_args', $dropdown_args, $post ); $pages = wp_dropdown_pages( $dropdown_args ); if ( ! empty( $pages ) ) : ?> <p class="post-attributes-label-wrapper parent-id-label-wrapper"><label class="post-attributes-label" for="parent_id"><?php _e( 'Parent' ); ?></label></p> <?php echo $pages; ?> <?php endif; // End empty pages check. endif; // End hierarchical check. if ( count( get_page_templates( $post ) ) > 0 && (int) get_option( 'page_for_posts' ) !== $post->ID ) : $template = ! empty( $post->page_template ) ? $post->page_template : false; ?> <p class="post-attributes-label-wrapper page-template-label-wrapper"><label class="post-attributes-label" for="page_template"><?php _e( 'Template' ); ?></label> <?php /** * Fires immediately after the label inside the 'Template' section * of the 'Page Attributes' meta box. * * @since 4.4.0 * * @param string|false $template The template used for the current post. * @param WP_Post $post The current post. */ do_action( 'page_attributes_meta_box_template', $template, $post ); ?> </p> <select name="page_template" id="page_template"> <?php /** * Filters the title of the default page template displayed in the drop-down. * * @since 4.1.0 * * @param string $label The display value for the default page template title. * @param string $context Where the option label is displayed. Possible values * include 'meta-box' or 'quick-edit'. */ $default_title = apply_filters( 'default_page_template_title', __( 'Default template' ), 'meta-box' ); ?> <option value="default"><?php echo esc_html( $default_title ); ?></option> <?php page_template_dropdown( $template, $post->post_type ); ?> </select> <?php endif; ?> <?php if ( post_type_supports( $post->post_type, 'page-attributes' ) ) : ?> <p class="post-attributes-label-wrapper menu-order-label-wrapper"><label class="post-attributes-label" for="menu_order"><?php _e( 'Order' ); ?></label></p> <input name="menu_order" type="text" size="4" id="menu_order" value="<?php echo esc_attr( $post->menu_order ); ?>" /> <?php /** * Fires before the help hint text in the 'Page Attributes' meta box. * * @since 4.9.0 * * @param WP_Post $post The current post. */ do_action( 'page_attributes_misc_attributes', $post ); ?> <?php if ( 'page' === $post->post_type && get_current_screen()->get_help_tabs() ) : ?> <p class="post-attributes-help-text"><?php _e( 'Need help? Use the Help tab above the screen title.' ); ?></p> <?php endif; endif; } // // Link-related Meta Boxes. // /** * Displays link create form fields. * * @since 2.7.0 * * @param object $link Current link object. */ function link_submit_meta_box( $link ) { ?> <div class="submitbox" id="submitlink"> <div id="minor-publishing"> <?php // Hidden submit button early on so that the browser chooses the right button when form is submitted with Return key. ?> <div style="display:none;"> <?php submit_button( __( 'Save' ), '', 'save', false ); ?> </div> <div id="minor-publishing-actions"> <div id="preview-action"> <?php if ( ! empty( $link->link_id ) ) { ?> <a class="preview button" href="<?php echo $link->link_url; ?>" target="_blank"><?php _e( 'Visit Link' ); ?></a> <?php } ?> </div> <div class="clear"></div> </div> <div id="misc-publishing-actions"> <div class="misc-pub-section misc-pub-private"> <label for="link_private" class="selectit"><input id="link_private" name="link_visible" type="checkbox" value="N" <?php checked( $link->link_visible, 'N' ); ?> /> <?php _e( 'Keep this link private' ); ?></label> </div> </div> </div> <div id="major-publishing-actions"> <?php /** This action is documented in wp-admin/includes/meta-boxes.php */ do_action( 'post_submitbox_start', null ); ?> <div id="delete-action"> <?php if ( ! empty( $_GET['action'] ) && 'edit' === $_GET['action'] && current_user_can( 'manage_links' ) ) { printf( '<a class="submitdelete deletion" href="%s" onclick="return confirm( \'%s\' );">%s</a>', wp_nonce_url( "link.php?action=delete&link_id=$link->link_id", 'delete-bookmark_' . $link->link_id ), /* translators: %s: Link name. */ esc_js( sprintf( __( "You are about to delete this link '%s'\n 'Cancel' to stop, 'OK' to delete." ), $link->link_name ) ), __( 'Delete' ) ); } ?> </div> <div id="publishing-action"> <?php if ( ! empty( $link->link_id ) ) { ?> <input name="save" type="submit" class="button button-primary button-large" id="publish" value="<?php esc_attr_e( 'Update Link' ); ?>" /> <?php } else { ?> <input name="save" type="submit" class="button button-primary button-large" id="publish" value="<?php esc_attr_e( 'Add Link' ); ?>" /> <?php } ?> </div> <div class="clear"></div> </div> <?php /** * Fires at the end of the Publish box in the Link editing screen. * * @since 2.5.0 */ do_action( 'submitlink_box' ); ?> <div class="clear"></div> </div> <?php } /** * Displays link categories form fields. * * @since 2.6.0 * * @param object $link Current link object. */ function link_categories_meta_box( $link ) { ?> <div id="taxonomy-linkcategory" class="categorydiv"> <ul id="category-tabs" class="category-tabs"> <li class="tabs"><a href="#categories-all"><?php _e( 'All categories' ); ?></a></li> <li class="hide-if-no-js"><a href="#categories-pop"><?php _ex( 'Most Used', 'categories' ); ?></a></li> </ul> <div id="categories-all" class="tabs-panel"> <ul id="categorychecklist" data-wp-lists="list:category" class="categorychecklist form-no-clear"> <?php if ( isset( $link->link_id ) ) { wp_link_category_checklist( $link->link_id ); } else { wp_link_category_checklist(); } ?> </ul> </div> <div id="categories-pop" class="tabs-panel" style="display: none;"> <ul id="categorychecklist-pop" class="categorychecklist form-no-clear"> <?php wp_popular_terms_checklist( 'link_category' ); ?> </ul> </div> <div id="category-adder" class="wp-hidden-children"> <a id="category-add-toggle" href="#category-add" class="taxonomy-add-new"><?php _e( '+ Add New Category' ); ?></a> <p id="link-category-add" class="wp-hidden-child"> <label class="screen-reader-text" for="newcat"> <?php /* translators: Hidden accessibility text. */ _e( '+ Add New Category' ); ?> </label> <input type="text" name="newcat" id="newcat" class="form-required form-input-tip" value="<?php esc_attr_e( 'New category name' ); ?>" aria-required="true" /> <input type="button" id="link-category-add-submit" data-wp-lists="add:categorychecklist:link-category-add" class="button" value="<?php esc_attr_e( 'Add' ); ?>" /> <?php wp_nonce_field( 'add-link-category', '_ajax_nonce', false ); ?> <span id="category-ajax-response"></span> </p> </div> </div> <?php } /** * Displays form fields for changing link target. * * @since 2.6.0 * * @param object $link Current link object. */ function link_target_meta_box( $link ) { ?> <fieldset><legend class="screen-reader-text"><span> <?php /* translators: Hidden accessibility text. */ _e( 'Target' ); ?> </span></legend> <p><label for="link_target_blank" class="selectit"> <input id="link_target_blank" type="radio" name="link_target" value="_blank" <?php echo ( isset( $link->link_target ) && ( '_blank' === $link->link_target ) ? 'checked="checked"' : '' ); ?> /> <?php _e( '<code>_blank</code> — new window or tab.' ); ?></label></p> <p><label for="link_target_top" class="selectit"> <input id="link_target_top" type="radio" name="link_target" value="_top" <?php echo ( isset( $link->link_target ) && ( '_top' === $link->link_target ) ? 'checked="checked"' : '' ); ?> /> <?php _e( '<code>_top</code> — current window or tab, with no frames.' ); ?></label></p> <p><label for="link_target_none" class="selectit"> <input id="link_target_none" type="radio" name="link_target" value="" <?php echo ( isset( $link->link_target ) && ( '' === $link->link_target ) ? 'checked="checked"' : '' ); ?> /> <?php _e( '<code>_none</code> — same window or tab.' ); ?></label></p> </fieldset> <p><?php _e( 'Choose the target frame for your link.' ); ?></p> <?php } /** * Displays 'checked' checkboxes attribute for XFN microformat options. * * @since 1.0.1 * * @global object $link Current link object. * * @param string $xfn_relationship XFN relationship category. Possible values are: * 'friendship', 'physical', 'professional', * 'geographical', 'family', 'romantic', 'identity'. * @param string $xfn_value Optional. The XFN value to mark as checked * if it matches the current link's relationship. * Default empty string. * @param mixed $deprecated Deprecated. Not used. */ function xfn_check( $xfn_relationship, $xfn_value = '', $deprecated = '' ) { global $link; if ( ! empty( $deprecated ) ) { _deprecated_argument( __FUNCTION__, '2.5.0' ); // Never implemented. } $link_rel = isset( $link->link_rel ) ? $link->link_rel : ''; // In PHP 5.3: $link_rel = $link->link_rel ?: ''; $link_rels = preg_split( '/\s+/', $link_rel ); // Mark the specified value as checked if it matches the current link's relationship. if ( '' !== $xfn_value && in_array( $xfn_value, $link_rels, true ) ) { echo ' checked="checked"'; } if ( '' === $xfn_value ) { // Mark the 'none' value as checked if the current link does not match the specified relationship. if ( 'family' === $xfn_relationship && ! array_intersect( $link_rels, array( 'child', 'parent', 'sibling', 'spouse', 'kin' ) ) ) { echo ' checked="checked"'; } if ( 'friendship' === $xfn_relationship && ! array_intersect( $link_rels, array( 'friend', 'acquaintance', 'contact' ) ) ) { echo ' checked="checked"'; } if ( 'geographical' === $xfn_relationship && ! array_intersect( $link_rels, array( 'co-resident', 'neighbor' ) ) ) { echo ' checked="checked"'; } // Mark the 'me' value as checked if it matches the current link's relationship. if ( 'identity' === $xfn_relationship && in_array( 'me', $link_rels, true ) ) { echo ' checked="checked"'; } } } /** * Displays XFN form fields. * * @since 2.6.0 * * @param object $link Current link object. */ function link_xfn_meta_box( $link ) { ?> <table class="links-table"> <tr> <th scope="row"><label for="link_rel"><?php /* translators: xfn: https://gmpg.org/xfn/ */ _e( 'rel:' ); ?></label></th> <td><input type="text" name="link_rel" id="link_rel" value="<?php echo ( isset( $link->link_rel ) ? esc_attr( $link->link_rel ) : '' ); ?>" /></td> </tr> <tr> <th scope="row"><?php /* translators: xfn: https://gmpg.org/xfn/ */ _e( 'identity' ); ?></th> <td><fieldset> <legend class="screen-reader-text"><span> <?php /* translators: Hidden accessibility text. xfn: https://gmpg.org/xfn/ */ _e( 'identity' ); ?> </span></legend> <label for="me"> <input type="checkbox" name="identity" value="me" id="me" <?php xfn_check( 'identity', 'me' ); ?> /> <?php _e( 'another web address of mine' ); ?></label> </fieldset></td> </tr> <tr> <th scope="row"><?php /* translators: xfn: https://gmpg.org/xfn/ */ _e( 'friendship' ); ?></th> <td><fieldset> <legend class="screen-reader-text"><span> <?php /* translators: Hidden accessibility text. xfn: https://gmpg.org/xfn/ */ _e( 'friendship' ); ?> </span></legend> <label for="contact"> <input class="valinp" type="radio" name="friendship" value="contact" id="contact" <?php xfn_check( 'friendship', 'contact' ); ?> /> <?php /* translators: xfn: https://gmpg.org/xfn/ */ _e( 'contact' ); ?> </label> <label for="acquaintance"> <input class="valinp" type="radio" name="friendship" value="acquaintance" id="acquaintance" <?php xfn_check( 'friendship', 'acquaintance' ); ?> /> <?php /* translators: xfn: https://gmpg.org/xfn/ */ _e( 'acquaintance' ); ?> </label> <label for="friend"> <input class="valinp" type="radio" name="friendship" value="friend" id="friend" <?php xfn_check( 'friendship', 'friend' ); ?> /> <?php /* translators: xfn: https://gmpg.org/xfn/ */ _e( 'friend' ); ?> </label> <label for="friendship"> <input name="friendship" type="radio" class="valinp" value="" id="friendship" <?php xfn_check( 'friendship' ); ?> /> <?php /* translators: xfn: https://gmpg.org/xfn/ */ _e( 'none' ); ?> </label> </fieldset></td> </tr> <tr> <th scope="row"> <?php /* translators: xfn: https://gmpg.org/xfn/ */ _e( 'physical' ); ?> </th> <td><fieldset> <legend class="screen-reader-text"><span> <?php /* translators: Hidden accessibility text. xfn: https://gmpg.org/xfn/ */ _e( 'physical' ); ?> </span></legend> <label for="met"> <input class="valinp" type="checkbox" name="physical" value="met" id="met" <?php xfn_check( 'physical', 'met' ); ?> /> <?php /* translators: xfn: https://gmpg.org/xfn/ */ _e( 'met' ); ?> </label> </fieldset></td> </tr> <tr> <th scope="row"> <?php /* translators: xfn: https://gmpg.org/xfn/ */ _e( 'professional' ); ?> </th> <td><fieldset> <legend class="screen-reader-text"><span> <?php /* translators: Hidden accessibility text. xfn: https://gmpg.org/xfn/ */ _e( 'professional' ); ?> </span></legend> <label for="co-worker"> <input class="valinp" type="checkbox" name="professional" value="co-worker" id="co-worker" <?php xfn_check( 'professional', 'co-worker' ); ?> /> <?php /* translators: xfn: https://gmpg.org/xfn/ */ _e( 'co-worker' ); ?> </label> <label for="colleague"> <input class="valinp" type="checkbox" name="professional" value="colleague" id="colleague" <?php xfn_check( 'professional', 'colleague' ); ?> /> <?php /* translators: xfn: https://gmpg.org/xfn/ */ _e( 'colleague' ); ?> </label> </fieldset></td> </tr> <tr> <th scope="row"><?php /* translators: xfn: https://gmpg.org/xfn/ */ _e( 'geographical' ); ?></th> <td><fieldset> <legend class="screen-reader-text"><span> <?php /* translators: Hidden accessibility text. xfn: https://gmpg.org/xfn/ */ _e( 'geographical' ); ?> </span></legend> <label for="co-resident"> <input class="valinp" type="radio" name="geographical" value="co-resident" id="co-resident" <?php xfn_check( 'geographical', 'co-resident' ); ?> /> <?php /* translators: xfn: https://gmpg.org/xfn/ */ _e( 'co-resident' ); ?> </label> <label for="neighbor"> <input class="valinp" type="radio" name="geographical" value="neighbor" id="neighbor" <?php xfn_check( 'geographical', 'neighbor' ); ?> /> <?php /* translators: xfn: https://gmpg.org/xfn/ */ _e( 'neighbor' ); ?> </label> <label for="geographical"> <input class="valinp" type="radio" name="geographical" value="" id="geographical" <?php xfn_check( 'geographical' ); ?> /> <?php /* translators: xfn: https://gmpg.org/xfn/ */ _e( 'none' ); ?> </label> </fieldset></td> </tr> <tr> <th scope="row"><?php /* translators: xfn: https://gmpg.org/xfn/ */ _e( 'family' ); ?></th> <td><fieldset> <legend class="screen-reader-text"><span> <?php /* translators: Hidden accessibility text. xfn: https://gmpg.org/xfn/ */ _e( 'family' ); ?> </span></legend> <label for="child"> <input class="valinp" type="radio" name="family" value="child" id="child" <?php xfn_check( 'family', 'child' ); ?> /> <?php /* translators: xfn: https://gmpg.org/xfn/ */ _e( 'child' ); ?> </label> <label for="kin"> <input class="valinp" type="radio" name="family" value="kin" id="kin" <?php xfn_check( 'family', 'kin' ); ?> /> <?php /* translators: xfn: https://gmpg.org/xfn/ */ _e( 'kin' ); ?> </label> <label for="parent"> <input class="valinp" type="radio" name="family" value="parent" id="parent" <?php xfn_check( 'family', 'parent' ); ?> /> <?php /* translators: xfn: https://gmpg.org/xfn/ */ _e( 'parent' ); ?> </label> <label for="sibling"> <input class="valinp" type="radio" name="family" value="sibling" id="sibling" <?php xfn_check( 'family', 'sibling' ); ?> /> <?php /* translators: xfn: https://gmpg.org/xfn/ */ _e( 'sibling' ); ?> </label> <label for="spouse"> <input class="valinp" type="radio" name="family" value="spouse" id="spouse" <?php xfn_check( 'family', 'spouse' ); ?> /> <?php /* translators: xfn: https://gmpg.org/xfn/ */ _e( 'spouse' ); ?> </label> <label for="family"> <input class="valinp" type="radio" name="family" value="" id="family" <?php xfn_check( 'family' ); ?> /> <?php /* translators: xfn: https://gmpg.org/xfn/ */ _e( 'none' ); ?> </label> </fieldset></td> </tr> <tr> <th scope="row"><?php /* translators: xfn: https://gmpg.org/xfn/ */ _e( 'romantic' ); ?></th> <td><fieldset> <legend class="screen-reader-text"><span> <?php /* translators: Hidden accessibility text. xfn: https://gmpg.org/xfn/ */ _e( 'romantic' ); ?> </span></legend> <label for="muse"> <input class="valinp" type="checkbox" name="romantic" value="muse" id="muse" <?php xfn_check( 'romantic', 'muse' ); ?> /> <?php /* translators: xfn: https://gmpg.org/xfn/ */ _e( 'muse' ); ?> </label> <label for="crush"> <input class="valinp" type="checkbox" name="romantic" value="crush" id="crush" <?php xfn_check( 'romantic', 'crush' ); ?> /> <?php /* translators: xfn: https://gmpg.org/xfn/ */ _e( 'crush' ); ?> </label> <label for="date"> <input class="valinp" type="checkbox" name="romantic" value="date" id="date" <?php xfn_check( 'romantic', 'date' ); ?> /> <?php /* translators: xfn: https://gmpg.org/xfn/ */ _e( 'date' ); ?> </label> <label for="romantic"> <input class="valinp" type="checkbox" name="romantic" value="sweetheart" id="romantic" <?php xfn_check( 'romantic', 'sweetheart' ); ?> /> <?php /* translators: xfn: https://gmpg.org/xfn/ */ _e( 'sweetheart' ); ?> </label> </fieldset></td> </tr> </table> <p><?php _e( 'If the link is to a person, you can specify your relationship with them using the above form. If you would like to learn more about the idea check out <a href="https://gmpg.org/xfn/">XFN</a>.' ); ?></p> <?php } /** * Displays advanced link options form fields. * * @since 2.6.0 * * @param object $link Current link object. */ function link_advanced_meta_box( $link ) { ?> <table class="links-table" cellpadding="0"> <tr> <th scope="row"><label for="link_image"><?php _e( 'Image Address' ); ?></label></th> <td><input type="text" name="link_image" class="code" id="link_image" maxlength="255" value="<?php echo ( isset( $link->link_image ) ? esc_attr( $link->link_image ) : '' ); ?>" /></td> </tr> <tr> <th scope="row"><label for="rss_uri"><?php _e( 'RSS Address' ); ?></label></th> <td><input name="link_rss" class="code" type="text" id="rss_uri" maxlength="255" value="<?php echo ( isset( $link->link_rss ) ? esc_attr( $link->link_rss ) : '' ); ?>" /></td> </tr> <tr> <th scope="row"><label for="link_notes"><?php _e( 'Notes' ); ?></label></th> <td><textarea name="link_notes" id="link_notes" rows="10"><?php echo ( isset( $link->link_notes ) ? $link->link_notes : '' ); // textarea_escaped ?></textarea></td> </tr> <tr> <th scope="row"><label for="link_rating"><?php _e( 'Rating' ); ?></label></th> <td><select name="link_rating" id="link_rating" size="1"> <?php for ( $rating = 0; $rating <= 10; $rating++ ) { echo '<option value="' . $rating . '"'; if ( isset( $link->link_rating ) && $link->link_rating === $rating ) { echo ' selected="selected"'; } echo '>' . $rating . '</option>'; } ?> </select> <?php _e( '(Leave at 0 for no rating.)' ); ?> </td> </tr> </table> <?php } /** * Displays post thumbnail meta box. * * @since 2.9.0 * * @param WP_Post $post Current post object. */ function post_thumbnail_meta_box( $post ) { $thumbnail_id = get_post_meta( $post->ID, '_thumbnail_id', true ); echo _wp_post_thumbnail_html( $thumbnail_id, $post->ID ); } /** * Displays fields for ID3 data. * * @since 3.9.0 * * @param WP_Post $post Current post object. */ function attachment_id3_data_meta_box( $post ) { $meta = array(); if ( ! empty( $post->ID ) ) { $meta = wp_get_attachment_metadata( $post->ID ); } foreach ( wp_get_attachment_id3_keys( $post, 'edit' ) as $key => $label ) : $value = ''; if ( ! empty( $meta[ $key ] ) ) { $value = $meta[ $key ]; } ?> <p> <label for="title"><?php echo $label; ?></label><br /> <input type="text" name="id3_<?php echo esc_attr( $key ); ?>" id="id3_<?php echo esc_attr( $key ); ?>" class="large-text" value="<?php echo esc_attr( $value ); ?>" /> </p> <?php endforeach; } /** * Registers the default post meta boxes, and runs the `do_meta_boxes` actions. * * @since 5.0.0 * * @param WP_Post $post The post object that these meta boxes are being generated for. */ function register_and_do_post_meta_boxes( $post ) { $post_type = $post->post_type; $post_type_object = get_post_type_object( $post_type ); $thumbnail_support = current_theme_supports( 'post-thumbnails', $post_type ) && post_type_supports( $post_type, 'thumbnail' ); if ( ! $thumbnail_support && 'attachment' === $post_type && $post->post_mime_type ) { if ( wp_attachment_is( 'audio', $post ) ) { $thumbnail_support = post_type_supports( 'attachment:audio', 'thumbnail' ) || current_theme_supports( 'post-thumbnails', 'attachment:audio' ); } elseif ( wp_attachment_is( 'video', $post ) ) { $thumbnail_support = post_type_supports( 'attachment:video', 'thumbnail' ) || current_theme_supports( 'post-thumbnails', 'attachment:video' ); } } $publish_callback_args = array( '__back_compat_meta_box' => true ); if ( post_type_supports( $post_type, 'revisions' ) && 'auto-draft' !== $post->post_status ) { $revisions = wp_get_latest_revision_id_and_total_count( $post->ID ); // We should aim to show the revisions meta box only when there are revisions. if ( ! is_wp_error( $revisions ) && $revisions['count'] > 1 ) { $publish_callback_args = array( 'revisions_count' => $revisions['count'], 'revision_id' => $revisions['latest_id'], '__back_compat_meta_box' => true, ); add_meta_box( 'revisionsdiv', __( 'Revisions' ), 'post_revisions_meta_box', null, 'normal', 'core', array( '__back_compat_meta_box' => true ) ); } } if ( 'attachment' === $post_type ) { wp_enqueue_script( 'image-edit' ); wp_enqueue_style( 'imgareaselect' ); add_meta_box( 'submitdiv', __( 'Save' ), 'attachment_submit_meta_box', null, 'side', 'core', array( '__back_compat_meta_box' => true ) ); add_action( 'edit_form_after_title', 'edit_form_image_editor' ); if ( wp_attachment_is( 'audio', $post ) ) { add_meta_box( 'attachment-id3', __( 'Metadata' ), 'attachment_id3_data_meta_box', null, 'normal', 'core', array( '__back_compat_meta_box' => true ) ); } } else { add_meta_box( 'submitdiv', __( 'Publish' ), 'post_submit_meta_box', null, 'side', 'core', $publish_callback_args ); } if ( current_theme_supports( 'post-formats' ) && post_type_supports( $post_type, 'post-formats' ) ) { add_meta_box( 'formatdiv', _x( 'Format', 'post format' ), 'post_format_meta_box', null, 'side', 'core', array( '__back_compat_meta_box' => true ) ); } // All taxonomies. foreach ( get_object_taxonomies( $post ) as $tax_name ) { $taxonomy = get_taxonomy( $tax_name ); if ( ! $taxonomy->show_ui || false === $taxonomy->meta_box_cb ) { continue; } $label = $taxonomy->labels->name; if ( ! is_taxonomy_hierarchical( $tax_name ) ) { $tax_meta_box_id = 'tagsdiv-' . $tax_name; } else { $tax_meta_box_id = $tax_name . 'div'; } add_meta_box( $tax_meta_box_id, $label, $taxonomy->meta_box_cb, null, 'side', 'core', array( 'taxonomy' => $tax_name, '__back_compat_meta_box' => true, ) ); } if ( post_type_supports( $post_type, 'page-attributes' ) || count( get_page_templates( $post ) ) > 0 ) { add_meta_box( 'pageparentdiv', $post_type_object->labels->attributes, 'page_attributes_meta_box', null, 'side', 'core', array( '__back_compat_meta_box' => true ) ); } if ( $thumbnail_support && current_user_can( 'upload_files' ) ) { add_meta_box( 'postimagediv', esc_html( $post_type_object->labels->featured_image ), 'post_thumbnail_meta_box', null, 'side', 'low', array( '__back_compat_meta_box' => true ) ); } if ( post_type_supports( $post_type, 'excerpt' ) ) { add_meta_box( 'postexcerpt', __( 'Excerpt' ), 'post_excerpt_meta_box', null, 'normal', 'core', array( '__back_compat_meta_box' => true ) ); } if ( post_type_supports( $post_type, 'trackbacks' ) ) { add_meta_box( 'trackbacksdiv', __( 'Send Trackbacks' ), 'post_trackback_meta_box', null, 'normal', 'core', array( '__back_compat_meta_box' => true ) ); } if ( post_type_supports( $post_type, 'custom-fields' ) ) { add_meta_box( 'postcustom', __( 'Custom Fields' ), 'post_custom_meta_box', null, 'normal', 'core', array( '__back_compat_meta_box' => ! (bool) get_user_meta( get_current_user_id(), 'enable_custom_fields', true ), '__block_editor_compatible_meta_box' => true, ) ); } /** * Fires in the middle of built-in meta box registration. * * @since 2.1.0 * @deprecated 3.7.0 Use {@see 'add_meta_boxes'} instead. * * @param WP_Post $post Post object. */ do_action_deprecated( 'dbx_post_advanced', array( $post ), '3.7.0', 'add_meta_boxes' ); /* * Allow the Discussion meta box to show up if the post type supports comments, * or if comments or pings are open. */ if ( comments_open( $post ) || pings_open( $post ) || post_type_supports( $post_type, 'comments' ) ) { add_meta_box( 'commentstatusdiv', __( 'Discussion' ), 'post_comment_status_meta_box', null, 'normal', 'core', array( '__back_compat_meta_box' => true ) ); } $stati = get_post_stati( array( 'public' => true ) ); if ( empty( $stati ) ) { $stati = array( 'publish' ); } $stati[] = 'private'; if ( in_array( get_post_status( $post ), $stati, true ) ) { /* * If the post type support comments, or the post has comments, * allow the Comments meta box. */ if ( comments_open( $post ) || pings_open( $post ) || $post->comment_count > 0 || post_type_supports( $post_type, 'comments' ) ) { add_meta_box( 'commentsdiv', __( 'Comments' ), 'post_comment_meta_box', null, 'normal', 'core', array( '__back_compat_meta_box' => true ) ); } } if ( ! ( 'pending' === get_post_status( $post ) && ! current_user_can( $post_type_object->cap->publish_posts ) ) ) { add_meta_box( 'slugdiv', __( 'Slug' ), 'post_slug_meta_box', null, 'normal', 'core', array( '__back_compat_meta_box' => true ) ); } if ( post_type_supports( $post_type, 'author' ) && current_user_can( $post_type_object->cap->edit_others_posts ) ) { add_meta_box( 'authordiv', __( 'Author' ), 'post_author_meta_box', null, 'normal', 'core', array( '__back_compat_meta_box' => true ) ); } /** * Fires after all built-in meta boxes have been added. * * @since 3.0.0 * * @param string $post_type Post type. * @param WP_Post $post Post object. */ do_action( 'add_meta_boxes', $post_type, $post ); /** * Fires after all built-in meta boxes have been added, contextually for the given post type. * * The dynamic portion of the hook name, `$post_type`, refers to the post type of the post. * * Possible hook names include: * * - `add_meta_boxes_post` * - `add_meta_boxes_page` * - `add_meta_boxes_attachment` * * @since 3.0.0 * * @param WP_Post $post Post object. */ do_action( "add_meta_boxes_{$post_type}", $post ); /** * Fires after meta boxes have been added. * * Fires once for each of the default meta box contexts: normal, advanced, and side. * * @since 3.0.0 * * @param string $post_type Post type of the post on Edit Post screen, 'link' on Edit Link screen, * 'dashboard' on Dashboard screen. * @param string $context Meta box context. Possible values include 'normal', 'advanced', 'side'. * @param WP_Post|object|string $post Post object on Edit Post screen, link object on Edit Link screen, * an empty string on Dashboard screen. */ do_action( 'do_meta_boxes', $post_type, 'normal', $post ); /** This action is documented in wp-admin/includes/meta-boxes.php */ do_action( 'do_meta_boxes', $post_type, 'advanced', $post ); /** This action is documented in wp-admin/includes/meta-boxes.php */ do_action( 'do_meta_boxes', $post_type, 'side', $post ); } includes/screen.php 0000644 00000014323 15135536347 0010361 0 ustar 00 <?php /** * WordPress Administration Screen API. * * @package WordPress * @subpackage Administration */ /** * Get the column headers for a screen * * @since 2.7.0 * * @param string|WP_Screen $screen The screen you want the headers for * @return string[] The column header labels keyed by column ID. */ function get_column_headers( $screen ) { static $column_headers = array(); if ( is_string( $screen ) ) { $screen = convert_to_screen( $screen ); } if ( ! isset( $column_headers[ $screen->id ] ) ) { /** * Filters the column headers for a list table on a specific screen. * * The dynamic portion of the hook name, `$screen->id`, refers to the * ID of a specific screen. For example, the screen ID for the Posts * list table is edit-post, so the filter for that screen would be * manage_edit-post_columns. * * @since 3.0.0 * * @param string[] $columns The column header labels keyed by column ID. */ $column_headers[ $screen->id ] = apply_filters( "manage_{$screen->id}_columns", array() ); } return $column_headers[ $screen->id ]; } /** * Get a list of hidden columns. * * @since 2.7.0 * * @param string|WP_Screen $screen The screen you want the hidden columns for * @return string[] Array of IDs of hidden columns. */ function get_hidden_columns( $screen ) { if ( is_string( $screen ) ) { $screen = convert_to_screen( $screen ); } $hidden = get_user_option( 'manage' . $screen->id . 'columnshidden' ); $use_defaults = ! is_array( $hidden ); if ( $use_defaults ) { $hidden = array(); /** * Filters the default list of hidden columns. * * @since 4.4.0 * * @param string[] $hidden Array of IDs of columns hidden by default. * @param WP_Screen $screen WP_Screen object of the current screen. */ $hidden = apply_filters( 'default_hidden_columns', $hidden, $screen ); } /** * Filters the list of hidden columns. * * @since 4.4.0 * @since 4.4.1 Added the `use_defaults` parameter. * * @param string[] $hidden Array of IDs of hidden columns. * @param WP_Screen $screen WP_Screen object of the current screen. * @param bool $use_defaults Whether to show the default columns. */ return apply_filters( 'hidden_columns', $hidden, $screen, $use_defaults ); } /** * Prints the meta box preferences for screen meta. * * @since 2.7.0 * * @global array $wp_meta_boxes * * @param WP_Screen $screen */ function meta_box_prefs( $screen ) { global $wp_meta_boxes; if ( is_string( $screen ) ) { $screen = convert_to_screen( $screen ); } if ( empty( $wp_meta_boxes[ $screen->id ] ) ) { return; } $hidden = get_hidden_meta_boxes( $screen ); foreach ( array_keys( $wp_meta_boxes[ $screen->id ] ) as $context ) { foreach ( array( 'high', 'core', 'default', 'low' ) as $priority ) { if ( ! isset( $wp_meta_boxes[ $screen->id ][ $context ][ $priority ] ) ) { continue; } foreach ( $wp_meta_boxes[ $screen->id ][ $context ][ $priority ] as $box ) { if ( false === $box || ! $box['title'] ) { continue; } // Submit box cannot be hidden. if ( 'submitdiv' === $box['id'] || 'linksubmitdiv' === $box['id'] ) { continue; } $widget_title = $box['title']; if ( is_array( $box['args'] ) && isset( $box['args']['__widget_basename'] ) ) { $widget_title = $box['args']['__widget_basename']; } $is_hidden = in_array( $box['id'], $hidden, true ); printf( '<label for="%1$s-hide"><input class="hide-postbox-tog" name="%1$s-hide" type="checkbox" id="%1$s-hide" value="%1$s" %2$s />%3$s</label>', esc_attr( $box['id'] ), checked( $is_hidden, false, false ), $widget_title ); } } } } /** * Gets an array of IDs of hidden meta boxes. * * @since 2.7.0 * * @param string|WP_Screen $screen Screen identifier * @return string[] IDs of hidden meta boxes. */ function get_hidden_meta_boxes( $screen ) { if ( is_string( $screen ) ) { $screen = convert_to_screen( $screen ); } $hidden = get_user_option( "metaboxhidden_{$screen->id}" ); $use_defaults = ! is_array( $hidden ); // Hide slug boxes by default. if ( $use_defaults ) { $hidden = array(); if ( 'post' === $screen->base ) { if ( in_array( $screen->post_type, array( 'post', 'page', 'attachment' ), true ) ) { $hidden = array( 'slugdiv', 'trackbacksdiv', 'postcustom', 'postexcerpt', 'commentstatusdiv', 'commentsdiv', 'authordiv', 'revisionsdiv' ); } else { $hidden = array( 'slugdiv' ); } } /** * Filters the default list of hidden meta boxes. * * @since 3.1.0 * * @param string[] $hidden An array of IDs of meta boxes hidden by default. * @param WP_Screen $screen WP_Screen object of the current screen. */ $hidden = apply_filters( 'default_hidden_meta_boxes', $hidden, $screen ); } /** * Filters the list of hidden meta boxes. * * @since 3.3.0 * * @param string[] $hidden An array of IDs of hidden meta boxes. * @param WP_Screen $screen WP_Screen object of the current screen. * @param bool $use_defaults Whether to show the default meta boxes. * Default true. */ return apply_filters( 'hidden_meta_boxes', $hidden, $screen, $use_defaults ); } /** * Register and configure an admin screen option * * @since 3.1.0 * * @param string $option An option name. * @param mixed $args Option-dependent arguments. */ function add_screen_option( $option, $args = array() ) { $current_screen = get_current_screen(); if ( ! $current_screen ) { return; } $current_screen->add_option( $option, $args ); } /** * Get the current screen object * * @since 3.1.0 * * @global WP_Screen $current_screen WordPress current screen object. * * @return WP_Screen|null Current screen object or null when screen not defined. */ function get_current_screen() { global $current_screen; if ( ! isset( $current_screen ) ) { return null; } return $current_screen; } /** * Set the current screen object * * @since 3.0.0 * * @param string|WP_Screen $hook_name Optional. The hook name (also known as the hook suffix) used to determine the screen, * or an existing screen object. */ function set_current_screen( $hook_name = '' ) { WP_Screen::get( $hook_name )->set_current_screen(); } includes/ms-deprecated.php 0000644 00000007272 15135536347 0011624 0 ustar 00 <?php /** * Multisite: Deprecated admin functions from past versions and WordPress MU * * These functions should not be used and will be removed in a later version. * It is suggested to use for the alternatives instead when available. * * @package WordPress * @subpackage Deprecated * @since 3.0.0 */ /** * Outputs the WPMU menu. * * @deprecated 3.0.0 */ function wpmu_menu() { _deprecated_function( __FUNCTION__, '3.0.0' ); // Deprecated. See #11763. } /** * Determines if the available space defined by the admin has been exceeded by the user. * * @deprecated 3.0.0 Use is_upload_space_available() * @see is_upload_space_available() */ function wpmu_checkAvailableSpace() { _deprecated_function( __FUNCTION__, '3.0.0', 'is_upload_space_available()' ); if ( ! is_upload_space_available() ) { wp_die( sprintf( /* translators: %s: Allowed space allocation. */ __( 'Sorry, you have used your space allocation of %s. Please delete some files to upload more files.' ), size_format( get_space_allowed() * MB_IN_BYTES ) ) ); } } /** * WPMU options. * * @deprecated 3.0.0 */ function mu_options( $options ) { _deprecated_function( __FUNCTION__, '3.0.0' ); return $options; } /** * Deprecated functionality for activating a network-only plugin. * * @deprecated 3.0.0 Use activate_plugin() * @see activate_plugin() */ function activate_sitewide_plugin() { _deprecated_function( __FUNCTION__, '3.0.0', 'activate_plugin()' ); return false; } /** * Deprecated functionality for deactivating a network-only plugin. * * @deprecated 3.0.0 Use deactivate_plugin() * @see deactivate_plugin() */ function deactivate_sitewide_plugin( $plugin = false ) { _deprecated_function( __FUNCTION__, '3.0.0', 'deactivate_plugin()' ); } /** * Deprecated functionality for determining if the current plugin is network-only. * * @deprecated 3.0.0 Use is_network_only_plugin() * @see is_network_only_plugin() */ function is_wpmu_sitewide_plugin( $file ) { _deprecated_function( __FUNCTION__, '3.0.0', 'is_network_only_plugin()' ); return is_network_only_plugin( $file ); } /** * Deprecated functionality for getting themes network-enabled themes. * * @deprecated 3.4.0 Use WP_Theme::get_allowed_on_network() * @see WP_Theme::get_allowed_on_network() */ function get_site_allowed_themes() { _deprecated_function( __FUNCTION__, '3.4.0', 'WP_Theme::get_allowed_on_network()' ); return array_map( 'intval', WP_Theme::get_allowed_on_network() ); } /** * Deprecated functionality for getting themes allowed on a specific site. * * @deprecated 3.4.0 Use WP_Theme::get_allowed_on_site() * @see WP_Theme::get_allowed_on_site() */ function wpmu_get_blog_allowedthemes( $blog_id = 0 ) { _deprecated_function( __FUNCTION__, '3.4.0', 'WP_Theme::get_allowed_on_site()' ); return array_map( 'intval', WP_Theme::get_allowed_on_site( $blog_id ) ); } /** * Deprecated functionality for determining whether a file is deprecated. * * @deprecated 3.5.0 */ function ms_deprecated_blogs_file() {} if ( ! function_exists( 'install_global_terms' ) ) : /** * Install global terms. * * @since 3.0.0 * @since 6.1.0 This function no longer does anything. * @deprecated 6.1.0 */ function install_global_terms() { _deprecated_function( __FUNCTION__, '6.1.0' ); } endif; /** * Synchronizes category and post tag slugs when global terms are enabled. * * @since 3.0.0 * @since 6.1.0 This function no longer does anything. * @deprecated 6.1.0 * * @param WP_Term|array $term The term. * @param string $taxonomy The taxonomy for `$term`. * @return WP_Term|array Always returns `$term`. */ function sync_category_tag_slugs( $term, $taxonomy ) { _deprecated_function( __FUNCTION__, '6.1.0' ); return $term; } includes/update.php 0000644 00000105536 15135536347 0010373 0 ustar 00 <?php /** * WordPress Administration Update API * * @package WordPress * @subpackage Administration */ /** * Selects the first update version from the update_core option. * * @since 2.7.0 * * @return object|array|false The response from the API on success, false on failure. */ function get_preferred_from_update_core() { $updates = get_core_updates(); if ( ! is_array( $updates ) ) { return false; } if ( empty( $updates ) ) { return (object) array( 'response' => 'latest' ); } return $updates[0]; } /** * Gets available core updates. * * @since 2.7.0 * * @param array $options Set $options['dismissed'] to true to show dismissed upgrades too, * set $options['available'] to false to skip not-dismissed updates. * @return array|false Array of the update objects on success, false on failure. */ function get_core_updates( $options = array() ) { $options = array_merge( array( 'available' => true, 'dismissed' => false, ), $options ); $dismissed = get_site_option( 'dismissed_update_core' ); if ( ! is_array( $dismissed ) ) { $dismissed = array(); } $from_api = get_site_transient( 'update_core' ); if ( ! isset( $from_api->updates ) || ! is_array( $from_api->updates ) ) { return false; } $updates = $from_api->updates; $result = array(); foreach ( $updates as $update ) { if ( 'autoupdate' === $update->response ) { continue; } if ( array_key_exists( $update->current . '|' . $update->locale, $dismissed ) ) { if ( $options['dismissed'] ) { $update->dismissed = true; $result[] = $update; } } else { if ( $options['available'] ) { $update->dismissed = false; $result[] = $update; } } } return $result; } /** * Gets the best available (and enabled) Auto-Update for WordPress core. * * If there's 1.2.3 and 1.3 on offer, it'll choose 1.3 if the installation allows it, else, 1.2.3. * * @since 3.7.0 * * @return object|false The core update offering on success, false on failure. */ function find_core_auto_update() { $updates = get_site_transient( 'update_core' ); if ( ! $updates || empty( $updates->updates ) ) { return false; } require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'; $auto_update = false; $upgrader = new WP_Automatic_Updater(); foreach ( $updates->updates as $update ) { if ( 'autoupdate' !== $update->response ) { continue; } if ( ! $upgrader->should_update( 'core', $update, ABSPATH ) ) { continue; } if ( ! $auto_update || version_compare( $update->current, $auto_update->current, '>' ) ) { $auto_update = $update; } } return $auto_update; } /** * Gets and caches the checksums for the given version of WordPress. * * @since 3.7.0 * * @param string $version Version string to query. * @param string $locale Locale to query. * @return array|false An array of checksums on success, false on failure. */ function get_core_checksums( $version, $locale ) { $http_url = 'http://api.wordpress.org/core/checksums/1.0/?' . http_build_query( compact( 'version', 'locale' ), '', '&' ); $url = $http_url; $ssl = wp_http_supports( array( 'ssl' ) ); if ( $ssl ) { $url = set_url_scheme( $url, 'https' ); } $options = array( 'timeout' => wp_doing_cron() ? 30 : 3, ); $response = wp_remote_get( $url, $options ); if ( $ssl && is_wp_error( $response ) ) { trigger_error( sprintf( /* translators: %s: Support forums URL. */ __( 'An unexpected error occurred. Something may be wrong with WordPress.org or this server’s configuration. If you continue to have problems, please try the <a href="%s">support forums</a>.' ), __( 'https://wordpress.org/support/forums/' ) ) . ' ' . __( '(WordPress could not establish a secure connection to WordPress.org. Please contact your server administrator.)' ), headers_sent() || WP_DEBUG ? E_USER_WARNING : E_USER_NOTICE ); $response = wp_remote_get( $http_url, $options ); } if ( is_wp_error( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) { return false; } $body = trim( wp_remote_retrieve_body( $response ) ); $body = json_decode( $body, true ); if ( ! is_array( $body ) || ! isset( $body['checksums'] ) || ! is_array( $body['checksums'] ) ) { return false; } return $body['checksums']; } /** * Dismisses core update. * * @since 2.7.0 * * @param object $update * @return bool */ function dismiss_core_update( $update ) { $dismissed = get_site_option( 'dismissed_update_core' ); $dismissed[ $update->current . '|' . $update->locale ] = true; return update_site_option( 'dismissed_update_core', $dismissed ); } /** * Undismisses core update. * * @since 2.7.0 * * @param string $version * @param string $locale * @return bool */ function undismiss_core_update( $version, $locale ) { $dismissed = get_site_option( 'dismissed_update_core' ); $key = $version . '|' . $locale; if ( ! isset( $dismissed[ $key ] ) ) { return false; } unset( $dismissed[ $key ] ); return update_site_option( 'dismissed_update_core', $dismissed ); } /** * Finds the available update for WordPress core. * * @since 2.7.0 * * @param string $version Version string to find the update for. * @param string $locale Locale to find the update for. * @return object|false The core update offering on success, false on failure. */ function find_core_update( $version, $locale ) { $from_api = get_site_transient( 'update_core' ); if ( ! isset( $from_api->updates ) || ! is_array( $from_api->updates ) ) { return false; } $updates = $from_api->updates; foreach ( $updates as $update ) { if ( $update->current === $version && $update->locale === $locale ) { return $update; } } return false; } /** * Returns core update footer message. * * @since 2.3.0 * * @param string $msg * @return string */ function core_update_footer( $msg = '' ) { if ( ! current_user_can( 'update_core' ) ) { /* translators: %s: WordPress version. */ return sprintf( __( 'Version %s' ), get_bloginfo( 'version', 'display' ) ); } $cur = get_preferred_from_update_core(); if ( ! is_object( $cur ) ) { $cur = new stdClass(); } if ( ! isset( $cur->current ) ) { $cur->current = ''; } if ( ! isset( $cur->response ) ) { $cur->response = ''; } // Include an unmodified $wp_version. require ABSPATH . WPINC . '/version.php'; $is_development_version = preg_match( '/alpha|beta|RC/', $wp_version ); if ( $is_development_version ) { return sprintf( /* translators: 1: WordPress version number, 2: URL to WordPress Updates screen. */ __( 'You are using a development version (%1$s). Cool! Please <a href="%2$s">stay updated</a>.' ), get_bloginfo( 'version', 'display' ), network_admin_url( 'update-core.php' ) ); } switch ( $cur->response ) { case 'upgrade': return sprintf( '<strong><a href="%s">%s</a></strong>', network_admin_url( 'update-core.php' ), /* translators: %s: WordPress version. */ sprintf( __( 'Get Version %s' ), $cur->current ) ); case 'latest': default: /* translators: %s: WordPress version. */ return sprintf( __( 'Version %s' ), get_bloginfo( 'version', 'display' ) ); } } /** * Returns core update notification message. * * @since 2.3.0 * * @global string $pagenow The filename of the current screen. * @return void|false */ function update_nag() { global $pagenow; if ( is_multisite() && ! current_user_can( 'update_core' ) ) { return false; } if ( 'update-core.php' === $pagenow ) { return; } $cur = get_preferred_from_update_core(); if ( ! isset( $cur->response ) || 'upgrade' !== $cur->response ) { return false; } $version_url = sprintf( /* translators: %s: WordPress version. */ esc_url( __( 'https://wordpress.org/documentation/wordpress-version/version-%s/' ) ), sanitize_title( $cur->current ) ); if ( current_user_can( 'update_core' ) ) { $msg = sprintf( /* translators: 1: URL to WordPress release notes, 2: New WordPress version, 3: URL to network admin, 4: Accessibility text. */ __( '<a href="%1$s">WordPress %2$s</a> is available! <a href="%3$s" aria-label="%4$s">Please update now</a>.' ), $version_url, $cur->current, network_admin_url( 'update-core.php' ), esc_attr__( 'Please update WordPress now' ) ); } else { $msg = sprintf( /* translators: 1: URL to WordPress release notes, 2: New WordPress version. */ __( '<a href="%1$s">WordPress %2$s</a> is available! Please notify the site administrator.' ), $version_url, $cur->current ); } wp_admin_notice( $msg, array( 'type' => 'warning', 'additional_classes' => array( 'update-nag', 'inline' ), 'paragraph_wrap' => false, ) ); } /** * Displays WordPress version and active theme in the 'At a Glance' dashboard widget. * * @since 2.5.0 */ function update_right_now_message() { $theme_name = wp_get_theme(); if ( current_user_can( 'switch_themes' ) ) { $theme_name = sprintf( '<a href="themes.php">%1$s</a>', $theme_name ); } $msg = ''; if ( current_user_can( 'update_core' ) ) { $cur = get_preferred_from_update_core(); if ( isset( $cur->response ) && 'upgrade' === $cur->response ) { $msg .= sprintf( '<a href="%s" class="button" aria-describedby="wp-version">%s</a> ', network_admin_url( 'update-core.php' ), /* translators: %s: WordPress version number, or 'Latest' string. */ sprintf( __( 'Update to %s' ), $cur->current ? $cur->current : __( 'Latest' ) ) ); } } /* translators: 1: Version number, 2: Theme name. */ $content = __( 'WordPress %1$s running %2$s theme.' ); /** * Filters the text displayed in the 'At a Glance' dashboard widget. * * Prior to 3.8.0, the widget was named 'Right Now'. * * @since 4.4.0 * * @param string $content Default text. */ $content = apply_filters( 'update_right_now_text', $content ); $msg .= sprintf( '<span id="wp-version">' . $content . '</span>', get_bloginfo( 'version', 'display' ), $theme_name ); echo "<p id='wp-version-message'>$msg</p>"; } /** * Retrieves plugins with updates available. * * @since 2.9.0 * * @return array */ function get_plugin_updates() { $all_plugins = get_plugins(); $upgrade_plugins = array(); $current = get_site_transient( 'update_plugins' ); foreach ( (array) $all_plugins as $plugin_file => $plugin_data ) { if ( isset( $current->response[ $plugin_file ] ) ) { $upgrade_plugins[ $plugin_file ] = (object) $plugin_data; $upgrade_plugins[ $plugin_file ]->update = $current->response[ $plugin_file ]; } } return $upgrade_plugins; } /** * Adds a callback to display update information for plugins with updates available. * * @since 2.9.0 */ function wp_plugin_update_rows() { if ( ! current_user_can( 'update_plugins' ) ) { return; } $plugins = get_site_transient( 'update_plugins' ); if ( isset( $plugins->response ) && is_array( $plugins->response ) ) { $plugins = array_keys( $plugins->response ); foreach ( $plugins as $plugin_file ) { add_action( "after_plugin_row_{$plugin_file}", 'wp_plugin_update_row', 10, 2 ); } } } /** * Displays update information for a plugin. * * @since 2.3.0 * * @param string $file Plugin basename. * @param array $plugin_data Plugin information. * @return void|false */ function wp_plugin_update_row( $file, $plugin_data ) { $current = get_site_transient( 'update_plugins' ); if ( ! isset( $current->response[ $file ] ) ) { return false; } $response = $current->response[ $file ]; $plugins_allowedtags = array( 'a' => array( 'href' => array(), 'title' => array(), ), 'abbr' => array( 'title' => array() ), 'acronym' => array( 'title' => array() ), 'code' => array(), 'em' => array(), 'strong' => array(), ); $plugin_name = wp_kses( $plugin_data['Name'], $plugins_allowedtags ); $plugin_slug = isset( $response->slug ) ? $response->slug : $response->id; if ( isset( $response->slug ) ) { $details_url = self_admin_url( 'plugin-install.php?tab=plugin-information&plugin=' . $plugin_slug . '§ion=changelog' ); } elseif ( isset( $response->url ) ) { $details_url = $response->url; } else { $details_url = $plugin_data['PluginURI']; } $details_url = add_query_arg( array( 'TB_iframe' => 'true', 'width' => 600, 'height' => 800, ), $details_url ); /** @var WP_Plugins_List_Table $wp_list_table */ $wp_list_table = _get_list_table( 'WP_Plugins_List_Table', array( 'screen' => get_current_screen(), ) ); if ( is_network_admin() || ! is_multisite() ) { if ( is_network_admin() ) { $active_class = is_plugin_active_for_network( $file ) ? ' active' : ''; } else { $active_class = is_plugin_active( $file ) ? ' active' : ''; } $requires_php = isset( $response->requires_php ) ? $response->requires_php : null; $compatible_php = is_php_version_compatible( $requires_php ); $notice_type = $compatible_php ? 'notice-warning' : 'notice-error'; printf( '<tr class="plugin-update-tr%s" id="%s" data-slug="%s" data-plugin="%s">' . '<td colspan="%s" class="plugin-update colspanchange">' . '<div class="update-message notice inline %s notice-alt"><p>', $active_class, esc_attr( $plugin_slug . '-update' ), esc_attr( $plugin_slug ), esc_attr( $file ), esc_attr( $wp_list_table->get_column_count() ), $notice_type ); if ( ! current_user_can( 'update_plugins' ) ) { printf( /* translators: 1: Plugin name, 2: Details URL, 3: Additional link attributes, 4: Version number. */ __( 'There is a new version of %1$s available. <a href="%2$s" %3$s>View version %4$s details</a>.' ), $plugin_name, esc_url( $details_url ), sprintf( 'class="thickbox open-plugin-details-modal" aria-label="%s"', /* translators: 1: Plugin name, 2: Version number. */ esc_attr( sprintf( __( 'View %1$s version %2$s details' ), $plugin_name, $response->new_version ) ) ), esc_attr( $response->new_version ) ); } elseif ( empty( $response->package ) ) { printf( /* translators: 1: Plugin name, 2: Details URL, 3: Additional link attributes, 4: Version number. */ __( 'There is a new version of %1$s available. <a href="%2$s" %3$s>View version %4$s details</a>. <em>Automatic update is unavailable for this plugin.</em>' ), $plugin_name, esc_url( $details_url ), sprintf( 'class="thickbox open-plugin-details-modal" aria-label="%s"', /* translators: 1: Plugin name, 2: Version number. */ esc_attr( sprintf( __( 'View %1$s version %2$s details' ), $plugin_name, $response->new_version ) ) ), esc_attr( $response->new_version ) ); } else { if ( $compatible_php ) { printf( /* translators: 1: Plugin name, 2: Details URL, 3: Additional link attributes, 4: Version number, 5: Update URL, 6: Additional link attributes. */ __( 'There is a new version of %1$s available. <a href="%2$s" %3$s>View version %4$s details</a> or <a href="%5$s" %6$s>update now</a>.' ), $plugin_name, esc_url( $details_url ), sprintf( 'class="thickbox open-plugin-details-modal" aria-label="%s"', /* translators: 1: Plugin name, 2: Version number. */ esc_attr( sprintf( __( 'View %1$s version %2$s details' ), $plugin_name, $response->new_version ) ) ), esc_attr( $response->new_version ), wp_nonce_url( self_admin_url( 'update.php?action=upgrade-plugin&plugin=' ) . $file, 'upgrade-plugin_' . $file ), sprintf( 'class="update-link" aria-label="%s"', /* translators: %s: Plugin name. */ esc_attr( sprintf( _x( 'Update %s now', 'plugin' ), $plugin_name ) ) ) ); } else { printf( /* translators: 1: Plugin name, 2: Details URL, 3: Additional link attributes, 4: Version number 5: URL to Update PHP page. */ __( 'There is a new version of %1$s available, but it does not work with your version of PHP. <a href="%2$s" %3$s>View version %4$s details</a> or <a href="%5$s">learn more about updating PHP</a>.' ), $plugin_name, esc_url( $details_url ), sprintf( 'class="thickbox open-plugin-details-modal" aria-label="%s"', /* translators: 1: Plugin name, 2: Version number. */ esc_attr( sprintf( __( 'View %1$s version %2$s details' ), $plugin_name, $response->new_version ) ) ), esc_attr( $response->new_version ), esc_url( wp_get_update_php_url() ) ); wp_update_php_annotation( '<br><em>', '</em>' ); } } /** * Fires at the end of the update message container in each * row of the plugins list table. * * The dynamic portion of the hook name, `$file`, refers to the path * of the plugin's primary file relative to the plugins directory. * * @since 2.8.0 * * @param array $plugin_data An array of plugin metadata. See get_plugin_data() * and the {@see 'plugin_row_meta'} filter for the list * of possible values. * @param object $response { * An object of metadata about the available plugin update. * * @type string $id Plugin ID, e.g. `w.org/plugins/[plugin-name]`. * @type string $slug Plugin slug. * @type string $plugin Plugin basename. * @type string $new_version New plugin version. * @type string $url Plugin URL. * @type string $package Plugin update package URL. * @type string[] $icons An array of plugin icon URLs. * @type string[] $banners An array of plugin banner URLs. * @type string[] $banners_rtl An array of plugin RTL banner URLs. * @type string $requires The version of WordPress which the plugin requires. * @type string $tested The version of WordPress the plugin is tested against. * @type string $requires_php The version of PHP which the plugin requires. * } */ do_action( "in_plugin_update_message-{$file}", $plugin_data, $response ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores echo '</p></div></td></tr>'; } } /** * Retrieves themes with updates available. * * @since 2.9.0 * * @return array */ function get_theme_updates() { $current = get_site_transient( 'update_themes' ); if ( ! isset( $current->response ) ) { return array(); } $update_themes = array(); foreach ( $current->response as $stylesheet => $data ) { $update_themes[ $stylesheet ] = wp_get_theme( $stylesheet ); $update_themes[ $stylesheet ]->update = $data; } return $update_themes; } /** * Adds a callback to display update information for themes with updates available. * * @since 3.1.0 */ function wp_theme_update_rows() { if ( ! current_user_can( 'update_themes' ) ) { return; } $themes = get_site_transient( 'update_themes' ); if ( isset( $themes->response ) && is_array( $themes->response ) ) { $themes = array_keys( $themes->response ); foreach ( $themes as $theme ) { add_action( "after_theme_row_{$theme}", 'wp_theme_update_row', 10, 2 ); } } } /** * Displays update information for a theme. * * @since 3.1.0 * * @param string $theme_key Theme stylesheet. * @param WP_Theme $theme Theme object. * @return void|false */ function wp_theme_update_row( $theme_key, $theme ) { $current = get_site_transient( 'update_themes' ); if ( ! isset( $current->response[ $theme_key ] ) ) { return false; } $response = $current->response[ $theme_key ]; $details_url = add_query_arg( array( 'TB_iframe' => 'true', 'width' => 1024, 'height' => 800, ), $current->response[ $theme_key ]['url'] ); /** @var WP_MS_Themes_List_Table $wp_list_table */ $wp_list_table = _get_list_table( 'WP_MS_Themes_List_Table' ); $active = $theme->is_allowed( 'network' ) ? ' active' : ''; $requires_wp = isset( $response['requires'] ) ? $response['requires'] : null; $requires_php = isset( $response['requires_php'] ) ? $response['requires_php'] : null; $compatible_wp = is_wp_version_compatible( $requires_wp ); $compatible_php = is_php_version_compatible( $requires_php ); printf( '<tr class="plugin-update-tr%s" id="%s" data-slug="%s">' . '<td colspan="%s" class="plugin-update colspanchange">' . '<div class="update-message notice inline notice-warning notice-alt"><p>', $active, esc_attr( $theme->get_stylesheet() . '-update' ), esc_attr( $theme->get_stylesheet() ), $wp_list_table->get_column_count() ); if ( $compatible_wp && $compatible_php ) { if ( ! current_user_can( 'update_themes' ) ) { printf( /* translators: 1: Theme name, 2: Details URL, 3: Additional link attributes, 4: Version number. */ __( 'There is a new version of %1$s available. <a href="%2$s" %3$s>View version %4$s details</a>.' ), $theme['Name'], esc_url( $details_url ), sprintf( 'class="thickbox open-plugin-details-modal" aria-label="%s"', /* translators: 1: Theme name, 2: Version number. */ esc_attr( sprintf( __( 'View %1$s version %2$s details' ), $theme['Name'], $response['new_version'] ) ) ), $response['new_version'] ); } elseif ( empty( $response['package'] ) ) { printf( /* translators: 1: Theme name, 2: Details URL, 3: Additional link attributes, 4: Version number. */ __( 'There is a new version of %1$s available. <a href="%2$s" %3$s>View version %4$s details</a>. <em>Automatic update is unavailable for this theme.</em>' ), $theme['Name'], esc_url( $details_url ), sprintf( 'class="thickbox open-plugin-details-modal" aria-label="%s"', /* translators: 1: Theme name, 2: Version number. */ esc_attr( sprintf( __( 'View %1$s version %2$s details' ), $theme['Name'], $response['new_version'] ) ) ), $response['new_version'] ); } else { printf( /* translators: 1: Theme name, 2: Details URL, 3: Additional link attributes, 4: Version number, 5: Update URL, 6: Additional link attributes. */ __( 'There is a new version of %1$s available. <a href="%2$s" %3$s>View version %4$s details</a> or <a href="%5$s" %6$s>update now</a>.' ), $theme['Name'], esc_url( $details_url ), sprintf( 'class="thickbox open-plugin-details-modal" aria-label="%s"', /* translators: 1: Theme name, 2: Version number. */ esc_attr( sprintf( __( 'View %1$s version %2$s details' ), $theme['Name'], $response['new_version'] ) ) ), $response['new_version'], wp_nonce_url( self_admin_url( 'update.php?action=upgrade-theme&theme=' ) . $theme_key, 'upgrade-theme_' . $theme_key ), sprintf( 'class="update-link" aria-label="%s"', /* translators: %s: Theme name. */ esc_attr( sprintf( _x( 'Update %s now', 'theme' ), $theme['Name'] ) ) ) ); } } else { if ( ! $compatible_wp && ! $compatible_php ) { printf( /* translators: %s: Theme name. */ __( 'There is a new version of %s available, but it does not work with your versions of WordPress and PHP.' ), $theme['Name'] ); if ( current_user_can( 'update_core' ) && current_user_can( 'update_php' ) ) { printf( /* translators: 1: URL to WordPress Updates screen, 2: URL to Update PHP page. */ ' ' . __( '<a href="%1$s">Please update WordPress</a>, and then <a href="%2$s">learn more about updating PHP</a>.' ), self_admin_url( 'update-core.php' ), esc_url( wp_get_update_php_url() ) ); wp_update_php_annotation( '</p><p><em>', '</em>' ); } elseif ( current_user_can( 'update_core' ) ) { printf( /* translators: %s: URL to WordPress Updates screen. */ ' ' . __( '<a href="%s">Please update WordPress</a>.' ), self_admin_url( 'update-core.php' ) ); } elseif ( current_user_can( 'update_php' ) ) { printf( /* translators: %s: URL to Update PHP page. */ ' ' . __( '<a href="%s">Learn more about updating PHP</a>.' ), esc_url( wp_get_update_php_url() ) ); wp_update_php_annotation( '</p><p><em>', '</em>' ); } } elseif ( ! $compatible_wp ) { printf( /* translators: %s: Theme name. */ __( 'There is a new version of %s available, but it does not work with your version of WordPress.' ), $theme['Name'] ); if ( current_user_can( 'update_core' ) ) { printf( /* translators: %s: URL to WordPress Updates screen. */ ' ' . __( '<a href="%s">Please update WordPress</a>.' ), self_admin_url( 'update-core.php' ) ); } } elseif ( ! $compatible_php ) { printf( /* translators: %s: Theme name. */ __( 'There is a new version of %s available, but it does not work with your version of PHP.' ), $theme['Name'] ); if ( current_user_can( 'update_php' ) ) { printf( /* translators: %s: URL to Update PHP page. */ ' ' . __( '<a href="%s">Learn more about updating PHP</a>.' ), esc_url( wp_get_update_php_url() ) ); wp_update_php_annotation( '</p><p><em>', '</em>' ); } } } /** * Fires at the end of the update message container in each * row of the themes list table. * * The dynamic portion of the hook name, `$theme_key`, refers to * the theme slug as found in the WordPress.org themes repository. * * @since 3.1.0 * * @param WP_Theme $theme The WP_Theme object. * @param array $response { * An array of metadata about the available theme update. * * @type string $new_version New theme version. * @type string $url Theme URL. * @type string $package Theme update package URL. * } */ do_action( "in_theme_update_message-{$theme_key}", $theme, $response ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores echo '</p></div></td></tr>'; } /** * Displays maintenance nag HTML message. * * @since 2.7.0 * * @global int $upgrading * * @return void|false */ function maintenance_nag() { // Include an unmodified $wp_version. require ABSPATH . WPINC . '/version.php'; global $upgrading; $nag = isset( $upgrading ); if ( ! $nag ) { $failed = get_site_option( 'auto_core_update_failed' ); /* * If an update failed critically, we may have copied over version.php but not other files. * In that case, if the installation claims we're running the version we attempted, nag. * This is serious enough to err on the side of nagging. * * If we simply failed to update before we tried to copy any files, then assume things are * OK if they are now running the latest. * * This flag is cleared whenever a successful update occurs using Core_Upgrader. */ $comparison = ! empty( $failed['critical'] ) ? '>=' : '>'; if ( isset( $failed['attempted'] ) && version_compare( $failed['attempted'], $wp_version, $comparison ) ) { $nag = true; } } if ( ! $nag ) { return false; } if ( current_user_can( 'update_core' ) ) { $msg = sprintf( /* translators: %s: URL to WordPress Updates screen. */ __( 'An automated WordPress update has failed to complete - <a href="%s">please attempt the update again now</a>.' ), 'update-core.php' ); } else { $msg = __( 'An automated WordPress update has failed to complete! Please notify the site administrator.' ); } wp_admin_notice( $msg, array( 'type' => 'warning', 'additional_classes' => array( 'update-nag', 'inline' ), 'paragraph_wrap' => false, ) ); } /** * Prints the JavaScript templates for update admin notices. * * @since 4.6.0 * * Template takes one argument with four values: * * param {object} data { * Arguments for admin notice. * * @type string id ID of the notice. * @type string className Class names for the notice. * @type string message The notice's message. * @type string type The type of update the notice is for. Either 'plugin' or 'theme'. * } */ function wp_print_admin_notice_templates() { ?> <script id="tmpl-wp-updates-admin-notice" type="text/html"> <div <# if ( data.id ) { #>id="{{ data.id }}"<# } #> class="notice {{ data.className }}"><p>{{{ data.message }}}</p></div> </script> <script id="tmpl-wp-bulk-updates-admin-notice" type="text/html"> <div id="{{ data.id }}" class="{{ data.className }} notice <# if ( data.errors ) { #>notice-error<# } else { #>notice-success<# } #>"> <p> <# if ( data.successes ) { #> <# if ( 1 === data.successes ) { #> <# if ( 'plugin' === data.type ) { #> <?php /* translators: %s: Number of plugins. */ printf( __( '%s plugin successfully updated.' ), '{{ data.successes }}' ); ?> <# } else { #> <?php /* translators: %s: Number of themes. */ printf( __( '%s theme successfully updated.' ), '{{ data.successes }}' ); ?> <# } #> <# } else { #> <# if ( 'plugin' === data.type ) { #> <?php /* translators: %s: Number of plugins. */ printf( __( '%s plugins successfully updated.' ), '{{ data.successes }}' ); ?> <# } else { #> <?php /* translators: %s: Number of themes. */ printf( __( '%s themes successfully updated.' ), '{{ data.successes }}' ); ?> <# } #> <# } #> <# } #> <# if ( data.errors ) { #> <button class="button-link bulk-action-errors-collapsed" aria-expanded="false"> <# if ( 1 === data.errors ) { #> <?php /* translators: %s: Number of failed updates. */ printf( __( '%s update failed.' ), '{{ data.errors }}' ); ?> <# } else { #> <?php /* translators: %s: Number of failed updates. */ printf( __( '%s updates failed.' ), '{{ data.errors }}' ); ?> <# } #> <span class="screen-reader-text"> <?php /* translators: Hidden accessibility text. */ _e( 'Show more details' ); ?> </span> <span class="toggle-indicator" aria-hidden="true"></span> </button> <# } #> </p> <# if ( data.errors ) { #> <ul class="bulk-action-errors hidden"> <# _.each( data.errorMessages, function( errorMessage ) { #> <li>{{ errorMessage }}</li> <# } ); #> </ul> <# } #> </div> </script> <?php } /** * Prints the JavaScript templates for update and deletion rows in list tables. * * @since 4.6.0 * * The update template takes one argument with four values: * * param {object} data { * Arguments for the update row * * @type string slug Plugin slug. * @type string plugin Plugin base name. * @type string colspan The number of table columns this row spans. * @type string content The row content. * } * * The delete template takes one argument with four values: * * param {object} data { * Arguments for the update row * * @type string slug Plugin slug. * @type string plugin Plugin base name. * @type string name Plugin name. * @type string colspan The number of table columns this row spans. * } */ function wp_print_update_row_templates() { ?> <script id="tmpl-item-update-row" type="text/template"> <tr class="plugin-update-tr update" id="{{ data.slug }}-update" data-slug="{{ data.slug }}" <# if ( data.plugin ) { #>data-plugin="{{ data.plugin }}"<# } #>> <td colspan="{{ data.colspan }}" class="plugin-update colspanchange"> {{{ data.content }}} </td> </tr> </script> <script id="tmpl-item-deleted-row" type="text/template"> <tr class="plugin-deleted-tr inactive deleted" id="{{ data.slug }}-deleted" data-slug="{{ data.slug }}" <# if ( data.plugin ) { #>data-plugin="{{ data.plugin }}"<# } #>> <td colspan="{{ data.colspan }}" class="plugin-update colspanchange"> <# if ( data.plugin ) { #> <?php printf( /* translators: %s: Plugin name. */ _x( '%s was successfully deleted.', 'plugin' ), '<strong>{{{ data.name }}}</strong>' ); ?> <# } else { #> <?php printf( /* translators: %s: Theme name. */ _x( '%s was successfully deleted.', 'theme' ), '<strong>{{{ data.name }}}</strong>' ); ?> <# } #> </td> </tr> </script> <?php } /** * Displays a notice when the user is in recovery mode. * * @since 5.2.0 */ function wp_recovery_mode_nag() { if ( ! wp_is_recovery_mode() ) { return; } $url = wp_login_url(); $url = add_query_arg( 'action', WP_Recovery_Mode::EXIT_ACTION, $url ); $url = wp_nonce_url( $url, WP_Recovery_Mode::EXIT_ACTION ); $message = sprintf( /* translators: %s: Recovery Mode exit link. */ __( 'You are in recovery mode. This means there may be an error with a theme or plugin. To exit recovery mode, log out or use the Exit button. <a href="%s">Exit Recovery Mode</a>' ), esc_url( $url ) ); wp_admin_notice( $message, array( 'type' => 'info' ) ); } /** * Checks whether auto-updates are enabled. * * @since 5.5.0 * * @param string $type The type of update being checked: Either 'theme' or 'plugin'. * @return bool True if auto-updates are enabled for `$type`, false otherwise. */ function wp_is_auto_update_enabled_for_type( $type ) { if ( ! class_exists( 'WP_Automatic_Updater' ) ) { require_once ABSPATH . 'wp-admin/includes/class-wp-automatic-updater.php'; } $updater = new WP_Automatic_Updater(); $enabled = ! $updater->is_disabled(); switch ( $type ) { case 'plugin': /** * Filters whether plugins auto-update is enabled. * * @since 5.5.0 * * @param bool $enabled True if plugins auto-update is enabled, false otherwise. */ return apply_filters( 'plugins_auto_update_enabled', $enabled ); case 'theme': /** * Filters whether themes auto-update is enabled. * * @since 5.5.0 * * @param bool $enabled True if themes auto-update is enabled, false otherwise. */ return apply_filters( 'themes_auto_update_enabled', $enabled ); } return false; } /** * Checks whether auto-updates are forced for an item. * * @since 5.6.0 * * @param string $type The type of update being checked: Either 'theme' or 'plugin'. * @param bool|null $update Whether to update. The value of null is internally used * to detect whether nothing has hooked into this filter. * @param object $item The update offer. * @return bool True if auto-updates are forced for `$item`, false otherwise. */ function wp_is_auto_update_forced_for_item( $type, $update, $item ) { /** This filter is documented in wp-admin/includes/class-wp-automatic-updater.php */ return apply_filters( "auto_update_{$type}", $update, $item ); } /** * Determines the appropriate auto-update message to be displayed. * * @since 5.5.0 * * @return string The update message to be shown. */ function wp_get_auto_update_message() { $next_update_time = wp_next_scheduled( 'wp_version_check' ); // Check if the event exists. if ( false === $next_update_time ) { $message = __( 'Automatic update not scheduled. There may be a problem with WP-Cron.' ); } else { $time_to_next_update = human_time_diff( (int) $next_update_time ); // See if cron is overdue. $overdue = ( time() - $next_update_time ) > 0; if ( $overdue ) { $message = sprintf( /* translators: %s: Duration that WP-Cron has been overdue. */ __( 'Automatic update overdue by %s. There may be a problem with WP-Cron.' ), $time_to_next_update ); } else { $message = sprintf( /* translators: %s: Time until the next update. */ __( 'Automatic update scheduled in %s.' ), $time_to_next_update ); } } return $message; } images/stars.png 0000644 00000001634 15135536347 0007673 0 ustar 00 �PNG IHDR % �m�; cIDATx�ݔMLA�UH*�B�(����/H4�Q�ŏ�у1r�c�AP�F�|U ��!F��i�->Ū�BQ��閶�v��|�n����99�?����/�y�2[ `}��T:KU�v�X̓��V��t�l4h�!�@���6埡I��7��e ����mҠ!& !�T���AG>qvB�X�|=�]����|Y\(���^N�����ᡦ# �^g�Tr��3�rbk4q�F�T�0�����MT7W���Be��)YhN"�uvb�C���y(��~�\���|X�D_�ݕK� S�"P�˛X���g��y��<&=�P�3��T�.'��a7u"�C(�<v�V�Pg�)����=Hak2��.�A�B<��~�||� 4�ʉ�]�s�*�q�# ֞,t$�Ůe�\�� �Id4i�F �T!��+�XomC��:���u?}��A�3���S&*]� Ts/7,�*��o�8K4�n�� ��t��J�E�9Ҡ8Ǹ5�K���!�k.�c,C�S>9V��;� Ǥ�6�k-��j�����=�~�ne��x���B�Jײgm~z�Q��{�Oר�<AE-q��v�!���<o�5�#������U`�l��i3X���˗�Y]�N�*� �� �\( #XmSAC��F��.]k�J"��n��P���0X�V2�}^J��yY"ZJ���+�U�uQN��oݗ���:�3k ��Q��K{S�@� v���*�mB�.�Nh 7�J����g�����K[c����6��� =9�b��0���c�B��J�I���z� �D�)���˓}�D=(z�ш��tF��Ѿ ��/>������ IEND�B`� images/w-logo-white.png 0000644 00000012423 15135536347 0011057 0 ustar 00 �PNG IHDR � � �� �PLTE �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������yx �tRNS !"#$%&'(*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~���������������������������������������������������������������������������������������������������������������������������xD� �IDATx��]i@U��(���b�99�C��V*i���H�/�JeHE��|!>��W�6�R{6)8�ٳ��"�9��T�d�x��}�=��Ϲpo��{�^g�{��k�����u8��MML[�~����7oX�*-q�@ǭ�ڍI�-����BAnv7����uE�e+Zt�k�q@��d�7�3���S*`��Jp�4g�ן�^��_����&���1���� ��34l|Rn�y��/i]<�]��]Ԡ�D l!��b`Ԛ�T�]� =ʞ�b���v�PD�Q��d�����?e�S�#��}�61;j �S�~a�i�9��R:�J�9�TI瓛���q'��_� ��#B�.��89���ݽU��uL�s�s���N��Z���M�&suIT��Mw�җn�t� l�gT����%�E� �Wl)�N/��(���J{^�|oX���7�|��9.�yw铝@�eZ�U>��s�������> ���m�'ޛ� k���/�\_��j�(���h�v,��kl��iJ����ڄ^܈��@�%����U���X[x��YZ��G?vY�S\l�f�ʹ��X [ו�`m������*uhW0��k3�$�n���ӿ2��|���J[��v��b>ׯ.� ��Eӗ�zf�R�V�*m���S��0���M��(Sz�e����(m�f|����� �M���]qC�y8��7��51.�?�_6��|�T*ni���� s��;��J)�C�����S��]��������Zō�:��M��^�X����}���}ۊ�k���u '�?*no��9)���S|�MN���Z�#�x�쵳/gb�B��iU�G�Eb�6��&ӆ}��3��R<�ܩWҾM#��-���h�8F��<�^�2� @��e��ϕ�;���������i���X�Bt���� ��b��"�߰�Gly7��_�Й����k�4`�i�������l�����e���\� �;H��J��\t{����� �A6��W$BE�Rdj��<����l��ѭ� ћ��T���j$����'���G������J> ��1� ?�>�8}p��b�B�έ��%���ԁ�g���V��6� �����Z��⅕iK��L3l@�;���2w��+��{�?r\tܼE�˟W)�L�4��]����e��2��v>�kx�8�@o���)̴��Fݽ���M���m���>�`��N���Vݧ#k�ݒg��i�^�=��k��f�/g9Z�31�Mn��h�o���=,=�cz�찤��6<z�G"�E�?w�,�Bʸ>����'��R������U�k[�O>J4i#����KI�_�.Qmg@Ֆ-�B0D��JR���Ș��zɔ/h ���u�҈V�YKZ�i�,��8"a�6�����R���R��� �W�s>U��5��g��_�ã�s,Eo��d���4�<D&����}��TEF��V��5E��ԏ ��8���u���ߛІ�cX�j|��� ��~��DiQ�����Nw��~U_�T���(/�,�� �!���C��п�P{��`��p����@�ɲ<�2>-�e�8�Ő@Y��{�@�ph<)� �/�#��[mP6��T�~>B��o�[����� ���bFSz7��\YKae`V�O�A���)1�\ӼV��>�S?N�o`K?bp��>.FR�v��9�'�oMv��/S���| ��eww���M�3N��� &R;�P~��ck��Nh���� H�'$������+�j�kA�|_#_ 1�����«ƺk��~J��1���(�'�&�O 6"cp;D1PBp�6#��V��=J����~��͝�>�x�d�ZG�⸄b(�n:[0��r���l��A� 4\��.�(^��(����I���5��<�3�n9d��< 1x��`'e�^Ѯ��W�c"P���_\o�����+��>��I<�9�K6�W��o�XU�>���p 2�Q� !M�K4����8a��X�D&ǵf�p���j������m@N���s�<kM�GC/�{��E���[ Pa\�C�4���Ю�C�-ĞDm#D�Җ�$�R]���Kf5��Q6p ���Q�`eK��q������{�ȵ[��baԚ-z!�e> Q���K�� �E��A��&0�ڎV�`D�\�w���lRXy��X� �o/������V�=�NAz3V��'��~�jk8�n������}���[�~5�B�o�v|���A�Dݿ }��U��O3�nÎo'��Y�]u��D-)/d��!4^���&�AM��1�H�%Z�/Bh�����e��_ϣ�R�p�u�jS뚄��D�;b_X=�kSl���]��� q��E�Sw=v]���1[v�,�����D��3a@9'��!���5 ��FM^�q��d�3�6����$��D³��FM��3�o��-�`-�e��J$\�t���\oF�f�� V��?jˎ�Jة�W] ��(��f�RV�'|�d�-��s7?�e5Y��Ed�h�r;�ٳ�J�秇�&�i���5��v��ڳ�o�!�p0�A�e����1�'��;}�eӎ�&�0J+���j����+}���%{v|�@�(�B��L��?f�(�H�ƨֈW h��V JX3�6�l���gS�7q�[{ ��L(��KP�Qd��l� S�=��4�k��M%�;��Z�&qD���p ����P���� �돖�Im[x����At���-������+�+�d�`�̢���4��G ����K���_�BM���Hj� �Ba���K@��uN�pE�:�rڮ-k�_�BaL0��+'K�1$�:qe@A�L�@@.�8�R�`8�!��O�V�T��{��� �P0��'�z+~�U��è2?�G= ޠ�m&%`6� g/�_��s) (�W`LQ��s�� tJ �T�_�3�.�����CѾ���5p���� ��IK��I���TR�D3�x���d��Di)LbO' {:x!�Qf O(ڷߺ�M��Lo/J�aS�rx���O(�7��K��ũQLr� ���5� $<���#��y�Lَ�d�y���^�'��7qP����g���p��\�^�+2�'�n^�#�@���� LBÕۨ��$8�)��)��@�g�!�(`�Ѿ;n��c%KSD�$[~;���&c�0�@���n %@���S�$[6My8��sq���sP�o��|YM��)HS���u��8��k�њhУ�ńΙQ�7�*ߌ�R���>�$�5� ��V�#l�P=wKB�*�6�!D=x� X�����b����N*>l�����4~% (+@�y�ܤb0��Mv\�;���+f���0k+[F�ks�� �>*=��j�攈N w�vD'r��H�a�0���w�o�E��o�5�!�H�ϙNf����l� گ�*�$>�{Vp����e��<�fy�o2H�E^|� �z5��ny�8:9�R ��]"a���q�KB~t���>�Brx<B-;�_E�����ç%h���j�@���:�vg �I�;��&#��]�;Q�$��W9�v@������;��Rv� ��h�X��k��3��n:D/s��S�m!?�4��,s���B[�q�2�3 Fn�{͂�Â�ɡ�a��X"� �a:�r�q��,~ՂuUl� Ki4\���w��C��3#D��<��]-�!(F�M�?;~�%�l�/MQvv�L �����\r�j��{��d��cT����c�T�EPg�A3�����)Q���J�ձ �����seJ U��MVR��:Sy�K ��2eT%��������ԣ(�Y֊9d�1�T�v�3���܂���#����-��s�)Z�b�/����PB�.��ܗ���8�E���Q�.�~eO%�� ;���Ĺ���ê@b�؏.Ș�-oWtc�D���JLv O�x�+�pi�ƕO���(1鞪�v�t:Z�zt��g��'�y*��{�?��E:T��9�-s:�eN�|�XG�ڥv�~�b�-_��@�`����̞��LE�d�l�D��!f�K�g���=\r�Y|��ܤ�#BC��!�#�'��ߔ���ռ���c����d���O��u6*��9���vቲ?�H��^A@���&���������:�m���u��HJv IEND�B`� images/w-logo-blue.png 0000644 00000006051 15135536347 0010666 0 ustar 00 �PNG IHDR P P ��� �PLTE t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t� t�fJܱ �tRNS !"#$%&'()*+,-.012345689:;<=>?@ABCDEFGHIJKLMNOPQRSVWXYZ[\]^`abcdfghmopqstuwxyz{|}~������������������������������������������������������������������������������������������������������������������������ %IDAT���C���7���qG$�6Q�s>����Ŋ9�1�DI�3�Y�˵dk��l�t�Zc���SуQ.��fO6|8�iN%9��������q>���1hZ�ںŵ�[��t W�?su�l�q���ʲ�=J^�J �[R2�Oqi}Ɣ,Y�Ժ��÷�:,Ek���A�k�Rx���ˮl��9��E���K'CYtOڜc�k�qy��Q2�tr�q�Mj*�;����-kң��u��G��vc����˚Q����Vuq��܋�4�3������U>��h�ED�B�o��|�0`�NO%!�rL��^=����ڕ�E � q�em���2?q�O��"F��V?qEauCcq��j� ;� �@�eꦧӁ��j r�Z���C�A_��Y yt��G[ҁ�>u߁g�@�6�ʠ������mu7c����d�N ���m��1�5�C#�\1�?љ����3��Vݷ�d���$st�7�{}�H�V9��c�1P�-z싈� �T���� �T5eaT�-�F�<&�/�oF9� ��PT2�p��WA�l>RmSGNsl��G��4��/�8R��I��֑�ϰ[�=���O�}�W�k����Z�%ɵ�f-���n"�'Jv�'F�vu8�N�%�m {H(�NR�J)�q�*�B:l|�b^W�( c�R���~%��ո6��jq��Y/d�L����Es0��+� \��x�m�fj0[��zH)��ڮ$��0v��Q�Z?��9#�;q�U��1�&p���F��yi:�z�(��mS��8N�5*��9P"�WH)��Eɶc�F�$�2MU�����0J�%��J1�I��UJ�;�s\�Jq+Fn��-���xHKY����}�+�+qmQ���l�t��5�i(\+���8��1W�Α�3�8�V� �dy�n�(��G��?qw+a\��D�w��xƃJ� W���$�e%AZ�f�v'F�RD�1*�l(�=���Y� 0[�*\����?�$>�^Yʁf���4�Z�0T�jԡ��Yf�BG�b9���W� *�a7�{^��Y���/�V\�ԡ�$��\+��d����_�0\����!��$�L�V����)�e,��+@Ʃ1fȕ��e�� �c�&G�؋k��z(�1��r�X �1�XkY���Vc0��A�ǁݲ��B����q�1�����`̐c��1`�J�πI2�z0v�V�%���m��BrL�a�A�y�1�� ���� �K��e��7P#P�Y�k1����ߪ�ye�̀�r��LU@����2�ec�*�*��FfDqm9�k9fUd�F2��1�~%��mYՊ{�B�3�����^�*�a�mW\��6Y*1�Q��=9V����b� F���c�J����5��ȌH �d��:�� k4P-c ��BP+��j�sH�2p��z`Y������F�v)��v9�ŸGZ��v>