diff --git a/apps/dashboard/lib/Controller/DashboardApiController.php b/apps/dashboard/lib/Controller/DashboardApiController.php index d39441ddb47ba..0fd6130d9c42a 100644 --- a/apps/dashboard/lib/Controller/DashboardApiController.php +++ b/apps/dashboard/lib/Controller/DashboardApiController.php @@ -202,6 +202,7 @@ public function getLayout(): DataResponse { #[NoAdminRequired] #[ApiRoute(verb: 'POST', url: '/api/v3/layout')] public function updateLayout(array $layout): DataResponse { + $layout = $this->service->sanitizeLayout($layout); $this->userConfig->setValueString($this->userId, 'dashboard', 'layout', implode(',', $layout)); return new DataResponse(['layout' => $layout]); } diff --git a/apps/dashboard/lib/Service/DashboardService.php b/apps/dashboard/lib/Service/DashboardService.php index feb8cf307d480..c94617d69417b 100644 --- a/apps/dashboard/lib/Service/DashboardService.php +++ b/apps/dashboard/lib/Service/DashboardService.php @@ -31,12 +31,30 @@ public function __construct( */ public function getLayout(): array { $systemDefault = $this->appConfig->getAppValueString('layout', 'recommendations,spreed,mail,calendar'); - return array_values(array_filter( + return $this->sanitizeLayout( explode(',', $this->userConfig->getValueString($this->userId, 'dashboard', 'layout', $systemDefault)), - fn (string $value) => $value !== '') ); } + /** + * @param list $layout + * @return list + */ + public function sanitizeLayout(array $layout): array { + $seen = []; + $result = []; + foreach ($layout as $value) { + if ($value === '' || isset($seen[$value])) { + continue; + } + + $seen[$value] = true; + $result[] = $value; + } + + return $result; + } + /** * @return list */ diff --git a/apps/dashboard/tests/DashboardServiceTest.php b/apps/dashboard/tests/DashboardServiceTest.php index 4c53644995b1f..15cf3382f403c 100644 --- a/apps/dashboard/tests/DashboardServiceTest.php +++ b/apps/dashboard/tests/DashboardServiceTest.php @@ -44,6 +44,25 @@ protected function setUp(): void { ); } + public function testGetLayoutRemovesEmptyAndDuplicateEntries(): void { + $this->appConfig->method('getAppValueString') + ->with('layout', 'recommendations,spreed,mail,calendar') + ->willReturn('recommendations,spreed,mail,calendar'); + $this->userConfig->method('getValueString') + ->with('alice', 'dashboard', 'layout', 'recommendations,spreed,mail,calendar') + ->willReturn('spreed,,mail,mail,calendar,spreed'); + + $layout = $this->service->getLayout(); + + $this->assertSame(['spreed', 'mail', 'calendar'], $layout); + } + + public function testSanitizeLayoutRemovesEmptyAndDuplicateEntries(): void { + $layout = $this->service->sanitizeLayout(['files', 'calendar', 'files', '', 'mail', 'calendar']); + + $this->assertSame(['files', 'calendar', 'mail'], $layout); + } + public function testGetBirthdate(): void { $user = $this->createMock(IUser::class); $this->userManager->method('get')