<!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][8251] trunk: Insert a new "since" filter for the activity stream to be used by HeartBeat check feature</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/8251">8251</a></dd>
<dt>Author</dt> <dd>imath</dd>
<dt>Date</dt> <dd>2014-04-08 06:55:30 +0000 (Tue, 08 Apr 2014)</dd>
</dl>

<h3>Log Message</h3>
<pre>Insert a new "since" filter for the activity stream to be used by HeartBeat check feature

In rare cases, the field date_recorded of the activity table can be updated to a date greater than the most recent activity in terms of id.
The Heartbeat feature was based on the id field so far, while the activity stream is sorting items according to the date_recorded field. As a result, in this very particular case, activities with an id greater than the activity (which date has been updated) will be loaded again in the stream and this until a new activity is published.
To prevent this to happen, we introduce this new "since" filter to be used by the HeartBeat feature once it gets the timestamp of the most recent activity displayed in the stream.

props SGr33n, boonebgorges, imath

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

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkbpactivitybpactivityclassesphp">trunk/bp-activity/bp-activity-classes.php</a></li>
<li><a href="#trunkbpactivitybpactivityfiltersphp">trunk/bp-activity/bp-activity-filters.php</a></li>
<li><a href="#trunkbpactivitybpactivitytemplatephp">trunk/bp-activity/bp-activity-template.php</a></li>
<li><a href="#trunkbptemplatesbplegacybuddypressfunctionsphp">trunk/bp-templates/bp-legacy/buddypress-functions.php</a></li>
<li><a href="#trunkbptemplatesbplegacyjsbuddypressjs">trunk/bp-templates/bp-legacy/js/buddypress.js</a></li>
<li><a href="#trunkteststestcasesactivityclassBP_Activity_Activityphp">trunk/tests/testcases/activity/class.BP_Activity_Activity.php</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkbpactivitybpactivityclassesphp"></a>
<div class="modfile"><h4>Modified: trunk/bp-activity/bp-activity-classes.php (8250 => 8251)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/bp-activity/bp-activity-classes.php        2014-04-07 18:30:36 UTC (rev 8250)
+++ trunk/bp-activity/bp-activity-classes.php   2014-04-08 06:55:30 UTC (rev 8251)
</span><span class="lines">@@ -1189,8 +1189,7 @@
</span><span class="cx">   * @since BuddyPress (1.5.0)
</span><span class="cx">   *
</span><span class="cx">   * @param array $filter_array {
</span><del>-        *     Fields and values to filter by. Each can be either a single
-        *     string, a comma-separated list, or an array of values.
</del><ins>+         *     Fields and values to filter by.
</ins><span class="cx">    *     @type array|string|id $user_id User ID(s).
</span><span class="cx">   *     @type array|string $object Corresponds to the 'component'
</span><span class="cx">   *           column in the database.
</span><span class="lines">@@ -1200,6 +1199,11 @@
</span><span class="cx">   *           column in the database.
</span><span class="cx">   *     @type array|string|int $secondary_id Corresponds to the
</span><span class="cx">   *           'secondary_item_id' column in the database.
</span><ins>+        *     @type int $offset Return only those items with an ID greater
+        *           than the offset value.
+        *     @type string $since Return only those items that have a
+        *           date_recorded value greater than a given MySQL-formatted
+        *           date.
</ins><span class="cx">    * }
</span><span class="cx">   * @return string The filter clause, for use in a SQL query.
</span><span class="cx">   */
</span><span class="lines">@@ -1242,6 +1246,15 @@
</span><span class="cx">                  $filter_sql[] = "a.id >= {$sid_sql}";
</span><span class="cx">          }
</span><span class="cx"> 
</span><ins>+               if ( ! empty( $filter_array['since'] ) ) {
+                       // Validate that this is a proper Y-m-d H:i:s date
+                       // Trick: parse to UNIX date then translate back
+                       $translated_date = date( 'Y-m-d H:i:s', strtotime( $filter_array['since'] ) );
+                       if ( $translated_date === $filter_array['since'] ) {
+                               $filter_sql[] = "a.date_recorded > '{$translated_date}'";
+                       }
+               }
+
</ins><span class="cx">           if ( empty( $filter_sql ) )
</span><span class="cx">                  return false;
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkbpactivitybpactivityfiltersphp"></a>
<div class="modfile"><h4>Modified: trunk/bp-activity/bp-activity-filters.php (8250 => 8251)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/bp-activity/bp-activity-filters.php        2014-04-07 18:30:36 UTC (rev 8250)
+++ trunk/bp-activity/bp-activity-filters.php   2014-04-08 06:55:30 UTC (rev 8251)
</span><span class="lines">@@ -429,7 +429,7 @@
</span><span class="cx"> function bp_activity_newest_class( $classes = '' ) {
</span><span class="cx">  $bp = buddypress();
</span><span class="cx"> 
</span><del>-       if ( ! empty( $bp->activity->new_update_id ) && $bp->activity->new_update_id == bp_get_activity_id() ) {
</del><ins>+        if ( ! empty( $bp->activity->last_recorded ) && $bp->activity->last_recorded == bp_get_activity_date_recorded() ) {
</ins><span class="cx">           $classes .= ' new-update';
</span><span class="cx">  }
</span><span class="cx"> 
</span><span class="lines">@@ -438,12 +438,38 @@
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> /**
</span><ins>+ * Check if Activity Heartbeat feature i on to add a timestamp class.
+ *
+ * @since BuddyPress (2.0.0)
+ *
+ * @param string $classes
+ * @return string $classes
+ */
+function bp_activity_timestamp_class( $classes = '' ) {
+
+       if ( ! bp_activity_do_heartbeat() ) {
+               return $classes;
+       }
+
+       $activity_date = bp_get_activity_date_recorded();
+
+       if ( empty( $activity_date ) ) {
+               return $classes;
+       }
+       
+       $classes .= ' date-recorded-' . strtotime( $activity_date );
+
+       return $classes;
+}
+add_filter( 'bp_get_activity_css_class', 'bp_activity_timestamp_class', 9, 1 );
+
+/**
</ins><span class="cx">  * Use WordPress Heartbeat API to check for latest activity update.
</span><span class="cx">  *
</span><span class="cx">  * @since BuddyPress (2.0.0)
</span><span class="cx">  *
</span><span class="cx">  * @uses bp_activity_get_last_updated() to get the recorded date of the last activity
</span><del>-
</del><ins>+ *
</ins><span class="cx">  * @param array $response
</span><span class="cx">  * @param array $data
</span><span class="cx">  * @return array $response
</span><span class="lines">@@ -451,7 +477,7 @@
</span><span class="cx"> function bp_activity_heartbeat_last_recorded( $response = array(), $data = array() ) {
</span><span class="cx">  $bp = buddypress();
</span><span class="cx"> 
</span><del>-       if ( empty( $data['bp_activity_last_id'] ) ) {
</del><ins>+        if ( empty( $data['bp_activity_last_recorded'] ) ) {
</ins><span class="cx">           return $response;
</span><span class="cx">  }
</span><span class="cx"> 
</span><span class="lines">@@ -459,12 +485,12 @@
</span><span class="cx">  // filters), but force the offset to get only new items
</span><span class="cx">  $activity_latest_args = bp_parse_args(
</span><span class="cx">          bp_ajax_querystring( 'activity' ),
</span><del>-               array( 'offset' => absint( $data['bp_activity_last_id'] ) + 1 ),
</del><ins>+                array( 'since' => date( 'Y-m-d H:i:s', $data['bp_activity_last_recorded'] ) ),
</ins><span class="cx">           'activity_latest_args'
</span><span class="cx">  );
</span><span class="cx"> 
</span><span class="cx">  $newest_activities = array();
</span><del>-       $last_activity_id  = 0;
</del><ins>+        $last_activity_recorded = 0;
</ins><span class="cx"> 
</span><span class="cx">  // Temporarly add a just-posted class for new activity items
</span><span class="cx">  add_filter( 'bp_get_activity_css_class', 'bp_activity_newest_class', 10, 1 );
</span><span class="lines">@@ -474,22 +500,23 @@
</span><span class="cx">          while ( bp_activities() ) {
</span><span class="cx">                  bp_the_activity();
</span><span class="cx"> 
</span><del>-                       if ( $last_activity_id < bp_get_activity_id() ) {
-                               $last_activity_id = bp_get_activity_id();
</del><ins>+                        $atime = strtotime( bp_get_activity_date_recorded() );
+                       if ( $last_activity_recorded < $atime ) {
+                               $last_activity_recorded = $atime;
</ins><span class="cx">                   }
</span><span class="cx"> 
</span><span class="cx">                  bp_get_template_part( 'activity/entry' );
</span><span class="cx">          }
</span><span class="cx">  }
</span><span class="cx"> 
</span><del>-       $newest_activities['activities'] = ob_get_contents();
-       $newest_activities['last_id']    = $last_activity_id;
</del><ins>+        $newest_activities['activities']    = ob_get_contents();
+       $newest_activities['last_recorded'] = $last_activity_recorded;
</ins><span class="cx">   ob_end_clean();
</span><span class="cx"> 
</span><span class="cx">  // Remove the temporary filter
</span><span class="cx">  remove_filter( 'bp_get_activity_css_class', 'bp_activity_newest_class', 10, 1 );
</span><span class="cx"> 
</span><del>-       if ( ! empty( $newest_activities['last_id'] ) ) {
</del><ins>+        if ( ! empty( $newest_activities['last_recorded'] ) ) {
</ins><span class="cx">           $response['bp_activity_newest_activities'] = $newest_activities;
</span><span class="cx">  }
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkbpactivitybpactivitytemplatephp"></a>
<div class="modfile"><h4>Modified: trunk/bp-activity/bp-activity-template.php (8250 => 8251)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/bp-activity/bp-activity-template.php       2014-04-07 18:30:36 UTC (rev 8250)
+++ trunk/bp-activity/bp-activity-template.php  2014-04-08 06:55:30 UTC (rev 8251)
</span><span class="lines">@@ -547,6 +547,7 @@
</span><span class="cx">          'primary_id'        => $primary_id,  // object ID to filter on e.g. a group_id or forum_id or blog_id etc.
</span><span class="cx">          'secondary_id'      => false,        // secondary object ID to filter on e.g. a post_id
</span><span class="cx">          'offset'            => false,        // return only items >= this ID
</span><ins>+               'since'             => false,        // return only items recorded since this Y-m-d H:i:s date
</ins><span class="cx"> 
</span><span class="cx">          'meta_query'        => false,        // filter on activity meta. See WP_Meta_Query for format
</span><span class="cx"> 
</span><span class="lines">@@ -641,8 +642,8 @@
</span><span class="cx">  // into bp-custom.php or your theme's functions.php
</span><span class="cx">  if ( isset( $_GET['afilter'] ) && apply_filters( 'bp_activity_enable_afilter_support', false ) )
</span><span class="cx">          $filter = array( 'object' => $_GET['afilter'] );
</span><del>-       else if ( ! empty( $user_id ) || ! empty( $object ) || ! empty( $action ) || ! empty( $primary_id ) || ! empty( $secondary_id ) || ! empty( $offset ) )
-               $filter = array( 'user_id' => $user_id, 'object' => $object, 'action' => $action, 'primary_id' => $primary_id, 'secondary_id' => $secondary_id, 'offset' => $offset );
</del><ins>+        else if ( ! empty( $user_id ) || ! empty( $object ) || ! empty( $action ) || ! empty( $primary_id ) || ! empty( $secondary_id ) || ! empty( $offset ) || ! empty( $since ) )
+               $filter = array( 'user_id' => $user_id, 'object' => $object, 'action' => $action, 'primary_id' => $primary_id, 'secondary_id' => $secondary_id, 'offset' => $offset, 'since' => $since );
</ins><span class="cx">   else
</span><span class="cx">          $filter = false;
</span><span class="cx"> 
</span><span class="lines">@@ -2805,7 +2806,7 @@
</span><span class="cx">  */
</span><span class="cx"> function bp_activity_recurse_comments_activity_ids( $activity = array(), $activity_ids = array() ) {
</span><span class="cx">  if ( is_array( $activity ) && ! empty( $activity['activities'] ) ) {
</span><del>-               $activity = $activity['activities'][0]; 
</del><ins>+                $activity = $activity['activities'][0];
</ins><span class="cx">   }
</span><span class="cx"> 
</span><span class="cx">  if ( ! empty( $activity->children ) ) {
</span></span></pre></div>
<a id="trunkbptemplatesbplegacybuddypressfunctionsphp"></a>
<div class="modfile"><h4>Modified: trunk/bp-templates/bp-legacy/buddypress-functions.php (8250 => 8251)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/bp-templates/bp-legacy/buddypress-functions.php    2014-04-07 18:30:36 UTC (rev 8250)
+++ trunk/bp-templates/bp-legacy/buddypress-functions.php       2014-04-08 06:55:30 UTC (rev 8251)
</span><span class="lines">@@ -706,10 +706,10 @@
</span><span class="cx">  if ( empty( $activity_id ) )
</span><span class="cx">          exit( '-1<div id="message" class="error"><p>' . __( 'There was a problem posting your update, please try again.', 'buddypress' ) . '</p></div>' );
</span><span class="cx"> 
</span><del>-       $last_id = isset( $_POST['offset'] ) ? absint( $_POST['offset'] ) + 1 : 0;
-       if ( $last_id ) {
-               $activity_args = array( 'offset' => $last_id );
-               $bp->activity->new_update_id = $activity_id;
</del><ins>+        $last_recorded = isset( $_POST['since'] ) ? date( 'Y-m-d H:i:s', intval( $_POST['since'] ) ) : 0;
+       if ( $last_recorded ) {
+               $activity_args = array( 'since' => $last_recorded );
+               $bp->activity->last_recorded = $last_recorded;
</ins><span class="cx">           add_filter( 'bp_get_activity_css_class', 'bp_activity_newest_class', 10, 1 );
</span><span class="cx">  } else {
</span><span class="cx">          $activity_args = array( 'include' => $activity_id );
</span><span class="lines">@@ -722,7 +722,7 @@
</span><span class="cx">          }
</span><span class="cx">  }
</span><span class="cx"> 
</span><del>-       if ( ! empty( $last_id ) ) {
</del><ins>+        if ( ! empty( $last_recorded ) ) {
</ins><span class="cx">           remove_filter( 'bp_get_activity_css_class', 'bp_activity_newest_class', 10, 1 );
</span><span class="cx">  }
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkbptemplatesbplegacyjsbuddypressjs"></a>
<div class="modfile"><h4>Modified: trunk/bp-templates/bp-legacy/js/buddypress.js (8250 => 8251)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/bp-templates/bp-legacy/js/buddypress.js    2014-04-07 18:30:36 UTC (rev 8250)
+++ trunk/bp-templates/bp-legacy/js/buddypress.js       2014-04-08 06:55:30 UTC (rev 8251)
</span><span class="lines">@@ -6,7 +6,7 @@
</span><span class="cx"> 
</span><span class="cx"> // Global variables to temporarly store newest activities
</span><span class="cx"> var newest_activities = '';
</span><del>-var activity_last_id  = 0;
</del><ins>+var activity_last_recorded  = 0;
</ins><span class="cx"> 
</span><span class="cx"> jq(document).ready( function() {
</span><span class="cx">  /**** Page Load Actions *******************************************************/
</span><span class="lines">@@ -91,7 +91,7 @@
</span><span class="cx"> 
</span><span class="cx">  /* New posts */
</span><span class="cx">  jq("#aw-whats-new-submit").on( 'click', function() {
</span><del>-               var last_displayed_id = 0;
</del><ins>+                var last_date_recorded = 0;
</ins><span class="cx">           var button = jq(this);
</span><span class="cx">          var form = button.closest("form#whats-new-form");
</span><span class="cx"> 
</span><span class="lines">@@ -111,13 +111,18 @@
</span><span class="cx">          var item_id = jq("#whats-new-post-in").val();
</span><span class="cx">          var content = jq("#whats-new").val();
</span><span class="cx">          var firstrow = jq( '#buddypress ul.activity-list li' ).first();
</span><ins>+               var activity_row = firstrow;
</ins><span class="cx"> 
</span><del>-               if ( firstrow.hasClass( 'load-newest' ) ) {
-                       last_displayed_id = firstrow.next().prop( 'id' ) ? firstrow.next().prop( 'id' ).replace( 'activity-','' ) : 0;
-               } else {
-                       last_displayed_id = firstrow.prop( 'id' ) ? firstrow.prop( 'id' ).replace( 'activity-','' ) : 0;
</del><ins>+                if ( activity_row.hasClass( 'load-newest' ) ) {
+                       activity_row = firstrow.next();
</ins><span class="cx">           }
</span><span class="cx"> 
</span><ins>+               timestamp = activity_row.prop( 'class' ).match( /date-recorded-([0-9]+)/ );
+               
+               if ( timestamp ) {
+                       last_date_recorded = timestamp[1];
+               }
+
</ins><span class="cx">           /* Set object for non-profile posts */
</span><span class="cx">          if ( item_id > 0 ) {
</span><span class="cx">                  object = jq("#whats-new-post-object").val();
</span><span class="lines">@@ -130,7 +135,7 @@
</span><span class="cx">                  'content': content,
</span><span class="cx">                  'object': object,
</span><span class="cx">                  'item_id': item_id,
</span><del>-                       'offset': last_displayed_id,
</del><ins>+                        'since': last_date_recorded,
</ins><span class="cx">                   '_bp_as_nonce': jq('#_bp_as_nonce').val() || ''
</span><span class="cx">          },
</span><span class="cx">          function(response) {
</span><span class="lines">@@ -157,7 +162,7 @@
</span><span class="cx"> 
</span><span class="cx">                          jq("#activity-stream").prepend(response);
</span><span class="cx"> 
</span><del>-                               if ( ! last_displayed_id )
</del><ins>+                                if ( ! last_date_recorded )
</ins><span class="cx">                                   jq("#activity-stream li:first").addClass('new-update just-posted');
</span><span class="cx"> 
</span><span class="cx">                          if ( 0 != jq("#latest-update").length ) {
</span><span class="lines">@@ -184,7 +189,7 @@
</span><span class="cx"> 
</span><span class="cx">                          // reset vars to get newest activities
</span><span class="cx">                          newest_activities = '';
</span><del>-                               activity_last_id  = 0;
</del><ins>+                                activity_last_recorded  = 0;
</ins><span class="cx">                   }
</span><span class="cx"> 
</span><span class="cx">                  jq("#whats-new-options").animate({
</span><span class="lines">@@ -305,6 +310,7 @@
</span><span class="cx">                  var id        = li.attr('id').substr( 9, li.attr('id').length );
</span><span class="cx">                  var link_href = target.attr('href');
</span><span class="cx">                  var nonce     = link_href.split('_wpnonce=');
</span><ins>+                       var timestamp = li.prop( 'class' ).match( /date-recorded-([0-9]+)/ );
</ins><span class="cx"> 
</span><span class="cx">                  nonce = nonce[1];
</span><span class="cx"> 
</span><span class="lines">@@ -325,9 +331,9 @@
</span><span class="cx">                                  li.slideUp(300);
</span><span class="cx"> 
</span><span class="cx">                                  // reset vars to get newest activities
</span><del>-                                       if ( activity_last_id == id ) {
</del><ins>+                                        if ( timestamp && activity_last_recorded == timestamp[1] ) {
</ins><span class="cx">                                           newest_activities = '';
</span><del>-                                               activity_last_id  = 0;
</del><ins>+                                                activity_last_recorded  = 0;
</ins><span class="cx">                                   }
</span><span class="cx">                          }
</span><span class="cx">                  });
</span><span class="lines">@@ -337,7 +343,8 @@
</span><span class="cx"> 
</span><span class="cx">          // Spam activity stream items
</span><span class="cx">          if ( target.hasClass( 'spam-activity' ) ) {
</span><del>-                       var li = target.parents( 'div.activity ul li' );
</del><ins>+                        var li        = target.parents( 'div.activity ul li' );
+                       var timestamp = li.prop( 'class' ).match( /date-recorded-([0-9]+)/ );
</ins><span class="cx">                   target.addClass( 'loading' );
</span><span class="cx"> 
</span><span class="cx">                  jq.post( ajaxurl, {
</span><span class="lines">@@ -354,9 +361,9 @@
</span><span class="cx">                          } else {
</span><span class="cx">                                  li.slideUp( 300 );
</span><span class="cx">                                  // reset vars to get newest activities
</span><del>-                                       if ( activity_last_id == id ) {
</del><ins>+                                        if ( timestamp && activity_last_recorded == timestamp[1] ) {
</ins><span class="cx">                                           newest_activities = '';
</span><del>-                                               activity_last_id  = 0;
</del><ins>+                                                activity_last_recorded  = 0;
</ins><span class="cx">                                   }
</span><span class="cx">                          }
</span><span class="cx">                  });
</span><span class="lines">@@ -407,6 +414,22 @@
</span><span class="cx">                  event.preventDefault();
</span><span class="cx"> 
</span><span class="cx">                  target.parent().hide();
</span><ins>+
+                       /** 
+                        * If a plugin is updating the recorded_date of an activity 
+                        * it will be loaded as a new one. We need to look in the
+                        * stream and eventually remove similar ids to avoid "double".
+                        */
+                       activity_html = jq.parseHTML( newest_activities );
+                       
+                       jq.each( activity_html, function( i, el ){
+                               if( 'LI' == el.nodeName && jq(el).hasClass( 'just-posted' ) ) {
+                                       if( jq( '#' + jq(el).attr( 'id' ) ).length )
+                                               jq( '#' + jq(el).attr( 'id' ) ).remove();
+                               }
+                       } );
+
+                       // Now the stream is cleaned, prepend newest
</ins><span class="cx">                   jq( '#buddypress ul.activity-list' ).prepend( newest_activities );
</span><span class="cx"> 
</span><span class="cx">                  // reset the newest activities now they're displayed
</span><span class="lines">@@ -1492,21 +1515,26 @@
</span><span class="cx"> 
</span><span class="cx">  // Set the last id to request after
</span><span class="cx">  jq( document ).on( 'heartbeat-send.buddypress', function( e, data ) {
</span><ins>+               
+               firstrow = 0;
</ins><span class="cx"> 
</span><span class="cx">          // First row is default latest activity id
</span><span class="cx">          if ( jq( '#buddypress ul.activity-list li' ).first().prop( 'id' ) ) {
</span><del>-                       firstrow = jq( '#buddypress ul.activity-list li' ).first().prop( 'id' ).replace( 'activity-','' );
-               } else {
-                       firstrow = 0;
</del><ins>+                        // getting the timestamp
+                       timestamp = jq( '#buddypress ul.activity-list li' ).first().prop( 'class' ).match( /date-recorded-([0-9]+)/ );
+
+                       if ( timestamp ) {
+                               firstrow = timestamp[1];
+                       }
</ins><span class="cx">           }
</span><span class="cx"> 
</span><del>-               if ( 0 == activity_last_id || Number( firstrow ) > activity_last_id )
-                       activity_last_id = Number( firstrow );
</del><ins>+                if ( 0 == activity_last_recorded || Number( firstrow ) > activity_last_recorded )
+                       activity_last_recorded = Number( firstrow );
</ins><span class="cx"> 
</span><del>-               data['bp_activity_last_id'] = activity_last_id;
</del><ins>+                data['bp_activity_last_recorded'] = activity_last_recorded;
</ins><span class="cx">   });
</span><span class="cx"> 
</span><del>-       // Increment newest_activities and activity_last_id if data has been returned
</del><ins>+        // Increment newest_activities and activity_last_recorded if data has been returned
</ins><span class="cx">   jq( document ).on( 'heartbeat-tick', function( e, data ) {
</span><span class="cx"> 
</span><span class="cx">          // Only proceed if we have newest activities
</span><span class="lines">@@ -1515,7 +1543,7 @@
</span><span class="cx">          }
</span><span class="cx"> 
</span><span class="cx">          newest_activities = data['bp_activity_newest_activities']['activities'] + newest_activities;
</span><del>-               activity_last_id  = Number( data['bp_activity_newest_activities']['last_id'] );
</del><ins>+                activity_last_recorded  = Number( data['bp_activity_newest_activities']['last_recorded'] );
</ins><span class="cx"> 
</span><span class="cx">          if ( jq( '#buddypress ul.activity-list li' ).first().hasClass( 'load-newest' ) )
</span><span class="cx">                  return;
</span></span></pre></div>
<a id="trunkteststestcasesactivityclassBP_Activity_Activityphp"></a>
<div class="modfile"><h4>Modified: trunk/tests/testcases/activity/class.BP_Activity_Activity.php (8250 => 8251)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/tests/testcases/activity/class.BP_Activity_Activity.php    2014-04-07 18:30:36 UTC (rev 8250)
+++ trunk/tests/testcases/activity/class.BP_Activity_Activity.php       2014-04-08 06:55:30 UTC (rev 8251)
</span><span class="lines">@@ -282,7 +282,35 @@
</span><span class="cx">          $ids = wp_list_pluck( $activity['activities'], 'id' );
</span><span class="cx">          $this->assertEquals( array( $a3, $a2 ), $ids );
</span><span class="cx">  }
</span><ins>+
</ins><span class="cx">   /**
</span><ins>+        * @group get
+        */
+       public function test_get_with_since() {
+               $now = time();
+               $a1 = $this->factory->activity->create( array(
+                       'content' => 'Life Rules',
+                       'recorded_time' => date( 'Y-m-d H:i:s', $now - 100 ),
+               ) );
+               $a2 = $this->factory->activity->create( array(
+                       'content' => 'Life Drools',
+                       'recorded_time' => date( 'Y-m-d H:i:s', $now - 50 ),
+               ) );
+               $a3 = $this->factory->activity->create( array(
+                       'content' => 'Life Drools',
+                       'recorded_time' => date( 'Y-m-d H:i:s', $now - 10 ),
+               ) );
+
+               $activity = BP_Activity_Activity::get( array(
+                       'filter' => array(
+                               'since' => date( 'Y-m-d H:i:s', $now - 70 ),
+                       ),
+               ) );
+               $ids = wp_list_pluck( $activity['activities'], 'id' );
+               $this->assertEquals( array( $a3, $a2 ), $ids );
+       }
+
+       /**
</ins><span class="cx">    * @group get_id
</span><span class="cx">   */
</span><span class="cx">  public function test_get_id_with_item_id() {
</span></span></pre>
</div>
</div>

</body>
</html>