From 30c269c953b8938195e6e1e3f245f213f93d2045 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fran=C3=A7ois=20Agneray?= <francois.agneray@lam.fr>
Date: Thu, 31 Mar 2022 16:15:04 +0200
Subject: [PATCH] Fixed bugs data path (instance and dataset)

---
 .../data-path-form-control.component.html     |  57 ---
 .../data-path-form-control.component.ts       |  64 ---
 .../admin/admin-shared/components/index.ts    |   6 +-
 ...> path-select-form-control.component.html} |   7 +-
 ... => path-select-form-control.component.ts} |  36 +-
 .../components/instance-form.component.html   |  47 +-
 .../components/instance-form.component.ts     |  11 +-
 .../dataset/dataset-form.component.html       |   9 +-
 .../image/image-form.component.html           |   5 +-
 .../actions/admin-file-explorer.actions.ts    |   6 +-
 .../actions/attribute-distinct.actions.ts     |   6 +-
 .../app/admin/store/actions/column.actions.ts |   6 +-
 .../app/admin/store/actions/table.actions.ts  |   6 +-
 .../home/components/welcome.component.ts      |   2 +-
 client/src/app/instance/instance.component.ts |   2 +-
 .../renderer/download-renderer.component.ts   |   6 +-
 .../components/instance-card.component.ts     |   2 +-
 .../app/shared/components/navbar.component.ts |   2 +-
 conf-dev/create-db.sh                         |   2 +-
 server/app/dependencies.php                   |  12 +-
 server/app/routes.php                         |   8 +-
 .../doctrine-proxy/__CG__AppEntityDataset.php |  40 +-
 .../doctrine-proxy/__CG__AppEntityImage.php   | 415 ++++++++++++++++++
 .../__CG__AppEntityInstance.php               |  26 +-
 server/src/Action/AdminFileExplorerAction.php |   2 +-
 .../src/Action/DatasetFileExplorerAction.php  |  50 ++-
 .../src/Action/DownloadInstanceFileAction.php |  96 ----
 server/src/Action/InstanceAction.php          |   1 +
 ...ion.php => InstanceFileExplorerAction.php} |  72 +--
 server/src/Action/InstanceListAction.php      |   1 +
 server/src/Entity/Instance.php                |  18 +
 server/tests/Action/InstanceActionTest.php    |   1 +
 .../tests/Action/InstanceListActionTest.php   |   1 +
 33 files changed, 663 insertions(+), 362 deletions(-)
 delete mode 100644 client/src/app/admin/admin-shared/components/data-path-form-control.component.html
 delete mode 100644 client/src/app/admin/admin-shared/components/data-path-form-control.component.ts
 rename client/src/app/admin/admin-shared/components/{file-select-form-control.component.html => path-select-form-control.component.html} (86%)
 rename client/src/app/admin/admin-shared/components/{file-select-form-control.component.ts => path-select-form-control.component.ts} (74%)
 create mode 100644 server/doctrine-proxy/__CG__AppEntityImage.php
 delete mode 100644 server/src/Action/DownloadInstanceFileAction.php
 rename server/src/Action/{DownloadFileAction.php => InstanceFileExplorerAction.php} (54%)

diff --git a/client/src/app/admin/admin-shared/components/data-path-form-control.component.html b/client/src/app/admin/admin-shared/components/data-path-form-control.component.html
deleted file mode 100644
index 0c0f9e99..00000000
--- a/client/src/app/admin/admin-shared/components/data-path-form-control.component.html
+++ /dev/null
@@ -1,57 +0,0 @@
-<form [formGroup]="form" novalidate>
-    <div class="form-group">
-        <label for="data_path">Data path</label>
-        <div class="input-group mb-3">
-            <div class="input-group-prepend">
-                <button (click)="openModal(template); $event.stopPropagation()" class="btn btn-outline-secondary" type="button">
-                    <span class="fas fa-folder-open"></span>
-                </button>
-            </div>
-            <input type="text" class="form-control" id="data_path" name="data_path" formControlName="data_path">
-        </div>
-    </div>
-</form>
-
-<ng-template #template>
-    <div class="modal-header">
-        <h4 class="modal-title pull-left">ANIS file explorer</h4>
-    </div>
-    <div>
-        <app-spinner *ngIf="filesIsLoading"></app-spinner>
-
-        <p class="ml-3 mt-3">
-            <span class="far fa-folder"></span>
-            {{ fileExplorerPath }}
-        </p>
-        <div *ngIf="filesIsLoaded" class="table-responsive">
-            <table class="table table-hover" aria-describedby="File explorer">
-                <thead>
-                    <tr>
-                        <th scope="col"></th>
-                        <th scope="col">Name</th>
-                        <th scope="col">Size</th>
-                        <th scope="col">MimeType</th>
-                    </tr>
-                </thead>
-                <tbody>
-                    <tr *ngFor="let fileInfo of files" (click)="dataPathAction(fileInfo)" [class.pointer]="fileInfo.type === 'dir' && fileInfo.name !== '.'">
-                        <td>
-                            <span *ngIf="fileInfo.type === 'dir'"><span class="far fa-folder"></span></span>
-                            <span *ngIf="fileInfo.type === 'file'"><span class="far fa-file"></span></span>
-                        </td>
-                        <td class="align-middle">
-                            {{ fileInfo.name }}
-                        </td>
-                        <td class="align-middle">{{ fileInfo.size | formatFileSize: false }}</td>
-                        <td class="align-middle">{{ fileInfo.mimetype }}</td>
-                    </tr>
-                </tbody>
-            </table>
-        </div>
-    </div>
-    <div class="modal-footer">
-        <button (click)="modalRef.hide()" class="btn btn-danger">Cancel</button>
-        &nbsp;
-        <button [disabled]="fileExplorerPristine" (click)="selectDirectory()" class="btn btn-primary">Select this directory</button>
-    </div>
-</ng-template>
diff --git a/client/src/app/admin/admin-shared/components/data-path-form-control.component.ts b/client/src/app/admin/admin-shared/components/data-path-form-control.component.ts
deleted file mode 100644
index 7201e67a..00000000
--- a/client/src/app/admin/admin-shared/components/data-path-form-control.component.ts
+++ /dev/null
@@ -1,64 +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, Output, TemplateRef, EventEmitter } from '@angular/core';
-import { FormGroup } from '@angular/forms';
-
-import { BsModalService } from 'ngx-bootstrap/modal';
-import { BsModalRef } from 'ngx-bootstrap/modal/bs-modal-ref.service';
-
-import { FileInfo } from 'src/app/admin/store/models';
-
-@Component({
-    selector: 'app-data-path-form-control',
-    templateUrl: 'data-path-form-control.component.html'
-})
-export class DataPathFormControlComponent {
-    @Input() form: FormGroup;
-    @Input() files: FileInfo[];
-    @Input() filesIsLoading: boolean;
-    @Input() filesIsLoaded: boolean;
-    @Output() loadRootDirectory: EventEmitter<string> = new EventEmitter();
-
-    modalRef: BsModalRef;
-    fileExplorerPath = '';
-    fileExplorerPristine = true;
-
-    constructor(private modalService: BsModalService) { }
-
-    openModal(template: TemplateRef<any>) {
-        this.fileExplorerPristine = true;
-        this.fileExplorerPath = this.form.controls.data_path.value;
-        if (!this.fileExplorerPath) {
-            this.fileExplorerPath = '';
-        }
-        this.modalRef = this.modalService.show(template);
-        this.loadRootDirectory.emit(this.fileExplorerPath);
-    }
-
-    dataPathAction(fileInfo: FileInfo): void {
-        if (fileInfo.name === '.' || fileInfo.type !== 'dir') {
-            return;
-        }
-
-        if (fileInfo.name === '..') {
-            this.fileExplorerPath = this.fileExplorerPath.substr(0, this.fileExplorerPath.lastIndexOf("/"));
-        } else {
-            this.fileExplorerPath += `/${fileInfo.name}`;
-        }
-        this.fileExplorerPristine = false;
-        this.loadRootDirectory.emit(this.fileExplorerPath);
-    }
-
-    selectDirectory() {
-        this.form.controls.data_path.setValue(this.fileExplorerPath);
-        this.form.markAsDirty();
-        this.modalRef.hide();
-    }
-}
diff --git a/client/src/app/admin/admin-shared/components/index.ts b/client/src/app/admin/admin-shared/components/index.ts
index 57eea4f1..b60b506e 100644
--- a/client/src/app/admin/admin-shared/components/index.ts
+++ b/client/src/app/admin/admin-shared/components/index.ts
@@ -8,11 +8,9 @@
  */
 
 import { DeleteBtnComponent } from './delete-btn.component';
