mirror of
https://github.com/BookStackApp/BookStack.git
synced 2025-07-30 04:23:11 +03:00
Watching: Prevent issues when watchable or user is deleted
- Adds filtering to the watched items list in notification preferences so that deleted (recycle bin) items are removed via query. - Adds relations and logic to properly remove watches upon user and entity delete events, to old watches in database do not linger. - Adds testing to cover the above. Did not add migration for existing data, since patch will be close to introduction, and lingering DB entries don't open a security concern, just some potential confusion in specific potential scenarios. Probably not work extra migration risk, although could add in future if concerns/issues are found. Related to #4499
This commit is contained in:
@ -12,6 +12,7 @@ use BookStack\Activity\Tools\ActivityLogger;
|
||||
use BookStack\Activity\Tools\UserEntityWatchOptions;
|
||||
use BookStack\Activity\WatchLevels;
|
||||
use BookStack\Entities\Models\Entity;
|
||||
use BookStack\Entities\Tools\TrashCan;
|
||||
use BookStack\Settings\UserNotificationPreferences;
|
||||
use Illuminate\Support\Facades\Notification;
|
||||
use Tests\TestCase;
|
||||
@ -370,4 +371,32 @@ class WatchTest extends TestCase
|
||||
|
||||
$notifications->assertNothingSentTo($editor);
|
||||
}
|
||||
|
||||
public function test_watches_deleted_on_user_delete()
|
||||
{
|
||||
$editor = $this->users->editor();
|
||||
$page = $this->entities->page();
|
||||
|
||||
$watches = new UserEntityWatchOptions($editor, $page);
|
||||
$watches->updateLevelByValue(WatchLevels::COMMENTS);
|
||||
$this->assertDatabaseHas('watches', ['user_id' => $editor->id]);
|
||||
|
||||
$this->asAdmin()->delete($editor->getEditUrl());
|
||||
|
||||
$this->assertDatabaseMissing('watches', ['user_id' => $editor->id]);
|
||||
}
|
||||
|
||||
public function test_watches_deleted_on_item_delete()
|
||||
{
|
||||
$editor = $this->users->editor();
|
||||
$page = $this->entities->page();
|
||||
|
||||
$watches = new UserEntityWatchOptions($editor, $page);
|
||||
$watches->updateLevelByValue(WatchLevels::COMMENTS);
|
||||
$this->assertDatabaseHas('watches', ['watchable_type' => 'page', 'watchable_id' => $page->id]);
|
||||
|
||||
$this->entities->destroy($page);
|
||||
|
||||
$this->assertDatabaseMissing('watches', ['watchable_type' => 'page', 'watchable_id' => $page->id]);
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ use BookStack\Entities\Repos\BookRepo;
|
||||
use BookStack\Entities\Repos\BookshelfRepo;
|
||||
use BookStack\Entities\Repos\ChapterRepo;
|
||||
use BookStack\Entities\Repos\PageRepo;
|
||||
use BookStack\Entities\Tools\TrashCan;
|
||||
use BookStack\Users\Models\User;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
|
||||
@ -197,6 +198,16 @@ class EntityProvider
|
||||
return $draftPage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fully destroy the given entity from the system, bypassing the recycle bin
|
||||
* stage. Still runs through main app deletion logic.
|
||||
*/
|
||||
public function destroy(Entity $entity)
|
||||
{
|
||||
$trash = app()->make(TrashCan::class);
|
||||
$trash->destroyEntity($entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Entity|Entity[] $entities
|
||||
*/
|
||||
|
@ -124,6 +124,23 @@ class UserPreferencesTest extends TestCase
|
||||
$resp->assertDontSee('All Page Updates & Comments');
|
||||
}
|
||||
|
||||
public function test_notification_preferences_dont_error_on_deleted_items()
|
||||
{
|
||||
$editor = $this->users->editor();
|
||||
$book = $this->entities->book();
|
||||
|
||||
$options = new UserEntityWatchOptions($editor, $book);
|
||||
$options->updateLevelByValue(WatchLevels::COMMENTS);
|
||||
|
||||
$this->actingAs($editor)->delete($book->getUrl());
|
||||
$book->refresh();
|
||||
$this->assertNotNull($book->deleted_at);
|
||||
|
||||
$resp = $this->actingAs($editor)->get('/preferences/notifications');
|
||||
$resp->assertOk();
|
||||
$resp->assertDontSee($book->name);
|
||||
}
|
||||
|
||||
public function test_notification_preferences_not_accessible_to_guest()
|
||||
{
|
||||
$this->setSettings(['app-public' => 'true']);
|
||||
|
Reference in New Issue
Block a user