blogs->id, 'new_blog', __( 'New site created', 'buddypress' ), 'bp_blogs_format_activity_action_new_blog', __( 'New Sites', 'buddypress' ), array( 'activity', 'member' ), 0 ); } /** * Fires after the registry of the default blog component activity actions. * * @since 1.1.0 */ do_action( 'bp_blogs_register_activity_actions' ); } add_action( 'bp_register_activity_actions', 'bp_blogs_register_activity_actions' ); /** * Set up the tracking arguments for the 'post' post type. * * @since 2.5.0 This was moved out of the BP_Blogs_Component class. * * @see bp_activity_get_post_type_tracking_args() for information on parameters. * * @param object|null $params Tracking arguments. * @param string|int $post_type Post type to track. * @return object|null */ function bp_blogs_register_post_tracking_args( $params = null, $post_type = 0 ) { /** * Filters the post types to track for the Blogs component. * * @since 1.5.0 * @deprecated 2.3.0 * * Make sure plugins still using 'bp_blogs_record_post_post_types' * to track their post types will generate new_blog_post activities * See https://buddypress.trac.wordpress.org/ticket/6306 * * @param array $value Array of post types to track. */ $post_types = apply_filters( 'bp_blogs_record_post_post_types', array( 'post' ) ); $post_types_array = array_flip( $post_types ); if ( ! isset( $post_types_array[ $post_type ] ) ) { return $params; } // Set specific params for the 'post' post type. $params->component_id = buddypress()->blogs->id; $params->action_id = 'new_blog_post'; $params->admin_filter = __( 'New post published', 'buddypress' ); $params->format_callback = 'bp_blogs_format_activity_action_new_blog_post'; $params->front_filter = __( 'Posts', 'buddypress' ); $params->contexts = array( 'activity', 'member' ); $params->position = 5; if ( post_type_supports( $post_type, 'comments' ) ) { $params->comment_action_id = 'new_blog_comment'; /** * Filters the post types to track for the Blogs component. * * @since 1.5.0 * @deprecated 2.5.0 * * Make sure plugins still using 'bp_blogs_record_comment_post_types' * to track comment about their post types will generate new_blog_comment activities * See https://buddypress.trac.wordpress.org/ticket/6306 * * @param array $value Array of post types to track. */ $comment_post_types = apply_filters( 'bp_blogs_record_comment_post_types', array( 'post' ) ); $comment_post_types_array = array_flip( $comment_post_types ); if ( isset( $comment_post_types_array[ $post_type ] ) ) { $params->comments_tracking = new stdClass(); $params->comments_tracking->component_id = buddypress()->blogs->id; $params->comments_tracking->action_id = 'new_blog_comment'; $params->comments_tracking->admin_filter = __( 'New post comment posted', 'buddypress' ); $params->comments_tracking->format_callback = 'bp_blogs_format_activity_action_new_blog_comment'; $params->comments_tracking->front_filter = __( 'Comments', 'buddypress' ); $params->comments_tracking->contexts = array( 'activity', 'member' ); $params->comments_tracking->position = 10; } } return $params; } add_filter( 'bp_activity_get_post_type_tracking_args', 'bp_blogs_register_post_tracking_args', 10, 2 ); /** * Format 'new_blog' activity actions. * * @since 2.0.0 * * @param string $action Static activity action. * @param object $activity Activity data object. * @return string */ function bp_blogs_format_activity_action_new_blog( $action, $activity ) { $blog_url = bp_blogs_get_blogmeta( $activity->item_id, 'url' ); $blog_name = bp_blogs_get_blogmeta( $activity->item_id, 'name' ); $action = sprintf( /* translators: 1: the activity user link. 2: the blog link. */ esc_html__( '%1$s created the site %2$s', 'buddypress' ), bp_core_get_userlink( $activity->user_id ), '' . esc_html( $blog_name ) . '' ); // Legacy filter - requires the BP_Blogs_Blog object. if ( has_filter( 'bp_blogs_activity_created_blog_action' ) ) { $user_blog = BP_Blogs_Blog::get_user_blog( $activity->user_id, $activity->item_id ); if ( $user_blog ) { $recorded_blog = new BP_Blogs_Blog( $user_blog ); } if ( isset( $recorded_blog ) ) { $action = apply_filters( 'bp_blogs_activity_created_blog_action', $action, $recorded_blog, $blog_name, bp_blogs_get_blogmeta( $activity->item_id, 'description' ) ); } } /** * Filters the new blog activity action for the new blog. * * @since 2.0.0 * * @param string $action Constructed activity action. * @param object $activity Activity data object. */ return apply_filters( 'bp_blogs_format_activity_action_new_blog', $action, $activity ); } /** * Format 'new_blog_post' activity actions. * * @since 2.0.0 * * @param string $action Static activity action. * @param object $activity Activity data object. * @return string Constructed activity action. */ function bp_blogs_format_activity_action_new_blog_post( $action, $activity ) { $blog_url = bp_blogs_get_blogmeta( $activity->item_id, 'url' ); $blog_name = bp_blogs_get_blogmeta( $activity->item_id, 'name' ); if ( empty( $blog_url ) || empty( $blog_name ) ) { $blog_url = get_home_url( $activity->item_id ); $blog_name = get_blog_option( $activity->item_id, 'blogname' ); bp_blogs_update_blogmeta( $activity->item_id, 'url', $blog_url ); bp_blogs_update_blogmeta( $activity->item_id, 'name', $blog_name ); } /** * When the post is published we are faking an activity object * to which we add 2 properties : * - the post url * - the post title * This is done to build the 'post link' part of the activity * action string. * NB: in this case the activity has not yet been created. */ if ( isset( $activity->post_url ) ) { $post_url = $activity->post_url; /** * The post_url property is not set, we need to build the url * thanks to the post id which is also saved as the secondary * item id property of the activity object. */ } else { $post_url = add_query_arg( 'p', $activity->secondary_item_id, trailingslashit( $blog_url ) ); } // Should be the case when the post has just been published. if ( isset( $activity->post_title ) ) { $post_title = $activity->post_title; // If activity already exists try to get the post title from activity meta. } else if ( ! empty( $activity->id ) ) { $post_title = bp_activity_get_meta( $activity->id, 'post_title' ); } /** * In case the post was published without a title * or the activity meta was not found. */ if ( empty( $post_title ) ) { // Defaults to no title. $post_title = __( '(no title)', 'buddypress' ); switch_to_blog( $activity->item_id ); $post = get_post( $activity->secondary_item_id ); if ( is_a( $post, 'WP_Post' ) ) { // Does the post have a title ? if ( ! empty( $post->post_title ) ) { $post_title = $post->post_title; } // Make sure the activity exists before saving the post title in activity meta. if ( ! empty( $activity->id ) ) { bp_activity_update_meta( $activity->id, 'post_title', $post_title ); } } restore_current_blog(); } // Build the 'post link' part of the activity action string. $post_link = '' . esc_html( $post_title ) . ''; $user_link = bp_core_get_userlink( $activity->user_id ); // Build the complete activity action string. if ( is_multisite() ) { $action = sprintf( /* translators: 1: the activity user link. 2: the post link. 3: the blog link. */ esc_html_x( '%1$s wrote a new post, %2$s, on the site %3$s', '`new_blog_post` activity action', 'buddypress' ), $user_link, $post_link, '' . esc_html( $blog_name ) . '' ); } else { $action = sprintf( /* translators: 1: the activity user link. 2: the post link. */ esc_html_x( '%1$s wrote a new post, %2$s', '`new_blog_post` activity action', 'buddypress' ), $user_link, $post_link ); } // Legacy filter - requires the post object. if ( has_filter( 'bp_blogs_activity_new_post_action' ) ) { switch_to_blog( $activity->item_id ); $post = get_post( $activity->secondary_item_id ); restore_current_blog(); if ( ! empty( $post ) && ! is_wp_error( $post ) ) { $action = apply_filters( 'bp_blogs_activity_new_post_action', $action, $post, $post_url ); } } /** * Filters the new blog post action for the new blog. * * @since 2.0.0 * * @param string $action Constructed activity action. * @param object $activity Activity data object. */ return apply_filters( 'bp_blogs_format_activity_action_new_blog_post', $action, $activity ); } /** * Format 'new_blog_comment' activity actions. * * @since 2.0.0 * * @param string $action Static activity action. * @param object $activity Activity data object. * @return string Constructed activity action. */ function bp_blogs_format_activity_action_new_blog_comment( $action, $activity ) { /** * When the comment is published we are faking an activity object * to which we add 4 properties : * - the post url * - the post title * - the blog url * - the blog name * This is done to build the 'post link' part of the activity * action string. * NB: in this case the activity has not yet been created. */ $blog_url = false; // Try to get the blog url from the activity object. if ( isset( $activity->blog_url ) ) { $blog_url = $activity->blog_url; } else { $blog_url = bp_blogs_get_blogmeta( $activity->item_id, 'url' ); } $blog_name = false; // Try to get the blog name from the activity object. if ( isset( $activity->blog_name ) ) { $blog_name = $activity->blog_name; } else { $blog_name = bp_blogs_get_blogmeta( $activity->item_id, 'name' ); } if ( empty( $blog_url ) || empty( $blog_name ) ) { $blog_url = get_home_url( $activity->item_id ); $blog_name = get_blog_option( $activity->item_id, 'blogname' ); bp_blogs_update_blogmeta( $activity->item_id, 'url', $blog_url ); bp_blogs_update_blogmeta( $activity->item_id, 'name', $blog_name ); } $post_url = false; // Try to get the post url from the activity object. if ( isset( $activity->post_url ) ) { $post_url = $activity->post_url; /** * The post_url property is not set, we need to build the url * thanks to the post id which is also saved as the secondary * item id property of the activity object. */ } elseif ( ! empty( $activity->id ) ) { $post_url = bp_activity_get_meta( $activity->id, 'post_url' ); } $post_title = false; // Should be the case when the comment has just been published. if ( isset( $activity->post_title ) ) { $post_title = $activity->post_title; // If activity already exists try to get the post title from activity meta. } elseif ( ! empty( $activity->id ) ) { $post_title = bp_activity_get_meta( $activity->id, 'post_title' ); } // Should only be empty at the time of post creation. if ( empty( $post_url ) || empty( $post_title ) ) { switch_to_blog( $activity->item_id ); $comment = get_comment( $activity->secondary_item_id ); if ( ! empty( $comment->comment_post_ID ) ) { $post_url = add_query_arg( 'p', $comment->comment_post_ID, trailingslashit( $blog_url ) ); bp_activity_update_meta( $activity->id, 'post_url', $post_url ); $post = get_post( $comment->comment_post_ID ); if ( is_a( $post, 'WP_Post' ) ) { $post_title = $post->post_title; bp_activity_update_meta( $activity->id, 'post_title', $post_title ); } } restore_current_blog(); } $post_link = '' . esc_html( $post_title ) . ''; $user_link = bp_core_get_userlink( $activity->user_id ); if ( is_multisite() ) { $action = sprintf( /* translators: 1: the activity user link. 2: the post link. 3: the blog link. */ esc_html_x( '%1$s commented on the post, %2$s, on the site %3$s', '`new_blog_comment` activity action', 'buddypress' ), $user_link, $post_link, '' . esc_html( $blog_name ) . '' ); } else { $action = sprintf( /* translators: 1: the activity user link. 2: the post link. */ esc_html_x( '%1$s commented on the post, %2$s', '`new_blog_comment` activity action', 'buddypress' ), $user_link, $post_link ); } // Legacy filter - requires the comment object. if ( has_filter( 'bp_blogs_activity_new_comment_action' ) ) { switch_to_blog( $activity->item_id ); $comment = get_comment( $activity->secondary_item_id ); restore_current_blog(); if ( ! empty( $comment ) && ! is_wp_error( $comment ) ) { $action = apply_filters( 'bp_blogs_activity_new_comment_action', $action, $comment, $post_url . '#' . $activity->secondary_item_id ); } } /** * Filters the new blog comment action for the new blog. * * @since 2.0.0 * * @param string $action Constructed activity action. * @param object $activity Activity data object. */ return apply_filters( 'bp_blogs_format_activity_action_new_blog_comment', $action, $activity ); } /** * Fetch data related to blogs at the beginning of an activity loop. * * This reduces database overhead during the activity loop. * * @since 2.0.0 * * @param array $activities Array of activity items. * @return array */ function bp_blogs_prefetch_activity_object_data( $activities ) { if ( empty( $activities ) ) { return $activities; } $blog_ids = array(); foreach ( $activities as $activity ) { if ( buddypress()->blogs->id !== $activity->component ) { continue; } $blog_ids[] = $activity->item_id; } if ( ! empty( $blog_ids ) ) { bp_blogs_update_meta_cache( $blog_ids ); } return $activities; } add_filter( 'bp_activity_prefetch_object_data', 'bp_blogs_prefetch_activity_object_data' ); /** * Record blog-related activity to the activity stream. * * @since 1.0.0 * * @see bp_activity_add() for description of parameters. * * @param array|string $args { * See {@link bp_activity_add()} for complete description of arguments. * The arguments listed here have different default values from * bp_activity_add(). * @type string $component Default: 'blogs'. * } * @return WP_Error|bool|int On success, returns the activity ID. False on failure. */ function bp_blogs_record_activity( $args = '' ) { $r = bp_parse_args( $args, array( 'user_id' => bp_loggedin_user_id(), 'action' => '', 'content' => '', 'primary_link' => '', 'component' => buddypress()->blogs->id, 'type' => false, 'item_id' => false, 'secondary_item_id' => false, 'recorded_time' => bp_core_current_time(), 'hide_sitewide' => false, ) ); if ( ! empty( $r['action'] ) ) { /** * Filters the action associated with activity for activity stream. * * @since 1.2.0 * * @param string $value Action for the activity stream. */ $r['action'] = apply_filters( 'bp_blogs_record_activity_action', $r['action'] ); } if ( ! empty( $r['content'] ) ) { /** * Filters the content associated with activity for activity stream. * * @since 1.2.0 * * @param string $value Generated summary from content for the activity stream. * @param string $value Content for the activity stream. * @param array $r Array of arguments used for the activity stream item. */ $r['content'] = apply_filters( 'bp_blogs_record_activity_content', bp_activity_create_summary( $r['content'], $r ), $r['content'], $r ); } // Check for an existing entry and update if one exists. $id = bp_activity_get_activity_id( array( 'user_id' => $r['user_id'], 'component' => $r['component'], 'type' => $r['type'], 'item_id' => $r['item_id'], 'secondary_item_id' => $r['secondary_item_id'], ) ); return bp_activity_add( array( 'id' => $id, 'user_id' => $r['user_id'], 'action' => $r['action'], 'content' => $r['content'], 'primary_link' => $r['primary_link'], 'component' => $r['component'], 'type' => $r['type'], 'item_id' => $r['item_id'], 'secondary_item_id' => $r['secondary_item_id'], 'recorded_time' => $r['recorded_time'], 'hide_sitewide' => $r['hide_sitewide'] ) ); } /** * Delete a blog-related activity stream item. * * @since 1.0.0 * * @see bp_activity_delete() for description of parameters. * * @param array|string $args { * See {@link bp_activity_delete()} for complete description of arguments. * The arguments listed here have different default values from * bp_activity_add(). * @type string $component Default: 'blogs'. * } * @return bool True on success, false on failure. */ function bp_blogs_delete_activity( $args = '' ) { $r = bp_parse_args( $args, array( 'item_id' => false, 'component' => buddypress()->blogs->id, 'type' => false, 'user_id' => false, 'secondary_item_id' => false, ) ); bp_activity_delete_by_item_id( $r ); } /** * Check if a blog post's activity item should be closed from commenting. * * This mirrors the {@link comments_open()} and {@link _close_comments_for_old_post()} * functions, but for use with the BuddyPress activity stream to be as * lightweight as possible. * * By lightweight, we actually mirror a few of the blog's commenting settings * to blogmeta and checks the values in blogmeta instead. This is to prevent * multiple {@link switch_to_blog()} calls in the activity stream. * * @since 2.0.0 * * @param object $activity The BP_Activity_Activity object. * @return bool */ function bp_blogs_comments_open( $activity ) { $open = true; $blog_id = $activity->item_id; // See if we've mirrored the close comments option before. $days_old = bp_blogs_get_blogmeta( $blog_id, 'close_comments_days_old' ); // We've never cached these items before, so do it now. if ( '' === $days_old ) { switch_to_blog( $blog_id ); // Use comments_open(). remove_filter( 'comments_open', 'bp_comments_open', 10 ); $open = comments_open( $activity->secondary_item_id ); add_filter( 'comments_open', 'bp_comments_open', 10, 2 ); // Might as well mirror values to blogmeta since we're here! $thread_depth = get_option( 'thread_comments' ); if ( ! empty( $thread_depth ) ) { $thread_depth = get_option( 'thread_comments_depth' ); } else { // Perhaps filter this? $thread_depth = 1; } bp_blogs_update_blogmeta( $blog_id, 'close_comments_for_old_posts', get_option( 'close_comments_for_old_posts' ) ); bp_blogs_update_blogmeta( $blog_id, 'close_comments_days_old', get_option( 'close_comments_days_old' ) ); bp_blogs_update_blogmeta( $blog_id, 'thread_comments_depth', $thread_depth ); restore_current_blog(); // Check blogmeta and manually check activity item. // Basically a copy of _close_comments_for_old_post(). } else { // Comments are closed. if ( 'closed' == bp_activity_get_meta( $activity->id, 'post_comment_status' ) ) { return false; } if ( ! bp_blogs_get_blogmeta( $blog_id, 'close_comments_for_old_posts' ) ) { return $open; } $days_old = (int) $days_old; if ( ! $days_old ) { return $open; } /* Commenting out for now - needs some more thought... should we add the post type to activity meta? $post = get_post($post_id); // This filter is documented in wp-includes/comment.php $post_types = apply_filters( 'close_comments_for_post_types', array( 'post' ) ); if ( ! in_array( $post->post_type, $post_types ) ) return $open; */ if ( time() - strtotime( $activity->date_recorded ) > ( $days_old * DAY_IN_SECONDS ) ) { return false; } return $open; } return $open; } /** SITE TRACKING *******************************************************/ /** * Add an activity entry for a newly-created site. * * Hooked to the 'bp_blogs_new_blog' action. * * @since 2.6.0 * * @param BP_Blogs_Blog $recorded_blog Current site being recorded. Passed by reference. * @param bool $is_private Whether the current site being recorded is private. * @param bool $is_recorded Whether the current site was recorded. */ function bp_blogs_record_activity_on_site_creation( $recorded_blog, $is_private, $is_recorded, $no_activity ) { // Only record this activity if the blog is public. if ( ! $is_private && ! $no_activity && bp_blogs_is_blog_trackable( $recorded_blog->blog_id, $recorded_blog->user_id ) ) { bp_blogs_record_activity( array( 'user_id' => $recorded_blog->user_id, /** * Filters the activity created blog primary link. * * @since 1.1.0 * * @param string $value Blog primary link. * @param int $value Blog ID. */ 'primary_link' => apply_filters( 'bp_blogs_activity_created_blog_primary_link', bp_blogs_get_blogmeta( $recorded_blog->blog_id, 'url' ), $recorded_blog->blog_id ), 'type' => 'new_blog', 'item_id' => $recorded_blog->blog_id ) ); } } add_action( 'bp_blogs_new_blog', 'bp_blogs_record_activity_on_site_creation', 10, 4 ); /** * Deletes the 'new_blog' activity entry when a site is deleted. * * @since 2.6.0 * * @param int $blog_id Site ID. */ function bp_blogs_delete_new_blog_activity_for_site( $blog_id, $user_id = 0 ) { $args = array( 'item_id' => $blog_id, 'component' => buddypress()->blogs->id, 'type' => 'new_blog' ); /** * In the case a user is removed, make sure he is the author of the 'new_blog' activity * when trying to delete it. */ if ( ! empty( $user_id ) ) { $args['user_id'] = $user_id; } bp_blogs_delete_activity( $args ); } add_action( 'bp_blogs_remove_blog', 'bp_blogs_delete_new_blog_activity_for_site', 10, 1 ); add_action( 'bp_blogs_remove_blog_for_user', 'bp_blogs_delete_new_blog_activity_for_site', 10, 2 ); /** * Delete all 'blogs' activity items for a site when the site is deleted. * * @since 2.6.0 * * @param int $blog_id Site ID. */ function bp_blogs_delete_activity_for_site( $blog_id ) { bp_blogs_delete_activity( array( 'item_id' => $blog_id, 'component' => buddypress()->blogs->id, 'type' => false ) ); } add_action( 'bp_blogs_remove_data_for_blog', 'bp_blogs_delete_activity_for_site' ); /** * Remove a blog post activity item from the activity stream. * * @since 1.0.0 * * @param int $post_id ID of the post to be removed. * @param int $blog_id Optional. Defaults to current blog ID. * @param int $user_id Optional. Defaults to the logged-in user ID. This param * is currently unused in the function (but is passed to hooks). * @return bool */ function bp_blogs_remove_post( $post_id, $blog_id = 0, $user_id = 0 ) { global $wpdb; if ( empty( $wpdb->blogid ) ) { return false; } $post_id = (int) $post_id; if ( ! $blog_id ) { $blog_id = (int) $wpdb->blogid; } if ( ! $user_id ) { $user_id = bp_loggedin_user_id(); } /** * Fires before removal of a blog post activity item from the activity stream. * * @since 1.5.0 * * @param int $blog_id ID of the blog associated with the post that was removed. * @param int $post_id ID of the post that was removed. * @param int $user_id ID of the user having the blog removed for. */ do_action( 'bp_blogs_before_remove_post', $blog_id, $post_id, $user_id ); bp_blogs_delete_activity( array( 'item_id' => $blog_id, 'secondary_item_id' => $post_id, 'component' => buddypress()->blogs->id, 'type' => 'new_blog_post' ) ); /** * Fires after removal of a blog post activity item from the activity stream. * * @since 1.0.0 * * @param int $blog_id ID of the blog associated with the post that was removed. * @param int $post_id ID of the post that was removed. * @param int $user_id ID of the user having the blog removed for. */ do_action( 'bp_blogs_remove_post', $blog_id, $post_id, $user_id ); } add_action( 'delete_post', 'bp_blogs_remove_post' ); /** POST COMMENT SYNCHRONIZATION ****************************************/ /** * Syncs activity comments and posts them back as blog comments. * * Note: This is only a one-way sync - activity comments -> blog comment. * * For blog post -> activity comment, see {@link bp_activity_post_type_comment()}. * * @since 2.0.0 * @since 2.5.0 Allow custom post types to sync their comments with activity ones * * @param int $comment_id The activity ID for the posted activity comment. * @param array $params Parameters for the activity comment. * @param object $parent_activity Parameters of the parent activity item (in this case, the blog post). */ function bp_blogs_sync_add_from_activity_comment( $comment_id, $params, $parent_activity ) { // if parent activity isn't a post type having the buddypress-activity support, stop now! if ( ! bp_activity_type_supports( $parent_activity->type, 'post-type-comment-tracking' ) ) { return; } // If activity comments are disabled for blog posts, stop now! if ( bp_disable_blogforum_comments() ) { return; } // Do not sync if the activity comment was marked as spam. $activity = new BP_Activity_Activity( $comment_id ); if ( $activity->is_spam ) { return; } // Check if comments are still open for parent item. $comments_open = bp_blogs_comments_open( $parent_activity ); if ( ! $comments_open ) { return; } // Get userdata. if ( $params['user_id'] == bp_loggedin_user_id() ) { $user = buddypress()->loggedin_user->userdata; } else { $user = bp_core_get_core_userdata( $params['user_id'] ); } // Get associated post type and set default comment parent. $post_type = bp_activity_post_type_get_tracking_arg( $parent_activity->type, 'post_type' ); $comment_parent = 0; // See if a parent WP comment ID exists. if ( ! empty( $params['parent_id'] ) && ! empty( $post_type ) ) { $comment_parent = bp_activity_get_meta( $params['parent_id'], "bp_blogs_{$post_type}_comment_id" ); } // Comment args. $args = array( 'comment_post_ID' => $parent_activity->secondary_item_id, 'comment_author' => bp_core_get_user_displayname( $params['user_id'] ), 'comment_author_email' => $user->user_email, 'comment_author_url' => bp_core_get_user_domain( $params['user_id'], $user->user_nicename, $user->user_login ), 'comment_content' => $params['content'], 'comment_type' => '', // Could be interesting to add 'BuddyPress' here... 'comment_parent' => (int) $comment_parent, 'user_id' => $params['user_id'], 'comment_approved' => 1 ); // Prevent separate activity entry being made. remove_action( 'comment_post', 'bp_activity_post_type_comment', 10 ); // Handle multisite. switch_to_blog( $parent_activity->item_id ); // Handle timestamps for the WP comment after we've switched to the blog. $args['comment_date'] = current_time( 'mysql' ); $args['comment_date_gmt'] = current_time( 'mysql', 1 ); // Post the comment. $post_comment_id = wp_insert_comment( $args ); // Add meta to comment. add_comment_meta( $post_comment_id, 'bp_activity_comment_id', $comment_id ); // Add meta to activity comment. if ( ! empty( $post_type ) ) { bp_activity_update_meta( $comment_id, "bp_blogs_{$post_type}_comment_id", $post_comment_id ); } // Resave activity comment with WP comment permalink. // // in bp_blogs_activity_comment_permalink(), we change activity comment // permalinks to use the post comment link // // @todo since this is done after AJAX posting, the activity comment permalink // doesn't change on the front end until the next page refresh. $resave_activity = new BP_Activity_Activity( $comment_id ); $resave_activity->primary_link = get_comment_link( $post_comment_id ); /** * Now that the activity id exists and the post comment was created, we don't need to update * the content of the comment as there are no chances it has evolved. */ remove_action( 'bp_activity_before_save', 'bp_blogs_sync_activity_edit_to_post_comment', 20 ); $resave_activity->save(); // Add the edit activity comment hook back. add_action( 'bp_activity_before_save', 'bp_blogs_sync_activity_edit_to_post_comment', 20 ); // Multisite again! restore_current_blog(); // Add the comment hook back. add_action( 'comment_post', 'bp_activity_post_type_comment', 10, 2 ); /** * Fires after activity comments have been synced and posted as blog comments. * * @since 2.0.0 * * @param int $comment_id The activity ID for the posted activity comment. * @param array $args Array of args used for the comment syncing. * @param object $parent_activity Parameters of the blog post parent activity item. * @param object $user User data object for the blog comment. */ do_action( 'bp_blogs_sync_add_from_activity_comment', $comment_id, $args, $parent_activity, $user ); } add_action( 'bp_activity_comment_posted', 'bp_blogs_sync_add_from_activity_comment', 10, 3 ); /** * Deletes the blog comment when the associated activity comment is deleted. * * Note: This is hooked on the 'bp_activity_delete_comment_pre' filter instead * of the 'bp_activity_delete_comment' action because we need to fetch the * activity comment children before they are deleted. * * @since 2.0.0 * @since 2.5.0 Add the $delected parameter * * @param bool $retval Whether BuddyPress should continue or not. * @param int $parent_activity_id The parent activity ID for the activity comment. * @param int $activity_id The activity ID for the pending deleted activity comment. * @param bool $deleted Whether the comment was deleted or not. * @return bool */ function bp_blogs_sync_delete_from_activity_comment( $retval, $parent_activity_id, $activity_id, &$deleted ) { // Check if parent activity is a blog post. $parent_activity = new BP_Activity_Activity( $parent_activity_id ); // if parent activity isn't a post type having the buddypress-activity support, stop now! if ( ! bp_activity_type_supports( $parent_activity->type, 'post-type-comment-tracking' ) ) { return $retval; } // Fetch the activity comments for the activity item. $activity = bp_activity_get( array( 'in' => $activity_id, 'display_comments' => 'stream', 'spam' => 'all', ) ); // Get all activity comment IDs for the pending deleted item. $activity_ids = bp_activity_recurse_comments_activity_ids( $activity ); $activity_ids[] = $activity_id; // Handle multisite. // switch to the blog where the comment was made. switch_to_blog( $parent_activity->item_id ); // Remove associated blog comments. bp_blogs_remove_associated_blog_comments( $activity_ids, current_user_can( 'moderate_comments' ) ); // Multisite again! restore_current_blog(); // Rebuild activity comment tree // emulate bp_activity_delete_comment(). BP_Activity_Activity::rebuild_activity_comment_tree( $parent_activity_id ); // Avoid the error message although the comments were successfully deleted. $deleted = true; // We're overriding the default bp_activity_delete_comment() functionality // so we need to return false. return false; } add_filter( 'bp_activity_delete_comment_pre', 'bp_blogs_sync_delete_from_activity_comment', 10, 4 ); /** * Updates the blog comment when the associated activity comment is edited. * * @since 2.0.0 * * @param BP_Activity_Activity $activity The activity object. */ function bp_blogs_sync_activity_edit_to_post_comment( BP_Activity_Activity $activity ) { // This is a new entry, so stop! // We only want edits! if ( empty( $activity->id ) || bp_disable_blogforum_comments() ) { return; } // Fetch parent activity item. $parent_activity = new BP_Activity_Activity( $activity->item_id ); // If parent activity isn't a post type having the buddypress-activity support for comments, stop now! if ( ! bp_activity_type_supports( $parent_activity->type, 'post-type-comment-tracking' ) ) { return; } $post_type = bp_activity_post_type_get_tracking_arg( $parent_activity->type, 'post_type' ); // No associated post type for this activity comment, stop. if ( ! $post_type ) { return; } // Try to see if a corresponding blog comment exists. $post_comment_id = bp_activity_get_meta( $activity->id, "bp_blogs_{$post_type}_comment_id" ); if ( empty( $post_comment_id ) ) { return; } // Handle multisite. switch_to_blog( $parent_activity->item_id ); // Get the comment status. $post_comment_status = wp_get_comment_status( $post_comment_id ); $old_comment_status = $post_comment_status; // No need to edit the activity, as it's the activity who's updating the comment. remove_action( 'transition_comment_status', 'bp_activity_transition_post_type_comment_status', 10 ); remove_action( 'bp_activity_post_type_comment', 'bp_blogs_comment_sync_activity_comment', 10 ); if ( 1 === $activity->is_spam && 'spam' !== $post_comment_status ) { wp_spam_comment( $post_comment_id ); } elseif ( ! $activity->is_spam ) { if ( 'spam' === $post_comment_status ) { wp_unspam_comment( $post_comment_id ); } elseif ( 'trash' === $post_comment_status ) { wp_untrash_comment( $post_comment_id ); } else { // Update the blog post comment. wp_update_comment( array( 'comment_ID' => $post_comment_id, 'comment_content' => $activity->content, ) ); } } // Restore actions. add_action( 'transition_comment_status', 'bp_activity_transition_post_type_comment_status', 10, 3 ); add_action( 'bp_activity_post_type_comment', 'bp_blogs_comment_sync_activity_comment', 10, 4 ); restore_current_blog(); } add_action( 'bp_activity_before_save', 'bp_blogs_sync_activity_edit_to_post_comment', 20 ); /** * When a post is trashed, remove each comment's associated activity meta. * * When a post is trashed and later untrashed, we currently don't reinstate * activity items for these comments since their activity entries are already * deleted when initially trashed. * * Since these activity entries are deleted, we need to remove the deleted * activity comment IDs from each comment's meta when a post is trashed. * * @since 2.0.0 * * @param int $post_id The post ID. * @param array $comments Array of comment statuses. The key is comment ID, the * value is the $comment->comment_approved value. */ function bp_blogs_remove_activity_meta_for_trashed_comments( $post_id = 0, $comments = array() ) { if ( ! empty( $comments ) ) { foreach ( array_keys( $comments ) as $comment_id ) { delete_comment_meta( $comment_id, 'bp_activity_comment_id' ); } } } add_action( 'trashed_post_comments', 'bp_blogs_remove_activity_meta_for_trashed_comments', 10, 2 ); /** * Filter 'new_blog_comment' bp_has_activities() loop to include new- and old-style blog activity comment items. * * In BuddyPress 2.0, the schema for storing activity items related to blog * posts changed. Instead creating new top-level 'new_blog_comment' activity * items, blog comments are recorded in the activity stream as comments on the * 'new_blog_post' activity items corresponding to the parent post. This filter * ensures that the 'new_blog_comment' filter in bp_has_activities() (which * powers the 'Comments' filter in the activity directory dropdown) includes * both old-style and new-style activity comments. * * @since 2.1.0 * @since 2.5.0 Used for any synced Post type comments, in wp-admin or front-end contexts. * * @param array $args Arguments passed from bp_parse_args() in bp_has_activities(). * @return array $args */ function bp_blogs_new_blog_comment_query_backpat( $args ) { global $wpdb; $bp = buddypress(); // If activity comments are disabled for blog posts, stop now! if ( bp_disable_blogforum_comments() ) { return $args; } // Get the associated post type. $post_type = bp_activity_post_type_get_tracking_arg( $args['action'], 'post_type' ); // Bail if this is not an activity associated with a post type. if ( empty( $post_type ) ) { return $args; } // Bail if this is an activity about posts and not comments. if ( bp_activity_post_type_get_tracking_arg( $args['action'], 'comment_action_id' ) ) { return $args; } // Comment synced ? $activity_ids = $wpdb->get_col( $wpdb->prepare( "SELECT activity_id FROM {$bp->activity->table_name_meta} WHERE meta_key = %s", "bp_blogs_{$post_type}_comment_id" ) ); if ( empty( $activity_ids ) ) { return $args; } // Init the filter query. $filter_query = array(); if ( ! isset( $args['scope'] ) || 'null' === $args['scope'] ) { $args['scope'] = ''; } elseif ( 'just-me' === $args['scope'] ) { $filter_query = array( 'relation' => 'AND', array( 'column' => 'user_id', 'value' => bp_displayed_user_id(), ), ); $args['scope'] = ''; } $filter_query[] = array( 'relation' => 'OR', array( 'column' => 'type', 'value' => $args['action'], ), array( 'column' => 'id', 'value' => $activity_ids, 'compare' => 'IN' ), ); $args['filter_query'] = $filter_query; // Make sure to have comment in stream mode && avoid duplicate content. $args['display_comments'] = 'stream'; // Finally reset the action. $args['action'] = ''; $args['type'] = ''; // Return the original arguments. return $args; } add_filter( 'bp_after_has_activities_parse_args', 'bp_blogs_new_blog_comment_query_backpat' ); add_filter( 'bp_activity_list_table_filter_activity_type_items', 'bp_blogs_new_blog_comment_query_backpat' ); /** * Utility function to set up some variables for use in the activity loop. * * Grabs the blog's comment depth and the post's open comment status options * for later use in the activity and activity comment loops. * * This is to prevent having to requery these items later on. * * @since 2.0.0 * * @see bp_blogs_disable_activity_commenting() * @see bp_blogs_setup_comment_loop_globals_on_ajax() * * @param object $activity The BP_Activity_Activity object. */ function bp_blogs_setup_activity_loop_globals( $activity ) { if ( ! is_object( $activity ) ) { return; } // The activity type does not support comments or replies ? stop now! if ( ! bp_activity_type_supports( $activity->type, 'post-type-comment-reply' ) ) { return; } if ( empty( $activity->id ) ) { return; } // If we've already done this before, stop now! if ( isset( buddypress()->blogs->allow_comments[ $activity->id ] ) ) { return; } $allow_comments = bp_blogs_comments_open( $activity ); $thread_depth = bp_blogs_get_blogmeta( $activity->item_id, 'thread_comments_depth' ); $moderation = bp_blogs_get_blogmeta( $activity->item_id, 'comment_moderation' ); // Initialize a local object so we won't have to query this again in the // comment loop. if ( ! isset( buddypress()->blogs->allow_comments ) ) { buddypress()->blogs->allow_comments = array(); } if ( ! isset( buddypress()->blogs->thread_depth ) ) { buddypress()->blogs->thread_depth = array(); } if ( ! isset( buddypress()->blogs->comment_moderation ) ) { buddypress()->blogs->comment_moderation = array(); } /* * Cache comment settings in the buddypress() singleton for later reference. * * See bp_blogs_disable_activity_commenting() / bp_blogs_can_comment_reply(). * * thread_depth is keyed by activity ID instead of blog ID because when we're * in an actvity comment loop, we don't have access to the blog ID... * * Should probably object cache these values instead... */ buddypress()->blogs->allow_comments[ $activity->id ] = $allow_comments; buddypress()->blogs->thread_depth[ $activity->id ] = $thread_depth; buddypress()->blogs->comment_moderation[ $activity->id ] = $moderation; } /** * Set up some globals used in the activity comment loop when AJAX is used. * * @since 2.0.0 * * @see bp_blogs_setup_activity_loop_globals() */ function bp_blogs_setup_comment_loop_globals_on_ajax() { // Not AJAX? stop now! if ( ! defined( 'DOING_AJAX' ) ) { return; } if ( false === (bool) constant( 'DOING_AJAX' ) ) { return; } // Get the parent activity item. $comment = bp_activity_current_comment(); $parent_activity = new BP_Activity_Activity( $comment->item_id ); // Setup the globals. bp_blogs_setup_activity_loop_globals( $parent_activity ); } add_action( 'bp_before_activity_comment', 'bp_blogs_setup_comment_loop_globals_on_ajax' ); /** * Disable activity commenting for blog posts based on certain criteria. * * If activity commenting is enabled for blog posts, we still need to disable * commenting if: * - comments are disabled for the WP blog post from the admin dashboard * - the WP blog post is supposed to be automatically closed from comments * based on a certain age * - the activity entry is a 'new_blog_comment' type * * @since 2.0.0 * * @param bool $retval Is activity commenting enabled for this activity entry. * @return bool */ function bp_blogs_disable_activity_commenting( $retval ) { global $activities_template; // If activity commenting is disabled, return current value. if ( bp_disable_blogforum_comments() || ! isset( $activities_template->in_the_loop ) ) { return $retval; } $type = bp_get_activity_type(); // It's a post type supporting comment tracking. if ( bp_activity_type_supports( $type, 'post-type-comment-tracking' ) ) { // The activity type is supporting comments or replies. if ( bp_activity_type_supports( $type, 'post-type-comment-reply' ) ) { // Setup some globals we'll need to reference later. bp_blogs_setup_activity_loop_globals( $activities_template->activity ); // If comments are closed for the WP blog post, we should disable // activity comments for this activity entry. if ( ! isset( buddypress()->blogs->allow_comments[ bp_get_activity_id() ] ) || ! buddypress()->blogs->allow_comments[ bp_get_activity_id() ] ) { $retval = false; } // If comments need moderation, disable activity commenting. if ( isset( buddypress()->blogs->comment_moderation[ bp_get_activity_id() ] ) && buddypress()->blogs->comment_moderation[ bp_get_activity_id() ] ) { $retval = false; } // The activity type does not support comments or replies. } else { $retval = false; } } return $retval; } add_filter( 'bp_activity_can_comment', 'bp_blogs_disable_activity_commenting' ); /** * Limit the display of post type synced comments. * * @since 2.5.0 * * When viewing the synced comments in stream mode, this prevents comments to * be displayed twice, and avoids a Javascript error as the form to add replies * is not available. * * @param int $retval The comment count for the activity. * @return int The comment count, or 0 to hide activity comment replies. */ function bp_blogs_post_type_comments_avoid_duplicates( $retval ) { /** * Only limit the display when Post type comments are synced with * activity comments. */ if ( bp_disable_blogforum_comments() ) { return $retval; } if ( 'activity_comment' !== bp_get_activity_type() ) { return $retval; } // Check the parent activity. $parent_activity = new BP_Activity_Activity( bp_get_activity_item_id() ); if ( isset( $parent_activity->type ) && bp_activity_post_type_get_tracking_arg( $parent_activity->type, 'post_type' ) ) { $retval = 0; } return $retval; } add_filter( 'bp_activity_get_comment_count', 'bp_blogs_post_type_comments_avoid_duplicates' ); /** * Check if an activity comment associated with a blog post can be replied to. * * By default, disables replying to activity comments if the corresponding WP * blog post no longer accepts comments. * * This check uses a locally-cached value set in {@link bp_blogs_disable_activity_commenting()} * via {@link bp_blogs_setup_activity_loop_globals()}. * * @since 2.0.0 * * @param bool $retval Are replies allowed for this activity reply. * @param object|array $comment The activity comment object. * * @return bool */ function bp_blogs_can_comment_reply( $retval, $comment ) { if ( is_array( $comment ) ) { $comment = (object) $comment; } // Check comment depth and disable if depth is too large. if ( isset( buddypress()->blogs->thread_depth[$comment->item_id] ) ){ if ( bp_activity_get_comment_depth( $comment ) >= buddypress()->blogs->thread_depth[$comment->item_id] ) { $retval = false; } } // Check if we should disable activity replies based on the parent activity. if ( isset( buddypress()->blogs->allow_comments[$comment->item_id] ) ){ // The blog post has closed off commenting, so we should disable all activity // comments under the parent 'new_blog_post' activity entry. if ( ! buddypress()->blogs->allow_comments[ $comment->item_id ] ) { $retval = false; } } // If comments need moderation, disable activity commenting. if ( isset( buddypress()->blogs->comment_moderation[ $comment->item_id ] ) && buddypress()->blogs->comment_moderation[ $comment->item_id ] ) { $retval = false; } return $retval; } add_filter( 'bp_activity_can_comment_reply', 'bp_blogs_can_comment_reply', 10, 2 ); /** * Changes activity comment permalinks to use the blog comment permalink * instead of the activity permalink. * * This is only done if activity commenting is allowed and whether the parent * activity item is a 'new_blog_post' entry. * * @since 2.0.0 * * @param string $retval The activity comment permalink. * @return string */ function bp_blogs_activity_comment_permalink( $retval = '' ) { global $activities_template; // Get the current comment ID. $item_id = isset( $activities_template->activity->current_comment->item_id ) ? $activities_template->activity->current_comment->item_id : false; // Maybe adjust the link if item ID exists. if ( ( false !== $item_id ) && isset( buddypress()->blogs->allow_comments[ $item_id ] ) ) { $retval = $activities_template->activity->current_comment->primary_link; } return $retval; } add_filter( 'bp_get_activity_comment_permalink', 'bp_blogs_activity_comment_permalink' ); /** * Changes single activity comment entries to use the blog comment permalink. * * This is only done if the activity comment is associated with a blog comment. * * @since 2.0.1 * * @param string $retval The activity permalink. * @param BP_Activity_Activity $activity Activity object. * @return string */ function bp_blogs_activity_comment_single_permalink( $retval, $activity ) { if ( 'activity_comment' !== $activity->type ) { return $retval; } if ( bp_disable_blogforum_comments() ) { return $retval; } $parent_activity = new BP_Activity_Activity( $activity->item_id ); if ( isset( $parent_activity->type ) && bp_activity_post_type_get_tracking_arg( $parent_activity->type, 'post_type' ) ) { $retval = $activity->primary_link; } return $retval; } add_filter( 'bp_activity_get_permalink', 'bp_blogs_activity_comment_single_permalink', 10, 2 ); /** * Formats single activity comment entries to use the blog comment action. * * This is only done if the activity comment is associated with a blog comment. * * @since 2.0.1 * * @param string $retval The activity action. * @param BP_Activity_Activity $activity Activity object. * @return string */ function bp_blogs_activity_comment_single_action( $retval, $activity ) { if ( 'activity_comment' !== $activity->type ) { return $retval; } if ( bp_disable_blogforum_comments() ) { return $retval; } $parent_activity = new BP_Activity_Activity( $activity->item_id ); if ( ! isset( $parent_activity->type ) ) { return $retval; } $post_type = bp_activity_post_type_get_tracking_arg( $parent_activity->type, 'post_type' ); if ( ! $post_type ) { return $retval; } $blog_comment_id = bp_activity_get_meta( $activity->id, "bp_blogs_{$post_type}_comment_id" ); if ( ! empty( $blog_comment_id ) ) { $bp = buddypress(); // Check if a comment action id is set for the parent activity. $comment_action_id = bp_activity_post_type_get_tracking_arg( $parent_activity->type, 'comment_action_id' ); // Use the action string callback for the activity type. if ( ! empty( $comment_action_id ) ) { // Fake a 'new_{post_type}_comment' by cloning the activity object. $object = clone $activity; // Set the type of the activity to be a comment about a post type. $object->type = $comment_action_id; // Use the blog ID as the item_id. $object->item_id = $parent_activity->item_id; // Use comment ID as the secondary_item_id. $object->secondary_item_id = $blog_comment_id; // Get the format callback for this activity comment. $format_callback = bp_activity_post_type_get_tracking_arg( $comment_action_id, 'format_callback' ); // Now format the activity action using the 'new_{post_type}_comment' action callback. if ( is_callable( $format_callback ) ) { $retval = call_user_func_array( $format_callback, array( '', $object ) ); } } } return $retval; } add_filter( 'bp_get_activity_action_pre_meta', 'bp_blogs_activity_comment_single_action', 10, 2 );