Ticket #6712: 6712.04.patch
File 6712.04.patch, 18.2 KB (added by , 7 years ago) |
---|
-
new file src/bp-notifications/bp-notifications-settings.php
new file mode 100644
- + 1 <?php 2 /** 3 * BuddyPress Notifications Settings. 4 * 5 * Functions and hooks related to the Settings component. 6 * 7 * @package BuddyPress 8 * @since 2.7.0 9 */ 10 11 /** REGISTRATION *********************************************************/ 12 13 /** 14 * Register our screen notification type with the Settings component. 15 * 16 * @since 2.7.0 17 */ 18 function bp_notifications_register_notification_settings_type() { 19 bp_settings_register_notification_type( 'screen', array( 20 'label' => esc_html__( 'Screen', 'buddypress' ), 21 'post_variable' => 'screen' 22 ) ); 23 } 24 add_action( 'bp_setup_globals', 'bp_notifications_register_notification_settings_type' ); 25 26 /** HOOKS ****************************************************************/ 27 28 /** 29 * Block screen notification if a user has disabled it from their settings page. 30 * 31 * @since 2.7.0 32 * 33 * @param BP_Notification_Notification $n 34 */ 35 function bp_notifications_block_screen_notifications_for_user_hook( $n ) { 36 $disabled = bp_get_user_meta( $n->user_id, 'screen_notifications_disabled', true ); 37 38 $component_action = $n->component_action; 39 40 // Edge cases - the following actions are shared. 41 if ( 'member_promoted_to_admin' === $component_action ) { 42 $component_action = 'member_promoted_to_mod'; 43 } elseif ( 'membership_request_accepted' === $component_action ) { 44 $component_action = 'membership_request_rejected'; 45 } 46 47 // Block notification from being recorded by wiping out the component name. 48 if ( isset( $disabled[ $component_action ] ) ) { 49 $n->component_name = ''; 50 } 51 } 52 add_action( 'bp_notification_before_save', 'bp_notifications_block_screen_notifications_for_user_hook' ); 53 54 /** SCREEN ***************************************************************/ 55 56 /** 57 * Revamp the "Settings > Notifications" page to use a checkbox interface. 58 * 59 * @since 2.7.0 60 */ 61 function bp_notifications_new_checkbox_interface() { 62 if ( ! did_action( 'bp_before_member_settings_template' ) ) { 63 return; 64 } 65 66 add_action( 'bp_notification_settings', 'bp_notifications_new_checkbox_interface_end_buffer', 99999999 ); 67 68 ob_start(); 69 } 70 add_action( 'bp_notification_settings', 'bp_notifications_new_checkbox_interface', -99999999 ); 71 72 /** 73 * Umm... this is madness! Proof of concept. Not commit-worthy. 74 * 75 * @since 2.7.0 76 */ 77 function bp_notifications_new_checkbox_interface_end_buffer() { 78 $html = ob_get_contents(); 79 ob_end_clean(); 80 81 $notification_types = bp_settings_get_notification_types(); 82 if ( empty( $notification_types ) ) { 83 return; 84 } 85 86 $options = array(); 87 88 libxml_use_internal_errors( false ); 89 $dom = new DOMDocument(); 90 $dom->loadHTML( $html ); 91 92 // Loop through each notification group. 93 foreach ( $dom->getElementsByTagName('tbody') as $node ) { 94 $option = ''; 95 96 $group_title = $node->parentNode->childNodes->item(0)->childNodes->item(0)->childNodes->item(2)->nodeValue; 97 $key = sanitize_title( $group_title ); 98 99 $options[ $key ] = array( 100 'title' => $group_title 101 ); 102 103 $options[ $key ][ 'options' ] = array(); 104 105 // Loop through the <tr>'s to grab the options for each group. 106 foreach ( $node->childNodes as $item ) { 107 if ( $item->childNodes->item(4) ) { 108 $option = $item->childNodes->item(4)->firstChild->attributes->getNamedItem( 'name' )->value; 109 $option = str_replace( 'notifications[', '', $option ); 110 $option = str_replace( ']', '', $option ); 111 112 113 } 114 115 // Option description. 116 if ( $item->childNodes->item(2) && ! empty( $option ) ) { 117 $options[ $key ][ 'options' ][ $option ] = $item->childNodes->item(2)->textContent; 118 } 119 120 } 121 122 } 123 124 unset( $dom ); 125 126 foreach( $options as $group_key => $group ) { 127 if ( empty( $group['options'] ) ) { 128 continue; 129 } 130 131 ?> 132 133 <fieldset> 134 <legend><?php esc_html_e( $group['title'] ); ?></legend> 135 136 <?php foreach ( $group['options'] as $key => $desc ) : ?> 137 <p id="<?php esc_attr_e( $key ); ?>"><?php esc_html_e( $desc ); ?></p> 138 139 <?php 140 foreach ( $notification_types as $ntype => $nargs ) : 141 $metakey = bp_settings_get_metakey_for_notification_type( $key, $ntype ); 142 if ( '' === $metakey ) { 143 continue; 144 } 145 146 $setting = bp_settings_get_notification_user_setting( 0, $metakey, $ntype ); 147 ?> 148 149 <label for="<?php printf( '%1$s-%2$s', $ntype, $metakey ); ?>" aria-describedby="<?php esc_attr_e( $key ); ?>"> 150 <input name="<?php printf( '%1$s[%2$s]', bp_settings_get_notification_type( $ntype )->post_variable, esc_attr( $metakey ) ); ?>" id="<?php printf( '%1$s-%2$s', $ntype, $metakey ); ?>" value="yes" <?php checked( $setting, 'yes', true ); ?> type="checkbox"> 151 <?php esc_html_e( bp_settings_get_notification_type( $ntype )->label ); ?></label> 152 153 <?php endforeach; ?> 154 155 <?php endforeach; ?> 156 157 </fieldset> 158 159 <?php 160 } 161 } 162 163 /** 164 * Saves custom user settings for our notification type. 165 * 166 * @since 2.7.0 167 */ 168 function bp_notifications_save_screen_notification_settings() { 169 $ntype = bp_settings_get_notification_type( 'screen' ); 170 if ( false === $ntype || empty( $_POST[ $ntype->post_variable ] ) ) { 171 return; 172 } 173 174 $removed = array_diff_key( bp_notifications_get_mapped_notification_actions(), $_POST[ $ntype->post_variable ] ); 175 176 // Edge cases - These options are shared with other email options. 177 unset( $removed['member_promoted_to_admin'], $removed[ 'membership_request_accepted' ] ); 178 179 // We only save disabled screen notification data. 180 bp_update_user_meta( bp_displayed_user_id(), 'screen_notifications_disabled', array_fill_keys( array_keys( $removed ), 1 ) ); 181 } 182 add_action( 'bp_core_notification_settings_after_save', 'bp_notifications_save_screen_notification_settings' ); 183 184 /** UTILITY **************************************************************/ 185 186 /** 187 * Fetch mappped notification actions to notification meta keys. 188 * 189 * @since 2.7.0 190 * @todo Would be nice to merge this with bp_email_get_type_schema() somehow... 191 * 192 * @return array 193 */ 194 function bp_notifications_get_mapped_notification_actions() { 195 $actions = array( 196 // Activity 197 // notification action -> meta key 198 'new_at_mention' => 'activity_new_mention', 199 'comment_reply' => 'activity_new_reply', 200 201 // Friends 202 'friendship_requested' => 'friends_friendship_request', 203 'friendship_accepted' => 'friends_friendship_accepted', 204 205 // Groups 206 'group_invite' => 'groups_invite', 207 'member_promoted_to_admin' => 'groups_admin_promotion', 208 'member_promoted_to_mod' => 'groups_admin_promotion', 209 'new_membership_request' => 'groups_membership_request', 210 'membership_request_accepted' => 'membership_request_completed', 211 'membership_request_rejected' => 'membership_request_completed', 212 213 // Messages 214 'new_message' => 'messages_new_message' 215 ); 216 217 return apply_filters( 'bp_notifications_get_mapped_notification_actions', $actions ); 218 } 219 220 /** 221 * Get custom metakey for our notification type. 222 * 223 * @since 2.7.0 224 * 225 * @param string $key Current notification key. 226 */ 227 function bp_notifications_get_metakey_for_notification_type( $key ) { 228 $key = str_replace( 'notification_', '', $key ); 229 230 $mapped_keys = array_flip( bp_notifications_get_mapped_notification_actions() ); 231 232 if ( isset( $mapped_keys[ $key ] ) ) { 233 return $mapped_keys[ $key ]; 234 235 /* 236 * 3rd-party screen notifications need to be registered on the 237 * 'bp_notifications_get_mapped_notification_actions' filter. 238 */ 239 } else { 240 return ''; 241 } 242 } 243 add_filter( 'bp_settings_get_metakey_for_screen_notification_type', 'bp_notifications_get_metakey_for_notification_type', 50 ); 244 245 /** 246 * Get user setting for our screen notification type. 247 * 248 * @since 2.7.0 249 * 250 * @param string $setting Current setting. Default: 'yes'. 251 * @param int $user_id User ID. 252 * @param string $key Meta key. 253 * @return string 254 */ 255 function bp_notifications_filter_user_notification_setting( $setting, $user_id, $key ) { 256 $disabled_screen_notifications = bp_get_user_meta( $user_id, 'screen_notifications_disabled', true ); 257 258 if ( isset( $disabled_screen_notifications[ $key ] ) ) { 259 $setting = 'no'; 260 } 261 262 return $setting; 263 } 264 add_filter( 'bp_settings_get_screen_notification_user_setting', 'bp_notifications_filter_user_notification_setting', 10, 3 ); 265 No newline at end of file -
src/bp-notifications/classes/class-bp-notifications-component.php
52 52 'cache', 53 53 ); 54 54 55 if ( bp_is_active( 'settings' ) ) { 56 $includes[] = 'settings'; 57 } 58 55 59 if ( ! buddypress()->do_autoload ) { 56 60 $includes[] = 'classes'; 57 61 } -
src/bp-notifications/classes/class-bp-notifications-notification.php
125 125 */ 126 126 do_action_ref_array( 'bp_notification_before_save', array( &$this ) ); 127 127 128 // Ensure that the 'component_name' property is always set. 129 if ( empty( $this->component_name ) ) { 130 return false; 131 } 132 128 133 $data = array( 129 134 'user_id' => $this->user_id, 130 135 'item_id' => $this->item_id, -
src/bp-settings/bp-settings-functions.php
77 77 $sanitized_settings[ $key ] = $value; 78 78 } 79 79 80 // Handle new checkbox interface, specifically unchecked settings. 81 $enabled_notifications = array_intersect( array_keys( $settings ), $registered_notification_settings ); 82 $removed_notifications = array_unique( array_diff( $registered_notification_settings, $enabled_notifications ) ); 83 foreach ( $removed_notifications as $key ) { 84 $sanitized_settings[ $key ] = 'no'; 85 } 86 80 87 return $sanitized_settings; 81 88 } 82 89 … … 103 110 104 111 return $key_whitelist; 105 112 } 113 114 /** 115 * Register a notification type on the "Settings > Notifications" page. 116 * 117 * Used primarily to output a user's "Settings > Notifications" page, but 118 * could be used for other things. 119 * 120 * You should run this function after 'bp_setup_globals'. 121 * 122 * @since 2.7.0 123 * 124 * @param string $type The notification type to register. 125 * @param array $args { 126 * Array of arguments to register for the notification type. 127 * @type string $label Label for the notification type. 128 * @type string $post_variable Unique post variable used for input name in form. 129 * } 130 * @return bool 131 */ 132 function bp_settings_register_notification_type( $type = '', $args = array() ) { 133 if ( ! did_action( 'bp_setup_globals' ) ) { 134 _doing_it_wrong( __FUNCTION__, "Function must be called after the 'bp_setup_globals' hook." ); 135 return false; 136 } 137 138 if ( empty( $type ) || empty( $args ) ) { 139 return false; 140 } 141 142 if ( ! isset( buddypress()->settings->notification_type ) ) { 143 buddypress()->settings->notification_type = array(); 144 } 145 146 $component = sanitize_key( $type ); 147 148 buddypress()->settings->notification_type[$type] = (object) $args; 149 150 return true; 151 } 152 153 /** 154 * Fetch all available notification types. 155 * 156 * @since 2.7.0 157 * 158 * @return array 159 */ 160 function bp_settings_get_notification_types() { 161 if ( ! isset( buddypress()->settings->notification_type ) ) { 162 $types = array(); 163 } 164 165 // Email always exists by default. 166 $types['email'] = (object) array( 167 'label' => esc_html__( 'Email', 'buddypress' ), 168 'post_variable' => 'notifications' 169 ); 170 171 /** 172 * Filters the notification types for 3rd-party notification types. 173 */ 174 $custom_types = apply_filters( 'bp_settings_get_notification_types', buddypress()->settings->notification_type ); 175 176 return $types + $custom_types; 177 } 178 179 /** 180 * Get data about a registered notification type 181 * 182 * @since 2.7.0 183 * 184 * @param string $type Notification type. 185 * @return object|false 186 */ 187 function bp_settings_get_notification_type( $type ) { 188 $types = bp_settings_get_notification_types(); 189 if ( isset( $types[ $type ] ) ) { 190 return $types[ $type ]; 191 } else { 192 return false; 193 } 194 } 195 196 /** 197 * Get the meta key used to store the user's setting for the notification type 198 * 199 * @since 2.7.0 200 * 201 * @param string $key Meta key to use when checking user meta. 202 * @param string $type Notification type to check against. 203 * @return string 204 */ 205 function bp_settings_get_metakey_for_notification_type( $key, $type ) { 206 // Email already works. 207 if ( 'email' === $type ) { 208 return $key; 209 } 210 211 $unmodified_key = $key; 212 213 /** 214 * Dynamic filter used to determine the meta key for the current notification type. 215 * 216 * @since 2.7.0 217 * 218 * @param string $key Meta key to use when checking user meta. 219 */ 220 $key = apply_filters( "bp_settings_get_metakey_for_{$type}_notification_type", $key ); 221 222 // If key didn't change, return blank string. 223 if ( $key === bp_settings_get_metakey_for_notification_type( $unmodified_key, 'email' ) ) { 224 return ''; 225 } else { 226 return $key; 227 } 228 } 229 230 /** 231 * Get a user's notification user setting, depending on the notification type 232 * 233 * @since 2.7.0 234 * 235 * @param string $key Meta key to use when checking user meta. 236 * @param string $type Notification type to check against. 237 * @return string 238 */ 239 function bp_settings_get_notification_user_setting( $user_id = 0, $key = '', $type = '' ) { 240 if ( 0 === $user_id ) { 241 $user_id = bp_displayed_user_id(); 242 } 243 244 // Email is hardcoded. 245 if ( 'email' === $type ) { 246 $setting = bp_get_user_meta( $user_id, $key, true ); 247 248 // No setting saved; default is 'yes'. 249 if ( '' === $setting ) { 250 $setting = 'yes'; 251 } 252 253 return $setting; 254 } 255 256 /** 257 * Allow 3rd-party notification types to filter the notification user setting 258 * 259 * Filter name is dynamic, depending on the notification type. 260 * 261 * @since 2.7.0 262 * 263 * @param string $setting Defaults to 'yes'. 264 * @param int $user_id User ID to fetch the notification setting for. 265 * @param string $key Notification key. 266 */ 267 return apply_filters( "bp_settings_get_{$type}_notification_user_setting", 'yes', $user_id, $key ); 268 } -
src/bp-settings/classes/class-bp-settings-component.php
117 117 'user_has_access' => $access 118 118 ); 119 119 120 // Add Email nav item. Formerly called 'Notifications', we 121 // retain the old slug and function names for backward compat. 120 // Add Notifications nav item. 122 121 $sub_nav[] = array( 123 'name' => __( ' Email', 'buddypress' ),122 'name' => __( 'Notifications', 'buddypress' ), 124 123 'slug' => 'notifications', 125 124 'parent_url' => $settings_link, 126 125 'parent_slug' => $slug, … … 195 194 $wp_admin_nav[] = array( 196 195 'parent' => 'my-account-' . $this->id, 197 196 'id' => 'my-account-' . $this->id . '-notifications', 198 'title' => __( ' Email', 'buddypress' ),197 'title' => __( 'Notifications', 'buddypress' ), 199 198 'href' => trailingslashit( $settings_link . 'notifications' ), 200 199 'position' => 20 201 200 ); -
src/bp-templates/bp-legacy/buddypress/members/single/settings/notifications.php
9 9 /** This action is documented in bp-templates/bp-legacy/buddypress/members/single/settings/profile.php */ 10 10 do_action( 'bp_before_member_settings_template' ); ?> 11 11 12 <h2 class="bp-screen-reader-text"><?php _e( 'Notification Settings', 'buddypress' ); ?></h2> 13 12 14 <form action="<?php echo bp_displayed_user_domain() . bp_get_settings_slug() . '/notifications'; ?>" method="post" class="standard-form" id="settings-form"> 13 <p><?php _e( 'Send an email notice when:', 'buddypress' ); ?></p>14 15 15 16 <?php 16 17 -
src/bp-templates/bp-legacy/css/buddypress.css
875 875 word-wrap: normal !important; 876 876 } 877 877 878 /* Settings > Notifications */ 879 #settings-form.standard-form fieldset { 880 margin-bottom: 1em; 881 } 882 883 #settings-form.standard-form legend { 884 margin-bottom: 15px; 885 } 886 887 #settings-form.standard-form fieldset p { 888 clear: both; 889 margin-bottom: 0; 890 } 891 892 #settings-form.standard-form label { 893 float: left; 894 margin-bottom: 1em; 895 margin-right: 3em; 896 } 897 878 898 /*-------------------------------------------------------------- 879 899 3.6 - Ajax Loading 880 900 --------------------------------------------------------------*/ -
tests/phpunit/testcases/notifications/functions.php
394 394 } 395 395 396 396 /** 397 * @group notification_settings 398 */ 399 public function test_notifications_disabled_by_user() { 400 $u = $this->factory->user->create(); 401 402 // Disable notifications for the 'new_at_mention' action. 403 bp_update_user_meta( $u, 'screen_notifications_disabled', array( 'new_at_mention' => 1 ) ); 404 405 // This should be blocked. 406 $n1 = $this->factory->notification->create( array( 407 'component_name' => 'activity', 408 'component_action' => 'new_at_mention', 409 'item_id' => 99, 410 'user_id' => $u, 411 ) ); 412 413 // This should pass through. 414 $n2 = $this->factory->notification->create( array( 415 'component_name' => 'activity', 416 'component_action' => 'kwyjibo', 417 'item_id' => 99, 418 'user_id' => $u, 419 ) ); 420 421 $this->assertFalse( $n1 ); 422 $this->assertNotFalse( $n2 ); 423 } 424 425 /** 397 426 * Used in test_notification_callback_parameter_integrity() test. 398 427 */ 399 428 public function dummy_notification_callback( $action, $item_id, $secondary_item_id, $total_items, $format = 'string', $id = 0 ) {