Merge remote-tracking branch 'origin/bulk-operations' into bulk-operations

# Conflicts:
#	public/mix-manifest.json
#	resources/js/components/FilesView/ContextMenu.vue
This commit is contained in:
Peter Papp
2020-12-13 17:52:24 +01:00
23 changed files with 155 additions and 103 deletions

View File

@@ -95,9 +95,9 @@ class BrowseController extends Controller
public function latest() {
// Get User
// TODO: SFORMATOVAT!
$user = User::with(['latest_uploads' => function($query) {
$query->sortable(); }])
$query->sortable();
}])
->where('id', Auth::id())
->first();
@@ -134,7 +134,6 @@ class BrowseController extends Controller
$user_id = Auth::id();
// Get folder trash items
// TODO: do funkcii nizsie potrebujeme tiez sortable, totizto foldre v kosi vies tiez prechadzat, takisto spojazdnit na frontende. Lokacie mame 'trash-root' a 'trash'
if ($request->query('trash')) {
// Get folders and files
@@ -142,12 +141,14 @@ class BrowseController extends Controller
->with('parent')
->where('user_id', $user_id)
->where('parent_id', $unique_id)
->sortable()
->get();
$files = FileManagerFile::onlyTrashed()
->with('parent')
->where('user_id', $user_id)
->where('folder_id', $unique_id)
->sortable()
->get();
// Collect folders and files to single array
@@ -158,13 +159,13 @@ class BrowseController extends Controller
$folders = FileManagerFolder::with(['parent', 'shared:token,id,item_id,permission,protected,expire_in'])
->where('user_id', $user_id)
->where('parent_id', $unique_id)
->sortable(['name', 'DESC'])
->sortable()
->get();
$files = FileManagerFile::with(['parent', 'shared:token,id,item_id,permission,protected,expire_in'])
->where('user_id', $user_id)
->where('folder_id', $unique_id)
->sortable(['name', 'DESC'])
->sortable()
->get();
// Collect folders and files to single array

View File

@@ -107,7 +107,15 @@ class AccountController extends Controller
*/
public function update_user_settings(Request $request)
{
// TODO: validation
// Validate request
$validator = Validator::make($request->all(), [
'name' => 'string',
'value' => 'string',
]);
// Return error
if ($validator->fails()) abort(400, 'Bad input');
// Get user
$user = Auth::user();

View File

@@ -195,15 +195,14 @@ class User extends Authenticatable
*/
public function getFolderTreeAttribute()
{
// Get sorting setup
$sort = strtolower(request()->input('sort'));
$direction = strtolower(request()->input('direction'));
// TODO: pozor pozor tu by sme mali pouzit sortable(), tak ako si pouzil v BrowseController
return FileManagerFolder::with(['folders.shared', 'shared:token,id,item_id,permission,protected,expire_in'])
->where('parent_id', 0)
->where('user_id', $this->id)
->orderBy($sort , $direction)
->sortable($sort , $direction)
->get();
}

View File

@@ -163,11 +163,16 @@
},
mounted() {
this.$checkOS()
// Handle mobile navigation scale animation
events.$on('show:mobile-navigation', () => this.isScaledDown = true)
events.$on('hide:mobile-navigation', () => this.isScaledDown = false)
events.$on('mobileMenu:show', () => this.isScaledDown = true)
events.$on('fileItem:deselect', () => this.isScaledDown = false)
events.$on('mobileSortingAndPreview', (state) => {
this.isScaledDown = state
})
}
}
</script>

View File

