From 8edc01631c0dbceb2dfcdfeca82c5650ae8602c9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fran=C3=A7ois=20Agneray?= <francois.agneray@lam.fr>
Date: Thu, 23 Jun 2022 15:23:40 +0200
Subject: [PATCH] Redirect to instance first webpage

---
 .../components/instance-form.component.html   | 25 --------
 .../components/instance-form.component.ts     |  7 ---
 .../components/instance-navbar.component.html |  2 +-
 .../components/instance-navbar.component.ts   |  9 +++
 .../src/app/instance/home/components/index.ts |  5 --
 .../home/components/welcome.component.html    |  7 ---
 .../home/components/welcome.component.scss    | 17 ------
 .../home/components/welcome.component.spec.ts | 60 -------------------
 .../home/components/welcome.component.ts      | 35 -----------
 .../app/instance/home/home-routing.module.ts  | 31 ----------
 .../src/app/instance/home/home.component.html |  3 -
 .../app/instance/home/home.component.spec.ts  | 48 ---------------
 .../src/app/instance/home/home.component.ts   | 42 -------------
 client/src/app/instance/home/home.module.ts   | 30 ----------
 .../app/instance/instance-routing.module.ts   |  2 -
 .../src/app/instance/instance.component.html  |  1 +
 client/src/app/instance/instance.component.ts | 17 +++++-
 .../app/metamodel/models/instance.model.ts    |  5 --
 .../metamodel/selectors/webpage.selector.ts   | 10 ++++
 conf-dev/create-db.sh                         |  6 +-
 .../__CG__AppEntityInstance.php               | 48 +--------------
 server/src/Action/InstanceAction.php          |  4 --
 server/src/Action/InstanceListAction.php      |  4 --
 server/src/Entity/Instance.php                | 36 -----------
 24 files changed, 44 insertions(+), 410 deletions(-)
 delete mode 100644 client/src/app/instance/home/components/index.ts
 delete mode 100644 client/src/app/instance/home/components/welcome.component.html
 delete mode 100644 client/src/app/instance/home/components/welcome.component.scss
 delete mode 100644 client/src/app/instance/home/components/welcome.component.spec.ts
 delete mode 100644 client/src/app/instance/home/components/welcome.component.ts
 delete mode 100644 client/src/app/instance/home/home-routing.module.ts
 delete mode 100644 client/src/app/instance/home/home.component.html
 delete mode 100644 client/src/app/instance/home/home.component.spec.ts
 delete mode 100644 client/src/app/instance/home/home.component.ts
 delete mode 100644 client/src/app/instance/home/home.module.ts

diff --git a/client/src/app/admin/instance/components/instance-form.component.html b/client/src/app/admin/instance/components/instance-form.component.html
index 531367d9..0dc13e93 100644
--- a/client/src/app/admin/instance/components/instance-form.component.html
+++ b/client/src/app/admin/instance/components/instance-form.component.html
@@ -114,31 +114,6 @@
                 (loadDirectory)="onChangeFileSelect($event)">
             </app-path-select-form-control>
         </accordion-group>
-        <accordion-group heading="Home page" [isOpen]="true">
-            <div class="form-group">
-                <label for="home_component">Component</label>
-                <select class="form-control" name="home_component" formControlName="home_component">
-                    <option value="WelcomeComponent">WelcomeComponent</option>
-                </select>
-            </div>
-            <div formGroupName="home_component_config">
-                <div class="form-group">
-                    <label for="home_component_text">Text</label>
-                    <textarea class="form-control" id="home_component_text" formControlName="home_component_text" rows="3"></textarea>
-                </div>
-                <app-path-select-form-control
-                    [form]="getHomeConfigFormGroup()"
-                    [disabled]="isFilesPathEmpty()"
-                    [controlName]="'home_component_logo'"
-                    [controlLabel]="'Logo'"
-                    [files]="files"
-                    [filesIsLoading]="filesIsLoading"
-                    [filesIsLoaded]="filesIsLoaded"
-                    [selectType]="'file'"
-                    (loadDirectory)="onChangeFileSelect($event)">
-                </app-path-select-form-control>
-            </div>
-        </accordion-group>
         <accordion-group heading="Functionalities" [isOpen]="true">
             <div class="custom-control custom-switch mb-2">
                 <input class="custom-control-input" type="checkbox" id="samp_enabled" name="samp_enabled" formControlName="samp_enabled">
