bp-activity-functions.php 131 KB
Newer Older
root's avatar
root committed
1
2
<?php
/**
lechuck's avatar
lechuck committed
3
 * BuddyPress Activity Functions.
root's avatar
root committed
4
 *
lechuck's avatar
lechuck committed
5
 * Functions for the Activity Streams component.
root's avatar
root committed
6
7
8
 *
 * @package BuddyPress
 * @subpackage ActivityFunctions
lechuck's avatar
lechuck committed
9
 * @since 1.5.0
root's avatar
root committed
10
11
 */

lechuck's avatar
lechuck committed
12
// Exit if accessed directly.
ale's avatar
ale committed
13
defined( 'ABSPATH' ) || exit;
root's avatar
root committed
14
15

/**
lechuck's avatar
lechuck committed
16
 * Check whether the $bp global lists an activity directory page.
root's avatar
root committed
17
 *
lechuck's avatar
lechuck committed
18
 * @since 1.5.0
root's avatar
root committed
19
 *
lechuck's avatar
lechuck committed
20
 * @return bool True if activity directory page is found, otherwise false.
root's avatar
root committed
21
22
 */
function bp_activity_has_directory() {
lucha's avatar
lucha committed
23
	return (bool) !empty( buddypress()->pages->activity->id );
root's avatar
root committed
24
25
}

lechuck's avatar
lechuck committed
26
27
28
29
30
/**
 * Are mentions enabled or disabled?
 *
 * The Mentions feature does a number of things, all of which will be turned
 * off if you disable mentions:
ale's avatar
ale committed
31
 *   - Detecting and auto-linking @username in all BP/WP content.
lechuck's avatar
lechuck committed
32
 *   - Sending BP notifications and emails to users when they are mentioned
ale's avatar
ale committed
33
34
 *     using the @username syntax.
 *   - The Public Message button on user profiles.
lechuck's avatar
lechuck committed
35
36
37
38
39
40
 *
 * Mentions are enabled by default. To disable, put the following line in
 * bp-custom.php or your theme's functions.php file:
 *
 *   add_filter( 'bp_activity_do_mentions', '__return_false' );
 *
lechuck's avatar
lechuck committed
41
 * @since 1.8.0
lechuck's avatar
lechuck committed
42
43
44
45
 *
 * @return bool $retval True to enable mentions, false to disable.
 */
function bp_activity_do_mentions() {
ale's avatar
ale committed
46
47
48
49

	/**
	 * Filters whether or not mentions are enabled.
	 *
lechuck's avatar
lechuck committed
50
	 * @since 1.8.0
ale's avatar
ale committed
51
52
53
	 *
	 * @param bool $enabled True to enable mentions, false to disable.
	 */
lechuck's avatar
lechuck committed
54
55
56
	return (bool) apply_filters( 'bp_activity_do_mentions', true );
}

lucha's avatar
lucha committed
57
58
59
60
/**
 * Should BuddyPress load the mentions scripts and related assets, including results to prime the
 * mentions suggestions?
 *
lechuck's avatar
lechuck committed
61
 * @since 2.1.0
ale's avatar
ale committed
62
63
 *
 * @return bool True if mentions scripts should be loaded.
lucha's avatar
lucha committed
64
65
 */
function bp_activity_maybe_load_mentions_scripts() {
ale's avatar
ale committed
66
67
	$mentions_enabled = bp_activity_do_mentions() && bp_is_user_active();
	$load_mentions    = $mentions_enabled && ( bp_is_activity_component() || is_admin() );
lucha's avatar
lucha committed
68

ale's avatar
ale committed
69
70
71
	/**
	 * Filters whether or not BuddyPress should load mentions scripts and assets.
	 *
lechuck's avatar
lechuck committed
72
	 * @since 2.1.0
ale's avatar
ale committed
73
74
75
76
77
	 *
	 * @param bool $load_mentions    True to load mentions assets, false otherwise.
	 * @param bool $mentions_enabled True if mentions are enabled.
	 */
	return (bool) apply_filters( 'bp_activity_maybe_load_mentions_scripts', $load_mentions, $mentions_enabled );
lucha's avatar
lucha committed
78
79
}

root's avatar
root committed
80
/**
lechuck's avatar
lechuck committed
81
 * Locate usernames in an activity content string, as designated by an @ sign.
root's avatar
root committed
82
 *
lechuck's avatar
lechuck committed
83
 * @since 1.5.0
root's avatar
root committed
84
 *
lucha's avatar
lucha committed
85
 * @param string $content The content of the activity, usually found in
ale's avatar
ale committed
86
 *                        $activity->content.
lucha's avatar
lucha committed
87
 * @return array|bool Associative array with user ID as key and username as
ale's avatar
ale committed
88
 *                    value. Boolean false if no mentions found.
root's avatar
root committed
89
90
 */
function bp_activity_find_mentions( $content ) {
lucha's avatar
lucha committed
91

lechuck's avatar
lechuck committed
92
	$pattern = '/[@]+([A-Za-z0-9-_\.@]+)\b/';
root's avatar
root committed
93
94
	preg_match_all( $pattern, $content, $usernames );

lechuck's avatar
lechuck committed
95
	// Make sure there's only one instance of each username.
lucha's avatar
lucha committed
96
97
	$usernames = array_unique( $usernames[1] );

lechuck's avatar
lechuck committed
98
	// Bail if no usernames.
lucha's avatar
lucha committed
99
	if ( empty( $usernames ) ) {
root's avatar
root committed
100
		return false;
lucha's avatar
lucha committed
101
	}
root's avatar
root committed
102

lechuck's avatar
lechuck committed
103
104
	$mentioned_users = array();

lechuck's avatar
lechuck committed
105
	// We've found some mentions! Check to see if users exist.
lucha's avatar
lucha committed
106
	foreach( (array) array_values( $usernames ) as $username ) {
lechuck's avatar
lechuck committed
107
		$user_id = bp_activity_get_userid_from_mentionname( $username );
lechuck's avatar
lechuck committed
108

lechuck's avatar
lechuck committed
109
		// The user ID exists, so let's add it to our array.
lechuck's avatar
lechuck committed
110
		if ( ! empty( $user_id ) ) {
lucha's avatar
lucha committed
111
			$mentioned_users[ $user_id ] = $username;
lechuck's avatar
lechuck committed
112
113
114
		}
	}

lucha's avatar
lucha committed
115
	if ( empty( $mentioned_users ) ) {
lechuck's avatar
lechuck committed
116
		return false;
lucha's avatar
lucha committed
117
	}
lechuck's avatar
lechuck committed
118

lechuck's avatar
lechuck committed
119
120
121
122
123
124
125
126
	/**
	 * Filters the mentioned users.
	 *
	 * @since 2.5.0
	 *
	 * @param array $mentioned_users Associative array with user IDs as keys and usernames as values.
	 */
	return apply_filters( 'bp_activity_mentioned_users', $mentioned_users );
root's avatar
root committed
127
128
129
}

