From 8af012bc2aaa0dacc64c0f69e78b34a2e0472119 Mon Sep 17 00:00:00 2001
From: Dan Brown <ssddanbrown@googlemail.com>
Date: Sun, 30 Aug 2015 11:47:58 +0100
Subject: [PATCH] Added filtering to activity lists. Fixes #14.

---
 app/Activity.php                     | 26 +++++++++++---
 app/Book.php                         | 11 ------
 app/Entity.php                       | 11 ------
 app/Http/routes.php                  | 18 ++--------
 app/Services/ActivityService.php     | 54 +++++++++++++++++++++++-----
 resources/lang/en/activities.php     | 38 ++++++++++----------
 resources/views/books/show.blade.php |  2 +-
 7 files changed, 90 insertions(+), 70 deletions(-)

diff --git a/app/Activity.php b/app/Activity.php
index 64dd23518..bbbe5e893 100644
--- a/app/Activity.php
+++ b/app/Activity.php
@@ -5,22 +5,31 @@ namespace Oxbow;
 use Illuminate\Database\Eloquent\Model;
 
 /**
- * @property string key
- * @property \User user
+ * @property string  key
+ * @property \User   user
  * @property \Entity entity
- * @property string extra
+ * @property string  extra
  */
 class Activity extends Model
 {
+
+    /**
+     * Get the entity for this activity.
+     * @return bool
+     */
     public function entity()
     {
-        if($this->entity_id) {
+        if ($this->entity_id) {
             return $this->morphTo('entity')->first();
         } else {
             return false;
         }
     }
 
+    /**
+     * Get the user this activity relates to.
+     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
+     */
     public function user()
     {
         return $this->belongsTo('Oxbow\User');
@@ -35,4 +44,13 @@ class Activity extends Model
         return trans('activities.' . $this->key);
     }
 
+    /**
+     * Checks if another Activity matches the general information of another.
+     * @param $activityB
+     * @return bool
+     */
+    public function isSimilarTo($activityB) {
+        return [$this->key, $this->entitiy_type, $this->entitiy_id] === [$activityB->key, $activityB->entitiy_type, $activityB->entitiy_id];
+    }
+
 }
diff --git a/app/Book.php b/app/Book.php
index c4e691431..8a4be213f 100644
--- a/app/Book.php
+++ b/app/Book.php
@@ -37,15 +37,4 @@ class Book extends Entity
         return $pages->sortBy('priority');
     }
 
-    /**
-     * Gets only the most recent activity for this book
-     * @param int $limit
-     * @param int $page
-     * @return mixed
-     */
-    public function recentActivity($limit = 20, $page=0)
-    {
-        return $this->hasMany('Oxbow\Activity')->orderBy('created_at', 'desc')->skip($limit*$page)->take($limit)->get();
-    }
-
 }
diff --git a/app/Entity.php b/app/Entity.php
index a09ba2e45..2e047cbdb 100644
--- a/app/Entity.php
+++ b/app/Entity.php
@@ -44,15 +44,4 @@ class Entity extends Model
         return $this->morphMany('Oxbow\Activity', 'entity')->orderBy('created_at', 'desc');
     }
 
-    /**
-     * Gets only the most recent activity
-     * @param int $limit
-     * @param int $page
-     * @return mixed
-     */
-    public function recentActivity($limit = 20, $page=0)
-    {
-        return $this->activity()->skip($limit*$page)->take($limit)->get();
-    }
-
 }
diff --git a/app/Http/routes.php b/app/Http/routes.php
index 63b0d5262..eb945c72f 100644
--- a/app/Http/routes.php
+++ b/app/Http/routes.php
@@ -1,21 +1,6 @@
 <?php
 
