From 6d4c88a0574509c3a6ab3d82d8c4fdaeb15eb2d2 Mon Sep 17 00:00:00 2001 From: Ian Dunn Date: Thu, 10 May 2018 19:51:58 +0000 Subject: [PATCH] Privacy: Replace intrusive policy update notice with menu bubbles. Previously, when a plugin updated its suggested privacy policy text, an admin notice was shown on all screens in the Administration Panels. That was done in order to make sure that administrators were aware of it, so that they could update their policy if needed. That was a very heavy-handed and intrusive approach, though, which leads to a poor user experience, and notice fatigue. An alternative approach is to use bubble notifications in the menu, similar to when plugins have updates that need to be installed. That still makes it obvious that something needs the administrator's attention, but is not as distracting as a notice. The notice will still appear on the Privacy page, though, since it is relevant to that screen, and provides an explanation of why the bubble is appearing. Props azaozz, xkon, iandunn. Fixes #43954. See #43953. git-svn-id: https://develop.svn.wordpress.org/trunk@43223 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-admin/css/forms.css | 2 +- src/wp-admin/includes/admin-filters.php | 4 +- src/wp-admin/includes/misc.php | 84 +++++++++++++++---------- src/wp-admin/menu.php | 11 +++- src/wp-includes/default-filters.php | 4 -- src/wp-includes/functions.php | 10 --- 6 files changed, 64 insertions(+), 51 deletions(-) diff --git a/src/wp-admin/css/forms.css b/src/wp-admin/css/forms.css index 2e47430f06..164878ac7e 100644 --- a/src/wp-admin/css/forms.css +++ b/src/wp-admin/css/forms.css @@ -1089,7 +1089,7 @@ table.form-table td .updated p { } .tools-privacy-edit { - margin: 2.3em 0; + margin: 1.5em 0; } .tools-privacy-policy-page span { diff --git a/src/wp-admin/includes/admin-filters.php b/src/wp-admin/includes/admin-filters.php index 68592b6d41..233f40f6dd 100644 --- a/src/wp-admin/includes/admin-filters.php +++ b/src/wp-admin/includes/admin-filters.php @@ -138,7 +138,7 @@ add_filter( 'wp_privacy_personal_data_export_page', 'wp_privacy_process_personal add_action( 'wp_privacy_personal_data_export_file', 'wp_privacy_generate_personal_data_export_file', 10 ); // Privacy policy text changes check. -add_action( 'admin_init', array( 'WP_Privacy_Policy_Content', 'text_change_check' ), 20 ); +add_action( 'admin_init', array( 'WP_Privacy_Policy_Content', 'text_change_check' ), 100 ); // Show a "postbox" with the text suggestions for a privacy policy. add_action( 'edit_form_after_title', array( 'WP_Privacy_Policy_Content', 'notice' ) ); @@ -146,5 +146,5 @@ add_action( 'edit_form_after_title', array( 'WP_Privacy_Policy_Content', 'notice // Add the suggested policy text from WordPress. add_action( 'admin_init', array( 'WP_Privacy_Policy_Content', 'add_suggested_content' ), 1 ); -// Stop checking for text changes after the policy page is updated. +// Update the cached policy info when the policy page is updated. add_action( 'post_updated', array( 'WP_Privacy_Policy_Content', '_policy_page_updated' ) ); diff --git a/src/wp-admin/includes/misc.php b/src/wp-admin/includes/misc.php index ff8e78a98e..e9d7641595 100644 --- a/src/wp-admin/includes/misc.php +++ b/src/wp-admin/includes/misc.php @@ -1320,69 +1320,94 @@ final class WP_Privacy_Policy_Content { // The site doesn't have a privacy policy. if ( empty( $policy_page_id ) ) { - return; + return false; } if ( ! current_user_can( 'edit_post', $policy_page_id ) ) { - return; - } - - // Also run when the option doesn't exist yet. - if ( get_option( '_wp_privacy_text_change_check' ) === 'no-check' ) { - return; + 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 all screens in wp-admin. + // 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 { - // Stop checking. - update_option( '_wp_privacy_text_change_check', 'no-check' ); + $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 an admin notice when some privacy info has changed. + * Output a warning when some privacy info has changed. * * @since 4.9.6 */ public static function policy_text_changed_notice() { global $post; - $policy_page_id = (int) get_option( 'wp_page_for_privacy_policy' ); + + $screen = get_current_screen()->id; + + if ( 'privacy' !== $screen ) { + return; + } ?>