/**
lechuck's avatar
lechuck committed
130
 * Reset a user's unread mentions list and count.
root's avatar
root committed
131
 *
lechuck's avatar
lechuck committed
132
 * @since 1.5.0
root's avatar
root committed
133
 *
lechuck's avatar
lechuck committed
134
 * @param int $user_id The id of the user whose unread mentions are being reset.
root's avatar
root committed
135
136
137
 */
function bp_activity_clear_new_mentions( $user_id ) {
	bp_delete_user_meta( $user_id, 'bp_new_mention_count' );
lucha's avatar
lucha committed
138
	bp_delete_user_meta( $user_id, 'bp_new_mentions'      );
lechuck's avatar
lechuck committed
139
140
141
142
143
144
145
146
147

	/**
	 * Fires once mentions has been reset for a given user.
	 *
	 * @since  2.5.0
	 *
	 * @param  int $user_id The id of the user whose unread mentions are being reset.
	 */
	do_action( 'bp_activity_clear_new_mentions', $user_id );
root's avatar
root committed
148
149
150
}

/**
lechuck's avatar
lechuck committed
151
152
153
154
155
156
 * Adjusts mention count for mentioned users in activity items.
 *
 * This function is useful if you only have the activity ID handy and you
 * haven't parsed an activity item for @mentions yet.
 *
 * Currently, only used in {@link bp_activity_delete()}.
root's avatar
root committed
157
 *
lechuck's avatar
lechuck committed
158
 * @since 1.5.0
root's avatar
root committed
159
 *
ale's avatar
ale committed
160
161
162
 * @param int    $activity_id The unique id for the activity item.
 * @param string $action      Can be 'delete' or 'add'. Defaults to 'add'.
 * @return bool
lechuck's avatar
lechuck committed
163
164
 */
function bp_activity_adjust_mention_count( $activity_id = 0, $action = 'add' ) {
lucha's avatar
lucha committed
165

lechuck's avatar
lechuck committed
166
	// Bail if no activity ID passed.
lucha's avatar
lucha committed
167
	if ( empty( $activity_id ) ) {
lechuck's avatar
lechuck committed
168
		return false;
lucha's avatar
lucha committed
169
	}
lechuck's avatar
lechuck committed
170

lechuck's avatar
lechuck committed
171
	// Get activity object.
lucha's avatar
lucha committed
172
	$activity  = new BP_Activity_Activity( $activity_id );
lechuck's avatar
lechuck committed
173

lechuck's avatar
lechuck committed
174
	// Try to find mentions.
lechuck's avatar
lechuck committed
175
176
	$usernames = bp_activity_find_mentions( strip_tags( $activity->content ) );

lechuck's avatar
lechuck committed
177
	// Still empty? Stop now.
lucha's avatar
lucha committed
178
	if ( empty( $usernames ) ) {
lechuck's avatar
lechuck committed
179
		return false;
lucha's avatar
lucha committed
180
	}
lechuck's avatar
lechuck committed
181

lechuck's avatar
lechuck committed
182
	// Increment mention count foreach mentioned user.
lucha's avatar
lucha committed
183
	foreach( (array) array_keys( $usernames ) as $user_id ) {
lechuck's avatar
lechuck committed
184
185
186
187
188
		bp_activity_update_mention_count_for_user( $user_id, $activity_id, $action );
	}
}

/**
lechuck's avatar
lechuck committed
189
 * Update the mention count for a given user.
lechuck's avatar
lechuck committed
190
191
192
193
 *
 * This function should be used when you've already parsed your activity item
 * for @mentions.
 *
lechuck's avatar
lechuck committed
194
 * @since 1.7.0
lechuck's avatar
lechuck committed
195
 *
ale's avatar
ale committed
196
197
198
 * @param int    $user_id     The user ID.
 * @param int    $activity_id The unique ID for the activity item.
 * @param string $action      'delete' or 'add'. Default: 'add'.
lechuck's avatar
lechuck committed
199
 * @return bool
root's avatar
root committed
200
 */