-import { DataPathFormControlComponent } from './data-path-form-control.component';
-import { FileSelectFormControlComponent } from './file-select-form-control.component';
+import { PathSelectFormControlComponent } from './path-select-form-control.component';
 
 export const adminSharedComponents = [
     DeleteBtnComponent,
-    DataPathFormControlComponent,
-    FileSelectFormControlComponent
+    PathSelectFormControlComponent
 ];
diff --git a/client/src/app/admin/admin-shared/components/file-select-form-control.component.html b/client/src/app/admin/admin-shared/components/path-select-form-control.component.html
similarity index 86%
rename from client/src/app/admin/admin-shared/components/file-select-form-control.component.html
rename to client/src/app/admin/admin-shared/components/path-select-form-control.component.html
index b1f9db74..786c6539 100644
--- a/client/src/app/admin/admin-shared/components/file-select-form-control.component.html
+++ b/client/src/app/admin/admin-shared/components/path-select-form-control.component.html
@@ -37,7 +37,7 @@
                     <tr *ngFor="let fileInfo of files" 
                         (click)="click(fileInfo)" 
                         [class.table-active]="checkFileSelected(fileInfo)"
-                        [class.pointer]="fileInfo.name !== '.'">
+                        [class.pointer]="checkPointer(fileInfo)">
                         <td>
                             <span *ngIf="fileInfo.type === 'dir'"><span class="far fa-folder"></span></span>
                             <span *ngIf="fileInfo.type === 'file'"><span class="far fa-file"></span></span>
@@ -55,6 +55,9 @@
     <div class="modal-footer">
         <button (click)="modalRef.hide()" class="btn btn-danger">Cancel</button>
         &nbsp;
-        <button [disabled]="!fileSelected" (click)="selectFile()" class="btn btn-primary">Select this file</button>
+        <button [disabled]="selectType === 'file' && !fileSelected" (click)="onSubmit()" class="btn btn-primary">
+            <ng-container *ngIf="selectType === 'file'">Select this file</ng-container>
+            <ng-container *ngIf="selectType === 'directory'">Select this directory</ng-container>
+        </button>
     </div>
 </ng-template>
diff --git a/client/src/app/admin/admin-shared/components/file-select-form-control.component.ts b/client/src/app/admin/admin-shared/components/path-select-form-control.component.ts
similarity index 74%
rename from client/src/app/admin/admin-shared/components/file-select-form-control.component.ts
rename to client/src/app/admin/admin-shared/components/path-select-form-control.component.ts
index 5c4d0fc0..4f7926e0 100644
--- a/client/src/app/admin/admin-shared/components/file-select-form-control.component.ts
+++ b/client/src/app/admin/admin-shared/components/path-select-form-control.component.ts
@@ -16,10 +16,10 @@ import { BsModalRef } from 'ngx-bootstrap/modal/bs-modal-ref.service';
 import { FileInfo } from 'src/app/admin/store/models';
 
 @Component({
-    selector: 'app-file-select-form-control',
-    templateUrl: 'file-select-form-control.component.html'
+    selector: 'app-path-select-form-control',
+    templateUrl: 'path-select-form-control.component.html'
 })
