' . __( 'Create a New Site' ) . '';
}
if ( current_user_can( 'create_users' ) ) {
$actions['create-user'] = '' . __( 'Create a New User' ) . '';
}
$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 '
';
/* 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 "
';
}
/**
* 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 = '
';
$community_events_notice .= '';
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.
*/
?>
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 '
';
$notice .= '';
}
/**
* 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 ) {
$url = 'http://api.wordpress.org/core/browse-happy/1.1/';
$options = array(
'body' => array( 'useragent' => $_SERVER['HTTP_USER_AGENT'] ),
'user-agent' => 'WordPress/' . wp_get_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
);
}
?>
%2$s %3$s',
esc_url( wp_get_update_php_url() ),
__( 'Learn more about updating PHP' ),
/* translators: Hidden accessibility text. */
__( '(opens in a new tab)' )
);
?>
visit the Site Health screen to gather information about your site now.' ),
esc_url( admin_url( 'site-health.php' ) )
);
?>
1 ) : ?>
0 && false !== $get_issues ) : ?>
%1$d item on the Site Health screen.',
'Take a look at the %1$d items on the Site Health screen.',
$issues_total
),
$issues_total,
esc_url( admin_url( 'site-health.php' ) )
);
?>
false,
'test_type' => false,
);
$file = wp_handle_upload( $_FILES[ $form ], $overrides );
if ( isset( $file['error'] ) ) {
wp_die( $file['error'] );
}
if ( 'pluginzip' === $form || 'themezip' === $form ) {
if ( ! wp_zip_file_is_valid( $file['file'] ) ) {
wp_delete_file( $file['file'] );
if ( 'pluginzip' === $form ) {
$plugins_page = sprintf(
'%s',
self_admin_url( 'plugin-install.php' ),
__( 'Return to the Plugin Installer' )
);
wp_die( __( 'Incompatible Archive.' ) . ' ' . $plugins_page );
}
if ( 'themezip' === $form ) {
$themes_page = sprintf(
'%s',
self_admin_url( 'theme-install.php' ),
__( 'Return to the Theme Installer' )
);
wp_die( __( 'Incompatible Archive.' ) . ' ' . $themes_page );
}
}
}
$this->filename = $_FILES[ $form ]['name'];
$this->package = $file['file'];
// Construct the attachment array.
$attachment = array(
'post_title' => $this->filename,
'post_content' => $file['url'],
'post_mime_type' => $file['type'],
'guid' => $file['url'],
'context' => 'upgrader',
'post_status' => 'private',
);
// Save the data.
$this->id = wp_insert_attachment( $attachment, $file['file'] );
// Schedule a cleanup for 2 hours from now in case of failed installation.
wp_schedule_single_event( time() + 2 * HOUR_IN_SECONDS, 'upgrader_scheduled_cleanup', array( $this->id ) );
} elseif ( is_numeric( $_GET[ $urlholder ] ) ) {
// Numeric Package = previously uploaded file, see above.
$this->id = (int) $_GET[ $urlholder ];
$attachment = get_post( $this->id );
if ( empty( $attachment ) ) {
wp_die( __( 'Please select a file' ) );
}
$this->filename = $attachment->post_title;
$this->package = get_attached_file( $attachment->ID );
} else {
// Else, It's set to something, Back compat for plugins using the old (pre-3.3) File_Uploader handler.
$uploads = wp_upload_dir();
if ( ! ( $uploads && false === $uploads['error'] ) ) {
wp_die( $uploads['error'] );
}
$this->filename = sanitize_file_name( $_GET[ $urlholder ] );
$this->package = $uploads['basedir'] . '/' . $this->filename;
if ( ! str_starts_with( realpath( $this->package ), realpath( $uploads['basedir'] ) ) ) {
wp_die( __( 'Please select a file' ) );
}
}
}
/**
* Deletes the attachment/uploaded file.
*
* @since 3.2.2
*
* @return bool Whether the cleanup was successful.
*/
public function cleanup() {
if ( $this->id ) {
wp_delete_attachment( $this->id );
} elseif ( file_exists( $this->package ) ) {
return @unlink( $this->package );
}
return true;
}
}
class-wp-privacy-requests-table.php 0000644 00000034707 15233111622 0013426 0 ustar 00 '',
'email' => __( 'Requester' ),
'status' => __( 'Status' ),
'created_timestamp' => __( 'Requested' ),
'next_steps' => __( 'Next steps' ),
);
return $columns;
}
/**
* Normalizes the admin URL to the current page (by request_type).
*
* @since 5.3.0
*
* @return string URL to the current admin page.
*/
protected function get_admin_url() {
$pagenow = str_replace( '_', '-', $this->request_type );
if ( 'remove-personal-data' === $pagenow ) {
$pagenow = 'erase-personal-data';
}
return admin_url( $pagenow . '.php' );
}
/**
* Gets a list of sortable columns.
*
* @since 4.9.6
*
* @return array Default sortable columns.
*/
protected function get_sortable_columns() {
/*
* The initial sorting is by 'Requested' (post_date) and descending.
* With initial sorting, the first click on 'Requested' should be ascending.
* With 'Requester' sorting active, the next click on 'Requested' should be descending.
*/
$desc_first = isset( $_GET['orderby'] );
return array(
'email' => 'requester',
'created_timestamp' => array( 'requested', $desc_first ),
);
}
/**
* Returns the default primary column.
*
* @since 4.9.6
*
* @return string Default primary column name.
*/
protected function get_default_primary_column_name() {
return 'email';
}
/**
* Counts the number of requests for each status.
*
* @since 4.9.6
*
* @global wpdb $wpdb WordPress database abstraction object.
*
* @return object Number of posts for each status.
*/
protected function get_request_counts() {
global $wpdb;
$cache_key = $this->post_type . '-' . $this->request_type;
$counts = wp_cache_get( $cache_key, 'counts' );
if ( false !== $counts ) {
return $counts;
}
$results = (array) $wpdb->get_results(
$wpdb->prepare(
"SELECT post_status, COUNT( * ) AS num_posts
FROM {$wpdb->posts}
WHERE post_type = %s
AND post_name = %s
GROUP BY post_status",
$this->post_type,
$this->request_type
),
ARRAY_A
);
$counts = array_fill_keys( get_post_stati(), 0 );
foreach ( $results as $row ) {
$counts[ $row['post_status'] ] = $row['num_posts'];
}
$counts = (object) $counts;
wp_cache_set( $cache_key, $counts, 'counts' );
return $counts;
}
/**
* Gets an associative array ( id => link ) with the list of views available on this table.
*
* @since 4.9.6
*
* @return string[] An array of HTML links keyed by their view.
*/
protected function get_views() {
$current_status = isset( $_REQUEST['filter-status'] ) ? sanitize_text_field( $_REQUEST['filter-status'] ) : '';
$statuses = _wp_privacy_statuses();
$views = array();
$counts = $this->get_request_counts();
$total_requests = absint( array_sum( (array) $counts ) );
// Normalized admin URL.
$admin_url = $this->get_admin_url();
$status_label = sprintf(
/* translators: %s: Number of requests. */
_nx(
'All (%s)',
'All (%s)',
$total_requests,
'requests'
),
number_format_i18n( $total_requests )
);
$views['all'] = array(
'url' => esc_url( $admin_url ),
'label' => $status_label,
'current' => empty( $current_status ),
);
foreach ( $statuses as $status => $label ) {
$post_status = get_post_status_object( $status );
if ( ! $post_status ) {
continue;
}
$total_status_requests = absint( $counts->{$status} );
if ( ! $total_status_requests ) {
continue;
}
$status_label = sprintf(
translate_nooped_plural( $post_status->label_count, $total_status_requests ),
number_format_i18n( $total_status_requests )
);
$status_link = add_query_arg( 'filter-status', $status, $admin_url );
$views[ $status ] = array(
'url' => esc_url( $status_link ),
'label' => $status_label,
'current' => $status === $current_status,
);
}
return $this->get_views_links( $views );
}
/**
* Gets bulk actions.
*
* @since 4.9.6
*
* @return array Array of bulk action labels keyed by their action.
*/
protected function get_bulk_actions() {
return array(
'resend' => __( 'Resend confirmation requests' ),
'complete' => __( 'Mark requests as completed' ),
'delete' => __( 'Delete requests' ),
);
}
/**
* Process bulk actions.
*
* @since 4.9.6
* @since 5.6.0 Added support for the `complete` action.
*/
public function process_bulk_action() {
$action = $this->current_action();
$request_ids = isset( $_REQUEST['request_id'] ) ? wp_parse_id_list( wp_unslash( $_REQUEST['request_id'] ) ) : array();
if ( empty( $request_ids ) ) {
return;
}
$count = 0;
$failures = 0;
check_admin_referer( 'bulk-privacy_requests' );
switch ( $action ) {
case 'resend':
foreach ( $request_ids as $request_id ) {
$resend = _wp_privacy_resend_request( $request_id );
if ( $resend && ! is_wp_error( $resend ) ) {
++$count;
} else {
++$failures;
}
}
if ( $failures ) {
add_settings_error(
'bulk_action',
'bulk_action',
sprintf(
/* translators: %d: Number of requests. */
_n(
'%d confirmation request failed to resend.',
'%d confirmation requests failed to resend.',
$failures
),
$failures
),
'error'
);
}
if ( $count ) {
add_settings_error(
'bulk_action',
'bulk_action',
sprintf(
/* translators: %d: Number of requests. */
_n(
'%d confirmation request re-sent successfully.',
'%d confirmation requests re-sent successfully.',
$count
),
$count
),
'success'
);
}
break;
case 'complete':
foreach ( $request_ids as $request_id ) {
$result = _wp_privacy_completed_request( $request_id );
if ( $result && ! is_wp_error( $result ) ) {
++$count;
}
}
add_settings_error(
'bulk_action',
'bulk_action',
sprintf(
/* translators: %d: Number of requests. */
_n(
'%d request marked as complete.',
'%d requests marked as complete.',
$count
),
$count
),
'success'
);
break;
case 'delete':
foreach ( $request_ids as $request_id ) {
if ( wp_delete_post( $request_id, true ) ) {
++$count;
} else {
++$failures;
}
}
if ( $failures ) {
add_settings_error(
'bulk_action',
'bulk_action',
sprintf(
/* translators: %d: Number of requests. */
_n(
'%d request failed to delete.',
'%d requests failed to delete.',
$failures
),
$failures
),
'error'
);
}
if ( $count ) {
add_settings_error(
'bulk_action',
'bulk_action',
sprintf(
/* translators: %d: Number of requests. */
_n(
'%d request deleted successfully.',
'%d requests deleted successfully.',
$count
),
$count
),
'success'
);
}
break;
}
}
/**
* Prepares items to output.
*
* @since 4.9.6
* @since 5.1.0 Added support for column sorting.
*/
public function prepare_items() {
$this->items = array();
$posts_per_page = $this->get_items_per_page( $this->request_type . '_requests_per_page' );
$args = array(
'post_type' => $this->post_type,
'post_name__in' => array( $this->request_type ),
'posts_per_page' => $posts_per_page,
'offset' => isset( $_REQUEST['paged'] ) ? max( 0, absint( $_REQUEST['paged'] ) - 1 ) * $posts_per_page : 0,
'post_status' => 'any',
's' => isset( $_REQUEST['s'] ) ? sanitize_text_field( $_REQUEST['s'] ) : '',
);
$orderby_mapping = array(
'requester' => 'post_title',
'requested' => 'post_date',
);
if ( isset( $_REQUEST['orderby'] ) && isset( $orderby_mapping[ $_REQUEST['orderby'] ] ) ) {
$args['orderby'] = $orderby_mapping[ $_REQUEST['orderby'] ];
}
if ( isset( $_REQUEST['order'] ) && in_array( strtoupper( $_REQUEST['order'] ), array( 'ASC', 'DESC' ), true ) ) {
$args['order'] = strtoupper( $_REQUEST['order'] );
}
if ( ! empty( $_REQUEST['filter-status'] ) ) {
$filter_status = isset( $_REQUEST['filter-status'] ) ? sanitize_text_field( $_REQUEST['filter-status'] ) : '';
$args['post_status'] = $filter_status;
}
$requests_query = new WP_Query( $args );
$requests = $requests_query->posts;
foreach ( $requests as $request ) {
$this->items[] = wp_get_user_request( $request->ID );
}
$this->items = array_filter( $this->items );
$this->set_pagination_args(
array(
'total_items' => $requests_query->found_posts,
'per_page' => $posts_per_page,
)
);
}
/**
* Returns the markup for the Checkbox column.
*
* @since 4.9.6
*
* @param WP_User_Request $item Item being shown.
* @return string Checkbox column markup.
*/
public function column_cb( $item ) {
return sprintf(
'' .
'',
esc_attr( $item->ID ),
/* translators: Hidden accessibility text. %s: Email address. */
sprintf( __( 'Select %s' ), $item->email )
);
}
/**
* Status column.
*
* @since 4.9.6
*
* @param WP_User_Request $item Item being shown.
* @return string|void Status column markup. Returns a string if no status is found,
* otherwise it displays the markup.
*/
public function column_status( $item ) {
$status = get_post_status( $item->ID );
$status_object = get_post_status_object( $status );
if ( ! $status_object || empty( $status_object->label ) ) {
return '-';
}
$timestamp = false;
switch ( $status ) {
case 'request-confirmed':
$timestamp = $item->confirmed_timestamp;
break;
case 'request-completed':
$timestamp = $item->completed_timestamp;
break;
}
echo '';
echo esc_html( $status_object->label );
if ( $timestamp ) {
echo '' . $this->get_timestamp_as_date( $timestamp ) . '';
}
echo '';
}
/**
* Converts a timestamp for display.
*
* @since 4.9.6
*
* @param int $timestamp Event timestamp.
* @return string Human readable date.
*/
protected function get_timestamp_as_date( $timestamp ) {
if ( empty( $timestamp ) ) {
return '';
}
$time_diff = time() - $timestamp;
if ( $time_diff >= 0 && $time_diff < DAY_IN_SECONDS ) {
/* translators: %s: Human-readable time difference. */
return sprintf( __( '%s ago' ), human_time_diff( $timestamp ) );
}
return sprintf(
/* translators: 1: privacy request date format, 2: privacy request time format. */
__( '%1$s at %2$s' ),
/* translators: privacy request date format. See https://www.php.net/manual/en/datetime.format.php */
date_i18n( __( 'Y/m/d' ), $timestamp ),
/* translators: privacy request time format. See https://www.php.net/manual/en/datetime.format.php */
date_i18n( __( 'g:i a' ), $timestamp )
);
}
/**
* Handles the default column.
*
* @since 4.9.6
* @since 5.7.0 Added `manage_{$this->screen->id}_custom_column` action.
*
* @param WP_User_Request $item Item being shown.
* @param string $column_name Name of column being shown.
*/
public function column_default( $item, $column_name ) {
/**
* Fires for each custom column of a specific request type in the Privacy Requests list table.
*
* Custom columns are registered using the {@see 'manage_export-personal-data_columns'}
* and the {@see 'manage_erase-personal-data_columns'} filters.
*
* The dynamic portion of the hook name, `$this->screen->id`, refers to the ID given to the list table
* according to which screen it's displayed on.
*
* Possible hook names include:
*
* - `manage_export-personal-data_custom_column`
* - `manage_erase-personal-data_custom_column`
*
* @since 5.7.0
*
* @param string $column_name The name of the column to display.
* @param WP_User_Request $item The item being shown.
*/
do_action( "manage_{$this->screen->id}_custom_column", $column_name, $item );
}
/**
* Returns the markup for the Created timestamp column. Overridden by children.
*
* @since 5.7.0
*
* @param WP_User_Request $item Item being shown.
* @return string Human readable date.
*/
public function column_created_timestamp( $item ) {
return $this->get_timestamp_as_date( $item->created_timestamp );
}
/**
* Actions column. Overridden by children.
*
* @since 4.9.6
*
* @param WP_User_Request $item Item being shown.
* @return string Email column markup.
*/
public function column_email( $item ) {
return sprintf( '%2$s %3$s', esc_url( 'mailto:' . $item->email ), $item->email, $this->row_actions( array() ) );
}
/**
* Returns the markup for the next steps column. Overridden by children.
*
* @since 4.9.6
*
* @param WP_User_Request $item Item being shown.
*/
public function column_next_steps( $item ) {}
/**
* Generates content for a single row of the table,
*
* @since 4.9.6
*
* @param WP_User_Request $item The current item.
*/
public function single_row( $item ) {
$status = $item->status;
echo '
';
$this->single_row_columns( $item );
echo '
';
}
/**
* Embeds scripts used to perform actions. Overridden by children.
*
* @since 4.9.6
*/
public function embed_scripts() {}
}
class-wp-site-icon.php 0000644 00000014416 15233111622 0010700 0 ustar 00 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;
}
}
user.php 0000644 00000055754 15233111622 0006247 0 ustar 00 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', __( 'Error: Please enter a username.' ) );
}
/* checking that nickname has been typed */
if ( $update && empty( $user->nickname ) ) {
$errors->add( 'nickname', __( 'Error: 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', __( 'Error: Please enter a password.' ), array( 'form-field' => 'pass1' ) );
}
// Check for "\" in password.
if ( str_contains( wp_unslash( $pass1 ), '\\' ) ) {
$errors->add( 'pass', __( 'Error: 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', __( 'Error: 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', __( 'Error: 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', __( 'Error: 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', __( 'Error: Sorry, that username is not allowed.' ) );
}
// Checking email address.
if ( empty( $user->user_email ) ) {
$errors->add( 'empty_email', __( 'Error: Please enter an email address.' ), array( 'form-field' => 'email' ) );
} elseif ( ! is_email( $user->user_email ) ) {
$errors->add( 'invalid_email', __( 'Error: 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', __( 'Error: 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 SQL query string for the user's drafts query.
*
* @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(
'
%1$s %2$s
',
__( 'Notice:' ),
__( 'You are using the auto-generated password for your account. Would you like to change it?' )
);
$default_password_nag_message .= sprintf(
'
%2$s | ',
esc_url( get_edit_profile_url() . '#password' ),
__( 'Yes, take me to my profile page' )
);
$default_password_nag_message .= sprintf(
'%2$s
',
'?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() {
?>
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;
}
class-wp-theme-install-list-table.php 0000644 00000036357 15233111622 0013622 0 ustar 00 features = $_REQUEST['features'];
}
$paged = $this->get_pagenum();
$per_page = 36;
// These are the tabs which are shown on the page,
$tabs = array();
$tabs['dashboard'] = __( 'Search' );
if ( 'search' === $tab ) {
$tabs['search'] = __( 'Search Results' );
}
$tabs['upload'] = _x( 'Upload', 'noun' );
$tabs['featured'] = _x( 'Featured', 'themes' );
//$tabs['popular'] = _x( 'Popular', 'themes' );
$tabs['new'] = _x( 'Latest', 'themes' );
$tabs['updated'] = _x( 'Recently Updated', 'themes' );
$nonmenu_tabs = array( 'theme-information' ); // Valid actions to perform which do not have a Menu item.
/** This filter is documented in wp-admin/theme-install.php */
$tabs = apply_filters( 'install_themes_tabs', $tabs );
/**
* Filters tabs not associated with a menu item on the Install Themes screen.
*
* @since 2.8.0
*
* @param string[] $nonmenu_tabs The tabs that don't have a menu item on
* the Install Themes screen.
*/
$nonmenu_tabs = apply_filters( 'install_themes_nonmenu_tabs', $nonmenu_tabs );
// If a non-valid menu tab has been selected, And it's not a non-menu action.
if ( empty( $tab ) || ( ! isset( $tabs[ $tab ] ) && ! in_array( $tab, (array) $nonmenu_tabs, true ) ) ) {
$tab = key( $tabs );
}
$args = array(
'page' => $paged,
'per_page' => $per_page,
'fields' => $theme_field_defaults,
);
switch ( $tab ) {
case 'search':
$type = isset( $_REQUEST['type'] ) ? wp_unslash( $_REQUEST['type'] ) : 'term';
switch ( $type ) {
case 'tag':
$args['tag'] = array_map( 'sanitize_key', $search_terms );
break;
case 'term':
$args['search'] = $search_string;
break;
case 'author':
$args['author'] = $search_string;
break;
}
if ( ! empty( $this->features ) ) {
$args['tag'] = $this->features;
$_REQUEST['s'] = implode( ',', $this->features );
$_REQUEST['type'] = 'tag';
}
add_action( 'install_themes_table_header', 'install_theme_search_form', 10, 0 );
break;
case 'featured':
// case 'popular':
case 'new':
case 'updated':
$args['browse'] = $tab;
break;
default:
$args = false;
break;
}
/**
* Filters API request arguments for each Install Themes screen tab.
*
* The dynamic portion of the hook name, `$tab`, refers to the theme install
* tab.
*
* Possible hook names include:
*
* - `install_themes_table_api_args_dashboard`
* - `install_themes_table_api_args_featured`
* - `install_themes_table_api_args_new`
* - `install_themes_table_api_args_search`
* - `install_themes_table_api_args_updated`
* - `install_themes_table_api_args_upload`
*
* @since 3.7.0
*
* @param array|false $args Theme install API arguments.
*/
$args = apply_filters( "install_themes_table_api_args_{$tab}", $args );
if ( ! $args ) {
return;
}
$api = themes_api( 'query_themes', $args );
if ( is_wp_error( $api ) ) {
wp_die( '
\n";
wp_ob_end_flush_all();
flush();
}
/**
* @since 2.8.0
*
* @param string $content
* @return string[] Array of function names.
*/
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, '' );
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 administration 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';
}
?>
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 '\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 '';
}
/**
* 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 );
?>
$hash,
'newemail' => $value,
);
update_option( 'adminhash', $new_admin_email, false );
$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,
A site administrator (###USERNAME###) 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.4 in the future. Check if the current version is lower.
if ( version_compare( $version, '7.4', '<' ) ) {
$response['is_lower_than_future_minimum'] = true;
// Force showing of warnings.
$response['is_acceptable'] = false;
}
return $response;
}
import.php 0000644 00000014731 15233111622 0006571 0 ustar 00 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() {
$locale = get_user_locale();
$cache_key = 'popular_importers_' . md5( $locale . wp_get_wp_version() );
$popular_importers = get_site_transient( $cache_key );
if ( ! $popular_importers ) {
$url = add_query_arg(
array(
'locale' => $locale,
'version' => wp_get_wp_version(),
),
'http://api.wordpress.org/core/importers/1.1/'
);
$options = array( 'user-agent' => 'WordPress/' . wp_get_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',
),
);
}
schema.php 0000644 00000130731 15233111622 0006516 0 ustar 00 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),
KEY type_status_author (post_type,post_status,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) unsigned NOT NULL auto_increment,
site_id bigint(20) unsigned 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) unsigned 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) unsigned NOT NULL auto_increment,
email varchar(255) NOT NULL default '',
IP varchar(30) NOT NULL default '',
blog_id bigint(20) unsigned 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) unsigned 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) unsigned NOT NULL auto_increment,
site_id bigint(20) unsigned 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) unsigned 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' => '',
'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' => 'https://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,
// 6.9.0
'wp_notes_notify' => 1,
);
// 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 = 'off';
} else {
$autoload = 'on';
}
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() {
$wp_roles = wp_roles();
// Disable role updates to the database while populating roles.
$original_use_db = $wp_roles->use_db;
$wp_roles->use_db = false;
// 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();
// Save the updated roles to the database.
if ( $original_use_db ) {
update_option( $wp_roles->role_key, $wp_roles->roles, true );
}
// Restore original value for writing to database.
$wp_roles->use_db = $original_use_db;
}
/**
* 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;
/**
* Fires before a network is populated.
*
* @since 6.9.0
*
* @param int $network_id ID of network to populate.
* @param string $domain The domain name for the network.
* @param string $email Email address for the network administrator.
* @param string $site_name The name of the network.
* @param string $path The path to append to the network's domain name.
* @param bool $subdomain_install Whether the network is a subdomain installation or a subdirectory installation.
*/
do_action( 'before_populate_network', $network_id, $domain, $email, $site_name, $path, $subdomain_install );
$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,
)
);
// Remove the cron event since Recovery Mode is not used in Multisite.
if ( wp_next_scheduled( 'recovery_mode_clean_expired_keys' ) ) {
wp_clear_scheduled_hook( 'recovery_mode_clean_expired_keys' );
}
/*
* 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();
/**
* Fires after a network is created when converting a single site to multisite.
*
* @since 6.9.0
*
* @param int $network_id ID of network created.
* @param string $domain The domain name for the network.
* @param string $email Email address for the network administrator.
* @param string $site_name The name of the network.
* @param string $path The path to append to the network's domain name.
* @param bool $subdomain_install Whether the network is a subdomain installation or a subdirectory installation.
*/
do_action( 'after_upgrade_to_multisite', $network_id, $domain, $email, $site_name, $path, $subdomain_install );
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 = '
' . __( 'Warning! Wildcard DNS may not be configured correctly!' ) . '
';
$msg .= '
' . sprintf(
/* translators: %s: Host name. */
__( 'The installer attempted to contact a random hostname (%s) on your domain.' ),
'' . $hostname . ''
);
if ( ! empty( $errstr ) ) {
/* translators: %s: Error message. */
$msg .= ' ' . sprintf( __( 'This resulted in an error message: %s' ), '' . $errstr . '' );
}
$msg .= '
';
$msg .= '
' . 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.' ),
'*'
) . '
';
$msg .= '
' . __( '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.' ) . '
';
return new WP_Error( 'no_wildcard_dns', $msg );
}
}
/**
* Fires after a network is fully populated.
*
* @since 6.9.0
*
* @param int $network_id ID of network created.
* @param string $domain The domain name for the network.
* @param string $email Email address for the network administrator.
* @param string $site_name The name of the network.
* @param string $path The path to append to the network's domain name.
* @param bool $subdomain_install Whether the network is a subdomain installation or a subdirectory installation.
*/
do_action( 'after_populate_network', $network_id, $domain, $email, $site_name, $path, $subdomain_install );
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'
);
$allowed_file_types = array();
$all_mime_types = get_allowed_mime_types();
foreach ( $all_mime_types as $ext => $mime ) {
array_push( $allowed_file_types, ...explode( '|', $ext ) );
}
$upload_filetypes = array_unique( $allowed_file_types );
$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();
}
class-automatic-upgrader-skin.php 0000644 00000007117 15233111622 0013121 0 ustar 00 options['context'] = $context;
}
/*
* TODO: Fix up request_filesystem_credentials(), or split it, to allow us to request a no-output version.
* This will output a credentials form in event of failure. We don't want that, so just hide with a buffer.
*/
ob_start();
$result = parent::request_filesystem_credentials( $error, $context, $allow_relaxed_file_ownership );
ob_end_clean();
return $result;
}
/**
* Retrieves the upgrade messages.
*
* @since 3.7.0
*
* @return string[] Messages during an upgrade.
*/
public function get_upgrade_messages() {
return $this->messages;
}
/**
* Stores a message about the upgrade.
*
* @since 3.7.0
* @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 ) ) {
$string = $feedback->get_error_message();
} elseif ( is_array( $feedback ) ) {
return;
} else {
$string = $feedback;
}
if ( ! empty( $this->upgrader->strings[ $string ] ) ) {
$string = $this->upgrader->strings[ $string ];
}
if ( str_contains( $string, '%' ) ) {
if ( ! empty( $args ) ) {
$string = vsprintf( $string, $args );
}
}
$string = trim( $string );
// Only allow basic HTML in the messages, as it'll be used in emails/logs rather than direct browser output.
$string = wp_kses(
$string,
array(
'a' => array(
'href' => true,
),
'br' => true,
'em' => true,
'strong' => true,
)
);
if ( empty( $string ) ) {
return;
}
$this->messages[] = $string;
}
/**
* Creates a new output buffer.
*
* @since 3.7.0
*/
public function header() {
ob_start();
}
/**
* Retrieves the buffered content, deletes the buffer, and processes the output.
*
* @since 3.7.0
*/
public function footer() {
$output = ob_get_clean();
if ( ! empty( $output ) ) {
$this->feedback( $output );
}
}
}
screen.php 0000644 00000014352 15233111622 0006535 0 ustar 00 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 Global meta box state.
*
* @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(
'',
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();
}
class-wp-ms-sites-list-table.php 0000644 00000053165 15233111622 0012614 0 ustar 00 status_list = array(
'archived' => array( 'site-archived', __( 'Archived' ) ),
'spam' => array( 'site-spammed', _x( 'Spam', 'site' ) ),
'deleted' => array( 'site-deleted', __( 'Flagged for Deletion' ) ),
'mature' => array( 'site-mature', __( 'Mature' ) ),
);
parent::__construct(
array(
'plural' => 'sites',
'screen' => isset( $args['screen'] ) ? $args['screen'] : null,
)
);
}
/**
* @return bool
*/
public function ajax_user_can() {
return current_user_can( 'manage_sites' );
}
/**
* Prepares the list of sites for display.
*
* @since 3.1.0
*
* @global string $mode List table view mode.
* @global string $s
* @global wpdb $wpdb WordPress database abstraction object.
*/
public function prepare_items() {
global $mode, $s, $wpdb;
if ( ! empty( $_REQUEST['mode'] ) ) {
$mode = 'excerpt' === $_REQUEST['mode'] ? 'excerpt' : 'list';
set_user_setting( 'sites_list_mode', $mode );
} else {
$mode = get_user_setting( 'sites_list_mode', 'list' );
}
$per_page = $this->get_items_per_page( 'sites_network_per_page' );
$pagenum = $this->get_pagenum();
$s = isset( $_REQUEST['s'] ) ? wp_unslash( trim( $_REQUEST['s'] ) ) : '';
$wild = '';
if ( str_contains( $s, '*' ) ) {
$wild = '*';
$s = trim( $s, '*' );
}
/*
* If the network is large and a search is not being performed, show only
* the latest sites with no paging in order to avoid expensive count queries.
*/
if ( ! $s && wp_is_large_network() ) {
if ( ! isset( $_REQUEST['orderby'] ) ) {
$_GET['orderby'] = '';
$_REQUEST['orderby'] = '';
}
if ( ! isset( $_REQUEST['order'] ) ) {
$_GET['order'] = 'DESC';
$_REQUEST['order'] = 'DESC';
}
}
$args = array(
'number' => (int) $per_page,
'offset' => (int) ( ( $pagenum - 1 ) * $per_page ),
'network_id' => get_current_network_id(),
);
if ( empty( $s ) ) {
// Nothing to do.
} elseif ( preg_match( '/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/', $s )
|| preg_match( '/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.?$/', $s )
|| preg_match( '/^[0-9]{1,3}\.[0-9]{1,3}\.?$/', $s )
|| preg_match( '/^[0-9]{1,3}\.$/', $s )
) {
// IPv4 address.
$reg_blog_ids = $wpdb->get_col(
$wpdb->prepare(
"SELECT blog_id FROM {$wpdb->registration_log} WHERE {$wpdb->registration_log}.IP LIKE %s",
$wpdb->esc_like( $s ) . ( ! empty( $wild ) ? '%' : '' )
)
);
if ( $reg_blog_ids ) {
$args['site__in'] = $reg_blog_ids;
}
} elseif ( is_numeric( $s ) && empty( $wild ) ) {
$args['ID'] = $s;
} else {
$args['search'] = $s;
if ( ! is_subdomain_install() ) {
$args['search_columns'] = array( 'path' );
}
}
$order_by = isset( $_REQUEST['orderby'] ) ? $_REQUEST['orderby'] : '';
if ( 'registered' === $order_by ) {
// 'registered' is a valid field name.
} elseif ( 'lastupdated' === $order_by ) {
$order_by = 'last_updated';
} elseif ( 'blogname' === $order_by ) {
if ( is_subdomain_install() ) {
$order_by = 'domain';
} else {
$order_by = 'path';
}
} elseif ( 'blog_id' === $order_by ) {
$order_by = 'id';
} elseif ( ! $order_by ) {
$order_by = false;
}
$args['orderby'] = $order_by;
if ( $order_by ) {
$args['order'] = ( isset( $_REQUEST['order'] ) && 'DESC' === strtoupper( $_REQUEST['order'] ) ) ? 'DESC' : 'ASC';
}
if ( wp_is_large_network() ) {
$args['no_found_rows'] = true;
} else {
$args['no_found_rows'] = false;
}
// Take into account the role the user has selected.
$status = isset( $_REQUEST['status'] ) ? wp_unslash( trim( $_REQUEST['status'] ) ) : '';
if ( in_array( $status, array( 'public', 'archived', 'mature', 'spam', 'deleted' ), true ) ) {
$args[ $status ] = 1;
}
/**
* Filters the arguments for the site query in the sites list table.
*
* @since 4.6.0
*
* @param array $args An array of get_sites() arguments.
*/
$args = apply_filters( 'ms_sites_list_table_query_args', $args );
$_sites = get_sites( $args );
if ( is_array( $_sites ) ) {
update_site_cache( $_sites );
$this->items = array_slice( $_sites, 0, $per_page );
}
$total_sites = get_sites(
array_merge(
$args,
array(
'count' => true,
'offset' => 0,
'number' => 0,
)
)
);
$this->set_pagination_args(
array(
'total_items' => $total_sites,
'per_page' => $per_page,
)
);
}
/**
*/
public function no_items() {
_e( 'No sites found.' );
}
/**
* Gets links to filter sites by status.
*
* @since 5.3.0
*
* @return array
*/
protected function get_views() {
$counts = wp_count_sites();
$statuses = array(
/* translators: %s: Number of sites. */
'all' => _nx_noop(
'All (%s)',
'All (%s)',
'sites'
),
/* translators: %s: Number of sites. */
'public' => _n_noop(
'Public (%s)',
'Public (%s)'
),
/* translators: %s: Number of sites. */
'archived' => _n_noop(
'Archived (%s)',
'Archived (%s)'
),
/* translators: %s: Number of sites. */
'mature' => _n_noop(
'Mature (%s)',
'Mature (%s)'
),
/* translators: %s: Number of sites. */
'spam' => _nx_noop(
'Spam (%s)',
'Spam (%s)',
'sites'
),
/* translators: %s: Number of sites. */
'deleted' => _n_noop(
'Flagged for Deletion (%s)',
'Flagged for Deletion (%s)'
),
);
$view_links = array();
$requested_status = isset( $_REQUEST['status'] ) ? wp_unslash( trim( $_REQUEST['status'] ) ) : '';
$url = 'sites.php';
foreach ( $statuses as $status => $label_count ) {
if ( (int) $counts[ $status ] > 0 ) {
$label = sprintf(
translate_nooped_plural( $label_count, $counts[ $status ] ),
number_format_i18n( $counts[ $status ] )
);
$full_url = 'all' === $status ? $url : add_query_arg( 'status', $status, $url );
$view_links[ $status ] = array(
'url' => esc_url( $full_url ),
'label' => $label,
'current' => $requested_status === $status || ( '' === $requested_status && 'all' === $status ),
);
}
}
return $this->get_views_links( $view_links );
}
/**
* @return array
*/
protected function get_bulk_actions() {
$actions = array();
if ( current_user_can( 'delete_sites' ) ) {
$actions['delete'] = __( 'Delete' );
}
$actions['spam'] = _x( 'Mark as spam', 'site' );
$actions['notspam'] = _x( 'Not spam', 'site' );
return $actions;
}
/**
* @global string $mode List table view mode.
*
* @param string $which The location of the pagination nav markup: Either 'top' or 'bottom'.
*/
protected function pagination( $which ) {
global $mode;
parent::pagination( $which );
if ( 'top' === $which ) {
$this->view_switcher( $mode );
}
}
/**
* Displays extra controls between bulk actions and pagination.
*
* @since 5.3.0
*
* @param string $which The location of the extra table nav markup: Either 'top' or 'bottom'.
*/
protected function extra_tablenav( $which ) {
?>
'site-query-submit' ) );
}
}
?>
'',
'blogname' => __( 'URL' ),
'lastupdated' => __( 'Last Updated' ),
'registered' => _x( 'Registered', 'site' ),
'users' => __( 'Users' ),
);
if ( has_filter( 'wpmublogsaction' ) ) {
$sites_columns['plugins'] = __( 'Actions' );
}
/**
* Filters the displayed site columns in Sites list table.
*
* @since MU (3.0.0)
*
* @param string[] $sites_columns An array of displayed site columns. Default 'cb',
* 'blogname', 'lastupdated', 'registered', 'users'.
*/
return apply_filters( 'wpmu_blogs_columns', $sites_columns );
}
/**
* @return array
*/
protected function get_sortable_columns() {
if ( is_subdomain_install() ) {
$blogname_abbr = __( 'Domain' );
$blogname_orderby_text = __( 'Table ordered by Site Domain Name.' );
} else {
$blogname_abbr = __( 'Path' );
$blogname_orderby_text = __( 'Table ordered by Site Path.' );
}
return array(
'blogname' => array( 'blogname', false, $blogname_abbr, $blogname_orderby_text ),
'lastupdated' => array( 'lastupdated', true, __( 'Last Updated' ), __( 'Table ordered by Last Updated.' ) ),
'registered' => array( 'blog_id', true, _x( 'Registered', 'site' ), __( 'Table ordered by Site Registered Date.' ), 'desc' ),
);
}
/**
* Handles the checkbox column output.
*
* @since 4.3.0
* @since 5.9.0 Renamed `$blog` to `$item` to match parent class for PHP 8 named parameter support.
*
* @param array $item Current site.
*/
public function column_cb( $item ) {
// Restores the more descriptive, specific name for use within this method.
$blog = $item;
if ( ! is_main_site( $blog['blog_id'] ) ) :
$blogname = untrailingslashit( $blog['domain'] . $blog['path'] );
?>
%2$s',
esc_url( network_admin_url( 'site-info.php?id=' . $blog['blog_id'] ) ),
$blogname
);
$this->site_states( $blog );
?>
';
printf(
/* translators: 1: Site title, 2: Site tagline. */
__( '%1$s – %2$s' ),
get_option( 'blogname' ),
'' . get_option( 'blogdescription' ) . ''
);
echo '';
restore_current_blog();
}
}
/**
* Handles the lastupdated column output.
*
* @since 4.3.0
*
* @global string $mode List table view mode.
*
* @param array $blog Current site.
*/
public function column_lastupdated( $blog ) {
global $mode;
if ( 'list' === $mode ) {
$date = __( 'Y/m/d' );
} else {
$date = __( 'Y/m/d g:i:s a' );
}
if ( '0000-00-00 00:00:00' === $blog['last_updated'] ) {
_e( 'Never' );
} else {
echo mysql2date( $date, $blog['last_updated'] );
}
}
/**
* Handles the registered column output.
*
* @since 4.3.0
*
* @global string $mode List table view mode.
*
* @param array $blog Current site.
*/
public function column_registered( $blog ) {
global $mode;
if ( 'list' === $mode ) {
$date = __( 'Y/m/d' );
} else {
$date = __( 'Y/m/d g:i:s a' );
}
if ( '0000-00-00 00:00:00' === $blog['registered'] ) {
echo '—';
} else {
echo mysql2date( $date, $blog['registered'] );
}
}
/**
* Handles the users column output.
*
* @since 4.3.0
*
* @param array $blog Current site.
*/
public function column_users( $blog ) {
$user_count = wp_cache_get( $blog['blog_id'] . '_user_count', 'blog-details' );
if ( ! $user_count ) {
$blog_users = new WP_User_Query(
array(
'blog_id' => $blog['blog_id'],
'fields' => 'ID',
'number' => 1,
'count_total' => true,
)
);
$user_count = $blog_users->get_total();
wp_cache_set( $blog['blog_id'] . '_user_count', $user_count, 'blog-details', 12 * HOUR_IN_SECONDS );
}
printf(
'%2$s',
esc_url( network_admin_url( 'site-users.php?id=' . $blog['blog_id'] ) ),
number_format_i18n( $user_count )
);
}
/**
* Handles the plugins column output.
*
* @since 4.3.0
*
* @param array $blog Current site.
*/
public function column_plugins( $blog ) {
if ( has_filter( 'wpmublogsaction' ) ) {
/**
* Fires inside the auxiliary 'Actions' column of the Sites list table.
*
* By default this column is hidden unless something is hooked to the action.
*
* @since MU (3.0.0)
*
* @param int $blog_id The site ID.
*/
do_action( 'wpmublogsaction', $blog['blog_id'] );
}
}
/**
* Handles output for the default column.
*
* @since 4.3.0
* @since 5.9.0 Renamed `$blog` to `$item` to match parent class for PHP 8 named parameter support.
*
* @param array $item Current site.
* @param string $column_name Current column name.
*/
public function column_default( $item, $column_name ) {
// Restores the more descriptive, specific name for use within this method.
$blog = $item;
/**
* Fires for each registered custom column in the Sites list table.
*
* @since 3.1.0
*
* @param string $column_name The name of the column to display.
* @param int $blog_id The site ID.
*/
do_action( 'manage_sites_custom_column', $column_name, $blog['blog_id'] );
}
/**
* Generates the list table rows.
*
* @since 3.1.0
*/
public function display_rows() {
foreach ( $this->items as $blog ) {
$blog = $blog->to_array();
$class = '';
reset( $this->status_list );
foreach ( $this->status_list as $status => $col ) {
if ( '1' === $blog[ $status ] ) {
$class = " class='{$col[0]}'";
}
}
echo "
";
$this->single_row_columns( $blog );
echo '
';
}
}
/**
* Determines whether to output comma-separated site states.
*
* @since 5.3.0
*
* @param array $site
*/
protected function site_states( $site ) {
$site_states = array();
// $site is still an array, so get the object.
$_site = WP_Site::get_instance( $site['blog_id'] );
if ( is_main_site( $_site->id ) ) {
$site_states['main'] = __( 'Main' );
}
reset( $this->status_list );
$site_status = isset( $_REQUEST['status'] ) ? wp_unslash( trim( $_REQUEST['status'] ) ) : '';
foreach ( $this->status_list as $status => $col ) {
if ( '1' === $_site->{$status} && $site_status !== $status ) {
$site_states[ $col[0] ] = $col[1];
}
}
/**
* Filters the default site display states for items in the Sites list table.
*
* @since 5.3.0
*
* @param string[] $site_states An array of site states. Default 'Main',
* 'Archived', 'Mature', 'Spam', 'Flagged for Deletion'.
* @param WP_Site $site The current site object.
*/
$site_states = apply_filters( 'display_site_states', $site_states, $_site );
if ( ! empty( $site_states ) ) {
$state_count = count( $site_states );
$i = 0;
echo ' — ';
foreach ( $site_states as $state ) {
++$i;
$separator = ( $i < $state_count ) ? ', ' : '';
echo "{$state}{$separator}";
}
}
}
/**
* Gets the name of the default primary column.
*
* @since 4.3.0
*
* @return string Name of the default primary column, in this case, 'blogname'.
*/
protected function get_default_primary_column_name() {
return 'blogname';
}
/**
* Generates and displays row action links.
*
* @since 4.3.0
* @since 5.9.0 Renamed `$blog` to `$item` to match parent class for PHP 8 named parameter support.
*
* @param array $item Site being acted upon.
* @param string $column_name Current column name.
* @param string $primary Primary column name.
* @return string Row actions output for sites in Multisite, or an empty string
* if the current column is not the primary column.
*/
protected function handle_row_actions( $item, $column_name, $primary ) {
if ( $primary !== $column_name ) {
return '';
}
// Restores the more descriptive, specific name for use within this method.
$blog = $item;
$blogname = untrailingslashit( $blog['domain'] . $blog['path'] );
// Preordered.
$actions = array(
'edit' => '',
'backend' => '',
'activate' => '',
'deactivate' => '',
'archive' => '',
'unarchive' => '',
'spam' => '',
'unspam' => '',
'delete' => '',
'visit' => '',
);
$actions['edit'] = sprintf(
'%2$s',
esc_url( network_admin_url( 'site-info.php?id=' . $blog['blog_id'] ) ),
__( 'Edit' )
);
$actions['backend'] = sprintf(
'%2$s',
esc_url( get_admin_url( $blog['blog_id'] ) ),
__( 'Dashboard' )
);
if ( ! is_main_site( $blog['blog_id'] ) ) {
if ( '1' === $blog['deleted'] ) {
$actions['activate'] = sprintf(
'%2$s',
esc_url(
wp_nonce_url(
network_admin_url( 'sites.php?action=confirm&action2=activateblog&id=' . $blog['blog_id'] ),
'activateblog_' . $blog['blog_id']
)
),
_x( 'Remove Deletion Flag', 'site' )
);
} else {
$actions['deactivate'] = sprintf(
'%2$s',
esc_url(
wp_nonce_url(
network_admin_url( 'sites.php?action=confirm&action2=deactivateblog&id=' . $blog['blog_id'] ),
'deactivateblog_' . $blog['blog_id']
)
),
__( 'Flag for Deletion' )
);
}
if ( '1' === $blog['archived'] ) {
$actions['unarchive'] = sprintf(
'%2$s',
esc_url(
wp_nonce_url(
network_admin_url( 'sites.php?action=confirm&action2=unarchiveblog&id=' . $blog['blog_id'] ),
'unarchiveblog_' . $blog['blog_id']
)
),
__( 'Unarchive' )
);
} else {
$actions['archive'] = sprintf(
'%2$s',
esc_url(
wp_nonce_url(
network_admin_url( 'sites.php?action=confirm&action2=archiveblog&id=' . $blog['blog_id'] ),
'archiveblog_' . $blog['blog_id']
)
),
_x( 'Archive', 'verb; site' )
);
}
if ( '1' === $blog['spam'] ) {
$actions['unspam'] = sprintf(
'%2$s',
esc_url(
wp_nonce_url(
network_admin_url( 'sites.php?action=confirm&action2=unspamblog&id=' . $blog['blog_id'] ),
'unspamblog_' . $blog['blog_id']
)
),
_x( 'Not Spam', 'site' )
);
} else {
$actions['spam'] = sprintf(
'%2$s',
esc_url(
wp_nonce_url(
network_admin_url( 'sites.php?action=confirm&action2=spamblog&id=' . $blog['blog_id'] ),
'spamblog_' . $blog['blog_id']
)
),
_x( 'Spam', 'site' )
);
}
if ( current_user_can( 'delete_site', $blog['blog_id'] ) ) {
$actions['delete'] = sprintf(
'%2$s',
esc_url(
wp_nonce_url(
network_admin_url( 'sites.php?action=confirm&action2=deleteblog&id=' . $blog['blog_id'] ),
'deleteblog_' . $blog['blog_id']
)
),
__( 'Delete Permanently' )
);
}
}
$actions['visit'] = sprintf(
'%2$s',
esc_url( get_home_url( $blog['blog_id'], '/' ) ),
__( 'Visit' )
);
/**
* Filters the action links displayed for each site in the Sites list table.
*
* The 'Edit', 'Dashboard', 'Delete Permanently', and 'Visit' links are displayed by
* default for each site. The site's status determines whether to show the
* 'Remove Deletion Flag' or 'Flag for Deletion' link, 'Unarchive' or 'Archive' links, and
* 'Not Spam' or 'Spam' link for each site.
*
* @since 3.1.0
*
* @param string[] $actions An array of action links to be displayed.
* @param int $blog_id The site ID.
* @param string $blogname Site path, formatted depending on whether it is a sub-domain
* or subdirectory multisite installation.
*/
$actions = apply_filters( 'manage_sites_action_links', array_filter( $actions ), $blog['blog_id'], $blogname );
return $this->row_actions( $actions );
}
}
class-bulk-plugin-upgrader-skin.php 0000644 00000005036 15233111622 0013362 0 ustar 00 upgrader->strings['skin_before_update_header'] = __( 'Updating Plugin %1$s (%2$d/%3$d)' );
}
/**
* Performs an action before a bulk plugin update.
*
* @since 3.0.0
*
* @param string $title
*/
public function before( $title = '' ) {
parent::before( $this->plugin_info['Title'] );
}
/**
* Performs an action following a bulk plugin update.
*
* @since 3.0.0
*
* @param string $title
*/
public function after( $title = '' ) {
parent::after( $this->plugin_info['Title'] );
$this->decrement_update_count( 'plugin' );
}
/**
* Displays the footer following the bulk update process.
*
* @since 3.0.0
*/
public function bulk_footer() {
parent::bulk_footer();
$update_actions = array(
'plugins_page' => sprintf(
'%s',
self_admin_url( 'plugins.php' ),
__( 'Go to Plugins page' )
),
'updates_page' => sprintf(
'%s',
self_admin_url( 'update-core.php' ),
__( 'Go to WordPress Updates page' )
),
);
if ( ! current_user_can( 'activate_plugins' ) ) {
unset( $update_actions['plugins_page'] );
}
/**
* Filters the list of action links available following bulk plugin updates.
*
* @since 3.0.0
*
* @param string[] $update_actions Array of plugin action links.
* @param array $plugin_info Array of information for the last-updated plugin.
*/
$update_actions = apply_filters( 'update_bulk_plugins_complete_actions', $update_actions, $this->plugin_info );
if ( ! empty( $update_actions ) ) {
$this->feedback( implode( ' | ', (array) $update_actions ) );
}
}
}
class-wp-comments-list-table.php 0000644 00000100633 15233111622 0012666 0 ustar 00 'comments',
'singular' => 'comment',
'ajax' => true,
'screen' => isset( $args['screen'] ) ? $args['screen'] : null,
)
);
}
/**
* Adds avatars to comment author names.
*
* @since 3.1.0
*
* @param string $name Comment author name.
* @param int $comment_id Comment ID.
* @return string Avatar with the user name.
*/
public function floated_admin_avatar( $name, $comment_id ) {
$comment = get_comment( $comment_id );
$avatar = get_avatar( $comment, 32, 'mystery' );
return "$avatar $name";
}
/**
* @return bool
*/
public function ajax_user_can() {
return current_user_can( 'edit_posts' );
}
/**
* @global string $mode List table view mode.
* @global int $post_id
* @global string $comment_status
* @global string $comment_type
* @global string $search
*/
public function prepare_items() {
global $mode, $post_id, $comment_status, $comment_type, $search;
if ( ! empty( $_REQUEST['mode'] ) ) {
$mode = 'excerpt' === $_REQUEST['mode'] ? 'excerpt' : 'list';
set_user_setting( 'posts_list_mode', $mode );
} else {
$mode = get_user_setting( 'posts_list_mode', 'list' );
}
$comment_status = isset( $_REQUEST['comment_status'] ) ? $_REQUEST['comment_status'] : 'all';
if ( ! in_array( $comment_status, array( 'all', 'mine', 'moderated', 'approved', 'spam', 'trash' ), true ) ) {
$comment_status = 'all';
}
$comment_type = '';
if ( ! empty( $_REQUEST['comment_type'] ) && 'note' !== $_REQUEST['comment_type'] ) {
$comment_type = $_REQUEST['comment_type'];
}
$search = ( isset( $_REQUEST['s'] ) ) ? $_REQUEST['s'] : '';
$post_type = ( isset( $_REQUEST['post_type'] ) ) ? sanitize_key( $_REQUEST['post_type'] ) : '';
$user_id = ( isset( $_REQUEST['user_id'] ) ) ? $_REQUEST['user_id'] : '';
$orderby = ( isset( $_REQUEST['orderby'] ) ) ? $_REQUEST['orderby'] : '';
$order = ( isset( $_REQUEST['order'] ) ) ? $_REQUEST['order'] : '';
$comments_per_page = $this->get_per_page( $comment_status );
$doing_ajax = wp_doing_ajax();
if ( isset( $_REQUEST['number'] ) ) {
$number = (int) $_REQUEST['number'];
} else {
$number = $comments_per_page + min( 8, $comments_per_page ); // Grab a few extra.
}
$page = $this->get_pagenum();
if ( isset( $_REQUEST['start'] ) ) {
$start = $_REQUEST['start'];
} else {
$start = ( $page - 1 ) * $comments_per_page;
}
if ( $doing_ajax && isset( $_REQUEST['offset'] ) ) {
$start += $_REQUEST['offset'];
}
$status_map = array(
'mine' => '',
'moderated' => 'hold',
'approved' => 'approve',
'all' => '',
);
$args = array(
'status' => isset( $status_map[ $comment_status ] ) ? $status_map[ $comment_status ] : $comment_status,
'search' => $search,
'user_id' => $user_id,
'offset' => $start,
'number' => $number,
'post_id' => $post_id,
'type' => $comment_type,
'type__not_in' => array( 'note' ),
'orderby' => $orderby,
'order' => $order,
'post_type' => $post_type,
'update_comment_post_cache' => true,
);
/**
* Filters the arguments for the comment query in the comments list table.
*
* @since 5.1.0
*
* @param array $args An array of get_comments() arguments.
*/
$args = apply_filters( 'comments_list_table_query_args', $args );
$_comments = get_comments( $args );
if ( is_array( $_comments ) ) {
$this->items = array_slice( $_comments, 0, $comments_per_page );
$this->extra_items = array_slice( $_comments, $comments_per_page );
$_comment_post_ids = array_unique( wp_list_pluck( $_comments, 'comment_post_ID' ) );
$this->pending_count = get_pending_comments_num( $_comment_post_ids );
}
$total_comments = get_comments(
array_merge(
$args,
array(
'count' => true,
'offset' => 0,
'number' => 0,
'orderby' => 'none',
)
)
);
$this->set_pagination_args(
array(
'total_items' => $total_comments,
'per_page' => $comments_per_page,
)
);
}
/**
* @param string $comment_status
* @return int
*/
public function get_per_page( $comment_status = 'all' ) {
$comments_per_page = $this->get_items_per_page( 'edit_comments_per_page' );
/**
* Filters the number of comments listed per page in the comments list table.
*
* @since 2.6.0
*
* @param int $comments_per_page The number of comments to list per page.
* @param string $comment_status The comment status name. Default 'All'.
*/
return apply_filters( 'comments_per_page', $comments_per_page, $comment_status );
}
/**
* @global string $comment_status
*/
public function no_items() {
global $comment_status;
if ( 'moderated' === $comment_status ) {
_e( 'No comments awaiting moderation.' );
} elseif ( 'trash' === $comment_status ) {
_e( 'No comments found in Trash.' );
} else {
_e( 'No comments found.' );
}
}
/**
* @global int $post_id
* @global string $comment_status
* @global string $comment_type
*/
protected function get_views() {
global $post_id, $comment_status, $comment_type;
$status_links = array();
$num_comments = ( $post_id ) ? wp_count_comments( $post_id ) : wp_count_comments();
$statuses = array(
/* translators: %s: Number of comments. */
'all' => _nx_noop(
'All (%s)',
'All (%s)',
'comments'
), // Singular not used.
/* translators: %s: Number of comments. */
'mine' => _nx_noop(
'Mine (%s)',
'Mine (%s)',
'comments'
),
/* translators: %s: Number of comments. */
'moderated' => _nx_noop(
'Pending (%s)',
'Pending (%s)',
'comments'
),
/* translators: %s: Number of comments. */
'approved' => _nx_noop(
'Approved (%s)',
'Approved (%s)',
'comments'
),
/* translators: %s: Number of comments. */
'spam' => _nx_noop(
'Spam (%s)',
'Spam (%s)',
'comments'
),
/* translators: %s: Number of comments. */
'trash' => _nx_noop(
'Trash (%s)',
'Trash (%s)',
'comments'
),
);
if ( ! EMPTY_TRASH_DAYS ) {
unset( $statuses['trash'] );
}
$link = admin_url( 'edit-comments.php' );
if ( ! empty( $comment_type ) && 'all' !== $comment_type ) {
$link = add_query_arg( 'comment_type', $comment_type, $link );
}
foreach ( $statuses as $status => $label ) {
if ( 'mine' === $status ) {
$current_user_id = get_current_user_id();
$num_comments->mine = get_comments(
array(
'post_id' => $post_id ? $post_id : 0,
'user_id' => $current_user_id,
'count' => true,
'orderby' => 'none',
)
);
$link = add_query_arg( 'user_id', $current_user_id, $link );
} else {
$link = remove_query_arg( 'user_id', $link );
}
if ( ! isset( $num_comments->$status ) ) {
$num_comments->$status = 10;
}
$link = add_query_arg( 'comment_status', $status, $link );
if ( $post_id ) {
$link = add_query_arg( 'p', absint( $post_id ), $link );
}
/*
// I toyed with this, but decided against it. Leaving it in here in case anyone thinks it is a good idea. ~ Mark
if ( !empty( $_REQUEST['s'] ) )
$link = add_query_arg( 's', esc_attr( wp_unslash( $_REQUEST['s'] ) ), $link );
*/
$status_links[ $status ] = array(
'url' => esc_url( $link ),
'label' => sprintf(
translate_nooped_plural( $label, $num_comments->$status ),
sprintf(
'%s',
( 'moderated' === $status ) ? 'pending' : $status,
number_format_i18n( $num_comments->$status )
)
),
'current' => $status === $comment_status,
);
}
/**
* Filters the comment status links.
*
* @since 2.5.0
* @since 5.1.0 The 'Mine' link was added.
*
* @param string[] $status_links An associative array of fully-formed comment status links. Includes 'All', 'Mine',
* 'Pending', 'Approved', 'Spam', and 'Trash'.
*/
return apply_filters( 'comment_status_links', $this->get_views_links( $status_links ) );
}
/**
* @global string $comment_status
*
* @return array
*/
protected function get_bulk_actions() {
global $comment_status;
if ( ! current_user_can( 'moderate_comments' ) ) {
return array(); // Return an empty array if the user doesn't have permission
}
$actions = array();
if ( in_array( $comment_status, array( 'all', 'approved' ), true ) ) {
$actions['unapprove'] = __( 'Unapprove' );
}
if ( in_array( $comment_status, array( 'all', 'moderated' ), true ) ) {
$actions['approve'] = __( 'Approve' );
}
if ( in_array( $comment_status, array( 'all', 'moderated', 'approved', 'trash' ), true ) ) {
$actions['spam'] = _x( 'Mark as spam', 'comment' );
}
if ( 'trash' === $comment_status ) {
$actions['untrash'] = __( 'Restore' );
} elseif ( 'spam' === $comment_status ) {
$actions['unspam'] = _x( 'Not spam', 'comment' );
}
if ( in_array( $comment_status, array( 'trash', 'spam' ), true ) || ! EMPTY_TRASH_DAYS ) {
$actions['delete'] = __( 'Delete permanently' );
} else {
$actions['trash'] = __( 'Move to Trash' );
}
return $actions;
}
/**
* @global string $comment_status
* @global string $comment_type
*
* @param string $which
*/
protected function extra_tablenav( $which ) {
global $comment_status, $comment_type;
static $has_items;
if ( ! isset( $has_items ) ) {
$has_items = $this->has_items();
}
echo '
';
if ( 'top' === $which ) {
ob_start();
$this->comment_type_dropdown( $comment_type );
/**
* Fires just before the Filter submit button for comment types.
*
* @since 3.5.0
*/
do_action( 'restrict_manage_comments' );
$output = ob_get_clean();
if ( ! empty( $output ) && $this->has_items() ) {
echo $output;
submit_button( __( 'Filter' ), '', 'filter_action', false, array( 'id' => 'post-query-submit' ) );
}
}
if ( ( 'spam' === $comment_status || 'trash' === $comment_status ) && $has_items
&& current_user_can( 'moderate_comments' )
) {
wp_nonce_field( 'bulk-destroy', '_destroy_nonce' );
$title = ( 'spam' === $comment_status ) ? esc_attr__( 'Empty Spam' ) : esc_attr__( 'Empty Trash' );
submit_button( $title, 'apply', 'delete_all', false );
}
/**
* Fires after the Filter submit button for comment types.
*
* @since 2.5.0
* @since 5.6.0 The `$which` parameter was added.
*
* @param string $comment_status The comment status name. Default 'All'.
* @param string $which The location of the extra table nav markup: Either 'top' or 'bottom'.
*/
do_action( 'manage_comments_nav', $comment_status, $which );
echo '
';
}
/**
* @return string|false
*/
public function current_action() {
if ( isset( $_REQUEST['delete_all'] ) || isset( $_REQUEST['delete_all2'] ) ) {
return 'delete_all';
}
return parent::current_action();
}
/**
* @global int $post_id
*
* @return string[] Array of column titles keyed by their column name.
*/
public function get_columns() {
global $post_id;
$columns = array();
if ( $this->checkbox ) {
$columns['cb'] = '';
}
$columns['author'] = __( 'Author' );
$columns['comment'] = _x( 'Comment', 'column name' );
if ( ! $post_id ) {
/* translators: Column name or table row header. */
$columns['response'] = __( 'In response to' );
}
$columns['date'] = _x( 'Submitted on', 'column name' );
return $columns;
}
/**
* Displays a comment type drop-down for filtering on the Comments list table.
*
* @since 5.5.0
* @since 5.6.0 Renamed from `comment_status_dropdown()` to `comment_type_dropdown()`.
*
* @param string $comment_type The current comment type slug.
*/
protected function comment_type_dropdown( $comment_type ) {
/**
* Filters the comment types shown in the drop-down menu on the Comments list table.
*
* @since 2.7.0
*
* @param string[] $comment_types Array of comment type labels keyed by their name.
*/
$comment_types = apply_filters(
'admin_comment_types_dropdown',
array(
'comment' => __( 'Comments' ),
'pings' => __( 'Pings' ),
)
);
if ( $comment_types && is_array( $comment_types ) ) {
printf(
'',
/* translators: Hidden accessibility text. */
__( 'Filter by comment type' )
);
echo '';
}
}
/**
* @return array
*/
protected function get_sortable_columns() {
return array(
'author' => array( 'comment_author', false, __( 'Author' ), __( 'Table ordered by Comment Author.' ) ),
'response' => array( 'comment_post_ID', false, _x( 'In Response To', 'column name' ), __( 'Table ordered by Post Replied To.' ) ),
'date' => 'comment_date',
);
}
/**
* Gets the name of the default primary column.
*
* @since 4.3.0
*
* @return string Name of the default primary column, in this case, 'comment'.
*/
protected function get_default_primary_column_name() {
return 'comment';
}
/**
* Displays the comments table.
*
* Overrides the parent display() method to render extra comments.
*
* @since 3.1.0
*/
public function display() {
wp_nonce_field( 'fetch-list-' . get_class( $this ), '_ajax_fetch_list_nonce' );
static $has_items;
if ( ! isset( $has_items ) ) {
$has_items = $this->has_items();
if ( $has_items ) {
$this->display_tablenav( 'top' );
}
}
$this->screen->render_screen_reader_content( 'heading_list' );
?>
';
}
/**
* @since 5.9.0 Renamed `$comment` to `$item` to match parent class for PHP 8 named parameter support.
*
* @param WP_Comment $item The comment object.
* @param string $column_name The custom column's name.
*/
public function column_default( $item, $column_name ) {
// Restores the more descriptive, specific name for use within this method.
$comment = $item;
/**
* Fires when the default column output is displayed for a single row.
*
* @since 2.8.0
*
* @param string $column_name The custom column's name.
* @param string $comment_id The comment ID as a numeric string.
*/
do_action( 'manage_comments_custom_column', $column_name, $comment->comment_ID );
}
}
image.php 0000644 00000123360 15233111622 0006340 0 ustar 00 crop( $src_x, $src_y, $src_w, $src_h, $dst_w, $dst_h, $src_abs );
if ( is_wp_error( $src ) ) {
return $src;
}
if ( ! $dst_file ) {
$dst_file = str_replace( wp_basename( $src_file ), 'cropped-' . wp_basename( $src_file ), $src_file );
}
/*
* The directory containing the original file may no longer exist when
* using a replication plugin.
*/
wp_mkdir_p( dirname( $dst_file ) );
$dst_file = dirname( $dst_file ) . '/' . wp_unique_filename( dirname( $dst_file ), wp_basename( $dst_file ) );
$result = $editor->save( $dst_file );
if ( is_wp_error( $result ) ) {
return $result;
}
if ( ! empty( $result['path'] ) ) {
return $result['path'];
}
return $dst_file;
}
/**
* Compare the existing image sub-sizes (as saved in the attachment meta)
* to the currently registered image sub-sizes, and return the difference.
*
* Registered sub-sizes that are larger than the image are skipped.
*
* @since 5.3.0
*
* @param int $attachment_id The image attachment post ID.
* @return array[] Associative array of arrays of image sub-size information for
* missing image sizes, keyed by image size name.
*/
function wp_get_missing_image_subsizes( $attachment_id ) {
if ( ! wp_attachment_is_image( $attachment_id ) ) {
return array();
}
$registered_sizes = wp_get_registered_image_subsizes();
$image_meta = wp_get_attachment_metadata( $attachment_id );
// Meta error?
if ( empty( $image_meta ) ) {
return $registered_sizes;
}
// Use the originally uploaded image dimensions as full_width and full_height.
if ( ! empty( $image_meta['original_image'] ) ) {
$image_file = wp_get_original_image_path( $attachment_id );
$imagesize = wp_getimagesize( $image_file );
}
if ( ! empty( $imagesize ) ) {
$full_width = $imagesize[0];
$full_height = $imagesize[1];
} else {
$full_width = (int) $image_meta['width'];
$full_height = (int) $image_meta['height'];
}
$possible_sizes = array();
// Skip registered sizes that are too large for the uploaded image.
foreach ( $registered_sizes as $size_name => $size_data ) {
if ( image_resize_dimensions( $full_width, $full_height, $size_data['width'], $size_data['height'], $size_data['crop'] ) ) {
$possible_sizes[ $size_name ] = $size_data;
}
}
if ( empty( $image_meta['sizes'] ) ) {
$image_meta['sizes'] = array();
}
/*
* Remove sizes that already exist. Only checks for matching "size names".
* It is possible that the dimensions for a particular size name have changed.
* For example the user has changed the values on the Settings -> Media screen.
* However we keep the old sub-sizes with the previous dimensions
* as the image may have been used in an older post.
*/
$missing_sizes = array_diff_key( $possible_sizes, $image_meta['sizes'] );
/**
* Filters the array of missing image sub-sizes for an uploaded image.
*
* @since 5.3.0
*
* @param array[] $missing_sizes Associative array of arrays of image sub-size information for
* missing image sizes, keyed by image size name.
* @param array $image_meta The image meta data.
* @param int $attachment_id The image attachment post ID.
*/
return apply_filters( 'wp_get_missing_image_subsizes', $missing_sizes, $image_meta, $attachment_id );
}
/**
* If any of the currently registered image sub-sizes are missing,
* create them and update the image meta data.
*
* @since 5.3.0
*
* @param int $attachment_id The image attachment post ID.
* @return array|WP_Error The updated image meta data array or WP_Error object
* if both the image meta and the attached file are missing.
*/
function wp_update_image_subsizes( $attachment_id ) {
$image_meta = wp_get_attachment_metadata( $attachment_id );
$image_file = wp_get_original_image_path( $attachment_id );
if ( empty( $image_meta ) || ! is_array( $image_meta ) ) {
/*
* Previously failed upload?
* If there is an uploaded file, make all sub-sizes and generate all of the attachment meta.
*/
if ( ! empty( $image_file ) ) {
$image_meta = wp_create_image_subsizes( $image_file, $attachment_id );
} else {
return new WP_Error( 'invalid_attachment', __( 'The attached file cannot be found.' ) );
}
} else {
$missing_sizes = wp_get_missing_image_subsizes( $attachment_id );
if ( empty( $missing_sizes ) ) {
return $image_meta;
}
// This also updates the image meta.
$image_meta = _wp_make_subsizes( $missing_sizes, $image_file, $image_meta, $attachment_id );
}
/** This filter is documented in wp-admin/includes/image.php */
$image_meta = apply_filters( 'wp_generate_attachment_metadata', $image_meta, $attachment_id, 'update' );
// Save the updated metadata.
wp_update_attachment_metadata( $attachment_id, $image_meta );
return $image_meta;
}
/**
* Updates the attached file and image meta data when the original image was edited.
*
* @since 5.3.0
* @since 6.0.0 The `$filesize` value was added to the returned array.
* @access private
*
* @param array $saved_data The data returned from WP_Image_Editor after successfully saving an image.
* @param string $original_file Path to the original file.
* @param array $image_meta The image meta data.
* @param int $attachment_id The attachment post ID.
* @return array The updated image meta data.
*/
function _wp_image_meta_replace_original( $saved_data, $original_file, $image_meta, $attachment_id ) {
$new_file = $saved_data['path'];
// Update the attached file meta.
update_attached_file( $attachment_id, $new_file );
// Width and height of the new image.
$image_meta['width'] = $saved_data['width'];
$image_meta['height'] = $saved_data['height'];
// Make the file path relative to the upload dir.
$image_meta['file'] = _wp_relative_upload_path( $new_file );
// Add image file size.
$image_meta['filesize'] = wp_filesize( $new_file );
// Store the original image file name in image_meta.
$image_meta['original_image'] = wp_basename( $original_file );
return $image_meta;
}
/**
* Creates image sub-sizes, adds the new data to the image meta `sizes` array, and updates the image metadata.
*
* Intended for use after an image is uploaded. Saves/updates the image metadata after each
* sub-size is created. If there was an error, it is added to the returned image metadata array.
*
* @since 5.3.0
*
* @param string $file Full path to the image file.
* @param int $attachment_id Attachment ID to process.
* @return array The image attachment meta data.
*/
function wp_create_image_subsizes( $file, $attachment_id ) {
$imagesize = wp_getimagesize( $file );
if ( empty( $imagesize ) ) {
// File is not an image.
return array();
}
// Default image meta.
$image_meta = array(
'width' => $imagesize[0],
'height' => $imagesize[1],
'file' => _wp_relative_upload_path( $file ),
'filesize' => wp_filesize( $file ),
'sizes' => array(),
);
// Fetch additional metadata from EXIF/IPTC.
$exif_meta = wp_read_image_metadata( $file );
if ( $exif_meta ) {
$image_meta['image_meta'] = $exif_meta;
}
/**
* Filters the "BIG image" threshold value.
*
* If the original image width or height is above the threshold, it will be scaled down. The threshold is
* used as max width and max height. The scaled down image will be used as the largest available size, including
* the `_wp_attached_file` post meta value.
*
* Returning `false` from the filter callback will disable the scaling.
*
* @since 5.3.0
*
* @param int $threshold The threshold value in pixels. Default 2560.
* @param array $imagesize {
* Indexed array of the image width and height in pixels.
*
* @type int $0 The image width.
* @type int $1 The image height.
* }
* @param string $file Full path to the uploaded image file.
* @param int $attachment_id Attachment post ID.
*/
$threshold = (int) apply_filters( 'big_image_size_threshold', 2560, $imagesize, $file, $attachment_id );
/*
* If the original image's dimensions are over the threshold,
* scale the image and use it as the "full" size.
*/
$scale_down = false;
$convert = false;
if ( $threshold && ( $image_meta['width'] > $threshold || $image_meta['height'] > $threshold ) ) {
// The image will be converted if needed on saving.
$scale_down = true;
} else {
// The image may need to be converted regardless of its dimensions.
$output_format = wp_get_image_editor_output_format( $file, $imagesize['mime'] );
if (
is_array( $output_format ) &&
array_key_exists( $imagesize['mime'], $output_format ) &&
$output_format[ $imagesize['mime'] ] !== $imagesize['mime']
) {
$convert = true;
}
}
if ( $scale_down || $convert ) {
$editor = wp_get_image_editor( $file );
if ( is_wp_error( $editor ) ) {
// This image cannot be edited.
return $image_meta;
}
if ( $scale_down ) {
// Resize the image. This will also convet it if needed.
$resized = $editor->resize( $threshold, $threshold );
} elseif ( $convert ) {
// The image will be converted (if possible) when saved.
$resized = true;
}
$rotated = null;
// If there is EXIF data, rotate according to EXIF Orientation.
if ( ! is_wp_error( $resized ) && is_array( $exif_meta ) ) {
$resized = $editor->maybe_exif_rotate();
$rotated = $resized; // bool true or WP_Error
}
if ( ! is_wp_error( $resized ) ) {
/*
* Append "-scaled" to the image file name. It will look like "my_image-scaled.jpg".
* This doesn't affect the sub-sizes names as they are generated from the original image (for best quality).
*/
if ( $scale_down ) {
$saved = $editor->save( $editor->generate_filename( 'scaled' ) );
} elseif ( $convert ) {
// Pass an empty string to avoid adding a suffix to converted file names.
$saved = $editor->save( $editor->generate_filename( '' ) );
} else {
$saved = $editor->save();
}
if ( ! is_wp_error( $saved ) ) {
$image_meta = _wp_image_meta_replace_original( $saved, $file, $image_meta, $attachment_id );
// If the image was rotated update the stored EXIF data.
if ( true === $rotated && ! empty( $image_meta['image_meta']['orientation'] ) ) {
$image_meta['image_meta']['orientation'] = 1;
}
} else {
// TODO: Log errors.
}
} else {
// TODO: Log errors.
}
} elseif ( ! empty( $exif_meta['orientation'] ) && 1 !== (int) $exif_meta['orientation'] ) {
// Rotate the whole original image if there is EXIF data and "orientation" is not 1.
$editor = wp_get_image_editor( $file );
if ( is_wp_error( $editor ) ) {
// This image cannot be edited.
return $image_meta;
}
// Rotate the image.
$rotated = $editor->maybe_exif_rotate();
if ( true === $rotated ) {
// Append `-rotated` to the image file name.
$saved = $editor->save( $editor->generate_filename( 'rotated' ) );
if ( ! is_wp_error( $saved ) ) {
$image_meta = _wp_image_meta_replace_original( $saved, $file, $image_meta, $attachment_id );
// Update the stored EXIF data.
if ( ! empty( $image_meta['image_meta']['orientation'] ) ) {
$image_meta['image_meta']['orientation'] = 1;
}
} else {
// TODO: Log errors.
}
}
}
/*
* Initial save of the new metadata.
* At this point the file was uploaded and moved to the uploads directory
* but the image sub-sizes haven't been created yet and the `sizes` array is empty.
*/
wp_update_attachment_metadata( $attachment_id, $image_meta );
$new_sizes = wp_get_registered_image_subsizes();
/**
* Filters the image sizes automatically generated when uploading an image.
*
* @since 2.9.0
* @since 4.4.0 Added the `$image_meta` argument.
* @since 5.3.0 Added the `$attachment_id` argument.
*
* @param array $new_sizes Associative array of image sizes to be created.
* @param array $image_meta The image meta data: width, height, file, sizes, etc.
* @param int $attachment_id The attachment post ID for the image.
*/
$new_sizes = apply_filters( 'intermediate_image_sizes_advanced', $new_sizes, $image_meta, $attachment_id );
return _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id );
}
/**
* Low-level function to create image sub-sizes.
*
* Updates the image meta after each sub-size is created.
* Errors are stored in the returned image metadata array.
*
* @since 5.3.0
* @access private
*
* @param array $new_sizes Array defining what sizes to create.
* @param string $file Full path to the image file.
* @param array $image_meta The attachment meta data array.
* @param int $attachment_id Attachment ID to process.
* @return array The attachment meta data with updated `sizes` array. Includes an array of errors encountered while resizing.
*/
function _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id ) {
if ( empty( $image_meta ) || ! is_array( $image_meta ) ) {
// Not an image attachment.
return array();
}
// Check if any of the new sizes already exist.
if ( isset( $image_meta['sizes'] ) && is_array( $image_meta['sizes'] ) ) {
foreach ( $image_meta['sizes'] as $size_name => $size_meta ) {
/*
* Only checks "size name" so we don't override existing images even if the dimensions
* don't match the currently defined size with the same name.
* To change the behavior, unset changed/mismatched sizes in the `sizes` array in image meta.
*/
if ( array_key_exists( $size_name, $new_sizes ) ) {
unset( $new_sizes[ $size_name ] );
}
}
} else {
$image_meta['sizes'] = array();
}
if ( empty( $new_sizes ) ) {
// Nothing to do...
return $image_meta;
}
/*
* Sort the image sub-sizes in order of priority when creating them.
* This ensures there is an appropriate sub-size the user can access immediately
* even when there was an error and not all sub-sizes were created.
*/
$priority = array(
'medium' => null,
'large' => null,
'thumbnail' => null,
'medium_large' => null,
);
$new_sizes = array_filter( array_merge( $priority, $new_sizes ) );
$editor = wp_get_image_editor( $file );
if ( is_wp_error( $editor ) ) {
// The image cannot be edited.
return $image_meta;
}
// If stored EXIF data exists, rotate the source image before creating sub-sizes.
if ( ! empty( $image_meta['image_meta'] ) ) {
$rotated = $editor->maybe_exif_rotate();
if ( is_wp_error( $rotated ) ) {
// TODO: Log errors.
}
}
if ( method_exists( $editor, 'make_subsize' ) ) {
foreach ( $new_sizes as $new_size_name => $new_size_data ) {
$new_size_meta = $editor->make_subsize( $new_size_data );
if ( is_wp_error( $new_size_meta ) ) {
// TODO: Log errors.
} else {
// Save the size meta value.
$image_meta['sizes'][ $new_size_name ] = $new_size_meta;
wp_update_attachment_metadata( $attachment_id, $image_meta );
}
}
} else {
// Fall back to `$editor->multi_resize()`.
$created_sizes = $editor->multi_resize( $new_sizes );
if ( ! empty( $created_sizes ) ) {
$image_meta['sizes'] = array_merge( $image_meta['sizes'], $created_sizes );
wp_update_attachment_metadata( $attachment_id, $image_meta );
}
}
return $image_meta;
}
/**
* Copy parent attachment properties to newly cropped image.
*
* @since 6.5.0
*
* @param string $cropped Path to the cropped image file.
* @param int $parent_attachment_id Parent file Attachment ID.
* @param string $context Control calling the function.
* @return array Properties of attachment.
*/
function wp_copy_parent_attachment_properties( $cropped, $parent_attachment_id, $context = '' ) {
$parent = get_post( $parent_attachment_id );
$parent_url = wp_get_attachment_url( $parent->ID );
$parent_basename = wp_basename( $parent_url );
$url = str_replace( wp_basename( $parent_url ), wp_basename( $cropped ), $parent_url );
$size = wp_getimagesize( $cropped );
$image_type = $size ? $size['mime'] : 'image/jpeg';
$sanitized_post_title = sanitize_file_name( $parent->post_title );
$use_original_title = (
( '' !== trim( $parent->post_title ) ) &&
/*
* Check if the original image has a title other than the "filename" default,
* meaning the image had a title when originally uploaded or its title was edited.
*/
( $parent_basename !== $sanitized_post_title ) &&
( pathinfo( $parent_basename, PATHINFO_FILENAME ) !== $sanitized_post_title )
);
$use_original_description = ( '' !== trim( $parent->post_content ) );
$attachment = array(
'post_title' => $use_original_title ? $parent->post_title : wp_basename( $cropped ),
'post_content' => $use_original_description ? $parent->post_content : $url,
'post_mime_type' => $image_type,
'guid' => $url,
'context' => $context,
);
// Copy the image caption attribute (post_excerpt field) from the original image.
if ( '' !== trim( $parent->post_excerpt ) ) {
$attachment['post_excerpt'] = $parent->post_excerpt;
}
// Copy the image alt text attribute from the original image.
if ( '' !== trim( $parent->_wp_attachment_image_alt ) ) {
$attachment['meta_input'] = array(
'_wp_attachment_image_alt' => wp_slash( $parent->_wp_attachment_image_alt ),
);
}
$attachment['post_parent'] = $parent_attachment_id;
return $attachment;
}
/**
* Generates attachment meta data and create image sub-sizes for images.
*
* @since 2.1.0
* @since 6.0.0 The `$filesize` value was added to the returned array.
* @since 6.7.0 The 'image/heic' mime type is supported.
*
* @param int $attachment_id Attachment ID to process.
* @param string $file Filepath of the attached image.
* @return array Metadata for attachment.
*/
function wp_generate_attachment_metadata( $attachment_id, $file ) {
$attachment = get_post( $attachment_id );
$metadata = array();
$support = false;
$mime_type = get_post_mime_type( $attachment );
if ( 'image/heic' === $mime_type || ( preg_match( '!^image/!', $mime_type ) && file_is_displayable_image( $file ) ) ) {
// Make thumbnails and other intermediate sizes.
$metadata = wp_create_image_subsizes( $file, $attachment_id );
} elseif ( wp_attachment_is( 'video', $attachment ) ) {
$metadata = wp_read_video_metadata( $file );
$support = current_theme_supports( 'post-thumbnails', 'attachment:video' ) || post_type_supports( 'attachment:video', 'thumbnail' );
} elseif ( wp_attachment_is( 'audio', $attachment ) ) {
$metadata = wp_read_audio_metadata( $file );
$support = current_theme_supports( 'post-thumbnails', 'attachment:audio' ) || post_type_supports( 'attachment:audio', 'thumbnail' );
}
/*
* wp_read_video_metadata() and wp_read_audio_metadata() return `false`
* if the attachment does not exist in the local filesystem,
* so make sure to convert the value to an array.
*/
if ( ! is_array( $metadata ) ) {
$metadata = array();
}
if ( $support && ! empty( $metadata['image']['data'] ) ) {
// Check for existing cover.
$hash = md5( $metadata['image']['data'] );
$posts = get_posts(
array(
'fields' => 'ids',
'post_type' => 'attachment',
'post_mime_type' => $metadata['image']['mime'],
'post_status' => 'inherit',
'posts_per_page' => 1,
'meta_key' => '_cover_hash',
'meta_value' => $hash,
)
);
$exists = reset( $posts );
if ( ! empty( $exists ) ) {
update_post_meta( $attachment_id, '_thumbnail_id', $exists );
} else {
$ext = '.jpg';
switch ( $metadata['image']['mime'] ) {
case 'image/gif':
$ext = '.gif';
break;
case 'image/png':
$ext = '.png';
break;
case 'image/webp':
$ext = '.webp';
break;
}
$basename = str_replace( '.', '-', wp_basename( $file ) ) . '-image' . $ext;
$uploaded = wp_upload_bits( $basename, '', $metadata['image']['data'] );
if ( false === $uploaded['error'] ) {
$image_attachment = array(
'post_mime_type' => $metadata['image']['mime'],
'post_type' => 'attachment',
'post_content' => '',
);
/**
* Filters the parameters for the attachment thumbnail creation.
*
* @since 3.9.0
*
* @param array $image_attachment An array of parameters to create the thumbnail.
* @param array $metadata Current attachment metadata.
* @param array $uploaded {
* Information about the newly-uploaded file.
*
* @type string $file Filename of the newly-uploaded file.
* @type string $url URL of the uploaded file.
* @type string $type File type.
* }
*/
$image_attachment = apply_filters( 'attachment_thumbnail_args', $image_attachment, $metadata, $uploaded );
$sub_attachment_id = wp_insert_attachment( $image_attachment, $uploaded['file'] );
add_post_meta( $sub_attachment_id, '_cover_hash', $hash );
$attach_data = wp_generate_attachment_metadata( $sub_attachment_id, $uploaded['file'] );
wp_update_attachment_metadata( $sub_attachment_id, $attach_data );
update_post_meta( $attachment_id, '_thumbnail_id', $sub_attachment_id );
}
}
} elseif ( 'application/pdf' === $mime_type ) {
// Try to create image thumbnails for PDFs.
$fallback_sizes = array(
'thumbnail',
'medium',
'large',
);
/**
* Filters the image sizes generated for non-image mime types.
*
* @since 4.7.0
*
* @param string[] $fallback_sizes An array of image size names.
* @param array $metadata Current attachment metadata.
*/
$fallback_sizes = apply_filters( 'fallback_intermediate_image_sizes', $fallback_sizes, $metadata );
$registered_sizes = wp_get_registered_image_subsizes();
$merged_sizes = array_intersect_key( $registered_sizes, array_flip( $fallback_sizes ) );
// Force thumbnails to be soft crops.
if ( isset( $merged_sizes['thumbnail'] ) && is_array( $merged_sizes['thumbnail'] ) ) {
$merged_sizes['thumbnail']['crop'] = false;
}
// Only load PDFs in an image editor if we're processing sizes.
if ( ! empty( $merged_sizes ) ) {
$editor = wp_get_image_editor( $file );
if ( ! is_wp_error( $editor ) ) { // No support for this type of file.
/*
* PDFs may have the same file filename as JPEGs.
* Ensure the PDF preview image does not overwrite any JPEG images that already exist.
*/
$dirname = dirname( $file ) . '/';
$ext = '.' . pathinfo( $file, PATHINFO_EXTENSION );
$preview_file = $dirname . wp_unique_filename( $dirname, wp_basename( $file, $ext ) . '-pdf.jpg' );
$uploaded = $editor->save( $preview_file, 'image/jpeg' );
unset( $editor );
// Resize based on the full size image, rather than the source.
if ( ! is_wp_error( $uploaded ) ) {
$image_file = $uploaded['path'];
unset( $uploaded['path'] );
$metadata['sizes'] = array(
'full' => $uploaded,
);
// Save the meta data before any image post-processing errors could happen.
wp_update_attachment_metadata( $attachment_id, $metadata );
// Create sub-sizes saving the image meta after each.
$metadata = _wp_make_subsizes( $merged_sizes, $image_file, $metadata, $attachment_id );
}
}
}
}
// Remove the blob of binary data from the array.
unset( $metadata['image']['data'] );
// Capture file size for cases where it has not been captured yet, such as PDFs.
if ( ! isset( $metadata['filesize'] ) && file_exists( $file ) ) {
$metadata['filesize'] = wp_filesize( $file );
}
/**
* Filters the generated attachment meta data.
*
* @since 2.1.0
* @since 5.3.0 The `$context` parameter was added.
*
* @param array $metadata An array of attachment meta data.
* @param int $attachment_id Current attachment ID.
* @param string $context Additional context. Can be 'create' when metadata was initially created for new attachment
* or 'update' when the metadata was updated.
*/
return apply_filters( 'wp_generate_attachment_metadata', $metadata, $attachment_id, 'create' );
}
/**
* Converts a fraction string to a decimal.
*
* @since 2.5.0
*
* @param string $str Fraction string.
* @return int|float Returns calculated fraction or integer 0 on invalid input.
*/
function wp_exif_frac2dec( $str ) {
if ( ! is_scalar( $str ) || is_bool( $str ) ) {
return 0;
}
if ( ! is_string( $str ) ) {
return $str; // This can only be an integer or float, so this is fine.
}
// Fractions passed as a string must contain a single `/`.
if ( substr_count( $str, '/' ) !== 1 ) {
if ( is_numeric( $str ) ) {
return (float) $str;
}
return 0;
}
list( $numerator, $denominator ) = explode( '/', $str );
// Both the numerator and the denominator must be numbers.
if ( ! is_numeric( $numerator ) || ! is_numeric( $denominator ) ) {
return 0;
}
// The denominator must not be zero.
if ( 0 == $denominator ) { // phpcs:ignore Universal.Operators.StrictComparisons.LooseEqual -- Deliberate loose comparison.
return 0;
}
return $numerator / $denominator;
}
/**
* Converts the exif date format to a unix timestamp.
*
* @since 2.5.0
*
* @param string $str A date string expected to be in Exif format (Y:m:d H:i:s).
* @return int|false The unix timestamp, or false on failure.
*/
function wp_exif_date2ts( $str ) {
list( $date, $time ) = explode( ' ', trim( $str ) );
list( $y, $m, $d ) = explode( ':', $date );
return strtotime( "{$y}-{$m}-{$d} {$time}" );
}
/**
* Gets extended image metadata, exif or iptc as available.
*
* Retrieves the EXIF metadata aperture, credit, camera, caption, copyright, iso
* created_timestamp, focal_length, shutter_speed, and title.
*
* The IPTC metadata that is retrieved is APP13, credit, byline, created date
* and time, caption, copyright, and title. Also includes FNumber, Model,
* DateTimeDigitized, FocalLength, ISOSpeedRatings, and ExposureTime.
*
* @todo Try other exif libraries if available.
* @since 2.5.0
*
* @param string $file
* @return array|false Image metadata array on success, false on failure.
*/
function wp_read_image_metadata( $file ) {
if ( ! file_exists( $file ) ) {
return false;
}
$image_size = wp_getimagesize( $file );
if ( false === $image_size ) {
return false;
}
list( , , $image_type ) = $image_size;
/*
* EXIF contains a bunch of data we'll probably never need formatted in ways
* that are difficult to use. We'll normalize it and just extract the fields
* that are likely to be useful. Fractions and numbers are converted to
* floats, dates to unix timestamps, and everything else to strings.
*/
$meta = array(
'aperture' => 0,
'credit' => '',
'camera' => '',
'caption' => '',
'created_timestamp' => 0,
'copyright' => '',
'focal_length' => 0,
'iso' => 0,
'shutter_speed' => 0,
'title' => '',
'orientation' => 0,
'keywords' => array(),
);
$iptc = array();
$info = array();
/*
* Read IPTC first, since it might contain data not available in exif such
* as caption, description etc.
*/
if ( is_callable( 'iptcparse' ) ) {
wp_getimagesize( $file, $info );
if ( ! empty( $info['APP13'] ) ) {
// Don't silence errors when in debug mode, unless running unit tests.
if ( defined( 'WP_DEBUG' ) && WP_DEBUG
&& ! defined( 'WP_RUN_CORE_TESTS' )
) {
$iptc = iptcparse( $info['APP13'] );
} else {
// Silencing notice and warning is intentional. See https://core.trac.wordpress.org/ticket/42480
$iptc = @iptcparse( $info['APP13'] );
}
if ( ! is_array( $iptc ) ) {
$iptc = array();
}
// Headline, "A brief synopsis of the caption".
if ( ! empty( $iptc['2#105'][0] ) ) {
$meta['title'] = trim( $iptc['2#105'][0] );
/*
* Title, "Many use the Title field to store the filename of the image,
* though the field may be used in many ways".
*/
} elseif ( ! empty( $iptc['2#005'][0] ) ) {
$meta['title'] = trim( $iptc['2#005'][0] );
}
if ( ! empty( $iptc['2#120'][0] ) ) { // Description / legacy caption.
$caption = trim( $iptc['2#120'][0] );
mbstring_binary_safe_encoding();
$caption_length = strlen( $caption );
reset_mbstring_encoding();
if ( empty( $meta['title'] ) && $caption_length < 80 ) {
// Assume the title is stored in 2:120 if it's short.
$meta['title'] = $caption;
}
$meta['caption'] = $caption;
}
if ( ! empty( $iptc['2#110'][0] ) ) { // Credit.
$meta['credit'] = trim( $iptc['2#110'][0] );
} elseif ( ! empty( $iptc['2#080'][0] ) ) { // Creator / legacy byline.
$meta['credit'] = trim( $iptc['2#080'][0] );
}
if ( ! empty( $iptc['2#055'][0] ) && ! empty( $iptc['2#060'][0] ) ) { // Created date and time.
$meta['created_timestamp'] = strtotime( $iptc['2#055'][0] . ' ' . $iptc['2#060'][0] );
}
if ( ! empty( $iptc['2#116'][0] ) ) { // Copyright.
$meta['copyright'] = trim( $iptc['2#116'][0] );
}
if ( ! empty( $iptc['2#025'][0] ) ) { // Keywords array.
$meta['keywords'] = array_values( $iptc['2#025'] );
}
}
}
$exif = array();
/**
* Filters the image types to check for exif data.
*
* @since 2.5.0
*
* @param int[] $image_types Array of image types to check for exif data. Each value
* is usually one of the `IMAGETYPE_*` constants.
*/
$exif_image_types = apply_filters( 'wp_read_image_metadata_types', array( IMAGETYPE_JPEG, IMAGETYPE_TIFF_II, IMAGETYPE_TIFF_MM ) );
if ( is_callable( 'exif_read_data' ) && in_array( $image_type, $exif_image_types, true ) ) {
// Don't silence errors when in debug mode, unless running unit tests.
if ( defined( 'WP_DEBUG' ) && WP_DEBUG
&& ! defined( 'WP_RUN_CORE_TESTS' )
) {
$exif = exif_read_data( $file );
} else {
// Silencing notice and warning is intentional. See https://core.trac.wordpress.org/ticket/42480
$exif = @exif_read_data( $file );
}
if ( ! is_array( $exif ) ) {
$exif = array();
}
$exif_description = '';
$exif_usercomment = '';
if ( ! empty( $exif['ImageDescription'] ) ) {
$exif_description = trim( $exif['ImageDescription'] );
}
if ( ! empty( $exif['COMPUTED']['UserComment'] ) ) {
$exif_usercomment = trim( $exif['COMPUTED']['UserComment'] );
}
if ( $exif_description ) {
mbstring_binary_safe_encoding();
$description_length = strlen( $exif_description );
reset_mbstring_encoding();
if ( empty( $meta['title'] ) && $description_length < 80 ) {
// Assume the title is stored in ImageDescription.
$meta['title'] = $exif_description;
}
// If both user comments and description are present.
if ( empty( $meta['caption'] ) && $exif_usercomment ) {
if ( ! empty( $meta['title'] ) && $exif_description === $meta['title'] ) {
$caption = $exif_usercomment;
} else {
if ( $exif_description === $exif_usercomment ) {
$caption = $exif_description;
} else {
$caption = trim( $exif_description . ' ' . $exif_usercomment );
}
}
$meta['caption'] = $caption;
}
if ( empty( $meta['caption'] ) && $exif_usercomment ) {
$meta['caption'] = $exif_usercomment;
}
if ( empty( $meta['caption'] ) ) {
$meta['caption'] = $exif_description;
}
} elseif ( empty( $meta['caption'] ) && $exif_usercomment ) {
$meta['caption'] = $exif_usercomment;
$description_length = strlen( $exif_usercomment );
if ( empty( $meta['title'] ) && $description_length < 80 ) {
$meta['title'] = trim( $exif_usercomment );
}
} elseif ( empty( $meta['caption'] ) && ! empty( $exif['Comments'] ) ) {
$meta['caption'] = trim( $exif['Comments'] );
}
if ( empty( $meta['credit'] ) ) {
if ( ! empty( $exif['Artist'] ) ) {
$meta['credit'] = trim( $exif['Artist'] );
} elseif ( ! empty( $exif['Author'] ) ) {
$meta['credit'] = trim( $exif['Author'] );
}
}
if ( empty( $meta['copyright'] ) && ! empty( $exif['Copyright'] ) ) {
$meta['copyright'] = trim( $exif['Copyright'] );
}
if ( ! empty( $exif['FNumber'] ) && is_scalar( $exif['FNumber'] ) ) {
$meta['aperture'] = round( wp_exif_frac2dec( $exif['FNumber'] ), 2 );
}
if ( ! empty( $exif['Model'] ) ) {
$meta['camera'] = trim( $exif['Model'] );
}
if ( empty( $meta['created_timestamp'] ) && ! empty( $exif['DateTimeDigitized'] ) ) {
$meta['created_timestamp'] = wp_exif_date2ts( $exif['DateTimeDigitized'] );
}
if ( ! empty( $exif['FocalLength'] ) ) {
$meta['focal_length'] = (string) $exif['FocalLength'];
if ( is_scalar( $exif['FocalLength'] ) ) {
$meta['focal_length'] = (string) wp_exif_frac2dec( $exif['FocalLength'] );
}
}
if ( ! empty( $exif['ISOSpeedRatings'] ) ) {
$meta['iso'] = is_array( $exif['ISOSpeedRatings'] ) ? reset( $exif['ISOSpeedRatings'] ) : $exif['ISOSpeedRatings'];
$meta['iso'] = trim( $meta['iso'] );
}
if ( ! empty( $exif['ExposureTime'] ) ) {
$meta['shutter_speed'] = (string) $exif['ExposureTime'];
if ( is_scalar( $exif['ExposureTime'] ) ) {
$meta['shutter_speed'] = (string) wp_exif_frac2dec( $exif['ExposureTime'] );
}
}
if ( ! empty( $exif['Orientation'] ) ) {
$meta['orientation'] = $exif['Orientation'];
}
}
foreach ( array( 'title', 'caption', 'credit', 'copyright', 'camera', 'iso' ) as $key ) {
if ( $meta[ $key ] && ! wp_is_valid_utf8( $meta[ $key ] ) ) {
$meta[ $key ] = utf8_encode( $meta[ $key ] );
}
}
foreach ( $meta['keywords'] as $key => $keyword ) {
if ( ! wp_is_valid_utf8( $keyword ) ) {
$meta['keywords'][ $key ] = utf8_encode( $keyword );
}
}
$meta = wp_kses_post_deep( $meta );
/**
* Filters the array of meta data read from an image's exif data.
*
* @since 2.5.0
* @since 4.4.0 The `$iptc` parameter was added.
* @since 5.0.0 The `$exif` parameter was added.
*
* @param array $meta Image meta data.
* @param string $file Path to image file.
* @param int $image_type Type of image, one of the `IMAGETYPE_XXX` constants.
* @param array $iptc IPTC data.
* @param array $exif EXIF data.
*/
return apply_filters( 'wp_read_image_metadata', $meta, $file, $image_type, $iptc, $exif );
}
/**
* Validates that file is an image.
*
* @since 2.5.0
*
* @param string $path File path to test if valid image.
* @return bool True if valid image, false if not valid image.
*/
function file_is_valid_image( $path ) {
$size = wp_getimagesize( $path );
return ! empty( $size );
}
/**
* Validates that file is suitable for displaying within a web page.
*
* @since 2.5.0
*
* @param string $path File path to test.
* @return bool True if suitable, false if not suitable.
*/
function file_is_displayable_image( $path ) {
$displayable_image_types = array( IMAGETYPE_GIF, IMAGETYPE_JPEG, IMAGETYPE_PNG, IMAGETYPE_BMP, IMAGETYPE_ICO, IMAGETYPE_WEBP, IMAGETYPE_AVIF );
$info = wp_getimagesize( $path );
if ( empty( $info ) ) {
$result = false;
} elseif ( ! in_array( $info[2], $displayable_image_types, true ) ) {
$result = false;
} else {
$result = true;
}
/**
* Filters whether the current image is displayable in the browser.
*
* @since 2.5.0
*
* @param bool $result Whether the image can be displayed. Default true.
* @param string $path Path to the image.
*/
return apply_filters( 'file_is_displayable_image', $result, $path );
}
/**
* Loads an image resource for editing.
*
* @since 2.9.0
*
* @param int $attachment_id Attachment ID.
* @param string $mime_type Image mime type.
* @param string|int[] $size Optional. Image size. Accepts any registered image size name, or an array
* of width and height values in pixels (in that order). Default 'full'.
* @return resource|GdImage|false The resulting image resource or GdImage instance on success,
* false on failure.
*/
function load_image_to_edit( $attachment_id, $mime_type, $size = 'full' ) {
$filepath = _load_image_to_edit_path( $attachment_id, $size );
if ( empty( $filepath ) ) {
return false;
}
switch ( $mime_type ) {
case 'image/jpeg':
$image = imagecreatefromjpeg( $filepath );
break;
case 'image/png':
$image = imagecreatefrompng( $filepath );
break;
case 'image/gif':
$image = imagecreatefromgif( $filepath );
break;
case 'image/webp':
$image = false;
if ( function_exists( 'imagecreatefromwebp' ) ) {
$image = imagecreatefromwebp( $filepath );
}
break;
default:
$image = false;
break;
}
if ( is_gd_image( $image ) ) {
/**
* Filters the current image being loaded for editing.
*
* @since 2.9.0
*
* @param resource|GdImage $image Current image.
* @param int $attachment_id Attachment ID.
* @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).
*/
$image = apply_filters( 'load_image_to_edit', $image, $attachment_id, $size );
if ( function_exists( 'imagealphablending' ) && function_exists( 'imagesavealpha' ) ) {
imagealphablending( $image, false );
imagesavealpha( $image, true );
}
}
return $image;
}
/**
* Retrieves the path or URL of an attachment's attached file.
*
* If the attached file is not present on the local filesystem (usually due to replication plugins),
* then the URL of the file is returned if `allow_url_fopen` is supported.
*
* @since 3.4.0
* @access private
*
* @param int $attachment_id Attachment ID.
* @param string|int[] $size Optional. Image size. Accepts any registered image size name, or an array
* of width and height values in pixels (in that order). Default 'full'.
* @return string|false File path or URL on success, false on failure.
*/
function _load_image_to_edit_path( $attachment_id, $size = 'full' ) {
$filepath = get_attached_file( $attachment_id );
if ( $filepath && file_exists( $filepath ) ) {
if ( 'full' !== $size ) {
$data = image_get_intermediate_size( $attachment_id, $size );
if ( $data ) {
$filepath = path_join( dirname( $filepath ), $data['file'] );
/**
* Filters the path to an attachment's file when editing the image.
*
* The filter is evaluated for all image sizes except 'full'.
*
* @since 3.1.0
*
* @param string $path Path to the current image.
* @param int $attachment_id Attachment ID.
* @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).
*/
$filepath = apply_filters( 'load_image_to_edit_filesystempath', $filepath, $attachment_id, $size );
}
}
} elseif ( function_exists( 'fopen' ) && ini_get( 'allow_url_fopen' ) ) {
/**
* Filters the path to an attachment's URL when editing the image.
*
* The filter is only evaluated if the file isn't stored locally and `allow_url_fopen` is enabled on the server.
*
* @since 3.1.0
*
* @param string|false $image_url Current image URL.
* @param int $attachment_id Attachment ID.
* @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).
*/
$filepath = apply_filters( 'load_image_to_edit_attachmenturl', wp_get_attachment_url( $attachment_id ), $attachment_id, $size );
}
/**
* Filters the returned path or URL of the current image.
*
* @since 2.9.0
*
* @param string|false $filepath File path or URL to current image, or false.
* @param int $attachment_id Attachment ID.
* @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).
*/
return apply_filters( 'load_image_to_edit_path', $filepath, $attachment_id, $size );
}
/**
* Copies an existing image file.
*
* @since 3.4.0
* @access private
*
* @param int $attachment_id Attachment ID.
* @return string|false New file path on success, false on failure.
*/
function _copy_image_file( $attachment_id ) {
$dst_file = get_attached_file( $attachment_id );
$src_file = $dst_file;
if ( ! file_exists( $src_file ) ) {
$src_file = _load_image_to_edit_path( $attachment_id );
}
if ( $src_file ) {
$dst_file = str_replace( wp_basename( $dst_file ), 'copy-' . wp_basename( $dst_file ), $dst_file );
$dst_file = dirname( $dst_file ) . '/' . wp_unique_filename( dirname( $dst_file ), wp_basename( $dst_file ) );
/*
* The directory containing the original file may no longer
* exist when using a replication plugin.
*/
wp_mkdir_p( dirname( $dst_file ) );
if ( ! copy( $src_file, $dst_file ) ) {
$dst_file = false;
}
} else {
$dst_file = false;
}
return $dst_file;
}
class-wp-privacy-data-removal-requests-list-table.php 0000644 00000013123 15233111622 0016736 0 ustar 00 status;
$request_id = $item->ID;
$row_actions = array();
if ( 'request-confirmed' !== $status ) {
/** This filter is documented in wp-admin/includes/ajax-actions.php */
$erasers = apply_filters( 'wp_privacy_personal_data_erasers', array() );
$erasers_count = count( $erasers );
$nonce = wp_create_nonce( 'wp-privacy-erase-personal-data-' . $request_id );
$remove_data_markup = '';
$remove_data_markup .= '' .
'' . __( 'Erasing data...' ) . ' ' .
'' . __( 'Erasure completed.' ) . '' .
'' . __( 'Force erasure has failed.' ) . ' ';
$remove_data_markup .= '';
$row_actions['remove-data'] = $remove_data_markup;
}
if ( 'request-completed' !== $status ) {
$complete_request_markup = '';
$complete_request_markup .= sprintf(
'%s',
esc_url(
wp_nonce_url(
add_query_arg(
array(
'action' => 'complete',
'request_id' => array( $request_id ),
),
admin_url( 'erase-personal-data.php' )
),
'bulk-privacy_requests'
)
),
esc_attr(
sprintf(
/* translators: %s: Request email. */
__( 'Mark export request for “%s” as completed.' ),
$item->email
)
),
__( 'Complete request' )
);
$complete_request_markup .= '';
}
if ( ! empty( $complete_request_markup ) ) {
$row_actions['complete-request'] = $complete_request_markup;
}
return sprintf( '%2$s %3$s', esc_url( 'mailto:' . $item->email ), $item->email, $this->row_actions( $row_actions ) );
}
/**
* Outputs the Next steps column.
*
* @since 4.9.6
*
* @param WP_User_Request $item Item being shown.
*/
public function column_next_steps( $item ) {
$status = $item->status;
switch ( $status ) {
case 'request-pending':
esc_html_e( 'Waiting for confirmation' );
break;
case 'request-confirmed':
/** This filter is documented in wp-admin/includes/ajax-actions.php */
$erasers = apply_filters( 'wp_privacy_personal_data_erasers', array() );
$erasers_count = count( $erasers );
$request_id = $item->ID;
$nonce = wp_create_nonce( 'wp-privacy-erase-personal-data-' . $request_id );
echo '
';
?>
';
break;
case 'request-failed':
echo '';
break;
case 'request-completed':
echo '' . esc_html__( 'Remove request' ) . '';
break;
}
}
}
file.php 0000644 00000277534 15233111622 0006212 0 ustar 00 __( '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() {
?>
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 = __( 'An error occurred. Please try again later.' );
} 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|null $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|null $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', __( 'No 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 );
}
}
}
$mime_type = wp_remote_retrieve_header( $response, 'content-type' );
if ( $mime_type && 'tmp' === pathinfo( $tmpfname, PATHINFO_EXTENSION ) ) {
$valid_mime_types = array_flip( get_allowed_mime_types() );
if ( ! empty( $valid_mime_types[ $mime_type ] ) ) {
$extensions = explode( '|', $valid_mime_types[ $mime_type ] );
$new_image_name = substr( $tmpfname, 0, -4 ) . ".{$extensions[0]}";
if ( 0 === validate_file( $new_image_name ) ) {
if ( rename( $tmpfname, $new_image_name ) ) {
$tmpfname = $new_image_name;
}
if ( ( $tmpfname !== $new_image_name ) && file_exists( $new_image_name ) ) {
unlink( $new_image_name );
}
}
}
}
$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.' ),
'' . esc_html( $filename_for_errors ) . ''
),
( ! function_exists( 'sodium_crypto_sign_verify_detached' ) ? 'sodium_crypto_sign_verify_detached' : 'sha384' )
);
}
// 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.' ),
'' . esc_html( $filename_for_errors ) . ''
),
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.' ),
'' . esc_html( $filename_for_errors ) . ''
),
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.' ),
'' . esc_html( $filename_for_errors ) . ''
),
// 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://developer.wordpress.org/advanced-administration/wordpress/wp-config/#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, false );
}
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 = __( 'Error: 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 );
?>
wp_opcache_invalidate_directory()'
);
wp_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 );
}
class-wp-terms-list-table.php 0000644 00000051360 15233111622 0012175 0 ustar 00 'tags',
'singular' => 'tag',
'screen' => isset( $args['screen'] ) ? $args['screen'] : null,
)
);
$action = $this->screen->action;
$post_type = $this->screen->post_type;
$taxonomy = $this->screen->taxonomy;
if ( empty( $taxonomy ) ) {
$taxonomy = 'post_tag';
}
if ( ! taxonomy_exists( $taxonomy ) ) {
wp_die( __( 'Invalid taxonomy.' ) );
}
$tax = get_taxonomy( $taxonomy );
// @todo Still needed? Maybe just the show_ui part.
if ( empty( $post_type ) || ! in_array( $post_type, get_post_types( array( 'show_ui' => true ) ), true ) ) {
$post_type = 'post';
}
}
/**
* @return bool
*/
public function ajax_user_can() {
return current_user_can( get_taxonomy( $this->screen->taxonomy )->cap->manage_terms );
}
/**
*/
public function prepare_items() {
$taxonomy = $this->screen->taxonomy;
$tags_per_page = $this->get_items_per_page( "edit_{$taxonomy}_per_page" );
if ( 'post_tag' === $taxonomy ) {
/**
* Filters the number of terms displayed per page for the Tags list table.
*
* @since 2.8.0
*
* @param int $tags_per_page Number of tags to be displayed. Default 20.
*/
$tags_per_page = apply_filters( 'edit_tags_per_page', $tags_per_page );
/**
* Filters the number of terms displayed per page for the Tags list table.
*
* @since 2.7.0
* @deprecated 2.8.0 Use {@see 'edit_tags_per_page'} instead.
*
* @param int $tags_per_page Number of tags to be displayed. Default 20.
*/
$tags_per_page = apply_filters_deprecated( 'tagsperpage', array( $tags_per_page ), '2.8.0', 'edit_tags_per_page' );
} elseif ( 'category' === $taxonomy ) {
/**
* Filters the number of terms displayed per page for the Categories list table.
*
* @since 2.8.0
*
* @param int $tags_per_page Number of categories to be displayed. Default 20.
*/
$tags_per_page = apply_filters( 'edit_categories_per_page', $tags_per_page );
}
$search = ! empty( $_REQUEST['s'] ) ? trim( wp_unslash( $_REQUEST['s'] ) ) : '';
$args = array(
'taxonomy' => $taxonomy,
'search' => $search,
'page' => $this->get_pagenum(),
'number' => $tags_per_page,
'hide_empty' => 0,
);
if ( ! empty( $_REQUEST['orderby'] ) ) {
$args['orderby'] = trim( wp_unslash( $_REQUEST['orderby'] ) );
}
if ( ! empty( $_REQUEST['order'] ) ) {
$args['order'] = trim( wp_unslash( $_REQUEST['order'] ) );
}
$args['offset'] = ( $args['page'] - 1 ) * $args['number'];
// Save the values because 'number' and 'offset' can be subsequently overridden.
$this->callback_args = $args;
if ( is_taxonomy_hierarchical( $taxonomy ) && ! isset( $args['orderby'] ) ) {
// We'll need the full set of terms then.
$args['number'] = 0;
$args['offset'] = $args['number'];
}
$this->items = get_terms( $args );
$this->set_pagination_args(
array(
'total_items' => wp_count_terms(
array(
'taxonomy' => $taxonomy,
'search' => $search,
)
),
'per_page' => $tags_per_page,
)
);
}
/**
*/
public function no_items() {
echo get_taxonomy( $this->screen->taxonomy )->labels->not_found;
}
/**
* @return array
*/
protected function get_bulk_actions() {
$actions = array();
if ( current_user_can( get_taxonomy( $this->screen->taxonomy )->cap->delete_terms ) ) {
$actions['delete'] = __( 'Delete' );
}
return $actions;
}
/**
* @return string
*/
public function current_action() {
if ( isset( $_REQUEST['action'] ) && isset( $_REQUEST['delete_tags'] ) && 'delete' === $_REQUEST['action'] ) {
return 'bulk-delete';
}
return parent::current_action();
}
/**
* @return string[] Array of column titles keyed by their column name.
*/
public function get_columns() {
$columns = array(
'cb' => '',
'name' => _x( 'Name', 'term name' ),
'description' => __( 'Description' ),
'slug' => __( 'Slug' ),
);
if ( 'link_category' === $this->screen->taxonomy ) {
$columns['links'] = __( 'Links' );
} else {
$columns['posts'] = _x( 'Count', 'Number/count of items' );
}
return $columns;
}
/**
* @return array
*/
protected function get_sortable_columns() {
$taxonomy = $this->screen->taxonomy;
if ( ! isset( $_GET['orderby'] ) && is_taxonomy_hierarchical( $taxonomy ) ) {
$name_orderby_text = __( 'Table ordered hierarchically.' );
} else {
$name_orderby_text = __( 'Table ordered by Name.' );
}
return array(
'name' => array( 'name', false, _x( 'Name', 'term name' ), $name_orderby_text, 'asc' ),
'description' => array( 'description', false, __( 'Description' ), __( 'Table ordered by Description.' ) ),
'slug' => array( 'slug', false, __( 'Slug' ), __( 'Table ordered by Slug.' ) ),
'posts' => array( 'count', false, _x( 'Count', 'Number/count of items' ), __( 'Table ordered by Posts Count.' ) ),
'links' => array( 'count', false, __( 'Links' ), __( 'Table ordered by Links.' ) ),
);
}
/**
*/
public function display_rows_or_placeholder() {
$taxonomy = $this->screen->taxonomy;
$number = $this->callback_args['number'];
$offset = $this->callback_args['offset'];
// Convert it to table rows.
$count = 0;
if ( empty( $this->items ) || ! is_array( $this->items ) ) {
echo '
';
$this->no_items();
echo '
';
return;
}
if ( is_taxonomy_hierarchical( $taxonomy ) && ! isset( $this->callback_args['orderby'] ) ) {
if ( ! empty( $this->callback_args['search'] ) ) {// Ignore children on searches.
$children = array();
} else {
$children = _get_term_hierarchy( $taxonomy );
}
/*
* Some funky recursion to get the job done (paging & parents mainly) is contained within.
* Skip it for non-hierarchical taxonomies for performance sake.
*/
$this->_rows( $taxonomy, $this->items, $children, $offset, $number, $count );
} else {
foreach ( $this->items as $term ) {
$this->single_row( $term );
}
}
}
/**
* @param string $taxonomy
* @param array $terms
* @param array $children
* @param int $start
* @param int $per_page
* @param int $count
* @param int $parent_term
* @param int $level
*/
private function _rows( $taxonomy, $terms, &$children, $start, $per_page, &$count, $parent_term = 0, $level = 0 ) {
$end = $start + $per_page;
foreach ( $terms as $key => $term ) {
if ( $count >= $end ) {
break;
}
if ( $term->parent !== $parent_term && empty( $_REQUEST['s'] ) ) {
continue;
}
// If the page starts in a subtree, print the parents.
if ( $count === $start && $term->parent > 0 && empty( $_REQUEST['s'] ) ) {
$my_parents = array();
$parent_ids = array();
$p = $term->parent;
while ( $p ) {
$my_parent = get_term( $p, $taxonomy );
$my_parents[] = $my_parent;
$p = $my_parent->parent;
if ( in_array( $p, $parent_ids, true ) ) { // Prevent parent loops.
break;
}
$parent_ids[] = $p;
}
unset( $parent_ids );
$num_parents = count( $my_parents );
while ( $my_parent = array_pop( $my_parents ) ) {
echo "\t";
$this->single_row( $my_parent, $level - $num_parents );
--$num_parents;
}
}
if ( $count >= $start ) {
echo "\t";
$this->single_row( $term, $level );
}
++$count;
unset( $terms[ $key ] );
if ( isset( $children[ $term->term_id ] ) && empty( $_REQUEST['s'] ) ) {
$this->_rows( $taxonomy, $terms, $children, $start, $per_page, $count, $term->term_id, $level + 1 );
}
}
}
/**
* @global string $taxonomy Global taxonomy.
*
* @param WP_Term $tag Term object.
* @param int $level
*/
public function single_row( $tag, $level = 0 ) {
global $taxonomy;
$tag = sanitize_term( $tag, $taxonomy );
$this->level = $level;
if ( $tag->parent ) {
$count = count( get_ancestors( $tag->term_id, $taxonomy, 'taxonomy' ) );
$level = 'level-' . $count;
} else {
$level = 'level-0';
}
echo '
';
$this->single_row_columns( $tag );
echo '
';
}
/**
* @since 5.9.0 Renamed `$tag` to `$item` to match parent class for PHP 8 named parameter support.
*
* @param WP_Term $item Term object.
* @return string
*/
public function column_cb( $item ) {
// Restores the more descriptive, specific name for use within this method.
$tag = $item;
if ( current_user_can( 'delete_term', $tag->term_id ) ) {
return sprintf(
'' .
'',
$tag->term_id,
/* translators: Hidden accessibility text. %s: Taxonomy term name. */
sprintf( __( 'Select %s' ), $tag->name )
);
}
return ' ';
}
/**
* @param WP_Term $tag Term object.
* @return string
*/
public function column_name( $tag ) {
$taxonomy = $this->screen->taxonomy;
$pad = str_repeat( '— ', max( 0, $this->level ) );
/**
* Filters display of the term name in the terms list table.
*
* The default output may include padding due to the term's
* current level in the term hierarchy.
*
* @since 2.5.0
*
* @see WP_Terms_List_Table::column_name()
*
* @param string $pad_tag_name The term name, padded if not top-level.
* @param WP_Term $tag Term object.
*/
$name = apply_filters( 'term_name', $pad . ' ' . $tag->name, $tag );
$qe_data = get_term( $tag->term_id, $taxonomy, OBJECT, 'edit' );
$uri = wp_doing_ajax() ? wp_get_referer() : $_SERVER['REQUEST_URI'];
$edit_link = get_edit_term_link( $tag, $taxonomy, $this->screen->post_type );
if ( $edit_link ) {
$edit_link = add_query_arg(
'wp_http_referer',
urlencode( wp_unslash( $uri ) ),
$edit_link
);
$name = sprintf(
'%s',
esc_url( $edit_link ),
/* translators: %s: Taxonomy term name. */
esc_attr( sprintf( __( '“%s” (Edit)' ), $tag->name ) ),
$name
);
}
$output = sprintf(
'%s ',
$name
);
/** This filter is documented in wp-admin/includes/class-wp-terms-list-table.php */
$quick_edit_enabled = apply_filters( 'quick_edit_enabled_for_taxonomy', true, $taxonomy );
if ( $quick_edit_enabled ) {
$output .= '
';
$output .= '
' . $qe_data->name . '
';
/** This filter is documented in wp-admin/edit-tag-form.php */
$output .= '
';
}
/**
* Fires after outputting the fields for the inline editor for posts and pages.
*
* @since 4.9.8
*
* @param WP_Post $post The current post object.
* @param WP_Post_Type $post_type_object The current post's post type object.
*/
do_action( 'add_inline_data', $post, $post_type_object );
echo '
';
}
/**
* Outputs the in-line comment reply-to form in the Comments list table.
*
* @since 2.7.0
*
* @global WP_List_Table $wp_list_table
*
* @param int $position Optional. The value of the 'position' input field. Default 1.
* @param bool $checkbox Optional. The value of the 'checkbox' input field. Default false.
* @param string $mode Optional. If set to 'single', will use WP_Post_Comments_List_Table,
* otherwise WP_Comments_List_Table. Default 'single'.
* @param bool $table_row Optional. Whether to use a table instead of a div element. Default true.
*/
function wp_comment_reply( $position = 1, $checkbox = false, $mode = 'single', $table_row = true ) {
global $wp_list_table;
/**
* Filters the in-line comment reply-to form output in the Comments
* list table.
*
* Returning a non-empty value here will short-circuit display
* of the in-line comment-reply form in the Comments list table,
* echoing the returned value instead.
*
* @since 2.7.0
*
* @see wp_comment_reply()
*
* @param string $content The reply-to form content.
* @param array $args An array of default args.
*/
$content = apply_filters(
'wp_comment_reply',
'',
array(
'position' => $position,
'checkbox' => $checkbox,
'mode' => $mode,
)
);
if ( ! empty( $content ) ) {
echo $content;
return;
}
if ( ! $wp_list_table ) {
if ( 'single' === $mode ) {
$wp_list_table = _get_list_table( 'WP_Post_Comments_List_Table' );
} else {
$wp_list_table = _get_list_table( 'WP_Comments_List_Table' );
}
}
?>
";
return $r;
}
/**
* Prints the form in the Custom Fields meta box.
*
* @since 1.2.0
*
* @global wpdb $wpdb WordPress database abstraction object.
*
* @param WP_Post $post Optional. The post being edited.
*/
function meta_form( $post = null ) {
global $wpdb;
$post = get_post( $post );
/**
* Filters values for the meta key dropdown in the Custom Fields meta box.
*
* Returning a non-null value will effectively short-circuit and avoid a
* potentially expensive query against postmeta.
*
* @since 4.4.0
*
* @param array|null $keys Pre-defined meta keys to be used in place of a postmeta query. Default null.
* @param WP_Post $post The current post object.
*/
$keys = apply_filters( 'postmeta_form_keys', null, $post );
if ( null === $keys ) {
/**
* Filters the number of custom fields to retrieve for the drop-down
* in the Custom Fields meta box.
*
* @since 2.1.0
*
* @param int $limit Number of custom fields to retrieve. Default 30.
*/
$limit = apply_filters( 'postmeta_form_limit', 30 );
$keys = $wpdb->get_col(
$wpdb->prepare(
"SELECT DISTINCT meta_key
FROM $wpdb->postmeta
WHERE meta_key NOT BETWEEN '_' AND '_z'
HAVING meta_key NOT LIKE %s
ORDER BY meta_key
LIMIT %d",
$wpdb->esc_like( '_' ) . '%',
$limit
)
);
}
if ( $keys ) {
natcasesort( $keys );
}
?>
" . esc_html( $template ) . '';
}
}
/**
* Prints out option HTML elements for the page parents drop-down.
*
* @since 1.5.0
* @since 4.4.0 `$post` argument was added.
*
* @global wpdb $wpdb WordPress database abstraction object.
*
* @param int $default_page Optional. The default page ID to be pre-selected. Default 0.
* @param int $parent_page Optional. The parent page ID. Default 0.
* @param int $level Optional. Page depth level. Default 0.
* @param int|WP_Post $post Post ID or WP_Post object.
* @return void|false Void on success, false if the page has no children.
*/
function parent_dropdown( $default_page = 0, $parent_page = 0, $level = 0, $post = null ) {
global $wpdb;
$post = get_post( $post );
$items = $wpdb->get_results(
$wpdb->prepare(
"SELECT ID, post_parent, post_title
FROM $wpdb->posts
WHERE post_parent = %d AND post_type = 'page'
ORDER BY menu_order",
$parent_page
)
);
if ( $items ) {
foreach ( $items as $item ) {
// A page cannot be its own parent.
if ( $post && $post->ID && (int) $item->ID === $post->ID ) {
continue;
}
$pad = str_repeat( ' ', $level * 3 );
$selected = selected( $default_page, $item->ID, false );
echo "\n\t';
parent_dropdown( $default_page, $item->ID, $level + 1 );
}
} else {
return false;
}
}
/**
* Prints out option HTML elements for role selectors.
*
* @since 2.1.0
*
* @param string $selected Slug for the role that should be already selected.
*/
function wp_dropdown_roles( $selected = '' ) {
$r = '';
$editable_roles = array_reverse( get_editable_roles() );
foreach ( $editable_roles as $role => $details ) {
$name = translate_user_role( $details['name'] );
// Preselect specified role.
if ( $selected === $role ) {
$r .= "\n\t";
} else {
$r .= "\n\t";
}
}
echo $r;
}
/**
* Outputs the form used by the importers to accept the data to be imported.
*
* @since 2.0.0
*
* @param string $action The action attribute for the form.
*/
function wp_import_upload_form( $action ) {
/**
* Filters the maximum allowed upload size for import files.
*
* @since 2.3.0
*
* @see wp_max_upload_size()
*
* @param int $max_upload_size Allowed upload size. Default 1 MB.
*/
$bytes = apply_filters( 'import_upload_size_limit', wp_max_upload_size() );
$size = size_format( $bytes );
$upload_dir = wp_upload_dir();
if ( ! empty( $upload_dir['error'] ) ) :
$upload_directory_error = '
' . __( 'Before you can upload your import file, you will need to fix the following error:' ) . '
';
$upload_directory_error .= '
' . $upload_dir['error'] . '
';
wp_admin_notice(
$upload_directory_error,
array(
'additional_classes' => array( 'error' ),
'paragraph_wrap' => false,
)
);
else :
?>
id ) ) {
return;
}
$page = $screen->id;
if ( ! isset( $wp_meta_boxes ) ) {
$wp_meta_boxes = array();
}
if ( ! isset( $wp_meta_boxes[ $page ] ) ) {
$wp_meta_boxes[ $page ] = array();
}
if ( ! isset( $wp_meta_boxes[ $page ][ $context ] ) ) {
$wp_meta_boxes[ $page ][ $context ] = array();
}
foreach ( array_keys( $wp_meta_boxes[ $page ] ) as $a_context ) {
foreach ( array( 'high', 'core', 'default', 'low' ) as $a_priority ) {
if ( ! isset( $wp_meta_boxes[ $page ][ $a_context ][ $a_priority ][ $id ] ) ) {
continue;
}
// If a core box was previously removed, don't add.
if ( ( 'core' === $priority || 'sorted' === $priority )
&& false === $wp_meta_boxes[ $page ][ $a_context ][ $a_priority ][ $id ]
) {
return;
}
// If a core box was previously added by a plugin, don't add.
if ( 'core' === $priority ) {
/*
* If the box was added with default priority, give it core priority
* to maintain sort order.
*/
if ( 'default' === $a_priority ) {
$wp_meta_boxes[ $page ][ $a_context ]['core'][ $id ] = $wp_meta_boxes[ $page ][ $a_context ]['default'][ $id ];
unset( $wp_meta_boxes[ $page ][ $a_context ]['default'][ $id ] );
}
return;
}
// If no priority given and ID already present, use existing priority.
if ( empty( $priority ) ) {
$priority = $a_priority;
/*
* Else, if we're adding to the sorted priority, we don't know the title
* or callback. Grab them from the previously added context/priority.
*/
} elseif ( 'sorted' === $priority ) {
$title = $wp_meta_boxes[ $page ][ $a_context ][ $a_priority ][ $id ]['title'];
$callback = $wp_meta_boxes[ $page ][ $a_context ][ $a_priority ][ $id ]['callback'];
$callback_args = $wp_meta_boxes[ $page ][ $a_context ][ $a_priority ][ $id ]['args'];
}
// An ID can be in only one priority and one context.
if ( $priority !== $a_priority || $context !== $a_context ) {
unset( $wp_meta_boxes[ $page ][ $a_context ][ $a_priority ][ $id ] );
}
}
}
if ( empty( $priority ) ) {
$priority = 'low';
}
if ( ! isset( $wp_meta_boxes[ $page ][ $context ][ $priority ] ) ) {
$wp_meta_boxes[ $page ][ $context ][ $priority ] = array();
}
$wp_meta_boxes[ $page ][ $context ][ $priority ][ $id ] = array(
'id' => $id,
'title' => $title,
'callback' => $callback,
'args' => $callback_args,
);
}
/**
* Renders a "fake" meta box with an information message,
* shown on the block editor, when an incompatible meta box is found.
*
* @since 5.0.0
*
* @param mixed $data_object The data object being rendered on this screen.
* @param array $box {
* Custom formats meta box arguments.
*
* @type string $id Meta box 'id' attribute.
* @type string $title Meta box title.
* @type callable $old_callback The original callback for this meta box.
* @type array $args Extra meta box arguments.
* }
*/
function do_block_editor_incompatible_meta_box( $data_object, $box ) {
$plugin = _get_plugin_from_callback( $box['old_callback'] );
$plugins = get_plugins();
echo '
';
if ( $plugin ) {
/* translators: %s: The name of the plugin that generated this meta box. */
printf( __( 'This meta box, from the %s plugin, is not compatible with the block editor.' ), "{$plugin['Name']}" );
} else {
_e( 'This meta box is not compatible with the block editor.' );
}
echo '
';
/* translators: %s: A link to install the Classic Editor plugin. */
printf( __( 'Please install the Classic Editor plugin to use this meta box.' ), esc_url( $install_url ) );
echo '
';
/* translators: %s: A link to activate the Classic Editor plugin. */
printf( __( 'Please activate the Classic Editor plugin to use this meta box.' ), esc_url( $activate_url ) );
echo '
';
/* translators: %s: A link to use the Classic Editor plugin. */
printf( __( 'Please open the classic editor to use this meta box.' ), esc_url( $edit_url ) );
echo '
';
}
}
/**
* Internal helper function to find the plugin from a meta box callback.
*
* @since 5.0.0
*
* @access private
*
* @param callable $callback The callback function to check.
* @return array|null The plugin that the callback belongs to, or null if it doesn't belong to a plugin.
*/
function _get_plugin_from_callback( $callback ) {
try {
if ( is_array( $callback ) ) {
$reflection = new ReflectionMethod( $callback[0], $callback[1] );
} elseif ( is_string( $callback ) && str_contains( $callback, '::' ) ) {
$reflection = new ReflectionMethod( $callback );
} else {
$reflection = new ReflectionFunction( $callback );
}
} catch ( ReflectionException $exception ) {
// We could not properly reflect on the callable, so we abort here.
return null;
}
// Don't show an error if it's an internal PHP function.
if ( ! $reflection->isInternal() ) {
// Only show errors if the meta box was registered by a plugin.
$filename = wp_normalize_path( $reflection->getFileName() );
$plugin_dir = wp_normalize_path( WP_PLUGIN_DIR );
if ( str_starts_with( $filename, $plugin_dir ) ) {
$filename = str_replace( $plugin_dir, '', $filename );
$filename = preg_replace( '|^/([^/]*/).*$|', '\\1', $filename );
$plugins = get_plugins();
foreach ( $plugins as $name => $plugin ) {
if ( str_starts_with( $name, $filename ) ) {
return $plugin;
}
}
}
}
return null;
}
/**
* Meta-Box template function.
*
* @since 2.5.0
*
* @global array $wp_meta_boxes Global meta box state.
*
* @param string|WP_Screen $screen The screen identifier. If you have used add_menu_page() or
* add_submenu_page() to create a new screen (and hence screen_id)
* make sure your menu slug conforms to the limits of sanitize_key()
* otherwise the 'screen' menu may not correctly render on your page.
* @param string $context The screen context for which to display meta boxes.
* @param mixed $data_object Gets passed to the meta box callback function as the first parameter.
* Often this is the object that's the focus of the current screen,
* for example a `WP_Post` or `WP_Comment` object.
* @return int Number of meta_boxes.
*/
function do_meta_boxes( $screen, $context, $data_object ) {
global $wp_meta_boxes;
static $already_sorted = false;
if ( empty( $screen ) ) {
$screen = get_current_screen();
} elseif ( is_string( $screen ) ) {
$screen = convert_to_screen( $screen );
}
$page = $screen->id;
$hidden = get_hidden_meta_boxes( $screen );
printf( '
', esc_attr( $context ) );
/*
* Grab the ones the user has manually sorted.
* Pull them out of their previous context/priority and into the one the user chose.
*/
$sorted = get_user_option( "meta-box-order_$page" );
if ( ! $already_sorted && $sorted ) {
foreach ( $sorted as $box_context => $ids ) {
foreach ( explode( ',', $ids ) as $id ) {
if ( $id && 'dashboard_browser_nag' !== $id ) {
add_meta_box( $id, null, null, $screen, $box_context, 'sorted' );
}
}
}
}
$already_sorted = true;
$i = 0;
if ( isset( $wp_meta_boxes[ $page ][ $context ] ) ) {
foreach ( array( 'high', 'sorted', 'core', 'default', 'low' ) as $priority ) {
if ( isset( $wp_meta_boxes[ $page ][ $context ][ $priority ] ) ) {
foreach ( (array) $wp_meta_boxes[ $page ][ $context ][ $priority ] as $box ) {
if ( false === $box || ! $box['title'] ) {
continue;
}
$block_compatible = true;
if ( is_array( $box['args'] ) ) {
// If a meta box is just here for back compat, don't show it in the block editor.
if ( $screen->is_block_editor() && isset( $box['args']['__back_compat_meta_box'] ) && $box['args']['__back_compat_meta_box'] ) {
continue;
}
if ( isset( $box['args']['__block_editor_compatible_meta_box'] ) ) {
$block_compatible = (bool) $box['args']['__block_editor_compatible_meta_box'];
unset( $box['args']['__block_editor_compatible_meta_box'] );
}
// If the meta box is declared as incompatible with the block editor, override the callback function.
if ( ! $block_compatible && $screen->is_block_editor() ) {
$box['old_callback'] = $box['callback'];
$box['callback'] = 'do_block_editor_incompatible_meta_box';
}
if ( isset( $box['args']['__back_compat_meta_box'] ) ) {
$block_compatible = $block_compatible || (bool) $box['args']['__back_compat_meta_box'];
unset( $box['args']['__back_compat_meta_box'] );
}
}
++$i;
// get_hidden_meta_boxes() doesn't apply in the block editor.
$hidden_class = ( ! $screen->is_block_editor() && in_array( $box['id'], $hidden, true ) ) ? ' hide-if-js' : '';
echo '
' . "\n";
if ( WP_DEBUG && ! $block_compatible && 'edit' === $screen->parent_base && ! $screen->is_block_editor() && ! isset( $_GET['meta-box-loader'] ) ) {
$plugin = _get_plugin_from_callback( $box['callback'] );
if ( $plugin ) {
$meta_box_not_compatible_message = sprintf(
/* translators: %s: The name of the plugin that generated this meta box. */
__( 'This meta box, from the %s plugin, is not compatible with the block editor.' ),
"{$plugin['Name']}"
);
wp_admin_notice(
$meta_box_not_compatible_message,
array(
'additional_classes' => array( 'error', 'inline' ),
)
);
}
}
call_user_func( $box['callback'], $data_object, $box );
echo "
\n";
echo "
\n";
}
}
}
}
echo '
';
return $i;
}
/**
* Removes a meta box from one or more screens.
*
* @since 2.6.0
* @since 4.4.0 The `$screen` parameter now accepts an array of screen IDs.
*
* @global array $wp_meta_boxes Global meta box state.
*
* @param string $id Meta box ID (used in the 'id' attribute for the meta box).
* @param string|array|WP_Screen $screen The screen or screens on which the meta box is shown (such as a
* post type, 'link', or 'comment'). Accepts a single screen ID,
* WP_Screen object, or array of screen IDs.
* @param string $context The context within the screen where the box is set to display.
* Contexts vary from screen to screen. Post edit screen contexts
* include 'normal', 'side', and 'advanced'. Comments screen contexts
* include 'normal' and 'side'. Menus meta boxes (accordion sections)
* all use the 'side' context.
*/
function remove_meta_box( $id, $screen, $context ) {
global $wp_meta_boxes;
if ( empty( $screen ) ) {
$screen = get_current_screen();
} elseif ( is_string( $screen ) ) {
$screen = convert_to_screen( $screen );
} elseif ( is_array( $screen ) ) {
foreach ( $screen as $single_screen ) {
remove_meta_box( $id, $single_screen, $context );
}
}
if ( ! isset( $screen->id ) ) {
return;
}
$page = $screen->id;
if ( ! isset( $wp_meta_boxes ) ) {
$wp_meta_boxes = array();
}
if ( ! isset( $wp_meta_boxes[ $page ] ) ) {
$wp_meta_boxes[ $page ] = array();
}
if ( ! isset( $wp_meta_boxes[ $page ][ $context ] ) ) {
$wp_meta_boxes[ $page ][ $context ] = array();
}
foreach ( array( 'high', 'core', 'default', 'low' ) as $priority ) {
$wp_meta_boxes[ $page ][ $context ][ $priority ][ $id ] = false;
}
}
/**
* Meta Box Accordion Template Function.
*
* Largely made up of abstracted code from do_meta_boxes(), this
* function serves to build meta boxes as list items for display as
* a collapsible accordion.
*
* @since 3.6.0
*
* @uses global $wp_meta_boxes Used to retrieve registered meta boxes.
*
* @param string|object $screen The screen identifier.
* @param string $context The screen context for which to display accordion sections.
* @param mixed $data_object Gets passed to the section callback function as the first parameter.
* @return int Number of meta boxes as accordion sections.
*/
function do_accordion_sections( $screen, $context, $data_object ) {
global $wp_meta_boxes;
wp_enqueue_script( 'accordion' );
if ( empty( $screen ) ) {
$screen = get_current_screen();
} elseif ( is_string( $screen ) ) {
$screen = convert_to_screen( $screen );
}
$page = $screen->id;
$hidden = get_hidden_meta_boxes( $screen );
?>
$id,
'title' => $title,
'callback' => $callback,
'before_section' => '',
'after_section' => '',
'section_class' => '',
);
$section = wp_parse_args( $args, $defaults );
if ( 'misc' === $page ) {
_deprecated_argument(
__FUNCTION__,
'3.0.0',
sprintf(
/* translators: %s: misc */
__( 'The "%s" options group has been removed. Use another settings group.' ),
'misc'
)
);
$page = 'general';
}
if ( 'privacy' === $page ) {
_deprecated_argument(
__FUNCTION__,
'3.5.0',
sprintf(
/* translators: %s: privacy */
__( 'The "%s" options group has been removed. Use another settings group.' ),
'privacy'
)
);
$page = 'reading';
}
$wp_settings_sections[ $page ][ $id ] = $section;
}
/**
* Adds a new field to a section of a settings page.
*
* Part of the Settings API. Use this to define a settings field that will show
* as part of a settings section inside a settings page. The fields are shown using
* do_settings_fields() in do_settings_sections().
*
* The $callback argument should be the name of a function that echoes out the
* HTML input tags for this setting field. Use get_option() to retrieve existing
* values to show.
*
* @since 2.7.0
* @since 4.2.0 The `$class` argument was added.
*
* @global array $wp_settings_fields Storage array of settings fields and info about their pages/sections.
*
* @param string $id Slug-name to identify the field. Used in the 'id' attribute of tags.
* @param string $title Formatted title of the field. Shown as the label for the field
* during output.
* @param callable $callback Function that fills the field with the desired form inputs. The
* function should echo its output.
* @param string $page The slug-name of the settings page on which to show the section
* (general, reading, writing, ...).
* @param string $section Optional. The slug-name of the section of the settings page
* in which to show the box. Default 'default'.
* @param array $args {
* Optional. Extra arguments that get passed to the callback function.
*
* @type string $label_for When supplied, the setting title will be wrapped
* in a `
' . get_comment_author_link( $comment ) . '', $comment_post_link, '' . __( '[Pending]' ) . '' ); } else { printf( /* translators: 1: Comment author, 2: Notification if the comment is pending. */ __( 'From %1$s %2$s' ), '' . get_comment_author_link( $comment ) . '', '' . __( '[Pending]' ) . '' ); } ?>
comment_type ) { case 'pingback': $type = __( 'Pingback' ); break; case 'trackback': $type = __( 'Trackback' ); break; default: $type = ucwords( $comment->comment_type ); } $type = esc_html( $type ); ?>$type", $comment_post_link, '' . __( '[Pending]' ) . '' ); } else { printf( /* translators: 1: Type of comment, 2: Notification if the comment is pending. */ _x( '%1$s %2$s', 'dashboard' ), "$type", '' . __( '[Pending]' ) . '' ); } ?>
' . __( 'No activity yet!' ) . '
'; echo '