lechuck's avatar
lechuck committed
201
function bp_activity_update_mention_count_for_user( $user_id, $activity_id, $action = 'add' ) {
lucha's avatar
lucha committed
202
203

	if ( empty( $user_id ) || empty( $activity_id ) ) {
lechuck's avatar
lechuck committed
204
		return false;
lucha's avatar
lucha committed
205
	}
lechuck's avatar
lechuck committed
206

lechuck's avatar
lechuck committed
207
	// Adjust the mention list and count for the member.
lechuck's avatar
lechuck committed
208
	$new_mention_count = (int) bp_get_user_meta( $user_id, 'bp_new_mention_count', true );
lucha's avatar
lucha committed
209
210
	$new_mentions      =       bp_get_user_meta( $user_id, 'bp_new_mentions',      true );

lechuck's avatar
lechuck committed
211
	// Make sure new mentions is an array.
lucha's avatar
lucha committed
212
	if ( empty( $new_mentions ) ) {
lechuck's avatar
lechuck committed
213
		$new_mentions = array();
lucha's avatar
lucha committed
214
	}
lechuck's avatar
lechuck committed
215
216
217
218

	switch ( $action ) {
		case 'delete' :
			$key = array_search( $activity_id, $new_mentions );
root's avatar
root committed
219

lechuck's avatar
lechuck committed
220
221
			if ( $key !== false ) {
				unset( $new_mentions[$key] );
root's avatar
root committed
222
223
			}

lechuck's avatar
lechuck committed
224
			break;
root's avatar
root committed
225

lechuck's avatar
lechuck committed
226
227
228
229
230
231
232
		case 'add' :
		default :
			if ( !in_array( $activity_id, $new_mentions ) ) {
				$new_mentions[] = (int) $activity_id;
			}

			break;
root's avatar
root committed
233
	}
lechuck's avatar
lechuck committed
234

lechuck's avatar
lechuck committed
235
	// Get an updated mention count.
lechuck's avatar
lechuck committed
236
237
	$new_mention_count = count( $new_mentions );

lechuck's avatar
lechuck committed
238
	// Resave the user_meta.
lechuck's avatar
lechuck committed
239
240
241
242
	bp_update_user_meta( $user_id, 'bp_new_mention_count', $new_mention_count );
	bp_update_user_meta( $user_id, 'bp_new_mentions',      $new_mentions );

	return true;
root's avatar
root committed
243
244
245
}

/**
lechuck's avatar
lechuck committed
246
 * Determine a user's "mentionname", the name used for that user in @-mentions.
root's avatar
root committed
247
 *
lechuck's avatar
lechuck committed
248
 * @since 1.9.0
root's avatar
root committed
249
 *
ale's avatar
ale committed
250
 * @param int|string $user_id ID of the user to get @-mention name for.
lechuck's avatar
lechuck committed
251
 * @return string $mentionname User name appropriate for @-mentions.
root's avatar
root committed
252
 */
lechuck's avatar
lechuck committed
253
254
function bp_activity_get_user_mentionname( $user_id ) {
	$mentionname = '';
root's avatar
root committed
255

lechuck's avatar
lechuck committed
256
257
258
259
260
261
262
263
	$userdata = bp_core_get_core_userdata( $user_id );

	if ( $userdata ) {
		if ( bp_is_username_compatibility_mode() ) {
			$mentionname = str_replace( ' ', '-', $userdata->user_login );
		} else {
			$mentionname = $userdata->user_nicename;
		}
root's avatar
root committed
264
265
	}

lechuck's avatar
lechuck committed
266
267
268
269
270
271
	return $mentionname;
}

/**
 * Get a user ID from a "mentionname", the name used for a user in @-mentions.
 *
lechuck's avatar
lechuck committed
272
 * @since 1.9.0
lechuck's avatar
lechuck committed
273
 *
ale's avatar
ale committed
274
 * @param string $mentionname Username of user in @-mentions.
lechuck's avatar
lechuck committed
275
276
277
278
279
 * @return int|bool ID of the user, if one is found. Otherwise false.
 */
function bp_activity_get_userid_from_mentionname( $mentionname ) {
	$user_id = false;

lechuck's avatar
lechuck committed
280
281
282
283
284
285
286
287
	/*
	 * In username compatibility mode, hyphens are ambiguous between
	 * actual hyphens and converted spaces.
	 *
	 * @todo There is the potential for username clashes between 'foo bar'
	 * and 'foo-bar' in compatibility mode. Come up with a system for
	 * unique mentionnames.
	 */
lechuck's avatar
lechuck committed
288
	if ( bp_is_username_compatibility_mode() ) {
lechuck's avatar
lechuck committed
289
		// First, try the raw username.
lechuck's avatar
lechuck committed
290
291
292
293
294
295
296
297
298
299
300
301
302
		$userdata = get_user_by( 'login', $mentionname );

		// Doing a direct query to use proper regex. Necessary to
		// account for hyphens + spaces in the same user_login.
		if ( empty( $userdata ) || ! is_a( $userdata, 'WP_User' ) ) {
			global $wpdb;
			$regex   = esc_sql( str_replace( '-', '[ \-]', $mentionname ) );
			$user_id = $wpdb->get_var( "SELECT ID FROM {$wpdb->users} WHERE user_login REGEXP '{$regex}'" );
		} else {
			$user_id = $userdata->ID;
		}

	// When username compatibility mode is disabled, the mentionname is
lechuck's avatar
lechuck committed
303
	// the same as the nicename.
root's avatar
root committed
304
	} else {
lechuck's avatar
lechuck committed
305
		$user_id = bp_core_get_userid_from_nicename( $mentionname );
root's avatar
root committed
306
307
308
	}


lechuck's avatar
lechuck committed
309
	return $user_id;
root's avatar
root committed
310
311
312
313
314
}

/** Actions ******************************************************************/

/**
lechuck's avatar
lechuck committed
315
316
317
318
319
320
321
322
323
324
325
326
327
 * Register an activity 'type' and its action description/callback.
 *
 * Activity actions are strings used to describe items in the activity stream,
 * such as 'Joe became a registered member' or 'Bill and Susie are now
 * friends'. Each activity type (such as 'new_member' or 'friendship_created')
 * used by a component should be registered using this function.
 *
 * While it's possible to post items to the activity stream whose types are
 * not registered using bp_activity_set_action(), it is not recommended;
 * unregistered types will not be displayed properly in the activity admin
 * panel, and dynamic action generation (which is essential for multilingual
 * sites, etc) will not work.
 *
lechuck's avatar
lechuck committed
328
 * @since 1.1.0
lechuck's avatar
lechuck committed
329
 *
ale's avatar
ale committed
330
331
332
333
334
335
336
337
 * @param  string        $component_id    The unique string ID of the component.
 * @param  string        $type            The action type.
 * @param  string        $description     The action description.
 * @param  callable|bool $format_callback Callback for formatting the action string.
 * @param  string|bool   $label           String to describe this action in the activity stream filter dropdown.
 * @param  array         $context         Optional. Activity stream contexts where the filter should appear.
 *                                        Values: 'activity', 'member', 'member_groups', 'group'.
 * @param  int           $position        Optional. The position of the action when listed in dropdowns.
lechuck's avatar
lechuck committed
338
 * @return bool False if any param is empty, otherwise true.
root's avatar
root committed
339
 */