ID != $policy_page_id ) { - ?> - -

$old_data['policy_text'], 'removed' => $time, ); - array_unshift( $checked, $data ); + + $checked[] = $data; } } $update_cache = true; @@ -1525,11 +1550,6 @@ final class WP_Privacy_Policy_Content { } } - // Stop checking for changes after the page has been loaded. - if ( get_option( '_wp_privacy_text_change_check' ) !== 'no-check' ) { - update_option( '_wp_privacy_text_change_check', 'no-check' ); - } - return $checked; } diff --git a/src/wp-admin/menu.php b/src/wp-admin/menu.php index 0ad302ebda..bf9eb72abb 100644 --- a/src/wp-admin/menu.php +++ b/src/wp-admin/menu.php @@ -263,14 +263,21 @@ if ( ! is_multisite() && defined( 'WP_ALLOW_MULTISITE' ) && WP_ALLOW_MULTISITE ) $submenu['tools.php'][50] = array( __( 'Network Setup' ), 'setup_network', 'network.php' ); } -$menu[80] = array( __( 'Settings' ), 'manage_options', 'options-general.php', '', 'menu-top menu-icon-settings', 'menu-settings', 'dashicons-admin-settings' ); +$change_notice = ''; +if ( current_user_can( 'manage_privacy_options' ) && WP_Privacy_Policy_Content::text_change_check() ) { + $change_notice = ' ' . number_format_i18n( 1 ) . ''; +} + +// translators: %s is the update notification bubble, if updates are available. +$menu[80] = array( sprintf( __( 'Settings %s' ), $change_notice ), 'manage_options', 'options-general.php', '', 'menu-top menu-icon-settings', 'menu-settings', 'dashicons-admin-settings' ); $submenu['options-general.php'][10] = array( _x( 'General', 'settings screen' ), 'manage_options', 'options-general.php' ); $submenu['options-general.php'][15] = array( __( 'Writing' ), 'manage_options', 'options-writing.php' ); $submenu['options-general.php'][20] = array( __( 'Reading' ), 'manage_options', 'options-reading.php' ); $submenu['options-general.php'][25] = array( __( 'Discussion' ), 'manage_options', 'options-discussion.php' ); $submenu['options-general.php'][30] = array( __( 'Media' ), 'manage_options', 'options-media.php' ); $submenu['options-general.php'][40] = array( __( 'Permalinks' ), 'manage_options', 'options-permalink.php' ); - $submenu['options-general.php'][45] = array( __( 'Privacy' ), 'manage_privacy_options', 'privacy.php' ); + // translators: %s is the update notification bubble, if updates are available. + $submenu['options-general.php'][45] = array( sprintf( __( 'Privacy %s' ), $change_notice ), 'manage_privacy_options', 'privacy.php' ); $_wp_last_utility_menu = 80; // The index of the last top-level menu in the utility menu group diff --git a/src/wp-includes/default-filters.php b/src/wp-includes/default-filters.php index 7ecff3ade1..61c45e6231 100644 --- a/src/wp-includes/default-filters.php +++ b/src/wp-includes/default-filters.php @@ -562,8 +562,4 @@ add_filter( 'pre_oembed_result', 'wp_filter_pre_oembed_result', 10, 3 ); // Capabilities add_filter( 'user_has_cap', 'wp_maybe_grant_install_languages_cap', 1 ); -// Trigger the check for policy text changes after active plugins change. -add_action( 'update_site_option_active_sitewide_plugins', '_wp_privacy_active_plugins_change' ); -add_action( 'update_option_active_plugins', '_wp_privacy_active_plugins_change' ); - unset( $filter, $action ); diff --git a/src/wp-includes/functions.php b/src/wp-includes/functions.php index 341a1746d9..763b98df28 100644 --- a/src/wp-includes/functions.php +++ b/src/wp-includes/functions.php @@ -6248,16 +6248,6 @@ function wp_privacy_anonymize_data( $type, $data = '' ) { return apply_filters( 'wp_privacy_anonymize_data', $anonymous, $type, $data ); } -/** - * Trigger the check for policy text changes. - * - * @since 4.9.6 - * @access private - */ -function _wp_privacy_active_plugins_change() { - update_option( '_wp_privacy_text_change_check', 'check' ); -} - /** * Schedule a `WP_Cron` job to delete expired export files. *