PluginProbe
bbPress / 2.6.17
bbPress v2.6.17
2.6.17 trunk 2.0 2.0-beta-1 2.0-beta-2b 2.0-beta-3 2.0-beta-3b 2.0-rc-2 2.0-rc-3 2.0-rc-4 2.0-rc-5 2.0.1 2.0.2 2.0.3 2.1 2.1-beta-1 2.1-rc1 2.1-rc2 2.1-rc3 2.1-rc4 2.1.1 2.1.2 2.1.3 2.2 2.2.1 All 72 releases
← All changes | includes/users/functions.php +887 -760 2.22.6.17 View file →
@@ -7,38 +7,35 @@
7 7 * @subpackage Functions
8 8 */
9 9
10 10 // Exit if accessed directly
11 -if ( !defined( 'ABSPATH' ) ) exit;
11 +defined( 'ABSPATH' ) || exit;
12 12
13 13 /**
14 14 * Redirect back to $url when attempting to use the login page
15 15 *
16 - * @since bbPress (r2815)
16 + * @since 2.0.0 bbPress (r2815)
17 17 *
18 18 * @param string $url The url
19 19 * @param string $raw_url Raw url
20 20 * @param object $user User object
21 - * @uses is_wp_error() To check if the user param is a {@link WP_Error}
22 - * @uses admin_url() To get the admin url
23 - * @uses home_url() To get the home url
24 - * @uses esc_url() To escape the url
25 - * @uses wp_safe_redirect() To redirect
26 21 */
27 22 function bbp_redirect_login( $url = '', $raw_url = '', $user = '' ) {
28 23
29 24 // Raw redirect_to was passed, so use it
30 - if ( !empty( $raw_url ) )
25 + if ( ! empty( $raw_url ) ) {
31 26 $url = $raw_url;
32 27
33 28 // $url was manually set in wp-login.php to redirect to admin
34 - elseif ( admin_url() == $url )
29 + } elseif ( admin_url() === $url ) {
35 30 $url = home_url();
36 31
37 32 // $url is empty
38 - elseif ( empty( $url ) )
33 + } elseif ( empty( $url ) ) {
39 34 $url = home_url();
35 + }
40 36
37 + // Filter & return
41 38 return apply_filters( 'bbp_redirect_login', $url, $raw_url, $user );
42 39 }
43 40
44 41 /**
@@ -43,54 +40,47 @@
43 40
44 41 /**
45 42 * Is an anonymous topic/reply being made?
46 43 *
47 - * @since bbPres (r2688)
44 + * @since 2.0.0 bbPress (r2688)
48 45 *
49 - * @uses is_user_logged_in() Is the user logged in?
50 - * @uses bbp_allow_anonymous() Is anonymous posting allowed?
51 - * @uses apply_filters() Calls 'bbp_is_anonymous' with the return value
52 46 * @return bool True if anonymous is allowed and user is not logged in, false if
53 47 * anonymous is not allowed or user is logged in
54 48 */
55 49 function bbp_is_anonymous() {
56 - if ( !is_user_logged_in() && bbp_allow_anonymous() )
57 - $is_anonymous = true;
58 - else
59 - $is_anonymous = false;
50 + $is_anonymous = ( ! is_user_logged_in() && bbp_allow_anonymous() );
60 51
61 - return apply_filters( 'bbp_is_anonymous', $is_anonymous );
52 + // Filter & return
53 + return (bool) apply_filters( 'bbp_is_anonymous', $is_anonymous );
62 54 }
63 55
64 56 /**
65 57 * Echoes the values for current poster (uses WP comment cookies)
66 58 *
67 - * @since bbPress (r2734)
59 + * @since 2.0.0 bbPress (r2734)
68 60 *
69 61 * @param string $key Which value to echo?
70 - * @uses bbp_get_current_anonymous_user_data() To get the current anonymous user
71 - * data
72 62 */
73 63 function bbp_current_anonymous_user_data( $key = '' ) {
74 - echo bbp_get_current_anonymous_user_data( $key );
64 + echo esc_attr( bbp_get_current_anonymous_user_data( $key ) );
75 65 }
76 66
77 67 /**
78 68 * Get the cookies for current poster (uses WP comment cookies).
79 69 *
80 - * @since bbPress (r2734)
70 + * @since 2.0.0 bbPress (r2734)
81 71 *
82 72 * @param string $key Optional. Which value to get? If not given, then
83 73 * an array is returned.
84 - * @uses sanitize_comment_cookies() To sanitize the current poster data
85 - * @uses wp_get_current_commenter() To get the current poster data *
86 74 * @return string|array Cookie(s) for current poster
87 75 */
88 76 function bbp_get_current_anonymous_user_data( $key = '' ) {
77 +
78 + // Array of allowed cookie names
89 79 $cookie_names = array(
90 - 'name' => 'comment_author',
91 - 'email' => 'comment_author_email',
92 - 'website' => 'comment_author_url',
80 + 'name' => 'comment_author',
81 + 'email' => 'comment_author_email',
82 + 'url' => 'comment_author_url',
93 83
94 84 // Here just for the sake of them, use the above ones
95 85 'comment_author' => 'comment_author',
96 86 'comment_author_email' => 'comment_author_email',
@@ -96,15 +86,20 @@
96 86 'comment_author_email' => 'comment_author_email',
97 87 'comment_author_url' => 'comment_author_url',
98 88 );
99 89
100 - sanitize_comment_cookies();
90 + // Get the current poster's info from the cookies
91 + $bbp_current_poster = wp_get_current_commenter();
101 92
102 - $bbp_current_poster = wp_get_current_commenter();
93 + // Sanitize the cookie key being retrieved
94 + $key = sanitize_key( $key );
103 95
104 - if ( !empty( $key ) && in_array( $key, array_keys( $cookie_names ) ) )
105 - return $bbp_current_poster[$cookie_names[$key]];
96 + // Maybe return a specific key
97 + if ( ! empty( $key ) && in_array( $key, array_keys( $cookie_names ), true ) ) {
98 + return $bbp_current_poster[ $cookie_names[ $key ] ];
99 + }
106 100
101 + // Return all keys
107 102 return $bbp_current_poster;
108 103 }
109 104
110 105 /**
@@ -109,972 +104,1010 @@
109 104
110 105 /**
111 106 * Set the cookies for current poster (uses WP comment cookies)
112 107 *
113 - * @since bbPress (r2734)
108 + * @since 2.0.0 bbPress (r2734)
114 109 *
115 - * @param array $anonymous_data With keys 'bbp_anonymous_name',
116 - * 'bbp_anonymous_email', 'bbp_anonymous_website'.
117 - * Should be sanitized (see
118 - * {@link bbp_filter_anonymous_post_data()} for
119 - * sanitization)
120 - * @uses apply_filters() Calls 'comment_cookie_lifetime' for cookie lifetime.
121 - * Defaults to 30000000.
110 + * @param array $anonymous_data Optional - if it's an anonymous post. Do not
111 + * supply if supplying $author_id. Should be
112 + * sanitized (see {@link bbp_filter_anonymous_post_data()}
122 113 */
123 114 function bbp_set_current_anonymous_user_data( $anonymous_data = array() ) {
124 - if ( empty( $anonymous_data ) || !is_array( $anonymous_data ) )
115 +
116 + // Bail if empty or not an array
117 + if ( empty( $anonymous_data ) || ! is_array( $anonymous_data ) ) {
125 118 return;
119 + }
126 120
127 - $comment_cookie_lifetime = apply_filters( 'comment_cookie_lifetime', 30000000 );
121 + // Setup cookie expiration
122 + $lifetime = (int) apply_filters( 'comment_cookie_lifetime', 30000000 );
123 + $expiry = time() + $lifetime;
124 + $secure = ( 'https' === parse_url( home_url(), PHP_URL_SCHEME ) );
128 125
129 - setcookie( 'comment_author_' . COOKIEHASH, $anonymous_data['bbp_anonymous_name'], time() + $comment_cookie_lifetime, COOKIEPATH, COOKIE_DOMAIN );
130 - setcookie( 'comment_author_email_' . COOKIEHASH, $anonymous_data['bbp_anonymous_email'], time() + $comment_cookie_lifetime, COOKIEPATH, COOKIE_DOMAIN );
131 - setcookie( 'comment_author_url_' . COOKIEHASH, $anonymous_data['bbp_anonymous_website'], time() + $comment_cookie_lifetime, COOKIEPATH, COOKIE_DOMAIN );
126 + // Set the cookies
127 + setcookie( 'comment_author_' . COOKIEHASH, $anonymous_data['bbp_anonymous_name'], $expiry, COOKIEPATH, COOKIE_DOMAIN, $secure );
128 + setcookie( 'comment_author_email_' . COOKIEHASH, $anonymous_data['bbp_anonymous_email'], $expiry, COOKIEPATH, COOKIE_DOMAIN, $secure );
129 + setcookie( 'comment_author_url_' . COOKIEHASH, $anonymous_data['bbp_anonymous_website'], $expiry, COOKIEPATH, COOKIE_DOMAIN, $secure );
132 130 }
133 131
134 132 /**
135 133 * Get the poster IP address
136 134 *
137 - * @since bbPress (r3120)
135 + * @since 2.0.0 bbPress (r3120)
136 + * @since 2.6.0 bbPress (r5609) Added `empty()` check for unit tests
138 137 *
139 138 * @return string
140 139 */
141 140 function bbp_current_author_ip() {
142 - $retval = preg_replace( '/[^0-9a-fA-F:., ]/', '', $_SERVER['REMOTE_ADDR'] );
143 141
144 - return apply_filters( 'bbp_current_author_ip', $retval );
142 + // Check for remote address
143 + $remote_address = ! empty( $_SERVER['REMOTE_ADDR'] )
144 + ? wp_unslash( $_SERVER['REMOTE_ADDR'] )
145 + : '127.0.0.1';
146 +
147 + // Remove any unsavory bits
148 + $retval = preg_replace( '/[^0-9a-fA-F:., ]/', '', $remote_address );
149 +
150 + // Filter & return
151 + return apply_filters( 'bbp_current_author_ip', $retval, $remote_address );
145 152 }
146 153
147 154 /**
148 155 * Get the poster user agent
149 156 *
150 - * @since bbPress (r3446)
157 + * @since 2.0.0 bbPress (r3446)
151 158 *
152 159 * @return string
153 160 */
154 161 function bbp_current_author_ua() {
155 - $retval = !empty( $_SERVER['HTTP_USER_AGENT'] ) ? substr( $_SERVER['HTTP_USER_AGENT'], 0, 254 ) : '';
162 + $retval = ! empty( $_SERVER['HTTP_USER_AGENT'] )
163 + ? mb_substr( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ), 0, 254 )
164 + : '';
156 165
166 + // Filter & return
157 167 return apply_filters( 'bbp_current_author_ua', $retval );
158 168 }
159 169
160 -/** Post Counts ***************************************************************/
170 +/** Edit **********************************************************************/
161 171
162 172 /**
163 - * Return the raw database count of topics by a user
173 + * Filter user profile data according to the current user's field permissions.
164 174 *
165 - * @since bbPress (r3633)
166 - * @global WPDB $wpdb
167 - * @uses bbp_get_user_id()
168 - * @uses get_posts_by_author_sql()
169 - * @uses bbp_get_topic_post_type()
170 - * @uses apply_filters()
171 - * @return int Raw DB count of topics
175 + * @since 2.6.17
176 + *
177 + * @param array $data Submitted user profile data.
178 + * @param int $user_id User being edited.
179 + * @return array Filtered user profile data.
172 180 */
173 -function bbp_get_user_topic_count_raw( $user_id = 0 ) {
174 - $user_id = bbp_get_user_id( $user_id );
175 - if ( empty( $user_id ) )
176 - return false;
181 +function bbp_filter_user_edit_post_data( $data = array(), $user_id = 0 ) {
182 + $user_id = bbp_get_user_id( $user_id, false, false );
183 + $user = get_userdata( $user_id );
177 184
178 - global $wpdb;
185 + // Remove general profile fields.
186 + if ( ! bbp_current_user_can_edit_user_field( 'profile', $user_id ) ) {
187 + unset(
188 + $data['first_name'],
189 + $data['last_name'],
190 + $data['nickname'],
191 + $data['display_name'],
192 + $data['url'],
193 + $data['description'],
194 + $data['locale']
195 + );
179 196
180 - $where = get_posts_by_author_sql( bbp_get_topic_post_type(), true, $user_id );
181 - $count = (int) $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->posts} {$where}" );
197 + // Remove dynamic WordPress contact methods.
198 + if ( ! empty( $user ) ) {
199 + foreach ( array_keys( wp_get_user_contact_methods( $user ) ) as $contact_method ) {
200 + unset( $data[ $contact_method ] );
201 + }
202 + }
203 + }
182 204
183 - return (int) apply_filters( 'bbp_get_user_topic_count_raw', $count, $user_id );
184 -}
205 + // Preserve the existing email address.
206 + if ( ! bbp_current_user_can_edit_user_field( 'email', $user_id ) ) {
207 + $data['email'] = ! empty( $user->user_email )
208 + ? $user->user_email
209 + : '';
210 + }
185 211
186 -/**
187 - * Return the raw database count of replies by a user
188 - *
189 - * @since bbPress (r3633)
190 - * @global WPDB $wpdb
191 - * @uses bbp_get_user_id()
192 - * @uses get_posts_by_author_sql()
193 - * @uses bbp_get_reply_post_type()
194 - * @uses apply_filters()
195 - * @return int Raw DB count of replies
196 - */
197 -function bbp_get_user_reply_count_raw( $user_id = 0 ) {
198 - $user_id = bbp_get_user_id( $user_id );
199 - if ( empty( $user_id ) )
200 - return false;
212 + // Remove password fields.
213 + if ( ! bbp_current_user_can_edit_user_field( 'password', $user_id ) ) {
214 + unset( $data['pass1'], $data['pass2'] );
215 + }
201 216
202 - global $wpdb;
217 + // Remove the WordPress site role.
218 + if ( ! bbp_current_user_can_edit_user_field( 'site_role', $user_id ) ) {
219 + unset( $data['role'] );
220 + }
203 221
204 - $where = get_posts_by_author_sql( bbp_get_reply_post_type(), true, $user_id );
205 - $count = (int) $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->posts} {$where}" );
206 -
207 - return (int) apply_filters( 'bbp_get_user_reply_count_raw', $count, $user_id );
222 + // Filter & return.
223 + return (array) apply_filters( 'bbp_filter_user_edit_post_data', $data, $user_id, bbp_get_current_user_id() );
208 224 }
209 225
210 -/** Favorites *****************************************************************/
211 -
212 226 /**
213 - * Get the users who have made the topic favorite
227 + * Return whether an email change requires confirmation from the edited user.
214 228 *
215 - * @since bbPress (r2658)
229 + * Self-service changes require confirmation by default, while privileged edits
230 + * to another user update the address directly.
216 231 *
217 - * @param int $topic_id Optional. Topic id
218 - * @uses wpdb::get_col() To execute our query and get the column back
219 - * @uses apply_filters() Calls 'bbp_get_topic_favoriters' with the users and
220 - * topic id
221 - * @return array|bool Results if the topic has any favoriters, otherwise false
232 + * @since 2.6.17
233 + *
234 + * @param int $user_id User being edited.
235 + * @return bool Whether confirmation is required.
222 236 */
223 -function bbp_get_topic_favoriters( $topic_id = 0 ) {
224 - if ( empty( $topic_id ) )
225 - return;
237 +function bbp_user_email_change_requires_confirmation( $user_id = 0 ) {
238 + $user_id = bbp_get_user_id( $user_id, false, false );
239 + $retval = ( bbp_get_current_user_id() === $user_id );
226 240
227 - global $wpdb;
228 -
229 - // Get the users who have favorited the topic
230 - $key = $wpdb->prefix . '_bbp_favorites';
231 - $users = $wpdb->get_col( "SELECT user_id FROM {$wpdb->usermeta} WHERE meta_key = '{$key}' and FIND_IN_SET('{$topic_id}', meta_value) > 0" );
232 - $users = apply_filters( 'bbp_get_topic_favoriters', $users, $topic_id );
233 -
234 - if ( !empty( $users ) )
235 - return $users;
236 -
237 - return false;
241 + // Filter & return.
242 + return (bool) apply_filters( 'bbp_user_email_change_requires_confirmation', $retval, $user_id, bbp_get_current_user_id() );
238 243 }
239 244
240 245 /**
241 - * Get a user's favorite topics
246 + * Handles the front end user editing from POST requests
242 247 *
243 - * @since bbPress (r2652)
248 + * @since 2.0.0 bbPress (r2790)
244 249 *
245 - * @param int $user_id Optional. User id
246 - * @uses bbp_get_user_favorites_topic_ids() To get the user's favorites
247 - * @uses bbp_has_topics() To get the topics
248 - * @uses apply_filters() Calls 'bbp_get_user_favorites' with the topic query and
249 - * user id
250 - * @return array|bool Results if user has favorites, otherwise false
250 + * @param string $action The requested action to compare this function to
251 251 */
252 -function bbp_get_user_favorites( $user_id = 0 ) {
253 - $user_id = bbp_get_user_id( $user_id );
254 - if ( empty( $user_id ) )
255 - return false;
252 +function bbp_edit_user_handler( $action = '' ) {
256 253
257 - // If user has favorites, load them
258 - $favorites = bbp_get_user_favorites_topic_ids( $user_id );
259 - if ( !empty( $favorites ) ) {
254 + // Bail if action is not `bbp-update-user`
255 + if ( 'bbp-update-user' !== $action ) {
256 + return;
257 + }
260 258
261 - // Setup the topics query
262 - $topics_query = bbp_has_topics( array( 'post__in' => $favorites ) );
259 + // Bail if in wp-admin
260 + if ( is_admin() ) {
261 + return;
262 + }
263 263
264 - return apply_filters( 'bbp_get_user_favorites', $topics_query, $user_id );
264 + // Get the displayed user ID
265 + $user_id = bbp_get_displayed_user_id();
266 +
267 + // Request check
268 + if ( ! bbp_is_user_profile_form_post_request( $user_id ) ) {
269 + bbp_add_error( 'bbp_update_user_nonce', __( '<strong>Error</strong>: Are you sure you wanted to do that?', 'bbpress' ) );
270 + return;
265 271 }
266 272
267 - return false;
268 -}
273 + // Cap check
274 + if ( ! current_user_can( 'edit_user', $user_id ) ) {
275 + bbp_add_error( 'bbp_update_user_capability', __( '<strong>Error</strong>: Are you sure you wanted to do that?', 'bbpress' ) );
276 + return;
277 + }
269 278
270 -/**
271 - * Get a user's favorite topics' ids
272 - *
273 - * @since bbPress (r2652)
274 - *
275 - * @param int $user_id Optional. User id
276 - * @uses bbp_get_user_id() To get the user id
277 - * @uses get_user_option() To get the user favorites
278 - * @uses apply_filters() Calls 'bbp_get_user_favorites_topic_ids' with
279 - * the favorites and user id
280 - * @return array|bool Results if user has favorites, otherwise false
281 - */
282 -function bbp_get_user_favorites_topic_ids( $user_id = 0 ) {
283 - $user_id = bbp_get_user_id( $user_id );
284 - if ( empty( $user_id ) )
285 - return false;
279 + // Enforce field-level profile permissions before validating and saving.
280 + $_POST = bbp_filter_user_edit_post_data( $_POST, $user_id );
286 281
287 - $favorites = (string) get_user_option( '_bbp_favorites', $user_id );
288 - $favorites = (array) explode( ',', $favorites );
289 - $favorites = array_filter( $favorites );
282 + // Empty email check
283 + if ( empty( $_POST['email'] ) ) {
284 + bbp_add_error( 'bbp_user_email_empty', __( '<strong>Error</strong>: That is not a valid email address.', 'bbpress' ), array( 'form-field' => 'email' ) );
285 + return;
286 + }
290 287
291 - return apply_filters( 'bbp_get_user_favorites_topic_ids', $favorites, $user_id );
292 -}
288 + // Get the users current email address to use for comparisons
289 + $user_email = bbp_get_displayed_user_field( 'user_email', 'raw' );
293 290
294 -/**
295 - * Check if a topic is in user's favorites or not
296 - *
297 - * @since bbPress (r2652)
298 - *
299 - * @param int $user_id Optional. User id
300 - * @param int $topic_id Optional. Topic id
301 - * @uses bbp_get_user_id() To get the user id
302 - * @uses bbp_get_user_favorites_topic_ids() To get the user favorites
303 - * @uses bbp_get_topic() To get the topic
304 - * @uses bbp_get_topic_id() To get the topic id
305 - * @uses apply_filters() Calls 'bbp_is_user_favorite' with the bool, user id,
306 - * topic id and favorites
307 - * @return bool True if the topic is in user's favorites, otherwise false
308 - */
309 -function bbp_is_user_favorite( $user_id = 0, $topic_id = 0 ) {
291 + // Bail if no email change
292 + if ( $user_email !== $_POST['email'] ) {
310 293
311 - $user_id = bbp_get_user_id( $user_id, true, true );
312 - if ( empty( $user_id ) )
313 - return false;
294 + // Check that new email address is valid
295 + if ( ! is_email( $_POST['email'] ) ) {
296 + bbp_add_error( 'bbp_user_email_invalid', __( '<strong>Error</strong>: That is not a valid email address.', 'bbpress' ), array( 'form-field' => 'email' ) );
297 + return;
298 + }
314 299
315 - $retval = false;
316 - $favorites = bbp_get_user_favorites_topic_ids( $user_id );
300 + // Check if email address is already in use
301 + if ( email_exists( $_POST['email'] ) ) {
302 + bbp_add_error( 'bbp_user_email_taken', __( '<strong>Error</strong>: That email address is already in use.', 'bbpress' ), array( 'form-field' => 'email' ) );
303 + return;
304 + }
317 305
318 - if ( !empty( $favorites ) ) {
319 -
320 - // Checking a specific topic id
321 - if ( !empty( $topic_id ) ) {
322 - $topic = bbp_get_topic( $topic_id );
323 - $topic_id = !empty( $topic ) ? $topic->ID : 0;
306 + if ( bbp_user_email_change_requires_confirmation( $user_id ) ) {
307 + // Update the option.
308 + $option = array(
309 + 'hash' => md5( $_POST['email'] . time() . wp_rand() ),
310 + 'newemail' => $_POST['email'],
311 + );
312 + update_user_meta( $user_id, '_new_email', $option );
324 313
325 - // Using the global topic id
326 - } elseif ( bbp_get_topic_id() ) {
327 - $topic_id = bbp_get_topic_id();
314 + // Attempt to notify the user of email address change.
315 + bbp_edit_user_email_send_notification( $user_id, $option );
328 316
329 - // Use the current post id
330 - } elseif ( !bbp_get_topic_id() ) {
331 - $topic_id = get_the_ID();
317 + // Set the POST email variable back to the user's email address
318 + // so `edit_user()` does not attempt to update it. This is not ideal,
319 + // but it's also what send_confirmation_on_profile_email() does.
320 + $_POST['email'] = $user_email;
332 321 }
333 -
334 - // Is topic_id in the user's favorites
335 - if ( !empty( $topic_id ) ) {
336 - $retval = in_array( $topic_id, $favorites );
337 - }
338 322 }
339 323
340 - return (bool) apply_filters( 'bbp_is_user_favorite', (bool) $retval, $user_id, $topic_id, $favorites );
341 -}
324 + // Do action based on who's profile you're editing
325 + $edit_action = bbp_is_user_home_edit()
326 + ? 'personal_options_update'
327 + : 'edit_user_profile_update';
342 328
343 -/**
344 - * Add a topic to user's favorites
345 - *
346 - * @since bbPress (r2652)
347 - *
348 - * @param int $user_id Optional. User id
349 - * @param int $topic_id Optional. Topic id
350 - * @uses bbp_get_user_favorites_topic_ids() To get the user favorites
351 - * @uses update_user_option() To update the user favorites
352 - * @uses do_action() Calls 'bbp_add_user_favorite' with the user id and topic id
353 - * @return bool Always true
354 - */
355 -function bbp_add_user_favorite( $user_id = 0, $topic_id = 0 ) {
356 - if ( empty( $user_id ) || empty( $topic_id ) )
357 - return false;
329 + do_action( $edit_action, $user_id );
358 330
359 - $favorites = (array) bbp_get_user_favorites_topic_ids( $user_id );
360 - $topic = bbp_get_topic( $topic_id );
361 - if ( empty( $topic ) )
362 - return false;
331 + // Prevent edit_user() from wiping out the user's Toolbar on front setting
332 + if ( ! isset( $_POST['admin_bar_front'] ) && _get_admin_bar_pref( 'front', $user_id ) ) {
333 + $_POST['admin_bar_front'] = 1;
334 + }
363 335
364 - if ( !in_array( $topic_id, $favorites ) ) {
365 - $favorites[] = $topic_id;
366 - $favorites = array_filter( $favorites );
367 - $favorites = (string) implode( ',', $favorites );
368 - update_user_option( $user_id, '_bbp_favorites', $favorites );
336 + // Bail if errors already exist
337 + if ( bbp_has_errors() ) {
338 + return;
369 339 }
370 340
371 - do_action( 'bbp_add_user_favorite', $user_id, $topic_id );
341 + // Handle user edit
342 + $edit_user = edit_user( $user_id );
372 343
373 - return true;
374 -}
344 + // Error(s) editng the user, so copy them into the global
345 + if ( is_wp_error( $edit_user ) ) {
346 + bbpress()->errors = $edit_user;
375 347
376 -/**
377 - * Remove a topic from user's favorites
378 - *
379 - * @since bbPress (r2652)
380 - *
381 - * @param int $user_id Optional. User id
382 - * @param int $topic_id Optional. Topic id
383 - * @uses bbp_get_user_favorites_topic_ids() To get the user favorites
384 - * @uses update_user_option() To update the user favorites
385 - * @uses delete_user_option() To delete the user favorites meta
386 - * @uses do_action() Calls 'bbp_remove_user_favorite' with the user & topic id
387 - * @return bool True if the topic was removed from user's favorites, otherwise
388 - * false
389 - */
390 -function bbp_remove_user_favorite( $user_id, $topic_id ) {
391 - if ( empty( $user_id ) || empty( $topic_id ) )
392 - return false;
348 + // Successful edit to redirect
349 + } elseif ( is_integer( $edit_user ) ) {
393 350
394 - $favorites = (array) bbp_get_user_favorites_topic_ids( $user_id );
395 - if ( empty( $favorites ) )
396 - return false;
351 + // Maybe update super admin ability
352 + if ( is_multisite() && ! bbp_is_user_home_edit() && current_user_can( 'manage_network_options' ) && is_super_admin() ) {
353 + empty( $_POST['super_admin'] )
354 + ? revoke_super_admin( $edit_user )
355 + : grant_super_admin( $edit_user );
356 + }
397 357
398 - $pos = array_search( $topic_id, $favorites );
399 - if ( is_numeric( $pos ) ) {
400 - array_splice( $favorites, $pos, 1 );
401 - $favorites = array_filter( $favorites );
358 + // Redirect
359 + $args = array( 'updated' => 'true' );
360 + $user_url = bbp_get_user_profile_edit_url( $edit_user );
361 + $redirect = add_query_arg( $args, $user_url );
402 362
403 - if ( !empty( $favorites ) ) {
404 - $favorites = implode( ',', $favorites );
405 - update_user_option( $user_id, '_bbp_favorites', $favorites );
406 - } else {
407 - delete_user_option( $user_id, '_bbp_favorites' );
408 - }
363 + bbp_redirect( $redirect );
409 364 }
410 -
411 - do_action( 'bbp_remove_user_favorite', $user_id, $topic_id );
412 -
413 - return true;
414 365 }
415 366
416 367 /**
417 - * Handles the front end adding and removing of favorite topics
368 + * Handles user email address updating from GET requests
418 369 *
419 - * @uses bbp_get_user_id() To get the user id
420 - * @uses bbp_verify_nonce_request() To verify the nonce and check the request
421 - * @uses current_user_can() To check if the current user can edit the user
422 - * @uses bbPress:errors:add() To log the error messages
423 - * @uses bbp_is_user_favorite() To check if the topic is in user's favorites
424 - * @uses bbp_remove_user_favorite() To remove the user favorite
425 - * @uses bbp_add_user_favorite() To add the user favorite
426 - * @uses do_action() Calls 'bbp_favorites_handler' with success, user id, topic
427 - * id and action
428 - * @uses bbp_is_favorites() To check if it's the favorites page
429 - * @uses bbp_get_favorites_link() To get the favorites page link
430 - * @uses bbp_get_topic_permalink() To get the topic permalink
431 - * @uses wp_safe_redirect() To redirect to the url
370 + * @since 2.6.0 bbPress (r5660)
371 + *
372 + * @param string $action
432 373 */
433 -function bbp_favorites_handler() {
374 +function bbp_user_email_change_handler( $action = '' ) {
434 375
435 - if ( !bbp_is_favorites_active() )
436 - return false;
376 + // Bail if action is not `bbp-update-user-email`
377 + if ( 'bbp-update-user-email' !== $action ) {
378 + return;
379 + }
437 380
438 - // Bail if not a GET action
439 - if ( 'GET' !== strtoupper( $_SERVER['REQUEST_METHOD'] ) )
381 + // Bail if not on users own profile
382 + if ( ! bbp_is_user_home_edit() ) {
440 383 return;
384 + }
441 385
442 - // Bail if required GET actions aren't passed
443 - if ( empty( $_GET['topic_id'] ) || empty( $_GET['action'] ) )
386 + // Bail if not attempting to modify user email address
387 + if ( empty( $_GET['newuseremail'] ) && empty( $_GET['dismiss'] ) ) {
444 388 return;
389 + }
445 390
446 - // Setup possible get actions
447 - $possible_actions = array(
448 - 'bbp_favorite_add',
449 - 'bbp_favorite_remove',
450 - );
391 + // Get the displayed user ID & option key
392 + $user_id = bbp_get_displayed_user_id();
393 + $key = '_new_email';
394 + $redirect_to = bbp_get_user_profile_edit_url( $user_id );
451 395
452 - // Bail if actions aren't meant for this function
453 - if ( !in_array( $_GET['action'], $possible_actions ) )
454 - return;
396 + // Execute confirmed email change.
397 + if ( ! empty( $_GET['newuseremail'] ) ) {
455 398
456 - // What action is taking place?
457 - $action = $_GET['action'];
458 - $topic_id = intval( $_GET['topic_id'] );
459 - $user_id = bbp_get_user_id( 0, true, true );
399 + // Check for email address change option
400 + $new_email = get_user_meta( $user_id, $key, true );
460 401
461 - // Check for empty topic
462 - if ( empty( $topic_id ) ) {
463 - bbp_add_error( 'bbp_favorite_topic_id', __( '<strong>ERROR</strong>: No topic was found! Which topic are you marking/unmarking as favorite?', 'bbpress' ) );
402 + // Redirect if *no* email address change exists
403 + if ( false === $new_email ) {
404 + bbp_redirect( $redirect_to );
405 + }
464 406
465 - // Check nonce
466 - } elseif ( ! bbp_verify_nonce_request( 'toggle-favorite_' . $topic_id ) ) {
467 - bbp_add_error( 'bbp_favorite_nonce', __( '<strong>ERROR</strong>: Are you sure you wanted to do that?', 'bbpress' ) );
407 + // Cleanup & redirect if *invalid* email address change exists
408 + if ( empty( $new_email['hash'] ) || empty( $new_email['newemail'] ) ) {
409 + delete_user_meta( $user_id, $key );
468 410
469 - // Check current user's ability to edit the user
470 - } elseif ( !current_user_can( 'edit_user', $user_id ) ) {
471 - bbp_add_error( 'bbp_favorite_permissions', __( '<strong>ERROR</strong>: You don\'t have the permission to edit favorites of that user!', 'bbpress' ) );
472 - }
411 + bbp_redirect( $redirect_to );
412 + }
473 413
474 - // Bail if errors
475 - if ( bbp_has_errors() )
476 - return;
414 + // Compare hashes, and update user if hashes match
415 + if ( hash_equals( $new_email['hash'], $_GET['newuseremail'] ) ) {
477 416
478 - /** No errors *************************************************************/
417 + // Does another user have this email address already?
418 + if ( email_exists( $new_email['newemail'] ) ) {
419 + delete_user_meta( $user_id, $key );
479 420
480 - $is_favorite = bbp_is_user_favorite( $user_id, $topic_id );
481 - $success = false;
421 + bbp_add_error( 'bbp_user_email_taken', __( '<strong>Error</strong>: That email address is already in use.', 'bbpress' ), array( 'form-field' => 'email' ) );
482 422
483 - if ( true == $is_favorite && 'bbp_favorite_remove' == $action )
484 - $success = bbp_remove_user_favorite( $user_id, $topic_id );
485 - elseif ( false == $is_favorite && 'bbp_favorite_add' == $action )
486 - $success = bbp_add_user_favorite( $user_id, $topic_id );
423 + // Email address is good to change to
424 + } else {
487 425
488 - // Do additional favorites actions
489 - do_action( 'bbp_favorites_handler', $success, $user_id, $topic_id, $action );
426 + // Create a stdClass (for easy call to wp_update_user())
427 + $user = new stdClass();
428 + $user->ID = $user_id;
429 + $user->user_email = esc_html( trim( $new_email['newemail'] ) );
490 430
491 - // Success!
492 - if ( true == $success ) {
431 + // Attempt to update user email
432 + $update_user = wp_update_user( $user );
493 433
494 - // Redirect back from whence we came
495 - if ( bbp_is_favorites() ) {
496 - $redirect = bbp_get_favorites_permalink( $user_id );
497 - } elseif ( bbp_is_single_user() ) {
498 - $redirect = bbp_get_user_profile_url();
499 - } elseif ( is_singular( bbp_get_topic_post_type() ) ) {
500 - $redirect = bbp_get_topic_permalink( $topic_id );
501 - } elseif ( is_single() || is_page() ) {
502 - $redirect = get_permalink();
434 + // Error(s) editing the user, so copy them into the global
435 + if ( is_wp_error( $update_user ) ) {
436 + bbpress()->errors = $update_user;
437 +
438 + // All done, so redirect and show the updated message
439 + } else {
440 +
441 + // Update signups table, if signups table & entry exists
442 + // For Multisite & BuddyPress compatibility
443 + $bbp_db = bbp_db();
444 + if ( ! empty( $bbp_db->signups ) && $bbp_db->get_var( $bbp_db->prepare( "SELECT user_login FROM {$bbp_db->signups} WHERE user_login = %s", bbp_get_displayed_user_field( 'user_login', 'raw' ) ) ) ) {
445 + $bbp_db->query( $bbp_db->prepare( "UPDATE {$bbp_db->signups} SET user_email = %s WHERE user_login = %s", $user->user_email, bbp_get_displayed_user_field( 'user_login', 'raw' ) ) );
446 + }
447 +
448 + delete_user_meta( $user_id, $key );
449 +
450 + bbp_redirect( add_query_arg( array( 'updated' => 'true' ), $redirect_to ) );
451 + }
452 + }
503 453 }
504 454
505 - wp_safe_redirect( $redirect );
455 + // Delete new email address from user options
456 + } elseif ( ! empty( $_GET['dismiss'] ) && ( "{$user_id}{$key}" === $_GET['dismiss'] ) ) {
457 + if ( ! bbp_verify_nonce_request( "dismiss-{$user_id}{$key}" ) ) {
458 + bbp_add_error( 'bbp_dismiss_new_email_nonce', __( '<strong>Error</strong>: Are you sure you wanted to do that?', 'bbpress' ) );
459 + return;
460 + }
506 461
507 - // For good measure
508 - exit();
509 -
510 - // Fail! Handle errors
511 - } elseif ( true == $is_favorite && 'bbp_favorite_remove' == $action ) {
512 - bbp_add_error( 'bbp_favorite_remove', __( '<strong>ERROR</strong>: There was a problem removing that topic from favorites!', 'bbpress' ) );
513 - } elseif ( false == $is_favorite && 'bbp_favorite_add' == $action ) {
514 - bbp_add_error( 'bbp_favorite_add', __( '<strong>ERROR</strong>: There was a problem favoriting that topic!', 'bbpress' ) );
462 + delete_user_meta( $user_id, $key );
463 + bbp_redirect( $redirect_to );
515 464 }
516 465 }
517 466
518 -/** Subscriptions *************************************************************/
519 -
520 467 /**
521 - * Get the users who have subscribed to the topic
468 + * Sends an email when an email address change occurs on POST requests
522 469 *
523 - * @since bbPress (r2668)
470 + * @since 2.6.0 bbPress (r5660)
524 471 *
525 - * @param int $topic_id Optional. Topic id
526 - * @uses wpdb::get_col() To execute our query and get the column back
527 - * @uses apply_filters() Calls 'bbp_get_topic_subscribers' with the subscribers
528 - * @return array|bool Results if the topic has any subscribers, otherwise false
472 + * @see send_confirmation_on_profile_email()
529 473 */
530 -function bbp_get_topic_subscribers( $topic_id = 0 ) {
531 - if ( empty( $topic_id ) ) return;
474 +function bbp_edit_user_email_send_notification( $user_id = 0, $args = array() ) {
532 475
533 - global $wpdb;
476 + // Parse args
477 + $r = bbp_parse_args(
478 + $args,
479 + array(
480 + 'hash' => '',
481 + 'newemail' => '',
482 + )
483 + );
534 484
535 - $key = $wpdb->prefix . '_bbp_subscriptions';
536 - $users = wp_cache_get( 'bbp_get_topic_subscribers_' . $topic_id, 'bbpress' );
537 - if ( empty( $users ) ) {
538 - $users = $wpdb->get_col( "SELECT user_id FROM {$wpdb->usermeta} WHERE meta_key = '{$key}' and FIND_IN_SET('{$topic_id}', meta_value) > 0" );
539 - wp_cache_set( 'bbp_get_topic_subscribers_' . $topic_id, $users, 'bbpress' );
485 + // Bail if any relevant parameters are empty
486 + if ( empty( $user_id ) || empty( $r['hash'] ) || empty( $r['newemail'] ) ) {
487 + bbp_add_error( 'bbp_user_email_invalid_hash', __( '<strong>Error</strong>: An error occurred while updating your email address.', 'bbpress' ), array( 'form-field' => 'email' ) );
488 + return;
540 489 }
541 490
542 - if ( !empty( $users ) ) {
543 - $users = apply_filters( 'bbp_get_topic_subscribers', $users );
544 - return $users;
545 - }
491 + // Build the nonced URL to dismiss the pending change
492 + $user_login = bbp_get_displayed_user_field( 'user_login', 'raw' );
493 + $user_url = bbp_get_user_profile_edit_url( $user_id );
494 + $confirm_url = add_query_arg(
495 + array(
496 + 'action' => 'bbp-update-user-email',
497 + 'newuseremail' => $r['hash']
498 + ),
499 + $user_url
500 + );
546 501
547 - return false;
502 + /* translators: 1: Username, 2: Confirmation URL, 3: New email address, 4: Site name, 5: Site URL */
503 + $email_text = __(
504 + '%1$s
505 +
506 +Someone requested a change to the email address on your account.
507 +
508 +Please click the following link to confirm this change:
509 +%2$s
510 +
511 +If you did not request this, you can safely ignore and delete this notification.
512 +
513 +This email was sent to: %3$s
514 +
515 +Regards,
516 +The %4$s Team
517 +%5$s',
518 + 'bbpress'
519 + );
520 +
521 + /**
522 + * Filter the email text sent when a user changes emails.
523 + *
524 + * The following strings have a special meaning and will get replaced dynamically:
525 + *
526 + * %1$s - The current user's username
527 + * %2$s - The link to click on to confirm the email change
528 + * %3$s - The new email
529 + * %4$s - The name of the site
530 + * %5$s - The URL to the site
531 + *
532 + * @param string $email_text Text in the email.
533 + * @param string $r New user email that the current user has changed to.
534 + */
535 + $content = apply_filters( 'bbp_user_email_update_content', $email_text, $r );
536 +
537 + // Build the email message
538 + $message = sprintf( $content, $user_login, $confirm_url, $r['newemail'], get_site_option( 'site_name' ), network_home_url() );
539 +
540 + // Build the email subject
541 + /* translators: %s: Site name */
542 + $subject = sprintf( __( '[%s] New Email Address', 'bbpress' ), wp_specialchars_decode( get_option( 'blogname' ) ) );
543 +
544 + // Send the email
545 + wp_mail( $r['newemail'], $subject, $message );
548 546 }
549 547
550 548 /**
551 - * Get a user's subscribed topics
549 + * Conditionally hook the core WordPress output actions to the end of the
550 + * default user's edit profile template
552 551 *
553 - * @since bbPress (r2668)
552 + * This allows clever plugin authors to conditionally unhook the WordPress core
553 + * output actions if they don't want any unexpected junk to appear there, and
554 + * also avoids needing to pollute the templates with additional logic and actions.
554 555 *
555 - * @param int $user_id Optional. User id
556 - * @uses bbp_get_user_subscribed_topic_ids() To get the user's subscriptions
557 - * @uses bbp_has_topics() To get the topics
558 - * @uses apply_filters() Calls 'bbp_get_user_subscriptions' with the topic query
559 - * and user id
560 - * @return array|bool Results if user has subscriptions, otherwise false
556 + * @since 2.2.0 bbPress (r4273)
561 557 */
562 -function bbp_get_user_subscriptions( $user_id = 0 ) {
558 +function bbp_user_edit_after() {
559 + $action = bbp_is_user_home_edit() ? 'show_user_profile' : 'edit_user_profile';
563 560
564 - // Default to the displayed user
565 - $user_id = bbp_get_user_id( $user_id );
566 - if ( empty( $user_id ) )
567 - return false;
561 + do_action( $action, get_userdata( bbp_get_displayed_user_id() ) );
562 +}
568 563
569 - // If user has subscriptions, load them
570 - $subscriptions = bbp_get_user_subscribed_topic_ids( $user_id );
571 - if ( !empty( $subscriptions ) ) {
572 - $query = bbp_has_topics( array( 'post__in' => $subscriptions ) );
573 - return apply_filters( 'bbp_get_user_subscriptions', $query, $user_id );
564 +/** User Queries **************************************************************/
565 +
566 +/**
567 + * Get the topics that a user created
568 + *
569 + * @since 2.0.0 bbPress (r2660)
570 + * @since 2.6.0 bbPress (r6618) Signature changed to accept an array of arguments
571 + *
572 + * @param array $args Optional. Arguments to pass into bbp_has_topics()
573 + *
574 + * @return bool True if user has started topics, otherwise false
575 + */
576 +function bbp_get_user_topics_started( $args = array() ) {
577 +
578 + // Backwards compat for pre-2.6.0
579 + if ( is_numeric( $args ) ) {
580 + $args = array(
581 + 'author' => bbp_get_user_id( $args, false, false )
582 + );
574 583 }
575 584
576 - return false;
585 + // Default arguments
586 + $defaults = array(
587 + 'author' => bbp_get_displayed_user_id()
588 + );
589 +
590 + // Parse arguments
591 + $r = bbp_parse_args( $args, $defaults, 'get_user_topics_started' );
592 +
593 + // Get the topics
594 + $query = bbp_has_topics( $r );
595 + $user_id = $r['author'];
596 +
597 + // Filter & return
598 + return apply_filters( 'bbp_get_user_topics_started', $query, $user_id, $r, $args );
577 599 }
578 600
579 601 /**
580 - * Get a user's subscribed topics' ids
602 + * Get the replies that a user created
581 603 *
582 - * @since bbPress (r2668)
604 + * @since 2.2.0 bbPress (r4225)
605 + * @since 2.6.0 bbPress (r6618) Signature changed to accept an array of arguments
583 606 *
584 - * @param int $user_id Optional. User id
585 - * @uses bbp_get_user_id() To get the user id
586 - * @uses get_user_option() To get the user's subscriptions
587 - * @uses apply_filters() Calls 'bbp_get_user_subscribed_topic_ids' with
588 - * the subscriptions and user id
589 - * @return array|bool Results if user has subscriptions, otherwise false
607 + * @param array $args Optional. Arguments to pass into bbp_has_replies()
608 + *
609 + * @return bool True if user has created replies, otherwise false
590 610 */
591 -function bbp_get_user_subscribed_topic_ids( $user_id = 0 ) {
592 - $user_id = bbp_get_user_id( $user_id );
593 - if ( empty( $user_id ) )
594 - return false;
611 +function bbp_get_user_replies_created( $args = array() ) {
595 612
596 - $subscriptions = (string) get_user_option( '_bbp_subscriptions', $user_id );
597 - $subscriptions = (array) explode( ',', $subscriptions );
598 - $subscriptions = array_filter( $subscriptions );
613 + // Backwards compat for pre-2.6.0
614 + if ( is_numeric( $args ) ) {
615 + $args = array(
616 + 'author' => bbp_get_user_id( $args, false, false ),
617 + 'post_type' => bbp_get_reply_post_type(),
618 + 'order' => 'DESC'
619 + );
620 + }
599 621
600 - return apply_filters( 'bbp_get_user_subscribed_topic_ids', $subscriptions, $user_id );
622 + // Default arguments
623 + $defaults = array(
624 + 'author' => bbp_get_displayed_user_id(),
625 + 'post_type' => bbp_get_reply_post_type(),
626 + 'order' => 'DESC'
627 + );
628 +
629 + // Parse arguments
630 + $r = bbp_parse_args( $args, $defaults, 'get_user_replies_created' );
631 +
632 + // Get the replies
633 + $query = bbp_has_replies( $r );
634 + $user_id = $r['author'];
635 +
636 + // Filter & return
637 + return apply_filters( 'bbp_get_user_replies_created', $query, $user_id, $r, $args );
601 638 }
602 639
603 640 /**
604 - * Check if a topic is in user's subscription list or not
641 + * Get user IDs from nicenames
605 642 *
606 - * @since bbPress (r2668)
643 + * This function is primarily used when saving object moderators
607 644 *
608 - * @param int $user_id Optional. User id
609 - * @param int $topic_id Optional. Topic id
610 - * @uses bbp_get_user_id() To get the user id
611 - * @uses bbp_get_user_subscribed_topic_ids() To get the user's subscriptions
612 - * @uses bbp_get_topic() To get the topic
613 - * @uses bbp_get_topic_id() To get the topic id
614 - * @uses apply_filters() Calls 'bbp_is_user_subscribed' with the bool, user id,
615 - * topic id and subsriptions
616 - * @return bool True if the topic is in user's subscriptions, otherwise false
645 + * @since 2.6.0 bbPress
646 + *
647 + * @param mixed $user_nicenames
648 + * @return array
617 649 */
618 -function bbp_is_user_subscribed( $user_id = 0, $topic_id = 0 ) {
650 +function bbp_get_user_ids_from_nicenames( $user_nicenames = array() ) {
619 651
620 - // Validate user
621 - $user_id = bbp_get_user_id( $user_id, true, true );
622 - if ( empty( $user_id ) )
623 - return false;
652 + // Default value
653 + $retval = array();
624 654
625 - $retval = false;
626 - $subscriptions = bbp_get_user_subscribed_topic_ids( $user_id );
655 + // Only query if nicenames
656 + if ( ! empty( $user_nicenames ) ) {
627 657
628 - if ( !empty( $subscriptions ) ) {
658 + // Maybe explode by comma
659 + $user_nicenames = ( is_string( $user_nicenames ) && strstr( $user_nicenames, ',' ) )
660 + ? explode( ',', $user_nicenames )
661 + : (array) $user_nicenames;
629 662
630 - // Checking a specific topic id
631 - if ( !empty( $topic_id ) ) {
632 - $topic = bbp_get_topic( $topic_id );
633 - $topic_id = !empty( $topic ) ? $topic->ID : 0;
663 + // Sanitize each nicename in the array
664 + $user_nicenames = array_map( 'sanitize_title', $user_nicenames );
634 665
635 - // Using the global topic id
636 - } elseif ( bbp_get_topic_id() ) {
637 - $topic_id = bbp_get_topic_id();
666 + // Get users
667 + $users = get_users(
668 + array(
669 + 'nicename__in' => $user_nicenames
670 + )
671 + );
638 672
639 - // Use the current post id
640 - } elseif ( !bbp_get_topic_id() ) {
641 - $topic_id = get_the_ID();
673 + // Pluck or empty
674 + if ( ! empty( $users ) ) {
675 + $retval = wp_list_pluck( $users, 'ID' );
642 676 }
677 + }
643 678
644 - // Is topic_id in the user's favorites
645 - if ( !empty( $topic_id ) ) {
646 - $retval = in_array( $topic_id, $subscriptions );
679 + // Filter & return
680 + return (array) apply_filters( 'bbp_get_user_ids_from_nicenames', $retval, $user_nicenames );
681 +}
682 +
683 +/**
684 + * Get user nicenames from IDs
685 + *
686 + * This function is primarily used when saving object moderators
687 + *
688 + * @since 2.6.0 bbPress
689 + *
690 + * @param mixed $user_ids
691 + * @return array
692 + */
693 +function bbp_get_user_nicenames_from_ids( $user_ids = array() ) {
694 +
695 + // Default value
696 + $retval = array();
697 +
698 + // Only query if nicenames
699 + if ( ! empty( $user_ids ) ) {
700 +
701 + // Get users
702 + $users = get_users(
703 + array(
704 + 'include' => $user_ids
705 + )
706 + );
707 +
708 + // Pluck or empty
709 + if ( ! empty( $users ) ) {
710 + $retval = wp_list_pluck( $users, 'user_nicename' );
647 711 }
648 712 }
649 713
650 - return (bool) apply_filters( 'bbp_is_user_subscribed', (bool) $retval, $user_id, $topic_id, $subscriptions );
714 + // Filter & return
715 + return (array) apply_filters( 'bbp_get_user_nicenames_from_ids', $retval, $user_ids );
651 716 }
652 717
718 +/** Post Counts ***************************************************************/
719 +
653 720 /**
654 - * Add a topic to user's subscriptions
721 + * Return the raw database count of topics by a user
655 722 *
656 - * @since bbPress (r2668)
723 + * @since 2.1.0 bbPress (r3633)
657 724 *
658 - * @param int $user_id Optional. User id
659 - * @param int $topic_id Optional. Topic id
660 - * @uses bbp_get_user_subscribed_topic_ids() To get the user's subscriptions
661 - * @uses bbp_get_topic() To get the topic
662 - * @uses update_user_option() To update the user's subscriptions
663 - * @uses do_action() Calls 'bbp_add_user_subscription' with the user & topic id
664 - * @return bool Always true
725 + * @param int $user_id User ID to get count for
726 + *
727 + * @return int Raw DB count of topics
665 728 */
666 -function bbp_add_user_subscription( $user_id = 0, $topic_id = 0 ) {
667 - if ( empty( $user_id ) || empty( $topic_id ) )
668 - return false;
729 +function bbp_get_user_topic_count_raw( $user_id = 0 ) {
730 + $user_id = bbp_get_user_id( $user_id );
731 + $bbp_db = bbp_db();
732 + $statii = "'" . implode( "', '", bbp_get_public_topic_statuses() ) . "'";
733 + $sql = "SELECT COUNT(*)
734 + FROM {$bbp_db->posts}
735 + WHERE post_type = %s
736 + AND post_status IN ({$statii})
737 + AND post_author = %d";
669 738
670 - $subscriptions = (array) bbp_get_user_subscribed_topic_ids( $user_id );
739 + $query = $bbp_db->prepare( $sql, bbp_get_topic_post_type(), $user_id );
740 + $count = (int) $bbp_db->get_var( $query );
671 741
672 - $topic = bbp_get_topic( $topic_id );
673 - if ( empty( $topic ) )
674 - return false;
742 + // Filter & return
743 + return (int) apply_filters( 'bbp_get_user_topic_count_raw', $count, $user_id );
744 +}
675 745
676 - if ( !in_array( $topic_id, $subscriptions ) ) {
677 - $subscriptions[] = $topic_id;
678 - $subscriptions = array_filter( $subscriptions );
679 - $subscriptions = (string) implode( ',', $subscriptions );
680 - update_user_option( $user_id, '_bbp_subscriptions', $subscriptions );
746 +/**
747 + * Return the raw database count of replies by a user
748 + *
749 + * @since 2.1.0 bbPress (r3633)
750 + *
751 + * @param int $user_id User ID to get count for
752 + *
753 + * @return int Raw DB count of replies
754 + */
755 +function bbp_get_user_reply_count_raw( $user_id = 0 ) {
756 + $user_id = bbp_get_user_id( $user_id );
757 + $bbp_db = bbp_db();
758 + $statii = "'" . implode( "', '", bbp_get_public_reply_statuses() ) . "'";
759 + $sql = "SELECT COUNT(*)
760 + FROM {$bbp_db->posts}
761 + WHERE post_type = %s
762 + AND post_status IN ({$statii})
763 + AND post_author = %d";
681 764
682 - wp_cache_delete( 'bbp_get_topic_subscribers_' . $topic_id, 'bbpress' );
683 - }
765 + $query = $bbp_db->prepare( $sql, bbp_get_reply_post_type(), $user_id );
766 + $count = (int) $bbp_db->get_var( $query );
684 767
685 - do_action( 'bbp_add_user_subscription', $user_id, $topic_id );
686 -
687 - return true;
768 + // Filter & return
769 + return (int) apply_filters( 'bbp_get_user_reply_count_raw', $count, $user_id );
688 770 }
689 771
690 772 /**
691 - * Remove a topic from user's subscriptions
773 + * Bump the topic count for a user by a certain amount.
692 774 *
693 - * @since bbPress (r2668)
775 + * @since 2.6.0 bbPress (r5309)
776 + * @since 2.6.17 Rebuild the count when the user option is missing.
694 777 *
695 - * @param int $user_id Optional. User id
696 - * @param int $topic_id Optional. Topic id
697 - * @uses bbp_get_user_subscribed_topic_ids() To get the user's subscriptions
698 - * @uses update_user_option() To update the user's subscriptions
699 - * @uses delete_user_option() To delete the user's subscriptions meta
700 - * @uses do_action() Calls 'bbp_remove_user_subscription' with the user id and
701 - * topic id
702 - * @return bool True if the topic was removed from user's subscriptions,
703 - * otherwise false
778 + * @param int $user_id
779 + * @param int $difference
704 780 */
705 -function bbp_remove_user_subscription( $user_id, $topic_id ) {
706 - if ( empty( $user_id ) || empty( $topic_id ) )
781 +function bbp_bump_user_topic_count( $user_id = 0, $difference = 1 ) {
782 +
783 + // Bail if no bump
784 + if ( empty( $difference ) ) {
707 785 return false;
786 + }
708 787
709 - $subscriptions = (array) bbp_get_user_subscribed_topic_ids( $user_id );
710 -
711 - if ( empty( $subscriptions ) )
788 + // Validate user ID
789 + $user_id = bbp_get_user_id( $user_id );
790 + if ( empty( $user_id ) ) {
712 791 return false;
792 + }
713 793
714 - $pos = array_search( $topic_id, $subscriptions );
715 - if ( is_numeric( $pos ) ) {
716 - array_splice( $subscriptions, $pos, 1 );
717 - $subscriptions = array_filter( $subscriptions );
794 + // Get the current count, accounting for persisted changes if it is missing
795 + $difference = (int) $difference;
796 + $count = ( false === get_user_option( '_bbp_topic_count', $user_id ) )
797 + ? bbp_get_user_topic_count_raw( $user_id ) - $difference
798 + : bbp_get_user_topic_count( $user_id, true );
718 799
719 - if ( !empty( $subscriptions ) ) {
720 - $subscriptions = implode( ',', $subscriptions );
721 - update_user_option( $user_id, '_bbp_subscriptions', $subscriptions );
722 - } else {
723 - delete_user_option( $user_id, '_bbp_subscriptions' );
724 - }
800 + $user_topic_count = (int) bbp_number_not_negative( $count + $difference );
725 801
726 - wp_cache_delete( 'bbp_get_topic_subscribers_' . $topic_id, 'bbpress' );
727 - }
802 + // Add them up and filter them
803 + $new_count = (int) apply_filters( 'bbp_bump_user_topic_count', $user_topic_count, $user_id, $difference, $count );
728 804
729 - do_action( 'bbp_remove_user_subscription', $user_id, $topic_id );
805 + // Preserve absolute count filters before using the atomic difference
806 + $difference = ( $new_count === $user_topic_count )
807 + ? $new_count - $count
808 + : false;
730 809
731 - return true;
810 + return bbp_update_user_topic_count( $user_id, $new_count, $difference );
732 811 }
733 812
734 813 /**
735 - * Handles the front end subscribing and unsubscribing topics
814 + * Bump the reply count for a user by a certain amount.
736 815 *
737 - * @uses bbp_is_subscriptions_active() To check if the subscriptions are active
738 - * @uses bbp_get_user_id() To get the user id
739 - * @uses bbp_verify_nonce_request() To verify the nonce and check the request
740 - * @uses current_user_can() To check if the current user can edit the user
741 - * @uses bbPress:errors:add() To log the error messages
742 - * @uses bbp_is_user_subscribed() To check if the topic is in user's
743 - * subscriptions
744 - * @uses bbp_remove_user_subscription() To remove the user subscription
745 - * @uses bbp_add_user_subscription() To add the user subscription
746 - * @uses do_action() Calls 'bbp_subscriptions_handler' with success, user id,
747 - * topic id and action
748 - * @uses bbp_is_subscription() To check if it's the subscription page
749 - * @uses bbp_get_subscription_link() To get the subscription page link
750 - * @uses bbp_get_topic_permalink() To get the topic permalink
751 - * @uses wp_safe_redirect() To redirect to the url
816 + * @since 2.6.0 bbPress (r5309)
817 + * @since 2.6.17 Rebuild the count when the user option is missing.
818 + *
819 + * @param int $user_id
820 + * @param int $difference
752 821 */
753 -function bbp_subscriptions_handler() {
822 +function bbp_bump_user_reply_count( $user_id = 0, $difference = 1 ) {
754 823
755 - if ( !bbp_is_subscriptions_active() )
824 + // Bail if no bump
825 + if ( empty( $difference ) ) {
756 826 return false;
827 + }
757 828
758 - // Bail if not a GET action
759 - if ( 'GET' !== strtoupper( $_SERVER['REQUEST_METHOD'] ) )
760 - return;
829 + // Validate user ID
830 + $user_id = bbp_get_user_id( $user_id );
831 + if ( empty( $user_id ) ) {
832 + return false;
833 + }
761 834
762 - // Bail if required GET actions aren't passed
763 - if ( empty( $_GET['topic_id'] ) || empty( $_GET['action'] ) )
764 - return;
835 + // Get the current count, accounting for persisted changes if it is missing
836 + $difference = (int) $difference;
837 + $count = ( false === get_user_option( '_bbp_reply_count', $user_id ) )
838 + ? bbp_get_user_reply_count_raw( $user_id ) - $difference
839 + : bbp_get_user_reply_count( $user_id, true );
765 840
766 - // Setup possible get actions
767 - $possible_actions = array(
768 - 'bbp_subscribe',
769 - 'bbp_unsubscribe',
770 - );
841 + $user_reply_count = (int) bbp_number_not_negative( $count + $difference );
771 842
772 - // Bail if actions aren't meant for this function
773 - if ( !in_array( $_GET['action'], $possible_actions ) )
774 - return;
843 + // Add them up and filter them
844 + $new_count = (int) apply_filters( 'bbp_bump_user_reply_count', $user_reply_count, $user_id, $difference, $count );
775 845
776 - // Get required data
777 - $action = $_GET['action'];
778 - $user_id = bbp_get_user_id( 0, true, true );
779 - $topic_id = intval( $_GET['topic_id'] );
846 + // Preserve absolute count filters before using the atomic difference
847 + $difference = ( $new_count === $user_reply_count )
848 + ? $new_count - $count
849 + : false;
780 850
781 - // Check for empty topic
782 - if ( empty( $topic_id ) ) {
783 - bbp_add_error( 'bbp_subscription_topic_id', __( '<strong>ERROR</strong>: No topic was found! Which topic are you subscribing/unsubscribing to?', 'bbpress' ) );
851 + return bbp_update_user_reply_count( $user_id, $new_count, $difference );
852 +}
784 853
785 - // Check nonce
786 - } elseif ( ! bbp_verify_nonce_request( 'toggle-subscription_' . $topic_id ) ) {
787 - bbp_add_error( 'bbp_subscription_topic_id', __( '<strong>ERROR</strong>: Are you sure you wanted to do that?', 'bbpress' ) );
854 +/**
855 + * Update user counts when a topic or reply changes authors.
856 + *
857 + * @since 2.6.17
858 + *
859 + * @param int $post_id Post ID.
860 + * @param WP_Post $post_after Post object following the update.
861 + * @param WP_Post $post_before Post object before the update.
862 + */
863 +function bbp_update_counts_on_post_author_change( $post_id = 0, $post_after = false, $post_before = false ) {
788 864
789 - // Check current user's ability to edit the user
790 - } elseif ( !current_user_can( 'edit_user', $user_id ) ) {
791 - bbp_add_error( 'bbp_subscription_permissions', __( '<strong>ERROR</strong>: You don\'t have the permission to edit favorites of that user!', 'bbpress' ) );
865 + // Bail if the author or post type did not change as expected
866 + if ( ( $post_after->post_author === $post_before->post_author ) || ( $post_after->post_type !== $post_before->post_type ) ) {
867 + return;
792 868 }
793 869
794 - // Bail if we have errors
795 - if ( bbp_has_errors() )
796 - return;
870 + // Set topic public membership
871 + if ( bbp_get_topic_post_type() === $post_after->post_type ) {
872 + $public_statuses = bbp_get_public_topic_statuses();
873 + $was_public = in_array( $post_before->post_status, $public_statuses, true );
874 + $is_public = in_array( $post_after->post_status, $public_statuses, true );
875 + $is_topic = true;
797 876
798 - /** No errors *************************************************************/
877 + // Set reply public membership
878 + } elseif ( bbp_get_reply_post_type() === $post_after->post_type ) {
879 + $public_statuses = bbp_get_public_reply_statuses();
880 + $was_public = in_array( $post_before->post_status, $public_statuses, true );
881 + $is_public = in_array( $post_after->post_status, $public_statuses, true );
882 + $is_topic = false;
799 883
800 - $is_subscription = bbp_is_user_subscribed( $user_id, $topic_id );
801 - $success = false;
884 + // Bail if this is not a topic or reply
885 + } else {
886 + return;
887 + }
802 888
803 - if ( true == $is_subscription && 'bbp_unsubscribe' == $action )
804 - $success = bbp_remove_user_subscription( $user_id, $topic_id );
805 - elseif ( false == $is_subscription && 'bbp_subscribe' == $action )
806 - $success = bbp_add_user_subscription( $user_id, $topic_id );
889 + // The transition callback already handles posts that were not public
890 + if ( ! $was_public ) {
891 + return;
892 + }
807 893
808 - // Do additional subscriptions actions
809 - do_action( 'bbp_subscriptions_handler', $success, $user_id, $topic_id, $action );
894 + // Transfer a public contribution between authors
895 + if ( $is_public ) {
896 + if ( $is_topic ) {
897 + bbp_bump_user_topic_count( $post_before->post_author, -1 );
898 + bbp_bump_user_topic_count( $post_after->post_author, 1 );
899 + } else {
900 + bbp_bump_user_reply_count( $post_before->post_author, -1 );
901 + bbp_bump_user_reply_count( $post_after->post_author, 1 );
902 + }
810 903
811 - // Success!
812 - if ( true == $success ) {
813 -
814 - // Redirect back from whence we came
815 - if ( bbp_is_subscriptions() ) {
816 - $redirect = bbp_get_subscriptions_permalink( $user_id );
817 - } elseif ( bbp_is_single_user() ) {
818 - $redirect = bbp_get_user_profile_url();
819 - } elseif ( is_singular( bbp_get_topic_post_type() ) ) {
820 - $redirect = bbp_get_topic_permalink( $topic_id );
821 - } elseif ( is_single() || is_page() ) {
822 - $redirect = get_permalink();
904 + // Repair both authors after the transition callback targeted the new author
905 + } else {
906 + foreach ( bbp_get_unique_array_values( array( $post_before->post_author, $post_after->post_author ) ) as $user_id ) {
907 + if ( $is_topic ) {
908 + bbp_update_user_topic_count( $user_id, bbp_get_user_topic_count_raw( $user_id ) );
909 + } else {
910 + bbp_update_user_reply_count( $user_id, bbp_get_user_reply_count_raw( $user_id ) );
911 + }
823 912 }
824 -
825 - wp_safe_redirect( $redirect );
826 -
827 - // For good measure
828 - exit();
829 -
830 - // Fail! Handle errors
831 - } elseif ( true == $is_subscription && 'bbp_unsubscribe' == $action ) {
832 - bbp_add_error( 'bbp_unsubscribe', __( '<strong>ERROR</strong>: There was a problem unsubscribing from that topic!', 'bbpress' ) );
833 - } elseif ( false == $is_subscription && 'bbp_subscribe' == $action ) {
834 - bbp_add_error( 'bbp_subscribe', __( '<strong>ERROR</strong>: There was a problem subscribing to that topic!', 'bbpress' ) );
835 913 }
836 914 }
837 915
838 -/** Edit **********************************************************************/
839 -
840 916 /**
841 - * Handles the front end user editing
917 + * Update topic engagements when a topic or reply changes authors.
842 918 *
843 - * @uses is_multisite() To check if it's a multisite
844 - * @uses bbp_is_user_home() To check if the user is at home (the display page
845 - * is the one of the logged in user)
846 - * @uses get_option() To get the displayed user's new email id option
847 - * @uses wpdb::prepare() To sanitize our sql query
848 - * @uses wpdb::get_var() To execute our query and get back the variable
849 - * @uses wpdb::query() To execute our query
850 - * @uses wp_update_user() To update the user
851 - * @uses delete_option() To delete the displayed user's email id option
852 - * @uses bbp_get_user_profile_edit_url() To get the edit profile url
853 - * @uses wp_safe_redirect() To redirect to the url
854 - * @uses bbp_verify_nonce_request() To verify the nonce and check the request
855 - * @uses current_user_can() To check if the current user can edit the user
856 - * @uses do_action() Calls 'personal_options_update' or
857 - * 'edit_user_options_update' (based on if it's the user home)
858 - * with the displayed user id
859 - * @uses edit_user() To edit the user based on the post data
860 - * @uses get_userdata() To get the user data
861 - * @uses is_email() To check if the string is an email id or not
862 - * @uses wpdb::get_blog_prefix() To get the blog prefix
863 - * @uses is_network_admin() To check if the user is the network admin
864 - * @uses is_super_admin() To check if the user is super admin
865 - * @uses revoke_super_admin() To revoke super admin priviledges
866 - * @uses grant_super_admin() To grant super admin priviledges
867 - * @uses is_wp_error() To check if the value retrieved is a {@link WP_Error}
919 + * @since 2.6.17
920 + *
921 + * @param int $post_id Post ID.
922 + * @param WP_Post $post_after Post object following the update.
923 + * @param WP_Post $post_before Post object before the update.
868 924 */
869 -function bbp_edit_user_handler() {
925 +function bbp_recalculate_engagements_on_post_author_change( $post_id = 0, $post_after = false, $post_before = false ) {
870 926
871 - // Bail if not a POST action
872 - if ( 'POST' !== strtoupper( $_SERVER['REQUEST_METHOD'] ) )
927 + // Bail if the author did not change
928 + if ( $post_after->post_author === $post_before->post_author ) {
873 929 return;
930 + }
874 931
875 - // Bail if action is not 'bbp-update-user'
876 - if ( empty( $_POST['action'] ) || ( 'bbp-update-user' !== $_POST['action'] ) )
932 + // Get the topic ID from a topic or reply
933 + if ( bbp_get_topic_post_type() === $post_after->post_type ) {
934 + $topic_id = $post_id;
935 + } elseif ( bbp_get_reply_post_type() === $post_after->post_type ) {
936 + $topic_id = bbp_get_reply_topic_id( $post_id );
937 + } else {
877 938 return;
939 + }
878 940
879 - // Get the displayed user ID
880 - $user_id = bbp_get_displayed_user_id();
941 + // Recalculate engagements and their count
942 + bbp_recalculate_topic_engagements( $topic_id );
943 + bbp_update_topic_voice_count( $topic_id );
944 +}
881 945
882 - // Execute confirmed email change. See send_confirmation_on_profile_email().
883 - if ( is_multisite() && bbp_is_user_home_edit() && isset( $_GET['newuseremail'] ) ) {
946 +/**
947 + * Update counts and engagements when a deleted user's posts are reassigned.
948 + *
949 + * WordPress reassigns post authors directly in the database, bypassing the
950 + * normal post update actions. Record affected topics before that write, then
951 + * repair the replacement user's counts and those topics after it completes.
952 + *
953 + * @since 2.6.17
954 + *
955 + * @param int $user_id ID of the user being deleted.
956 + * @param int|null $reassign ID of the user receiving the posts.
957 + */
958 +function bbp_update_counts_on_user_reassignment( $user_id = 0, $reassign = null ) {
959 + static $topic_ids = array();
884 960
885 - $new_email = get_option( $user_id . '_new_email' );
961 + $user_id = (int) $user_id;
962 + $reassign = (int) $reassign;
886 963
887 - if ( $new_email['hash'] == $_GET['newuseremail'] ) {
888 - $user = new stdClass();
889 - $user->ID = $user_id;
890 - $user->user_email = esc_html( trim( $new_email['newemail'] ) );
964 + // Bail if posts are not being reassigned to another user
965 + if ( empty( $user_id ) || empty( $reassign ) || ( $user_id === $reassign ) ) {
966 + return;
967 + }
891 968
892 - global $wpdb;
969 + $key = get_current_blog_id() . ':' . $user_id . ':' . $reassign;
893 970
894 - if ( $wpdb->get_var( $wpdb->prepare( "SELECT user_login FROM {$wpdb->signups} WHERE user_login = %s", bbp_get_displayed_user_field( 'user_login' ) ) ) ) {
895 - $wpdb->query( $wpdb->prepare( "UPDATE {$wpdb->signups} SET user_email = %s WHERE user_login = %s", $user->user_email, bbp_get_displayed_user_field( 'user_login' ) ) );
896 - }
971 + // Record affected topics before WordPress changes their authors directly
972 + if ( 'delete_user' === current_filter() ) {
973 + $bbp_db = bbp_db();
974 + $topic_type = bbp_get_topic_post_type();
975 + $reply_type = bbp_get_reply_post_type();
976 + $query = $bbp_db->prepare(
977 + "SELECT DISTINCT CASE WHEN post_type = %s THEN ID ELSE post_parent END FROM {$bbp_db->posts} WHERE post_author = %d AND post_type IN ( %s, %s )",
978 + $topic_type,
979 + $user_id,
980 + $topic_type,
981 + $reply_type
982 + );
897 983
898 - wp_update_user( get_object_vars( $user ) );
899 - delete_option( $user_id . '_new_email' );
900 -
901 - wp_safe_redirect( add_query_arg( array( 'updated' => 'true' ), bbp_get_user_profile_edit_url( $user_id ) ) );
902 - exit();
903 - }
904 -
905 - // Delete new email address from user options
906 - } elseif ( is_multisite() && bbp_is_user_home_edit() && !empty( $_GET['dismiss'] ) && ( $user_id . '_new_email' == $_GET['dismiss'] ) ) {
907 - delete_option( $user_id . '_new_email' );
908 - wp_safe_redirect( add_query_arg( array( 'updated' => 'true' ), bbp_get_user_profile_edit_url( $user_id ) ) );
909 - exit();
984 + $topic_ids[ $key ] = wp_parse_id_list( array_filter( $bbp_db->get_col( $query ) ) );
985 + return;
910 986 }
911 987
912 - // Nonce check
913 - if ( ! bbp_verify_nonce_request( 'update-user_' . $user_id ) ) {
914 - bbp_add_error( 'bbp_update_user_nonce', __( '<strong>ERROR</strong>: Are you sure you wanted to do that?', 'bbpress' ) );
988 + // Bail unless WordPress completed the reassignment recorded above
989 + if ( ( 'deleted_user' !== current_filter() ) || ! isset( $topic_ids[ $key ] ) ) {
915 990 return;
916 991 }
917 992
918 - // Cap check
919 - if ( ! current_user_can( 'edit_user', $user_id ) ) {
920 - bbp_add_error( 'bbp_update_user_capability', __( '<strong>ERROR</strong>: Are you sure you wanted to do that?', 'bbpress' ) );
993 + $affected_topic_ids = $topic_ids[ $key ];
994 + unset( $topic_ids[ $key ] );
995 +
996 + // Bail if the deleted user did not author any topics or replies
997 + if ( empty( $affected_topic_ids ) ) {
921 998 return;
922 999 }
923 1000
924 - // Do action based on who's profile you're editing
925 - $edit_action = bbp_is_user_home_edit() ? 'personal_options_update' : 'edit_user_profile_update';
926 - do_action( $edit_action, $user_id );
1001 + // Recount contributions for the replacement user
1002 + bbp_update_user_topic_count( $reassign, bbp_get_user_topic_count_raw( $reassign ) );
1003 + bbp_update_user_reply_count( $reassign, bbp_get_user_reply_count_raw( $reassign ) );
927 1004
928 - // Handle user edit
929 - $edit_user = edit_user( $user_id );
930 -
931 - // Error(s) editng the user, so copy them into the global
932 - if ( is_wp_error( $edit_user ) ) {
933 - bbpress()->errors = $edit_user;
934 -
935 - // Successful edit to redirect
936 - } elseif ( is_integer( $edit_user ) ) {
937 -
938 - // Maybe update super admin ability
939 - if ( is_multisite() && ! bbp_is_user_home_edit() ) {
940 - empty( $_POST['super_admin'] ) ? revoke_super_admin( $edit_user ) : grant_super_admin( $edit_user );
941 - }
942 -
943 - $redirect = add_query_arg( array( 'updated' => 'true' ), bbp_get_user_profile_edit_url( $edit_user ) );
944 -
945 - wp_safe_redirect( $redirect );
946 - exit;
1005 + // Rebuild engagements and voices for each affected topic
1006 + foreach ( $affected_topic_ids as $topic_id ) {
1007 + bbp_recalculate_topic_engagements( $topic_id, true );
1008 + bbp_update_topic_voice_count( $topic_id );
947 1009 }
948 1010 }
949 1011
950 1012 /**
951 - * Conditionally hook the core WordPress output actions to the end of the
952 - * default user's edit profile template.
1013 + * Helper function used to increase (by one) the count of topics for a user when
1014 + * a topic is published.
953 1015 *
954 - * This allows clever plugin authors to conditionally unhook the WordPress core
955 - * output actions if they don't want any unexpected junk to appear there, and
956 - * also avoids needing to pollute the templates with additional logic and actions.
1016 + * @since 2.6.0 bbPress (r5309)
957 1017 *
958 - * @since bbPress (r4273)
959 - *
960 - * @uses bbp_is_user_home_edit() To switch the action fired
961 - * @uses get_userdata() To get the current user's data
962 - * @uses bbp_get_displayed_user_id() To get the currently displayed user ID
1018 + * @param int $topic_id Topic ID.
963 1019 */
964 -function bbp_user_edit_after() {
965 - $action = bbp_is_user_home_edit() ? 'show_user_profile' : 'edit_user_profile';
1020 +function bbp_increase_user_topic_count( $topic_id = 0 ) {
966 1021
967 - do_action( $action, get_userdata( bbp_get_displayed_user_id() ) );
1022 + // Bail if topic is not public
1023 + if ( ! bbp_is_topic_public( $topic_id ) ) {
1024 + return false;
1025 + }
1026 +
1027 + $user_id = bbp_get_topic_author_id( $topic_id );
1028 + return bbp_bump_user_topic_count( $user_id, 1 );
968 1029 }
969 1030
970 -/** User Queries **************************************************************/
971 -
972 1031 /**
973 - * Get the topics that a user created
1032 + * Helper function used to increase (by one) the count of replies for a user when
1033 + * a reply is published.
974 1034 *
975 - * @since bbPress (r2660)
1035 + * @since 2.6.0 bbPress (r5309)
976 1036 *
977 - * @param int $user_id Optional. User id
978 - * @uses bbp_get_user_id() To get the topic id
979 - * @uses bbp_has_topics() To get the topics created by the user
980 - * @return array|bool Results if the user has created topics, otherwise false
1037 + * @param int $reply_id Reply ID.
981 1038 */
982 -function bbp_get_user_topics_started( $user_id = 0 ) {
983 -
984 - // Validate user
985 - $user_id = bbp_get_user_id( $user_id );
986 - if ( empty( $user_id ) )
987 - return false;
1039 +function bbp_increase_user_reply_count( $reply_id = 0 ) {
988 1040
989 - // Query defaults
990 - $default_query = array(
991 - 'author' => $user_id,
992 - 'show_stickies' => false,
993 - 'order' => 'DESC',
994 - );
995 -
996 - // Try to get the topics
997 - $query = bbp_has_topics( $default_query );
998 - if ( empty( $query ) )
1041 + // Bail if reply is not public
1042 + if ( ! bbp_is_reply_public( $reply_id ) ) {
999 1043 return false;
1044 + }
1000 1045
1001 - return apply_filters( 'bbp_get_user_topics_started', $query, $user_id );
1046 + $user_id = bbp_get_reply_author_id( $reply_id );
1047 + return bbp_bump_user_reply_count( $user_id, 1 );
1002 1048 }
1003 1049
1004 1050 /**
1005 - * Get the replies that a user created
1051 + * Helper function used to decrease (by one) the count of topics for a user when
1052 + * a topic is unpublished.
1006 1053 *
1007 - * @since bbPress (r4225)
1054 + * @since 2.6.0 bbPress (r5309)
1008 1055 *
1009 - * @param int $user_id Optional. User id
1010 - * @uses bbp_get_user_id() To get the topic id
1011 - * @uses bbp_has_replies() To get the topics created by the user
1012 - * @return array|bool Results if the user has created topics, otherwise false
1056 + * @param int $topic_id Topic ID.
1013 1057 */
1014 -function bbp_get_user_replies_created( $user_id = 0 ) {
1015 -
1016 - // Validate user
1017 - $user_id = bbp_get_user_id( $user_id );
1018 - if ( empty( $user_id ) )
1058 +function bbp_decrease_user_topic_count( $topic_id = 0 ) {
1059 +
1060 + // Bail if topic is not public
1061 + if ( ! bbp_is_topic_public( $topic_id ) ) {
1019 1062 return false;
1063 + }
1020 1064
1021 - // Try to get the topics
1022 - $query = bbp_has_replies( array(
1023 - 'post_type' => array( bbp_get_topic_post_type(), bbp_get_reply_post_type() ),
1024 - 'post_parent' => 'any',
1025 - 'posts_per_page' => bbp_get_replies_per_page(),
1026 - 'paged' => bbp_get_paged(),
1027 - 'orderby' => 'date',
1028 - 'order' => 'DESC',
1029 - 'author' => $user_id,
1030 - 'show_stickies' => false,
1031 - ) );
1032 -
1033 - return apply_filters( 'bbp_get_user_replies_created', $query, $user_id );
1065 + $user_id = bbp_get_topic_author_id( $topic_id );
1066 + return bbp_bump_user_topic_count( $user_id, -1 );
1034 1067 }
1035 1068
1036 1069 /**
1037 - * Get the total number of users on the forums
1070 + * Helper function used to decrease (by one) the count of replies for a user when
1071 + * a reply is unpublished.
1038 1072 *
1039 - * @since bbPress (r2769)
1040 - * @uses wp_cache_get() Check if query is in cache
1041 - * @uses get_users() To execute our query and get the var back
1042 - * @uses wp_cache_set() Set the query in the cache
1043 - * @uses apply_filters() Calls 'bbp_get_total_users' with number of users
1044 - * @return int Total number of users
1073 + * @since 2.6.0 bbPress (r5309)
1074 + *
1075 + * @param int $reply_id Reply ID.
1045 1076 */
1046 -function bbp_get_total_users() {
1047 - $user_count = count_users();
1048 - return apply_filters( 'bbp_get_total_users', (int) $user_count['total_users'] );
1077 +function bbp_decrease_user_reply_count( $reply_id = 0 ) {
1078 +
1079 + // Bail if reply is not public
1080 + if ( ! bbp_is_reply_public( $reply_id ) ) {
1081 + return false;
1082 + }
1083 +
1084 + $user_id = bbp_get_reply_author_id( $reply_id );
1085 + return bbp_bump_user_reply_count( $user_id, -1 );
1049 1086 }
1050 1087
1051 -/** Premissions ***************************************************************/
1088 +/** Permissions ***************************************************************/
1052 1089
1053 1090 /**
1054 - * Redirect if unathorized user is attempting to edit another user
1091 + * Redirect if unauthorized user is attempting to edit another user
1055 1092 *
1056 1093 * This is hooked to 'bbp_template_redirect' and controls the conditions under
1057 1094 * which a user can edit another user (or themselves.) If these conditions are
1058 - * met. We assume a user cannot perform this task, and look for ways they can
1095 + * met, we assume a user cannot perform this task, and look for ways they can
1059 1096 * earn the ability to access this template.
1060 - *
1061 - * @since bbPress (r3605)
1062 1097 *
1063 - * @uses bbp_is_topic_edit()
1064 - * @uses current_user_can()
1065 - * @uses bbp_get_topic_id()
1066 - * @uses wp_safe_redirect()
1067 - * @uses bbp_get_topic_permalink()
1098 + * @since 2.1.0 bbPress (r3605)
1068 1099 */
1069 1100 function bbp_check_user_edit() {
1070 1101
1071 - // Bail if not editing a topic
1072 - if ( ! bbp_is_single_user_edit() )
1102 + // Bail if not editing a user
1103 + if ( ! bbp_is_single_user_edit() ) {
1073 1104 return;
1105 + }
1074 1106
1075 1107 // Default to false
1076 1108 $redirect = true;
1109 + $user_id = bbp_get_displayed_user_id();
1077 1110
1078 1111 // Allow user to edit their own profile
1079 1112 if ( bbp_is_user_home_edit() ) {
1080 1113 $redirect = false;
@@ -1079,9 +1112,9 @@
1079 1112 if ( bbp_is_user_home_edit() ) {
1080 1113 $redirect = false;
1081 1114
1082 1115 // Allow if current user can edit the displayed user
1083 - } elseif ( current_user_can( 'edit_user', bbp_get_displayed_user_id() ) ) {
1116 + } elseif ( current_user_can( 'edit_user', $user_id ) ) {
1084 1117 $redirect = false;
1085 1118
1086 1119 // Allow if user can manage network users, or edit-any is enabled
1087 1120 } elseif ( current_user_can( 'manage_network_users' ) || apply_filters( 'enable_edit_any_user_configuration', false ) ) {
@@ -1087,30 +1120,33 @@
1087 1120 } elseif ( current_user_can( 'manage_network_users' ) || apply_filters( 'enable_edit_any_user_configuration', false ) ) {
1088 1121 $redirect = false;
1089 1122 }
1090 1123
1091 - // Maybe redirect back to profile page
1092 - if ( true === $redirect ) {
1093 - wp_safe_redirect( bbp_get_user_profile_url( bbp_get_displayed_user_id() ) );
1094 - exit();
1124 + // Allow conclusion to be overridden
1125 + $redirect = (bool) apply_filters( 'bbp_check_user_edit', $redirect, $user_id );
1126 +
1127 + // Bail if not redirecting
1128 + if ( false === $redirect ) {
1129 + return;
1095 1130 }
1131 +
1132 + // Filter redirect URL
1133 + $profile_url = bbp_get_user_profile_url( $user_id );
1134 + $redirect_to = apply_filters( 'bbp_check_user_edit_redirect_to', $profile_url, $user_id );
1135 +
1136 + // Redirect
1137 + bbp_redirect( $redirect_to );
1096 1138 }
1097 1139
1098 1140 /**
1099 1141 * Check if a user is blocked, or cannot spectate the forums.
1100 1142 *
1101 - * @since bbPress (r2996)
1102 - *
1103 - * @uses is_user_logged_in() To check if user is logged in
1104 - * @uses is_super_admin() To check if user is a super admin
1105 - * @uses current_user_can() To check if the current user can spectate
1106 - * @uses is_bbpress() To check if in a bbPress section of the site
1107 - * @uses bbp_set_404() To set a 404 status
1143 + * @since 2.0.0 bbPress (r2996)
1108 1144 */
1109 1145 function bbp_forum_enforce_blocked() {
1110 1146
1111 - // Bail if not logged in or super admin
1112 - if ( ! is_user_logged_in() || is_super_admin() ) {
1147 + // Bail if not logged in or keymaster
1148 + if ( ! is_user_logged_in() || bbp_is_user_keymaster() ) {
1113 1149 return;
1114 1150 }
1115 1151
1116 1152 // Set 404 if in bbPress and user cannot spectate
@@ -1118,44 +1154,135 @@
1118 1154 bbp_set_404();
1119 1155 }
1120 1156 }
1121 1157
1158 +/** Sanitization **************************************************************/
1159 +
1160 +/**
1161 + * Sanitize displayed user data, when viewing and editing any user.
1162 + *
1163 + * This somewhat monolithic function handles the escaping and sanitization of
1164 + * user data for a bbPress profile. There are two reasons this all happens here:
1165 + *
1166 + * 1. bbPress took a similar approach to WordPress, and funnels all user profile
1167 + * data through a central helper. This eventually calls sanitize_user_field()
1168 + * which applies a few context based filters, which some third party plugins
1169 + * might be relying on bbPress to play nicely with.
1170 + *
1171 + * 2. Early versions of bbPress 2.x templates did not escape this data meaning
1172 + * a backwards compatible approach like this one was necessary to protect
1173 + * existing installations that may have custom template parts.
1174 + *
1175 + * @since 2.6.0 bbPress (r5368)
1176 + *
1177 + * @param string $value
1178 + * @param string $field
1179 + * @param string $context
1180 + * @return string
1181 + */
1182 +function bbp_sanitize_displayed_user_field( $value = '', $field = '', $context = 'display' ) {
1183 +
1184 + // Bail if not editing or displaying (maybe we'll do more here later)
1185 + if ( ! in_array( $context, array( 'edit', 'display' ), true ) ) {
1186 + return $value;
1187 + }
1188 +
1189 + // By default, no filter set (consider making this an array later)
1190 + $filter = false;
1191 +
1192 + // Big switch statement to decide which user field we're sanitizing and how
1193 + switch ( $field ) {
1194 +
1195 + // Description is a paragraph
1196 + case 'description' :
1197 + $filter = ( 'edit' === $context ) ? '' : 'wp_kses_data';
1198 + break;
1199 +
1200 + // Email addresses are sanitized with a specific function
1201 + case 'user_email' :
1202 + $filter = 'sanitize_email';
1203 + break;
1204 +
1205 + // Name & login fields
1206 + case 'user_login' :
1207 + case 'display_name' :
1208 + case 'first_name' :
1209 + case 'last_name' :
1210 + case 'nick_name' :
1211 + $filter = ( 'edit' === $context ) ? 'esc_attr' : 'esc_html';
1212 + break;
1213 +
1214 + // wp-includes/default-filters.php escapes this for us via esc_url()
1215 + case 'user_url' :
1216 + break;
1217 + }
1218 +
1219 + // Run any applicable filters on the value
1220 + if ( ! empty( $filter ) ) {
1221 + $value = call_user_func( $filter, $value );
1222 + }
1223 +
1224 + return $value;
1225 +}
1226 +
1122 1227 /** Converter *****************************************************************/
1123 1228
1124 1229 /**
1125 - * Convert passwords from previous platfrom encryption to WordPress encryption.
1230 + * Convert passwords from previous platform encryption to WordPress encryption.
1126 1231 *
1127 - * @since bbPress (r3813)
1128 - * @global WPDB $wpdb
1232 + * @since 2.1.0 bbPress (r3813)
1233 + * @since 2.6.10 bbPress (r7244) Switched from direct query to get_user_by()
1129 1234 */
1130 1235 function bbp_user_maybe_convert_pass() {
1131 1236
1132 - // Bail if no username
1133 - $username = !empty( $_POST['log'] ) ? $_POST['log'] : '';
1134 - if ( empty( $username ) )
1237 + // Sanitize login
1238 + $login = ! empty( $_POST['log'] )
1239 + ? sanitize_user( wp_unslash( $_POST['log'] ) )
1240 + : '';
1241 +
1242 + // Sanitize password
1243 + $pass = ! empty( $_POST['pwd'] )
1244 + ? trim( $_POST['pwd'] )
1245 + : '';
1246 +
1247 + // Bail if no username or password
1248 + if ( empty( $login ) || empty( $pass ) ) {
1135 1249 return;
1250 + }
1136 1251
1137 - global $wpdb;
1252 + // Get user by login...
1253 + $user = get_user_by( 'login', $login );
1138 1254
1139 - // Bail if no user password to convert
1140 - $row = $wpdb->get_row( "SELECT * FROM {$wpdb->users} INNER JOIN {$wpdb->usermeta} ON user_id = ID WHERE meta_key = '_bbp_class' AND user_login = '{$username}' LIMIT 1" );
1141 - if ( empty( $row ) || is_wp_error( $row ) )
1255 + // ...or get user by email
1256 + if ( empty( $user ) && strpos( $login, '@' ) ) {
1257 + $user = get_user_by( 'email', $login );
1258 + }
1259 +
1260 + // Bail if no user
1261 + if ( empty( $user ) ) {
1142 1262 return;
1263 + }
1143 1264
1144 - // Setup admin (to include converter)
1145 - require_once( bbpress()->includes_dir . 'admin/admin.php' );
1265 + // Get converter class from usermeta
1266 + $class = get_user_meta( $user->ID, '_bbp_class', true );
1146 1267
1147 - // Create the admin object
1148 - bbp_admin();
1268 + // Bail if no converter class in meta
1269 + if ( empty( $class ) || ! is_string( $class ) ) {
1270 + return;
1271 + }
1149 1272
1150 - // Convert password
1151 - require_once( bbpress()->admin->admin_dir . 'converter.php' );
1152 - require_once( bbpress()->admin->admin_dir . 'converters/' . $row->meta_value . '.php' );
1273 + // Setup the converter
1274 + bbp_setup_converter();
1153 1275
1154 - // Create the converter
1155 - $converter = bbp_new_converter( $row->meta_value );
1276 + // Try to instantiate the converter class
1277 + $converter = bbp_new_converter( $class );
1156 1278
1157 - // Try to call the conversion method
1158 - if ( is_a( $converter, 'BBP_Converter_Base' ) && method_exists( $converter, 'callback_pass' ) ) {
1159 - $converter->callback_pass( $username, $_POST['pwd'] );
1279 + // Bail if no converter
1280 + if ( empty( $converter ) ) {
1281 + return;
1282 + }
1283 +
1284 + // Try to call the password conversion callback method
1285 + if ( ( $converter instanceof BBP_Converter_Base ) && method_exists( $converter, 'callback_pass' ) ) {
1286 + $converter->callback_pass( $login, $pass );
1160 1287 }
1161 1288 }