ale's avatar
ale committed
340
function bp_activity_set_action( $component_id, $type, $description, $format_callback = false, $label = false, $context = array(), $position = 0 ) {
lechuck's avatar
lechuck committed
341
	$bp = buddypress();
root's avatar
root committed
342

lechuck's avatar
lechuck committed
343
	// Return false if any of the above values are not set.
lechuck's avatar
lechuck committed
344
	if ( empty( $component_id ) || empty( $type ) || empty( $description ) ) {
root's avatar
root committed
345
		return false;
lechuck's avatar
lechuck committed
346
	}
root's avatar
root committed
347

lechuck's avatar
lechuck committed
348
	// Set activity action.
lechuck's avatar
lechuck committed
349
	if ( ! isset( $bp->activity->actions ) || ! is_object( $bp->activity->actions ) ) {
lechuck's avatar
lechuck committed
350
351
352
		$bp->activity->actions = new stdClass;
	}

lechuck's avatar
lechuck committed
353
	// Verify callback.
lechuck's avatar
lechuck committed
354
355
356
357
358
	if ( ! is_callable( $format_callback ) ) {
		$format_callback = '';
	}

	if ( ! isset( $bp->activity->actions->{$component_id} ) || ! is_object( $bp->activity->actions->{$component_id} ) ) {
lechuck's avatar
lechuck committed
359
360
361
		$bp->activity->actions->{$component_id} = new stdClass;
	}

ale's avatar
ale committed
362
363
364
	/**
	 * Filters the action type being set for the current activity item.
	 *
lechuck's avatar
lechuck committed
365
	 * @since 1.1.0
ale's avatar
ale committed
366
367
368
369
370
371
372
373
374
375
	 *
	 * @param array    $array           Array of arguments for action type being set.
	 * @param string   $component_id    ID of the current component being set.
	 * @param string   $type            Action type being set.
	 * @param string   $description     Action description for action being set.
	 * @param callable $format_callback Callback for formatting the action string.
	 * @param string   $label           String to describe this action in the activity stream filter dropdown.
	 * @param array    $context         Activity stream contexts where the filter should appear. 'activity', 'member',
	 *                                  'member_groups', 'group'.
	 */
lechuck's avatar
lechuck committed
376
377
378
379
	$bp->activity->actions->{$component_id}->{$type} = apply_filters( 'bp_activity_set_action', array(
		'key'             => $type,
		'value'           => $description,
		'format_callback' => $format_callback,
lucha's avatar
lucha committed
380
381
		'label'           => $label,
		'context'         => $context,
ale's avatar
ale committed
382
		'position'        => $position,
lucha's avatar
lucha committed
383
	), $component_id, $type, $description, $format_callback, $label, $context );
root's avatar
root committed
384

lechuck's avatar
lechuck committed
385
386
387
388
389
390
391
392
393
394
	// Sort the actions of the affected component.
	$action_array = (array) $bp->activity->actions->{$component_id};
	$action_array = bp_sort_by_key( $action_array, 'position', 'num' );

	// Restore keys.
	$bp->activity->actions->{$component_id} = new stdClass;
	foreach ( $action_array as $key_ordered ) {
		$bp->activity->actions->{$component_id}->{$key_ordered['key']} = $key_ordered;
	}

root's avatar
root committed
395
396
397
398
	return true;
}

/**
ale's avatar
ale committed
399
400
 * Set tracking arguments for a given post type.
 *
lechuck's avatar
lechuck committed
401
 * @since 2.2.0
ale's avatar
ale committed
402
403
404
405
406
407
408
 *
 * @global $wp_post_types
 *
 * @param string $post_type The name of the post type, as registered with WordPress. Eg 'post' or 'page'.
 * @param array  $args {
 *     An associative array of tracking parameters. All items are optional.
 *     @type string   $bp_activity_admin_filter String to use in the Dashboard > Activity dropdown.
lucha's avatar
lucha committed
409
 *     @type string   $bp_activity_front_filter String to use in the front-end dropdown.
ale's avatar
ale committed
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
 *     @type string   $bp_activity_new_post     String format to use for generating the activity action. Should be a
 *                                              translatable string where %1$s is replaced by a user link and %2$s is
 *                                              the URL of the newly created post.
 *     @type string   $bp_activity_new_post_ms  String format to use for generating the activity action on Multisite.
 *                                              Should be a translatable string where %1$s is replaced by a user link,
 *                                              %2$s is the URL of the newly created post, and %3$s is a link to
 *                                              the site.
 *     @type string   $component_id             ID of the BuddyPress component to associate the activity item.
 *     @type string   $action_id                Value for the 'type' param of the new activity item.
 *     @type callable $format_callback          Callback for formatting the activity action string.
 *                                              Default: 'bp_activity_format_activity_action_custom_post_type_post'.
 *     @type array    $contexts                 The directory contexts in which the filter will show.
 *                                              Default: array( 'activity' ).
 *     @type array    $position                 Position of the item in filter dropdowns.
 *     @type string   $singular                 Singular, translatable name of the post type item. If no value is
 *                                              provided, it's pulled from the 'singular_name' of the post type.
 *     @type bool     $activity_comment         Whether to allow comments on the activity items. Defaults to true if
 *                                              the post type does not natively support comments, otherwise false.
 * }
 * @return bool
 */
