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.

Built from https://develop.svn.wordpress.org/trunk@43223


git-svn-id: http://core.svn.wordpress.org/trunk@43052 1a063a9b-81f0-0310-95a4-ce76da25c4cd
This commit is contained in:
iandunn 2018-05-10 19:52:21 +00:00
parent 7b5f40133a
commit 8af721fff8
12 changed files with 70 additions and 57 deletions

View File

@ -1089,7 +1089,7 @@ table.form-table td .updated p {
} }
.tools-privacy-edit { .tools-privacy-edit {
margin: 2.3em 0; margin: 1.5em 0;
} }
.tools-privacy-policy-page span { .tools-privacy-policy-page span {

File diff suppressed because one or more lines are too long

View File

@ -1089,7 +1089,7 @@ table.form-table td .updated p {
} }
.tools-privacy-edit { .tools-privacy-edit {
margin: 2.3em 0; margin: 1.5em 0;
} }
.tools-privacy-policy-page span { .tools-privacy-policy-page span {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -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 ); add_action( 'wp_privacy_personal_data_export_file', 'wp_privacy_generate_personal_data_export_file', 10 );
// Privacy policy text changes check. // 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. // Show a "postbox" with the text suggestions for a privacy policy.
add_action( 'edit_form_after_title', array( 'WP_Privacy_Policy_Content', 'notice' ) ); 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 the suggested policy text from WordPress.
add_action( 'admin_init', array( 'WP_Privacy_Policy_Content', 'add_suggested_content' ), 1 ); 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' ) ); add_action( 'post_updated', array( 'WP_Privacy_Policy_Content', '_policy_page_updated' ) );

View File

@ -1320,69 +1320,94 @@ final class WP_Privacy_Policy_Content {
// The site doesn't have a privacy policy. // The site doesn't have a privacy policy.
if ( empty( $policy_page_id ) ) { if ( empty( $policy_page_id ) ) {
return; return false;
} }
if ( ! current_user_can( 'edit_post', $policy_page_id ) ) { if ( ! current_user_can( 'edit_post', $policy_page_id ) ) {
return; return false;
}
// Also run when the option doesn't exist yet.
if ( get_option( '_wp_privacy_text_change_check' ) === 'no-check' ) {
return;
} }
$old = (array) get_post_meta( $policy_page_id, '_wp_suggested_privacy_policy_content' ); $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; $new = self::$policy_content;
// Remove the extra values added to the meta. // Remove the extra values added to the meta.
foreach ( $old as $key => $data ) { foreach ( $old as $key => $data ) {
if ( ! empty( $data['removed'] ) ) {
unset( $old[ $key ] );
continue;
}
$old[ $key ] = array( $old[ $key ] = array(
'plugin_name' => $data['plugin_name'], 'plugin_name' => $data['plugin_name'],
'policy_text' => $data['policy_text'], '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. // The == operator (equal, not identical) was used intentionally.
// See http://php.net/manual/en/language.operators.array.php // See http://php.net/manual/en/language.operators.array.php
if ( $new != $old ) { if ( $new != $old ) {
// A plugin was activated or deactivated, or some policy text has changed. // 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' ) ); add_action( 'admin_notices', array( 'WP_Privacy_Policy_Content', 'policy_text_changed_notice' ) );
$state = 'changed';
} else { } else {
// Stop checking. $state = 'not-changed';
update_option( '_wp_privacy_text_change_check', 'no-check' );
} }
// 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 * @since 4.9.6
*/ */
public static function policy_text_changed_notice() { public static function policy_text_changed_notice() {
global $post; global $post;
$policy_page_id = (int) get_option( 'wp_page_for_privacy_policy' );
$screen = get_current_screen()->id;
if ( 'privacy' !== $screen ) {
return;
}
?> ?>
<div class="policy-text-updated notice notice-warning is-dismissible"> <div class="policy-text-updated notice notice-warning is-dismissible">
<p><?php <p><?php
_e( 'The suggested privacy policy text has changed. Please update your privacy policy.' );
_e( 'The suggested privacy policy text has changed.' );
if ( empty( $post ) || $post->ID != $policy_page_id ) {
?>
<a href="<?php echo get_edit_post_link( $policy_page_id ); ?>"><?php _e( 'Edit the privacy policy.' ); ?></a>
<?php
}
?></p> ?></p>
</div> </div>
<?php <?php
} }
/** /**
* Stop checking for changed privacy info when the policy page is updated. * Update the cached policy info when the policy page is updated.
* *
* @since 4.9.6 * @since 4.9.6
* @access private * @access private
@ -1394,9 +1419,8 @@ final class WP_Privacy_Policy_Content {
return; return;
} }
// The policy page was updated. // Update the cache in case the user hasn't visited the policy guide.
// Stop checking for text changes. self::get_suggested_policy_text();
update_option( '_wp_privacy_text_change_check', 'no-check' );
// Remove updated|removed status. // Remove updated|removed status.
$old = (array) get_post_meta( $policy_page_id, '_wp_suggested_privacy_policy_content' ); $old = (array) get_post_meta( $policy_page_id, '_wp_suggested_privacy_policy_content' );
@ -1496,7 +1520,7 @@ final class WP_Privacy_Policy_Content {
foreach ( $new as $new_data ) { foreach ( $new as $new_data ) {
if ( ! empty( $new_data['plugin_name'] ) && ! empty( $new_data['policy_text'] ) ) { if ( ! empty( $new_data['plugin_name'] ) && ! empty( $new_data['policy_text'] ) ) {
$new_data['added'] = $time; $new_data['added'] = $time;
array_unshift( $checked, $new_data ); $checked[] = $new_data;
} }
} }
$update_cache = true; $update_cache = true;
@ -1511,7 +1535,8 @@ final class WP_Privacy_Policy_Content {
'policy_text' => $old_data['policy_text'], 'policy_text' => $old_data['policy_text'],
'removed' => $time, 'removed' => $time,
); );
array_unshift( $checked, $data );
$checked[] = $data;
} }
} }
$update_cache = true; $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; return $checked;
} }

View File

@ -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' ); $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 = ' <span class="update-plugins 1"><span class="plugin-count">' . number_format_i18n( 1 ) . '</span></span>';
}
// 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'][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'][15] = array( __( 'Writing' ), 'manage_options', 'options-writing.php' );
$submenu['options-general.php'][20] = array( __( 'Reading' ), 'manage_options', 'options-reading.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'][25] = array( __( 'Discussion' ), 'manage_options', 'options-discussion.php' );
$submenu['options-general.php'][30] = array( __( 'Media' ), 'manage_options', 'options-media.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'][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 $_wp_last_utility_menu = 80; // The index of the last top-level menu in the utility menu group

View File

@ -562,8 +562,4 @@ add_filter( 'pre_oembed_result', 'wp_filter_pre_oembed_result', 10, 3 );
// Capabilities // Capabilities
add_filter( 'user_has_cap', 'wp_maybe_grant_install_languages_cap', 1 ); 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 ); unset( $filter, $action );

View File

@ -6248,16 +6248,6 @@ function wp_privacy_anonymize_data( $type, $data = '' ) {
return apply_filters( 'wp_privacy_anonymize_data', $anonymous, $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. * Schedule a `WP_Cron` job to delete expired export files.
* *

View File

@ -4,7 +4,7 @@
* *
* @global string $wp_version * @global string $wp_version
*/ */
$wp_version = '5.0-alpha-43222'; $wp_version = '5.0-alpha-43223';
/** /**
* Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema. * Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.