-export class FileSelectFormControlComponent implements OnChanges {
+export class PathSelectFormControlComponent implements OnChanges {
     @Input() form: FormGroup;
     @Input() disabled: boolean = false;
     @Input() controlName: string;
@@ -27,6 +27,7 @@ export class FileSelectFormControlComponent implements OnChanges {
     @Input() files: FileInfo[];
     @Input() filesIsLoading: boolean;
     @Input() filesIsLoaded: boolean;
+    @Input() selectType: string;
     @Output() loadDirectory: EventEmitter<string> = new EventEmitter();
     @Output() select: EventEmitter<FileInfo> = new EventEmitter();
 
@@ -77,19 +78,36 @@ export class FileSelectFormControlComponent implements OnChanges {
     }
 
     buildFilePath(fileInfo: FileInfo) {
-        let fileSelected = '';
+        let fullPath = '';
         if (this.fileExplorerPath !== '') {
-            fileSelected += `${this.fileExplorerPath}/`;
+            fullPath += `${this.fileExplorerPath}/`;
+        } else {
+            fullPath += '/';
         }
-        return `${fileSelected}${fileInfo.name}`;
+        return `${fullPath}${fileInfo.name}`;
     }
 
     checkFileSelected(fileInfo: FileInfo) {
-        return this.buildFilePath(fileInfo) === this.fileSelected;
+        return this.buildFilePath(fileInfo) === this.fileSelected && this.selectType === 'file';
     }
 
-    selectFile() {
-        this.form.controls[this.controlName].setValue(this.fileSelected);
+    checkPointer(fileInfo: FileInfo) {
+        if (this.selectType === 'file' && fileInfo.name !== '.') {
+            return true;
+        } else if (this.selectType === 'directory' && fileInfo.type === 'dir' && fileInfo.name !== '.') {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    onSubmit() {
+        if (this.selectType === 'file') {
+            this.form.controls[this.controlName].setValue(this.fileSelected);
+        } else {
+            this.form.controls[this.controlName].setValue(this.fileExplorerPath);
+        }
+        
         this.form.markAsDirty();
         this.select.emit(this.fileInfoSelected);
         this.modalRef.hide();
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 8c9a484c..41712766 100644
--- a/client/src/app/admin/instance/components/instance-form.component.html
+++ b/client/src/app/admin/instance/components/instance-form.component.html
@@ -17,13 +17,27 @@
                 <label for="display">Display</label>
                 <input type="number" class="form-control" id="display" name="display" formControlName="display">
             </div>
-            <app-data-path-form-control
+            <app-path-select-form-control
                 [form]="form"
+                [controlName]="'data_path'"
+                [controlLabel]="'Data path'"
                 [files]="files"
                 [filesIsLoading]="filesIsLoading"
                 [filesIsLoaded]="filesIsLoaded"
-                (loadRootDirectory)="loadRootDirectory.emit($event)">
-            </app-data-path-form-control>
+                [selectType]="'directory'"
+                (loadDirectory)="loadRootDirectory.emit($event)">
+            </app-path-select-form-control>
+            <app-path-select-form-control
+                [form]="form"
+                [disabled]="isDataPathEmpty()"
+                [controlName]="'files_path'"
+                [controlLabel]="'Files path'"
+                [files]="files"
+                [filesIsLoading]="filesIsLoading"
+                [filesIsLoaded]="filesIsLoaded"
+                [selectType]="'directory'"
+                (loadDirectory)="onChangeFilesPath($event)">
+            </app-path-select-form-control>
             <div class="custom-control custom-radio custom-control-inline">
                 <input type="radio" class="custom-control-input" id="public" formControlName="public" [value]="true">
                 <label class="custom-control-label" for="public"><span class="fas fa-globe"></span> Public</label>
@@ -34,16 +48,17 @@
             </div>
         </accordion-group>
         <accordion-group heading="Design" [isOpen]="true">
-            <app-file-select-form-control
+            <app-path-select-form-control
                 [form]="form"
-                [disabled]="isDataPathEmpty()"
+                [disabled]="isFilesPathEmpty()"
                 [controlName]="'portal_logo'"
                 [controlLabel]="'Portal logo'"
                 [files]="files"
                 [filesIsLoading]="filesIsLoading"
                 [filesIsLoaded]="filesIsLoaded"
+                [selectType]="'file'"
                 (loadDirectory)="onChangeFileSelect($event)">
-            </app-file-select-form-control>
+            </app-path-select-form-control>
             <div class="form-row">
                 <div class="form-group col-md-6">
                     <label for="design_color_picker">Instance color (picker)</label>
@@ -64,26 +79,28 @@
                     <input type="text" class="form-control" id="design_background_color_input" [value]="form.value.design_background_color" formControlName="design_background_color">
                 </div>
             </div>
-            <app-file-select-form-control
+            <app-path-select-form-control
                 [form]="form"
-                [disabled]="isDataPathEmpty()"
+                [disabled]="isFilesPathEmpty()"
                 [controlName]="'design_logo'"
                 [controlLabel]="'Logo'"
                 [files]="files"
                 [filesIsLoading]="filesIsLoading"
                 [filesIsLoaded]="filesIsLoaded"
+                [selectType]="'file'"
                 (loadDirectory)="onChangeFileSelect($event)">
-            </app-file-select-form-control>
-            <app-file-select-form-control
+            </app-path-select-form-control>
+            <app-path-select-form-control
                 [form]="form"
-                [disabled]="isDataPathEmpty()"
+                [disabled]="isFilesPathEmpty()"
                 [controlName]="'design_favicon'"
                 [controlLabel]="'Favicon'"
                 [files]="files"
                 [filesIsLoading]="filesIsLoading"
                 [filesIsLoaded]="filesIsLoaded"
+                [selectType]="'file'"
                 (loadDirectory)="onChangeFileSelect($event)">
-            </app-file-select-form-control>
+            </app-path-select-form-control>
         </accordion-group>
         <accordion-group heading="Home page" [isOpen]="true">
             <div class="form-group">
@@ -97,15 +114,17 @@
                     <label for="home_component_text">Text</label>
                     <textarea class="form-control" id="home_component_text" formControlName="home_component_text" rows="3"></textarea>
                 </div>
-                <app-file-select-form-control
+                <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-file-select-form-control>
+                </app-path-select-form-control>
             </div>
         </accordion-group>
         <accordion-group heading="Functionalities" [isOpen]="true">
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 a9d6620b..cd351cb4 100644
--- a/client/src/app/admin/instance/components/instance-form.component.ts
+++ b/client/src/app/admin/instance/components/instance-form.component.ts
@@ -31,6 +31,7 @@ export class InstanceFormComponent implements OnInit {
         description: new FormControl('', [Validators.required]),
         display: new FormControl('', [Validators.required]),
         data_path: new FormControl(''),
+        files_path: new FormControl(''),
         public: new FormControl(true, [Validators.required]),
         portal_logo: new FormControl(''),
         design_color: new FormControl('#7AC29A', [Validators.required]),
@@ -75,8 +76,16 @@ the fast implementation of a project data exchange platform in a dedicated infor
         return this.form.controls.data_path.value == '';
     }
 
+    isFilesPathEmpty() {
+        return this.form.controls.files_path.value == '';
+    }
+
+    onChangeFilesPath(path: string) {
+        this.loadRootDirectory.emit(`${this.form.controls.data_path.value}${path}`)
+    }
+
     onChangeFileSelect(path: string) {
-        this.loadRootDirectory.emit(`${this.form.controls.data_path.value}${path}`);
+        this.loadRootDirectory.emit(`${this.form.controls.data_path.value}${this.form.controls.files_path.value}${path}`);
     }
 
     getHomeConfigFormGroup() {
diff --git a/client/src/app/admin/instance/dataset/components/dataset/dataset-form.component.html b/client/src/app/admin/instance/dataset/components/dataset/dataset-form.component.html
index 425366e6..0200ca1d 100644
--- a/client/src/app/admin/instance/dataset/components/dataset/dataset-form.component.html
+++ b/client/src/app/admin/instance/dataset/components/dataset/dataset-form.component.html
@@ -40,13 +40,16 @@
                 <label for="description">Description</label>
                 <textarea class="form-control" rows="5" id="description" name="description" formControlName="description"></textarea>
             </div>
-            <app-data-path-form-control
+            <app-path-select-form-control
                 [form]="form"
+                [controlName]="'data_path'"
+                [controlLabel]="'Data path'"
                 [files]="files"
                 [filesIsLoading]="filesIsLoading"
                 [filesIsLoaded]="filesIsLoaded"
-                (loadRootDirectory)="onChangeDataPath($event)">
-            </app-data-path-form-control>
+                [selectType]="'directory'"
+                (loadDirectory)="onChangeDataPath($event)">
+            </app-path-select-form-control>
             <div class="form-group">
                 <label for="display">Display</label>
                 <input type="number" class="form-control" id="display" name="display" formControlName="display">
diff --git a/client/src/app/admin/instance/dataset/components/image/image-form.component.html b/client/src/app/admin/instance/dataset/components/image/image-form.component.html
index da9163ce..1c8392e9 100644
--- a/client/src/app/admin/instance/dataset/components/image/image-form.component.html
+++ b/client/src/app/admin/instance/dataset/components/image/image-form.component.html
@@ -1,16 +1,17 @@
 <app-spinner *ngIf="fitsImageLimitsIsLoading"></app-spinner>
 
 <form *ngIf="!fitsImageLimitsIsLoading" [formGroup]="form" (ngSubmit)="submit()" novalidate>
-    <app-file-select-form-control
+    <app-path-select-form-control
         [form]="form"
         [controlName]="'file_path'"
         [controlLabel]="'File path'"
         [files]="files"
         [filesIsLoading]="filesIsLoading"
         [filesIsLoaded]="filesIsLoaded"
+        [selectType]="'file'"
         (loadDirectory)="onChangeFileSelect($event)"
         (select)="onFileSelect($event)">
-    </app-file-select-form-control>
+    </app-path-select-form-control>
     <div class="form-group">
         <label for="file_size">File size</label>
         <input type="number" class="form-control" id="file_size" name="file_size" formControlName="file_size">
diff --git a/client/src/app/admin/store/actions/admin-file-explorer.actions.ts b/client/src/app/admin/store/actions/admin-file-explorer.actions.ts
index d73c963f..e54fda8b 100644
--- a/client/src/app/admin/store/actions/admin-file-explorer.actions.ts
+++ b/client/src/app/admin/store/actions/admin-file-explorer.actions.ts
@@ -11,6 +11,6 @@ import { createAction, props } from '@ngrx/store';
  
 import { FileInfo } from '../models';
 
-export const loadFiles = createAction('[Metamodel] Load Files', props<{ path: string }>());
-export const loadFilesSuccess = createAction('[Metamodel] Load Files Success', props<{ files: FileInfo[] }>());
-export const loadFilesFail = createAction('[Metamodel] Load Files Fail');
\ No newline at end of file
+export const loadFiles = createAction('[Admin] Load Files', props<{ path: string }>());
+export const loadFilesSuccess = createAction('[Admin] Load Files Success', props<{ files: FileInfo[] }>());
+export const loadFilesFail = createAction('[Admin] Load Files Fail');
\ No newline at end of file
diff --git a/client/src/app/admin/store/actions/attribute-distinct.actions.ts b/client/src/app/admin/store/actions/attribute-distinct.actions.ts
index 59f6d37d..0ca8bb7d 100644
--- a/client/src/app/admin/store/actions/attribute-distinct.actions.ts
+++ b/client/src/app/admin/store/actions/attribute-distinct.actions.ts
@@ -11,6 +11,6 @@ import { createAction, props } from '@ngrx/store';
 
 import { Attribute } from 'src/app/metamodel/models';
 
-export const loadAttributeDistinctList = createAction('[Metamodel] Load Attribute Distinct List', props<{ attribute: Attribute }>());
-export const loadAttributeDistinctListSuccess = createAction('[Metamodel] Load Attribute List Distinct Success', props<{ values: string[] }>());
-export const loadAttributeDistinctListFail = createAction('[Metamodel] Load Attribute List Distinct Fail');
+export const loadAttributeDistinctList = createAction('[Admin] Load Attribute Distinct List', props<{ attribute: Attribute }>());
+export const loadAttributeDistinctListSuccess = createAction('[Admin] Load Attribute List Distinct Success', props<{ values: string[] }>());
+export const loadAttributeDistinctListFail = createAction('[Admin] Load Attribute List Distinct Fail');
diff --git a/client/src/app/admin/store/actions/column.actions.ts b/client/src/app/admin/store/actions/column.actions.ts
index 77ccf234..7cfb955a 100644
--- a/client/src/app/admin/store/actions/column.actions.ts
+++ b/client/src/app/admin/store/actions/column.actions.ts
@@ -11,6 +11,6 @@ import { createAction, props } from '@ngrx/store';
 
 import { Column } from '../models';
 
-export const loadColumnList = createAction('[Metamodel] Load Column List');
-export const loadColumnListSuccess = createAction('[Metamodel] Load Column List Success', props<{ columns: Column[] }>());
-export const loadColumnListFail = createAction('[Metamodel] Load Column List Fail');
+export const loadColumnList = createAction('[Admin] Load Column List');
+export const loadColumnListSuccess = createAction('[Admin] Load Column List Success', props<{ columns: Column[] }>());
+export const loadColumnListFail = createAction('[Admin] Load Column List Fail');
diff --git a/client/src/app/admin/store/actions/table.actions.ts b/client/src/app/admin/store/actions/table.actions.ts
index 2e10cc5e..6538d572 100644
--- a/client/src/app/admin/store/actions/table.actions.ts
+++ b/client/src/app/admin/store/actions/table.actions.ts
@@ -9,6 +9,6 @@
 
 import { createAction, props } from '@ngrx/store';
 
-export const loadTableList = createAction('[Metamodel] Load Table List', props<{ idDatabase: number }>());
-export const loadTableListSuccess = createAction('[Metamodel] Load Table List Success', props<{ tables: string[] }>());
-export const loadTableListFail = createAction('[Metamodel] Load Table List Fail');
+export const loadTableList = createAction('[Admin] Load Table List', props<{ idDatabase: number }>());
+export const loadTableListSuccess = createAction('[Admin] Load Table List Success', props<{ tables: string[] }>());
+export const loadTableListFail = createAction('[Admin] Load Table List Fail');
diff --git a/client/src/app/instance/home/components/welcome.component.ts b/client/src/app/instance/home/components/welcome.component.ts
index 6eaf4c53..316ec2b1 100644
--- a/client/src/app/instance/home/components/welcome.component.ts
+++ b/client/src/app/instance/home/components/welcome.component.ts
@@ -30,6 +30,6 @@ export class WelcomeComponent {
      * @return string
      */
     getLogoSrc(): string {
-        return `${this.apiUrl}/download-instance-file/${this.instance.name}/${this.instance.home_component_config.home_component_logo}`;
+        return `${this.apiUrl}/instance/${this.instance.name}/file-explorer${this.instance.home_component_config.home_component_logo}`;
     }
 }
diff --git a/client/src/app/instance/instance.component.ts b/client/src/app/instance/instance.component.ts
index ed464740..fe75d5f4 100644
--- a/client/src/app/instance/instance.component.ts
+++ b/client/src/app/instance/instance.component.ts
@@ -73,7 +73,7 @@ export class InstanceComponent implements OnInit, OnDestroy {
                     this.links.push({ label: instance.documentation_label, icon: 'fas fa-question', routerLink: 'documentation' });
                 }
                 if (instance.design_favicon !== '') {
-                    this.favIcon.href = `${this.config.apiUrl}/download-instance-file/${instance.name}/${instance.design_favicon}`;
+                    this.favIcon.href = `${this.config.apiUrl}/instance/${instance.name}/file-explorer${instance.design_favicon}`;
                 }
                 this.title.innerHTML = instance.label;
             }
diff --git a/client/src/app/instance/search/components/result/renderer/download-renderer.component.ts b/client/src/app/instance/search/components/result/renderer/download-renderer.component.ts
index 9cdb554f..e2cfc9a3 100644
--- a/client/src/app/instance/search/components/result/renderer/download-renderer.component.ts
+++ b/client/src/app/instance/search/components/result/renderer/download-renderer.component.ts
@@ -37,7 +37,11 @@ export class DownloadRendererComponent {
      * @return string
      */
     getHref(): string {
-        return `${getHost(this.appConfig.apiUrl)}/download-file/${this.datasetName}/${this.value}`;
+        let path = this.value;
+        if (path[0] !== '/') {
+            path = '/' + path;
+        }
+        return `${getHost(this.appConfig.apiUrl)}/dataset/${this.datasetName}/file-explorer${path}`;
     }
 
     /**
diff --git a/client/src/app/portal/components/instance-card.component.ts b/client/src/app/portal/components/instance-card.component.ts
index 28263dbc..7269b4ef 100644
--- a/client/src/app/portal/components/instance-card.component.ts
+++ b/client/src/app/portal/components/instance-card.component.ts
@@ -32,6 +32,6 @@ export class InstanceCardComponent {
      * @return string
      */
     getLogoSrc(): string {
-        return `${this.apiUrl}/download-instance-file/${this.instance.name}/${this.instance.portal_logo}`;
+        return `${this.apiUrl}/instance/${this.instance.name}/file-explorer${this.instance.portal_logo}`;
     }
 }
diff --git a/client/src/app/shared/components/navbar.component.ts b/client/src/app/shared/components/navbar.component.ts
index 24d37cb2..5af1e0ea 100644
--- a/client/src/app/shared/components/navbar.component.ts
+++ b/client/src/app/shared/components/navbar.component.ts
@@ -67,7 +67,7 @@ export class NavbarComponent {
      */
     getLogoHref(): string {
         if (this.instance.design_logo) {
-            return `${this.apiUrl}/download-instance-file/${this.instance.name}/${this.instance.design_logo}`;
+            return `${this.apiUrl}/instance/${this.instance.name}/file-explorer${this.instance.design_logo}`;
         }
         return 'assets/cesam_anis40.png';
     }
diff --git a/conf-dev/create-db.sh b/conf-dev/create-db.sh
index fed4add6..28a54e99 100644
--- a/conf-dev/create-db.sh
+++ b/conf-dev/create-db.sh
@@ -60,7 +60,7 @@ curl -d '{"label":"Spectra graph","value":"spectra_graph","display":20,"select_n
 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","display":10,"data_path":"\/DEFAULT","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,"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","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,"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 ANIS, SVOM and IRIS surveys
 curl -d '{"name":"anis_survey","label":"ANIS survey","description":"Survey used for testing","link":"https://anis.lam.fr","manager":"F. Agneray","id_database":1}' --header 'Content-Type: application/json' -X POST http://localhost/survey
diff --git a/server/app/dependencies.php b/server/app/dependencies.php
index fdd15944..366ed502 100644
--- a/server/app/dependencies.php
+++ b/server/app/dependencies.php
@@ -145,6 +145,10 @@ $container->set('App\Action\InstanceAction', function (ContainerInterface $c) {
     return new App\Action\InstanceAction($c->get('em'));
 });
 
+$container->set('App\Action\InstanceFileExplorerAction', function (ContainerInterface $c) {
+    return new App\Action\InstanceFileExplorerAction($c->get('em'), $c->get('settings')['data_path'], $c->get(SETTINGS)['token']);
+});
+
 $container->set('App\Action\DatasetFamilyListAction', function (ContainerInterface $c) {
     return new App\Action\DatasetFamilyListAction($c->get('em'));
 });
@@ -260,11 +264,3 @@ $container->set('App\Action\DownloadArchiveAction', function (ContainerInterface
 $container->set('App\Action\DatasetFileExplorerAction', function (ContainerInterface $c) {
     return new App\Action\DatasetFileExplorerAction($c->get('em'), $c->get('settings')['data_path'], $c->get(SETTINGS)['token']);
 });
-
-$container->set('App\Action\DownloadInstanceFileAction', function (ContainerInterface $c) {
-    return new App\Action\DownloadInstanceFileAction($c->get('em'), $c->get('settings')['data_path']);
-});
-
-$container->set('App\Action\DownloadFileAction', function (ContainerInterface $c) {
-    return new App\Action\DownloadFileAction($c->get('em'), $c->get('settings')['data_path'], $c->get(SETTINGS)['token']);
-});
diff --git a/server/app/routes.php b/server/app/routes.php
index 6247ccc5..b4e79e38 100644
--- a/server/app/routes.php
+++ b/server/app/routes.php
@@ -39,6 +39,7 @@ $app->group('', function (RouteCollectorProxy $group) {
     $group->map([OPTIONS, GET, PUT, DELETE], '/survey/{name}', App\Action\SurveyAction::class);
     $group->map([OPTIONS, GET, POST], '/instance', App\Action\InstanceListAction::class);
     $group->map([OPTIONS, GET, PUT, DELETE], '/instance/{name}', App\Action\InstanceAction::class);
+    $group->map([OPTIONS, GET], '/instance/{name}/file-explorer[{fpath:.*}]', App\Action\InstanceFileExplorerAction::class);
     $group->map([OPTIONS, GET, POST], '/instance/{name}/group', App\Action\GroupListAction::class);
     $group->map([OPTIONS, GET, POST], '/instance/{name}/dataset-family', App\Action\DatasetFamilyListAction::class);
     $group->map([OPTIONS, GET], '/instance/{name}/dataset', App\Action\DatasetListByInstanceAction::class);
@@ -46,6 +47,7 @@ $app->group('', function (RouteCollectorProxy $group) {
     $group->map([OPTIONS, GET, PUT, DELETE], '/dataset-family/{id}', App\Action\DatasetFamilyAction::class);
     $group->map([OPTIONS, GET, POST], '/dataset-family/{id}/dataset', App\Action\DatasetListAction::class);
     $group->map([OPTIONS, GET, PUT, DELETE], '/dataset/{name}', App\Action\DatasetAction::class);
+    $group->map([OPTIONS, GET], '/dataset/{name}/file-explorer[{fpath:.*}]', App\Action\DatasetFileExplorerAction::class);
     $group->map([OPTIONS, GET, POST], '/dataset/{name}/criteria-family', App\Action\CriteriaFamilyListAction::class);
     $group->map([OPTIONS, GET, PUT, DELETE], '/criteria-family/{id}', App\Action\CriteriaFamilyAction::class);
     $group->map([OPTIONS, GET, POST], '/dataset/{name}/output-family', App\Action\OutputFamilyListAction::class);
@@ -76,6 +78,7 @@ $app->group('', function (RouteCollectorProxy $group) {
 // Search actions
 $app->get('/search/{dname}', App\Action\SearchAction::class);
 
+// Result actions
 $app->get('/start-task-create-result/{dname}', App\Action\StartTaskCreateResultAction::class);
 $app->get('/is-result-available/{id}', App\Action\IsResultAvailableAction::class);
 $app->get('/download-result/{dname}/{id}', App\Action\DownloadResultAction::class);
@@ -84,8 +87,3 @@ $app->get('/download-result/{dname}/{id}', App\Action\DownloadResultAction::clas
 $app->get('/start-task-create-archive/{dname}', App\Action\StartTaskCreateArchiveAction::class);
 $app->get('/is-archive-available/{id}', App\Action\IsArchiveAvailableAction::class);
 $app->get('/download-archive/{dname}/{id}', App\Action\DownloadArchiveAction::class);
-
-// Explore and download individual files
-$app->get('/dataset-file-explorer/{dname}[{fpath:.*}]', App\Action\DatasetFileExplorerAction::class);
-$app->get('/download-instance-file/{iname}/[{fpath:.*}]', App\Action\DownloadInstanceFileAction::class);
-$app->get('/download-file/{dname}/[{fpath:.*}]', App\Action\DownloadFileAction::class);
diff --git a/server/doctrine-proxy/__CG__AppEntityDataset.php b/server/doctrine-proxy/__CG__AppEntityDataset.php
index eb60eb62..8b3dd799 100644
--- a/server/doctrine-proxy/__CG__AppEntityDataset.php
+++ b/server/doctrine-proxy/__CG__AppEntityDataset.php
@@ -67,10 +67,10 @@ class Dataset extends \App\Entity\Dataset implements \Doctrine\ORM\Proxy\Proxy
     public function __sleep()
     {
         if ($this->__isInitialized__) {
-            return ['__isInitialized__', 'name', 'tableRef', 'label', 'description', 'display', 'dataPath', 'public', 'infoSurveyEnabled', 'infoSurveyLabel', 'coneSearchEnabled', 'coneSearchOpened', 'coneSearchColumnRa', 'coneSearchColumnDec', 'coneSearchPlotEnabled', 'coneSearchPlotOpened', 'downloadEnabled', 'downloadOpened', 'downloadCsv', 'downloadAscii', 'downloadVo', 'downloadArchive', 'summaryEnabled', 'summaryOpened', 'serverLinkEnabled', 'serverLinkOpened', 'datatableEnabled', 'datatableOpened', 'datatableSelectableRows', 'survey', 'datasetFamily', 'attributes'];
+            return ['__isInitialized__', 'name', 'tableRef', 'label', 'description', 'display', 'dataPath', 'public', 'infoSurveyEnabled', 'infoSurveyLabel', 'coneSearchEnabled', 'coneSearchOpened', 'coneSearchColumnRa', 'coneSearchColumnDec', 'coneSearchPlotEnabled', 'downloadEnabled', 'downloadOpened', 'downloadJson', 'downloadCsv', 'downloadAscii', 'downloadVo', 'downloadArchive', 'summaryEnabled', 'summaryOpened', 'serverLinkEnabled', 'serverLinkOpened', 'datatableEnabled', 'datatableOpened', 'datatableSelectableRows', 'survey', 'datasetFamily', 'attributes'];
         }
 
-        return ['__isInitialized__', 'name', 'tableRef', 'label', 'description', 'display', 'dataPath', 'public', 'infoSurveyEnabled', 'infoSurveyLabel', 'coneSearchEnabled', 'coneSearchOpened', 'coneSearchColumnRa', 'coneSearchColumnDec', 'coneSearchPlotEnabled', 'coneSearchPlotOpened', 'downloadEnabled', 'downloadOpened', 'downloadCsv', 'downloadAscii', 'downloadVo', 'downloadArchive', 'summaryEnabled', 'summaryOpened', 'serverLinkEnabled', 'serverLinkOpened', 'datatableEnabled', 'datatableOpened', 'datatableSelectableRows', 'survey', 'datasetFamily', 'attributes'];
+        return ['__isInitialized__', 'name', 'tableRef', 'label', 'description', 'display', 'dataPath', 'public', 'infoSurveyEnabled', 'infoSurveyLabel', 'coneSearchEnabled', 'coneSearchOpened', 'coneSearchColumnRa', 'coneSearchColumnDec', 'coneSearchPlotEnabled', 'downloadEnabled', 'downloadOpened', 'downloadJson', 'downloadCsv', 'downloadAscii', 'downloadVo', 'downloadArchive', 'summaryEnabled', 'summaryOpened', 'serverLinkEnabled', 'serverLinkOpened', 'datatableEnabled', 'datatableOpened', 'datatableSelectableRows', 'survey', 'datasetFamily', 'attributes'];
     }
 
     /**
@@ -492,67 +492,67 @@ class Dataset extends \App\Entity\Dataset implements \Doctrine\ORM\Proxy\Proxy
     /**
      * {@inheritDoc}
      */
-    public function getConeSearchPlotOpened()
+    public function getDownloadEnabled()
     {
 
-        $this->__initializer__ && $this->__initializer__->__invoke($this, 'getConeSearchPlotOpened', []);
+        $this->__initializer__ && $this->__initializer__->__invoke($this, 'getDownloadEnabled', []);
 
-        return parent::getConeSearchPlotOpened();
+        return parent::getDownloadEnabled();
     }
 
     /**
      * {@inheritDoc}
      */
-    public function setConeSearchPlotOpened($coneSearchPlotOpened)
+    public function setDownloadEnabled($downloadEnabled)
     {
 
-        $this->__initializer__ && $this->__initializer__->__invoke($this, 'setConeSearchPlotOpened', [$coneSearchPlotOpened]);
+        $this->__initializer__ && $this->__initializer__->__invoke($this, 'setDownloadEnabled', [$downloadEnabled]);
 
-        return parent::setConeSearchPlotOpened($coneSearchPlotOpened);
+        return parent::setDownloadEnabled($downloadEnabled);
     }
 
     /**
      * {@inheritDoc}
      */
-    public function getDownloadEnabled()
+    public function getDownloadOpened()
     {
 
-        $this->__initializer__ && $this->__initializer__->__invoke($this, 'getDownloadEnabled', []);
+        $this->__initializer__ && $this->__initializer__->__invoke($this, 'getDownloadOpened', []);
 
-        return parent::getDownloadEnabled();
+        return parent::getDownloadOpened();
     }
 
     /**
      * {@inheritDoc}
      */
-    public function setDownloadEnabled($downloadEnabled)
+    public function setDownloadOpened($downloadOpened)
     {
 
-        $this->__initializer__ && $this->__initializer__->__invoke($this, 'setDownloadEnabled', [$downloadEnabled]);
+        $this->__initializer__ && $this->__initializer__->__invoke($this, 'setDownloadOpened', [$downloadOpened]);
 
-        return parent::setDownloadEnabled($downloadEnabled);
+        return parent::setDownloadOpened($downloadOpened);
     }
 
     /**
      * {@inheritDoc}
      */
-    public function getDownloadOpened()
+    public function getDownloadJson()
     {
 
-        $this->__initializer__ && $this->__initializer__->__invoke($this, 'getDownloadOpened', []);
+        $this->__initializer__ && $this->__initializer__->__invoke($this, 'getDownloadJson', []);
 
-        return parent::getDownloadOpened();
+        return parent::getDownloadJson();
     }
 
     /**
      * {@inheritDoc}
      */
-    public function setDownloadOpened($downloadOpened)
+    public function setDownloadJson($downloadJson)
     {
 
-        $this->__initializer__ && $this->__initializer__->__invoke($this, 'setDownloadOpened', [$downloadOpened]);
+        $this->__initializer__ && $this->__initializer__->__invoke($this, 'setDownloadJson', [$downloadJson]);
 
-        return parent::setDownloadOpened($downloadOpened);
+        return parent::setDownloadJson($downloadJson);
     }
 
     /**
diff --git a/server/doctrine-proxy/__CG__AppEntityImage.php b/server/doctrine-proxy/__CG__AppEntityImage.php
new file mode 100644
index 00000000..31bc931b
--- /dev/null
+++ b/server/doctrine-proxy/__CG__AppEntityImage.php
@@ -0,0 +1,415 @@
+<?php
+
+namespace DoctrineProxies\__CG__\App\Entity;
+
+
+/**
+ * DO NOT EDIT THIS FILE - IT WAS CREATED BY DOCTRINE'S PROXY GENERATOR
+ */
+class Image extends \App\Entity\Image implements \Doctrine\ORM\Proxy\Proxy
+{
+    /**
+     * @var \Closure the callback responsible for loading properties in the proxy object. This callback is called with
+     *      three parameters, being respectively the proxy object to be initialized, the method that triggered the
+     *      initialization process and an array of ordered parameters that were passed to that method.
+     *
+     * @see \Doctrine\Common\Proxy\Proxy::__setInitializer
+     */
+    public $__initializer__;
+
+    /**
+     * @var \Closure the callback responsible of loading properties that need to be copied in the cloned object
+     *
+     * @see \Doctrine\Common\Proxy\Proxy::__setCloner
+     */
+    public $__cloner__;
+
+    /**
+     * @var boolean flag indicating if this object was already initialized
+     *
+     * @see \Doctrine\Persistence\Proxy::__isInitialized
+     */
+    public $__isInitialized__ = false;
+
+    /**
+     * @var array<string, null> properties to be lazy loaded, indexed by property name
+     */
+    public static $lazyPropertiesNames = array (
+);
+
+    /**
+     * @var array<string, mixed> default values of properties to be lazy loaded, with keys being the property names
+     *
+     * @see \Doctrine\Common\Proxy\Proxy::__getLazyProperties
+     */
+    public static $lazyPropertiesDefaults = array (
+);
+
+
+
+    public function __construct(?\Closure $initializer = null, ?\Closure $cloner = null)
+    {
+
+        $this->__initializer__ = $initializer;
+        $this->__cloner__      = $cloner;
+    }
+
+
+
+
+
+
+
+    /**
+     * 
+     * @return array
+     */
+    public function __sleep()
+    {
+        if ($this->__isInitialized__) {
+            return ['__isInitialized__', 'id', 'filePath', 'fileSize', 'raMin', 'raMax', 'decMin', 'decMax', 'stretch', 'pmin', 'pmax', 'dataset'];
+        }
+
+        return ['__isInitialized__', 'id', 'filePath', 'fileSize', 'raMin', 'raMax', 'decMin', 'decMax', 'stretch', 'pmin', 'pmax', 'dataset'];
+    }
+
+    /**
+     * 
+     */
+    public function __wakeup()
+    {
+        if ( ! $this->__isInitialized__) {
+            $this->__initializer__ = function (Image $proxy) {
+                $proxy->__setInitializer(null);
+                $proxy->__setCloner(null);
+
+                $existingProperties = get_object_vars($proxy);
+
+                foreach ($proxy::$lazyPropertiesDefaults as $property => $defaultValue) {
+                    if ( ! array_key_exists($property, $existingProperties)) {
+                        $proxy->$property = $defaultValue;
+                    }
+                }
+            };
+
+        }
+    }
+
+    /**
+     * 
+     */
+    public function __clone()
+    {
+        $this->__cloner__ && $this->__cloner__->__invoke($this, '__clone', []);
+    }
+
+    /**
+     * Forces initialization of the proxy
+     */
+    public function __load()
+    {
+        $this->__initializer__ && $this->__initializer__->__invoke($this, '__load', []);
+    }
+
+    /**
+     * {@inheritDoc}
+     * @internal generated method: use only when explicitly handling proxy specific loading logic
+     */
+    public function __isInitialized()
+    {
+        return $this->__isInitialized__;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @internal generated method: use only when explicitly handling proxy specific loading logic
+     */
+    public function __setInitialized($initialized)
+    {
+        $this->__isInitialized__ = $initialized;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @internal generated method: use only when explicitly handling proxy specific loading logic
+     */
+    public function __setInitializer(\Closure $initializer = null)
+    {
+        $this->__initializer__ = $initializer;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @internal generated method: use only when explicitly handling proxy specific loading logic
+     */
+    public function __getInitializer()
+    {
+        return $this->__initializer__;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @internal generated method: use only when explicitly handling proxy specific loading logic
+     */
+    public function __setCloner(\Closure $cloner = null)
+    {
+        $this->__cloner__ = $cloner;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @internal generated method: use only when explicitly handling proxy specific cloning logic
+     */
+    public function __getCloner()
+    {
+        return $this->__cloner__;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @internal generated method: use only when explicitly handling proxy specific loading logic
+     * @deprecated no longer in use - generated code now relies on internal components rather than generated public API
+     * @static
+     */
+    public function __getLazyProperties()
+    {
+        return self::$lazyPropertiesDefaults;
+    }
+
+    
+    /**
+     * {@inheritDoc}
+     */
+    public function getId()
+    {
+        if ($this->__isInitialized__ === false) {
+            return (int)  parent::getId();
+        }
+
+
+        $this->__initializer__ && $this->__initializer__->__invoke($this, 'getId', []);
+
+        return parent::getId();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getFilePath()
+    {
+
+        $this->__initializer__ && $this->__initializer__->__invoke($this, 'getFilePath', []);
+
+        return parent::getFilePath();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function setFilePath($filePath)
+    {
+
+        $this->__initializer__ && $this->__initializer__->__invoke($this, 'setFilePath', [$filePath]);
+
+        return parent::setFilePath($filePath);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getFileSize()
+    {
+
+        $this->__initializer__ && $this->__initializer__->__invoke($this, 'getFileSize', []);
+
+        return parent::getFileSize();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function setFileSize($fileSize)
+    {
+
+        $this->__initializer__ && $this->__initializer__->__invoke($this, 'setFileSize', [$fileSize]);
+
+        return parent::setFileSize($fileSize);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getRaMin()
+    {
+
+        $this->__initializer__ && $this->__initializer__->__invoke($this, 'getRaMin', []);
+
+        return parent::getRaMin();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function setRaMin($raMin)
+    {
+
+        $this->__initializer__ && $this->__initializer__->__invoke($this, 'setRaMin', [$raMin]);
+
+        return parent::setRaMin($raMin);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getRaMax()
+    {
+
+        $this->__initializer__ && $this->__initializer__->__invoke($this, 'getRaMax', []);
+
+        return parent::getRaMax();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function setRaMax($raMax)
+    {
+
+        $this->__initializer__ && $this->__initializer__->__invoke($this, 'setRaMax', [$raMax]);
+
+        return parent::setRaMax($raMax);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getDecMin()
+    {
+
+        $this->__initializer__ && $this->__initializer__->__invoke($this, 'getDecMin', []);
+
+        return parent::getDecMin();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function setDecMin($decMin)
+    {
+
+        $this->__initializer__ && $this->__initializer__->__invoke($this, 'setDecMin', [$decMin]);
+
+        return parent::setDecMin($decMin);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getDecMax()
+    {
+
+        $this->__initializer__ && $this->__initializer__->__invoke($this, 'getDecMax', []);
+
+        return parent::getDecMax();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function setDecMax($decMax)
+    {
+
+        $this->__initializer__ && $this->__initializer__->__invoke($this, 'setDecMax', [$decMax]);
+
+        return parent::setDecMax($decMax);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getStretch()
+    {
+
+        $this->__initializer__ && $this->__initializer__->__invoke($this, 'getStretch', []);
+
+        return parent::getStretch();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function setStretch($stretch)
+    {
+
+        $this->__initializer__ && $this->__initializer__->__invoke($this, 'setStretch', [$stretch]);
+
+        return parent::setStretch($stretch);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getPmin()
+    {
+
+        $this->__initializer__ && $this->__initializer__->__invoke($this, 'getPmin', []);
+
+        return parent::getPmin();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function setPmin($pmin)
+    {
+
+        $this->__initializer__ && $this->__initializer__->__invoke($this, 'setPmin', [$pmin]);
+
+        return parent::setPmin($pmin);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getPmax()
+    {
+
+        $this->__initializer__ && $this->__initializer__->__invoke($this, 'getPmax', []);
+
+        return parent::getPmax();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function setPmax($pmax)
+    {
+
+        $this->__initializer__ && $this->__initializer__->__invoke($this, 'setPmax', [$pmax]);
+
+        return parent::setPmax($pmax);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getDataset()
+    {
+
+        $this->__initializer__ && $this->__initializer__->__invoke($this, 'getDataset', []);
+
+        return parent::getDataset();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function jsonSerialize(): array
+    {
+
+        $this->__initializer__ && $this->__initializer__->__invoke($this, 'jsonSerialize', []);
+
+        return parent::jsonSerialize();
+    }
+
+}
diff --git a/server/doctrine-proxy/__CG__AppEntityInstance.php b/server/doctrine-proxy/__CG__AppEntityInstance.php
index d5f1d1d1..7b3ee869 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', 'display', 'dataPath', 'public', 'portalLogo', 'designColor', 'designBackgroundColor', 'designLogo', 'designFavicon', 'homeComponent', 'homeComponentConfig', 'sampEnabled', 'searchByCriteriaAllowed', 'searchByCriteriaLabel', 'searchMultipleAllowed', 'searchMultipleLabel', 'searchMultipleAllDatasetsSelected', 'documentationAllowed', 'documentationLabel', 'datasetFamilies'];
+            return ['__isInitialized__', 'name', 'label', 'description', 'display', 'dataPath', 'filesPath', 'public', 'portalLogo', 'designColor', 'designBackgroundColor', 'designLogo', 'designFavicon', 'homeComponent', 'homeComponentConfig', 'sampEnabled', 'searchByCriteriaAllowed', 'searchByCriteriaLabel', 'searchMultipleAllowed', 'searchMultipleLabel', 'searchMultipleAllDatasetsSelected', 'documentationAllowed', 'documentationLabel', 'datasetFamilies'];
         }
 
-        return ['__isInitialized__', 'name', 'label', 'description', 'display', 'dataPath', 'public', 'portalLogo', 'designColor', 'designBackgroundColor', 'designLogo', 'designFavicon', 'homeComponent', 'homeComponentConfig', 'sampEnabled', 'searchByCriteriaAllowed', 'searchByCriteriaLabel', 'searchMultipleAllowed', 'searchMultipleLabel', 'searchMultipleAllDatasetsSelected', 'documentationAllowed', 'documentationLabel', 'datasetFamilies'];
+        return ['__isInitialized__', 'name', 'label', 'description', 'display', 'dataPath', 'filesPath', 'public', 'portalLogo', 'designColor', 'designBackgroundColor', 'designLogo', 'designFavicon', 'homeComponent', 'homeComponentConfig', 'sampEnabled', 'searchByCriteriaAllowed', 'searchByCriteriaLabel', 'searchMultipleAllowed', 'searchMultipleLabel', 'searchMultipleAllDatasetsSelected', 'documentationAllowed', 'documentationLabel', 'datasetFamilies'];
     }
 
     /**
@@ -280,6 +280,28 @@ class Instance extends \App\Entity\Instance implements \Doctrine\ORM\Proxy\Proxy
         return parent::setDataPath($dataPath);
     }
 
+    /**
+     * {@inheritDoc}
+     */
+    public function getFilesPath()
+    {
+
+        $this->__initializer__ && $this->__initializer__->__invoke($this, 'getFilesPath', []);
+
+        return parent::getFilesPath();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function setFilesPath($filesPath)
+    {
+
+        $this->__initializer__ && $this->__initializer__->__invoke($this, 'setFilesPath', [$filesPath]);
+
+        return parent::setFilesPath($filesPath);
+    }
+
     /**
      * {@inheritDoc}
      */
diff --git a/server/src/Action/AdminFileExplorerAction.php b/server/src/Action/AdminFileExplorerAction.php
index 8f86b80e..d3100f8e 100644
--- a/server/src/Action/AdminFileExplorerAction.php
+++ b/server/src/Action/AdminFileExplorerAction.php
@@ -61,7 +61,7 @@ final class AdminFileExplorerAction
         $path = $this->dataPath;
 
         if (array_key_exists('fpath', $args)) {
-            $path .= DIRECTORY_SEPARATOR . $args['fpath'];
+            $path .= $args['fpath'];
         }
 
         if (!file_exists($path)) {
diff --git a/server/src/Action/DatasetFileExplorerAction.php b/server/src/Action/DatasetFileExplorerAction.php
index 077b0be7..84dabe47 100644
--- a/server/src/Action/DatasetFileExplorerAction.php
+++ b/server/src/Action/DatasetFileExplorerAction.php
@@ -16,7 +16,7 @@ use Psr\Http\Message\ServerRequestInterface;
 use Psr\Http\Message\ResponseInterface;
 use Doctrine\ORM\EntityManagerInterface;
 use Slim\Exception\HttpNotFoundException;
-use Slim\Exception\HttpBadRequestException;
+use Nyholm\Psr7\Factory\Psr17Factory;
 
 /**
  * @author François Agneray <francois.agneray@lam.fr>
@@ -71,7 +71,7 @@ final class DatasetFileExplorerAction extends AbstractAction
         }
 
         // Search the correct dataset with primary key
-        $datasetName = $args['dname'];
+        $datasetName = $args['name'];
         $dataset = $this->em->find('App\Entity\Dataset', $datasetName);
 
         // If dataset is not found 404
@@ -102,17 +102,11 @@ final class DatasetFileExplorerAction extends AbstractAction
         }
 
         // Dataset data_path
-        $path = $this->dataPath . $dataset->getFullDataPath();
+        $instancePath = $dataset->getDatasetFamily()->getInstance()->getDataPath();
+        $path = $this->dataPath . $instancePath . $dataset->getDataPath();
 
         if (array_key_exists('fpath', $args)) {
-            $path .= DIRECTORY_SEPARATOR . $args['fpath'];
-        }
-
-        if (is_file($path)) {
-            throw new HttpBadRequestException(
-                $request,
-                'The requested path is a file'
-            );
+            $path .= $args['fpath'];
         }
 
         if (!file_exists($path)) {
@@ -122,18 +116,28 @@ final class DatasetFileExplorerAction extends AbstractAction
             );
         }
 
-        $files = array();
-
-        foreach (scandir($path) as $file) {
-            $files[] = array(
-                'name' => $file,
-                'size' => filesize($path . DIRECTORY_SEPARATOR . $file),
-                'type' => filetype($path . DIRECTORY_SEPARATOR . $file),
-                'mimetype' => mime_content_type($path . DIRECTORY_SEPARATOR . $file)
-            );
+        if (is_file($path)) {
+            // If the file found so stream it
+            $psr17Factory = new Psr17Factory();
+            $stream = $psr17Factory->createStreamFromFile($path, 'r');
+
+            return $response->withBody($stream)
+                ->withHeader('Content-Type', mime_content_type($path))
+                ->withHeader('Content-Length', filesize($path));
+        } else {
+            $files = array();
+
+            foreach (scandir($path) as $file) {
+                $files[] = array(
+                    'name' => $file,
+                    'size' => filesize($path . DIRECTORY_SEPARATOR . $file),
+                    'type' => filetype($path . DIRECTORY_SEPARATOR . $file),
+                    'mimetype' => mime_content_type($path . DIRECTORY_SEPARATOR . $file)
+                );
+            }
+
+            $response->getBody()->write(json_encode($files));
+            return $response;
         }
-
-        $response->getBody()->write(json_encode($files));
-        return $response;
     }
 }
diff --git a/server/src/Action/DownloadInstanceFileAction.php b/server/src/Action/DownloadInstanceFileAction.php
deleted file mode 100644
index c450dea0..00000000
--- a/server/src/Action/DownloadInstanceFileAction.php
+++ /dev/null
@@ -1,96 +0,0 @@
-<?php
-
-/*
- * This file is part of Anis Server.
- *
- * (c) 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.
- */
-declare(strict_types=1);
-
-namespace App\Action;
-
-use Psr\Http\Message\ServerRequestInterface;
-use Psr\Http\Message\ResponseInterface;
-use Doctrine\ORM\EntityManagerInterface;
-use Slim\Exception\HttpNotFoundException;
-use Nyholm\Psr7\Factory\Psr17Factory;
-
-/**
- * @author François Agneray <francois.agneray@lam.fr>
- * @package App\Action
- */
-final class DownloadInstanceFileAction extends AbstractAction
-{
-    /**
-     * Contains anis-server data path
-     *
-     * @var string
-     */
-    private $dataPath;
-
-    /**
-     * Create the classe before call __invoke to execute the action
-     *
-     * @param EntityManagerInterface $em       Doctrine Entity Manager Interface
-     * @param string                 $dataPath Contains anis-server data path
-     */
-    public function __construct(EntityManagerInterface $em, string $dataPath)
-    {
-        parent::__construct($em);
-        $this->dataPath = $dataPath;
-    }
-
-    /**
-     * `GET` Returns the file found
-     *
-     * @param  ServerRequestInterface $request  PSR-7 This object represents the HTTP request
-     * @param  ResponseInterface      $response PSR-7 This object represents the HTTP response
-     * @param  string[]               $args     This table contains information transmitted in the URL (see routes.php)
-     *
-     * @return ResponseInterface
-     */
-    public function __invoke(
-        ServerRequestInterface $request,
-        ResponseInterface $response,
-        array $args
-    ): ResponseInterface {
-        if ($request->getMethod() === OPTIONS) {
-            return $response->withHeader('Access-Control-Allow-Methods', 'GET, OPTIONS');
-        }
-
-        // Search the correct instance with primary key
-        $instanceName = $args['iname'];
-        $instance = $this->em->find('App\Entity\Instance', $instanceName);
-
-        // If dataset is not found 404
-        if (is_null($instance)) {
-            throw new HttpNotFoundException(
-                $request,
-                'Instance with name ' . $instanceName . ' is not found'
-            );
-        }
-
-        // Search the file
-        $filePath = $this->dataPath . $instance->getDataPath() . DIRECTORY_SEPARATOR . $args['fpath'];
-
-        // If the file not found 404
-        if (!file_exists($filePath)) {
-            throw new HttpNotFoundException(
-                $request,
-                'File with path ' . $args['fpath'] . ' is not found for the instance ' . $instanceName
-            );
-        }
-
-        // If the file found so stream it
-        $psr17Factory = new Psr17Factory();
-        $stream = $psr17Factory->createStreamFromFile($filePath, 'r');
-
-        return $response->withBody($stream)
-            ->withHeader('Content-Disposition', 'attachment; filename=' . basename($filePath) . ';')
-            ->withHeader('Content-Type', mime_content_type($filePath))
-            ->withHeader('Content-Length', filesize($filePath));
-    }
-}
diff --git a/server/src/Action/InstanceAction.php b/server/src/Action/InstanceAction.php
index 7f2b5789..a9044e59 100644
--- a/server/src/Action/InstanceAction.php
+++ b/server/src/Action/InstanceAction.php
@@ -99,6 +99,7 @@ final class InstanceAction extends AbstractAction
         $instance->setDescription($parsedBody['description']);
         $instance->setDisplay($parsedBody['display']);
         $instance->setDataPath($parsedBody['data_path']);
+        $instance->setFilesPath($parsedBody['files_path']);
         $instance->setPublic($parsedBody['public']);
         $instance->setPortalLogo($parsedBody['portal_logo']);
         $instance->setDesignColor($parsedBody['design_color']);
diff --git a/server/src/Action/DownloadFileAction.php b/server/src/Action/InstanceFileExplorerAction.php
similarity index 54%
rename from server/src/Action/DownloadFileAction.php
rename to server/src/Action/InstanceFileExplorerAction.php
index 196a0ad3..f613a420 100644
--- a/server/src/Action/DownloadFileAction.php
+++ b/server/src/Action/InstanceFileExplorerAction.php
@@ -22,7 +22,7 @@ use Nyholm\Psr7\Factory\Psr17Factory;
  * @author François Agneray <francois.agneray@lam.fr>
  * @package App\Action
  */
-final class DownloadFileAction extends AbstractAction
+final class InstanceFileExplorerAction extends AbstractAction
 {
     /**
      * Contains anis-server data path
@@ -32,7 +32,7 @@ final class DownloadFileAction extends AbstractAction
     private $dataPath;
 
     /**
-     * Contains settings to handle Json Web Token
+     * Contains settings to handle Json Web Token (app/settings.php)
      *
      * @var array
      */
@@ -41,9 +41,9 @@ final class DownloadFileAction extends AbstractAction
     /**
      * Create the classe before call __invoke to execute the action
      *
-     * @param EntityManagerInterface $em       Doctrine Entity Manager Interface
-     * @param string                 $dataPath Contains anis-server data path
-     * @param array                  $settings Settings about token
+     * @param EntityManagerInterface $em        Doctrine Entity Manager Interface
+     * @param string                 $dataPath  Contains anis-server data path
+     * @param array                  $settings  Settings about token
      */
     public function __construct(EntityManagerInterface $em, string $dataPath, array $settings)
     {
@@ -53,7 +53,7 @@ final class DownloadFileAction extends AbstractAction
     }
 
     /**
-     * `GET` Returns the file found
+     * `GET` Returns the list of files if path is a directory or stream file
      *
      * @param  ServerRequestInterface $request  PSR-7 This object represents the HTTP request
      * @param  ResponseInterface      $response PSR-7 This object represents the HTTP response
@@ -70,20 +70,18 @@ final class DownloadFileAction extends AbstractAction
             return $response->withHeader('Access-Control-Allow-Methods', 'GET, OPTIONS');
         }
 
-        // Search the correct dataset with primary key
-        $datasetName = $args['dname'];
-        $dataset = $this->em->find('App\Entity\Dataset', $datasetName);
+        // Search the correct instance with primary key
+        $instance = $this->em->find('App\Entity\Instance', $args['name']);
 
-        // If dataset is not found 404
-        if (is_null($dataset)) {
+        // If instance is not found 404
+        if (is_null($instance)) {
             throw new HttpNotFoundException(
                 $request,
-                'Dataset with name ' . $datasetName . ' is not found'
+                'Instance with name ' . $args['name'] . ' is not found'
             );
         }
 
         // If instance is private and authorization enabled
-        $instance = $dataset->getDatasetFamily()->getInstance();
         if (!$instance->getPublic() && boolval($this->settings['enabled'])) {
             $this->verifyInstanceAuthorization(
                 $request,
@@ -92,33 +90,41 @@ final class DownloadFileAction extends AbstractAction
             );
         }
 
-        // If dataset is private and authorization enabled
-        if (!$dataset->getPublic() && boolval($this->settings['enabled'])) {
-            $this->verifyDatasetAuthorization(
-                $request,
-                $dataset->getName(),
-                explode(',', $this->settings['admin_roles'])
-            );
-        }
+        $path = $this->dataPath . $instance->getDataPath() . $instance->getFilesPath();
 
-        // Search the file
-        $filePath = $this->dataPath . $dataset->getFullDataPath() . DIRECTORY_SEPARATOR . $args['fpath'];
+        if (array_key_exists('fpath', $args)) {
+            $path .= $args['fpath'];
+        }
 
-        // If the file not found 404
-        if (!file_exists($filePath)) {
+        if (!file_exists($path)) {
             throw new HttpNotFoundException(
                 $request,
-                'File with path ' . $args['fpath'] . ' is not found for the dataset ' . $datasetName
+                'The requested path is not found'
             );
         }
 
-        // If the file found so stream it
-        $psr17Factory = new Psr17Factory();
-        $stream = $psr17Factory->createStreamFromFile($filePath, 'r');
+        if (is_file($path)) {
+            // If the file found so stream it
+            $psr17Factory = new Psr17Factory();
+            $stream = $psr17Factory->createStreamFromFile($path, 'r');
+
+            return $response->withBody($stream)
+                ->withHeader('Content-Type', mime_content_type($path))
+                ->withHeader('Content-Length', filesize($path));
+        } else {
+            $files = array();
 
-        return $response->withBody($stream)
-            ->withHeader('Content-Disposition', 'attachment; filename=' . basename($filePath) . ';')
-            ->withHeader('Content-Type', mime_content_type($filePath))
-            ->withHeader('Content-Length', filesize($filePath));
+            foreach (scandir($path) as $file) {
+                $files[] = array(
+                    'name' => $file,
+                    'size' => filesize($path . DIRECTORY_SEPARATOR . $file),
+                    'type' => filetype($path . DIRECTORY_SEPARATOR . $file),
+                    'mimetype' => mime_content_type($path . DIRECTORY_SEPARATOR . $file)
+                );
+            }
+
+            $response->getBody()->write(json_encode($files));
+            return $response;
+        }
     }
 }
diff --git a/server/src/Action/InstanceListAction.php b/server/src/Action/InstanceListAction.php
index 1dc9f4cb..40825374 100644
--- a/server/src/Action/InstanceListAction.php
+++ b/server/src/Action/InstanceListAction.php
@@ -132,6 +132,7 @@ final class InstanceListAction extends AbstractAction
         $instance->setDescription($parsedBody['description']);
         $instance->setDisplay($parsedBody['display']);
         $instance->setDataPath($parsedBody['data_path']);
+        $instance->setFilesPath($parsedBody['files_path']);
         $instance->setPublic($parsedBody['public']);
         $instance->setPortalLogo($parsedBody['portal_logo']);
         $instance->setDesignColor($parsedBody['design_color']);
diff --git a/server/src/Entity/Instance.php b/server/src/Entity/Instance.php
index 044e3261..bfc15464 100644
--- a/server/src/Entity/Instance.php
+++ b/server/src/Entity/Instance.php
@@ -59,6 +59,13 @@ class Instance implements \JsonSerializable
      */
     protected $dataPath;
 
+    /**
+     * @var string
+     *
+     * @Column(type="string", name="files_path", nullable=true)
+     */
+    protected $filesPath;
+
     /**
      * @var bool
      *
@@ -230,6 +237,16 @@ class Instance implements \JsonSerializable
         $this->dataPath = $dataPath;
     }
 
+    public function getFilesPath()
+    {
+        return $this->filesPath;
+    }
+
+    public function setFilesPath($filesPath)
+    {
+        $this->filesPath = $filesPath;
+    }
+
     public function getPublic()
     {
         return $this->public;
@@ -412,6 +429,7 @@ class Instance implements \JsonSerializable
             'description' => $this->getDescription(),
             'display' => $this->getDisplay(),
             'data_path' => $this->getDataPath(),
+            'files_path' => $this->getFilesPath(),
             'public' => $this->getPublic(),
             'portal_logo' => $this->getPortalLogo(),
             'design_color' => $this->getDesignColor(),
diff --git a/server/tests/Action/InstanceActionTest.php b/server/tests/Action/InstanceActionTest.php
index 2b79c1c9..1340c77e 100644
--- a/server/tests/Action/InstanceActionTest.php
+++ b/server/tests/Action/InstanceActionTest.php
@@ -82,6 +82,7 @@ final class InstanceActionTest extends TestCase
             'description' => 'Test',
             'display' => 10,
             'data_path' => '/DEFAULT',
+            'files_path' => '/INSTANCE_FILES',
             'public' => true,
             'portal_logo' => '',
             'design_color' => '#7AC29A',
diff --git a/server/tests/Action/InstanceListActionTest.php b/server/tests/Action/InstanceListActionTest.php
index d263fcdd..2254c0b9 100644
--- a/server/tests/Action/InstanceListActionTest.php
+++ b/server/tests/Action/InstanceListActionTest.php
@@ -81,6 +81,7 @@ final class InstanceListActionTest extends TestCase
             'description' => 'Test',
             'display' => 10,
             'data_path' => '/DEFAULT',
+            'files_path' => '/INSTANCE_FILES',
             'public' => true,
             'portal_logo' => '',
             'design_color' => '#7AC29A',
-- 
GitLab