function bp_activity_set_post_type_tracking_args( $post_type = '', $args = array() ) {
	global $wp_post_types;

	if ( empty( $wp_post_types[ $post_type ] ) || ! post_type_supports( $post_type, 'buddypress-activity' ) || ! is_array( $args ) ) {
		return false;
	}

lechuck's avatar
lechuck committed
438
439
440
441
442
443
444
445
446
447
448
449
450
	$activity_labels = array(
		/* Post labels */
		'bp_activity_admin_filter',
		'bp_activity_front_filter',
		'bp_activity_new_post',
		'bp_activity_new_post_ms',
		/* Comment labels */
		'bp_activity_comments_admin_filter',
		'bp_activity_comments_front_filter',
		'bp_activity_new_comment',
		'bp_activity_new_comment_ms'
	);

ale's avatar
ale committed
451
	// Labels are loaded into the post type object.
lechuck's avatar
lechuck committed
452
	foreach ( $activity_labels as $label_type ) {
ale's avatar
ale committed
453
454
		if ( ! empty( $args[ $label_type ] ) ) {
			$wp_post_types[ $post_type ]->labels->{$label_type} = $args[ $label_type ];
lechuck's avatar
lechuck committed
455
			unset( $args[ $label_type ] );
ale's avatar
ale committed
456
457
458
459
460
461
462
463
464
465
466
467
		}
	}

	// If there are any additional args, put them in the bp_activity attribute of the post type.
	if ( ! empty( $args ) ) {
		$wp_post_types[ $post_type ]->bp_activity = $args;
	}
}

/**
 * Get tracking arguments for a specific post type.
 *
lechuck's avatar
lechuck committed
468
469
 * @since 2.2.0
 * @since 2.5.0 Add post type comments tracking args
ale's avatar
ale committed
470
471
472
473
474
475
476
477
478
 *
 * @param  string $post_type Name of the post type.
 * @return object The tracking arguments of the post type.
 */
function bp_activity_get_post_type_tracking_args( $post_type ) {
	if ( ! post_type_supports( $post_type, 'buddypress-activity' ) ) {
		return false;
	}

lechuck's avatar
lechuck committed
479
480
	$post_type_object           = get_post_type_object( $post_type );
	$post_type_support_comments = post_type_supports( $post_type, 'comments' );
ale's avatar
ale committed
481
482

	$post_type_activity = array(
lechuck's avatar
lechuck committed
483
484
485
486
487
488
489
490
491
492
		'component_id'            => buddypress()->activity->id,
		'action_id'               => 'new_' . $post_type,
		'format_callback'         => 'bp_activity_format_activity_action_custom_post_type_post',
		'front_filter'            => $post_type_object->labels->name,
		'contexts'                => array( 'activity' ),
		'position'                => 0,
		'singular'                => strtolower( $post_type_object->labels->singular_name ),
		'activity_comment'        => ! $post_type_support_comments,
		'comment_action_id'       => false,
		'comment_format_callback' => 'bp_activity_format_activity_action_custom_post_type_comment',
ale's avatar
ale committed
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
	);

	if ( ! empty( $post_type_object->bp_activity ) ) {
		$post_type_activity = bp_parse_args( (array) $post_type_object->bp_activity, $post_type_activity, $post_type . '_tracking_args' );
	}

	$post_type_activity = (object) $post_type_activity;

	// Try to get the admin filter from the post type labels.
	if ( ! empty( $post_type_object->labels->bp_activity_admin_filter ) ) {
		$post_type_activity->admin_filter = $post_type_object->labels->bp_activity_admin_filter;

	// Fall back to a generic name.
	} else {
		$post_type_activity->admin_filter = _x( 'New item published', 'Post Type generic activity post admin filter', 'buddypress' );
	}

	// Check for the front filter in the post type labels.
	if ( ! empty( $post_type_object->labels->bp_activity_front_filter ) ) {
		$post_type_activity->front_filter = $post_type_object->labels->bp_activity_front_filter;
	}

	// Try to get the action for new post type action on non-multisite installations.
	if ( ! empty( $post_type_object->labels->bp_activity_new_post ) ) {
		$post_type_activity->new_post_type_action = $post_type_object->labels->bp_activity_new_post;
	}

	// Try to get the action for new post type action on multisite installations.
	if ( ! empty( $post_type_object->labels->bp_activity_new_post_ms ) ) {
		$post_type_activity->new_post_type_action_ms = $post_type_object->labels->bp_activity_new_post_ms;
	}

lechuck's avatar
lechuck committed
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
	// If the post type supports comments and has a comment action id, build the comments tracking args
	if ( $post_type_support_comments && ! empty( $post_type_activity->comment_action_id ) ) {
		// Init a new container for the activity type for comments
		$post_type_activity->comments_tracking = new stdClass();

		// Build the activity type for comments
		$post_type_activity->comments_tracking->component_id = $post_type_activity->component_id;
		$post_type_activity->comments_tracking->action_id    = $post_type_activity->comment_action_id;

		// Try to get the comments admin filter from the post type labels.
		if ( ! empty( $post_type_object->labels->bp_activity_comments_admin_filter ) ) {
			$post_type_activity->comments_tracking->admin_filter = $post_type_object->labels->bp_activity_comments_admin_filter;

		// Fall back to a generic name.
		} else {
			$post_type_activity->comments_tracking->admin_filter = _x( 'New item comment posted', 'Post Type generic comments activity admin filter', 'buddypress' );
		}

		$post_type_activity->comments_tracking->format_callback = $post_type_activity->comment_format_callback;

		// Check for the comments front filter in the post type labels.
		if ( ! empty( $post_type_object->labels->bp_activity_comments_front_filter ) ) {
			$post_type_activity->comments_tracking->front_filter = $post_type_object->labels->bp_activity_comments_front_filter;

		// Fall back to a generic name.
		} else {
			$post_type_activity->comments_tracking->front_filter = _x( 'Item comments', 'Post Type generic comments activity front filter', 'buddypress' );
		}

		$post_type_activity->comments_tracking->contexts = $post_type_activity->contexts;
		$post_type_activity->comments_tracking->position = (int) $post_type_activity->position + 1;

		// Try to get the action for new post type comment action on non-multisite installations.
		if ( ! empty( $post_type_object->labels->bp_activity_new_comment ) ) {
			$post_type_activity->comments_tracking->new_post_type_comment_action = $post_type_object->labels->bp_activity_new_comment;
		}

		// Try to get the action for new post type comment action on multisite installations.
		if ( ! empty( $post_type_object->labels->bp_activity_new_comment_ms ) ) {
			$post_type_activity->comments_tracking->new_post_type_comment_action_ms = $post_type_object->labels->bp_activity_new_comment_ms;
		}
	}

	// Finally make sure we'll be able to find the post type this activity type is associated to.
	$post_type_activity->post_type = $post_type;

ale's avatar
ale committed
571
572
573
	/**
	 * Filters tracking arguments for a specific post type.
	 *
lechuck's avatar
lechuck committed
574
	 * @since 2.2.0
ale's avatar
ale committed
575
576
577
578
579
580
581
582
583
584
	 *
	 * @param object $post_type_activity The tracking arguments of the post type.
	 * @param string $post_type          Name of the post type.
	 */
	return apply_filters( 'bp_activity_get_post_type_tracking_args', $post_type_activity, $post_type );
}

