add sorting for every getData and navigator, Favourites need to be fixed

This commit is contained in:
Milos Holba
2020-12-08 19:14:53 +01:00
parent 2ba92ed3b6
commit 7e325c5101
8 changed files with 93 additions and 36 deletions
@@ -36,6 +36,7 @@ class BrowseController extends Controller
->with(['parent']) ->with(['parent'])
->where('user_id', $user_id) ->where('user_id', $user_id)
->whereIn('unique_id', filter_folders_ids($folders_trashed)) ->whereIn('unique_id', filter_folders_ids($folders_trashed))
->sortable()
->get(); ->get();
// Get files trashed // Get files trashed
@@ -43,6 +44,7 @@ class BrowseController extends Controller
->with(['parent']) ->with(['parent'])
->where('user_id', $user_id) ->where('user_id', $user_id)
->whereNotIn('folder_id', array_values(array_unique(recursiveFind($folders_trashed->toArray(), 'unique_id')))) ->whereNotIn('folder_id', array_values(array_unique(recursiveFind($folders_trashed->toArray(), 'unique_id'))))
->sortable()
->get(); ->get();
// Collect folders and files to single array // Collect folders and files to single array
@@ -72,11 +74,13 @@ class BrowseController extends Controller
$folders = FileManagerFolder::with(['parent', 'shared:token,id,item_id,permission,protected,expire_in']) $folders = FileManagerFolder::with(['parent', 'shared:token,id,item_id,permission,protected,expire_in'])
->where('user_id', $user_id) ->where('user_id', $user_id)
->whereIn('unique_id', $folder_ids) ->whereIn('unique_id', $folder_ids)
->sortable()
->get(); ->get();
$files = FileManagerFile::with(['parent', 'shared:token,id,item_id,permission,protected,expire_in']) $files = FileManagerFile::with(['parent', 'shared:token,id,item_id,permission,protected,expire_in'])
->where('user_id', $user_id) ->where('user_id', $user_id)
->whereIn('unique_id', $file_ids) ->whereIn('unique_id', $file_ids)
->sortable()
->get(); ->get();
// Collect folders and files to single array // Collect folders and files to single array
@@ -91,7 +95,8 @@ class BrowseController extends Controller
public function latest() { public function latest() {
// Get User // Get User
$user = User::with(['latest_uploads']) $user = User::with(['latest_uploads' => function($query) {
$query->sortable(); }])
->where('id', Auth::id()) ->where('id', Auth::id())
->first(); ->first();
@@ -107,7 +112,7 @@ class BrowseController extends Controller
// Get User // Get User
$uploads = FileManagerFile::with(['parent'])->where('user_id', Auth::id()) $uploads = FileManagerFile::with(['parent'])->where('user_id', Auth::id())
->whereUserScope('editor')->orderBy('created_at', 'DESC')->get(); ->whereUserScope('editor')->sortable()->get();
return $uploads; return $uploads;
} }
@@ -148,14 +153,12 @@ class BrowseController extends Controller
$folders = FileManagerFolder::with(['parent', 'shared:token,id,item_id,permission,protected,expire_in']) $folders = FileManagerFolder::with(['parent', 'shared:token,id,item_id,permission,protected,expire_in'])
->where('user_id', $user_id) ->where('user_id', $user_id)
->where('parent_id', $unique_id) ->where('parent_id', $unique_id)
// ->orderBy('created_at', 'DESC')
->sortable(['name', 'DESC']) ->sortable(['name', 'DESC'])
->get(); ->get();
$files = FileManagerFile::with(['parent', 'shared:token,id,item_id,permission,protected,expire_in']) $files = FileManagerFile::with(['parent', 'shared:token,id,item_id,permission,protected,expire_in'])
->where('user_id', $user_id) ->where('user_id', $user_id)
->where('folder_id', $unique_id) ->where('folder_id', $unique_id)
// ->orderBy('created_at', 'DESC')
->sortable(['name', 'DESC']) ->sortable(['name', 'DESC'])
->get(); ->get();
+10 -3
View File
@@ -195,10 +195,14 @@ class User extends Authenticatable
*/ */
public function getFolderTreeAttribute() public function getFolderTreeAttribute()
{ {
$sort = strtolower(request()->input('sort'));
$direction = strtolower(request()->input('direction'));
return FileManagerFolder::with(['folders.shared', 'shared:token,id,item_id,permission,protected,expire_in']) return FileManagerFolder::with(['folders.shared', 'shared:token,id,item_id,permission,protected,expire_in'])
->where('parent_id', 0) ->where('parent_id', 0)
->where('user_id', $this->id) ->where('user_id', $this->id)
->orderByDesc('created_at') ->orderBy($sort , $direction)
->get(); ->get();
} }
@@ -301,7 +305,10 @@ class User extends Authenticatable
*/ */
public function favourite_folders() public function favourite_folders()
{ {
return $this->belongsToMany(FileManagerFolder::class, 'favourite_folder', 'user_id', 'folder_unique_id', 'id', 'unique_id')->with('shared:token,id,item_id,permission,protected,expire_in'); $sort = strtolower(request()->input('sort') ? request()->input('sort') : 'created_at' );
$direction = strtolower(request()->input('direction') ? request()->input('direction') : 'desc');
return $this->belongsToMany(FileManagerFolder::class, 'favourite_folder', 'user_id', 'folder_unique_id', 'id', 'unique_id')->with('shared:token,id,item_id,permission,protected,expire_in')->orderBy($sort , $direction);
} }
/** /**
@@ -311,7 +318,7 @@ class User extends Authenticatable
*/ */
public function latest_uploads() public function latest_uploads()
{ {
return $this->hasMany(FileManagerFile::class)->with(['parent'])->orderBy('created_at', 'DESC')->take(40); return $this->hasMany(FileManagerFile::class)->with(['parent'])->take(40);
} }
/** /**
+8 -13
View File
@@ -27,10 +27,9 @@
"/chunks/database.js": "/chunks/database.js?id=0b21e6ff3bac5c963d9a", "/chunks/database.js": "/chunks/database.js?id=0b21e6ff3bac5c963d9a",
"/chunks/dynamic-page.js": "/chunks/dynamic-page.js?id=464c8e70974d492ce7f6", "/chunks/dynamic-page.js": "/chunks/dynamic-page.js?id=464c8e70974d492ce7f6",
"/chunks/environment-setup.js": "/chunks/environment-setup.js?id=826fbaa6cc4acab69f5d", "/chunks/environment-setup.js": "/chunks/environment-setup.js?id=826fbaa6cc4acab69f5d",
"/chunks/files.js": "/chunks/files.js?id=35936c1c34e3290a6072", "/chunks/files.js": "/chunks/files.js?id=76b46f43df974e899836",
"/chunks/files~chunks/settings-subscription~chunks/shared-files~chunks/shared-page~chunks/user-subscription.js": "/chunks/files~chunks/settings-subscription~chunks/shared-files~chunks/shared-page~chunks/user-subscription.js?id=8ccd35de575e8a91d02f", "/chunks/files~chunks/settings-subscription~chunks/shared-files~chunks/shared-page~chunks/user-subscription.js": "/chunks/files~chunks/settings-subscription~chunks/shared-files~chunks/shared-page~chunks/user-subscription.js?id=8ccd35de575e8a91d02f",
"/chunks/files~chunks/shared-files.js": "/chunks/files~chunks/shared-files.js?id=b0fbc9ca0e4f56538924", "/chunks/files~chunks/shared-files~chunks/shared-page.js": "/chunks/files~chunks/shared-files~chunks/shared-page.js?id=ac7424fc44caf9e64d1a",
"/chunks/files~chunks/shared-files~chunks/shared-page.js": "/chunks/files~chunks/shared-files~chunks/shared-page.js?id=b7a5e83f69ec780a8a8a",
"/chunks/files~chunks/shared-page.js": "/chunks/files~chunks/shared-page.js?id=ed167949ea9398f0fbeb", "/chunks/files~chunks/shared-page.js": "/chunks/files~chunks/shared-page.js?id=ed167949ea9398f0fbeb",
"/chunks/forgotten-password.js": "/chunks/forgotten-password.js?id=aba8c662fbc234892216", "/chunks/forgotten-password.js": "/chunks/forgotten-password.js?id=aba8c662fbc234892216",
"/chunks/installation-disclaimer.js": "/chunks/installation-disclaimer.js?id=7dfffa0f25308ba70b7a", "/chunks/installation-disclaimer.js": "/chunks/installation-disclaimer.js?id=7dfffa0f25308ba70b7a",
@@ -56,8 +55,8 @@
"/chunks/settings-storage.js": "/chunks/settings-storage.js?id=22e7978fe8a5cb488e52", "/chunks/settings-storage.js": "/chunks/settings-storage.js?id=22e7978fe8a5cb488e52",
"/chunks/settings-subscription.js": "/chunks/settings-subscription.js?id=367c9478591c7a4a2889", "/chunks/settings-subscription.js": "/chunks/settings-subscription.js?id=367c9478591c7a4a2889",
"/chunks/setup-wizard.js": "/chunks/setup-wizard.js?id=7da589bd335deefd5f65", "/chunks/setup-wizard.js": "/chunks/setup-wizard.js?id=7da589bd335deefd5f65",
"/chunks/shared-files.js": "/chunks/shared-files.js?id=07bf2c32935e1d8b9dac", "/chunks/shared-files.js": "/chunks/shared-files.js?id=5d9381e60cc1307f715a",
"/chunks/shared-page.js": "/chunks/shared-page.js?id=19e7fdb00a89d06ee29f", "/chunks/shared-page.js": "/chunks/shared-page.js?id=f0e6fb1cf70035c49362",
"/chunks/sign-in.js": "/chunks/sign-in.js?id=703fbd23d18816590337", "/chunks/sign-in.js": "/chunks/sign-in.js?id=703fbd23d18816590337",
"/chunks/sign-up.js": "/chunks/sign-up.js?id=cedca52c29abfb3c58f1", "/chunks/sign-up.js": "/chunks/sign-up.js?id=cedca52c29abfb3c58f1",
"/chunks/stripe-credentials.js": "/chunks/stripe-credentials.js?id=b99eb91043d1321187b9", "/chunks/stripe-credentials.js": "/chunks/stripe-credentials.js?id=b99eb91043d1321187b9",
@@ -76,12 +75,8 @@
"/chunks/user-storage.js": "/chunks/user-storage.js?id=a99910f95c3e39caa78b", "/chunks/user-storage.js": "/chunks/user-storage.js?id=a99910f95c3e39caa78b",
"/chunks/user-subscription.js": "/chunks/user-subscription.js?id=e8ea1e67f9ac0a835ed0", "/chunks/user-subscription.js": "/chunks/user-subscription.js?id=e8ea1e67f9ac0a835ed0",
"/chunks/users.js": "/chunks/users.js?id=aba8837f40fbb79f99b4", "/chunks/users.js": "/chunks/users.js?id=aba8837f40fbb79f99b4",
"/js/main.a76097b1c16b210f4474.hot-update.js": "/js/main.a76097b1c16b210f4474.hot-update.js", "/js/main.1f3d44f14db2262cc1c6.hot-update.js": "/js/main.1f3d44f14db2262cc1c6.hot-update.js",
"/chunks/files.a76097b1c16b210f4474.hot-update.js": "/chunks/files.a76097b1c16b210f4474.hot-update.js", "/js/main.3681a718c84b2523e874.hot-update.js": "/js/main.3681a718c84b2523e874.hot-update.js",
"/chunks/files~chunks/shared-files~chunks/shared-page.a76097b1c16b210f4474.hot-update.js": "/chunks/files~chunks/shared-files~chunks/shared-page.a76097b1c16b210f4474.hot-update.js", "/chunks/files~chunks/shared-files~chunks/shared-page.16e87dbca64505b5cfac.hot-update.js": "/chunks/files~chunks/shared-files~chunks/shared-page.16e87dbca64505b5cfac.hot-update.js",
"/chunks/shared-files.a76097b1c16b210f4474.hot-update.js": "/chunks/shared-files.a76097b1c16b210f4474.hot-update.js", "/js/main.9e6c37da83e8132013f6.hot-update.js": "/js/main.9e6c37da83e8132013f6.hot-update.js"
"/js/main.417c204635751bec57ca.hot-update.js": "/js/main.417c204635751bec57ca.hot-update.js",
"/chunks/files.417c204635751bec57ca.hot-update.js": "/chunks/files.417c204635751bec57ca.hot-update.js",
"/chunks/files~chunks/shared-files~chunks/shared-page.417c204635751bec57ca.hot-update.js": "/chunks/files~chunks/shared-files~chunks/shared-page.417c204635751bec57ca.hot-update.js",
"/chunks/shared-files.417c204635751bec57ca.hot-update.js": "/chunks/shared-files.417c204635751bec57ca.hot-update.js"
} }
@@ -28,7 +28,7 @@
</ul> </ul>
<ul class="menu-option-group"> <ul class="menu-option-group">
<li class="menu-option" @click="sort('date')"> <li class="menu-option" @click="sort('created_at')">
<div class="icon"> <div class="icon">
<calendar-icon size="17"/> <calendar-icon size="17"/>
</div> </div>
@@ -36,7 +36,7 @@
{{$t('preview_sorting.sort_date')}} {{$t('preview_sorting.sort_date')}}
</div> </div>
<div class="show-icon" > <div class="show-icon" >
<arrow-up-icon size="17" v-if="filter.field === 'date'" :class="{ 'arrow-down': filter.sort === 'ASC' }"/> <arrow-up-icon size="17" v-if="filter.field === 'created_at'" :class="{ 'arrow-down': filter.sort === 'ASC' }"/>
</div> </div>
</li> </li>
<li class="menu-option" @click="sort('name')" > <li class="menu-option" @click="sort('name')" >
@@ -114,8 +114,8 @@
let sorting = JSON.parse(localStorage.getItem('sorting')) let sorting = JSON.parse(localStorage.getItem('sorting'))
this.filter.sort = sorting ? sorting.sort : undefined this.filter.sort = sorting ? sorting.sort : 'DESC'
this.filter.field = sorting ? sorting.field : undefined this.filter.field = sorting ? sorting.field : 'created_at'
events.$on('sortingAndPreview-open', () => { events.$on('sortingAndPreview-open', () => {
this.isVisible = true this.isVisible = true
+21 -3
View File
@@ -308,11 +308,29 @@ const Helpers = {
return validated return validated
} }
Vue.prototype.$getDataByLocation = function() { Vue.prototype.$getDataByLocation = function() {
let previousFolder = store.getters.currentFolder let folder = store.getters.currentFolder
if(this.$isThisLocation('base')){ if(this.$isThisLocation('base') || this.$isThisLocation('public')){
this.$store.dispatch('getFolder', [{folder: previousFolder, back: false, init: false}]) this.$store.dispatch('getFolder', [{ folder: folder, back: true, init: false, sorting:true}])
} }
if(this.$isThisLocation('latest')) {
this.$store.dispatch('getLatest')
}
if(this.$isThisLocation('shared')) {
this.$store.dispatch('getShared')
}
if(this.$isThisLocation('participant_uploads')) {
this.$store.dispatch('getParticipantUploads')
}
if(this.$isThisLocation('trash-root')) {
this.$store.dispatch('getTrash')
}
this.$store.dispatch('getAppData')
} }
} }
} }
+33 -8
View File
@@ -20,6 +20,10 @@ const actions = {
getFolder: ({commit, getters}, [payload]) => { getFolder: ({commit, getters}, [payload]) => {
commit('LOADING_STATE', {loading: true, data: []}) commit('LOADING_STATE', {loading: true, data: []})
let getSort = JSON.parse(localStorage.getItem('sorting'))
let sorting = {sort : getSort ? getSort.sort : 'DESC' , field:getSort ? getSort.field : 'created_at'}
let sortingUrl = '?sort=' + sorting.field + '&direction=' + sorting.sort
if (payload.init) if (payload.init)
commit('FLUSH_FOLDER_HISTORY') commit('FLUSH_FOLDER_HISTORY')
@@ -32,7 +36,7 @@ const actions = {
// Set folder location // Set folder location
payload.folder.location = payload.folder.deleted_at || payload.folder.location === 'trash' ? 'trash' : 'base' payload.folder.location = payload.folder.deleted_at || payload.folder.location === 'trash' ? 'trash' : 'base'
if (! payload.back) if (! payload.back && !payload.sorting)
commit('STORE_PREVIOUS_FOLDER', getters.currentFolder) commit('STORE_PREVIOUS_FOLDER', getters.currentFolder)
let url = payload.folder.location === 'trash' let url = payload.folder.location === 'trash'
@@ -40,12 +44,12 @@ const actions = {
: '/folders/' + payload.folder.unique_id : '/folders/' + payload.folder.unique_id
axios axios
.get(getters.api + url) .get(getters.api + url + sortingUrl)
.then(response => { .then(response => {
commit('LOADING_STATE', {loading: false, data: response.data}) commit('LOADING_STATE', {loading: false, data: response.data})
commit('STORE_CURRENT_FOLDER', payload.folder) commit('STORE_CURRENT_FOLDER', payload.folder)
if (payload.back) if (payload.back && !payload.sorting)
commit('REMOVE_BROWSER_HISTORY') commit('REMOVE_BROWSER_HISTORY')
events.$emit('scrollTop') events.$emit('scrollTop')
@@ -71,6 +75,10 @@ const actions = {
getLatest: ({commit, getters}) => { getLatest: ({commit, getters}) => {
commit('LOADING_STATE', {loading: true, data: []}) commit('LOADING_STATE', {loading: true, data: []})
let getSort = JSON.parse(localStorage.getItem('sorting'))
let sorting = {sort : getSort ? getSort.sort : 'DESC' , field:getSort ? getSort.field : 'created_at'}
let sortingUrl = '?sort=' + sorting.field + '&direction=' + sorting.sort
commit('STORE_PREVIOUS_FOLDER', getters.currentFolder) commit('STORE_PREVIOUS_FOLDER', getters.currentFolder)
commit('STORE_CURRENT_FOLDER', { commit('STORE_CURRENT_FOLDER', {
name: i18n.t('sidebar.latest'), name: i18n.t('sidebar.latest'),
@@ -79,7 +87,7 @@ const actions = {
}) })
axios axios
.get(getters.api + '/latest') .get(getters.api + '/latest' + sortingUrl)
.then(response => { .then(response => {
commit('LOADING_STATE', {loading: false, data: response.data}) commit('LOADING_STATE', {loading: false, data: response.data})
events.$emit('scrollTop') events.$emit('scrollTop')
@@ -90,6 +98,10 @@ const actions = {
commit('LOADING_STATE', {loading: true, data: []}) commit('LOADING_STATE', {loading: true, data: []})
commit('FLUSH_FOLDER_HISTORY') commit('FLUSH_FOLDER_HISTORY')
let getSort = JSON.parse(localStorage.getItem('sorting'))
let sorting = {sort : getSort ? getSort.sort : 'DESC' , field:getSort ? getSort.field : 'created_at'}
let sortingUrl = '?sort=' + sorting.field + '&direction=' + sorting.sort
let currentFolder = { let currentFolder = {
name: i18n.t('sidebar.my_shared'), name: i18n.t('sidebar.my_shared'),
location: 'shared', location: 'shared',
@@ -99,7 +111,7 @@ const actions = {
commit('STORE_CURRENT_FOLDER', currentFolder) commit('STORE_CURRENT_FOLDER', currentFolder)
axios axios
.get(getters.api + '/shared-all') .get(getters.api + '/shared-all' + sortingUrl)
.then(response => { .then(response => {
commit('LOADING_STATE', {loading: false, data: response.data}) commit('LOADING_STATE', {loading: false, data: response.data})
commit('STORE_PREVIOUS_FOLDER', currentFolder) commit('STORE_PREVIOUS_FOLDER', currentFolder)
@@ -111,6 +123,10 @@ const actions = {
getParticipantUploads: ({commit, getters}) => { getParticipantUploads: ({commit, getters}) => {
commit('LOADING_STATE', {loading: true, data: []}) commit('LOADING_STATE', {loading: true, data: []})
let getSort = JSON.parse(localStorage.getItem('sorting'))
let sorting = {sort : getSort ? getSort.sort : 'DESC' , field:getSort ? getSort.field : 'created_at'}
let sortingUrl = '?sort=' + sorting.field + '&direction=' + sorting.sort
commit('STORE_PREVIOUS_FOLDER', getters.currentFolder) commit('STORE_PREVIOUS_FOLDER', getters.currentFolder)
commit('STORE_CURRENT_FOLDER', { commit('STORE_CURRENT_FOLDER', {
name: i18n.t('sidebar.participant_uploads'), name: i18n.t('sidebar.participant_uploads'),
@@ -119,7 +135,7 @@ const actions = {
}) })
axios axios
.get(getters.api + '/participant-uploads') .get(getters.api + '/participant-uploads' + sortingUrl)
.then(response => { .then(response => {
commit('LOADING_STATE', {loading: false, data: response.data}) commit('LOADING_STATE', {loading: false, data: response.data})
@@ -131,6 +147,11 @@ const actions = {
commit('LOADING_STATE', {loading: true, data: []}) commit('LOADING_STATE', {loading: true, data: []})
commit('FLUSH_FOLDER_HISTORY') commit('FLUSH_FOLDER_HISTORY')
let getSort = JSON.parse(localStorage.getItem('sorting'))
let sorting = {sort : getSort ? getSort.sort : 'DESC' , field:getSort ? getSort.field : 'created_at'}
let sortingUrl = '?sort=' + sorting.field + '&direction=' + sorting.sort
let trash = { let trash = {
name: i18n.t('locations.trash'), name: i18n.t('locations.trash'),
unique_id: undefined, unique_id: undefined,
@@ -140,7 +161,7 @@ const actions = {
commit('STORE_CURRENT_FOLDER', trash) commit('STORE_CURRENT_FOLDER', trash)
axios axios
.get(getters.api + '/trash') .get(getters.api + '/trash' + sortingUrl)
.then(response => { .then(response => {
commit('LOADING_STATE', {loading: false, data: response.data}) commit('LOADING_STATE', {loading: false, data: response.data})
commit('STORE_PREVIOUS_FOLDER', trash) commit('STORE_PREVIOUS_FOLDER', trash)
@@ -174,6 +195,10 @@ const actions = {
}, },
getFolderTree: ({commit, getters}) => { getFolderTree: ({commit, getters}) => {
let getSort = JSON.parse(localStorage.getItem('sorting'))
let sorting = {sort : getSort ? getSort.sort : 'DESC' , field:getSort ? getSort.field : 'created_at'}
let sortingUrl = '?sort=' + sorting.field + '&direction=' + sorting.sort
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
// Get route // Get route
@@ -187,7 +212,7 @@ const actions = {
route = '/api/navigation' route = '/api/navigation'
axios axios
.get(route) .get(route + sortingUrl)
.then(response => { .then(response => {
resolve(response) resolve(response)
+5 -1
View File
@@ -11,9 +11,13 @@ const defaultState = {
const actions = { const actions = {
getAppData: ({commit, getters}) => { getAppData: ({commit, getters}) => {
let getSort = JSON.parse(localStorage.getItem('sorting'))
let sorting = {sort : getSort ? getSort.sort : 'DESC' , field:getSort ? getSort.field : 'created_at'}
let sortingUrl = '?sort=' + sorting.field + '&direction=' + sorting.sort
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
axios axios
.get(getters.api + '/user') .get(getters.api + '/user' + sortingUrl)
.then((response) => { .then((response) => {
resolve(response) resolve(response)
+5
View File
@@ -101,6 +101,9 @@
<!--File browser--> <!--File browser-->
<FileBrowser/> <FileBrowser/>
<!-- Selecting preview list and sorting -->
<SortingAndPreview/>
</div> </div>
</div> </div>
</div> </div>
@@ -109,6 +112,7 @@
<script> <script>
import {ValidationProvider, ValidationObserver} from 'vee-validate/dist/vee-validate.full' import {ValidationProvider, ValidationObserver} from 'vee-validate/dist/vee-validate.full'
import MobileMultiSelectMenu from '@/components/FilesView/MobileMultiSelectMenu' import MobileMultiSelectMenu from '@/components/FilesView/MobileMultiSelectMenu'
import SortingAndPreview from '@/components/FilesView/SortingAndPreview'
import TreeMenuNavigator from '@/components/Others/TreeMenuNavigator' import TreeMenuNavigator from '@/components/Others/TreeMenuNavigator'
import FileFullPreview from '@/components/FilesView/FileFullPreview' import FileFullPreview from '@/components/FilesView/FileFullPreview'
import DesktopToolbar from '@/components/FilesView/DesktopToolbar' import DesktopToolbar from '@/components/FilesView/DesktopToolbar'
@@ -140,6 +144,7 @@
components: { components: {
MobileMultiSelectMenu, MobileMultiSelectMenu,
ValidationProvider, ValidationProvider,
SortingAndPreview,
ValidationObserver, ValidationObserver,
TreeMenuNavigator, TreeMenuNavigator,
FileFullPreview, FileFullPreview,