<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[BuddyPress][8190] trunk: Blogs: Support post comment synchronization in the activity stream.</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://buddypress.trac.wordpress.org/changeset/8190">8190</a></dd>
<dt>Author</dt> <dd>r-a-y</dd>
<dt>Date</dt> <dd>2014-03-28 22:52:14 +0000 (Fri, 28 Mar 2014)</dd>
</dl>

<h3>Log Message</h3>
<pre>Blogs: Support post comment synchronization in the activity stream.

Previously, if a reply to a blog post was made in the activity stream,
the reply would only exist in the activity stream and not as a comment
for the blog post.

This commit:
- Adds a blog comment whenever an activity reply is made under a blog post activity entry
- Adds an activity comment to the blog post's activity entry whenever a blog comment is made
- Checks to see if the blog post is open for comments (whether manually closed or due to age) (see <a href="http://buddypress.trac.wordpress.org/changeset/8189">r8189</a>) to determine whether the activity item can be replied to
- Handles content edit synchronization to either the post comment or the activity comment
- Handles deletion synchronization to a degree

See <a href="http://buddypress.trac.wordpress.org/ticket/5130">#5130</a></pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkbpactivitybpactivitytemplatephp">trunk/bp-activity/bp-activity-template.php</a></li>
<li><a href="#trunkbpblogsbpblogsactivityphp">trunk/bp-blogs/bp-blogs-activity.php</a></li>
<li><a href="#trunkbpblogsbpblogsfunctionsphp">trunk/bp-blogs/bp-blogs-functions.php</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkbpactivitybpactivitytemplatephp"></a>
<div class="modfile"><h4>Modified: trunk/bp-activity/bp-activity-template.php (8189 => 8190)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/bp-activity/bp-activity-template.php       2014-03-28 22:33:16 UTC (rev 8189)
+++ trunk/bp-activity/bp-activity-template.php  2014-03-28 22:52:14 UTC (rev 8190)
</span><span class="lines">@@ -2772,7 +2772,33 @@
</span><span class="cx">          return apply_filters( 'bp_get_send_public_message_link', wp_nonce_url( bp_get_activity_directory_permalink() . '?r=' . bp_get_displayed_user_mentionname() ) );
</span><span class="cx">  }
</span><span class="cx"> 
</span><ins>+/**
+ * Recurse through all activity comments and return the activity comment IDs.
+ *
+ * @since BuddyPress (2.0.0)
+ *
+ * @param array $activity Array of activities generated from {@link bp_activity_get()}.
+ * @param array $activity_ids Used for recursion purposes in this function.
+ * @return array
+ */
+function bp_activity_recurse_comments_activity_ids( $activity = array(), $activity_ids = array() ) {
+       if ( is_array( $activity ) && ! empty( $activity['activities'] ) ) {
+               $activity = $activity['activities'][0]; 
+       }
</ins><span class="cx"> 
</span><ins>+       if ( ! empty( $activity->children ) ) {
+               foreach ($activity->children as $child ) {
+                       $activity_ids[] = $child->id;
+
+                       if( ! empty( $child->children ) ) {
+                               $activity_ids = bp_activity_recurse_comments_activity_ids( $child, $activity_ids );
+                       }
+               }
+       }
+
+       return $activity_ids;
+}
+
</ins><span class="cx"> /**
</span><span class="cx">  * Output the mentioned user display name.
</span><span class="cx">  *
</span></span></pre></div>
<a id="trunkbpblogsbpblogsactivityphp"></a>
<div class="modfile"><h4>Modified: trunk/bp-blogs/bp-blogs-activity.php (8189 => 8190)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/bp-blogs/bp-blogs-activity.php     2014-03-28 22:33:16 UTC (rev 8189)
+++ trunk/bp-blogs/bp-blogs-activity.php        2014-03-28 22:52:14 UTC (rev 8190)
</span><span class="lines">@@ -431,4 +431,422 @@
</span><span class="cx">  }
</span><span class="cx"> 
</span><span class="cx">  return $open;
</span><del>-}
</del><span class="cx">\ No newline at end of file
</span><ins>+}
+
+/** 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_blogs_record_comment()}.
+ *
+ * @since BuddyPress (2.0.0)
+ *
+ * @param int $comment_id The activity ID for the posted activity comment.
+ * @param array $params Parameters for the activity comment.
+ * @param object 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 blog post, stop now!
+       if ( $parent_activity->type != 'new_blog_post' ) {
+               return;
+       }
+
+       // if activity comments are disabled for blog posts, stop now!
+       if ( bp_disable_blogforum_comments() ) {
+               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'] );
+       }
+
+       // see if a parent WP comment ID exists
+       if ( ! empty( $params['parent_id'] ) ) {
+               $comment_parent = bp_activity_get_meta( $params['parent_id'], 'bp_blogs_post_comment_id' );
+       } else {
+               $comment_parent = 0;
+       }
+
+       // 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'],
+
+               // commenting these out for now
+               //'comment_author_IP'    => '127.0.0.1',
+               //'comment_agent'        => '',
+
+               'comment_approved'     => 1
+       );
+
+       // prevent separate activity entry being made
+       remove_action( 'comment_post', 'bp_blogs_record_comment', 10, 2 );
+
+       // 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
+       bp_activity_update_meta( $comment_id, 'bp_blogs_post_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 frontend until the next page refresh.
+       $resave_activity = new BP_Activity_Activity( $comment_id );
+       $resave_activity->primary_link = get_comment_link( $post_comment_id );
+       $resave_activity->save();
+
+       // multisite again!
+       restore_current_blog();
+
+       // add the comment hook back
+       add_action( 'comment_post', 'bp_blogs_record_comment', 10, 2 );
+
+       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 BuddyPress (2.0.0)
+ *
+ * @param bool $retval
+ * @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.
+ */
+function bp_blogs_sync_delete_from_activity_comment( $retval, $parent_activity_id, $activity_id ) {
+       // check if parent activity is a blog post
+       $parent_activity = new BP_Activity_Activity( $parent_activity_id );
+       if ( 'new_blog_post' != $parent_activity->type ) {
+               return $retval;
+       }
+
+       // fetch the activity comments for the activity item
+       $activity = bp_activity_get( array(
+               'in'               => $activity_id,
+               'display_comments' => 'stream',
+       ) );
+
+       // 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 );
+
+       // 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, 3 );
+
+/**
+ * Updates the blog comment when the associated activity comment is edited.
+ *
+ * @since BuddyPress (2.0.0)
+ *
+ * @param BP_Activity_Activity $activity The activity object.
+ */
+function bp_blogs_sync_activity_edit_to_post_comment( BP_Activity_Activity $activity ) {
+       // not an activity comment? stop now!
+       if ( 'activity_comment' !== $activity->type ) {
+               return;
+       }
+
+       // this is a new entry, so stop!
+       // we only want edits!
+       if ( empty( $activity->id ) ) {
+               return;
+       }
+
+       // prevent recursion
+       remove_action( 'bp_activity_before_save', 'bp_blogs_sync_activity_edit_to_post_comment', 20 );
+
+       // Try to see if a corresponding blog comment exists
+       $post_comment_id = bp_activity_get_meta( $activity->id, 'bp_blogs_post_comment_id' );
+
+       if ( empty( $post_comment_id ) ) {
+               return;
+       }
+
+       // fetch parent activity item
+       $parent_activity = new BP_Activity_Activity( $activity->item_id );
+
+       // sanity check
+       if ( 'new_blog_post' !== $parent_activity->type ) {
+               return;
+       }
+
+       // handle multisite
+       switch_to_blog( $parent_activity->item_id );
+
+       // update the blog post comment
+       wp_update_comment( array(
+               'comment_ID'      => $post_comment_id,
+               'comment_content' => $activity->content
+       ) );
+
+       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 BuddyPress (2.0.0)
+ *
+ * @param int $post_id The post ID
+ * @param array $statuses 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, $statuses ) {
+       foreach ( $statuses as $comment_id => $comment_approved ) {
+               delete_comment_meta( $comment_id, 'bp_activity_comment_id' );
+       }
+}
+add_action( 'trashed_post_comments', 'bp_blogs_remove_activity_meta_for_trashed_comments', 10, 2 );
+
+/**
+ * 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 BuddyPress (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;
+       }
+
+       // parent not a blog post? stop now!
+       if ( 'new_blog_post' !== $activity->type ) {
+               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' );
+
+       // initialize a local object so we won't have to query this again in the
+       // comment loop
+       if ( empty( buddypress()->blogs->allow_comments ) ) {
+               buddypress()->blogs->allow_comments = array();
+       }
+       if ( empty( buddypress()->blogs->thread_depth ) ) {
+               buddypress()->blogs->thread_depth   = array();
+       }
+
+       // cache comment settings in the buddypress() singleton to reference later in
+       // the activity comment loop
+       // @see bp_blogs_disable_activity_replies()
+       //
+       // thread_depth is keyed by activity ID instead of blog ID because when we're
+       // in a 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;
+}
+
+/**
+ * Set up some globals used in the activity comment loop when AJAX is used.
+ *
+ * @since BuddyPress (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 BuddyPress (2.0.0)
+ *
+ * @param bool $retval Is activity commenting enabled for this activity entry?
+ * @return bool
+ */
+function bp_blogs_disable_activity_commenting( $retval ) {
+       // if activity commenting is disabled, return current value
+       if ( bp_disable_blogforum_comments() ) {
+               return $retval;
+       }
+
+       // activity commenting is enabled for blog posts
+       switch ( bp_get_activity_action_name() ) {
+
+               // we still have to disable activity commenting for 'new_blog_comment' items
+               // commenting should only be done on the parent 'new_blog_post' item
+               case 'new_blog_comment' :
+                       $retval = false;
+
+                       break;
+
+               // check if commenting is disabled for the WP blog post
+               // we should extrapolate this and automate this for plugins... or not
+               case 'new_blog_post' :
+                       global $activities_template;
+
+                       // 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 ( empty( buddypress()->blogs->allow_comments[bp_get_activity_id()] ) ) {
+                               $retval = false;
+                       }
+
+                       break;
+       }
+
+       return $retval;
+}
+add_filter( 'bp_activity_can_comment', 'bp_blogs_disable_activity_commenting' );
+
+/**
+ * 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 BuddyPress (2.0.0)
+ *
+ * @param bool $retval Are replies allowed for this activity reply?
+ * @param object $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 ( $comment->mptt_left > 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 ( empty( buddypress()->blogs->allow_comments[$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 BuddyPress (2.0.0)
+ *
+ * @param string $retval The activity comment permalink
+ * @return string
+ */
+function bp_blogs_activity_comment_permalink( $retval ) {
+       global $activities_template;
+
+       if ( isset( buddypress()->blogs->allow_comments[$activities_template->activity->current_comment->item_id] ) ){
+               $retval = $activities_template->activity->current_comment->primary_link;
+       }
+
+       return $retval;
+}
+add_filter( 'bp_get_activity_comment_permalink', 'bp_blogs_activity_comment_permalink' );
</ins></span></pre></div>
<a id="trunkbpblogsbpblogsfunctionsphp"></a>
<div class="modfile"><h4>Modified: trunk/bp-blogs/bp-blogs-functions.php (8189 => 8190)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/bp-blogs/bp-blogs-functions.php    2014-03-28 22:33:16 UTC (rev 8189)
+++ trunk/bp-blogs/bp-blogs-functions.php       2014-03-28 22:52:14 UTC (rev 8190)
</span><span class="lines">@@ -538,25 +538,97 @@
</span><span class="cx">          $post_permalink = get_permalink( $recorded_comment->comment_post_ID );
</span><span class="cx">          $comment_link   = get_comment_link( $recorded_comment->comment_ID );
</span><span class="cx"> 
</span><del>-               // Prepare to record in activity streams
-               if ( is_multisite() )
-                       $activity_action = sprintf( __( '%1$s commented on the post, %2$s, on the site %3$s', 'buddypress' ), bp_core_get_userlink( $user_id ), '<a href="' . $post_permalink . '">' . apply_filters( 'the_title', $recorded_comment->post->post_title ) . '</a>', '<a href="' . get_blog_option( $blog_id, 'home' ) . '">' . get_blog_option( $blog_id, 'blogname' ) . '</a>' );
-               else
-                       $activity_action = sprintf( __( '%1$s commented on the post, %2$s', 'buddypress' ), bp_core_get_userlink( $user_id ), '<a href="' . $post_permalink . '">' . apply_filters( 'the_title', $recorded_comment->post->post_title ) . '</a>' );
</del><ins>+                // Setup activity args
+               $args = array();
</ins><span class="cx"> 
</span><del>-               $activity_content       = $recorded_comment->comment_content;
</del><ins>+                $args['user_id']       = $user_id;
+               $args['content']       = apply_filters_ref_array( 'bp_blogs_activity_new_comment_content', array( $recorded_comment->comment_content, &$recorded_comment, $comment_link ) );
+               $args['primary_link']  = apply_filters_ref_array( 'bp_blogs_activity_new_comment_primary_link', array( $comment_link,     &$recorded_comment ) );
+               $args['recorded_time'] = $recorded_comment->comment_date_gmt;
</ins><span class="cx"> 
</span><del>-               // Record in activity streams
-               bp_blogs_record_activity( array(
-                       'user_id'           => $user_id,
-                       'content'           => apply_filters_ref_array( 'bp_blogs_activity_new_comment_content',      array( $activity_content, &$recorded_comment, $comment_link ) ),
-                       'primary_link'      => apply_filters_ref_array( 'bp_blogs_activity_new_comment_primary_link', array( $comment_link,     &$recorded_comment                ) ),
-                       'type'              => 'new_blog_comment',
-                       'item_id'           => $blog_id,
-                       'secondary_item_id' => $comment_id,
-                       'recorded_time'     => $recorded_comment->comment_date_gmt
-               ) );
</del><ins>+                // Setup some different activity args depending if activity commenting is
+               // enabled or not
</ins><span class="cx"> 
</span><ins>+               // if cannot comment, record separate activity entry
+               // this is the old way of doing things
+               if ( bp_disable_blogforum_comments() ) {
+                       $args['type']              = 'new_blog_comment';
+                       $args['item_id']           = $blog_id;
+                       $args['secondary_item_id'] = $comment_id;
+
+                       // record the activity entry
+                       bp_blogs_record_activity( $args );
+
+               // record comment as BP activity comment under the parent 'new_blog_post'
+               // activity item
+               } else {
+                       // this is a comment edit
+                       // check to see if corresponding activity entry already exists
+                       if ( ! empty( $_REQUEST['action'] ) ) {
+                               $existing_activity_id = get_comment_meta( $comment_id, 'bp_activity_comment_id', true );
+
+                               if ( ! empty( $existing_activity_id ) ) {
+                                       $args['id'] = $existing_activity_id;
+                               }
+                       }
+
+                       // find the parent 'new_blog_post' activity entry
+                       $parent_activity_id = bp_activity_get_activity_id( array(
+                               'user_id'           => $user_id,
+                               'component'         => 'blogs',
+                               'type'              => 'new_blog_post',
+                               'item_id'           => $blog_id,
+                               'secondary_item_id' => $recorded_comment->comment_post_ID
+                       ) );
+
+                       // we found the parent activity entry
+                       // so let's go ahead and reconfigure some activity args
+                       if ( ! empty( $parent_activity_id ) ) {
+                               // set the 'item_id' with the parent activity entry ID
+                               $args['item_id'] = $parent_activity_id;
+
+                               // now see if the WP parent comment has a BP activity ID
+                               $comment_parent = 0;
+                               if ( ! empty( $recorded_comment->comment_parent ) ) {
+                                       $comment_parent = get_comment_meta( $recorded_comment->comment_parent, 'bp_activity_comment_id', true );
+                               }
+
+                               // WP parent comment does not have a BP activity ID
+                               // so set to 'new_blog_post' activity ID
+                               if ( empty( $comment_parent ) ) {
+                                       $comment_parent = $parent_activity_id;
+                               }
+
+                               $args['secondary_item_id'] = $comment_parent;
+                               $args['component']         = 'activity';
+                               $args['type']              = 'activity_comment';
+
+                       // could not find corresponding parent activity entry
+                       // so wipe out $args array
+                       } else {
+                               $args = array();
+                       }
+
+                       // Record in activity streams
+                       if ( ! empty( $args ) ) {
+                               // @todo should we use bp_activity_new_comment()? that function will also send
+                               // an email to people in the activity comment thread
+                               //
+                               // what if a site already has some comment email notification plugin setup?
+                               // this is why I decided to go with bp_activity_add() to avoid any conflict
+                               // with existing comment email notification plugins
+                               $comment_activity_id = bp_activity_add( $args );
+
+                               if ( empty( $args['id'] ) ) {
+                                       // add meta to activity comment
+                                       bp_activity_update_meta( $comment_activity_id, 'bp_blogs_post_comment_id', $comment_id );
+
+                                       // add meta to comment
+                                       add_comment_meta( $comment_id, 'bp_activity_comment_id', $comment_activity_id );
+                               }
+                       }
+               }
+
</ins><span class="cx">           // Update the blogs last active date
</span><span class="cx">          bp_blogs_update_blogmeta( $blog_id, 'last_activity', bp_core_current_time() );
</span><span class="cx">  }
</span><span class="lines">@@ -729,14 +801,99 @@
</span><span class="cx"> function bp_blogs_remove_comment( $comment_id ) {
</span><span class="cx">  global $wpdb;
</span><span class="cx"> 
</span><del>-       // Delete activity stream item
-       bp_blogs_delete_activity( array( 'item_id' => $wpdb->blogid, 'secondary_item_id' => $comment_id, 'type' => 'new_blog_comment' ) );
</del><ins>+        // activity comments are disabled for blog posts
+       // which means that individual activity items exist for blog comments
+       if ( bp_disable_blogforum_comments() ) {
+               // Delete the individual activity stream item
+               bp_blogs_delete_activity( array(
+                       'item_id'           => $wpdb->blogid,
+                       'secondary_item_id' => $comment_id,
+                       'type'              => 'new_blog_comment'
+               ) );
</ins><span class="cx"> 
</span><ins>+       // activity comments are enabled for blog posts
+       // remove the associated activity item
+       } else {
+               // get associated activity ID from comment meta
+               $activity_id = get_comment_meta( $comment_id, 'bp_activity_comment_id', true );
+
+               // delete the associated activity comment
+               //
+               // also removes child post comments and associated activity comments
+               if ( ! empty( $activity_id ) && bp_is_active( 'activity' ) ) {
+                       // fetch the activity comments for the activity item
+                       $activity = bp_activity_get( array(
+                               'in'               => $activity_id,
+                               'display_comments' => 'stream',
+                       ) );
+
+                       // get all activity comment IDs for the pending deleted item
+                       if ( ! empty( $activity['activities'] ) ) {
+                               $activity_ids   = bp_activity_recurse_comments_activity_ids( $activity );
+                               $activity_ids[] = $activity_id;
+
+                               // delete activity items
+                               foreach ( $activity_ids as $activity_id ) {
+                                       bp_activity_delete( array(
+                                               'id' => $activity_id
+                                       ) );
+                               }
+
+                               // remove associated blog comments
+                               bp_blogs_remove_associated_blog_comments( $activity_ids );
+
+                               // rebuild activity comment tree
+                               BP_Activity_Activity::rebuild_activity_comment_tree( $activity['activities'][0]->item_id );
+                       }
+               }
+       }
+
</ins><span class="cx">   do_action( 'bp_blogs_remove_comment', $wpdb->blogid, $comment_id, bp_loggedin_user_id() );
</span><span class="cx"> }
</span><span class="cx"> add_action( 'delete_comment', 'bp_blogs_remove_comment' );
</span><span class="cx"> 
</span><span class="cx"> /**
</span><ins>+ * Removes blog comments that are associated with activity comments.
+ *
+ * @since BuddyPress (2.0.0)
+ *
+ * @see bp_blogs_remove_comment()
+ * @see bp_blogs_sync_delete_from_activity_comment()
+ *
+ * @param array $activity_ids The activity IDs to check association with blog
+ *              comments.
+ * @param bool $force_delete Whether to force delete the comments. If false,
+ *             comments are trashed instead.
+ */
+function bp_blogs_remove_associated_blog_comments( $activity_ids = array(), $force_delete = true ) {
+       // query args
+       $query_args = array(
+               'meta_query' => array(
+                       array(
+                               'key'     => 'bp_activity_comment_id',
+                               'value'   => implode( ',', (array) $activity_ids ),
+                               'compare' => 'IN',
+                       )
+               )
+       );
+
+       // get comment
+       $comment_query = new WP_Comment_Query;
+       $comments = $comment_query->query( $query_args );
+
+       // found the corresponding comments
+       // let's delete them!
+       foreach ( $comments as $comment ) {
+               wp_delete_comment( $comment->comment_ID, $force_delete );
+
+               // if we're trashing the comment, remove the meta key as well
+               if ( empty( $force_delete ) ) {
+                       delete_comment_meta( $comment->comment_ID, 'bp_activity_comment_id' );
+               }
+       }
+}
+
+/**
</ins><span class="cx">  * When a blog comment status transition occurs, update the relevant activity's status.
</span><span class="cx">  *
</span><span class="cx">  * @since BuddyPress (1.6.0)
</span><span class="lines">@@ -764,24 +921,29 @@
</span><span class="cx">   */
</span><span class="cx"> 
</span><span class="cx">  // This clause was moved in from bp_blogs_remove_comment() in BuddyPress 1.6. It handles delete/hold.
</span><del>-       if ( in_array( $new_status, array( 'delete', 'hold' ) ) )
</del><ins>+        if ( in_array( $new_status, array( 'delete', 'hold' ) ) ) {
</ins><span class="cx">           return bp_blogs_remove_comment( $comment->comment_ID );
</span><span class="cx"> 
</span><span class="cx">  // These clauses handle trash, spam, and un-spams.
</span><del>-       elseif ( in_array( $new_status, array( 'trash', 'spam' ) ) )
</del><ins>+        } elseif ( in_array( $new_status, array( 'trash', 'spam' ) ) ) {
</ins><span class="cx">           $action = 'spam_activity';
</span><del>-       elseif ( 'approved' == $new_status )
</del><ins>+        } elseif ( 'approved' == $new_status ) {
</ins><span class="cx">           $action = 'ham_activity';
</span><ins>+       }
</ins><span class="cx"> 
</span><span class="cx">  // Get the activity
</span><del>-       $activity_id = bp_activity_get_activity_id( array( 'component' => $bp->blogs->id, 'item_id' => get_current_blog_id(), 'secondary_item_id' => $comment->comment_ID, 'type' => 'new_blog_comment', ) );
</del><ins>+        if ( bp_disable_blogforum_comments() ) {
+               $activity_id = bp_activity_get_activity_id( array( 'component' => $bp->blogs->id, 'item_id' => get_current_blog_id(), 'secondary_item_id' => $comment->comment_ID, 'type' => 'new_blog_comment', ) );
+       } else {
+               $activity_id = get_comment_meta( $comment->comment_ID, 'bp_activity_comment_id', true );
+       }
</ins><span class="cx"> 
</span><span class="cx">  // Check activity item exists
</span><del>-       if ( ! $activity_id ) {
-
</del><ins>+        if ( empty( $activity_id ) ) {
</ins><span class="cx">           // If no activity exists, but the comment has been approved, record it into the activity table.
</span><del>-               if ( 'approved' == $new_status )
</del><ins>+                if ( 'approved' == $new_status ) {
</ins><span class="cx">                   return bp_blogs_record_comment( $comment->comment_ID, true );
</span><ins>+               }
</ins><span class="cx"> 
</span><span class="cx">          return;
</span><span class="cx">  }
</span></span></pre>
</div>
</div>

</body>
</html>