/**
 * Get tracking arguments for all post types.
 *
lechuck's avatar
lechuck committed
585
586
 * @since 2.2.0
 * @since 2.5.0 Include post type comments tracking args if needed
ale's avatar
ale committed
587
588
589
590
 *
 * @return array List of post types with their tracking arguments.
 */
function bp_activity_get_post_types_tracking_args() {
lechuck's avatar
lechuck committed
591
	// Fetch all public post types.
ale's avatar
ale committed
592
593
594
595
596
597
598
599
	$post_types = get_post_types( array( 'public' => true ), 'names' );

	$post_types_tracking_args = array();

	foreach ( $post_types as $post_type ) {
		$track_post_type = bp_activity_get_post_type_tracking_args( $post_type );

		if ( ! empty( $track_post_type ) ) {
lechuck's avatar
lechuck committed
600
601
602
603
604
605
606
607
608
609
610
611
612
613
			// Set the post type comments tracking args
			if ( ! empty( $track_post_type->comments_tracking->action_id ) ) {
				// Used to check support for comment tracking by activity type (new_post_type_comment)
				$track_post_type->comments_tracking->comments_tracking = true;

				// Used to be able to find the post type this activity type is associated to.
				$track_post_type->comments_tracking->post_type = $post_type;

				$post_types_tracking_args[ $track_post_type->comments_tracking->action_id ] = $track_post_type->comments_tracking;

				// Used to check support for comment tracking by activity type (new_post_type)
				$track_post_type->comments_tracking = true;
			}

ale's avatar
ale committed
614
615
616
617
618
619
620
621
			$post_types_tracking_args[ $track_post_type->action_id ] = $track_post_type;
		}

	}

	/**
	 * Filters tracking arguments for all post types.
	 *
lechuck's avatar
lechuck committed
622
	 * @since 2.2.0
ale's avatar
ale committed
623
624
625
626
627
628
629
	 *
	 * @param array $post_types_tracking_args Array of post types with
	 *                                        their tracking arguments.
	 */
	return apply_filters( 'bp_activity_get_post_types_tracking_args', $post_types_tracking_args );
}

lechuck's avatar
lechuck committed
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
/**
 * Check if the *Post Type* activity supports a specific feature.
 *
 * @since 2.5.0
 *
 * @param  string $activity_type The activity type to check.
 * @param  string $feature       The feature to check. Currently supports:
 *                               'post-type-comment-tracking', 'post-type-comment-reply' & 'comment-reply'.
 *                               See inline doc for more info.
 * @return bool
 */
function bp_activity_type_supports( $activity_type = '', $feature = '' ) {
	$retval = false;

	$bp = buddypress();

	switch ( $feature ) {
		/**
		 * Does this activity type support comment tracking?
		 *
		 * eg. 'new_blog_post' and 'new_blog_comment' will both return true.
		 */
		case 'post-type-comment-tracking' :
			// Set the activity track global if not set yet
			if ( empty( $bp->activity->track ) ) {
				$bp->activity->track = bp_activity_get_post_types_tracking_args();
			}

			if ( ! empty( $bp->activity->track[ $activity_type ]->comments_tracking ) ) {
				$retval = true;
			}
			break;

		/**
		 * Is this a parent activity type that support post comments?
		 *
		 * eg. 'new_blog_post' will return true; 'new_blog_comment' will return false.
		 */
		case 'post-type-comment-reply' :
			// Set the activity track global if not set yet.
			if ( empty( $bp->activity->track ) ) {
				$bp->activity->track = bp_activity_get_post_types_tracking_args();
			}

			if ( ! empty( $bp->activity->track[ $activity_type ]->comments_tracking ) && ! empty( $bp->activity->track[ $activity_type ]->comment_action_id ) ) {
				$retval = true;
			}
			break;

		/**
		 * Does this activity type support comment & reply?
		 */
		case 'comment-reply' :
			// Set the activity track global if not set yet.
			if ( empty( $bp->activity->track ) ) {
				$bp->activity->track = bp_activity_get_post_types_tracking_args();
			}

			// Post Type activities
			if ( ! empty( $bp->activity->track[ $activity_type ] ) ) {
				if ( isset( $bp->activity->track[ $activity_type ]->activity_comment ) ) {
					$retval = $bp->activity->track[ $activity_type ]->activity_comment;
				}

				// Eventually override with comment synchronization feature.
				if ( isset( $bp->activity->track[ $activity_type ]->comments_tracking ) ) {
					$retval = $bp->activity->track[ $activity_type ]->comments_tracking && ! bp_disable_blogforum_comments();
				}

			// Retired Forums component
			} elseif ( 'new_forum_topic' === $activity_type || 'new_forum_post' === $activity_type ) {
				$retval = ! bp_disable_blogforum_comments();

			// By Default, all other activity types are supporting comments.
			} else {
				$retval = true;
			}
			break;
	}

	return $retval;
}

/**
 * Get a specific tracking argument for a given activity type
 *
 * @since 2.5.0
 *
 * @param  string       $activity_type the activity type.
 * @param  string       $arg           the key of the tracking argument.
 * @return mixed        the value of the tracking arg, false if not found.
 */