@@ -466,6 +466,7 @@ export default {
this.$store.dispatch('fileInfoToggle', true)
},
deleteItem() {
// Dispatch remove item
// If is context menu open on non selected item delete this single item
if (!this.fileInfoDetail.includes(this.item)) {
this.$store.dispatch('deleteItem', this.item)

View File

@@ -84,11 +84,13 @@
<ToolbarButton
source="preview-sorting"
class="preview-sorting"
:action="$t('actions.sorting_view')"
:class="{ active: sortingAndPreview }"
@click.native=" sortingAndPreview = !sortingAndPreview"
/>
<ToolbarButton
:action="$t('actions.info_panel')"
:class="{ active: fileInfoVisible }"
@click.native="$store.dispatch('fileInfoToggle')"
source="info"
@@ -196,11 +198,11 @@ export default {
watch: {
sortingAndPreview () {
if(this.sortingAndPreview) {
events.$emit('sortingAndPreview-open')
events.$emit('sortingAndPreview', true)
}
if(!this.sortingAndPreview) {
events.$emit('sortingAndPreview-close')
events.$emit('sortingAndPreview', false)
}
}
},
@@ -256,6 +258,11 @@ export default {
}
},
},
mounted () {
events.$on('sortingAndPreview', (state) => {
this.sortingAndPreview = state
})
}
};
</script>

View File

