' . __( 'WordPress is created by a worldwide team of passionate individuals.' ) . "
\n"; +echo '' . sprintf( + /* translators: %s: https://make.wordpress.org/ */ + __( 'Want to see your name in lights on this page? Get involved in WordPress.' ), + __( 'https://make.wordpress.org/' ) +) . '
'; + foreach ( $credits['groups'] as $group_slug => $group_data ) { if ( $group_data['name'] ) { if ( 'Translators' == $group_data['name'] ) { @@ -101,13 +107,6 @@ } ?> -Get involved in WordPress.' ), - __( 'https://make.wordpress.org/' ) - ); -?>
-' . __( 'Cheatin’ uh?' ) . '
' . + '' . __( 'Something went wrong.' ) . '
' . '' . __( 'The current theme does not support uploading a custom header image.' ) . '
', 403 ); @@ -877,7 +877,7 @@ public function step_3() { if ( ! current_theme_supports( 'custom-header', 'uploads' ) ) { wp_die( - '' . __( 'Cheatin’ uh?' ) . '
' . + '' . __( 'Something went wrong.' ) . '
' . '' . __( 'The current theme does not support uploading a custom header image.' ) . '
', 403 ); @@ -885,7 +885,7 @@ public function step_3() { if ( ! empty( $_POST['skip-cropping'] ) && ! ( current_theme_supports( 'custom-header', 'flex-height' ) || current_theme_supports( 'custom-header', 'flex-width' ) ) ) { wp_die( - '' . __( 'Cheatin’ uh?' ) . '
' . + '' . __( 'Something went wrong.' ) . '
' . '' . __( 'The current theme does not support a flexible sized header image.' ) . '
', 403 ); diff --git a/src/wp-admin/customize.php b/src/wp-admin/customize.php index 7f9e5a9dc7952..d98c2a1718bb7 100644 --- a/src/wp-admin/customize.php +++ b/src/wp-admin/customize.php @@ -14,7 +14,7 @@ if ( ! current_user_can( 'customize' ) ) { wp_die( - '' . __( 'Cheatin’ uh?' ) . '
' . + '' . __( 'You need a higher level of permission.' ) . '
' . '' . __( 'Sorry, you are not allowed to customize this site.' ) . '
', 403 ); @@ -31,7 +31,7 @@ if ( ! current_user_can( get_post_type_object( 'customize_changeset' )->cap->edit_post, $changeset_post->ID ) ) { wp_die( - '' . __( 'Cheatin’ uh?' ) . '
' . + '' . __( 'You need a higher level of permission.' ) . '
' . '' . __( 'Sorry, you are not allowed to edit this changeset.' ) . '
', 403 ); @@ -42,18 +42,42 @@ get_post_time( 'G', true, $changeset_post ) < time() ); if ( $missed_schedule ) { - wp_publish_post( $changeset_post->ID ); + /* + * Note that an Ajax request spawns here instead of just calling `wp_publish_post( $changeset_post->ID )`. + * + * Because WP_Customize_Manager is not instantiated for customize.php with the `settings_previewed=false` + * argument, settings cannot be reliably saved. Some logic short-circuits if the current value is the + * same as the value being saved. This is particularly true for options via `update_option()`. + * + * By opening an Ajax request, this is avoided and the changeset is published. See #39221. + */ + $nonces = $wp_customize->get_nonces(); + $request_args = array( + 'nonce' => $nonces['save'], + 'customize_changeset_uuid' => $wp_customize->changeset_uuid(), + 'wp_customize' => 'on', + 'customize_changeset_status' => 'publish', + ); + ob_start(); + ?> + + + ' . __( 'Your scheduled changes just published' ) . '' . - '' . __( 'Customize New Changes' ) . '
', + '' . __( 'Customize New Changes' ) . '
' . $script, 200 ); } if ( in_array( get_post_status( $changeset_post->ID ), array( 'publish', 'trash' ), true ) ) { wp_die( - '' . __( 'Cheatin’ uh?' ) . '
' . - '' . __( 'This changeset has already been published and cannot be further modified.' ) . '
' . + '' . __( 'Something went wrong.' ) . '
' . + '' . __( 'This changeset cannot be further modified.' ) . '
' . '' . __( 'Customize New Changes' ) . '
', 403 ); diff --git a/src/wp-admin/edit-comments.php b/src/wp-admin/edit-comments.php index 47244f025e834..4076bf2d9eba5 100644 --- a/src/wp-admin/edit-comments.php +++ b/src/wp-admin/edit-comments.php @@ -10,7 +10,7 @@ require_once( dirname( __FILE__ ) . '/admin.php' ); if ( ! current_user_can( 'edit_posts' ) ) { wp_die( - '' . __( 'Cheatin’ uh?' ) . '
' . + '' . __( 'You need a higher level of permission.' ) . '
' . '' . __( 'Sorry, you are not allowed to edit comments.' ) . '
', 403 ); diff --git a/src/wp-admin/edit-form-advanced.php b/src/wp-admin/edit-form-advanced.php index 07c2dfe129c16..9e8da8588d989 100644 --- a/src/wp-admin/edit-form-advanced.php +++ b/src/wp-admin/edit-form-advanced.php @@ -226,33 +226,33 @@ require_once( ABSPATH . 'wp-admin/includes/meta-boxes.php' ); -$publish_callback_args = null; +$publish_callback_args = array( '__back_compat_meta_box' => true ); if ( post_type_supports($post_type, 'revisions') && 'auto-draft' != $post->post_status ) { $revisions = wp_get_post_revisions( $post_ID ); // We should aim to show the revisions meta box only when there are revisions. if ( count( $revisions ) > 1 ) { reset( $revisions ); // Reset pointer for key() - $publish_callback_args = array( 'revisions_count' => count( $revisions ), 'revision_id' => key( $revisions ) ); - add_meta_box('revisionsdiv', __('Revisions'), 'post_revisions_meta_box', null, 'normal', 'core'); + $publish_callback_args = array( 'revisions_count' => count( $revisions ), 'revision_id' => key( $revisions ), '__back_compat_meta_box' => true ); + add_meta_box('revisionsdiv', __('Revisions'), 'post_revisions_meta_box', null, 'normal', 'core', array( '__back_compat_meta_box' => true ) ); } } if ( 'attachment' == $post_type ) { wp_enqueue_script( 'image-edit' ); wp_enqueue_style( 'imgareaselect' ); - add_meta_box( 'submitdiv', __('Save'), 'attachment_submit_meta_box', null, 'side', 'core' ); + add_meta_box( 'submitdiv', __('Save'), 'attachment_submit_meta_box', null, 'side', 'core', array( '__back_compat_meta_box' => true ) ); add_action( 'edit_form_after_title', 'edit_form_image_editor' ); if ( wp_attachment_is( 'audio', $post ) ) { - add_meta_box( 'attachment-id3', __( 'Metadata' ), 'attachment_id3_data_meta_box', null, 'normal', 'core' ); + add_meta_box( 'attachment-id3', __( 'Metadata' ), 'attachment_id3_data_meta_box', null, 'normal', 'core', array( '__back_compat_meta_box' => true ) ); } } else { add_meta_box( 'submitdiv', __( 'Publish' ), 'post_submit_meta_box', null, 'side', 'core', $publish_callback_args ); } if ( current_theme_supports( 'post-formats' ) && post_type_supports( $post_type, 'post-formats' ) ) - add_meta_box( 'formatdiv', _x( 'Format', 'post format' ), 'post_format_meta_box', null, 'side', 'core' ); + add_meta_box( 'formatdiv', _x( 'Format', 'post format' ), 'post_format_meta_box', null, 'side', 'core', array( '__back_compat_meta_box' => true ) ); // all taxonomies foreach ( get_object_taxonomies( $post ) as $tax_name ) { @@ -267,24 +267,24 @@ else $tax_meta_box_id = $tax_name . 'div'; - add_meta_box( $tax_meta_box_id, $label, $taxonomy->meta_box_cb, null, 'side', 'core', array( 'taxonomy' => $tax_name ) ); + add_meta_box( $tax_meta_box_id, $label, $taxonomy->meta_box_cb, null, 'side', 'core', array( 'taxonomy' => $tax_name, '__back_compat_meta_box' => true ) ); } if ( post_type_supports( $post_type, 'page-attributes' ) || count( get_page_templates( $post ) ) > 0 ) { - add_meta_box( 'pageparentdiv', $post_type_object->labels->attributes, 'page_attributes_meta_box', null, 'side', 'core' ); + add_meta_box( 'pageparentdiv', $post_type_object->labels->attributes, 'page_attributes_meta_box', null, 'side', 'core', array( '__back_compat_meta_box' => true ) ); } if ( $thumbnail_support && current_user_can( 'upload_files' ) ) - add_meta_box('postimagediv', esc_html( $post_type_object->labels->featured_image ), 'post_thumbnail_meta_box', null, 'side', 'low'); + add_meta_box('postimagediv', esc_html( $post_type_object->labels->featured_image ), 'post_thumbnail_meta_box', null, 'side', 'low', array( '__back_compat_meta_box' => true ) ); if ( post_type_supports($post_type, 'excerpt') ) - add_meta_box('postexcerpt', __('Excerpt'), 'post_excerpt_meta_box', null, 'normal', 'core'); + add_meta_box('postexcerpt', __('Excerpt'), 'post_excerpt_meta_box', null, 'normal', 'core', array( '__back_compat_meta_box' => true ) ); if ( post_type_supports($post_type, 'trackbacks') ) - add_meta_box('trackbacksdiv', __('Send Trackbacks'), 'post_trackback_meta_box', null, 'normal', 'core'); + add_meta_box('trackbacksdiv', __('Send Trackbacks'), 'post_trackback_meta_box', null, 'normal', 'core', array( '__back_compat_meta_box' => true ) ); if ( post_type_supports($post_type, 'custom-fields') ) - add_meta_box('postcustom', __('Custom Fields'), 'post_custom_meta_box', null, 'normal', 'core'); + add_meta_box('postcustom', __('Custom Fields'), 'post_custom_meta_box', null, 'normal', 'core', array( '__back_compat_meta_box' => true ) ); /** * Fires in the middle of built-in meta box registration. @@ -299,7 +299,7 @@ // Allow the Discussion meta box to show up if the post type supports comments, // or if comments or pings are open. if ( comments_open( $post ) || pings_open( $post ) || post_type_supports( $post_type, 'comments' ) ) { - add_meta_box( 'commentstatusdiv', __( 'Discussion' ), 'post_comment_status_meta_box', null, 'normal', 'core' ); + add_meta_box( 'commentstatusdiv', __( 'Discussion' ), 'post_comment_status_meta_box', null, 'normal', 'core', array( '__back_compat_meta_box' => true ) ); } $stati = get_post_stati( array( 'public' => true ) ); @@ -312,15 +312,15 @@ // If the post type support comments, or the post has comments, allow the // Comments meta box. if ( comments_open( $post ) || pings_open( $post ) || $post->comment_count > 0 || post_type_supports( $post_type, 'comments' ) ) { - add_meta_box( 'commentsdiv', __( 'Comments' ), 'post_comment_meta_box', null, 'normal', 'core' ); + add_meta_box( 'commentsdiv', __( 'Comments' ), 'post_comment_meta_box', null, 'normal', 'core', array( '__back_compat_meta_box' => true ) ); } } if ( ! ( 'pending' == get_post_status( $post ) && ! current_user_can( $post_type_object->cap->publish_posts ) ) ) - add_meta_box('slugdiv', __('Slug'), 'post_slug_meta_box', null, 'normal', 'core'); + add_meta_box('slugdiv', __('Slug'), 'post_slug_meta_box', null, 'normal', 'core', array( '__back_compat_meta_box' => true ) ); if ( post_type_supports( $post_type, 'author' ) && current_user_can( $post_type_object->cap->edit_others_posts ) ) { - add_meta_box( 'authordiv', __( 'Author' ), 'post_author_meta_box', null, 'normal', 'core' ); + add_meta_box( 'authordiv', __( 'Author' ), 'post_author_meta_box', null, 'normal', 'core', array( '__back_compat_meta_box' => true ) ); } /** @@ -639,7 +639,7 @@ 'resize' => false, 'wp_autoresize_on' => $_wp_editor_expand, 'add_unload_trigger' => false, - 'wp_keep_scroll_position' => true, + 'wp_keep_scroll_position' => ! $is_IE, ), ) ); ?>| ' . esc_html( $group_item_datum['name'] ) . ' | '; + $group_html .= '' . wp_kses( $value, $allowed_tags, $allowed_protocols ) . ' | '; + $group_html .= '
|---|
" . esc_html__( 'Personal Data Export' ) . "
" ); + + // And now, all the Groups. + $groups = get_post_meta( $request_id, '_export_data_grouped', true ); + + // First, build an "About" group on the fly for this report. + $about_group = array( + /* translators: Header for the About section in a personal data export. */ + 'group_label' => _x( 'About', 'personal data group label' ), + 'items' => array( + 'about-1' => array( + array( + 'name' => _x( 'Report generated for', 'email address' ), + 'value' => $email_address, + ), + array( + 'name' => _x( 'For site', 'website name' ), + 'value' => get_bloginfo( 'name' ), + ), + array( + 'name' => _x( 'At URL', 'website URL' ), + 'value' => get_bloginfo( 'url' ), + ), + array( + 'name' => _x( 'On', 'date/time' ), + 'value' => current_time( 'mysql' ), + ), + ), + ), + ); + + // Merge in the special about group. + $groups = array_merge( array( 'about' => $about_group ), $groups ); + + // Now, iterate over every group in $groups and have the formatter render it in HTML. + foreach ( (array) $groups as $group_id => $group_data ) { + fwrite( $file, wp_privacy_generate_personal_data_export_group_html( $group_data ) ); + } + + fwrite( $file, "\n" ); + + // Close HTML. + fwrite( $file, "\n" ); + fclose( $file ); + + /* + * Now, generate the ZIP. + * + * If an archive has already been generated, then remove it and reuse the + * filename, to avoid breaking any URLs that may have been previously sent + * via email. + */ + $error = false; + $archive_url = get_post_meta( $request_id, '_export_file_url', true ); + $archive_pathname = get_post_meta( $request_id, '_export_file_path', true ); + + if ( empty( $archive_pathname ) || empty( $archive_url ) ) { + $archive_filename = $file_basename . '.zip'; + $archive_pathname = $exports_dir . $archive_filename; + $archive_url = $exports_url . $archive_filename; + + update_post_meta( $request_id, '_export_file_url', $archive_url ); + update_post_meta( $request_id, '_export_file_path', wp_normalize_path( $archive_pathname ) ); + } + + if ( ! empty( $archive_pathname ) && file_exists( $archive_pathname ) ) { + wp_delete_file( $archive_pathname ); + } + + $zip = new ZipArchive; + if ( true === $zip->open( $archive_pathname, ZipArchive::CREATE ) ) { + if ( ! $zip->addFile( $html_report_pathname, 'index.html' ) ) { + $error = __( 'Unable to add data to export file.' ); + } + + $zip->close(); + + if ( ! $error ) { + /** + * Fires right after all personal data has been written to the export file. + * + * @since 4.9.6 + * + * @param string $archive_pathname The full path to the export file on the filesystem. + * @param string $archive_url The URL of the archive file. + * @param string $html_report_pathname The full path to the personal data report on the filesystem. + * @param int $request_id The export request ID. + */ + do_action( 'wp_privacy_personal_data_export_file_created', $archive_pathname, $archive_url, $html_report_pathname, $request_id ); + } + } else { + $error = __( 'Unable to open export file (archive) for writing.' ); + } + + // And remove the HTML file. + unlink( $html_report_pathname ); + + if ( $error ) { + wp_send_json_error( $error ); + } +} + +/** + * Send an email to the user with a link to the personal data export file + * + * @since 4.9.6 + * + * @param int $request_id The request ID for this personal data export. + * @return true|WP_Error True on success or `WP_Error` on failure. + */ +function wp_privacy_send_personal_data_export_email( $request_id ) { + // Get the request data. + $request = wp_get_user_request_data( $request_id ); + + if ( ! $request || 'export_personal_data' !== $request->action_name ) { + return new WP_Error( 'invalid', __( 'Invalid request ID when sending personal data export email.' ) ); + } + + /** This filter is documented in wp-includes/functions.php */ + $expiration = apply_filters( 'wp_privacy_export_expiration', 3 * DAY_IN_SECONDS ); + $expiration_date = date_i18n( get_option( 'date_format' ), time() + $expiration ); + +/* translators: Do not translate EXPIRATION, LINK, SITENAME, SITEURL: those are placeholders. */ +$email_text = __( +'Howdy, + +Your request for an export of personal data has been completed. You may +download your personal data by clicking on the link below. For privacy +and security, we will automatically delete the file on ###EXPIRATION###, +so please download it before then. + +###LINK### + +Regards, +All at ###SITENAME### +###SITEURL###' +); + + /** + * Filters the text of the email sent with a personal data export file. + * + * The following strings have a special meaning and will get replaced dynamically: + * ###EXPIRATION### The date when the URL will be automatically deleted. + * ###LINK### URL of the personal data export file for the user. + * ###SITENAME### The name of the site. + * ###SITEURL### The URL to the site. + * + * @since 4.9.6 + * + * @param string $email_text Text in the email. + * @param int $request_id The request ID for this personal data export. + */ + $content = apply_filters( 'wp_privacy_personal_data_email_content', $email_text, $request_id ); + + $email_address = $request->email; + $export_file_url = get_post_meta( $request_id, '_export_file_url', true ); + $site_name = wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES ); + $site_url = home_url(); + + $content = str_replace( '###EXPIRATION###', $expiration_date, $content ); + $content = str_replace( '###LINK###', esc_url_raw( $export_file_url ), $content ); + $content = str_replace( '###EMAIL###', $email_address, $content ); + $content = str_replace( '###SITENAME###', $site_name, $content ); + $content = str_replace( '###SITEURL###', esc_url_raw( $site_url ), $content ); + + $mail_success = wp_mail( + $email_address, + sprintf( + __( '[%s] Personal Data Export' ), + $site_name + ), + $content + ); + + if ( ! $mail_success ) { + return new WP_Error( 'error', __( 'Unable to send personal data export email.' ) ); + } + + return true; +} + +/** + * Intercept personal data exporter page ajax responses in order to assemble the personal data export file. + * @see wp_privacy_personal_data_export_page + * @since 4.9.6 + * + * @param array $response The response from the personal data exporter for the given page. + * @param int $exporter_index The index of the personal data exporter. Begins at 1. + * @param string $email_address The email address of the user whose personal data this is. + * @param int $page The page of personal data for this exporter. Begins at 1. + * @param int $request_id The request ID for this personal data export. + * @param bool $send_as_email Whether the final results of the export should be emailed to the user. + * @param string $exporter_key The slug (key) of the exporter. + * @return array The filtered response. + */ +function wp_privacy_process_personal_data_export_page( $response, $exporter_index, $email_address, $page, $request_id, $send_as_email, $exporter_key ) { + /* Do some simple checks on the shape of the response from the exporter. + * If the exporter response is malformed, don't attempt to consume it - let it + * pass through to generate a warning to the user by default ajax processing. + */ + if ( ! is_array( $response ) ) { + return $response; + } + + if ( ! array_key_exists( 'done', $response ) ) { + return $response; + } + + if ( ! array_key_exists( 'data', $response ) ) { + return $response; + } + + if ( ! is_array( $response['data'] ) ) { + return $response; + } + + // Get the request data. + $request = wp_get_user_request_data( $request_id ); + + if ( ! $request || 'export_personal_data' !== $request->action_name ) { + wp_send_json_error( __( 'Invalid request ID when merging exporter data.' ) ); + } + + $export_data = array(); + + // First exporter, first page? Reset the report data accumulation array. + if ( 1 === $exporter_index && 1 === $page ) { + update_post_meta( $request_id, '_export_data_raw', $export_data ); + } else { + $export_data = get_post_meta( $request_id, '_export_data_raw', true ); + } + + // Now, merge the data from the exporter response into the data we have accumulated already. + $export_data = array_merge( $export_data, $response['data'] ); + update_post_meta( $request_id, '_export_data_raw', $export_data ); + + // If we are not yet on the last page of the last exporter, return now. + /** This filter is documented in wp-admin/includes/ajax-actions.php */ + $exporters = apply_filters( 'wp_privacy_personal_data_exporters', array() ); + $is_last_exporter = $exporter_index === count( $exporters ); + $exporter_done = $response['done']; + if ( ! $is_last_exporter || ! $exporter_done ) { + return $response; + } + + // Last exporter, last page - let's prepare the export file. + + // First we need to re-organize the raw data hierarchically in groups and items. + $groups = array(); + foreach ( (array) $export_data as $export_datum ) { + $group_id = $export_datum['group_id']; + $group_label = $export_datum['group_label']; + if ( ! array_key_exists( $group_id, $groups ) ) { + $groups[ $group_id ] = array( + 'group_label' => $group_label, + 'items' => array(), + ); + } + + $item_id = $export_datum['item_id']; + if ( ! array_key_exists( $item_id, $groups[ $group_id ]['items'] ) ) { + $groups[ $group_id ]['items'][ $item_id ] = array(); + } + + $old_item_data = $groups[ $group_id ]['items'][ $item_id ]; + $merged_item_data = array_merge( $export_datum['data'], $old_item_data ); + $groups[ $group_id ]['items'][ $item_id ] = $merged_item_data; + } + + // Then save the grouped data into the request. + delete_post_meta( $request_id, '_export_data_raw' ); + update_post_meta( $request_id, '_export_data_grouped', $groups ); + + /** + * Generate the export file from the collected, grouped personal data. + * + * @since 4.9.6 + * + * @param int $request_id The export request ID. + */ + do_action( 'wp_privacy_personal_data_export_file', $request_id ); + + // Clear the grouped data now that it is no longer needed. + delete_post_meta( $request_id, '_export_data_grouped' ); + + // If the destination is email, send it now. + if ( $send_as_email ) { + $mail_success = wp_privacy_send_personal_data_export_email( $request_id ); + if ( is_wp_error( $mail_success ) ) { + wp_send_json_error( $mail_success->get_error_message() ); + } + } else { + // Modify the response to include the URL of the export file so the browser can fetch it. + $export_file_url = get_post_meta( $request_id, '_export_file_url', true ); + if ( ! empty( $export_file_url ) ) { + $response['url'] = $export_file_url; + } + } + + // Update the request to completed state. + _wp_privacy_completed_request( $request_id ); + + return $response; +} diff --git a/src/wp-admin/includes/image.php b/src/wp-admin/includes/image.php index 8282499e8fae7..d33f7539f9d60 100644 --- a/src/wp-admin/includes/image.php +++ b/src/wp-admin/includes/image.php @@ -241,7 +241,7 @@ function wp_generate_attachment_metadata( $attachment_id, $file ) { $sizes[ $s ]['crop'] = $_wp_additional_image_sizes[ $s ]['crop']; } else { // Force thumbnails to be soft crops. - if ( ! 'thumbnail' === $s ) { + if ( 'thumbnail' !== $s ) { $sizes[ $s ]['crop'] = get_option( "{$s}_crop" ); } } @@ -343,7 +343,7 @@ function wp_read_image_metadata( $file ) { if ( ! file_exists( $file ) ) return false; - list( , , $sourceImageType ) = getimagesize( $file ); + list( , , $image_type ) = @getimagesize( $file ); /* * EXIF contains a bunch of data we'll probably never need formatted in ways @@ -372,10 +372,10 @@ function wp_read_image_metadata( $file ) { * as caption, description etc. */ if ( is_callable( 'iptcparse' ) ) { - getimagesize( $file, $info ); + @getimagesize( $file, $info ); if ( ! empty( $info['APP13'] ) ) { - $iptc = iptcparse( $info['APP13'] ); + $iptc = @iptcparse( $info['APP13'] ); // Headline, "A brief synopsis of the caption." if ( ! empty( $iptc['2#105'][0] ) ) { @@ -420,6 +420,8 @@ function wp_read_image_metadata( $file ) { } } + $exif = array(); + /** * Filters the image types to check for exif data. * @@ -427,7 +429,9 @@ function wp_read_image_metadata( $file ) { * * @param array $image_types Image types to check for exif data. */ - if ( is_callable( 'exif_read_data' ) && in_array( $sourceImageType, apply_filters( 'wp_read_image_metadata_types', array( IMAGETYPE_JPEG, IMAGETYPE_TIFF_II, IMAGETYPE_TIFF_MM ) ) ) ) { + $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 ) ) { $exif = @exif_read_data( $file ); if ( ! empty( $exif['ImageDescription'] ) ) { @@ -505,13 +509,15 @@ function wp_read_image_metadata( $file ) { * * @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 $sourceImageType Type of image. - * @param array $iptc IPTC data. + * @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, $sourceImageType, $iptc ); + return apply_filters( 'wp_read_image_metadata', $meta, $file, $image_type, $iptc, $exif ); } diff --git a/src/wp-admin/includes/media.php b/src/wp-admin/includes/media.php index 2ee607d2d56ca..aa9b7dd6fd633 100644 --- a/src/wp-admin/includes/media.php +++ b/src/wp-admin/includes/media.php @@ -355,7 +355,7 @@ function media_handle_upload($file_id, $post_id, $post_data = array(), $override } // Use image exif/iptc data for title and caption defaults if possible. - } elseif ( 0 === strpos( $type, 'image/' ) && $image_meta = @wp_read_image_metadata( $file ) ) { + } elseif ( 0 === strpos( $type, 'image/' ) && $image_meta = wp_read_image_metadata( $file ) ) { if ( trim( $image_meta['title'] ) && ! is_numeric( sanitize_title( $image_meta['title'] ) ) ) { $title = $image_meta['title']; } @@ -419,7 +419,7 @@ function media_handle_sideload( $file_array, $post_id, $desc = null, $post_data $content = ''; // Use image exif/iptc data for title and caption defaults if possible. - if ( $image_meta = @wp_read_image_metadata($file) ) { + if ( $image_meta = wp_read_image_metadata( $file ) ) { if ( trim( $image_meta['title'] ) && ! is_numeric( sanitize_title( $image_meta['title'] ) ) ) $title = $image_meta['title']; if ( trim( $image_meta['caption'] ) ) diff --git a/src/wp-admin/includes/meta-boxes.php b/src/wp-admin/includes/meta-boxes.php index 605242df8ebcb..a8cfd161d7565 100644 --- a/src/wp-admin/includes/meta-boxes.php +++ b/src/wp-admin/includes/meta-boxes.php @@ -903,7 +903,7 @@ function page_attributes_meta_box($post) { /** * Fires before the help hint text in the 'Page Attributes' meta box. * - * @since 4.8.0 + * @since 4.9.0 * * @param WP_Post $post The current post. */ diff --git a/src/wp-admin/includes/misc.php b/src/wp-admin/includes/misc.php index 7f5952c895ef1..3c414a857a567 100644 --- a/src/wp-admin/includes/misc.php +++ b/src/wp-admin/includes/misc.php @@ -77,12 +77,12 @@ function extract_from_markers( $filename, $marker ) { foreach ( $markerdata as $markerline ) { if ( false !== strpos( $markerline, '# END ' . $marker ) ) { $state = false; - if ( $state ) { - $result[] = $markerline; - } - if ( false !== strpos( $markerline, '# BEGIN ' . $marker ) ) { - $state = true; - } + } + if ( $state ) { + $result[] = $markerline; + } + if ( false !== strpos( $markerline, '# BEGIN ' . $marker ) ) { + $state = true; } } @@ -194,6 +194,8 @@ function insert_with_markers( $filename, $marker, $insertion ) { * @since 1.5.0 * * @global WP_Rewrite $wp_rewrite + * + * @return bool|null True on write success, false on failure. Null in multisite. */ function save_mod_rewrite_rules() { if ( is_multisite() ) @@ -201,8 +203,11 @@ function save_mod_rewrite_rules() { global $wp_rewrite; - $home_path = get_home_path(); - $htaccess_file = $home_path.'.htaccess'; + // Ensure get_home_path() is declared. + require_once( ABSPATH . 'wp-admin/includes/file.php' ); + + $home_path = get_home_path(); + $htaccess_file = $home_path . '.htaccess'; /* * If the file doesn't already exist check for write access to the directory @@ -226,7 +231,7 @@ function save_mod_rewrite_rules() { * * @global WP_Rewrite $wp_rewrite * - * @return bool True if web.config was updated successfully + * @return bool|null True on write success, false on failure. Null in multisite. */ function iis7_save_url_rewrite_rules(){ if ( is_multisite() ) @@ -234,7 +239,10 @@ function iis7_save_url_rewrite_rules(){ global $wp_rewrite; - $home_path = get_home_path(); + // Ensure get_home_path() is declared. + require_once( ABSPATH . 'wp-admin/includes/file.php' ); + + $home_path = get_home_path(); $web_config_file = $home_path . 'web.config'; // Using win_is_writable() instead of is_writable() because of a bug in Windows PHP @@ -598,6 +606,8 @@ function set_screen_options() { 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': @@ -1099,15 +1109,17 @@ function wp_admin_canonical_url() { * @since 4.9.0 */ function wp_admin_headers() { - $policy = 'same-origin'; + $policy = 'strict-origin-when-cross-origin'; /** - * Filters the admin referrer policy header value. Default 'same-origin'. + * Filters the admin referrer policy header value. * * @since 4.9.0 + * @since 4.9.5 The default value was changed to 'strict-origin-when-cross-origin'. + * * @link https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy * - * @param string $policy The referrer policy header value. + * @param string $policy The admin referrer policy header value. Default 'strict-origin-when-cross-origin'. */ $policy = apply_filters( 'admin_referrer_policy', $policy ); @@ -1148,7 +1160,7 @@ function update_option_new_admin_email( $old_value, $value ) { return; } - $hash = md5( $value . time() . mt_rand() ); + $hash = md5( $value . time() . wp_rand() ); $new_admin_email = array( 'hash' => $hash, 'newemail' => $value, @@ -1211,3 +1223,652 @@ function update_option_new_admin_email( $old_value, $value ) { 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; +} + +/** + * WP_Privacy_Policy_Content class. + * TODO: move this to a new file. + * + * @since 4.9.6 + */ +final class WP_Privacy_Policy_Content { + + private static $policy_content = array(); + + /** + * Constructor + * + * @since 4.9.6 + */ + private function __construct() {} + + /** + * Add content to the postbox shown when editing the privacy policy. + * + * Plugins and themes should suggest text for inclusion in the site's privacy policy. + * The suggested text should contain information about any functionality that affects user privacy, + * and will be shown in the Suggested Privacy Policy Content postbox. + * + * Intended for use from `wp_add_privacy_policy_content()`. + * + * @since 4.9.6 + * + * @param string $plugin_name The name of the plugin or theme that is suggesting content for the site's privacy policy. + * @param string $policy_text The suggested content for inclusion in the policy. + */ + public static function add( $plugin_name, $policy_text ) { + if ( empty( $plugin_name ) || empty( $policy_text ) ) { + return; + } + + $data = array( + 'plugin_name' => $plugin_name, + 'policy_text' => $policy_text, + ); + + if ( ! in_array( $data, self::$policy_content, true ) ) { + self::$policy_content[] = $data; + } + } + + /** + * Quick check if any privacy info has changed. + * + * @since 4.9.6 + */ + public static function text_change_check() { + + $policy_page_id = (int) get_option( 'wp_page_for_privacy_policy' ); + + // The site doesn't have a privacy policy. + if ( empty( $policy_page_id ) ) { + return false; + } + + if ( ! current_user_can( 'edit_post', $policy_page_id ) ) { + return false; + } + + $old = (array) get_post_meta( $policy_page_id, '_wp_suggested_privacy_policy_content' ); + + // Updates are not relevant if the user has not reviewed any suggestions yet. + if ( empty( $old ) ) { + return false; + } + + $cached = get_option( '_wp_suggested_policy_text_has_changed' ); + + /* + * When this function is called before `admin_init`, `self::$policy_content` + * has not been populated yet, so use the cached result from the last + * execution instead. + */ + if ( ! did_action( 'admin_init' ) ) { + return 'changed' === $cached; + } + + $new = self::$policy_content; + + // Remove the extra values added to the meta. + foreach ( $old as $key => $data ) { + if ( ! empty( $data['removed'] ) ) { + unset( $old[ $key ] ); + continue; + } + + $old[ $key ] = array( + 'plugin_name' => $data['plugin_name'], + 'policy_text' => $data['policy_text'], + ); + } + + // Normalize the order of texts, to facilitate comparison. + sort( $old ); + sort( $new ); + + // The == operator (equal, not identical) was used intentionally. + // See http://php.net/manual/en/language.operators.array.php + if ( $new != $old ) { + // A plugin was activated or deactivated, or some policy text has changed. + // Show a notice on the relevant screens to inform the admin. + add_action( 'admin_notices', array( 'WP_Privacy_Policy_Content', 'policy_text_changed_notice' ) ); + $state = 'changed'; + } else { + $state = 'not-changed'; + } + + // Cache the result for use before `admin_init` (see above). + if ( $cached !== $state ) { + update_option( '_wp_suggested_policy_text_has_changed', $state ); + } + + return 'changed' === $state; + } + + /** + * Output a warning when some privacy info has changed. + * + * @since 4.9.6 + */ + public static function policy_text_changed_notice() { + global $post; + + $screen = get_current_screen()->id; + + if ( 'privacy' !== $screen ) { + return; + } + + ?> +review the guide and update your privacy policy.' ), + esc_url( admin_url( 'tools.php?wp-privacy-policy-guide=1' ) ) + ); + ?>
++ Check out our guide%3$s for recommendations on what content to include, along with policies suggested by your plugins and theme.' ), + admin_url( 'tools.php?wp-privacy-policy-guide=1' ), + 'target="_blank"', + sprintf( + ' %s', + /* translators: accessibility text */ + __( '(opens in a new tab)' ) + ) + ); + ?> +
+' . sprintf( $removed, $date ) . '
'; + } + + $plugin_name = esc_html( $section['plugin_name'] ); + $toc_id = 'wp-privacy-policy-guide-' . sanitize_title( $plugin_name ); + $toc[] = sprintf( '
' . sprintf( __( 'Source: %s' ), $plugin_name ) . '
'; + $content .= $removed; + + $content .= '-
+
+
' . __( 'Who we are' ) . '
'; + + if ( $description ) { + $strings[] = '' . __( 'In this section you should note your site URL, as well as the name of the company, organization, or individual behind it, and some accurate contact information.' ) . '
'; + $strings[] = '' . __( 'The amount of information you may be required to show will vary depending on your local or national business regulations. You may, for example, be required to display a physical address, a registered address, or your company registration number.' ) . '
'; + } + + /* translators: %s Site URL */ + $strings[] = '' . $suggested_text . sprintf( __( 'Our website address is: %s.' ), get_bloginfo( 'url', 'display' ) ) . '
'; + + $strings[] = '' . __( 'What personal data we collect and why we collect it' ) . '
'; + + if ( $description ) { + $strings[] = '' . __( 'In this section you should note what personal data you collect from users and site visitors. This may include personal data, such as name, email address, personal account preferences; transactional data, such as purchase information; and technical data, such as information about cookies.' ) . '
'; + $strings[] = '' . __( 'You should also note any collection and retention of sensitive personal data, such as data concerning health.' ) . '
'; + $strings[] = '' . __( 'In addition to listing what personal data you collect, you need to note why you collect it. These explanations must note either the legal basis for your data collection and retention or the active consent the user has given.' ) . '
'; + $strings[] = '' . __( 'Personal data is not just created by a user’s interactions with your site. Personal data is also generated from technical processes such as contact forms, comments, cookies, analytics, and third party embeds.' ) . '
'; + $strings[] = '' . __( 'By default WordPress does not collect any personal data about visitors, and only collects the data shown on the User Profile screen from registered users. However some of your plugins may collect personal data. You should add the relevant information below.' ) . '
'; + } + + $strings[] = '' . __( 'Comments' ) . '
'; + + if ( $description ) { + $strings[] = '' . __( 'In this subsection you should note what information is captured through comments. We have noted the data which WordPress collects by default.' ) . '
'; + } + + $strings[] = '' . $suggested_text . __( 'When visitors leave comments on the site we collect the data shown in the comments form, and also the visitor’s IP address and browser user agent string to help spam detection.' ) . '
'; + $strings[] = '' . __( 'An anonymized string created from your email address (also called a hash) may be provided to the Gravatar service to see if you are using it. The Gravatar service privacy policy is available here: https://automattic.com/privacy/. After approval of your comment, your profile picture is visible to the public in the context of your comment.' ) . '
'; + $strings[] = '' . __( 'Media' ) . '
'; + + if ( $description ) { + $strings[] = '' . __( 'In this subsection you should note what information may be disclosed by users who can upload media files. All uploaded files are usually publicly accessible.' ) . '
'; + } + + $strings[] = '' . $suggested_text . __( 'If you upload images to the website, you should avoid uploading images with embedded location data (EXIF GPS) included. Visitors to the website can download and extract any location data from images on the website.' ) . '
'; + $strings[] = '' . __( 'Contact forms' ) . '
'; + + if ( $description ) { + $strings[] = '' . __( 'By default, WordPress does not include a contact form. If you use a contact form plugin, use this subsection to note what personal data is captured when someone submits a contact form, and how long you keep it. For example, you may note that you keep contact form submissions for a certain period for customer service purposes, but you do not use the information submitted through them for marketing purposes.' ) . '
'; + } + + $strings[] = '' . __( 'Cookies' ) . '
'; + + if ( $description ) { + $strings[] = '' . __( 'In this subsection you should list the cookies your web site uses, including those set by your plugins, social media, and analytics. We have provided the cookies which WordPress installs by default.' ) . '
'; + } + + $strings[] = '' . $suggested_text . __( 'If you leave a comment on our site you may opt-in to saving your name, email address and website in cookies. These are for your convenience so that you do not have to fill in your details again when you leave another comment. These cookies will last for one year.' ) . '
'; + $strings[] = '' . __( 'If you have an account and you log in to this site, we will set a temporary cookie to determine if your browser accepts cookies. This cookie contains no personal data and is discarded when you close your browser.' ) . '
'; + $strings[] = '' . __( 'When you log in, we will also set up several cookies to save your login information and your screen display choices. Login cookies last for two days, and screen options cookies last for a year. If you select "Remember Me", your login will persist for two weeks. If you log out of your account, the login cookies will be removed.' ) . '
'; + $strings[] = '' . __( 'If you edit or publish an article, an additional cookie will be saved in your browser. This cookie includes no personal data and simply indicates the post ID of the article you just edited. It expires after 1 day.' ) . '
'; + + $strings[] = '' . __( 'Embedded content from other websites' ) . '
'; + $strings[] = '' . $suggested_text . __( 'Articles on this site may include embedded content (e.g. videos, images, articles, etc.). Embedded content from other websites behaves in the exact same way as if the visitor has visited the other website.' ) . '
'; + $strings[] = '' . __( 'These websites may collect data about you, use cookies, embed additional third-party tracking, and monitor your interaction with that embedded content, including tracking your interaction with the embedded content if you have an account and are logged in to that website.' ) . '
'; + + $strings[] = '' . __( 'Analytics' ) . '
'; + + if ( $description ) { + $strings[] = '' . __( 'In this subsection you should note what analytics package you use, how users can opt out of analytics tracking, and a link to your analytics provider’s privacy policy, if any.' ) . '
'; + $strings[] = '' . __( 'By default WordPress does not collect any analytics data. However, many web hosting accounts collect some anonymous analytics data. You may also have installed a WordPress plugin that provides analytics services. In that case, add information from that plugin here.' ) . '
'; + } + + $strings[] = '' . __( 'Who we share your data with' ) . '
'; + + if ( $description ) { + $strings[] = '' . __( 'In this section you should name and list all third party providers with whom you share site data, including partners, cloud-based services, payment processors, and third party service providers, and note what data you share with them and why. Link to their own privacy policies if possible.' ) . '
'; + $strings[] = '' . __( 'By default WordPress does not share any personal data with anyone.' ) . '
'; + } + + $strings[] = '' . __( 'How long we retain your data' ) . '
'; + + if ( $description ) { + $strings[] = '' . __( 'In this section you should explain how long you retain personal data collected or processed by the web site. While it is your responsibility to come up with the schedule of how long you keep each dataset for and why you keep it, that information does need to be listed here. For example, you may want to say that you keep contact form entries for six months, analytics records for a year, and customer purchase records for ten years.' ) . '
'; + } + + $strings[] = '' . $suggested_text . __( 'If you leave a comment, the comment and its metadata are retained indefinitely. This is so we can recognize and approve any follow-up comments automatically instead of holding them in a moderation queue.' ) . '
'; + $strings[] = '' . __( 'For users that register on our website (if any), we also store the personal information they provide in their user profile. All users can see, edit, or delete their personal information at any time (except they cannot change their username). Website administrators can also see and edit that information.' ) . '
'; + + $strings[] = '' . __( 'What rights you have over your data' ) . '
'; + + if ( $description ) { + $strings[] = '' . __( 'In this section you should explain what rights your users have over their data and how they can invoke those rights.' ) . '
'; + } + + $strings[] = '' . $suggested_text . __( 'If you have an account on this site, or have left comments, you can request to receive an exported file of the personal data we hold about you, including any data you have provided to us. You can also request that we erase any personal data we hold about you. This does not include any data we are obliged to keep for administrative, legal, or security purposes.' ) . '
'; + + $strings[] = '' . __( 'Where we send your data' ) . '
'; + + if ( $description ) { + $strings[] = '' . __( 'In this section you should list all transfers of your site data outside the European Union and describe the means by which that data is safeguarded to European data protection standards. This could include your web hosting, cloud storage, or other third party services.' ) . '
'; + $strings[] = '' . __( 'European data protection law requires data about European residents which is transferred outside the European Union to be safeguarded to the same standards as if the data was in Europe. So in addition to listing where data goes, you should describe how you ensure that these standards are met either by yourself or by your third party providers, whether that is through an agreement such as Privacy Shield, model clauses in your contracts, or binding corporate rules.' ) . '
'; + } + + $strings[] = '' . $suggested_text . __( 'Visitor comments may be checked through an automated spam detection service.' ) . '
'; + + $strings[] = '' . __( 'Your contact information' ) . '
'; + + if ( $description ) { + $strings[] = '' . __( 'In this section you should provide a contact method for privacy-specific concerns. If you are required to have a Data Protection Officer, list their name and full contact details here as well.' ) . '
'; + } + + $strings[] = '' . __( 'Additional information' ) . '
'; + + if ( $description ) { + $strings[] = '' . __( 'If you use your site for commercial purposes and you engage in more complex collection or processing of personal data, you should note the following information in your privacy policy in addition to the information we have already discussed.' ) . '
'; + } + + $strings[] = '' . __( 'How we protect your data' ) . '
'; + + if ( $description ) { + $strings[] = '' . __( 'In this section you should explain what measures you have taken to protect your users’ data. This could include technical measures such as encryption; security measures such as two factor authentication; and measures such as staff training in data protection. If you have carried out a Privacy Impact Assessment, you can mention it here too.' ) . '
'; + } + + $strings[] = '' . __( 'What data breach procedures we have in place' ) . '
'; + + if ( $description ) { + $strings[] = '' . __( 'In this section you should explain what procedures you have in place to deal with data breaches, either potential or real, such as internal reporting systems, contact mechanisms, or bug bounties.' ) . '
'; + } + + $strings[] = '' . __( 'What third parties we receive data from' ) . '
'; + + if ( $description ) { + $strings[] = '' . __( 'If your web site receives data about users from third parties, including advertisers, this information must be included within the section of your privacy policy dealing with third party data.' ) . '
'; + } + + $strings[] = '' . __( 'What automated decision making and/or profiling we do with user data' ) . '
'; + + if ( $description ) { + $strings[] = '' . __( 'If your web site provides a service which includes automated decision making - for example, allowing customers to apply for credit, or aggregating their data into an advertising profile - you must note that this is taking place, and include information about how that information is used, what decisions are made with that aggregated data, and what rights users have over decisions made without human intervention.' ) . '
'; + } + + $strings[] = '' . __( 'Industry regulatory disclosure requirements' ) . '
'; + + if ( $description ) { + $strings[] = '' . __( 'If you are a member of a regulated industry, or if you are subject to additional privacy laws, you may be required to disclose that information here.' ) . '
'; + $strings[] = '' ) ) { + $strings[ $key ] = '' . $string . ''; + } + + if ( 0 === strpos( $string, '
' ) ) {
+ $strings[ $key ] = '' . $string . '';
+ }
+
+ if ( 0 === strpos( $string, '' ) ) {
+ $strings[ $key ] = '' . $string . '';
+ }
+ }
+ }
+
+ $content = implode( '', $strings );
+ // End of the suggested privacy policy text.
+
+ /**
+ * Filters the default content suggested for inclusion in a privacy policy.
+ *
+ * @since 4.9.6
+ * @since 5.0.0 Added the `$strings`, `$description`, and `$blocks` parameters.
+ *
+ * @param $content string The default policy content.
+ * @param $strings array An array of privacy policy content strings.
+ * @param $description bool Whether policy descriptions should be included.
+ * @param $blocks bool Whether the content should be formatted for the block editor.
+ */
+ return apply_filters( 'wp_get_default_privacy_policy_content', $content, $strings, $description, $blocks );
+ }
+
+ /**
+ * Add the suggested privacy policy text to the policy postbox.
+ *
+ * @since 4.9.6
+ */
+ public static function add_suggested_content() {
+ $content = self::get_default_content( true, false );
+ wp_add_privacy_policy_content( __( 'WordPress' ), $content );
+ }
+}
diff --git a/src/wp-admin/includes/options.php b/src/wp-admin/includes/options.php
index 053d5eec6425a..f011efd312369 100644
--- a/src/wp-admin/includes/options.php
+++ b/src/wp-admin/includes/options.php
@@ -51,7 +51,7 @@ function options_general_add_js() {
$("input[name='date_format']").click(function(){
if ( "date_format_custom_radio" != $(this).attr("id") )
- $( "input[name='date_format_custom']" ).val( $( this ).val() ).siblings( '.example' ).text( $( this ).parent( 'label' ).children( '.format-i18n' ).text() );
+ $( 'input[name="date_format_custom"]' ).val( $( this ).val() ).closest( 'fieldset' ).find( '.example' ).text( $( this ).parent( 'label' ).children( '.format-i18n' ).text() );
});
$( 'input[name="date_format_custom"]' ).on( 'click input', function() {
$( '#date_format_custom_radio' ).prop( 'checked', true );
@@ -59,18 +59,23 @@ function options_general_add_js() {
$("input[name='time_format']").click(function(){
if ( "time_format_custom_radio" != $(this).attr("id") )
- $( "input[name='time_format_custom']" ).val( $( this ).val() ).siblings( '.example' ).text( $( this ).parent( 'label' ).children( '.format-i18n' ).text() );
+ $( 'input[name="time_format_custom"]' ).val( $( this ).val() ).closest( 'fieldset' ).find( '.example' ).text( $( this ).parent( 'label' ).children( '.format-i18n' ).text() );
});
$( 'input[name="time_format_custom"]' ).on( 'click input', function() {
$( '#time_format_custom_radio' ).prop( 'checked', true );
});
$("input[name='date_format_custom'], input[name='time_format_custom']").change( function() {
- var format = $(this);
- format.siblings( '.spinner' ).addClass( 'is-active' );
- $.post(ajaxurl, {
- action: 'date_format_custom' == format.attr('name') ? 'date_format' : 'time_format',
+ var format = $( this ),
+ fieldset = format.closest( 'fieldset' ),
+ example = fieldset.find( '.example' ),
+ spinner = fieldset.find( '.spinner' );
+
+ spinner.addClass( 'is-active' );
+
+ $.post( ajaxurl, {
+ action: 'date_format_custom' == format.attr( 'name' ) ? 'date_format' : 'time_format',
date : format.val()
- }, function(d) { format.siblings( '.spinner' ).removeClass( 'is-active' ); format.siblings('.example').text(d); } );
+ }, function( d ) { spinner.removeClass( 'is-active' ); example.text( d ); } );
});
var languageSelect = $( '#WPLANG' );
diff --git a/src/wp-admin/includes/plugin-install.php b/src/wp-admin/includes/plugin-install.php
index 1714acda71f4f..eea74f1aeb072 100644
--- a/src/wp-admin/includes/plugin-install.php
+++ b/src/wp-admin/includes/plugin-install.php
@@ -584,12 +584,20 @@ function install_plugin_information() {
admin_init'
+ ),
+ '4.9.7'
+ );
+ return;
+ } elseif ( ! doing_action( 'admin_init' ) && ! did_action( 'admin_init' ) ) {
+ _doing_it_wrong(
+ __FUNCTION__,
+ sprintf(
+ /* translators: %s: admin_init */
+ __( 'The suggested privacy policy content should be added by using the %s (or later) action. Please see the inline documentation.' ),
+ 'admin_init'
+ ),
+ '4.9.7'
+ );
+ return;
+ }
+
+ if ( ! class_exists( 'WP_Privacy_Policy_Content' ) ) {
+ require_once( ABSPATH . 'wp-admin/includes/misc.php' );
+ }
+
+ WP_Privacy_Policy_Content::add( $plugin_name, $policy_text );
+}
diff --git a/src/wp-admin/includes/post.php b/src/wp-admin/includes/post.php
index 908b80c333085..a95ff78bc6967 100644
--- a/src/wp-admin/includes/post.php
+++ b/src/wp-admin/includes/post.php
@@ -814,7 +814,7 @@ function add_meta( $post_ID ) {
if ( is_string( $metavalue ) )
$metavalue = trim( $metavalue );
- if ( ('0' === $metavalue || ! empty ( $metavalue ) ) && ( ( ( '#NONE#' != $metakeyselect ) && !empty ( $metakeyselect) ) || !empty ( $metakeyinput ) ) ) {
+ if ( ( ( '#NONE#' != $metakeyselect ) && ! empty( $metakeyselect ) ) || ! empty( $metakeyinput ) ) {
/*
* We have a key/value pair. If both the select and the input
* for the key have data, the input takes precedence.
@@ -1147,6 +1147,10 @@ function wp_edit_attachments_query_vars( $q = false ) {
$q['post_parent'] = 0;
}
+ if ( isset( $q['mine'] ) || ( isset( $q['attachment-filter'] ) && 'mine' == $q['attachment-filter'] ) ) {
+ $q['author'] = get_current_user_id();
+ }
+
// Filter query clauses to include filenames.
if ( isset( $q['s'] ) ) {
add_filter( 'posts_clauses', '_filter_query_attachment_filenames' );
@@ -1870,3 +1874,289 @@ function redirect_post($post_id = '') {
wp_redirect( apply_filters( 'redirect_post_location', $location, $post_id ) );
exit;
}
+
+/**
+ * Return whether the post can be edited in the block editor.
+ *
+ * @since 5.0.0
+ *
+ * @param int|WP_Post $post Post ID or WP_Post object.
+ * @return bool Whether the post can be edited in the block editor.
+ */
+function use_block_editor_for_post( $post ) {
+ $post = get_post( $post );
+
+ if ( ! $post ) {
+ return false;
+ }
+
+ // We're in the meta box loader, so don't use the block editor.
+ if ( isset( $_GET['meta-box-loader'] ) ) {
+ check_admin_referer( 'meta-box-loader' );
+ return false;
+ }
+
+ $use_block_editor = use_block_editor_for_post_type( $post->post_type );
+
+ /**
+ * Filter whether a post is able to be edited in the block editor.
+ *
+ * @since 5.0.0
+ *
+ * @param bool $use_block_editor Whether the post can be edited or not.
+ * @param WP_Post $post The post being checked.
+ */
+ return apply_filters( 'use_block_editor_for_post', $use_block_editor, $post );
+}
+
+/**
+ * Return whether a post type is compatible with the block editor.
+ *
+ * The block editor depends on the REST API, and if the post type is not shown in the
+ * REST API, then it won't work with the block editor.
+ *
+ * @since 5.0.0
+ *
+ * @param string $post_type The post type.
+ * @return bool Whether the post type can be edited with the block editor.
+ */
+function use_block_editor_for_post_type( $post_type ) {
+ if ( ! post_type_exists( $post_type ) ) {
+ return false;
+ }
+
+ if ( ! post_type_supports( $post_type, 'editor' ) ) {
+ return false;
+ }
+
+ $post_type_object = get_post_type_object( $post_type );
+ if ( $post_type_object && ! $post_type_object->show_in_rest ) {
+ return false;
+ }
+
+ /**
+ * Filter whether a post is able to be edited in the block editor.
+ *
+ * @since 5.0.0
+ *
+ * @param bool $use_block_editor Whether the post type can be edited or not. Default true.
+ * @param string $post_type The post type being checked.
+ */
+ return apply_filters( 'use_block_editor_for_post_type', true, $post_type );
+}
+
+/**
+ * Returns all the block categories that will be shown in the block editor.
+ *
+ * @since 5.0.0
+ *
+ * @param WP_Post $post Post object.
+ * @return array Array of block categories.
+ */
+function get_block_categories( $post ) {
+ $default_categories = array(
+ array(
+ 'slug' => 'common',
+ 'title' => __( 'Common Blocks' ),
+ ),
+ array(
+ 'slug' => 'formatting',
+ 'title' => __( 'Formatting' ),
+ ),
+ array(
+ 'slug' => 'layout',
+ 'title' => __( 'Layout Elements' ),
+ ),
+ array(
+ 'slug' => 'widgets',
+ 'title' => __( 'Widgets' ),
+ ),
+ array(
+ 'slug' => 'embed',
+ 'title' => __( 'Embeds' ),
+ ),
+ array(
+ 'slug' => 'reusable',
+ 'title' => __( 'Reusable Blocks' ),
+ ),
+ );
+
+ /**
+ * Filter the default array of block categories.
+ *
+ * @since 5.0.0
+ *
+ * @param array $default_categories Array of block categories.
+ * @param WP_Post $post Post being loaded.
+ */
+ return apply_filters( 'block_categories', $default_categories, $post );
+}
+
+/**
+ * Prepares server-registered blocks for the block editor.
+ *
+ * Returns an associative array of registered block data keyed by block name. Data includes properties
+ * of a block relevant for client registration.
+ *
+ * @since 5.0.0
+ *
+ * @return array An associative array of registered block data.
+ */
+function get_block_editor_server_block_settings() {
+ $block_registry = WP_Block_Type_Registry::get_instance();
+ $blocks = array();
+ $keys_to_pick = array( 'title', 'description', 'icon', 'category', 'keywords', 'supports', 'attributes' );
+
+ foreach ( $block_registry->get_all_registered() as $block_name => $block_type ) {
+ foreach ( $keys_to_pick as $key ) {
+ if ( ! isset( $block_type->{ $key } ) ) {
+ continue;
+ }
+
+ if ( ! isset( $blocks[ $block_name ] ) ) {
+ $blocks[ $block_name ] = array();
+ }
+
+ $blocks[ $block_name ][ $key ] = $block_type->{ $key };
+ }
+ }
+
+ return $blocks;
+}
+
+/**
+ * Renders the meta boxes forms.
+ *
+ * @since 5.0.0
+ */
+function the_block_editor_meta_boxes() {
+ global $post, $current_screen, $wp_meta_boxes;
+
+ // Handle meta box state.
+ $_original_meta_boxes = $wp_meta_boxes;
+
+ /**
+ * Fires right before the meta boxes are rendered.
+ *
+ * This allows for the filtering of meta box data, that should already be
+ * present by this point. Do not use as a means of adding meta box data.
+ *
+ * @since 5.0.0
+ *
+ * @param array $wp_meta_boxes Global meta box state.
+ */
+ $wp_meta_boxes = apply_filters( 'filter_block_editor_meta_boxes', $wp_meta_boxes );
+ $locations = array( 'side', 'normal', 'advanced' );
+ $priorities = array( 'high', 'sorted', 'core', 'default', 'low' );
+
+ // Render meta boxes.
+ ?>
+
+
+
+
+ id ][ $location ] ) ) {
+ continue;
+ }
+
+ foreach ( $priorities as $priority ) {
+ if ( ! isset( $wp_meta_boxes[ $current_screen->id ][ $location ][ $priority ] ) ) {
+ continue;
+ }
+
+ $meta_boxes = (array) $wp_meta_boxes[ $current_screen->id ][ $location ][ $priority ];
+ foreach ( $meta_boxes as $meta_box ) {
+ if ( false == $meta_box || ! $meta_box['title'] ) {
+ continue;
+ }
+
+ $meta_boxes_per_location[ $location ][] = array(
+ 'id' => $meta_box['id'],
+ 'title' => $meta_box['title'],
+ );
+ }
+ }
+ }
+
+ /**
+ * Sadly we probably can not add this data directly into editor settings.
+ *
+ * Some meta boxes need admin_head to fire for meta box registry.
+ * admin_head fires after admin_enqueue_scripts, which is where we create our
+ * editor instance.
+ */
+ $script = 'window._wpLoadBlockEditor.then( function() {
+ wp.data.dispatch( \'core/edit-post\' ).setAvailableMetaBoxesPerLocation( ' . wp_json_encode( $meta_boxes_per_location ) . ' );
+ } );';
+
+ wp_add_inline_script( 'wp-edit-post', $script );
+
+ /**
+ * When `wp-edit-post` is output in the ``, the inline script needs to be manually printed. Otherwise,
+ * meta boxes will not display because inline scripts for `wp-edit-post` will not be printed again after this point.
+ */
+ if ( wp_script_is( 'wp-edit-post', 'done' ) ) {
+ printf( "\n", trim( $script ) );
+ }
+
+ // Reset meta box data.
+ $wp_meta_boxes = $_original_meta_boxes;
+}
+
+/**
+ * Renders the hidden form required for the meta boxes form.
+ *
+ * @since 5.0.0
+ *
+ * @param WP_Post $post Current post object.
+ */
+function the_block_editor_meta_box_post_form_hidden_fields( $post ) {
+ $form_extra = '';
+ if ( 'auto-draft' === $post->post_status ) {
+ $form_extra .= "";
+ }
+ $form_action = 'editpost';
+ $nonce_action = 'update-post_' . $post->ID;
+ $form_extra .= "";
+ $referer = wp_get_referer();
+ $current_user = wp_get_current_user();
+ $user_id = $current_user->ID;
+ wp_nonce_field( $nonce_action );
+ ?>
+
+
+
+
+
+
+
+ 768,
'medium_large_size_h' => 0,
+
+ // 4.9.6
+ 'wp_page_for_privacy_policy' => 0,
+
+ // 4.9.8
+ 'show_comments_cookies_opt_in' => 0,
);
// 3.3
diff --git a/src/wp-admin/includes/template.php b/src/wp-admin/includes/template.php
index ec0fb525911f9..e51903e442da3 100644
--- a/src/wp-admin/includes/template.php
+++ b/src/wp-admin/includes/template.php
@@ -337,6 +337,16 @@ function get_inline_data($post) {
if ( post_type_supports( $post->post_type, 'post-formats' ) )
echo '' . __( 'Cheatin’ uh?' ) . '
' . + '' . __( 'Something went wrong.' ) . '
' . '' . __( 'One of the selected users is not a member of this site.' ) . '
', 403 ); diff --git a/src/wp-admin/options-discussion.php b/src/wp-admin/options-discussion.php index 5f57819d4f1d2..be8034f405699 100644 --- a/src/wp-admin/options-discussion.php +++ b/src/wp-admin/options-discussion.php @@ -77,6 +77,13 @@ ); ?>+ + +
+