function bp_activity_post_type_get_tracking_arg( $activity_type, $arg = '' ) {
	if ( empty( $activity_type ) || empty( $arg ) ) {
		return false;
	}

	$bp = buddypress();

	// Set the activity track global if not set yet
	if ( empty( $bp->activity->track ) ) {
		$bp->activity->track = bp_activity_get_post_types_tracking_args();
	}

	if ( isset( $bp->activity->track[ $activity_type ]->{$arg} ) ) {
		return $bp->activity->track[ $activity_type ]->{$arg};
	} else {
		return false;
	}
}

ale's avatar
ale committed
741
742
743
/**
 * Get all components' activity actions, sorted by their position attribute.
 *
lechuck's avatar
lechuck committed
744
 * @since 2.2.0
ale's avatar
ale committed
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
 *
 * @return object Actions ordered by their position.
 */
function bp_activity_get_actions() {
	$bp = buddypress();

	$post_types = bp_activity_get_post_types_tracking_args();

	// Create the actions for the post types, if they haven't already been created.
	if ( ! empty( $post_types ) ) {
		foreach ( $post_types as $post_type ) {
			if ( isset( $bp->activity->actions->{$post_type->component_id}->{$post_type->action_id} ) ) {
				continue;
			}

			bp_activity_set_action(
				$post_type->component_id,
				$post_type->action_id,
				$post_type->admin_filter,
				$post_type->format_callback,
				$post_type->front_filter,
				$post_type->contexts,
				$post_type->position
			);
		}
	}

	return $bp->activity->actions;
}

/**
 * Retrieve the current action from a component and key.
root's avatar
root committed
777
 *
lechuck's avatar
lechuck committed
778
 * @since 1.1.0
root's avatar
root committed
779
 *
lechuck's avatar
lechuck committed
780
 * @param string $component_id The unique string ID of the component.
ale's avatar
ale committed
781
 * @param string $key          The action key.
lechuck's avatar
lechuck committed
782
 * @return string|bool Action value if found, otherwise false.
root's avatar
root committed
783
784
785
 */
function bp_activity_get_action( $component_id, $key ) {

lechuck's avatar
lechuck committed
786
	// Return false if any of the above values are not set.
lucha's avatar
lucha committed
787
	if ( empty( $component_id ) || empty( $key ) ) {
root's avatar
root committed
788
		return false;
lucha's avatar
lucha committed
789
790
	}

ale's avatar
ale committed
791
792
793
794
795
796
	$actions = bp_activity_get_actions();
	$retval  = false;

	if ( isset( $actions->{$component_id}->{$key} ) ) {
		$retval = $actions->{$component_id}->{$key};
	}
root's avatar
root committed
797

ale's avatar
ale committed
798
799
800
	/**
	 * Filters the current action by component and key.
	 *
lechuck's avatar
lechuck committed
801
	 * @since 1.1.0
ale's avatar
ale committed
802
803
804
805
806
	 *
	 * @param string|bool $retval       The action key.
	 * @param string      $component_id The unique string ID of the component.
	 * @param string      $key          The action key.
	 */
lucha's avatar
lucha committed
807
	return apply_filters( 'bp_activity_get_action', $retval, $component_id, $key );
root's avatar
root committed
808
809
}

lechuck's avatar
lechuck committed
810
/**
lechuck's avatar
lechuck committed
811
 * Fetch details of all registered activity types.
lechuck's avatar
lechuck committed
812
 *
lechuck's avatar
lechuck committed
813
 * @since 1.7.0
lechuck's avatar
lechuck committed
814
815
 *
 * @return array array( type => description ), ...
lechuck's avatar
lechuck committed
816
817
818
819
820
 */
function bp_activity_get_types() {
	$actions  = array();

	// Walk through the registered actions, and build an array of actions/values.
ale's avatar
ale committed
821
	foreach ( bp_activity_get_actions() as $action ) {
lechuck's avatar
lechuck committed
822
823
		$action = array_values( (array) $action );

lucha's avatar
lucha committed
824
		for ( $i = 0, $i_count = count( $action ); $i < $i_count; $i++ ) {
lechuck's avatar
lechuck committed
825
			$actions[ $action[$i]['key'] ] = $action[$i]['value'];
lucha's avatar
lucha committed
826
		}
lechuck's avatar
lechuck committed
827
828
	}

lechuck's avatar
lechuck committed
829
	// This was a mis-named activity type from before BP 1.6.
lechuck's avatar
lechuck committed
830
831
	unset( $actions['friends_register_activity_action'] );

ale's avatar
ale committed
832
833
834
	/**
	 * Filters the available activity types.
	 *
lechuck's avatar
lechuck committed
835
	 * @since 1.7.0
ale's avatar
ale committed
836
837
838
	 *
	 * @param array $actions Array of registered activity types.
	 */
lechuck's avatar
lechuck committed
839
840
841
	return apply_filters( 'bp_activity_get_types', $actions );
}

lucha's avatar
lucha committed
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
/**
 * Gets the current activity context.
 *
 * The "context" is the current view type, corresponding roughly to the
 * current component. Use this context to determine which activity actions
 * should be whitelisted for the filter dropdown.
 *
 * @since 2.8.0
 *
 * @return string Activity context. 'member', 'member_groups', 'group', 'activity'.
 */
function bp_activity_get_current_context() {
	// On member pages, default to 'member', unless this is a user's Groups activity.
	if ( bp_is_user() ) {
		if ( bp_is_active( 'groups' ) && bp_is_current_action( bp_get_groups_slug() ) ) {
			$context = 'member_groups';
		} else {
			$context = 'member';
		}

	// On individual group pages, default to 'group'.
	} elseif ( bp_is_active( 'groups' ) && bp_is_group() ) {
		$context = 'group';

	// 'activity' everywhere else.
	} else {
		$context = 'activity';
	}

	return $context;
}

/**
 * Gets a flat list of activity actions compatible with a given context.
 *
 * @since 2.8.0
 *
 * @param string $context Optional. Name of the context. Defaults to the current context.
 * @return array
 */