-/*
-|--------------------------------------------------------------------------
-| Application Routes
-|--------------------------------------------------------------------------
-|
-| Here is where you can register all of the routes for an application.
-| It's a breeze. Simply tell Laravel the URIs it should respond to
-| and give it the controller to call when that URI is requested.
-|
-*/
-
-Route::get('/test', function () {
-    return Auth::user()->can('users-edit');
-});
-
-// Authentication routes...
+// Authenticated routes...
 Route::group(['middleware' => 'auth'], function () {
 
     Route::group(['prefix' => 'books'], function () {
@@ -40,6 +25,7 @@ Route::group(['middleware' => 'auth'], function () {
         Route::get('/{bookSlug}/page/{pageSlug}/delete', 'PageController@showDelete');
         Route::put('/{bookSlug}/page/{pageSlug}', 'PageController@update');
         Route::delete('/{bookSlug}/page/{pageSlug}', 'PageController@destroy');
+
         //Revisions
         Route::get('/{bookSlug}/page/{pageSlug}/revisions', 'PageController@showRevisions');
         Route::get('/{bookSlug}/page/{pageSlug}/revisions/{revId}', 'PageController@showRevision');
diff --git a/app/Services/ActivityService.php b/app/Services/ActivityService.php
index 1691f06a1..a4259bf12 100644
--- a/app/Services/ActivityService.php
+++ b/app/Services/ActivityService.php
@@ -23,16 +23,16 @@ class ActivityService
     /**
      * Add activity data to database.
      * @param Entity $entity
-     * @param $activityKey
-     * @param int $bookId
-     * @param bool $extra
+     * @param        $activityKey
+     * @param int    $bookId
+     * @param bool   $extra
      */
     public function add(Entity $entity, $activityKey, $bookId = 0, $extra = false)
     {
         $this->activity->user_id = $this->user->id;
         $this->activity->book_id = $bookId;
         $this->activity->key = strtolower($activityKey);
-        if($extra !== false) {
+        if ($extra !== false) {
             $this->activity->extra = $extra;
         }
         $entity->activity()->save($this->activity);
@@ -41,8 +41,8 @@ class ActivityService
 
     /**
      * Adds a activity history with a message & without binding to a entitiy.
-     * @param $activityKey
-     * @param int $bookId
+     * @param            $activityKey
+     * @param int        $bookId
      * @param bool|false $extra
      */
     public function addMessage($activityKey, $bookId = 0, $extra = false)
@@ -50,7 +50,7 @@ class ActivityService
         $this->activity->user_id = $this->user->id;
         $this->activity->book_id = $bookId;
         $this->activity->key = strtolower($activityKey);
-        if($extra !== false) {
+        if ($extra !== false) {
             $this->activity->extra = $extra;
         }
         $this->activity->save();
@@ -68,7 +68,7 @@ class ActivityService
     public function removeEntity(Entity $entity)
     {
         $activities = $entity->activity;
-        foreach($activities as $activity) {
+        foreach ($activities as $activity) {
             $activity->extra = $entity->name;
             $activity->entity_id = 0;
             $activity->entity_type = null;
@@ -85,7 +85,45 @@ class ActivityService
     public function latest($count = 20, $page = 0)
     {
         return $this->activity->orderBy('created_at', 'desc')
+            ->skip($count * $page)->take($count)->get();
+    }
+
+    /**
+     * Gets the latest activity for an entitiy, Filtering out similar
+     * items to prevent a message activity list.
+     * @param Entity $entity
+     * @param int    $count
+     * @param int    $page
+     * @return array
+     */
+    function entityActivity($entity, $count = 20, $page = 0)
+    {
+        $activity = $entity->hasMany('Oxbow\Activity')->orderBy('created_at', 'desc')
             ->skip($count*$page)->take($count)->get();
+
+        return $this->filterSimilar($activity);
+    }
+
+    /**
+     * Filters out similar acitivity.
+     * @param Activity[] $activity
+     * @return array
+     */
+    protected function filterSimilar($activity) {
+        $newActivity = [];
+        $previousItem = false;
+        foreach($activity as $activityItem) {
+            if($previousItem === false) {
+                $previousItem = $activityItem;
+                $newActivity[] = $activityItem;
+                continue;
+            }
+            if(!$activityItem->isSimilarTo($previousItem)) {
+                $newActivity[] = $activityItem;
+            }
+            $previousItem = $activityItem;
+        }
+        return $newActivity;
     }
 
     /**
diff --git a/resources/lang/en/activities.php b/resources/lang/en/activities.php
index ea18d6693..7a3bcd760 100644
--- a/resources/lang/en/activities.php
+++ b/resources/lang/en/activities.php
@@ -8,31 +8,31 @@ return [
      */
 
     // Pages
-    'page_create' => 'created page',
-    'page_create_notification' => 'Page Successfully Created',
-    'page_update' => 'updated page',
-    'page_update_notification' => 'Page Successfully Updated',
-    'page_delete' => 'deleted page',
-    'page_delete_notification' => 'Page Successfully Created',
-    'page_restore' => 'restored page',
-    'page_restore_notification' => 'Page Successfully Restored',
+    'page_create'                 => 'created page',
+    'page_create_notification'    => 'Page Successfully Created',
+    'page_update'                 => 'updated page',
+    'page_update_notification'    => 'Page Successfully Updated',
+    'page_delete'                 => 'deleted page',
+    'page_delete_notification'    => 'Page Successfully Created',
+    'page_restore'                => 'restored page',
+    'page_restore_notification'   => 'Page Successfully Restored',
 
     // Chapters
-    'chapter_create' => 'created chapter',
+    'chapter_create'              => 'created chapter',
     'chapter_create_notification' => 'Chapter Successfully Created',
-    'chapter_update' => 'updated chapter',
+    'chapter_update'              => 'updated chapter',
     'chapter_update_notification' => 'Chapter Successfully Updated',
-    'chapter_delete' => 'deleted chapter',
+    'chapter_delete'              => 'deleted chapter',
     'chapter_delete_notification' => 'Chapter Successfully Deleted',
 
     // Books
-    'book_create' => 'created book',
-    'book_create_notification' => 'Book Successfully Created',
-    'book_update' => 'updated book',
-    'book_update_notification' => 'Book Successfully Updated',
-    'book_delete' => 'deleted book',
-    'book_delete_notification' => 'Book Successfully Deleted',
-    'book_sort'   => 'sorted book',
-    'book_sort_notification' => 'Book Successfully Re-sorted',
+    'book_create'                 => 'created book',
+    'book_create_notification'    => 'Book Successfully Created',
+    'book_update'                 => 'updated book',
+    'book_update_notification'    => 'Book Successfully Updated',
+    'book_delete'                 => 'deleted book',
+    'book_delete_notification'    => 'Book Successfully Deleted',
+    'book_sort'                   => 'sorted book',
+    'book_sort_notification'      => 'Book Successfully Re-sorted',
 
 ];
\ No newline at end of file
diff --git a/resources/views/books/show.blade.php b/resources/views/books/show.blade.php
index 9b9e00401..4f007e243 100644
--- a/resources/views/books/show.blade.php
+++ b/resources/views/books/show.blade.php
@@ -73,7 +73,7 @@
         <div class="col-md-3 col-md-offset-1">
             <div class="margin-top large"><br></div>
             <h3>Recent Activity</h3>
-            @include('partials/activity-list', ['activity' => $book->recentActivity()])
+            @include('partials/activity-list', ['activity' => Activity::entityActivity($book, 20, 0)])
         </div>
     </div>