diff --git src/bp-activity/bp-activity-actions.php src/bp-activity/bp-activity-actions.php
index 576628f..e355e48 100644
--- src/bp-activity/bp-activity-actions.php
+++ src/bp-activity/bp-activity-actions.php
@@ -13,6 +13,50 @@
 if ( !defined( 'ABSPATH' ) ) exit;
 
 /**
+ * Allow post types to register to the tracking feature
+ *
+ * @since BuddyPress (2.2.0)
+ *
+ * @uses buddypress()
+ * @uses get_post_types()
+ * @uses post_type_supports()
+ */
+function bp_activity_tracking_register_post_types() {
+	$bp = buddypress();
+	$bp->activity->track = array();
+
+	// Fetch public post types
+	$post_types = get_post_types( array( 'public' => true ), 'objects' );
+
+	foreach ( $post_types as $key => $object ) {
+		// Check if they support BuddyPress activity tracking feature
+		if ( ! post_type_supports( $key, 'buddypress-activity' ) ) {
+			continue;
+		}
+
+		// Register the post type in tracking global
+		$bp->activity->track[ 'new_' . $key ] = apply_filters( 'bp_activity_tracking_register_' . $key . '_post_type', array(
+			'component_id'      => $bp->activity->id,
+			'action_id'         => 'new_' . $key,
+			'admin_filter'      => sprintf( _x( 'New %s published', 'Custom Post Type generic activity post admin filter label', 'buddypress' ), strtolower( $object->labels->singular_name ) ),
+			'callback'          => 'bp_activity_format_activity_action_custom_post_type_post',
+			'filter'            => $object->labels->name,
+			'contexts'          => array( 'activity' ),
+			'singular'          => strtolower( $object->labels->singular_name ),
+			'plural'            => strtolower( $object->labels->name ),
+			'activity_commment' => ! post_type_supports( $key, 'comments' ),
+		), $object );
+
+		// Set activity action for the post type
+		call_user_func_array( 'bp_activity_set_action', array_diff_key(
+			$bp->activity->track[ 'new_' . $key ],
+			array_flip( array( 'singular', 'plural', 'activity_commment' ) )
+		) );
+	}
+}
+add_action( 'bp_fully_loaded', 'bp_activity_tracking_register_post_types' );
+
+/**
  * Allow core components and dependent plugins to register activity actions.
  *
  * @since BuddyPress (1.2.0)
@@ -766,3 +810,52 @@ function bp_ajax_get_suggestions() {
 	wp_send_json_success( $results );
 }
 add_action( 'wp_ajax_bp_get_suggestions', 'bp_ajax_get_suggestions' );
+
+/**
+ * Detect a change in post type status, and initiate an activity update if necessary.
+ *
+ * @since BuddyPress (2.2.0)
+ *
+ * @todo Support untrashing better
+ *
+ * @param string $new_status New status for the post.
+ * @param string $old_status Old status for the post.
+ * @param object $post Post data.
+ */
+function bp_activity_catch_transition_post_type_status( $new_status, $old_status, $post ) {
+	$bp = buddypress();
+
+	// This is an edit
+	if ( $new_status === $old_status ) {
+		if ( $new_status == 'publish' ) {
+			// Update the post type
+			if ( ! empty( $bp->activity->track[ 'new_' . $post->post_type ] ) ) {
+				bp_activity_post_type_update( $post );
+			}
+			return;
+		}
+	}
+
+	// Publishing a previously unpublished post
+	if ( 'publish' === $new_status ) {
+		// Untrashing the post type
+		// Nothing here yet
+		if ( 'trash' == $old_status ) {
+			do_action( 'bp_activity_post_type_untrash_' . $post->post_type, $post );
+		} else {
+			// Record the post type
+			if ( ! empty( $bp->activity->track[ 'new_' . $post->post_type ] ) && empty( $post->post_password ) ) {
+				bp_activity_post_type_publish( $post->ID, $post );
+			}
+		}
+
+	// Unpublishing a previously published post
+	} else if ( 'publish' === $old_status ) {
+		// Some form of pending status
+		// Only remove the activity entry
+		if ( ! empty( $bp->activity->track[ 'new_' . $post->post_type ] ) ) {
+			bp_activity_post_type_unpublish( $post->ID, $post );
+		}
+	}
+}
+add_action( 'transition_post_status', 'bp_activity_catch_transition_post_type_status', 10, 3 );
diff --git src/bp-activity/bp-activity-admin.php src/bp-activity/bp-activity-admin.php
index 5eb6c48..2883efa 100644
--- src/bp-activity/bp-activity-admin.php
+++ src/bp-activity/bp-activity-admin.php
@@ -1371,7 +1371,10 @@ class BP_Activity_List_Table extends WP_List_Table {
 			<select name="activity_type">
 				<option value="" <?php selected( ! $selected ); ?>><?php _e( 'View all actions', 'buddypress' ); ?></option>
 
-				<?php foreach ( $activity_actions as $component => $actions ) : ?>
+				<?php foreach ( $activity_actions as $component => $actions ) :
+					// Sort actions by their position attribute
+					$actions = bp_activity_sort_component_actions( $actions );
+				?>
 
 					<optgroup label="<?php echo ucfirst( $component ); ?>">
 
diff --git src/bp-activity/bp-activity-functions.php src/bp-activity/bp-activity-functions.php
index fcbbc07..e83a675 100644
--- src/bp-activity/bp-activity-functions.php
+++ src/bp-activity/bp-activity-functions.php
@@ -326,7 +326,7 @@ function bp_activity_get_userid_from_mentionname( $mentionname ) {
  *        'activity', 'member', 'member_groups', 'group'
  * @return bool False if any param is empty, otherwise true.
  */
-function bp_activity_set_action( $component_id, $type, $description, $format_callback = false, $label = false, $context = array() ) {
+function bp_activity_set_action( $component_id, $type, $description, $format_callback = false, $label = false, $context = array(), $position = 0 ) {
 	$bp = buddypress();
 
 	// Return false if any of the above values are not set
@@ -368,6 +368,7 @@ function bp_activity_set_action( $component_id, $type, $description, $format_cal
 		'format_callback' => $format_callback,
 		'label'           => $label,
 		'context'         => $context,
+		'position'        => $position,
 	), $component_id, $type, $description, $format_callback, $label, $context );
 
 	return true;
@@ -440,6 +441,46 @@ function bp_activity_get_types() {
 	return apply_filters( 'bp_activity_get_types', $actions );
 }
 
+/**
+ * Sort actions of a component by their position attribute
+ *
+ * @since BuddyPress (2.2.0)
+ *
+ * @param  object $actions component's activity actions
+ * @return object actions ordered by their position
+ */
+function bp_activity_sort_component_actions( $actions = null ) {
+	if ( empty( $actions ) ) {
+		return false;
+	}
+
+	$temp = array();
+
+	foreach ( $actions as $key => $params ) {
+		if ( empty( $temp[$params['position']]) ) {
+			$temp[$params['position']] = $params;
+		} else {
+			// increase numbers here to fit new items in.
+			do {
+				$params['position']++;
+			} while ( !empty( $temp[$params['position']] ) );
+
+			$temp[$params['position']] = $params;
+		}
+
+		unset( $actions->{$key} );
+	}
+
+	ksort( $temp );
+
+	// Restore keys
+	foreach ( $temp as $key_ordered  ) {
+		$actions->{$key_ordered['key']} = $key_ordered;
+	}
+
+	return $actions;
+}
+
 /** Favorites ****************************************************************/
 
 /**
@@ -1130,6 +1171,51 @@ function bp_activity_format_activity_action_activity_comment( $action, $activity
 	return apply_filters( 'bp_activity_comment_action', $action, $activity );
 }
 
+/**
+ * Format custom post type activity post actions.
+ *
+ * @since BuddyPress (2.2.0)
+ *
+ * @param string $action Static activity action.
+ * @param object $activity Activity data object.
+ * @return string
+ */
+function bp_activity_format_activity_action_custom_post_type_post( $action, $activity ) {
+	$bp = buddypress();
+
+	if ( empty( $activity->type ) || empty( $bp->activity->track[ $activity->type ] ) ) {
+		return $action;
+	}
+
+	$user_link = bp_core_get_userlink( $activity->user_id );
+	$blog_url  = get_home_url( $activity->item_id );
+
+	if ( empty( $activity->post_url ) ) {
+		$post_url  = add_query_arg( 'p', $activity->secondary_item_id, trailingslashit( $blog_url ) );
+	} else {
+		$post_url  = $activity->post_url;
+	}
+
+	$post_link = '<a href="' . $post_url . '">' . $bp->activity->track[ $activity->type ]['singular'] . '</a>';
+
+	$action  = sprintf( _x( '%1$s wrote a new %2$s', 'Activity Custom Post Type post action', 'buddypress' ), $user_link, $post_link );
+
+	if ( is_multisite() ) {
+		$blog_link = '<a href="' . $blog_url . '">' . get_blog_option( $activity->item_id, 'blogname' ) . '</a>';
+		$action    = sprintf( _x( '%1$s wrote a new %2$s, on the site %3$s', 'Activity Custom Post Type post action', 'buddypress' ), $user_link, $post_link, $blog_link );
+	}
+
+	/**
+	 * Filters the formatted custom post type activity post action string.
+	 *
+	 * @since BuddyPress (2.2.0)
+	 *
+	 * @param string               $action Activity action string value.
+	 * @param BP_Activity_Activity $activity Activity item object.
+	 */
+	return apply_filters( 'bp_activity_custom_post_type_post_action', $action, $activity );
+}
+
 /******************************************************************************
  * Business functions are where all the magic happens in BuddyPress. They will
  * handle the actual saving or manipulation of information. Usually they will
@@ -1513,6 +1599,206 @@ function bp_activity_post_update( $args = '' ) {
 }
 
 /**
+ * Publish an activity for the custom post type.
+ *
+ * @since BuddyPress (2.2.0)
+ *
+ * @param int     $post_id
+ * @param WP_Post $post
+ * @param int     $user_id
+ */
+function bp_activity_post_type_publish( $post_id = 0, $post = null, $user_id = 0 ) {
+	$bp = buddypress();
+
+	if ( ! is_a( $post, 'WP_Post' ) ) {
+		return;
+	}
+
+	if ( 'publish' != $post->post_status || ! empty( $post->post_password ) || empty( $bp->activity->track[ 'new_' . $post->post_type ] ) ) {
+		return;
+	}
+
+	if ( empty( $post_id ) ) {
+		$post_id = $post->ID;
+	}
+
+	$blog_id = get_current_blog_id();
+
+	if ( empty( $user_id ) ) {
+		$user_id = (int) $post->post_author;
+	}
+
+	$activity_post_object = (object) $bp->activity->track[ 'new_' . $post->post_type ];
+
+	$existing = bp_activity_get( array(
+		'filter' => array(
+			'action'       => $activity_post_object->action_id,
+			'primary_id'   => $blog_id,
+			'secondary_id' => $post_id,
+		)
+	) );
+
+	// Leave Components/plugins bail before the activity is posted
+	if ( ! empty( $existing['activities'] ) || false === apply_filters( "bp_activity_{$post->post_type}_pre_publish", true, $blog_id, $post_id, $user_id ) ) {
+		return;
+	}
+
+	// Record this in activity streams
+	$blog_url = get_home_url( $blog_id );
+	$post_url = add_query_arg(
+		'p',
+		$post_id,
+		trailingslashit( $blog_url )
+	);
+
+	/**
+	 * As the 'post' post type will no more use bp_blogs_record_post(), we need to be sure the filters
+	 * - 'bp_blogs_activity_new_post_content' can be applied on the content.
+	 * - 'bp_blogs_activity_new_post_primary_link' can be applied on the primary_link.
+	 */
+	$activity_args = array(
+		'user_id'           => $user_id,
+		'content'           => apply_filters( "bp_{$activity_post_object->component_id}_activity_new_post_content",      $post->post_content, $post, $post_url, $post->post_type ),
+		'primary_link'      => apply_filters( "bp_{$activity_post_object->component_id}_activity_new_post_primary_link", $post_url,           $post_id,         $post->post_type ),
+		'component'         => $activity_post_object->component_id,
+		'type'              => $activity_post_object->action_id,
+		'item_id'           => $blog_id,
+		'secondary_item_id' => $post_id,
+		'recorded_time'     => $post->post_date_gmt,
+		'hide_sitewide'     => apply_filters( 'bp_activity_post_type_publish_primary_link', false, $post ),
+	);
+
+	// Remove large images and replace them with just one image thumbnail
+	if ( ! empty( $activity_args['content'] ) ) {
+		$activity_args['content'] = bp_activity_thumbnail_content_images( $activity_args['content'], $activity_args['primary_link'], $activity_args );
+	}
+
+	if ( ! empty( $activity_args['content'] ) ) {
+		/**
+		 * As the 'post' post type will not use bp_blogs_record_activity(), we need to be sure the filter
+		 * 'bp_blogs_record_activity_content' can be applied on the action.
+		 */
+		$activity_args['content'] = apply_filters( "bp_{$activity_post_object->component_id}_record_activity_content", bp_create_excerpt( $activity_args['content'] ), $activity_args['content'], $activity_args, $post->post_type );
+	}
+
+	// Setup the action by using the format functions
+	$action_args = array_merge( $activity_args, array(
+		'post_title' => $post->post_title,
+		'post_url'   => $post_url,
+	) );
+
+	$activity_args['action'] = call_user_func_array( $activity_post_object->callback, array( '', (object) $action_args ) );
+
+	// Make sure the action is set
+	if ( empty( $activity_args['action'] ) ) {
+		return;
+	} else {
+		/**
+		 * As the 'post' post type will not use bp_blogs_record_activity(), we need to be sure the filter
+		 * 'bp_blogs_record_activity_action' can be applied on the action.
+		 */
+		$activity_args['action'] = apply_filters( "bp_{$activity_post_object->component_id}_record_activity_action", $activity_args['action'] );
+	}
+
+	do_action( 'bp_activity_post_type_published', bp_activity_add( $activity_args ), $post, $action_args );
+}
+
+/**
+ * Update an activity for the custom post type.
+ *
+ * @since BuddyPress (2.2.0)
+ *
+ * @param WP_Post $post
+ */
+function bp_activity_post_type_update( $post = null ) {
+	$bp = buddypress();
+
+	if ( ! is_a( $post, 'WP_Post' ) ) {
+		return;
+	}
+
+	if ( empty( $bp->activity->track[ 'new_' . $post->post_type ] ) ) {
+		return;
+	}
+
+	$activity_post_object = (object) $bp->activity->track[ 'new_' . $post->post_type ];
+
+	$activity_id = bp_activity_get_activity_id( array(
+		'component'         => $activity_post_object->component_id,
+		'item_id'           => get_current_blog_id(),
+		'secondary_item_id' => $post->ID,
+		'type'              => $activity_post_object->action_id,
+	) );
+
+	// activity ID doesn't exist, so stop!
+	if ( empty( $activity_id ) ) {
+		return;
+	}
+
+	// Delete the activity if the post was updated with a password
+	if ( ! empty( $post->post_password ) ) {
+		bp_activity_delete( array( 'id' => $activity_id ) );
+	}
+
+	// update the activity entry
+	$activity = new BP_Activity_Activity( $activity_id );
+
+	if ( ! empty( $post->post_content ) ) {
+		// Make sure to update the thumbnail image
+		$post_content = bp_activity_thumbnail_content_images( $post->post_content, $activity->primary_link, (array) $activity );
+
+		/**
+		 * Make sure to apply the excerpt
+		 *
+		 * As the 'post' post type will no more use bp_blogs_update_post(), we need to be sure the filter
+		 * 'bp_blogs_record_activity_content' can be applied on the action.
+		 */
+		$activity->content = apply_filters( "bp_{$activity_post_object->component_id}_record_activity_content", bp_create_excerpt( $post_content ), $post_content, (array) $activity, $post->post_type );
+	}
+
+	// Save the updated activity
+	$activity->save();
+
+	do_action( 'bp_activity_post_type_updated', $post, $activity );
+}
+
+/**
+ * Unpublish an activity for the custom post type.
+ *
+ * @since BuddyPress (2.2.0)
+ *
+ * @param int     $post_id
+ * @param WP_Post $post
+ */
+function bp_activity_post_type_unpublish( $post_id = 0, $post = null ) {
+	$bp = buddypress();
+
+	if ( ! is_a( $post, 'WP_Post' ) ) {
+		return;
+	}
+
+	if ( empty( $bp->activity->track[ 'new_' . $post->post_type ] ) ) {
+		return;
+	}
+
+	if ( empty( $post_id ) ) {
+		$post_id = $post->ID;
+	}
+
+	$activity_post_object = (object) $bp->activity->track[ 'new_' . $post->post_type ];
+
+	$delete_activity_args = array(
+		'item_id'           => get_current_blog_id(),
+		'secondary_item_id' => $post_id,
+		'component'         => $activity_post_object->component_id,
+		'type'              => $activity_post_object->action_id,
+		'user_id'           => false,
+	);
+
+	do_action( 'bp_activity_post_type_unpublished', bp_activity_delete_by_item_id( $delete_activity_args ), $delete_activity_args, $post );
+}
+
+/**
  * Add an activity comment.
  *
  * @since BuddyPress (1.2.0)
@@ -1988,6 +2274,7 @@ function bp_activity_delete_comment( $activity_id, $comment_id ) {
  * @return string $link Permalink for the activity item.
  */
 function bp_activity_get_permalink( $activity_id, $activity_obj = false ) {
+	$bp = buddypress();
 
 	if ( empty( $activity_obj ) ) {
 		$activity_obj = new BP_Activity_Activity( $activity_id );
@@ -1997,7 +2284,18 @@ function bp_activity_get_permalink( $activity_id, $activity_obj = false ) {
 		$activity_obj = $activity_obj->current_comment;
 	}
 
-	if ( 'new_blog_post' == $activity_obj->type || 'new_blog_comment' == $activity_obj->type || 'new_forum_topic' == $activity_obj->type || 'new_forum_post' == $activity_obj->type ) {
+	$use_primary_links = array(
+		'new_blog_post',
+		'new_blog_comment',
+		'new_forum_topic',
+		'new_forum_post',
+	);
+
+	if ( ! empty( $bp->activity->track ) ) {
+		$use_primary_links = array_merge( $use_primary_links, array_keys( $bp->activity->track ) );
+	}
+
+	if ( false !== array_search( $activity_obj->type, $use_primary_links ) ) {
 		$link = $activity_obj->primary_link;
 	} else {
 		if ( 'activity_comment' == $activity_obj->type ) {
diff --git src/bp-activity/bp-activity-template.php src/bp-activity/bp-activity-template.php
index d51c2f3..2892656 100644
--- src/bp-activity/bp-activity-template.php
+++ src/bp-activity/bp-activity-template.php
@@ -3315,30 +3315,38 @@ function bp_activity_filter_links( $args = false ) {
  */
 function bp_activity_can_comment() {
 	global $activities_template;
+	$bp = buddypress();
 
 	// Assume activity can be commented on
 	$can_comment = true;
 
 	// Determine ability to comment based on activity action name
 	$activity_action = bp_get_activity_action_name();
-	switch ( $activity_action ) {
-
-		// Maybe turn off for blog and forum updates
-		case 'new_blog_post'    :
-		case 'new_blog_comment' :
-		case 'new_forum_topic'  :
-		case 'new_forum_post'   :
-			if ( ! empty( $activities_template->disable_blogforum_replies ) ) {
-				$can_comment = false;
-			}
-			break;
 
-		// Turn off for activity comments
-		case 'activity_comment' :
-			$can_comment = false;
-			break;
+	$turn_off = 0;
+	if ( ! empty( $activities_template->disable_blogforum_replies ) ) {
+		$turn_off = 1;
+	}
+
+	$maybe_turn_off = array_fill_keys( array(
+		'new_blog_post',
+		'new_blog_comment',
+		'new_forum_topic',
+		'new_forum_post',
+	), $turn_off );
+
+	$maybe_turn_off['activity_comment'] = 1;
+
+	if ( ! empty( $bp->activity->track ) ) {
+		foreach ( array_keys( $bp->activity->track ) as $action  ) {
+			if ( empty( $bp->activity->track[ $action ]['activity_commment'] ) ) {
+				$maybe_turn_off[ $action ] = $turn_off;
+			}
+		}
 	}
 
+	$can_comment = empty( $maybe_turn_off[ $activity_action ] );
+
 	/**
 	 * Filters whether a comment can be made on an activity item.
 	 *
@@ -4368,6 +4376,9 @@ function bp_activity_show_filters( $context = '' ) {
 		// Walk through the registered actions, and prepare an the
 		// select box options.
 		foreach ( buddypress()->activity->actions as $actions ) {
+			// Sort actions by their position attribute
+			$actions = bp_activity_sort_component_actions($actions );
+
 			foreach ( $actions as $action ) {
 				if ( ! in_array( $context, (array) $action['context'] ) ) {
 					continue;
diff --git src/bp-blogs/bp-blogs-activity.php src/bp-blogs/bp-blogs-activity.php
index 8b67a16..0a43935 100644
--- src/bp-blogs/bp-blogs-activity.php
+++ src/bp-blogs/bp-blogs-activity.php
@@ -33,27 +33,23 @@ function bp_blogs_register_activity_actions() {
 			'new_blog',
 			__( 'New site created', 'buddypress' ),
 			'bp_blogs_format_activity_action_new_blog',
-			__( 'New Sites', 'buddypress' )
+			__( 'New Sites', 'buddypress' ),
+			0
 		);
 	}
 
-	bp_activity_set_action(
-		$bp->blogs->id,
-		'new_blog_post',
-		__( 'New post published', 'buddypress' ),
-		'bp_blogs_format_activity_action_new_blog_post',
-		__( 'Posts', 'buddypress' ),
-		array( 'activity', 'member' )
-	);
-
-	bp_activity_set_action(
-		$bp->blogs->id,
-		'new_blog_comment',
-		__( 'New post comment posted', 'buddypress' ),
-		'bp_blogs_format_activity_action_new_blog_comment',
-		__( 'Comments', 'buddypress' ),
-		array( 'activity', 'member' )
-	);
+	// Only add the comment type if the 'post' post type is trackable
+	if ( post_type_supports( 'post', 'buddypress-activity' ) ) {
+		bp_activity_set_action(
+			$bp->blogs->id,
+			'new_blog_comment',
+			__( 'New post comment posted', 'buddypress' ),
+			'bp_blogs_format_activity_action_new_blog_comment',
+			__( 'Comments', 'buddypress' ),
+			array( 'activity', 'member' ),
+			10
+		);
+	}
 
 	do_action( 'bp_blogs_register_activity_actions' );
 }
@@ -108,9 +104,17 @@ function bp_blogs_format_activity_action_new_blog_post( $action, $activity ) {
 		bp_blogs_update_blogmeta( $activity->item_id, 'name', $blog_name );
 	}
 
-	$post_url = add_query_arg( 'p', $activity->secondary_item_id, trailingslashit( $blog_url ) );
+	if ( empty( $activity->post_url ) ) {
+		$post_url = add_query_arg( 'p', $activity->secondary_item_id, trailingslashit( $blog_url ) );
+	} else {
+		$post_url = $activity->post_url;
+	}
 
-	$post_title = bp_activity_get_meta( $activity->id, 'post_title' );
+	if ( empty( $activity->post_title ) ) {
+		$post_title = bp_activity_get_meta( $activity->id, 'post_title' );
+	} else {
+		$post_title = $activity->post_title;
+	}
 
 	// Should only be empty at the time of post creation
 	if ( empty( $post_title ) ) {
diff --git src/bp-blogs/bp-blogs-filters.php src/bp-blogs/bp-blogs-filters.php
index 9cf0c92..b0e09dd 100644
--- src/bp-blogs/bp-blogs-filters.php
+++ src/bp-blogs/bp-blogs-filters.php
@@ -42,13 +42,58 @@ add_filter( 'wp_signup_location', 'bp_blogs_creation_location' );
  *
  * @since BuddyPress (2.1.0)
  *
- * @see bp_blogs_update_post()
+ * @see bp_blogs_update_post_activity_meta()
  *
  * @param array Current SQL clauses in array format
  * @return array
  */
 function bp_blogs_comments_clauses_select_by_id( $retval ) {
 	$retval['fields'] = 'comment_ID';
-	
+
 	return $retval;
-}
\ No newline at end of file
+}
+
+/**
+ * Check if the post can be published
+ *
+ * In deprecated bp_blogs_record_post(), this part was checking whether a post
+ * should be published or not.
+ *
+ * @since BuddyPress (2.2.0)
+ *
+ * @param  bool $return   by default true
+ * @param  int  $blog_id
+ * @param  int  $post_id
+ * @param  int  $user_id
+ * @return bool           true to authorize the post to be published, false otherwise
+ */
+function bp_blogs_post_pre_publish( $return = true, $blog_id = 0, $post_id = 0, $user_id = 0 ) {
+	$bp = buddypress();
+
+	// If blog is not trackable, do not record the activity.
+	if ( ! bp_blogs_is_blog_trackable( $blog_id, $user_id ) ) {
+		return false;
+	}
+
+	// Stop infinite loops with WordPress MU Sitewide Tags.
+	// That plugin changed the way its settings were stored at some point. Thus the dual check.
+	if ( ! empty( $bp->site_options['sitewide_tags_blog'] ) ) {
+		$st_options = maybe_unserialize( $bp->site_options['sitewide_tags_blog'] );
+		$tags_blog_id = isset( $st_options['tags_blog_id'] ) ? $st_options['tags_blog_id'] : 0;
+	} else {
+		$tags_blog_id = isset( $bp->site_options['tags_blog_id'] ) ? $bp->site_options['tags_blog_id'] : 0;
+	}
+
+	if ( (int) $blog_id == $tags_blog_id && apply_filters( 'bp_blogs_block_sitewide_tags_activity', true ) ) {
+		return false;
+	}
+
+	$is_blog_public = apply_filters( 'bp_is_blog_public', (int) get_blog_option( $blog_id, 'blog_public' ) );
+
+	if ( 0 === $is_blog_public && is_multisite() ) {
+		return false;
+	}
+
+	return $return;
+}
+add_filter( 'bp_activity_post_pre_publish', 'bp_blogs_post_pre_publish', 10, 4 );
diff --git src/bp-blogs/bp-blogs-functions.php src/bp-blogs/bp-blogs-functions.php
index 41f62f6..83f719c 100644
--- src/bp-blogs/bp-blogs-functions.php
+++ src/bp-blogs/bp-blogs-functions.php
@@ -402,197 +402,52 @@ function bp_blogs_update_option_thread_comments_depth( $oldvalue, $newvalue ) {
 add_action( 'update_option_thread_comments_depth', 'bp_blogs_update_option_thread_comments_depth', 10, 2 );
 
 /**
- * Detect a change in post status, and initiate an activity update if necessary.
+ * Record additional metas when a post is published.
  *
- * Posts get new activity updates when (a) they are being published, and (b)
- * they have not already been published. This enables proper posting for
- * regular posts as well as scheduled posts, while preventing post bumping.
+ * @since BuddyPress (2.2.0)
  *
- * See #4090, #3746, #2546 for background.
- *
- * @since BuddyPress (2.0.0)
- *
- * @todo Support untrashing better
- *
- * @param string $new_status New status for the post.
- * @param string $old_status Old status for the post.
- * @param object $post Post data.
+ * @param  int     $activity_id
+ * @param  WP_Post $post
  */
-function bp_blogs_catch_transition_post_status( $new_status, $old_status, $post ) {
-
-	// This is an edit
-	if ( $new_status === $old_status ) {
-		if ( $new_status == 'publish' ) {
-			bp_blogs_update_post( $post );
-			return;
-		}
-	}
-
-	// Publishing a previously unpublished post
-	if ( 'publish' === $new_status ) {
-		// Untrashing the post
-		// Nothing here yet
-		if ( 'trash' == $old_status ) {}
-
-		// Record the post
-		bp_blogs_record_post( $post->ID, $post );
-
-	// Unpublishing a previously published post
-	} else if ( 'publish' === $old_status ) {
-		// Some form of pending status
-		// Only remove the activity entry
-		bp_blogs_delete_activity( array(
-			'item_id'           => get_current_blog_id(),
-			'secondary_item_id' => $post->ID,
-			'component'         => buddypress()->blogs->id,
-			'type'              => 'new_blog_post'
-		) );
+function bp_blogs_publish_post_activity_meta( $activity_id, $post, $args ) {
+	if ( empty( $activity_id ) || 'post' != $post->post_type ) {
+		return;
 	}
-}
-add_action( 'transition_post_status', 'bp_blogs_catch_transition_post_status', 10, 3 );
-
-/**
- * Record a new blog post in the BuddyPress activity stream.
- *
- * @param int $post_id ID of the post being recorded.
- * @param object $post The WP post object passed to the 'save_post' action.
- * @param int $user_id Optional. The user to whom the activity item will be
- *        associated. Defaults to the post_author.
- * @return bool|null Returns false on failure.
- */
-function bp_blogs_record_post( $post_id, $post, $user_id = 0 ) {
-	global $bp, $wpdb;
-
-	$post_id = (int) $post_id;
-	$blog_id = (int) $wpdb->blogid;
-
-	// If blog is not trackable, do not record the activity.
-	if ( ! bp_blogs_is_blog_trackable( $blog_id, $user_id ) )
-		return false;
 
-	if ( !$user_id )
-		$user_id = (int) $post->post_author;
+	bp_activity_update_meta( $activity_id, 'post_title', $post->post_title );
 
-	// Stop infinite loops with WordPress MU Sitewide Tags.
-	// That plugin changed the way its settings were stored at some point. Thus the dual check.
-	if ( !empty( $bp->site_options['sitewide_tags_blog'] ) ) {
-		$st_options = maybe_unserialize( $bp->site_options['sitewide_tags_blog'] );
-		$tags_blog_id = isset( $st_options['tags_blog_id'] ) ? $st_options['tags_blog_id'] : 0;
+	if ( ! empty( $args['post_url'] ) ) {
+		$post_permalink = $args['post_url'];
 	} else {
-		$tags_blog_id = isset( $bp->site_options['tags_blog_id'] ) ? $bp->site_options['tags_blog_id'] : 0;
+		$post_permalink = $post->guid;
 	}
 
-	if ( (int) $blog_id == $tags_blog_id && apply_filters( 'bp_blogs_block_sitewide_tags_activity', true ) )
-		return false;
-
-	// Don't record this if it's not a post
-	if ( !in_array( $post->post_type, apply_filters( 'bp_blogs_record_post_post_types', array( 'post' ) ) ) )
-		return false;
-
-	$is_blog_public = apply_filters( 'bp_is_blog_public', (int)get_blog_option( $blog_id, 'blog_public' ) );
-
-	if ( 'publish' == $post->post_status && empty( $post->post_password ) ) {
-		if ( $is_blog_public || !is_multisite() ) {
+	bp_activity_update_meta( $activity_id, 'post_url',   $post_permalink );
 
-			// Record this in activity streams
-			$post_permalink = add_query_arg(
-				'p',
-				$post_id,
-				trailingslashit( get_home_url( $blog_id ) )
-			);
-
-			if ( is_multisite() )
-				$activity_action  = sprintf( __( '%1$s wrote a new post, %2$s, on the site %3$s', 'buddypress' ), bp_core_get_userlink( (int) $post->post_author ), '<a href="' . $post_permalink . '">' . $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 wrote a new post, %2$s', 'buddypress' ), bp_core_get_userlink( (int) $post->post_author ), '<a href="' . $post_permalink . '">' . $post->post_title . '</a>' );
-
-			// Make sure there's not an existing entry for this post (prevent bumping)
-			if ( bp_is_active( 'activity' ) ) {
-				$existing = bp_activity_get( array(
-					'filter' => array(
-						'action'       => 'new_blog_post',
-						'primary_id'   => $blog_id,
-						'secondary_id' => $post_id,
-					)
-				) );
-
-				if ( !empty( $existing['activities'] ) ) {
-					return;
-				}
-			}
-
-			$activity_content = $post->post_content;
-
-			$activity_id = bp_blogs_record_activity( array(
-				'user_id'           => (int) $post->post_author,
-				'content'           => apply_filters( 'bp_blogs_activity_new_post_content',      $activity_content, $post, $post_permalink ),
-				'primary_link'      => apply_filters( 'bp_blogs_activity_new_post_primary_link', $post_permalink,   $post_id               ),
-				'type'              => 'new_blog_post',
-				'item_id'           => $blog_id,
-				'secondary_item_id' => $post_id,
-				'recorded_time'     => $post->post_date_gmt,
-			) );
-
-			// save post title in activity meta
-			if ( bp_is_active( 'activity' ) ) {
-				bp_activity_update_meta( $activity_id, 'post_title', $post->post_title );
-				bp_activity_update_meta( $activity_id, 'post_url',   $post_permalink );
-			}
-		}
-
-		// Update the blogs last activity
-		bp_blogs_update_blogmeta( $blog_id, 'last_activity', bp_core_current_time() );
-	} else {
-		bp_blogs_remove_post( $post_id, $blog_id, $user_id );
-	}
+	// Update the blogs last activity
+	bp_blogs_update_blogmeta( $args['item_id'], 'last_activity', bp_core_current_time() );
 
-	do_action( 'bp_blogs_new_blog_post', $post_id, $post, $user_id );
+	do_action( 'bp_blogs_new_blog_post', $post->ID, $post, $args['user_id'] );
 }
+add_action( 'bp_activity_post_type_published', 'bp_blogs_publish_post_activity_meta', 10, 3 );
 
 /**
- * Updates a blog post's corresponding activity entry during a post edit.
+ * Updates a blog post's activity meta entry during a post edit.
  *
- * @since BuddyPress (2.0.0)
- *
- * @see bp_blogs_catch_transition_post_status()
+ * @since BuddyPress (2.2.0)
  *
- * @param WP_Post $post
+ * @param  WP_Post $post
+ * @param  BP_Actitivy_Activity $activity
  */
-function bp_blogs_update_post( $post ) {
-	if ( ! bp_is_active( 'activity' ) ) {
-		return;
-	}
-
-	$activity_id = bp_activity_get_activity_id( array(
-		'component'         => buddypress()->blogs->id,
-		'item_id'           => get_current_blog_id(),
-		'secondary_item_id' => $post->ID,
-		'type'              => 'new_blog_post',
-	 ) );
-
-	// activity ID doesn't exist, so stop!
-	if ( empty( $activity_id ) ) {
+function bp_blogs_update_post_activity_meta( $post, $activity ) {
+	if ( empty( $activity->id ) || 'post' != $post->post_type ) {
 		return;
 	}
 
-	// update the activity entry
-	$activity = new BP_Activity_Activity( $activity_id );
-
-	if ( ! empty( $post->post_content ) ) {
-		// Make sure to update the thumbnail image
-		$post_content = bp_activity_thumbnail_content_images( $post->post_content, $activity->primary_link, (array) $activity );
-
-		// Make sure to apply the blop post excerpt
-		$activity->content = apply_filters( 'bp_blogs_record_activity_content', bp_create_excerpt( $post_content ), $post_content, (array) $activity );
-	}
-
-	// Save the updated activity
-	$activity->save();
-
 	// update post title in activity meta
-	$existing_title = bp_activity_get_meta( $activity_id, 'post_title' );
+	$existing_title = bp_activity_get_meta( $activity->id, 'post_title' );
 	if ( $post->post_title !== $existing_title ) {
-		bp_activity_update_meta( $activity_id, 'post_title', $post->post_title );
+		bp_activity_update_meta( $activity->id, 'post_title', $post->post_title );
 
 		// now update activity meta for post comments... sigh
 		add_filter( 'comments_clauses', 'bp_blogs_comments_clauses_select_by_id' );
@@ -650,11 +505,12 @@ function bp_blogs_update_post( $post ) {
 
 	// add post comment status to activity meta if closed
 	if( 'closed' == $post->comment_status ) {
-		bp_activity_update_meta( $activity_id, 'post_comment_status', $post->comment_status );
+		bp_activity_update_meta( $activity->id, 'post_comment_status', $post->comment_status );
 	} else {
-		bp_activity_delete_meta( $activity_id, 'post_comment_status' );
+		bp_activity_delete_meta( $activity->id, 'post_comment_status' );
 	}
 }
+add_action( 'bp_activity_post_type_updated', 'bp_blogs_update_post_activity_meta', 10, 2 );
 
 /**
  * Record a new blog comment in the BuddyPress activity stream.
diff --git src/bp-blogs/bp-blogs-loader.php src/bp-blogs/bp-blogs-loader.php
index 112cc80..2d6b294 100644
--- src/bp-blogs/bp-blogs-loader.php
+++ src/bp-blogs/bp-blogs-loader.php
@@ -75,6 +75,24 @@ class BP_Blogs_Component extends BP_Component {
 
 		// Setup the globals
 		parent::setup_globals( $args );
+
+		/**
+		 * Setup the post post type to track
+		 *
+		 * In case the config is not multisite, the blog_public option
+		 * is ignored.
+		 */
+		if ( 0 !== (int) get_option( 'blog_public' ) || ! is_multisite() ) {
+			// Get all posts to track
+			$post_types = apply_filters( 'bp_blogs_record_post_post_types', array( 'post' ) );
+
+			foreach ( $post_types as $post_type ) {
+				add_post_type_support( $post_type, 'buddypress-activity' );
+			}
+		}
+
+		// Filter the generic track parameters for the 'post' post type
+		add_filter( 'bp_activity_tracking_register_post_post_type', array( $this, 'track_parameter' ), 10, 1 );
 	}
 
 	/**
@@ -246,6 +264,23 @@ class BP_Blogs_Component extends BP_Component {
 
 		parent::setup_title();
 	}
+
+	/**
+	 * Set up the track parameters for the 'post' post type
+	 *
+	 * @since  BuddyPress (2.2.0)
+	 */
+	public function track_parameter( $params = array() ) {
+		return array_merge( $params, array(
+			'component_id'      => $this->id,
+			'action_id'         => 'new_blog_post',
+			'admin_filter'      => __( 'New post published', 'buddypress' ),
+			'callback'          => 'bp_blogs_format_activity_action_new_blog_post',
+			'filter'            => __( 'Posts', 'buddypress' ),
+			'contexts'          => array( 'activity', 'member' ),
+			'order'             => 5,
+		) );
+	}
 }
 
 /**
diff --git src/bp-core/bp-core-actions.php src/bp-core/bp-core-actions.php
index 2bb2334..32d9e71 100644
--- src/bp-core/bp-core-actions.php
+++ src/bp-core/bp-core-actions.php
@@ -33,6 +33,7 @@ if ( !defined( 'ABSPATH' ) ) exit;
   */
 add_action( 'plugins_loaded',          'bp_loaded',                 10    );
 add_action( 'init',                    'bp_init',                   10    );
+add_action( 'wp_loaded',               'bp_fully_loaded',           10    );
 add_action( 'parse_query',             'bp_parse_query',            2     ); // Early for overrides
 add_action( 'wp',                      'bp_ready',                  10    );
 add_action( 'set_current_user',        'bp_setup_current_user',     10    );
diff --git src/bp-core/bp-core-dependency.php src/bp-core/bp-core-dependency.php
index 8ce93a7..f1c6f28 100644
--- src/bp-core/bp-core-dependency.php
+++ src/bp-core/bp-core-dependency.php
@@ -108,6 +108,17 @@ function bp_loaded() {
 }
 
 /**
+ * Fire the 'bp_fully_loaded' action, which fires after WordPress, plugins and themes have been loaded.
+ *
+ * Attached to 'wp_loaded'.
+ *
+ * @since  BuddyPress (2.2.0)
+ */
+function bp_fully_loaded() {
+	do_action( 'bp_fully_loaded' );
+}
+
+/**
  * Fire the 'bp_ready' action, which runs after BP is set up and the page is about to render.
  *
  * Attached to 'wp'.
diff --git src/bp-core/deprecated/2.2.php src/bp-core/deprecated/2.2.php
index e69de29..acfd25e 100644
--- src/bp-core/deprecated/2.2.php
+++ src/bp-core/deprecated/2.2.php
@@ -0,0 +1,65 @@
+<?php
+/**
+ * Deprecated functions
+ *
+ * @package BuddyPress
+ * @subpackage Core
+ * @deprecated 2.2.0
+ */
+
+// Exit if accessed directly
+if ( ! defined( 'ABSPATH' ) ) exit;
+
+/**
+ * Detect a change in post status, and initiate an activity update if necessary.
+ *
+ * Posts get new activity updates when (a) they are being published, and (b)
+ * they have not already been published. This enables proper posting for
+ * regular posts as well as scheduled posts, while preventing post bumping.
+ *
+ * See #4090, #3746, #2546 for background.
+ *
+ * @since BuddyPress (2.0.0)
+ * @deprecated BuddyPress (2.2.0)
+ *
+ * @todo Support untrashing better
+ *
+ * @param string $new_status New status for the post.
+ * @param string $old_status Old status for the post.
+ * @param object $post Post data.
+ */
+function bp_blogs_catch_transition_post_status( $new_status, $old_status, $post ) {
+	_deprecated_function( __FUNCTION__, '2.2', 'bp_activity_catch_transition_post_type_status()' );
+	bp_activity_catch_transition_post_type_status( $new_status, $old_status, $post );
+}
+
+/**
+ * Record a new blog post in the BuddyPress activity stream.
+ *
+ * @deprecated BuddyPress (2.2.0)
+ *
+ * @param int $post_id ID of the post being recorded.
+ * @param object $post The WP post object passed to the 'save_post' action.
+ * @param int $user_id Optional. The user to whom the activity item will be
+ *        associated. Defaults to the post_author.
+ * @return bool|null Returns false on failure.
+ */
+function bp_blogs_record_post( $post_id, $post, $user_id = 0 ) {
+	_deprecated_function( __FUNCTION__, '2.2', 'bp_activity_post_type_publish()' );
+	bp_activity_post_type_publish( $post_id, $post, $user_id );
+}
+
+/**
+ * Updates a blog post's corresponding activity entry during a post edit.
+ *
+ * @since BuddyPress (2.0.0)
+ * @deprecated BuddyPress (2.2.0)
+ *
+ * @see bp_blogs_catch_transition_post_status()
+ *
+ * @param WP_Post $post
+ */
+function bp_blogs_update_post( $post ) {
+	_deprecated_function( __FUNCTION__, '2.2', 'bp_activity_post_type_update()' );
+	bp_activity_post_type_update( $post );
+}
diff --git tests/phpunit/testcases/activity/actions.php tests/phpunit/testcases/activity/actions.php
index e69de29..a6d9350 100644
--- tests/phpunit/testcases/activity/actions.php
+++ tests/phpunit/testcases/activity/actions.php
@@ -0,0 +1,178 @@
+<?php
+/**
+ * @group activity
+ */
+class BP_Tests_Activity_Actions extends BP_UnitTestCase {
+	protected $old_current_user = 0;
+
+	public function setUp() {
+		parent::setUp();
+
+		$this->old_current_user = get_current_user_id();
+		$this->set_current_user( $this->factory->user->create( array( 'role' => 'subscriber' ) ) );
+	}
+
+	public function tearDown() {
+		parent::tearDown();
+		$this->set_current_user( $this->old_current_user );
+	}
+
+	/**
+	 * @group bp_activity_tracking_register_post_types
+	 * @group activity_tracking
+	 */
+	public function test_bp_activity_tracking_register_post_types() {
+		add_post_type_support( 'page', 'buddypress-activity' );
+		remove_post_type_support( 'post', 'buddypress-activity' );
+
+		register_post_type( 'foo', array(
+			'label'   => 'foo',
+			'public'   => true,
+			'supports' => array( 'buddypress-activity' ),
+		) );
+
+		register_post_type( 'bar', array(
+			'label'    => 'bar',
+			'public'   => false,
+			'supports' => array( 'buddypress-activity' ),
+		) );
+
+		$expected = array( 'new_page', 'new_foo' );
+
+		bp_activity_tracking_register_post_types();
+
+		$this->assertEquals( $expected, array_keys( buddypress()->activity->track ) );
+
+		// reset
+		add_post_type_support( 'post', 'buddypress-activity' );
+		remove_post_type_support( 'page', 'buddypress-activity' );
+		_unregister_post_type( 'foo' );
+		_unregister_post_type( 'bar' );
+	}
+
+	/**
+	 * @group bp_activity_catch_transition_post_type_status
+	 * @group activity_tracking
+	 */
+	public function test_bp_activity_catch_transition_post_type_status_publish() {
+		register_post_type( 'foo', array(
+			'label'   => 'foo',
+			'public'   => true,
+			'supports' => array( 'buddypress-activity' ),
+		) );
+
+		$post_id = $this->factory->post->create( array(
+			'post_status' => 'publish',
+			'post_type'   => 'foo',
+		) );
+
+		$post = get_post( $post_id );
+
+		// 'new' => 'publish'
+		$this->assertTrue( $this->activity_exists_for_post( $post_id, 'new_foo' ), 'Published post type should have activity' );
+
+		_unregister_post_type( 'foo' );
+	}
+
+	/**
+	 * @group bp_activity_catch_transition_post_type_status
+	 * @group activity_tracking
+	 */
+	public function test_bp_activity_catch_transition_post_type_status_publish_to_publish() {
+		register_post_type( 'foo', array(
+			'label'   => 'foo',
+			'public'   => true,
+			'supports' => array( 'buddypress-activity' ),
+		) );
+
+		$post_id = $this->factory->post->create( array(
+			'post_status' => 'publish',
+			'post_type'   => 'foo',
+		) );
+
+		$post = get_post( $post_id );
+
+		// 'new' => 'publish'
+		$this->assertTrue( $this->activity_exists_for_post( $post_id, 'new_foo' ), 'Published post type should have activity' );
+
+		$post->post_status = 'publish';
+		$post->post_content .= ' foo';
+
+		wp_update_post( $post );
+
+		$this->assertTrue( $this->activity_exists_for_post( $post_id, 'new_foo' ), 'Published post type should have activity (no change)' );
+
+		_unregister_post_type( 'foo' );
+	}
+
+	/**
+	 * @group bp_activity_catch_transition_post_type_status
+	 * @group activity_tracking
+	 */
+	public function test_bp_activity_catch_transition_post_type_status_publish_password() {
+		register_post_type( 'foo', array(
+			'label'   => 'foo',
+			'public'   => true,
+			'supports' => array( 'buddypress-activity' ),
+		) );
+
+		$post_id = $this->factory->post->create( array(
+			'post_status' => 'publish',
+			'post_type'   => 'foo',
+		) );
+
+		$post = get_post( $post_id );
+
+		// 'new' => 'publish'
+		$this->assertTrue( $this->activity_exists_for_post( $post_id, 'new_foo' ), 'Published post type should have activity' );
+
+		$post->post_status   = 'publish';
+		$post->post_password = 'foo';
+
+		wp_update_post( $post );
+
+		// 'publish' => 'publish' (password protected)
+		$this->assertFalse( $this->activity_exists_for_post( $post_id, 'new_foo' ), 'Password protected post type should not have activity' );
+
+		_unregister_post_type( 'foo' );
+	}
+
+	/**
+	 * @group bp_activity_catch_transition_post_type_status
+	 * @group activity_tracking
+	 */
+	public function test_bp_activity_catch_transition_post_type_status_publish_trash() {
+		register_post_type( 'foo', array(
+			'label'   => 'foo',
+			'public'   => true,
+			'supports' => array( 'buddypress-activity' ),
+		) );
+
+		$post_id = $this->factory->post->create( array(
+			'post_status' => 'publish',
+			'post_type'   => 'foo',
+		) );
+
+		$post = get_post( $post_id );
+
+		// 'new' => 'publish'
+		$this->assertTrue( $this->activity_exists_for_post( $post_id, 'new_foo' ), 'Published post type should have activity' );
+
+		wp_trash_post( $post->ID );
+
+		// 'publish' => 'publish' (password protected)
+		$this->assertFalse( $this->activity_exists_for_post( $post_id, 'new_foo' ), 'Unpublished post type should not have activity' );
+
+		_unregister_post_type( 'foo' );
+	}
+
+	protected function activity_exists_for_post( $post_id, $action ) {
+		$a = bp_activity_get( array(
+			'action'            => $action,
+			'item_id'           => get_current_blog_id(),
+			'secondary_item_id' => $post_id,
+		) );
+
+		return ! empty( $a['activities'] );
+	}
+}
diff --git tests/phpunit/testcases/activity/functions.php tests/phpunit/testcases/activity/functions.php
index 6cea6bd..35693eb 100644
--- tests/phpunit/testcases/activity/functions.php
+++ tests/phpunit/testcases/activity/functions.php
@@ -576,6 +576,113 @@ Bar!';
 	}
 
 	/**
+	 * @group activity_action
+	 * @group bp_activity_format_activity_action_custom_post_type_post
+	 * @group activity_tracking
+	 */
+	public function test_bp_activity_format_activity_action_custom_post_type_post_nonms() {
+		if ( is_multisite() ) {
+			return;
+		}
+
+		$bp = buddypress();
+
+		register_post_type( 'foo', array(
+			'label'   => 'foo',
+			'public'   => true,
+			'supports' => array( 'buddypress-activity' ),
+		) );
+
+		bp_activity_tracking_register_post_types();
+
+		$u = $this->factory->user->create();
+		$p = $this->factory->post->create( array(
+			'post_author' => $u,
+			'post_type'   => 'foo',
+		) );
+
+		$a = $this->factory->activity->create( array(
+			'component'         => $bp->activity->track['new_foo']['component_id'],
+			'type'              => $bp->activity->track['new_foo']['action_id'],
+			'user_id'           => $u,
+			'item_id'           => 1,
+			'secondary_item_id' => $p,
+		) );
+
+		$a_obj = new BP_Activity_Activity( $a );
+
+		$user_link = bp_core_get_userlink( $u );
+		$blog_url = get_home_url();
+		$post_url = add_query_arg( 'p', $p, trailingslashit( $blog_url ) );
+		$post_link = '<a href="' . $post_url . '">' . $bp->activity->track['new_foo']['singular'] . '</a>';
+
+		$expected = sprintf( '%s wrote a new %s', $user_link, $post_link );
+
+		$this->assertSame( $expected, $a_obj->action );
+
+		_unregister_post_type( 'foo' );
+	}
+
+	/**
+	 * @group activity_action
+	 * @group bp_activity_format_activity_action_custom_post_type_post_ms
+	 * @group activity_tracking
+	 */
+	public function test_bp_activity_format_activity_action_custom_post_type_post_ms() {
+		if ( ! is_multisite() ) {
+			return;
+		}
+
+		$bp = buddypress();
+
+		$b = $this->factory->blog->create();
+		$u = $this->factory->user->create();
+
+		switch_to_blog( $b );
+
+		register_post_type( 'foo', array(
+			'label'   => 'foo',
+			'public'   => true,
+			'supports' => array( 'buddypress-activity' ),
+		) );
+
+		bp_activity_tracking_register_post_types();
+
+		$p = $this->factory->post->create( array(
+			'post_author' => $u,
+			'post_type'   => 'foo',
+		) );
+
+		$activity_args = array(
+			'component'         => $bp->activity->track['new_foo']['component_id'],
+			'type'              => $bp->activity->track['new_foo']['action_id'],
+			'user_id'           => $u,
+			'item_id'           => $b,
+			'secondary_item_id' => $p,
+		);
+
+		$post_type_label = $bp->activity->track['new_foo']['singular'];
+
+		_unregister_post_type( 'foo' );
+
+		restore_current_blog();
+
+		$a = $this->factory->activity->create( $activity_args );
+
+		$a_obj = new BP_Activity_Activity( $a );
+
+		$user_link = bp_core_get_userlink( $u );
+		$blog_url = get_blog_option( $a_obj->item_id, 'home' );
+		$post_url = add_query_arg( 'p', $p, trailingslashit( $blog_url ) );
+
+		$post_link = '<a href="' . $post_url . '">' . $post_type_label . '</a>';
+
+		$expected = sprintf( '%s wrote a new %s, on the site %s', $user_link, $post_link, '<a href="' . $blog_url . '">' . get_blog_option( $a_obj->item_id, 'blogname' ) . '</a>' );
+
+		$this->assertSame( $expected, $a_obj->action );
+	}
+
+	/**
 	 * @group bp_activity_new_comment
 	 * @group cache
 	 */
diff --git tests/phpunit/testcases/blogs/activity.php tests/phpunit/testcases/blogs/activity.php
index cc16b1f..3db3e8f 100644
--- tests/phpunit/testcases/blogs/activity.php
+++ tests/phpunit/testcases/blogs/activity.php
@@ -2,6 +2,23 @@
 
 class BP_Tests_Blogs_Activity extends BP_UnitTestCase {
 	/**
+	 * @group bp_blogs_register_activity_actions
+	 */
+	public function test_bp_blogs_register_activity_actions() {
+		$bp = buddypress();
+
+		$expected = array( 'new_blog_post', 'new_blog_comment' );
+
+		if ( is_multisite() ) {
+			$expected = array_merge( array( 'new_blog' ), $expected );
+		}
+
+		$actions = array_keys( (array) bp_activity_sort_component_actions( $bp->activity->actions->blogs ) );
+
+		$this->assertEquals( $expected, $actions );
+	}
+
+	/**
 	 * @group activity_action
 	 * @group bp_blogs_format_activity_action_new_blog
 	 */
diff --git tests/phpunit/testcases/blogs/functions.php tests/phpunit/testcases/blogs/functions.php
index 6e03d5c..b2ee4eb 100644
--- tests/phpunit/testcases/blogs/functions.php
+++ tests/phpunit/testcases/blogs/functions.php
@@ -303,6 +303,84 @@ class BP_Tests_Blogs_Functions extends BP_UnitTestCase {
 	/**
 	 * @group bp_blogs_catch_transition_post_status
 	 */
+	public function test_transition_post_status_password_publish() {
+		$post_id = $this->factory->post->create( array(
+			'post_status'   => 'publish',
+			'post_type'     => 'post',
+			'post_password' => 'pass',
+		) );
+		$post = get_post( $post_id );
+
+		// 'new' => 'publish with password'
+		$this->assertFalse( $this->activity_exists_for_post( $post_id ), 'Published with password post should not have activity' );
+	}
+
+	/**
+	 * @group bp_blogs_catch_transition_post_status
+	 */
+	public function test_transition_post_status_publish_update_password() {
+		$post_id = $this->factory->post->create( array(
+			'post_status'   => 'publish',
+			'post_type'     => 'post',
+		) );
+		$post = get_post( $post_id );
+
+		// 'publish' => 'publish'
+		$this->assertTrue( $this->activity_exists_for_post( $post_id ), 'Published post should have activity' );
+
+		$post->post_content .= ' foo';
+		$post->post_password = 'pass';
+
+		wp_update_post( $post );
+
+		$this->assertFalse( $this->activity_exists_for_post( $post_id ), 'Updated with password post should not have activity' );
+	}
+
+	/**
+	 * @group bp_blogs_catch_transition_post_status
+	 */
+	public function test_transition_post_status_private_publish() {
+		$post_id = $this->factory->post->create( array(
+			'post_status'   => 'private',
+			'post_type'     => 'post',
+		) );
+		$post = get_post( $post_id );
+
+		// 'new' => 'private'
+		$this->assertFalse( $this->activity_exists_for_post( $post_id ), 'Private post should not have activity' );
+
+		$post->post_status = 'publish';
+
+		wp_update_post( $post );
+
+		// 'private' => 'publish'
+		$this->assertTrue( $this->activity_exists_for_post( $post_id ), 'Published post should have activity' );
+	}
+
+	/**
+	 * @group bp_blogs_catch_transition_post_status
+	 */
+	public function test_transition_post_status_publish_private() {
+		$post_id = $this->factory->post->create( array(
+			'post_status'   => 'publish',
+			'post_type'     => 'post',
+		) );
+		$post = get_post( $post_id );
+
+		// 'new' => 'publish'
+		$this->assertTrue( $this->activity_exists_for_post( $post_id ), 'Published post should have activity' );
+
+		$post->post_status = 'private';
+
+		wp_update_post( $post );
+
+		// 'publish' => 'private'
+		$this->assertFalse( $this->activity_exists_for_post( $post_id ), 'Private post should not have activity' );
+	}
+
+	/**
+	 * @group bp_blogs_catch_transition_post_status
+	 */
 	public function test_transition_post_status_draft_to_draft() {
 		$post_id = $this->factory->post->create( array(
 			'post_status' => 'draft',
@@ -477,6 +555,60 @@ class BP_Tests_Blogs_Functions extends BP_UnitTestCase {
 		$this->set_current_user( $old_user );
 	}
 
+	/**
+	 * @group bp_blogs_catch_transition_post_status
+	 */
+	public function test_bp_blogs_is_blog_trackable_false_publish_post() {
+		add_filter( 'bp_blogs_is_blog_trackable', '__return_false' );
+
+		$post_id = $this->factory->post->create( array(
+			'post_status'   => 'publish',
+			'post_type'     => 'post',
+		) );
+		$post = get_post( $post_id );
+
+		// 'new' => 'publish'
+		$this->assertFalse( $this->activity_exists_for_post( $post_id ), 'Not trackable blog post should not have activity' );
+
+		$post->post_content .= ' foo';
+
+		wp_update_post( $post );
+
+		// 'publish' => 'publish'
+		$this->assertFalse( $this->activity_exists_for_post( $post_id ), 'Not trackable blog post should not have activity' );
+
+		remove_filter( 'bp_blogs_is_blog_trackable', '__return_false' );
+	}
+
+	/**
+	 * @group bp_blogs_catch_transition_post_status
+	 */
+	public function test_bp_is_blog_public_zero_publish_post() {
+		if ( ! is_multisite() ) {
+			return;
+		}
+
+		add_filter( 'bp_is_blog_public', '__return_zero' );
+
+		$post_id = $this->factory->post->create( array(
+			'post_status'   => 'publish',
+			'post_type'     => 'post',
+		) );
+		$post = get_post( $post_id );
+
+		// 'new' => 'publish'
+		$this->assertFalse( $this->activity_exists_for_post( $post_id ), 'Not public blog post should not have activity' );
+
+		$post->post_content .= ' foo';
+
+		wp_update_post( $post );
+
+		// 'publish' => 'publish'
+		$this->assertFalse( $this->activity_exists_for_post( $post_id ), 'Not public blog post should not have activity' );
+
+		remove_filter( 'bp_is_blog_public', '__return_zero' );
+	}
+
 	protected function activity_exists_for_post( $post_id ) {
 		$a = bp_activity_get( array(
 			'component' => buddypress()->blogs->id,