function bp_activity_get_actions_for_context( $context = '' ) {
	if ( ! $context ) {
		$context = bp_activity_get_current_context();
	}

	$actions = array();
	foreach ( bp_activity_get_actions() as $component_actions ) {
		foreach ( $component_actions as $component_action ) {
			if ( in_array( $context, (array) $component_action['context'], true ) ) {
				$actions[] = $component_action;
			}
		}
	}

	return $actions;
}

root's avatar
root committed
899
900
901
/** Favorites ****************************************************************/

/**
lechuck's avatar
lechuck committed
902
 * Get a users favorite activity stream items.
root's avatar
root committed
903
 *
lechuck's avatar
lechuck committed
904
 * @since 1.2.0
root's avatar
root committed
905
 *
lechuck's avatar
lechuck committed
906
907
 * @param int $user_id ID of the user whose favorites are being queried.
 * @return array IDs of the user's favorite activity items.
root's avatar
root committed
908
909
910
 */
function bp_activity_get_user_favorites( $user_id = 0 ) {

lechuck's avatar
lechuck committed
911
	// Fallback to logged in user if no user_id is passed.
lucha's avatar
lucha committed
912
	if ( empty( $user_id ) ) {
lechuck's avatar
lechuck committed
913
		$user_id = bp_displayed_user_id();
lucha's avatar
lucha committed
914
	}
root's avatar
root committed
915

lechuck's avatar
lechuck committed
916
	// Get favorites for user.
root's avatar
root committed
917
918
	$favs = bp_get_user_meta( $user_id, 'bp_favorite_activities', true );

ale's avatar
ale committed
919
920
921
	/**
	 * Filters the favorited activity items for a specified user.
	 *
lechuck's avatar
lechuck committed
922
	 * @since 1.2.0
ale's avatar
ale committed
923
924
925
	 *
	 * @param array $favs Array of user's favorited activity items.
	 */
root's avatar
root committed
926
927
928
929
	return apply_filters( 'bp_activity_get_user_favorites', $favs );
}

/**
lechuck's avatar
lechuck committed
930
 * Add an activity stream item as a favorite for a user.
root's avatar
root committed
931
 *
lechuck's avatar
lechuck committed
932
 * @since 1.2.0
root's avatar
root committed
933
 *
lechuck's avatar
lechuck committed
934
 * @param int $activity_id ID of the activity item being favorited.
ale's avatar
ale committed
935
 * @param int $user_id     ID of the user favoriting the activity item.
lechuck's avatar
lechuck committed
936
 * @return bool True on success, false on failure.
root's avatar
root committed
937
938
939
 */
function bp_activity_add_user_favorite( $activity_id, $user_id = 0 ) {

lechuck's avatar
lechuck committed
940
	// Favorite activity stream items are for logged in users only.
lucha's avatar
lucha committed
941
	if ( ! is_user_logged_in() ) {
root's avatar
root committed
942
		return false;
lucha's avatar
lucha committed
943
	}
root's avatar
root committed
944

lechuck's avatar
lechuck committed
945
	// Fallback to logged in user if no user_id is passed.
lucha's avatar
lucha committed
946
	if ( empty( $user_id ) ) {
lechuck's avatar
lechuck committed
947
		$user_id = bp_loggedin_user_id();
lucha's avatar
lucha committed
948
	}
root's avatar
root committed
949

lechuck's avatar
lechuck committed
950
951
952
953
954
	$my_favs = bp_get_user_meta( $user_id, 'bp_favorite_activities', true );
	if ( empty( $my_favs ) || ! is_array( $my_favs ) ) {
		$my_favs = array();
	}

lechuck's avatar
lechuck committed
955
	// Bail if the user has already favorited this activity item.
lechuck's avatar
lechuck committed
956
957
958
959
	if ( in_array( $activity_id, $my_favs ) ) {
		return false;
	}

lechuck's avatar
lechuck committed
960
	// Add to user's favorites.
root's avatar
root committed
961
962
	$my_favs[] = $activity_id;

lechuck's avatar
lechuck committed
963
	// Update the total number of users who have favorited this activity.
root's avatar
root committed
964
	$fav_count = bp_activity_get_meta( $activity_id, 'favorite_count' );
lechuck's avatar
lechuck committed
965
	$fav_count = !empty( $fav_count ) ? (int) $fav_count + 1 : 1;
root's avatar
root committed
966

lechuck's avatar
lechuck committed
967
	// Update user meta.
lechuck's avatar
lechuck committed
968
	bp_update_user_meta( $user_id, 'bp_favorite_activities', $my_favs );
root's avatar
root committed
969

lechuck's avatar
lechuck committed
970
	// Update activity meta counts.
lechuck's avatar
lechuck committed
971
	if ( bp_activity_update_meta( $activity_id, 'favorite_count', $fav_count ) ) {
root's avatar
root committed
972

ale's avatar
ale committed
973
974
975
		/**
		 * Fires if bp_activity_update_meta() for favorite_count is successful and before returning a true value for success.
		 *
lechuck's avatar
lechuck committed
976
		 * @since 1.2.1
ale's avatar
ale committed
977
978
979
980
		 *
		 * @param int $activity_id ID of the activity item being favorited.
		 * @param int $user_id     ID of the user doing the favoriting.
		 */
root's avatar
root committed
981
982
		do_action( 'bp_activity_add_user_favorite', $activity_id, $user_id );

lechuck's avatar
lechuck committed
983
		// Success.
root's avatar
root committed
984
985
		return true;

lechuck's avatar
lechuck committed
986
	// Saving meta was unsuccessful for an unknown reason.
root's avatar
root committed
987
	} else {
ale's avatar
ale committed
988
989
990
991

		/**
		 * Fires if bp_activity_update_meta() for favorite_count is unsuccessful and before returning a false value for failure.
		 *
lechuck's avatar
lechuck committed
992
		 * @since 1.5.0
ale's avatar
ale committed
993
994
995
996
		 *
		 * @param int $activity_id ID of the activity item being favorited.
		 * @param int $user_id     ID of the user doing the favoriting.
		 */
root's avatar
root committed
997
998
999
1000
		do_action( 'bp_activity_add_user_favorite_fail', $activity_id, $user_id );

		return false;
	}