diff --git a/client/src/app/admin/instance/components/instance-form.component.ts b/client/src/app/admin/instance/components/instance-form.component.ts
index 22f1c6d2..00cb22c3 100644
--- a/client/src/app/admin/instance/components/instance-form.component.ts
+++ b/client/src/app/admin/instance/components/instance-form.component.ts
@@ -41,13 +41,6 @@ export class InstanceFormComponent implements OnInit {
         design_background_color: new FormControl('#FFFFFF', [Validators.required]),
         design_logo: new FormControl(''),
         design_favicon: new FormControl(''),
-        home_component: new FormControl('WelcomeComponent', [Validators.required]),
-        home_component_config: new FormGroup({
-            home_component_text: new FormControl(`AstroNomical Information System is a generic web tool aimed 
-at facilitating and homogenizing the implementation of astronomical data. It allows 
-the fast implementation of a project data exchange platform in a dedicated information system.`, [Validators.required]),
-            home_component_logo: new FormControl('home_component_logo.png', [Validators.required])
-        }),
         samp_enabled: new FormControl(true),
         back_to_portal: new FormControl(true),
         search_by_criteria_allowed: new FormControl(true),
diff --git a/client/src/app/instance/components/instance-navbar.component.html b/client/src/app/instance/components/instance-navbar.component.html
index 62d3505b..aa363e91 100644
--- a/client/src/app/instance/components/instance-navbar.component.html
+++ b/client/src/app/instance/components/instance-navbar.component.html
@@ -1,6 +1,6 @@
 <nav class="navbar navbar-light bg-light navbar-expand-md fixed-top border-bottom">
     <!-- Logo -->
-    <a routerLink="/instance/{{ instance.name }}" class="navbar-brand">
+    <a [routerLink]="getInstanceBaseHref()" class="navbar-brand">
         <img *ngIf="instance.public" [src]="getLogoHref()" alt="Instance logo" />
         <img *ngIf="!instance.public" [src]="getLogoHref() | authImage | async" alt="Instance logo" />
     </a>
diff --git a/client/src/app/instance/components/instance-navbar.component.ts b/client/src/app/instance/components/instance-navbar.component.ts
index acc6df6c..d40451e0 100644
--- a/client/src/app/instance/components/instance-navbar.component.ts
+++ b/client/src/app/instance/components/instance-navbar.component.ts
@@ -20,6 +20,7 @@ export class InstanceNavbarComponent {
     @Input() instance: Instance;
     @Input() webpageFamilyList: WebpageFamily[];
     @Input() webpageList: Webpage[];
+    @Input() firstWebpage: Webpage;
     @Output() login: EventEmitter<any> = new EventEmitter();
     @Output() logout: EventEmitter<any> = new EventEmitter();
     @Output() openEditProfile: EventEmitter<any> = new EventEmitter();
@@ -45,6 +46,14 @@ export class InstanceNavbarComponent {
         return 'assets/cesam_anis40.png';
     }
 
+    getInstanceBaseHref() {
+        if (this.firstWebpage) {
+            return `/instance/${this.instance.name}/webpage/${this.firstWebpage.id}`;
+        } else {
+            return `/instance/${this.instance.name}`;
+        }
+    }
+
     getWebpageListByFamily(webpageFamily) {
         return this.webpageList.filter(webpage => webpage.id_webpage_family === webpageFamily.id);
     }
diff --git a/client/src/app/instance/home/components/index.ts b/client/src/app/instance/home/components/index.ts
deleted file mode 100644
index 8973b374..00000000
--- a/client/src/app/instance/home/components/index.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-import { WelcomeComponent } from './welcome.component';
-
-export const dummiesComponents = [
-    WelcomeComponent
-];
diff --git a/client/src/app/instance/home/components/welcome.component.html b/client/src/app/instance/home/components/welcome.component.html
deleted file mode 100644
index f59854e3..00000000
--- a/client/src/app/instance/home/components/welcome.component.html
+++ /dev/null
@@ -1,7 +0,0 @@
-<div class="row align-items-center jumbotron">
-    <div class="col-6 col-md-4 order-md-2 mx-auto text-center">
-        <img *ngIf="instance.public" class="img-fluid mb-3 mb-md-0" [src]="getLogoSrc()" alt="Instance logo">
-        <img *ngIf="!instance.public" class="img-fluid mb-3 mb-md-0" [src]="getLogoSrc() | authImage | async" alt="Instance logo">
-    </div>
-    <div class="col-md-8 order-md-1 text-justify pr-md-5" [innerHtml]="instance.home_component_config.home_component_text"></div>
-</div>
diff --git a/client/src/app/instance/home/components/welcome.component.scss b/client/src/app/instance/home/components/welcome.component.scss
deleted file mode 100644
index 0c0f1a89..00000000
--- a/client/src/app/instance/home/components/welcome.component.scss
+++ /dev/null
@@ -1,17 +0,0 @@
-/**
- * This file is part of Anis Client.
- *
- * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-div.jumbotron p {
-    line-height: 35px;
-}
-
-.img-fluid {
-    height: auto;
-    max-width: 100%;
-}
\ No newline at end of file
diff --git a/client/src/app/instance/home/components/welcome.component.spec.ts b/client/src/app/instance/home/components/welcome.component.spec.ts
deleted file mode 100644
index 71b0e630..00000000
--- a/client/src/app/instance/home/components/welcome.component.spec.ts
+++ /dev/null
@@ -1,60 +0,0 @@
-import { TestBed, waitForAsync, ComponentFixture  } from '@angular/core/testing';
-import { RouterTestingModule } from '@angular/router/testing';
-
-import { WelcomeComponent } from './welcome.component';
-
-describe('[Instance][Home][Component] WelcomeComponent', () => {
-    let component: WelcomeComponent;
-    let fixture: ComponentFixture<WelcomeComponent>;
-
-    beforeEach(waitForAsync(() => {
-        TestBed.configureTestingModule({
-            imports: [RouterTestingModule],
-            declarations: [WelcomeComponent]
-        }).compileComponents();
-        fixture = TestBed.createComponent(WelcomeComponent);
-        component = fixture.componentInstance;
-    }));
-
-    it('should create the component', () => {
-        expect(component).toBeDefined();
-    });
-
-    it('#getLogoSrc() should return logo URL address', () => {
-        component.apiUrl = 'http://test.com';
-        component.instance = {
-            name: 'myInstance',
-            label: 'My Instance',
-            description: 'My Instance description',
-            scientific_manager: 'M. Dupont',
-            instrument: 'Multiple',
-            wavelength_domain: 'Visible',
-            display: 10,
-            data_path: 'data/path',
-            files_path: 'files',
-            public: true,
-            portal_logo: 'logo.png',
-            design_color: 'green',
-            design_background_color: 'darker green',
-            design_logo: '/path/to/logo',
-            design_favicon: '/path/to/favicon',
-            home_component: 'HomeComponent',
-            home_component_config: {
-                home_component_text: 'Description',
-                home_component_logo: '/path/to/logo'
-            },
-            samp_enabled: true,
-            back_to_portal: true,
-            search_by_criteria_allowed: true,
-            search_by_criteria_label: 'Search',
-            search_multiple_allowed: true,
-            search_multiple_label: 'Search multiple',
-            search_multiple_all_datasets_selected: true,
-            documentation_allowed: true,
-            documentation_label: 'Documentation',
-            nb_dataset_families: 1,
-            nb_datasets: 2
-        };
-        expect(component.getLogoSrc()).toBe('http://test.com/instance/myInstance/file-explorer/path/to/logo');
-    });
-});
diff --git a/client/src/app/instance/home/components/welcome.component.ts b/client/src/app/instance/home/components/welcome.component.ts
deleted file mode 100644
index 316ec2b1..00000000
--- a/client/src/app/instance/home/components/welcome.component.ts
+++ /dev/null
@@ -1,35 +0,0 @@
-/**
- * This file is part of Anis Client.
- *
- * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-import { Component, Input } from '@angular/core';
-
-import { Instance } from 'src/app/metamodel/models';
-
-/**
- * @class
- * @classdesc Welcome component.
- */
-@Component({
-    selector: 'app-welcome',
-    styleUrls: ['welcome.component.scss'],
-    templateUrl: 'welcome.component.html'
-})
-export class WelcomeComponent {
-    @Input() instance: Instance;
-    @Input() apiUrl: string;
-
-    /**
-     * Returns the logo url.
-     *
-     * @return string
-     */
-    getLogoSrc(): string {
-        return `${this.apiUrl}/instance/${this.instance.name}/file-explorer${this.instance.home_component_config.home_component_logo}`;
-    }
-}
diff --git a/client/src/app/instance/home/home-routing.module.ts b/client/src/app/instance/home/home-routing.module.ts
deleted file mode 100644
index 539509e8..00000000
--- a/client/src/app/instance/home/home-routing.module.ts
+++ /dev/null
@@ -1,31 +0,0 @@
-/**
- * This file is part of Anis Client.
- *
- * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-import { NgModule } from '@angular/core';
-import { Routes, RouterModule } from '@angular/router';
-
-import { HomeComponent } from './home.component';
-
-const routes: Routes = [
-    { path: '', component: HomeComponent }
-];
-
-/**
- * @class
- * @classdesc Home routing module.
- */
-@NgModule({
-    imports: [RouterModule.forChild(routes)],
-    exports: [RouterModule]
-})
-export class HomeRoutingModule { }
-
-export const routedComponents = [
-    HomeComponent
-];
diff --git a/client/src/app/instance/home/home.component.html b/client/src/app/instance/home/home.component.html
deleted file mode 100644
index 96b9457f..00000000
--- a/client/src/app/instance/home/home.component.html
+++ /dev/null
@@ -1,3 +0,0 @@
-<div class="container">
-    <app-welcome [instance]="instance | async" [apiUrl]="getApiUrl()"></app-welcome>
-</div>
diff --git a/client/src/app/instance/home/home.component.spec.ts b/client/src/app/instance/home/home.component.spec.ts
deleted file mode 100644
index a46d8384..00000000
--- a/client/src/app/instance/home/home.component.spec.ts
+++ /dev/null
@@ -1,48 +0,0 @@
-import { Component, Input } from '@angular/core';
-import { TestBed, waitForAsync, ComponentFixture  } from '@angular/core/testing';
-import { RouterTestingModule } from '@angular/router/testing';
-
-import { provideMockStore, MockStore } from '@ngrx/store/testing';
-
-import { HomeComponent } from './home.component';
-import { AppConfigService } from 'src/app/app-config.service';
-import { Instance } from '../../metamodel/models';
-
-describe('[Instance][Home] HomeComponent', () => {
-    @Component({ selector: '<app-welcome', template: '' })
-    class WelcomeStubComponent {
-        @Input() instance: Instance;
-        @Input() apiUrl: string;
-    }
-
-    let component: HomeComponent;
-    let fixture: ComponentFixture<HomeComponent>;
-    let store: MockStore;
-    let appConfigServiceStub = new AppConfigService();
-
-    beforeEach(waitForAsync(() => {
-        TestBed.configureTestingModule({
-            imports: [RouterTestingModule],
-            declarations: [
-                HomeComponent,
-                WelcomeStubComponent
-            ],
-            providers: [
-                provideMockStore({ }),
-                { provide: AppConfigService, useValue: appConfigServiceStub },
-            ]
-        }).compileComponents();
-        fixture = TestBed.createComponent(HomeComponent);
-        component = fixture.componentInstance;
-        store = TestBed.inject(MockStore);
-    }));
-
-    it('should create the component', () => {
-        expect(component).toBeDefined();
-    });
-
-    it('#getApiUrl() should return API URL address', () => {
-        appConfigServiceStub.apiUrl = 'http://test.com';
-        expect(component.getApiUrl()).toBe('http://test.com');
-    });
-});
diff --git a/client/src/app/instance/home/home.component.ts b/client/src/app/instance/home/home.component.ts
deleted file mode 100644
index 48dbbd6f..00000000
--- a/client/src/app/instance/home/home.component.ts
+++ /dev/null
@@ -1,42 +0,0 @@
-/**
- * This file is part of Anis Client.
- *
- * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-import { Component } from '@angular/core';
-
-import { Store } from '@ngrx/store';
-import { Observable } from 'rxjs';
-
-import { Instance } from 'src/app/metamodel/models';
-import * as instanceSelector from 'src/app/metamodel/selectors/instance.selector';
-import { AppConfigService } from 'src/app/app-config.service';
-
-/**
- * @class
- * @classdesc Home component.
- */
-@Component({
-    selector: 'app-home',
-    templateUrl: 'home.component.html'
-})
-export class HomeComponent {
-    public instance: Observable<Instance>;
-
-    constructor(private store: Store<{ }>, private config: AppConfigService) {
-        this.instance = this.store.select(instanceSelector.selectInstanceByRouteName);
-    }
-
-    /**
-     * Returns API url.
-     *
-     * @return string
-     */
-    getApiUrl(): string {
-        return this.config.apiUrl;
-    }
-}
diff --git a/client/src/app/instance/home/home.module.ts b/client/src/app/instance/home/home.module.ts
deleted file mode 100644
index 0859f9d1..00000000
--- a/client/src/app/instance/home/home.module.ts
+++ /dev/null
@@ -1,30 +0,0 @@
-/**
- * This file is part of Anis Client.
- *
- * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-import { NgModule } from '@angular/core';
-
-import { SharedModule } from 'src/app/shared/shared.module';
-import { HomeRoutingModule, routedComponents } from './home-routing.module';
-import { dummiesComponents } from './components';
-
-/**
- * @class
- * @classdesc Home module.
- */
-@NgModule({
-    imports: [
-        SharedModule,
-        HomeRoutingModule
-    ],
-    declarations: [
-        routedComponents,
-        dummiesComponents
-    ]
-})
-export class HomeModule { }
diff --git a/client/src/app/instance/instance-routing.module.ts b/client/src/app/instance/instance-routing.module.ts
index bd692d77..23bb9010 100644
--- a/client/src/app/instance/instance-routing.module.ts
+++ b/client/src/app/instance/instance-routing.module.ts
@@ -16,8 +16,6 @@ import { InstanceAuthGuard } from './instance-auth.guard';
 const routes: Routes = [
     { 
         path: ':iname', component: InstanceComponent, canActivate: [InstanceAuthGuard], children: [
-            //{ path: '', redirectTo: 'home', pathMatch: 'full' },
-            { path: 'home', loadChildren: () => import('./home/home.module').then(m => m.HomeModule) },
             { path: 'webpage', loadChildren: () => import('./webpage/webpage.module').then(m => m.WebpageModule) },
             { path: 'search', loadChildren: () => import('./search/search.module').then(m => m.SearchModule) },
             { path: 'search-multiple', loadChildren: () => import('./search-multiple/search-multiple.module').then(m => m.SearchMultipleModule) },
diff --git a/client/src/app/instance/instance.component.html b/client/src/app/instance/instance.component.html
index ee88a021..b52a3273 100644
--- a/client/src/app/instance/instance.component.html
+++ b/client/src/app/instance/instance.component.html
@@ -12,6 +12,7 @@
             [instance]="instance | async"
             [webpageFamilyList]="webpageFamilyList | async"
             [webpageList]="webpageList | async"
+            [firstWebpage]="firstWebpage | async"
             (login)="login()"
             (logout)="logout()"
             (openEditProfile)="openEditProfile()">
diff --git a/client/src/app/instance/instance.component.ts b/client/src/app/instance/instance.component.ts
index 57c0bcba..c6d60394 100644
--- a/client/src/app/instance/instance.component.ts
+++ b/client/src/app/instance/instance.component.ts
@@ -9,6 +9,7 @@
 
 import { Component, OnDestroy, OnInit } from '@angular/core';
 import { HttpClient } from '@angular/common/http';
+import { ActivatedRoute, Router } from '@angular/router';
 
 import { Store } from '@ngrx/store';
 import { Observable, Subscription } from 'rxjs';
@@ -52,9 +53,17 @@ export class InstanceComponent implements OnInit, OnDestroy {
     public webpageListIsLoading: Observable<boolean>;
     public webpageListIsLoaded: Observable<boolean>;
     public webpageList: Observable<Webpage[]>;
+    public firstWebpage: Observable<Webpage>;
     public instanceSubscription: Subscription;
+    public firstWebpageSubscription: Subscription;
 
-    constructor(private store: Store<{ }>, private config: AppConfigService, private http: HttpClient) {
+    constructor(
+        private store: Store<{ }>,
+        private config: AppConfigService,
+        private http: HttpClient,
+        private route: ActivatedRoute,
+        private router: Router
+    ) {
         this.instance = store.select(instanceSelector.selectInstanceByRouteName);
         this.isAuthenticated = store.select(authSelector.selectIsAuthenticated);
         this.userProfile = store.select(authSelector.selectUserProfile);
@@ -65,6 +74,7 @@ export class InstanceComponent implements OnInit, OnDestroy {
         this.webpageListIsLoading = store.select(webpageSelector.selectWebpageListIsLoading);
         this.webpageListIsLoaded = store.select(webpageSelector.selectWebpageListIsLoaded);
         this.webpageList = store.select(webpageSelector.selectAllWebpages);
+        this.firstWebpage = store.select(webpageSelector.selectFirstWebpage);
     }
 
     ngOnInit() {
@@ -84,6 +94,11 @@ export class InstanceComponent implements OnInit, OnDestroy {
                 this.body.style.backgroundColor = instance.design_background_color;
             }
         });
+        this.firstWebpageSubscription = this.firstWebpage.subscribe(webpage => {
+            if (webpage) {
+                this.router.navigate(['webpage', webpage.id], { relativeTo: this.route });
+            }
+        });
     }
 
     setFaviconHref(instance: Instance) {
diff --git a/client/src/app/metamodel/models/instance.model.ts b/client/src/app/metamodel/models/instance.model.ts
index 62945722..9a467958 100644
--- a/client/src/app/metamodel/models/instance.model.ts
+++ b/client/src/app/metamodel/models/instance.model.ts
@@ -28,11 +28,6 @@ export interface Instance {
     design_background_color: string;
     design_logo: string;
     design_favicon: string;
-    home_component: string;
-    home_component_config: {
-        home_component_text: string;
-        home_component_logo: string;
-    };
     samp_enabled: boolean;
     back_to_portal: boolean;
     search_by_criteria_allowed: boolean;
diff --git a/client/src/app/metamodel/selectors/webpage.selector.ts b/client/src/app/metamodel/selectors/webpage.selector.ts
index c81396fe..20a5be4d 100644
--- a/client/src/app/metamodel/selectors/webpage.selector.ts
+++ b/client/src/app/metamodel/selectors/webpage.selector.ts
@@ -11,6 +11,7 @@ import { createSelector } from '@ngrx/store';
 
 import * as reducer from '../metamodel.reducer';
 import * as fromWebpage from '../reducers/webpage.reducer';
+import * as webpageFamilySelector from './webpage-family.selector';
 
 export const selectWebpageState = createSelector(
     reducer.getMetamodelState,
@@ -52,3 +53,12 @@ export const selectWebpageByRouteId = createSelector(
     reducer.selectRouterState,
     (entities, router) => entities[router.state.params.id]
 );
+
+export const selectFirstWebpage = createSelector(
+    webpageFamilySelector.selectAllWebpageFamilies,
+    selectAllWebpages,
+    (webpageFamilyList, webpageList) => {
+        const firstWebpageFamily = webpageFamilyList[0];
+        return webpageList.filter(webpage => webpage.id_webpage_family === firstWebpageFamily.id)[0];
+    }
+);
\ No newline at end of file
diff --git a/conf-dev/create-db.sh b/conf-dev/create-db.sh
index 905a1ca6..8a87a2b9 100644
--- a/conf-dev/create-db.sh
+++ b/conf-dev/create-db.sh
@@ -8,7 +8,7 @@ set -e
 curl -d '{"label":"Test","dbname":"anis_test","dbtype":"pdo_pgsql","dbhost":"db","dbport":5432,"dblogin":"anis","dbpassword":"anis"}' --header 'Content-Type: application/json' -X POST http://localhost/database
 
 # Add default instance
-curl -d '{"name":"default","label":"Default instance","description":"Instance for the test","scientific_manager":"M. Durand","instrument":"Multiple","wavelength_domain":"Visible imaging / Spectroscopy","display":10,"data_path":"\/DEFAULT","files_path":"\/INSTANCE_FILES","public":true,"portal_logo":"","design_color":"#7AC29A","design_background_color":"#ffffff","design_logo":"/logo.png","design_favicon":"/favicon.ico","home_component":"WelcomeComponent","home_component_config":{"home_component_text":"AstroNomical Information System","home_component_logo":"/home_component_logo.png"},"samp_enabled":true,"back_to_portal":true,"search_by_criteria_allowed":true,"search_by_criteria_label":"Search","search_multiple_allowed":false,"search_multiple_label":"Search multiple","search_multiple_all_datasets_selected":false,"documentation_allowed":false,"documentation_label":"Documentation"}' --header 'Content-Type: application/json' -X POST http://localhost/instance
+curl -d '{"name":"default","label":"Default instance","description":"Instance for the test","scientific_manager":"M. Durand","instrument":"Multiple","wavelength_domain":"Visible imaging / Spectroscopy","display":10,"data_path":"\/DEFAULT","files_path":"\/INSTANCE_FILES","public":true,"portal_logo":"","design_color":"#7AC29A","design_background_color":"#ffffff","design_logo":"/logo.png","design_favicon":"/favicon.ico","samp_enabled":true,"back_to_portal":true,"search_by_criteria_allowed":true,"search_by_criteria_label":"Search","search_multiple_allowed":false,"search_multiple_label":"Search multiple","search_multiple_all_datasets_selected":false,"documentation_allowed":false,"documentation_label":"Documentation"}' --header 'Content-Type: application/json' -X POST http://localhost/instance
 
 # Add dataset families
 curl -d '{"label":"Default dataset family","display":10,"opened":true}' --header 'Content-Type: application/json' -X POST http://localhost/instance/default/dataset-family
@@ -109,3 +109,7 @@ curl -d '{"id":12,"name":"burst_id","label":"burst_id","form_label":"Burst ID","
 curl -d '{"id":13,"name":"pipeline_version","label":"pipeline_version","form_label":"Pipeline version","description":null,"primary_key":false,"output_display":130,"criteria_display":130,"search_type":null,"type":"float","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"selected":false,"order_by":true,"archive":false,"detail":false,"display_detail":130,"renderer_detail":null,"renderer_detail_config":null,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":null,"id_output_category":7}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/products/attribute
 curl -d '{"id":14,"name":"schema_version","label":"schema_version","form_label":"Schema version","description":null,"primary_key":false,"output_display":140,"criteria_display":140,"search_type":null,"type":"float","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"selected":false,"order_by":true,"archive":false,"detail":false,"display_detail":140,"renderer_detail":null,"renderer_detail_config":null,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":null,"id_output_category":7}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/products/attribute
 curl -d '{"id":15,"name":"src_id","label":"src_id","form_label":"SRC ID","description":null,"primary_key":false,"output_display":150,"criteria_display":150,"search_type":null,"type":"integer","operator":null,"dynamic_operator":true,"min":null,"max":null,"placeholder_min":null,"placeholder_max":null,"renderer":null,"renderer_config":null,"selected":false,"order_by":true,"archive":false,"detail":false,"display_detail":150,"renderer_detail":null,"renderer_detail_config":null,"options":null,"vo_utype":null,"vo_ucd":null,"vo_unit":null,"vo_description":null,"vo_datatype":null,"vo_size":null,"id_criteria_family":null,"id_output_category":7}' --header 'Content-Type: application/json' -X POST http://localhost/dataset/products/attribute
+
+# Add webpages
+curl -d '{"label":"Default","icon":null,"display":10}' --header 'Content-Type: application/json' -X POST http://localhost/instance/default/webpage-family
+curl -d '{"label":"Home","icon":"fas fa-home","display":10,"title":"Home","content":"<div class=\"row align-items-center jumbotron\"><div class=\"col-6 col-md-4 order-md-2 mx-auto text-center\"><img class=\"img-fluid mb-3 mb-md-0\" src=\"http://localhost:8080/instance/default/file-explorer/home_component_logo.png\" alt=\"Instance logo\"></div><div class=\"col-md-8 order-md-1 text-justify pr-md-5\"><h2 class=\"mb-3\">Welcome to the ANIS default instance</h2><p class=\"lead\">This service provides several sub-services to interact with the database.<br>Here is a brief presentation of these sub-services:</p><ul class=\"lead\"><li><a href=\"https://drf-gitlab.cea.fr/svom/sdb/api-import/-/wikis/home\">/import/</a> =&gt; This sub-service allows you to import data L0, L1 or SR3/SR4</li><li>/export-rest =&gt; =&gt; This sub-service allows you to request and export data from the database in json format with a specific URL. To build this URL you could <a class=\"btn btn-warning\" href=\"instance/default/search\">Go to search form</a></li></ul></div></div>"}' --header 'Content-Type: application/json' -X POST http://localhost/webpage-family/1/webpage
diff --git a/server/doctrine-proxy/__CG__AppEntityInstance.php b/server/doctrine-proxy/__CG__AppEntityInstance.php
index bd2aab26..a1f2d42b 100644
--- a/server/doctrine-proxy/__CG__AppEntityInstance.php
+++ b/server/doctrine-proxy/__CG__AppEntityInstance.php
@@ -67,10 +67,10 @@ class Instance extends \App\Entity\Instance implements \Doctrine\ORM\Proxy\Proxy
     public function __sleep()
     {
         if ($this->__isInitialized__) {
-            return ['__isInitialized__', 'name', 'label', 'description', 'scientificManager', 'instrument', 'wavelengthDomain', 'display', 'dataPath', 'filesPath', 'public', 'portalLogo', 'designColor', 'designBackgroundColor', 'designLogo', 'designFavicon', 'homeComponent', 'homeComponentConfig', 'sampEnabled', 'backToPortal', 'searchByCriteriaAllowed', 'searchByCriteriaLabel', 'searchMultipleAllowed', 'searchMultipleLabel', 'searchMultipleAllDatasetsSelected', 'documentationAllowed', 'documentationLabel', 'datasetFamilies'];
+            return ['__isInitialized__', 'name', 'label', 'description', 'scientificManager', 'instrument', 'wavelengthDomain', 'display', 'dataPath', 'filesPath', 'public', 'portalLogo', 'designColor', 'designBackgroundColor', 'designLogo', 'designFavicon', 'sampEnabled', 'backToPortal', 'searchByCriteriaAllowed', 'searchByCriteriaLabel', 'searchMultipleAllowed', 'searchMultipleLabel', 'searchMultipleAllDatasetsSelected', 'documentationAllowed', 'documentationLabel', 'datasetFamilies'];
         }
 
-        return ['__isInitialized__', 'name', 'label', 'description', 'scientificManager', 'instrument', 'wavelengthDomain', 'display', 'dataPath', 'filesPath', 'public', 'portalLogo', 'designColor', 'designBackgroundColor', 'designLogo', 'designFavicon', 'homeComponent', 'homeComponentConfig', 'sampEnabled', 'backToPortal', 'searchByCriteriaAllowed', 'searchByCriteriaLabel', 'searchMultipleAllowed', 'searchMultipleLabel', 'searchMultipleAllDatasetsSelected', 'documentationAllowed', 'documentationLabel', 'datasetFamilies'];
+        return ['__isInitialized__', 'name', 'label', 'description', 'scientificManager', 'instrument', 'wavelengthDomain', 'display', 'dataPath', 'filesPath', 'public', 'portalLogo', 'designColor', 'designBackgroundColor', 'designLogo', 'designFavicon', 'sampEnabled', 'backToPortal', 'searchByCriteriaAllowed', 'searchByCriteriaLabel', 'searchMultipleAllowed', 'searchMultipleLabel', 'searchMultipleAllDatasetsSelected', 'documentationAllowed', 'documentationLabel', 'datasetFamilies'];
     }
 
     /**
@@ -500,50 +500,6 @@ class Instance extends \App\Entity\Instance implements \Doctrine\ORM\Proxy\Proxy
         return parent::setDesignFavicon($designFavicon);
     }
 
-    /**
-     * {@inheritDoc}
-     */
-    public function getHomeComponent()
-    {
-
-        $this->__initializer__ && $this->__initializer__->__invoke($this, 'getHomeComponent', []);
-
-        return parent::getHomeComponent();
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public function setHomeComponent($homeComponent)
-    {
-
-        $this->__initializer__ && $this->__initializer__->__invoke($this, 'setHomeComponent', [$homeComponent]);
-
-        return parent::setHomeComponent($homeComponent);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public function getHomeComponentConfig()
-    {
-
-        $this->__initializer__ && $this->__initializer__->__invoke($this, 'getHomeComponentConfig', []);
-
-        return parent::getHomeComponentConfig();
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public function setHomeComponentConfig($homeComponentConfig)
-    {
-
-        $this->__initializer__ && $this->__initializer__->__invoke($this, 'setHomeComponentConfig', [$homeComponentConfig]);
-
-        return parent::setHomeComponentConfig($homeComponentConfig);
-    }
-
     /**
      * {@inheritDoc}
      */
diff --git a/server/src/Action/InstanceAction.php b/server/src/Action/InstanceAction.php
index 658c17e5..26b7dca1 100644
--- a/server/src/Action/InstanceAction.php
+++ b/server/src/Action/InstanceAction.php
@@ -78,8 +78,6 @@ final class InstanceAction extends AbstractAction
                 'design_background_color',
                 'design_logo',
                 'design_favicon',
-                'home_component',
-                'home_component_config',
                 'samp_enabled',
                 'back_to_portal',
                 'search_by_criteria_allowed',
@@ -138,8 +136,6 @@ final class InstanceAction extends AbstractAction
         $instance->setDesignBackgroundColor($parsedBody['design_background_color']);
         $instance->setDesignLogo($parsedBody['design_logo']);
         $instance->setDesignFavicon($parsedBody['design_favicon']);
-        $instance->setHomeComponent($parsedBody['home_component']);
-        $instance->setHomeComponentConfig($parsedBody['home_component_config']);
         $instance->setSampEnabled($parsedBody['samp_enabled']);
         $instance->setBackToPortal($parsedBody['back_to_portal']);
         $instance->setSearchByCriteriaAllowed($parsedBody['search_by_criteria_allowed']);
diff --git a/server/src/Action/InstanceListAction.php b/server/src/Action/InstanceListAction.php
index 2cf086ed..dce37113 100644
--- a/server/src/Action/InstanceListAction.php
+++ b/server/src/Action/InstanceListAction.php
@@ -78,8 +78,6 @@ final class InstanceListAction extends AbstractAction
                 'design_background_color',
                 'design_logo',
                 'design_favicon',
-                'home_component',
-                'home_component_config',
                 'samp_enabled',
                 'back_to_portal',
                 'search_by_criteria_allowed',
@@ -132,8 +130,6 @@ final class InstanceListAction extends AbstractAction
         $instance->setDesignBackgroundColor($parsedBody['design_background_color']);
         $instance->setDesignLogo($parsedBody['design_logo']);
         $instance->setDesignFavicon($parsedBody['design_favicon']);
-        $instance->setHomeComponent($parsedBody['home_component']);
-        $instance->setHomeComponentConfig($parsedBody['home_component_config']);
         $instance->setSampEnabled($parsedBody['samp_enabled']);
         $instance->setBackToPortal($parsedBody['back_to_portal']);
         $instance->setSearchByCriteriaAllowed($parsedBody['search_by_criteria_allowed']);
diff --git a/server/src/Entity/Instance.php b/server/src/Entity/Instance.php
index 88f42af0..3c43b778 100644
--- a/server/src/Entity/Instance.php
+++ b/server/src/Entity/Instance.php
@@ -129,20 +129,6 @@ class Instance implements \JsonSerializable
      */
     protected $designFavicon;
 
-    /**
-     * @var string
-     *
-     * @Column(type="string", name="home_component", nullable=true)
-     */
-    protected $homeComponent;
-
-    /**
-     * @var array
-     *
-     * @Column(type="json", name="home_component_config", nullable=true)
-     */
-    protected $homeComponentConfig;
-
     /**
      * @var bool
      *
@@ -365,26 +351,6 @@ class Instance implements \JsonSerializable
         $this->designFavicon = $designFavicon;
     }
 
-    public function getHomeComponent()
-    {
-        return $this->homeComponent;
-    }
-
-    public function setHomeComponent($homeComponent)
-    {
-        $this->homeComponent = $homeComponent;
-    }
-
-    public function getHomeComponentConfig()
-    {
-        return $this->homeComponentConfig;
-    }
-
-    public function setHomeComponentConfig($homeComponentConfig)
-    {
-        $this->homeComponentConfig = $homeComponentConfig;
-    }
-
     public function getSampEnabled()
     {
         return $this->sampEnabled;
@@ -507,8 +473,6 @@ class Instance implements \JsonSerializable
             'design_background_color' => $this->getDesignBackgroundColor(),
             'design_logo' => $this->getDesignLogo(),
             'design_favicon' => $this->getDesignFavicon(),
-            'home_component' => $this->getHomeComponent(),
-            'home_component_config' => $this->getHomeComponentConfig(),
             'samp_enabled' => $this->getSampEnabled(),
             'back_to_portal' => $this->getBackToPortal(),
             'search_by_criteria_allowed' => $this->getSearchByCriteriaAllowed(),
-- 
GitLab