3 namespace BookStack\Activity;
5 use BookStack\Activity\Models\Activity;
6 use BookStack\Entities\Models\Book;
7 use BookStack\Entities\Models\Chapter;
8 use BookStack\Entities\Models\Entity;
9 use BookStack\Entities\Models\Page;
10 use BookStack\Permissions\PermissionApplicator;
11 use BookStack\Users\Models\User;
12 use Illuminate\Database\Eloquent\Builder;
13 use Illuminate\Database\Eloquent\Relations\Relation;
17 protected PermissionApplicator $permissions;
19 public function __construct(PermissionApplicator $permissions)
21 $this->permissions = $permissions;
25 * Gets the latest activity.
27 public function latest(int $count = 20, int $page = 0): array
29 $activityList = $this->permissions
30 ->restrictEntityRelationQuery(Activity::query(), 'activities', 'entity_id', 'entity_type')
31 ->orderBy('created_at', 'desc')
32 ->with(['user', 'entity'])
33 ->skip($count * $page)
37 return $this->filterSimilar($activityList);
41 * Gets the latest activity for an entity, Filtering out similar
42 * items to prevent a message activity list.
44 public function entityActivity(Entity $entity, int $count = 20, int $page = 1): array
46 /** @var array<string, int[]> $queryIds */
47 $queryIds = [$entity->getMorphClass() => [$entity->id]];
49 if ($entity instanceof Book) {
50 $queryIds[(new Chapter())->getMorphClass()] = $entity->chapters()->scopes('visible')->pluck('id');
52 if ($entity instanceof Book || $entity instanceof Chapter) {
53 $queryIds[(new Page())->getMorphClass()] = $entity->pages()->scopes('visible')->pluck('id');
56 $query = Activity::query();
57 $query->where(function (Builder $query) use ($queryIds) {
58 foreach ($queryIds as $morphClass => $idArr) {
59 $query->orWhere(function (Builder $innerQuery) use ($morphClass, $idArr) {
60 $innerQuery->where('entity_type', '=', $morphClass)
61 ->whereIn('entity_id', $idArr);
66 $activity = $query->orderBy('created_at', 'desc')
67 ->with(['entity' => function (Relation $query) {
68 $query->withTrashed();
70 ->skip($count * ($page - 1))
74 return $this->filterSimilar($activity);
78 * Get the latest activity for a user, Filtering out similar items.
80 public function userActivity(User $user, int $count = 20, int $page = 0): array
82 $activityList = $this->permissions
83 ->restrictEntityRelationQuery(Activity::query(), 'activities', 'entity_id', 'entity_type')
84 ->orderBy('created_at', 'desc')
85 ->where('user_id', '=', $user->id)
86 ->skip($count * $page)
90 return $this->filterSimilar($activityList);
94 * Filters out similar activity.
96 * @param Activity[] $activities
98 protected function filterSimilar(iterable $activities): array
101 $previousItem = null;
103 foreach ($activities as $activityItem) {
104 if (!$previousItem || !$activityItem->isSimilarTo($previousItem)) {
105 $newActivity[] = $activityItem;
108 $previousItem = $activityItem;