diff --git a/app/Http/Controllers/WidgetController.php b/app/Http/Controllers/WidgetController.php index b7705523..0ddf968c 100644 --- a/app/Http/Controllers/WidgetController.php +++ b/app/Http/Controllers/WidgetController.php @@ -130,10 +130,14 @@ public function activityList(Request $request): JsonResponse $search = $request->input('search', ''); $paginator = Activity::query() - ->with(['causer', 'subject.user', 'subject.comments']) + ->with([ + 'causer', + 'subject.user', + 'subject.comments' => fn ($query) => $query->public(), + ]) ->where(function (Builder $query) use ($search) { $query->whereHasMorph('subject', ['App\Models\Item'], function (Builder $query) use ($search) { - $query->where('private', false); + $query->visibleForCurrentUser(); // Add search filter for items if (!empty($search)) { @@ -141,7 +145,8 @@ public function activityList(Request $request): JsonResponse } }) ->orWhereHasMorph('subject', ['App\Models\Comment'], function (Builder $query) use ($search) { - $query->where('private', false); + $query->public() + ->whereHas('item', fn ($itemQuery) => $itemQuery->visibleForCurrentUser()); // For comments, search the related item title if (!empty($search)) { diff --git a/app/Livewire/Activity.php b/app/Livewire/Activity.php index 8349e26b..f9d5d09a 100644 --- a/app/Livewire/Activity.php +++ b/app/Livewire/Activity.php @@ -24,13 +24,18 @@ public function table(Table $table): Table return $table ->query( ActivityModel::query() - ->with(['causer', 'subject.user', 'subject.comments']) + ->with([ + 'causer', + 'subject.user', + 'subject.comments' => fn ($query) => $query->public(), + ]) ->where(function (Builder $query) { $query->whereHasMorph('subject', ['App\Models\Item'], function (Builder $query) { - $query->where('private', false); + $query->visibleForCurrentUser(); }) ->orWhereHasMorph('subject', ['App\Models\Comment'], function (Builder $query) { - $query->where('private', false); + $query->public() + ->whereHas('item', fn ($itemQuery) => $itemQuery->visibleForCurrentUser()); }); }) ->whereNotNull('causer_id') diff --git a/app/Livewire/Welcome/RecentActivity.php b/app/Livewire/Welcome/RecentActivity.php index 0f26f8a9..079ecea5 100644 --- a/app/Livewire/Welcome/RecentActivity.php +++ b/app/Livewire/Welcome/RecentActivity.php @@ -24,13 +24,18 @@ public function table(Table $table): Table return $table ->query( Activity::query() - ->with(['causer', 'subject.user', 'subject.comments']) + ->with([ + 'causer', + 'subject.user', + 'subject.comments' => fn ($query) => $query->public(), + ]) ->where(function (Builder $query) { $query->whereHasMorph('subject', ['App\Models\Item'], function (Builder $query) { - $query->where('private', false); + $query->visibleForCurrentUser(); }) ->orWhereHasMorph('subject', ['App\Models\Comment'], function (Builder $query) { - $query->where('private', false); + $query->public() + ->whereHas('item', fn ($itemQuery) => $itemQuery->visibleForCurrentUser()); }); }) ->whereNotNull('causer_id') diff --git a/tests/Feature/Livewire/ActivityTest.php b/tests/Feature/Livewire/ActivityTest.php index 3c667c55..7d56ade6 100644 --- a/tests/Feature/Livewire/ActivityTest.php +++ b/tests/Feature/Livewire/ActivityTest.php @@ -2,6 +2,7 @@ use App\Models\Item; use Livewire\Livewire; +use App\Models\Project; use App\Livewire\Activity; use App\Enums\ItemActivity; @@ -95,3 +96,18 @@ $response->assertSee('Created the item'); $response->assertDontSee('Made item private'); }); + +test('activity page does not show activities for items in private projects', function () { + $user = createAndLoginUser(); + $project = Project::factory()->private()->create(); + $item = Item::factory()->create([ + 'project_id' => $project->id, + 'title' => 'Private Project Activity', + 'user_id' => $user->id, + ]); + + ItemActivity::createForItem($item, ItemActivity::Created); + + $this->get(route('activity')) + ->assertDontSee('Private Project Activity'); +}); diff --git a/tests/Feature/Livewire/Welcome/RecentActivityTest.php b/tests/Feature/Livewire/Welcome/RecentActivityTest.php index 2322315f..6f6a3f9f 100644 --- a/tests/Feature/Livewire/Welcome/RecentActivityTest.php +++ b/tests/Feature/Livewire/Welcome/RecentActivityTest.php @@ -4,6 +4,7 @@ use App\Models\User; use Livewire\Livewire; use App\Models\Comment; +use App\Models\Project; use App\Enums\ItemActivity; use Spatie\Activitylog\Models\Activity; use App\Livewire\Welcome\RecentActivity; @@ -55,6 +56,21 @@ $component->assertDontSee('Made item private'); }); +test('recent activity does not show activities for items in private projects', function () { + $user = createAndLoginUser(); + $project = Project::factory()->private()->create(); + $item = Item::factory()->create([ + 'project_id' => $project->id, + 'title' => 'Private Project Recent Activity', + 'user_id' => $user->id, + ]); + + ItemActivity::createForItem($item, ItemActivity::Created); + + Livewire::test(RecentActivity::class) + ->assertDontSee('Private Project Recent Activity'); +}); + test('recent activity does not show activities without causer', function () { $user = createAndLoginUser(); diff --git a/tests/Feature/Widget/WidgetTest.php b/tests/Feature/Widget/WidgetTest.php index 3db6d47b..aacd8bcb 100644 --- a/tests/Feature/Widget/WidgetTest.php +++ b/tests/Feature/Widget/WidgetTest.php @@ -1,6 +1,9 @@ settings->save(); }); +test('activity widget excludes private project items and private note counts', function () { + $activitySettings = app(ActivityWidgetSettings::class); + $activitySettings->enabled = true; + $activitySettings->allowed_domains = []; + $activitySettings->save(); + + $user = createUser(); + $this->actingAs($user); + $privateProject = Project::factory()->private()->create(); + $privateProjectItem = Item::factory()->create([ + 'project_id' => $privateProject->id, + 'title' => 'Private Project Widget Activity', + 'user_id' => $user->id, + ]); + $publicItem = Item::factory()->create([ + 'title' => 'Public Widget Activity', + 'user_id' => $user->id, + ]); + + Comment::factory()->create(['item_id' => $publicItem->id, 'private' => false]); + Comment::factory()->create(['item_id' => $publicItem->id, 'private' => true]); + ItemActivity::createForItem($privateProjectItem, ItemActivity::Created); + ItemActivity::createForItem($publicItem, ItemActivity::Created); + + $response = $this->getJson('/api/activity-widget/activities'); + + $response->assertSuccessful(); + + expect(json_encode($response->json(), JSON_THROW_ON_ERROR)) + ->not->toContain('Private Project Widget Activity'); + + $publicActivity = collect($response->json('activities')) + ->first(fn (array $activity) => str_contains($activity['description'], 'Public Widget Activity')); + + expect($publicActivity)->not->toBeNull() + ->and($publicActivity['comments'])->toBe(1); +}); + test('widget config endpoint returns configuration when enabled', function () { $response = $this->getJson('/api/widget/config');