@@ -13,21 +13,34 @@ import {events} from '@/bus'
computed: {
...mapGetters(['fileInfoDetail']),
title(){
// Title for multiple selected items
if(this.fileInfoDetail.length > 1 && this.fileInfoDetail.includes(this.draggedItem)) {
return this.$t('file_detail.selected_multiple')
}
// Title for single item
if((this.fileInfoDetail.length < 2 || !this.fileInfoDetail.includes(this.draggedItem)) && this.draggedItem ) {
return this.draggedItem.name
}
},
subtitle(){
// Subtitle for multiple selected items
if(this.fileInfoDetail.length > 1 && this.fileInfoDetail.includes(this.draggedItem) ) {
return this.fileInfoDetail.length + ' ' + this.$tc('file_detail.items', this.fileInfoDetail.length)
}
if((this.fileInfoDetail.length < 2 || !this.fileInfoDetail.includes(this.draggedItem)) && this.draggedItem && this.draggedItem.mimetype) {
return '.'+this.draggedItem.mimetype
if((this.fileInfoDetail.length < 2 || !this.fileInfoDetail.includes(this.draggedItem)) && this.draggedItem) {
// Subtitle for single folder
if(this.draggedItem.type === 'folder') {
return this.draggedItem.items == 0 ? this.$t('folder.empty') : this.$tc('folder.item_counts', this.draggedItem.items)
}
// Subtitle for single file
if(this.draggedItem !== 'folder' && this.draggedItem.mimetype){
return '.'+this.draggedItem.mimetype
}
}
},
},

View File

@@ -230,11 +230,11 @@
}
},
created() {
events.$on('mobileSelecting-start' , () => {
events.$on('mobileSelecting:start' , () => {
this.mobileMultiSelect =true
})
events.$on('mobileSelecting-stop' , () => {
events.$on('mobileSelecting:stop' , () => {
this.mobileMultiSelect = false
})

View File

@@ -157,6 +157,8 @@ export default {
clickedItem(e) {
events.$emit('contextMenu:hide')
events.$emit('sortingAndPreview', false)
if (!this.$isMobile()) {
if (e.ctrlKey || e.metaKey && !e.shiftKey) {
// Click + Ctrl
@@ -263,12 +265,12 @@ export default {
created() {
this.itemName = this.data.name
events.$on('mobileSelecting-start', () => {
events.$on('mobileSelecting:start', () => {
this.mobileMultiSelect = true
this.$store.commit('CLEAR_FILEINFO_DETAIL')
})
events.$on('mobileSelecting-stop', () => {
events.$on('mobileSelecting:stop', () => {
this.mobileMultiSelect = false
this.$store.commit('CLEAR_FILEINFO_DETAIL')
})

View File

@@ -168,6 +168,8 @@ export default {
clickedItem(e) {
events.$emit('contextMenu:hide')
events.$emit('sortingAndPreview', false)
if(!this.$isMobile()) {
if( (e.ctrlKey || e.metaKey ) && !e.shiftKey) {
@@ -271,12 +273,12 @@ export default {
created() {
this.itemName = this.data.name
events.$on('mobileSelecting-start', () => {
events.$on('mobileSelecting:start', () => {
this.mobileMultiSelect = true
this.$store.commit('CLEAR_FILEINFO_DETAIL')
})
events.$on('mobileSelecting-stop', () => {
events.$on('mobileSelecting:stop', () => {
this.mobileMultiSelect = false
this.$store.commit('CLEAR_FILEINFO_DETAIL')
})

View File

@@ -8,7 +8,7 @@
<grid-icon v-if="icon === 'th'" size="15" class="icon"></grid-icon>
<user-plus-icon v-if="icon === 'user-plus'" size="15" class="icon"></user-plus-icon>
<plus-icon v-if="icon === 'plus'" size="15" class="icon"></plus-icon>
<svg v-if="icon === 'preview-sorting'" size="15" class="icon"
<svg v-if="icon === 'preview-sorting'" size="15" class="icon preview-sorting"
width="15px" height="15px" viewBox="0 0 18 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="VueFileManager" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round">
<g id="Storage-Alert-Copy" transform="translate(-1092.000000, -28.000000)" stroke="#000000" stroke-width="1.4">

View File

@@ -3,15 +3,15 @@
<!--Actions for trash location with MASTER permission--->
<div v-if="$isThisLocation(['trash', 'trash-root']) && $checkPermission('master')" class="mobile-actions">
<MobileActionButton :class="{'active' : mobileSortingAndPreview}" @click.native="mobileSortingAndPreview = ! mobileSortingAndPreview" icon="preview-sorting">
{{$t('preview_sorting.preview_sorting_button')}}
</MobileActionButton>
<MobileMultiSelectButton @click.native="mobileMultiSelect = !mobileMultiSelect">
{{ $t('context_menu.select') }}
</MobileMultiSelectButton>
<MobileActionButton @click.native="$store.dispatch('emptyTrash')" icon="trash">
{{ $t('context_menu.empty_trash') }}
</MobileActionButton>
<MobileMultiSelectButton @click.native="mobileMultiSelect = !mobileMultiSelect">
{{ $t('context_menu.select') }}
</MobileMultiSelectButton>
<MobileActionButton class="preview-sorting" @click.native="mobileSortingAndPreview = ! mobileSortingAndPreview" icon="preview-sorting">
{{$t('preview_sorting.preview_sorting_button')}}
</MobileActionButton>
</div>
<!--ContextMenu for Base location with MASTER permission-->
@@ -25,20 +25,19 @@
<MobileMultiSelectButton @click.native="mobileMultiSelect = !mobileMultiSelect">
{{ $t('context_menu.select') }}
</MobileMultiSelectButton>
<!--TODO: toto tlacitko nepotrebuje mat active classu, spusta iba event, nie je sucastou prebiehajucej udalosti (z pohladu UX)-->
<MobileActionButton :class="{'active' : mobileSortingAndPreview}" @click.native="mobileSortingAndPreview = ! mobileSortingAndPreview" icon="preview-sorting">
<MobileActionButton class="preview-sorting" @click.native="mobileSortingAndPreview = ! mobileSortingAndPreview" icon="preview-sorting">
{{$t('preview_sorting.preview_sorting_button')}}
</MobileActionButton>
</div>
<!--ContextMenu for Base location with VISITOR permission-->
<div v-if="($isThisLocation(['base', 'shared', 'public']) && $checkPermission('visitor')) || ($isThisLocation(['latest', 'shared']) && $checkPermission('master'))" class="mobile-actions">
<MobileActionButton :class="{'active' : mobileSortingAndPreview}" @click.native="mobileSortingAndPreview = ! mobileSortingAndPreview" icon="preview-sorting">
{{$t('preview_sorting.preview_sorting_button')}}
</MobileActionButton>
<MobileMultiSelectButton @click.native="mobileMultiSelect = !mobileMultiSelect">
{{ $t('context_menu.select') }}
</MobileMultiSelectButton>
<MobileActionButton class="preview-sorting" @click.native="mobileSortingAndPreview = ! mobileSortingAndPreview" icon="preview-sorting">
{{$t('preview_sorting.preview_sorting_button')}}
</MobileActionButton>
</div>
<!--Upload Progressbar-->
@@ -80,20 +79,22 @@
mobileMultiSelect () {
if(this.mobileMultiSelect ) {
events.$emit('mobileSelecting-start')
events.$emit('mobileSelecting:start')
}
if(!this.mobileMultiSelect) {
events.$emit('mobileSelecting-stop')
events.$emit('mobileSelecting:stop')
}
},
mobileSortingAndPreview (oldValue , newValue) {
if(this.mobileSortingAndPreview) {
events.$emit('mobileSortingAndPreview-open')
events.$emit('mobileSortingAndPreview' , true)
events.$emit('mobileSortingAndPreviewVignette' , true)
this.mobileMultiSelect = false
}
if(!this.mobileSortingAndPreview) {
events.$emit('mobileSortingAndPreview-close')
events.$emit('mobileSortingAndPreview', false)
events.$emit('mobileSortingAndPreviewVignette' , false)
}
}
},
@@ -112,12 +113,12 @@
},
},
mounted () {
events.$on('mobileSelecting-stop', () => {
events.$on('mobileSelecting:stop', () => {
this.mobileMultiSelect = false
})
events.$on('mobileSortingAndPreview-close', () => {
this.mobileSortingAndPreview = false
events.$on('mobileSortingAndPreview', (state) => {
this.mobileSortingAndPreview = state
})
@@ -128,10 +129,17 @@
<style scoped lang="scss">
@import '@assets/vue-file-manager/_variables';
@import '@assets/vue-file-manager/_mixins';
.active {
/deep/.label {
color: $theme !important;
}
.preview-sorting {
background: $light_background !important;
/deep/ .label {
color: $text !important;
}
/deep/ .preview-sorting {
path, line, polyline, rect, circle {
stroke: $text !important;
}
}
}
#mobile-actions-wrapper {
@@ -167,5 +175,16 @@
#mobile-actions-wrapper {
background: $dark_mode_background;
}
.preview-sorting {
background: $dark_mode_foreground !important;
/deep/ .label {
color: $dark_mode_text_primary !important;
}
/deep/ .preview-sorting {
path, line, polyline, rect, circle {
stroke: $theme !important;
}
}
}
}
</style>

View File

@@ -27,11 +27,11 @@
}
},
mounted() {
events.$on('mobileSelecting-start' , () => {
events.$on('mobileSelecting:start' , () => {
this.mobileSelectingActive = true
})
events.$on('mobileSelecting-stop' , () => {
events.$on('mobileSelecting:stop' , () => {
this.mobileSelectingActive = false
})
}

View File

@@ -30,7 +30,7 @@ export default {
},
methods: {
closeSelecting() {
events.$emit('mobileSelecting-stop')
events.$emit('mobileSelecting:stop')
},
downloadItem() {
this.fileInfoDetail.forEach((item , i) => {
@@ -52,12 +52,12 @@ export default {
}
},
created() {
events.$on('mobileSelecting-start', () => {
events.$on('mobileSelecting:start', () => {
this.mobileMultiSelect = true
})
events.$on('mobileSelecting-stop', () => {
events.$on('mobileSelecting:stop', () => {
this.mobileMultiSelect = false
})

View File

@@ -1,6 +1,5 @@
<template>
<div class="options-wrapper">
<transition name="context-menu">
<transition name="context-menu" class="options-wrapper">
<div class="options" v-if="isVisible">
<div class="menu-options">
@@ -30,8 +29,7 @@
</ul>
<ul class="menu-option-group">
<!--TODO: co to ten c class atribute? :D -->
<li c class="menu-option" @click="sort('created_at')">
<li class="menu-option" @click="sort('created_at')">
<div class="icon">
<calendar-icon size="17"/>
</div>
@@ -44,7 +42,7 @@
</li>
<li class="menu-option" @click="sort('name')">
<div class="icon">
<alphabet-icon size="17" class="aplhabet-icon"/>
<alphabet-icon size="17" class="alphabet-icon"/>
</div>
<div class="text-label">
{{ $t('preview_sorting.sort_alphabet') }}
@@ -55,15 +53,8 @@
</li>
</ul>
</div>
</div>
</transition>
<!-- TODO: tento fade tu nemusi byt, mame uz jeden hlavny implementovany pre appku, iba odpalit spravny event, pozri Vignette.vue-->
<transition name="fade">
<div v-show="isVisible" class="vignette" @click.self="close"></div>
</transition>
</div>
</template>
<script>
@@ -104,7 +95,7 @@ export default {
methods: {
close() {
this.isVisible = false
events.$emit('mobileSortingAndPreview-close')
events.$emit('mobileSortingAndPreview', false)
},
sort(field) {
@@ -127,8 +118,11 @@ export default {
this.$getDataByLocation()
},
changePreview(previewType) {
this.$store.dispatch('changePreviewType', previewType)
}
this.close()
},
},
mounted() {
@@ -138,13 +132,8 @@ export default {
this.filter.sort = sorting ? sorting.sort : 'DESC'
this.filter.field = sorting ? sorting.field : 'created_at'
// TODO: tento event by som zrefaktoroval do jedneho, nech mame menej kodu pre takuto jednoduchu operaciu
events.$on('mobileSortingAndPreview-open', () => {
this.isVisible = true
})
events.$on('mobileSortingAndPreview-close', () => {
this.isVisible = false
events.$on('mobileSortingAndPreview', (state) => {
this.isVisible = state
})
}
}
@@ -167,10 +156,7 @@ export default {
margin-right: 20px;
line-height: 0;
/*
TODO: preklep
*/
.aplhabet-icon {
.alphabet-icon {
/deep/ line,
/deep/ polyline {
stroke: $text;
@@ -272,7 +258,7 @@ export default {
}
}
.icon {
.aplhabet-icon {
.alphabet-icon {
/deep/ line,
/deep/ polyline {
stroke: $dark_mode_text_primary;

View File

@@ -52,7 +52,7 @@
methods: {
showMobileNavigation() {
events.$emit('show:mobile-navigation')
events.$emit('mobileSelecting-stop')
events.$emit('mobileSelecting:stop')
},
goBack() {

View File

@@ -41,7 +41,7 @@
</li>
<li class="menu-option" @click="sort('name')" >
<div class="icon">
<alphabet-icon size="17" class="aplhabet-icon"/>
<alphabet-icon size="17" class="alphabet-icon"/>
</div>
<div class="text-label">
{{$t('preview_sorting.sort_alphabet')}}
@@ -112,7 +112,12 @@
this.$getDataByLocation()
},
changePreview(previewType) {
this.$store.dispatch('changePreviewType' , previewType)
this.isVisible = false
events.$emit('sortingAndPreview', false)
}
},
mounted () {
@@ -123,12 +128,8 @@
this.filter.sort = sorting ? sorting.sort : 'DESC'
this.filter.field = sorting ? sorting.field : 'created_at'
events.$on('sortingAndPreview-open', () => {
this.isVisible = true
})
events.$on('sortingAndPreview-close', () => {
this.isVisible = false
events.$on('sortingAndPreview', (state) => {
this.isVisible = state
})
}
@@ -153,7 +154,7 @@
.icon {
margin-right: 20px;
line-height: 0;
.aplhabet-icon {
.alphabet-icon {
/deep/line,
/deep/polyline {
stroke:$text ;
@@ -244,7 +245,7 @@
color: $dark_mode_text_primary;
.icon {
.aplhabet-icon {
.alphabet-icon {
/deep/line,
/deep/polyline {
stroke:$dark_mode_text_primary ;

View File

@@ -18,6 +18,7 @@
closePopup() {
events.$emit('popup:close')
events.$emit('mobileMenu:hide')
events.$emit('mobileSortingAndPreview', false)
}
},
created() {
@@ -30,6 +31,10 @@
events.$on('alert:open', () => this.isVisibleVignette = true)
events.$on('success:open', () => this.isVisibleVignette = true)
events.$on('confirm:open', () => this.isVisibleVignette = true)
events.$on('mobileSortingAndPreviewVignette', (state) => {
this.isVisibleVignette = state
})
}
}
</script>

View File

@@ -301,9 +301,6 @@ const Helpers = {
title: i18n.t('popup_mimetypes_blacklist.title'),
message: i18n.t('popup_mimetypes_blacklist.message', {mimetype: fileType[1]}),
})
}else {
// TODO: tento else tu nemusi byt, defaultne je uz prednastaveny true cize ak sa nevykona podmienka vyssie tak vzdy bude true
validated = true
}
}
return validated
@@ -315,11 +312,11 @@ const Helpers = {
let actions = {
'base' : ['getFolder', [{ folder: folder, back: true, init: false, sorting:true}]],
'public' : ['browseShared', [{ folder: folder, back: true, init: false, sorting:true}]],
'trash' : ['getFolder', [{ folder: folder, back: true, init: false, sorting:true}]],
'participant_uploads' : ['getParticipantUploads'],
'trash-root' : ['getTrash'],
'latest' : ['getLatest'],
'shared' : ['getShared'],
'trash-root' : ['getTrash'],
// 'trash' : ['getTrash'], TODO: skontrolovat a spojazdnit
'participant_uploads' : ['getParticipantUploads'],
}
this.$store.dispatch(...actions[folder.location])
@@ -330,14 +327,14 @@ const Helpers = {
// Get data of Navigator tree
this.$store.dispatch('getFolderTree')
}
Vue.prototype.$checkOS = function() {
// Handle styled scrollbar for Windows
if (navigator.userAgent.indexOf('Windows') != -1) {
let body = document.body
body.classList.add('windows')
}
}
}
}
export default Helpers
// Handle styled scrollbar for Windows
// TODO: toto treba dat jednoznacne na svoje spravne miesto
if (navigator.userAgent.indexOf('Windows') != -1) {
let body = document.body
body.classList.add('windows')
}
export default Helpers

View File

@@ -6,7 +6,9 @@
"preview": "更改预览",
"share": "Share item",
"upload": "上传文件",
"close": "Close"
"close": "Close",
"sorting_view": "Sorting and View",
"info_panel": "Info panel"
},
"activation": {
"stripe": {

View File

@@ -8,7 +8,9 @@
"upload": "Upload file",
"download": "Download item",
"print": "Print item",
"close": "Close"
"close": "Close",
"sorting_view": "Sorting and View",
"info_panel": "Info panel"
},
"activation": {
"stripe": {

View File

@@ -8,7 +8,9 @@
"upload": "Nahrať súbory",
"download": "Stiahnuť položku",
"print": "Vytlačiť položku",
"close": "Zatvoriť"
"close": "Zatvoriť",
"sorting_view" : "Zoradenie a zobrazenie ",
"info_panel" : "Informačný panel"
},
"activation": {
"stripe": {

View File

@@ -36,11 +36,11 @@ const actions = {
commit('STORE_PREVIOUS_FOLDER', getters.currentFolder)
let url = payload.folder.location === 'trash'
? '/folders/' + payload.folder.unique_id + '?trash=true'
: '/folders/' + payload.folder.unique_id
? '/folders/' + payload.folder.unique_id + getters.sorting.URI + '&trash=true'
: '/folders/' + payload.folder.unique_id + getters.sorting.URI
axios
.get(getters.api + url + getters.sorting.URI)
.get(getters.api + url)
.then(response => {
commit('LOADING_STATE', {loading: false, data: response.data})
commit('STORE_CURRENT_FOLDER', payload.folder)