Merge remote-tracking branch 'origin/version-v1.8.1'

# Conflicts:
#	config/vuefilemanager.php
#	public/chunks/admin-account.js
#	public/chunks/admin.js
#	public/chunks/app-appearance.js
#	public/chunks/app-billings.js
#	public/chunks/app-email.js
#	public/chunks/app-index.js
#	public/chunks/app-others.js
#	public/chunks/app-payments.js
#	public/chunks/app-settings.js
#	public/chunks/app-setup.js
#	public/chunks/billings-detail.js
#	public/chunks/contact-us.js
#	public/chunks/dashboard.js
#	public/chunks/database.js
#	public/chunks/environment-setup.js
#	public/chunks/files.js
#	public/chunks/files~chunks/shared-files~chunks/shared-page.js
#	public/chunks/installation-disclaimer.js
#	public/chunks/invoices.js
#	public/chunks/landing-page.js
#	public/chunks/pages.js
#	public/chunks/plan-create.js
#	public/chunks/plan-delete.js
#	public/chunks/plan-settings.js
#	public/chunks/plan-subscribers.js
#	public/chunks/plan.js
#	public/chunks/plans.js
#	public/chunks/profile.js
#	public/chunks/purchase-code.js
#	public/chunks/settings-create-payment-methods.js
#	public/chunks/settings-invoices.js
#	public/chunks/settings-payment-methods.js
#	public/chunks/settings-storage.js
#	public/chunks/settings-subscription.js
#	public/chunks/settings.js
#	public/chunks/shared-files.js
#	public/chunks/shared-page.js
#	public/chunks/sign-up.js
#	public/chunks/stripe-credentials.js
#	public/chunks/subscription-plans.js
#	public/chunks/subscription-service.js
#	public/chunks/upgrade-billing.js
#	public/chunks/upgrade.js
#	public/chunks/user-create.js
#	public/chunks/user-delete.js
#	public/chunks/user-detail.js
#	public/chunks/user-invoices.js
#	public/chunks/user-password.js
#	public/chunks/user-storage.js
#	public/chunks/user-subscription.js
#	public/chunks/user.js
#	public/chunks/users.js
#	public/js/main.js
#	public/mix-manifest.json
#	resources/js/views/FilePages/Files.vue
This commit is contained in:
Peter Papp
2021-02-07 18:05:56 +01:00
140 changed files with 17575 additions and 1524 deletions

View File

@@ -12,6 +12,7 @@
<!--Mobile Navigation-->
<MobileNavigation/>
<!-- Processing popup for zip -->
<ProcessingPopup/>
<!--Confirm Popup-->
@@ -58,7 +59,6 @@
<CookieDisclaimer/>
<!--Background vignette-->
<Vignette/>
</div>
</template>

View File

@@ -23,7 +23,7 @@
<!-- Single options -->
<OptionGroup v-if="multiSelectContextMenu">
<Option @click.native="restoreItem" v-if="item" :title="$t('context_menu.restore')" icon="restore"/>
<Option @click.native="deleteItem" :title="$t('context_menu.delete')" icon="trash"/>
<Option @click.native="deleteItem" v-if="item" :title="$t('context_menu.delete')" icon="trash"/>
<Option @click.native="emptyTrash" :title="$t('context_menu.empty_trash')" icon="empty-trash"/>
</OptionGroup>
@@ -34,6 +34,7 @@
<!-- Multi options -->
<OptionGroup v-if="!multiSelectContextMenu">
<Option @click.native="restoreItem" v-if="item" :title="$t('context_menu.restore')" icon="restore"/>
<Option @click.native="deleteItem" :title="$t('context_menu.delete')" icon="trash"/>
<Option @click.native="emptyTrash" :title="$t('context_menu.empty_trash')" icon="empty-trash"/>
</OptionGroup>
@@ -62,6 +63,7 @@
<OptionGroup v-if="item && multiSelectContextMenu">
<Option @click.native="ItemDetail" :title="$t('context_menu.detail')" icon="detail"/>
<Option @click.native="downloadItem" v-if="!isFolder" :title="$t('context_menu.download')" icon="download"/>
<Option @click.native="downloadFolder" v-if="isFolder" :title="$t('context_menu.zip_folder')" icon="zip-folder"/>
</OptionGroup>
<!-- Multi options -->
@@ -83,13 +85,15 @@
<!-- Base location with MASTER permission-->
<div v-if="$isThisLocation(['base', 'participant_uploads', 'latest']) && $checkPermission('master') && !showFromPreview" id="menu-list" class="menu-options">
<!-- No Files options -->
<OptionGroup v-if="!$isThisLocation(['participant_uploads', 'latest']) && multiSelectContextMenu && !item">
<Option @click.native="createFolder" :title="$t('context_menu.create_folder')" icon="create-folder"/>
</OptionGroup>
<!-- Single options -->
<OptionGroup v-if="!$isThisLocation(['participant_uploads', 'latest']) && multiSelectContextMenu">
<Option @click.native="addToFavourites" v-if="item && isFolder " :title="isInFavourites
? $t('context_menu.remove_from_favourites')
: $t('context_menu.add_to_favourites')" icon="favourites"/>
<Option @click.native="createFolder" :title="$t('context_menu.create_folder')" icon="create-folder"/>
<OptionGroup v-if="!$isThisLocation(['participant_uploads', 'latest']) && item && multiSelectContextMenu && isFolder">
<Option @click.native="addToFavourites" :title="isInFavourites ? $t('context_menu.remove_from_favourites') : $t('context_menu.add_to_favourites')" icon="favourites"/>
</OptionGroup>
@@ -106,6 +110,7 @@
<OptionGroup v-if="item && multiSelectContextMenu ">
<Option @click.native="ItemDetail" :title="$t('context_menu.detail')" icon="detail"/>
<Option @click.native="downloadItem" v-if="!isFolder" :title="$t('context_menu.download')" icon="download"/>
<Option @click.native="downloadFolder" v-if="isFolder" :title="$t('context_menu.zip_folder')" icon="zip-folder"/>
</OptionGroup>
<!-- Multi options -->
@@ -113,7 +118,6 @@
<Option @click.native="addToFavourites" v-if="item && !hasFile" :title=" isInFavourites
? $t('context_menu.remove_from_favourites')
: $t('context_menu.add_to_favourites')" icon="favourites"/>
<Option @click.native="createFolder" :title="$t('context_menu.create_folder')" icon="create-folder"/>
</OptionGroup>
<OptionGroup v-if="item && !multiSelectContextMenu">
@@ -129,11 +133,13 @@
<!-- Base & Public location with EDITOR permission-->
<div v-if="$isThisLocation(['base', 'public']) && $checkPermission('editor') && !showFromPreview " id="menu-list" class="menu-options">
<!-- Single options -->
<OptionGroup v-if="multiSelectContextMenu">
<!-- No Files options -->
<OptionGroup v-if="multiSelectContextMenu && !item">
<Option @click.native="createFolder" :title="$t('context_menu.create_folder')" icon="create-folder"/>
</OptionGroup>
<!-- Single options -->
<OptionGroup v-if="item && multiSelectContextMenu">
<Option @click.native="renameItem" :title=" $t('context_menu.rename')" icon="rename"/>
<Option @click.native="moveItem" :title="$t('context_menu.move')" icon="move-item"/>
@@ -143,12 +149,10 @@
<OptionGroup v-if="item && multiSelectContextMenu">
<Option @click.native="ItemDetail" :title="$t('context_menu.detail')" icon="detail"/>
<Option @click.native="downloadItem" v-if="!isFolder" :title="$t('context_menu.download')" icon="download"/>
<Option @click.native="downloadFolder" v-if="isFolder" :title="$t('context_menu.zip_folder')" icon="zip-folder"/>
</OptionGroup>
<!-- Multi options -->
<OptionGroup v-if="!multiSelectContextMenu">
<Option @click.native="createFolder" :title="$t('context_menu.create_folder')" icon="create-folder"/>
</OptionGroup>
<OptionGroup v-if="item && !multiSelectContextMenu">
<Option @click.native="moveItem" :title="$t('context_menu.move')" icon="move-item"/>
@@ -167,6 +171,7 @@
<OptionGroup v-if="item && multiSelectContextMenu">
<Option @click.native="ItemDetail" :title="$t('context_menu.detail')" icon="detail"/>
<Option @click.native="downloadItem" v-if="!isFolder" :title="$t('context_menu.download')" icon="download"/>
<Option @click.native="downloadFolder" v-if="isFolder" :title="$t('context_menu.zip_folder')" icon="zip-folder"/>
</OptionGroup>
<!-- Multi options -->
@@ -250,11 +255,21 @@ export default {
},
methods: {
downloadFolder(){
this.$store.dispatch('downloadFolder' , this.item)
},
emptyTrash() {
this.$store.dispatch('emptyTrash')
},
restoreItem() {
this.$store.dispatch('restoreItem', this.item)
// If is item not in selected items restore just this single item
if(!this.fileInfoDetail.includes(this.item))
this.$store.dispatch('restoreItem', this.item)
// If is item in selected items restore all items from fileInfoDetail
if(this.fileInfoDetail.includes(this.item))
this.$store.dispatch('restoreItem', null)
},
shareCancel() {
this.$store.dispatch('shareCancel')

View File

@@ -25,22 +25,22 @@
<SearchBar/>
</div>
<!--Files controlls-->
<!--Creating controls-->
<div class="toolbar-button-wrapper" v-if="$checkPermission(['master', 'editor'])">
<ToolbarButtonUpload :class="{ 'is-inactive': canUploadInView || !hasCapacity }" :action="$t('actions.upload')"/>
<ToolbarButton :class="{ 'is-inactive': canCreateFolderInView }" @click.native="createFolder" source="folder-plus" :action="$t('actions.create_folder')"/>
</div>
<div class="toolbar-button-wrapper" v-if="$checkPermission(['master', 'editor'])">
<!--File Controls-->
<div class="toolbar-button-wrapper" v-if="$checkPermission(['master', 'editor']) && ! $isMobile()">
<ToolbarButton source="move" :class="{ 'is-inactive': canMoveInView }" :action="$t('actions.move')" @click.native="moveItem"/>
<ToolbarButton v-if="!$isThisLocation(['public'])" source="share" :class="{ 'is-inactive': canShareInView }" :action="$t('actions.share')" @click.native="shareItem"/>
<ToolbarButton source="trash" :class="{ 'is-inactive': canDeleteInView }" :action="$t('actions.delete')" @click.native="deleteItem"/>
</div>
<!--View options-->
<!--View Controls-->
<div class="toolbar-button-wrapper">
<ToolbarButton source="preview-sorting" class="preview-sorting" :action="$t('actions.sorting_view')" :class="{ active: sortingAndPreview }" @click.stop.native="sortingAndPreview = !sortingAndPreview"/>
<ToolbarButton :action="$t('actions.info_panel')" :class="{ active: fileInfoVisible }" @click.native="$store.dispatch('fileInfoToggle')" source="info"/>
</div>
</div>
@@ -103,7 +103,7 @@ export default {
return !this.$isThisLocation(['base', 'public'])
},
canDeleteInView() {
return !this.$isThisLocation([
let locations = [
'trash',
'trash-root',
'base',
@@ -111,19 +111,22 @@ export default {
'latest',
'shared',
'public'
])
]
return !this.$isThisLocation(locations) || this.fileInfoDetail.length === 0
},
canUploadInView() {
return !this.$isThisLocation(['base', 'public'])
},
canMoveInView() {
return !this.$isThisLocation([
let locations = [
'base',
'participant_uploads',
'latest',
'shared',
'public'
])
]
return !this.$isThisLocation(locations) || this.fileInfoDetail.length === 0
},
canShareInView() {
let locations = [
@@ -134,7 +137,7 @@ export default {
'public'
]
return !this.$isThisLocation(locations) || this.fileInfoDetail.length > 1
return !this.$isThisLocation(locations) || this.fileInfoDetail.length > 1 || this.fileInfoDetail.length === 0
}
},
data() {

View File

@@ -1,72 +1,77 @@
<template>
<MultiSelected :title="title" :subtitle="subtitle" id="multi-select-ui" v-show="dragged" />
<MultiSelected :title="title" :subtitle="subtitle" id="multi-select-ui" v-show="isVisible"/>
</template>
<script>
import MultiSelected from '@/components/FilesView/MultiSelected'
import {mapGetters} from 'vuex'
import {events} from '@/bus'
import { mapGetters } from 'vuex'
import { events } from '@/bus'
export default {
name:"DragUI",
components: {MultiSelected},
computed: {
...mapGetters(['fileInfoDetail']),
title(){
export default {
name: 'DragUI',
components: { MultiSelected },
computed: {
...mapGetters(['fileInfoDetail']),
title() {
let filesLength = this.fileInfoDetail.length,
hasDraggedItem = this.fileInfoDetail.includes(this.draggedItem)
// Title for multiple selected items
if(this.fileInfoDetail.length > 1 && this.fileInfoDetail.includes(this.draggedItem)) {
return this.$t('file_detail.selected_multiple')
}
// Title for multiple selected items
if (filesLength > 1 && hasDraggedItem) {
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) {
// 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
}
}
},
},
data () {
return {
dragged: false,
draggedItem: undefined
// Title for single item
if ((filesLength < 2 || !hasDraggedItem) && this.draggedItem) {
return this.draggedItem.name
}
},
mounted () {
subtitle() {
let filesLength = this.fileInfoDetail.length,
hasDraggedItem = this.fileInfoDetail.includes(this.draggedItem)
// Hnadle Drag & Drop Ghost show
events.$on('dragstart', (data) => {
setTimeout(() => {
this.dragged = true
}, 50);
this.draggedItem = data
})
events.$on('drop', () => {
this.dragged = false
})
// Subtitle for multiple selected items
if (filesLength > 1 && hasDraggedItem) {
return filesLength + ' ' + this.$tc('file_detail.items', filesLength)
}
if ((filesLength < 2 || !hasDraggedItem) && 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
}
}
}
},
data() {
return {
isVisible: false,
draggedItem: undefined
}
},
created() {
// Handle Drag & Drop Ghost show
events.$on('dragstart', data => {
this.draggedItem = data
setTimeout(() => {
this.isVisible = true
}, 100)
})
events.$on('drop', () => {
this.isVisible = false
})
}
}
</script>
<style lang="scss" scoped>
@@ -82,38 +87,44 @@ import {events} from '@/bus'
padding: 10px;
border-radius: 8px;
box-shadow: 0 7px 25px 1px rgba(0, 0, 0, 0.12);
background:white;
/deep/.text{
.title {
color: $text;
}
.count {
color: $text-muted;
}
background: white;
/deep/ .text {
.title {
color: $text;
}
/deep/.icon-wrapper {
.icon {
stroke: $theme;
}
.count {
color: $text-muted;
}
}
/deep/ .icon-wrapper {
.icon {
stroke: $theme;
}
}
}
@media (prefers-color-scheme: dark) {
#multi-select-ui {
background: $dark_mode_foreground;
/deep/.text {
/deep/ .text {
.title {
color: $dark_mode_text_primary;
}
.count {
color: $dark_mode_text_secondary;
}
}
/deep/.icon-wrapper {
.icon {
}
/deep/ .icon-wrapper {
.icon {
stroke: $theme;
}
}
}
}
}
}

View File

@@ -53,7 +53,7 @@
<div class="sharelink">
<lock-icon v-if="isLocked" @click="shareItemOptions" class="lock-icon" size="17"></lock-icon>
<unlock-icon v-if="! isLocked" @click="shareItemOptions" class="lock-icon" size="17"></unlock-icon>
<CopyInput class="copy-sharelink" size="small" :value="fileInfoDetail[0].shared.link"/>
<CopyInput class="copy-sharelink" size="small" :item="fileInfoDetail[0]"/>
</div>
</ListInfoItem>

View File

@@ -25,14 +25,18 @@
<!--Image thumbnail-->
<img loading="lazy" v-if="isImage && data.thumbnail" class="image" :src="data.thumbnail" :alt="data.name"/>
<!-- If folder have set emoji -->
<Emoji class="emoji" v-if="isFolder && folderIconHandle" :emoji="folderIconHandle" size="80" />
<!--Else show only folder icon-->
<FontAwesomeIcon v-if="isFolder" :class="{'is-deleted': isDeleted}" class="folder-icon" icon="folder"/>
<FontAwesomeIcon v-if="isFolder && !folderIconHandle" :ref="`folder${this.data.unique_id}`" :class="{'is-deleted': isDeleted}" class="folder-icon" icon="folder"/>
</div>
<!--Name-->
<div class="item-name">
<!--Name-->
<b ref="name" @input="renameItem" @keydown.delete.stop :contenteditable="canEditName" class="name">
<b :ref="this.data.unique_id" @input="renameItem" @keydown.delete.stop @click.stop :contenteditable="canEditName" class="name">
{{ itemName }}
</b>
@@ -67,6 +71,7 @@
<script>
import { LinkIcon, UserPlusIcon, CheckIcon } from 'vue-feather-icons'
import Emoji from '@/components/Others/Emoji'
import { debounce } from 'lodash'
import { mapGetters } from 'vuex'
import { events } from '@/bus'
@@ -77,12 +82,28 @@ export default {
components: {
UserPlusIcon,
CheckIcon,
LinkIcon
LinkIcon,
Emoji
},
computed: {
...mapGetters([
'FilePreviewType', 'sharedDetail', 'fileInfoDetail'
]),
folderIconHandle(){
// If folder have set some color
if(this.data.icon_color) {
this.$nextTick(() => {
this.$refs[`folder${this.data.unique_id}`].firstElementChild.style.fill = `${this.data.icon_color}`
})
return false
}
// If folder have set some emoji
if(this.data.icon_emoji)
return this.data.icon_emoji
},
...mapGetters({ allData: 'data' }),
isClicked() {
return this.fileInfoDetail.some(element => element.unique_id == this.data.unique_id)
@@ -158,6 +179,10 @@ export default {
events.$emit('unClick')
if (!this.$isMobile()) {
// After click deselect new folder rename input
document.getSelection().removeAllRanges();
if (e.ctrlKey || e.metaKey && !e.shiftKey) {
// Click + Ctrl
if (this.fileInfoDetail.some(item => item.unique_id === this.data.unique_id)) {
@@ -267,6 +292,14 @@ export default {
created() {
this.itemName = this.data.name
events.$on('newFolder:focus', (unique_id) => {
if(this.data.unique_id == unique_id) {
this.$refs[unique_id].focus()
document.execCommand('selectAll')
}
})
events.$on('mobileSelecting:start', () => {
this.multiSelectMode = true
this.$store.commit('CLEAR_FILEINFO_DETAIL')
@@ -318,7 +351,7 @@ export default {
}
.select-box-active {
background-color: $text;
background-color: $theme;
.icon {
stroke: white;
@@ -452,6 +485,10 @@ export default {
display: flex;
align-items: center;
.emoji {
margin: 0 auto;
}
.file-link {
display: block;
}
@@ -568,10 +605,10 @@ export default {
}
.select-box-active {
background-color: #f4f5f6;
background-color: lighten($theme, 5%);
.icon {
stroke: $text;
stroke: white;
}
}

View File

@@ -1,9 +1,14 @@
<template>
<div class="file-wrapper" @click.stop="clickedItem" @dblclick="goToItem" spellcheck="false">
<!--List preview-->
<div :draggable="canDrag" @dragstart="$emit('dragstart')" @drop="
drop()
area = false" @dragleave="dragLeave" @dragover.prevent="dragEnter" class="file-item" :class="{'is-clicked' : isClicked , 'no-clicked' : !isClicked && this.$isMobile(), 'is-dragenter': area }">
<div
:draggable="canDrag"
@dragstart="$emit('dragstart')"
@drop="drop()"
@dragleave="dragLeave"
@dragover.prevent="dragEnter"
class="file-item" :class="{'is-clicked' : isClicked , 'no-clicked' : !isClicked && this.$isMobile(), 'is-dragenter': area }"
>
<!-- MultiSelecting for the mobile version -->
<transition name="slide-from-left">
<div class="check-select" v-if="mobileMultiSelect">
@@ -26,13 +31,17 @@
<!--Image thumbnail-->
<img loading="lazy" v-if="isImage && data.thumbnail" class="image" :src="data.thumbnail" :alt="data.name"/>
<!-- If folder have set emoji -->
<Emoji v-if="isFolder && folderIconHandle" :emoji="folderIconHandle" size="52" />
<!--Else show only folder icon-->
<FontAwesomeIcon v-if="isFolder" :class="{ 'is-deleted': isDeleted }" class="folder-icon" icon="folder"/>
<FontAwesomeIcon v-if="isFolder && !folderIconHandle" :ref="`folder${this.data.unique_id}`" :class="{ 'is-deleted': isDeleted }" class="folder-icon" icon="folder"/>
</div>
<!--Name-->
<div class="item-name">
<b ref="name" @input="renameItem" @keydown.delete.stop :contenteditable="canEditName" class="name">
<b :ref="this.data.unique_id" @input="renameItem" @keydown.delete.stop @click.stop :contenteditable="canEditName" class="name">
{{ itemName }}
</b>
@@ -69,6 +78,7 @@
<script>
import { LinkIcon, UserPlusIcon, CheckIcon } from 'vue-feather-icons'
import Emoji from '@/components/Others/Emoji'
import { debounce } from 'lodash'
import { mapGetters } from 'vuex'
import { events } from '@/bus'
@@ -79,11 +89,27 @@ export default {
components: {
UserPlusIcon,
LinkIcon,
CheckIcon
CheckIcon,
Emoji
},
computed: {
...mapGetters(['FilePreviewType', 'fileInfoDetail']),
...mapGetters({ allData: 'data' }),
folderIconHandle(){
// If folder have set some icon color
if(this.data.icon_color) {
this.$nextTick(() => {
this.$refs[`folder${this.data.unique_id}`].firstElementChild.style.fill = `${this.data.icon_color}`
})
return false
}
// If folder have set some emoji
if(this.data.icon_emoji)
return this.data.icon_emoji
},
isClicked() {
return this.fileInfoDetail.some(element => element.unique_id == this.data.unique_id)
},
@@ -140,6 +166,7 @@ export default {
},
methods: {
drop() {
this.area = false
events.$emit('drop')
},
showItemActions() {
@@ -162,6 +189,9 @@ export default {
if (!this.$isMobile()) {
// After click deselect new folder rename input
document.getSelection().removeAllRanges();
if ((e.ctrlKey || e.metaKey) && !e.shiftKey) {
// Click + Ctrl
@@ -265,8 +295,17 @@ export default {
}, 300)
},
created() {
this.itemName = this.data.name
events.$on('newFolder:focus', (unique_id) => {
if(this.data.unique_id == unique_id) {
this.$refs[unique_id].focus()
document.execCommand('selectAll')
}
})
events.$on('mobileSelecting:start', () => {
this.mobileMultiSelect = true
this.$store.commit('CLEAR_FILEINFO_DETAIL')
@@ -289,6 +328,7 @@ export default {
@import '@assets/vue-file-manager/_variables';
@import '@assets/vue-file-manager/_mixins';
.slide-from-left-move {
transition: transform 300s ease;
}
@@ -328,10 +368,10 @@ export default {
}
.select-box-active {
background-color: #f4f5f6;
background-color: $theme;
.icon {
stroke: $text;
stroke: white;
}
}
}
@@ -533,10 +573,10 @@ export default {
}
.select-box-active {
background-color: lighten($dark_mode_foreground, 10%);
background-color: $theme;
.icon {
stroke: $theme;
stroke: white;
}
}
}

View File

@@ -6,7 +6,7 @@
<p class="title">{{ fileInfoDetail[0].name }}</p>
<span class="file-count"> ({{ showingImageIndex + ' ' + $t('pronouns.of') + ' ' + filteredFiles.length }}) </span>
</div>
<span id="fast-preview-menu" class="fast-menu-icon" @click.stop="menuOpen" v-if="$checkPermission(['master', 'editor'])">
<span id="fast-preview-menu" class="fast-menu-icon" @click.stop="menuOpen" v-if="$checkPermission(['master', 'editor', 'visitor'])">
<more-horizontal-icon class="more-icon" size="14"> </more-horizontal-icon>
</span>
</div>

View File

@@ -5,7 +5,7 @@
<audio class="file audio" :class="{ 'file-shadow': !isMobileDevice }" v-if="fileInfoDetail[0].type == 'audio'" :src="currentFile.file_url" controlsList="nodownload" controls></audio>
<img v-if="fileInfoDetail[0].type === 'image' && currentFile.thumbnail" class="file" :class="{ 'file-shadow': !isMobileDevice }" id="image" :src="currentFile.file_url" />
<div class="video-wrapper" v-if="fileInfoDetail[0].type === 'video' && currentFile.file_url">
<video :src="currentFile.file_url" class="video" :class="{ 'file-shadow': !isMobileDevice }" controlsList="nodownload" disablePictureInPicture playsinline controls />
<video :src="currentFile.file_url" class="video" :class="{ 'file-shadow': !isMobileDevice }" controlsList="nodownload" disablePictureInPicture playsinline controls autoplay />
</div>
</div>
</div>

File diff suppressed because it is too large Load Diff

View File

@@ -42,7 +42,7 @@ import { events } from '@/bus'
@media (prefers-color-scheme: dark) {
.options {
background: $dark_mode_background;
background: $dark_mode_foreground;
}
}

View File

@@ -1,5 +1,5 @@
<template>
<li class="menu-option">
<li class="menu-option" :class="[icon === 'trash' ? 'danger' : '']">
<div class="icon">
<trash-2-icon v-if="icon === 'trash'" size="17"></trash-2-icon>
<life-buoy-icon v-if="icon === 'restore'" size="17"></life-buoy-icon>
@@ -12,6 +12,7 @@
<star-icon v-if="icon === 'favourites'" size="17"></star-icon>
<folder-plus-icon v-if="icon === 'create-folder'" size="17"></folder-plus-icon>
<smile-icon v-if="icon === 'no-options'" size="17"></smile-icon>
<paperclip-icon v-if="icon === 'zip-folder'" size="17"></paperclip-icon>
</div>
<div class="text-label">
{{ title }}
@@ -24,6 +25,7 @@ import {
CornerDownRightIcon,
DownloadCloudIcon,
FolderPlusIcon,
PaperclipIcon,
LifeBuoyIcon,
Trash2Icon,
Edit2Icon,
@@ -41,6 +43,7 @@ import {
CornerDownRightIcon,
DownloadCloudIcon,
FolderPlusIcon,
PaperclipIcon,
LifeBuoyIcon,
Trash2Icon,
SmileIcon,
@@ -57,6 +60,22 @@ import {
@import "@assets/vue-file-manager/_variables";
@import "@assets/vue-file-manager/_mixins";
.danger {
.text-label {
color: $danger !important;
}
.icon {
path,
line,
polyline,
rect,
circle,
polygon {
stroke: $danger !important;
}
}
}
.menu-option {
white-space: nowrap;
font-weight: 700;
@@ -95,6 +114,11 @@ import {
}
}
@media (prefers-color-scheme: dark) {
.danger {
&:hover {
background: rgba($danger, 0.1) !important;
}
}
.menu-option {
color: $dark_mode_text_primary;

View File

@@ -1,13 +1,13 @@
<template>
<transition name="popup">
<div class="popup" v-if="isZippingFiles">
<div class="popup" v-if="processingPopup">
<div class="popup-wrapper">
<div class="popup-content">
<div class="spinner-wrapper">
<Spinner/>
</div>
<h1 class="title">{{ $t('popup_zipping.title') }}</h1>
<p class="message">{{ $t('popup_zipping.message') }}</p>
<h1 class="title">{{ processingPopup.title }}</h1>
<p class="message">{{ processingPopup.message }}</p>
</div>
</div>
</div>
@@ -25,7 +25,7 @@ export default {
},
computed: {
...mapGetters([
'isZippingFiles'
'processingPopup'
])
}
}

View File

@@ -84,8 +84,6 @@ export default {
this.filter.field = field
console.log(this.filter);
// Set sorting direction
if (this.filter.sort === 'DESC')
this.filter.sort = 'ASC'

View File

@@ -5,11 +5,17 @@
:description="index.header_description"
></PageTitle>
<router-link class="sign-up-button" :to="{name: 'SignUp'}">
<!--User registration button-->
<router-link v-if="config.userRegistration" class="sign-up-button" :to="{name: 'SignUp'}">
<AuthButton class="button" icon="chevron-right" :text="$t('page_index.sign_up_button')" />
</router-link>
<div class="features">
<!--User login button-->
<router-link v-if="! config.userRegistration" class="sign-up-button" :to="{name: 'SignIn'}">
<AuthButton class="button" icon="chevron-right" :text="$t('page_index.menu.log_in')" />
</router-link>
<div class="features" v-if="config.isSaaS">
<div class="feature">
<credit-card-icon size="19" class="feature-icon"></credit-card-icon>
<b class="feature-title">{{ $t('page_index.sign_feature_1') }}</b>

View File

@@ -1,7 +1,7 @@
<template>
<div class="cookie-wrapper" v-if="isVisibleDisclaimer && config.isSaaS">
<span class="close-icon">
<x-icon @click="closeDisclaimer" size="12"></x-icon>
<span @click="closeDisclaimer" class="close-icon">
<x-icon size="12"></x-icon>
</span>
<i18n path="cookie_disclaimer.description" tag="p">
<router-link :to="{name: 'DynamicPage', params: {slug: 'cookie-policy'}}">{{ $t('cookie_disclaimer.button') }}</router-link>

View File

@@ -13,7 +13,7 @@
<!--Set password-->
<ValidationProvider tag="div" mode="passive" class="input-wrapper password" name="Title" rules="required" v-slot="{ errors }">
<label class="input-label">{{ $t('popup_create_folder.label') }}:</label>
<input v-model="name" :class="{'is-error': errors[0]}" type="text" :placeholder="$t('popup_create_folder.placeholder')">
<input v-model="name" :class="{'is-error': errors[0]}" type="text" ref="input" :placeholder="$t('popup_create_folder.placeholder')">
<span class="error-message" v-if="errors[0]">{{ errors[0] }}</span>
</ValidationProvider>
</ValidationObserver>
@@ -82,6 +82,15 @@
}
},
},
mounted() {
events.$on('popup:open', ({name}) => {
if (name === 'create-folder')
this.$nextTick(() => {
this.$refs.input.focus()
})
})
}
}
</script>

View File

@@ -0,0 +1,25 @@
<template>
<div v-show="transferEmoji" :style="{width: `${size}px`, height: `${size}px`}" v-html="transferEmoji"/>
</template>
<script>
import twemoji from 'twemoji'
export default {
name: 'Emoji',
props: ['emoji', 'size'],
computed: {
transferEmoji () {
// Transfer single emoji to twemoji
return twemoji.parse(this.emoji.char, {
folder: 'svg',
ext: '.svg',
attributes: () => ({
loading: 'lazy',
})
})
}
},
}
</script>

View File

@@ -1,95 +1,190 @@
<template>
<div class="inline-wrapper icon-append copy-input" :class="size" @click="copyUrl">
<input ref="sel" :value="value" id="link-input" type="text" class="input-text" readonly>
<div class="icon">
<link-icon v-if="! isCopiedLink" size="14"></link-icon>
<check-icon v-if="isCopiedLink" size="14"></check-icon>
<input ref="sel" :value="item.shared.link" id="link-input" type="text" class="input-text" readonly>
<div class="multi-icon">
<div class="icon-item">
<link-icon v-if="! isCopiedLink" size="14"></link-icon>
<check-icon v-if="isCopiedLink" size="14"></check-icon>
</div>
<div class="icon-item" @click.stop.prevent="menuForEmail">
<send-icon size="14"></send-icon>
</div>
</div>
</div>
</template>
<script>
import { LinkIcon, CheckIcon } from 'vue-feather-icons'
import { LinkIcon, CheckIcon, SendIcon } from 'vue-feather-icons'
import { events } from '@/bus'
export default {
name: 'CopyInput',
props: ['size', 'value'],
components: {
CheckIcon,
LinkIcon,
export default {
name: 'CopyInput',
props: ['size', 'item'],
components: {
CheckIcon,
LinkIcon,
SendIcon
},
data() {
return {
isCopiedLink: false
}
},
methods: {
menuForEmail() {
events.$emit('popup:open', {
name: 'share-edit',
item: this.item,
sentToEmail: true,
})
},
data() {
return {
isCopiedLink: false,
}
},
methods: {
copyUrl() {
copyUrl() {
// Get input value
var copyText = document.getElementById("link-input");
// Get input value
var copyText = document.getElementById('link-input')
// select link
copyText.select();
copyText.setSelectionRange(0, 99999);
// select link
copyText.select()
copyText.setSelectionRange(0, 99999)
// Copy
document.execCommand("copy");
// Copy
document.execCommand('copy')
// Mark button as copied
this.isCopiedLink = true
// Mark button as copied
this.isCopiedLink = true
// Reset copy button
setTimeout(() => {this.isCopiedLink = false}, 1000)
},
// Reset copy button
setTimeout(() => {
this.isCopiedLink = false
}, 1000)
}
}
}
</script>
<style lang="scss" scoped>
@import '@assets/vue-file-manager/_variables';
@import '@assets/vue-file-manager/_mixins';
@import "@assets/vue-file-manager/_inapp-forms.scss";
@import "@assets/vue-file-manager/_forms.scss";
@import '@assets/vue-file-manager/_variables';
@import '@assets/vue-file-manager/_mixins';
@import "@assets/vue-file-manager/_inapp-forms.scss";
@import "@assets/vue-file-manager/_forms.scss";
// Single page
.copy-input {
.multi-icon {
display: flex;
align-items: center;
background: $light_background;
border-bottom-right-radius: 8px;
border-top-right-radius: 8px;
&.small {
line,
path,
polygon {
stroke: $text !important;
}
&.icon-append {
.icon-item {
padding: 9px 10px;
display: flex;
align-items: center;
border-left: 1px solid $light_mode_border_darken;
cursor: pointer;
.icon {
padding: 10px;
}
}
&:hover {
background: $text;
input {
padding: 6px 10px;
@include font-size(13);
line,
polyline,
path,
polygon {
stroke: white !important;
}
}
.icon {
cursor: pointer;
&:first-child {
border-left: none;
}
&:last-child {
border-bottom-right-radius: 8px;
border-top-right-radius: 8px;
}
}
}
// Single page
.copy-input {
border: 1px solid $light_mode_border_darken;
border-radius: 8px;
&.small {
&.icon-append {
.icon {
padding: 10px;
}
}
input {
text-overflow: ellipsis;
&:disabled {
color: $text;
cursor: pointer;
}
padding: 6px 10px;
@include font-size(13);
}
}
@media (prefers-color-scheme: dark) {
.icon {
cursor: pointer;
}
.copy-input {
input {
color: $dark_mode_text_primary;
}
input {
text-overflow: ellipsis;
box-shadow: none;
&:disabled {
color: $text;
cursor: pointer;
}
}
}
@media (prefers-color-scheme: dark) {
.copy-input {
border-color: #333333;
}
.multi-icon {
background: $dark_mode_foreground;
box-shadow: 0 1px 5px rgba(0, 0, 0, 0.12);
line,
path,
polygon {
stroke: $dark_mode_text_primary !important;
}
.icon-item {
border-color: #333333;
&:hover {
background: rgba($theme, 0.1);
line,
polyline,
path,
polygon {
stroke: $theme !important;
}
}
}
}
.copy-input {
input {
color: $dark_mode_text_primary;
}
}
}
</style>

View File

@@ -35,12 +35,19 @@
}
p {
@include font-size(15);
font-size: 15px;
line-height: 1.6;
word-break: break-word;
font-weight: 600;
/deep/ a {
font-size: 15px;
color: $theme;
}
/deep/ b {
font-size: 15px;
font-weight: 700;
color: $theme;
}
}
@@ -71,10 +78,6 @@
}
}
@media only screen and (max-width: 960px) {
}
@media only screen and (max-width: 690px) {
.info-box {

View File

@@ -0,0 +1,197 @@
<template>
<div class="wrapper">
<label class="input-label">{{ label }}:</label>
<div class="input-wrapper" :class="{'is-error' : isError}" @click="$refs.input.focus()">
<div class="email-list">
<div class="email-tag" :class="{'mb-offset': getCharactersLength > 45}" v-for="(email, i) in emails" :key="i">
<span>{{ email }}</span>
<x-icon @click="removeEmail(email)" class="icon" size="14"/>
</div>
<input @keydown.delete=removeLastEmail($event) @keyup="handleEmail()" v-model="email" :size="inputSize" class="email-input" :placeholder="placeHolder" autocomplete="new-password" ref="input"/>
</div>
</div>
<span class="error-message" v-if="isError">{{ isError }}</span>
</div>
</template>
<script>
import { XIcon } from 'vue-feather-icons'
import { events } from '@/bus'
export default {
name: 'MultiEmailInput',
components: { XIcon },
props: ['isError', 'label'],
computed: {
getCharactersLength() {
return this.emails.join( '' ).length
},
placeHolder() {
return !this.emails.length ? this.$t( 'shared_form.email_placeholder' ) : ''
},
inputSize() {
return this.email && this.email.length > 14 ? this.email.length : 14
}
},
data() {
return {
emails: [],
email: undefined
}
},
methods: {
removeEmail( email ) {
this.emails = this.emails.filter( item => item !== email )
// After romove email send new emails list to parent
events.$emit( 'emailsInputValues', this.emails )
},
removeLastEmail( event ) {
// If is input empty and presse backspace remove last email from array
if ( event.code === 'Backspace' && this.email === '' )
this.emails.pop()
},
handleEmail() {
if ( this.email.length > 0 ) {
// Get index of @ and last dot
let lastDot = this.email.lastIndexOf( '.' )
let at = this.email.indexOf( '@' )
// Check if is after @ some dot, if email have @ anf if dont have more like one
if ( lastDot < at || at === -1 || this.email.match(/@/g).length > 1 ) return
// First email dont need to be separated by comma or space to be sended
if( this.emails.length === 0 )
events.$emit('emailsInputValues', [this.email])
// After come or backspace push the single email to array or emails
if ( this.email.includes(',') || this.email.includes(' ') ) {
let email = this.email.replace( /[","," "]/, '' )
this.email = ''
// Push single email to aray of emails
this.emails.push( email )
events.$emit( 'emailsInputValues', this.emails )
}
}
}
},
created() {
this.$nextTick(() => {
this.$refs.input.focus()
})
}
}
</script>
<style scoped lang="scss">
@import "@assets/vue-file-manager/_inapp-forms.scss";
@import '@assets/vue-file-manager/_forms';
.wrapper {
margin-bottom: 20px;
}
.input-label {
@include font-size(14);
font-weight: 700;
margin-bottom: 8px;
}
.input-wrapper {
margin-bottom: 0;
background: white;
max-width: 100%;
display: flex;
min-height: 50px;
border-radius: 8px;
padding: 6px 10px;
box-shadow: 0 1px 5px rgba(0, 0, 0, 0.12);
cursor: text;
border: 1px solid transparent;
@include transition(150ms);
&.is-error {
border: 1px solid $danger;
box-shadow: 0 0 7px rgba($danger, 0.3);
}
&:focus-within {
border: 1px solid $theme;
box-shadow: 0 1px 5px rgba($theme, 0.3);
}
.email-list {
display: flex;
flex-wrap: wrap;
.email-input {
font-size: 14px;
}
}
.email-tag {
white-space: nowrap;
display: flex;
padding: 5px 10px;
background: rgba($theme, .1);
border-radius: 8px;
margin-right: 5px;
align-items: center;
&.mb-offset {
margin-top: 3px;
margin-bottom: 3px;
}
span {
color: $theme;
font-weight: 700;
@include font-size(14);
}
.icon {
cursor: pointer;
margin-left: 4px;
}
}
.email-input {
width: auto;
border: none ;
font-weight: 700;
@include font-size(16);
padding-left: 11px;
&::placeholder {
color: rgba($text-muted, .5)
}
}
}
@media (prefers-color-scheme: dark) {
.input-wrapper {
background: $dark_mode_foreground;
.email-list {
.email-input {
background: $dark_mode_foreground;
color: $dark_mode_text_primary;
&::placeholder {
color: $dark_mode_text_secondary;
}
}
}
}
}
</style>

View File

@@ -2,7 +2,8 @@
<div class="popup-header">
<div class="icon">
<corner-down-right-icon v-if="icon === 'move'" size="15" class="title-icon"></corner-down-right-icon>
<link-icon v-if="icon === 'share'" size="17" class="title-icon"></link-icon>
<share-icon v-if="icon === 'share'" size="17" class="title-icon"></share-icon>
<!-- <link-icon v-if="icon === 'share'" size="17" class="title-icon"></link-icon> -->
<edit2-icon v-if="icon === 'edit'" size="17" class="title-icon"></edit2-icon>
</div>
<div class="label">
@@ -13,7 +14,7 @@
</template>
<script>
import {CornerDownRightIcon, LinkIcon, XIcon, Edit2Icon} from 'vue-feather-icons'
import {CornerDownRightIcon, LinkIcon, XIcon, Edit2Icon, ShareIcon} from 'vue-feather-icons'
import {events} from '@/bus'
export default {
@@ -23,6 +24,7 @@
],
components: {
CornerDownRightIcon,
ShareIcon,
Edit2Icon,
LinkIcon,
XIcon,

View File

@@ -58,7 +58,7 @@
left: 0;
right: 0;
bottom: 0;
z-index: 20;
z-index: 19;
overflow-y: auto;
display: grid;
padding: 40px;

View File

@@ -1,13 +1,13 @@
<template>
<PopupWrapper name="rename-item">
<!--Title-->
<PopupHeader :title="$t('popup_rename.title', {item: itemTypeTitle})" icon="edit" />
<PopupHeader :title="$t('popup_rename.title', {item: itemTypeTitle})" icon="edit"/>
<!--Content-->
<PopupContent>
<!--Item Thumbnail-->
<ThumbnailItem class="item-thumbnail" :item="pickedItem" info="metadata"/>
<ThumbnailItem class="item-thumbnail" :item="pickedItem" info="metadata" :setFolderIcon="setFolderIcon"/>
<!--Form to set sharing-->
<ValidationObserver @submit.prevent="changeName" ref="renameForm" v-slot="{ invalid }" tag="form" class="form-wrapper">
@@ -15,106 +15,189 @@
<!--Set password-->
<ValidationProvider tag="div" mode="passive" class="input-wrapper password" name="Name" rules="required" v-slot="{ errors }">
<label class="input-label">{{ $t('popup_rename.label') }}:</label>
<input v-model="pickedItem.name" :class="{'is-error': errors[0]}" type="text" :placeholder="$t('popup_rename.placeholder')">
<div class="input">
<input v-model="pickedItem.name" :class="{'is-error': errors[0]}" ref="input" type="text" :placeholder="$t('popup_rename.placeholder')">
<div @click="pickedItem.name = ''" class="close-icon-wrapper">
<x-icon class="close-icon" size="14"/>
</div>
</div>
<span class="error-message" v-if="errors[0]">{{ errors[0] }}</span>
</ValidationProvider>
<!--<SetFolderIcon v-if="isMoreOptions" :folderData="pickedItem" :unique_id="pickedItem.unique_id" />-->
<!-- <ActionButton v-if="pickedItem.type === 'folder'" @click.native.stop="moreOptions" :icon="isMoreOptions ? 'x' : 'pencil-alt'">{{ moreOptionsTitle }}</ActionButton> -->
</ValidationObserver>
</PopupContent>
<!--Actions-->
<PopupActions>
<ButtonBase
class="popup-button"
@click.native="$closePopup()"
button-style="secondary"
>{{ $t('popup_move_item.cancel') }}
<ButtonBase class="popup-button" @click.native="$closePopup()" button-style="secondary">{{ $t('popup_move_item.cancel') }}
</ButtonBase>
<ButtonBase
class="popup-button"
@click.native="changeName"
button-style="theme"
>{{ $t('popup_share_edit.save') }}
<ButtonBase class="popup-button" @click.native="changeName" button-style="theme">{{ $t('popup_share_edit.save') }}
</ButtonBase>
</PopupActions>
</PopupWrapper>
</template>
<script>
import {ValidationProvider, ValidationObserver} from 'vee-validate/dist/vee-validate.full'
import PopupWrapper from '@/components/Others/Popup/PopupWrapper'
import PopupActions from '@/components/Others/Popup/PopupActions'
import PopupContent from '@/components/Others/Popup/PopupContent'
import PopupHeader from '@/components/Others/Popup/PopupHeader'
import ThumbnailItem from '@/components/Others/ThumbnailItem'
import ActionButton from '@/components/Others/ActionButton'
import ButtonBase from '@/components/FilesView/ButtonBase'
import {required} from 'vee-validate/dist/rules'
import {events} from '@/bus'
import axios from 'axios'
import { ValidationProvider, ValidationObserver } from 'vee-validate/dist/vee-validate.full'
import PopupWrapper from '@/components/Others/Popup/PopupWrapper'
import PopupActions from '@/components/Others/Popup/PopupActions'
import PopupContent from '@/components/Others/Popup/PopupContent'
import PopupHeader from '@/components/Others/Popup/PopupHeader'
import SetFolderIcon from '@/components/Others/SetFolderIcon'
import ThumbnailItem from '@/components/Others/ThumbnailItem'
import ActionButton from '@/components/Others/ActionButton'
import ButtonBase from '@/components/FilesView/ButtonBase'
import { XIcon } from 'vue-feather-icons'
import { required } from 'vee-validate/dist/rules'
import { events } from '@/bus'
import axios from 'axios'
export default {
name: 'RenameItem',
components: {
ValidationProvider,
ValidationObserver,
ThumbnailItem,
ActionButton,
PopupWrapper,
PopupActions,
PopupContent,
PopupHeader,
ButtonBase,
required,
export default {
name: 'RenameItem',
components: {
ValidationProvider,
ValidationObserver,
SetFolderIcon,
ThumbnailItem,
ActionButton,
PopupWrapper,
PopupActions,
PopupContent,
PopupHeader,
ButtonBase,
required,
XIcon
},
computed: {
itemTypeTitle() {
return this.pickedItem && this.pickedItem.type === 'folder' ? this.$t('types.folder') : this.$t('types.file')
},
computed: {
itemTypeTitle() {
return this.pickedItem && this.pickedItem.type === 'folder' ? this.$t('types.folder') : this.$t('types.file')
},
},
data() {
return {
pickedItem: undefined,
}
},
methods: {
changeName() {
if (this.pickedItem.name && this.pickedItem.name !== '') {
let item = {
unique_id: this.pickedItem.unique_id,
type: this.pickedItem.type,
name: this.pickedItem.name
}
// Rename item request
this.$store.dispatch('renameItem', item)
// Rename item in view
events.$emit('change:name', item)
this.$closePopup()
}
},
},
mounted() {
// Show popup
events.$on('popup:open', args => {
if (args.name !== 'rename-item') return
// Store picked item
this.pickedItem = args.item
})
moreOptionsTitle() {
return this.isMoreOptions ? this.$t('shared_form.button_close_options') : this.$t('shared_form.button_folder_icon_open')
}
},
data() {
return {
pickedItem: undefined,
isMoreOptions: false,
setFolderIcon: undefined
}
},
methods: {
moreOptions() {
this.isMoreOptions = !this.isMoreOptions
this.setFolderIcon = undefined
},
changeName() {
if (this.pickedItem.name && this.pickedItem.name !== '') {
let item = {
unique_id: this.pickedItem.unique_id,
type: this.pickedItem.type,
name: this.pickedItem.name,
folder_icon: this.setFolderIcon ? this.setFolderIcon : null
}
// Rename item request
this.$store.dispatch('renameItem', item)
// Rename item in view
events.$emit('change:name', item)
this.$closePopup()
}
}
},
mounted() {
// Show popup
events.$on('popup:open', args => {
if (args.name !== 'rename-item') return
this.$nextTick(() => {
this.$refs.input.focus()
})
this.isMoreOptions = false
this.setFolderIcon = undefined
// Store picked item
this.pickedItem = args.item
})
events.$on('setFolderIcon', (icon) => {
this.setFolderIcon = !icon ? undefined : icon.value
})
}
}
</script>
<style scoped lang="scss">
@import "@assets/vue-file-manager/_inapp-forms.scss";
@import '@assets/vue-file-manager/_forms';
@import "@assets/vue-file-manager/_inapp-forms.scss";
@import '@assets/vue-file-manager/_forms';
.item-thumbnail {
margin-bottom: 20px;
.input {
position: relative;
display: flex;
justify-content: flex-end;
align-items: center;
.close-icon-wrapper {
width: 22px;
height: 22px;
position: absolute;
cursor: pointer;
right: 15px;
border-radius: 6px;
display: flex;
justify-content: center;
align-items: center;
&:hover {
.close-icon {
line {
stroke: $theme;
}
}
}
.close-icon {
line {
stroke: rgba($text-muted, 0.3);
}
}
}
}
.item-thumbnail {
margin-bottom: 20px;
}
@media (prefers-color-scheme: dark) {
.close-icon-wrapper {
&:hover {
.close-icon {
line {
stroke: $theme !important;
}
}
}
.close-icon {
line {
stroke: rgba($dark_mode_text_primary, 0.3) !important;
}
}
}
}
</style>

View File

@@ -0,0 +1,498 @@
<template>
<div class="set-folder-icon">
<TabWrapper >
<!-- Emojis -->
<TabOption :selected="true" id="emoji-list" :title="$t('popup_rename.tab_emoji_title')" icon="emoji">
<div class="select-emoji-wrapper">
<label class="main-label">Pick Yout Emoji Icon:</label>
<!-- Selected Emoji input -->
<div @click.stop="openMenu" class="select-input-wrapper">
<div class="select-input" v-if="selectedEmoji">
<Emoji class="emoji-preview" :emoji="selectedEmoji" size="25"></Emoji>
<span>{{selectedEmoji.name}}</span>
</div>
<div class="not-selected" v-if="! selectedEmoji">
<span> {{$t('popup_rename.set_emoji_input_placeholder')}}</span>
</div>
<chevron-down-icon v-if="!selectOpen" size="19"/>
<div v-if="selectOpen" @click="resetEmoji" class="select-input-icon-wrapper">
<x-icon size="14" class="select-input-icon"/>
</div>
</div>
<!-- Emojis List -->
<transition v-if="selectOpen" name="slide-in">
<div class="emoji-wrapper">
<input @click.stop @input="filterEmojis" v-model="searchInput" class="emoji-input" :placeholder="$t('popup_rename.search_emoji_input_placeholder')" >
<!-- Navigation of Emojis Groups -->
<ul v-show="searchInput.length < 1" class="groups-list">
<li @click.stop="scrollToGroup(group.name)" v-for="(group,i) in emojiGroups" :key="i" class="group-option" :class="{'active' : group.name === groupInView}">
<Emoji :emoji="group.emoji" size="33"/>
</li>
</ul>
<!-- All Emojis -->
<div v-show="searchInput.length < 1" @scroll="checkGroupInView" id="group-box" class="group-wrapper">
<div v-for="(group, name) in allEmoji" :key="name" class="options-wrapper" :id="`group-${name}`">
<label class="group-name-label">{{name}}</label>
<ul class="options-list">
<li @click="setIcon({'emoji':emoji})" v-for="(emoji,i) in group" :key="i" class="option">
<Emoji :emoji="emoji" size="33"/>
</li>
</ul>
</div>
</div>
<!-- Searched emojis -->
<div v-if="searchInput.length > 0" class="group-wrapper">
<div class="options-wrapper">
<ul class="options-list">
<li @click="setIcon({'emoji':emoji})" v-for="(emoji,i) in filteredEmojis" :key="i" class="option" >
<Emoji :emoji="emoji" size="33"/>
</li>
</ul>
<span class="not-found" v-if="filteredEmojis.length === 0"> {{$t('popup_rename.emoji_list_not_found')}}</span>
</div>
</div>
</div>
</transition>
</div>
</TabOption>
<!-- Colors -->
<TabOption :title="$t('popup_rename.tab_color_title')" icon="folder">
<div class="color-pick-wrapper">
<label class="main-label">{{$t('popup_rename.color_pick_label')}}</label>
<ul class="color-wrapper">
<li v-for="(color, index) in colors"
:key="index"
@click="setIcon({'color': color})"
class="single-color"
:class="{'active-color': color === selectedColor }"
:style="{background:color}" />
</ul>
</div>
</TabOption>
</TabWrapper>
</div>
</template>
<script>
import { SmileIcon, FolderIcon, ChevronDownIcon, XIcon } from 'vue-feather-icons'
import TabWrapper from '@/components/Others/TabWrapper'
import TabOption from '@/components/Others/TabOption'
import Emoji from '@/components/Others/Emoji'
import lodash from 'lodash'
import { mapGetters } from 'vuex'
import { events } from '@/bus'
export default {
name: "SetFolderIcon",
props: ['folderData', 'unique_id'],
components: {
ChevronDownIcon ,
TabWrapper,
TabOption,
FolderIcon,
SmileIcon,
XIcon,
Emoji
},
computed: {
...mapGetters(['emojis', 'emojiGroups']),
allEmoji() {
return _.groupBy(this.emojis, 'group')
},
},
data () {
return {
selectedEmoji: undefined,
selectedColor: undefined,
searchInput: '',
filteredEmojis: [],
selectOpen: false,
groupInView: 'Smileys & Emotion',
colors: [ '#FF6633', '#FFB399', '#FF33FF', '#FFFF99', '#00B3E6',
'#E6B333', '#3366E6', '#999966', '#99FF99', '#B34D4D',
'#80B300', '#809900', '#E6B3B3', '#6680B3' ]
}
},
methods: {
checkGroupInView: _.debounce(function() {
this.emojiGroups.forEach(group => {
let element = document.getElementById(`group-${group.name}`).getBoundingClientRect()
let groupBox = document.getElementById('group-box').getBoundingClientRect()
// Check if the group is in the viewport of group-box
if(element.top < groupBox.top && element.bottom > groupBox.top){
this.groupInView = group.name
}
})
}, 200),
scrollToGroup( name ) {
let group = document.getElementById(`group-${name}`)
group.scrollIntoView({ behavior: "smooth" })
this.groupInView = name
},
filterEmojis: _.debounce(function( emoji ){
this.filteredEmojis = this.emojis.filter(emoji => emoji.name.includes(this.searchInput))
}, 800),
openMenu() {
this.selectOpen = ! this.selectOpen
this.searchInput = ''
this.groupInView = 'Smileys & Emotion'
},
setIcon( value ) {
if(value.emoji){
this.selectedEmoji = value.emoji
this.selectedColor = undefined
}
if(value.color) {
this.selectedColor = value.color
this.selectedEmoji = undefined
}
events.$emit('setFolderIcon', { 'value':value, 'unique_id':this.unique_id })
this.selectOpen = false
},
resetEmoji(){
this.selectedEmoji = undefined
events.$emit('setFolderIcon', undefined)
}
},
mounted () {
this.selectOpen = false
events.$on('unClick', () => {
this.selectOpen = false
})
}
}
</script>
<style scoped lang="scss">
@import "@assets/vue-file-manager/_inapp-forms.scss";
@import '@assets/vue-file-manager/_forms';
.color-pick-wrapper {
.color-wrapper {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
margin-bottom: 20px;
.single-color {
width: 40px;
height: 40px;
list-style: none;
margin: 8px;
border-radius: 8px;
cursor: pointer;
&.active-color {
border: 2px solid $text;
}
&:hover {
border: 2px solid $text;
}
}
}
}
.select-emoji-wrapper{
margin-bottom: 20px;
}
.main-label {
@include font-size(14);
font-weight: 700;
margin-bottom: 8px;
display: block;
}
.emoji-wrapper {
height: 350px;
width: 100%;
position: absolute;
border: 1px solid transparent;
box-shadow: 0 1px 5px rgba(0, 0, 0, 0.12);
border-radius: 8px;
background: white;
display: flex;
flex-direction: column;
padding: 10px;
top: 152px;
.loader {
width: 100%;
height: 100%;
position: relative;
}
.groups-list {
display: flex;
flex-direction: row;
flex-wrap: wrap;
margin-bottom: 20px;
.active {
background: $light_background;
border-radius: 8px;
}
.group-option {
list-style: none;
width: 45px;
height: 45px;
padding: 6px;
cursor: pointer;
&:hover {
background: $light_background;
border-radius: 8px;
}
}
}
.emoji-input {
width: 100%;
border-radius: 8px;
padding: 4px;
margin-bottom: 20px;
background: $light_background;
border: none;
padding: 13px 20px;
font-weight: 700;
&::placeholder {
font-weight: 700;
color: $light_text;
}
}
.group-wrapper {
height: 100%;
display: flex;
flex-direction: column;
overflow: hidden;
overflow-y: scroll;
padding: 0px;
.options-wrapper {
display: flex;
flex-wrap: wrap;
margin-bottom: 10px;
&:last-child {
margin-bottom: 0px;
}
.options-list {
display: flex;
flex-wrap: wrap;
justify-content: center;
}
.group-name-label {
width: 100%;
@include font-size(14);
font-weight: 700;
margin-bottom: 10px;
}
.option {
list-style: none;
width: 45px;
height: 45px;
padding: 6px;
cursor: pointer;
&:hover {
background: $light_background;
border-radius: 8px;
}
}
.not-found {
align-self: center;
margin:auto;
font-weight: 700;
padding: 10px;
border-radius: 8px;
background:$light_background ;
box-shadow: 0 1px 5px rgba(0, 0, 0, 0.12);
}
}
}
}
.select-input-wrapper {
height: 50px;
padding: 13px 20px;
border: 1px solid transparent;
border-radius: 8px;
box-shadow: 0 1px 5px rgba(0, 0, 0, 0.12);
cursor: pointer;
display: flex;
justify-content: space-between;
align-items: center;
.select-input-icon-wrapper {
width: 22px;
height: 22px;
border-radius: 6px;
display: flex;
justify-content: center;
align-items: center;
&:hover {
background: $light_background !important;
.select-input-icon {
line {
stroke: $theme;
}
}
}
.select-input-icon {
line {
stroke: $text;
}
}
}
.select-input {
@include font-size(16);
font-weight: 700;
display: flex;
flex-direction: row;
align-items: center;
.emoji-preview {
margin-right: 10px;
}
}
.not-selected {
span {
color: rgba($text, 0.5);
@include font-size(15);
font-weight: 700
}
}
}
.wrapper {
margin-bottom: 10px;
}
.set-folder-icon {
position: relative;
}
.slide-in-enter-active {
transition: all 5s ease;
}
.slide-in-enter
{
opacity: 0;
transform: translateY(-50px);
}
@media (prefers-color-scheme: dark) {
.color-pick-wrapper{
.color-wrapper{
.single-color {
&.active-color {
border: 2px solid ;
}
&:hover {
border: 2px solid $dark_mode_text_primary;
}
}
}
}
.emoji-wrapper {
background: $dark_mode_background;
.emoji-input {
background: $dark_mode_foreground ;
}
.groups-list{
.active{
background: $dark_mode_foreground !important;
}
.group-option {
&:hover {
background: $dark_mode_foreground !important;
}
}
}
.options-wrapper {
.option {
&:hover {
background: $dark_mode_foreground !important;
}
}
.not-found {
background: $dark_mode_foreground !important;
}
}
}
.select-input-wrapper {
background: $dark_mode_foreground;
.not-selected {
span {
color:$dark_mode_text_secondary;
}
}
.select-input-icon-wrapper {
&:hover {
background: rgba($theme, 0.1) !important;
.select-input-icon {
line {
stroke: $theme !important;
}
}
}
.select-input-icon {
line {
stroke:$dark_mode_text_primary !important;
}
}
}
}
}
</style>

View File

@@ -1,7 +1,7 @@
<template>
<PopupWrapper name="share-create">
<!--Title-->
<PopupHeader :title="$t('popup_share_create.title', {item: itemTypeTitle})" icon="share" />
<PopupHeader :title="$t('popup_share_create.title', {item: itemTypeTitle})" icon="share"/>
<!--Content-->
<PopupContent>
@@ -9,8 +9,27 @@
<!--Item Thumbnail-->
<ThumbnailItem class="item-thumbnail" :item="pickedItem" info="metadata"/>
<!-- Infobox for successfull sended email -->
<InfoBox v-if="isGeneratedShared && sharedViaEmail" class="info-box-wrapper">
<p v-html="$t('shared_form.email_successfully_send_message')"></p>
</InfoBox>
<!--Form to set sharing-->
<ValidationObserver v-if="! isGeneratedShared" ref="shareForm" v-slot="{ invalid }" tag="form" class="form-wrapper">
<ValidationObserver @submit.prevent v-if="! isGeneratedShared" ref="shareForm" v-slot="{ invalid }" tag="form" class="form-wrapper">
<TabWrapper>
<!-- Share via link -->
<TabOption :selected="true" :title="$t('shared_form.share_by_link')" icon="link"/>
<!-- Share via Email -->
<TabOption :title="$t('shared_form.share_by_email')" icon="email">
<ValidationProvider tag="div" mode="passive" name="Email" rules="required" v-slot="{ errors }">
<MultiEmailInput rules="required" v-model="shareOptions.emails" :label="$t('shared_form.recipients_label')" :isError="errors[0]"/>
</ValidationProvider>
</TabOption>
</TabWrapper>
<!--Permision Select-->
<ValidationProvider v-if="isFolder" tag="div" mode="passive" class="input-wrapper" name="Permission" rules="required" v-slot="{ errors }">
@@ -49,201 +68,212 @@
<!--Copy generated link-->
<div v-if="isGeneratedShared" class="form-wrapper">
<div class="input-wrapper">
<label class="input-label">{{ $t('shared_form.label_shared_url') }}:</label>
<CopyInput size="small" :value="shareLink" />
<label class="input-label">{{ this.sharedViaEmail ? $t('shared_form.label_share_vie_email') : $t('shared_form.label_shared_url') }}:</label>
<CopyInput size="small" :item="pickedItem"/>
</div>
</div>
</PopupContent>
<!--Actions-->
<PopupActions>
<ButtonBase
v-if="! isGeneratedShared"
class="popup-button"
@click.native="$closePopup()"
button-style="secondary"
>{{ $t('popup_move_item.cancel') }}
<ButtonBase v-if="! isGeneratedShared" class="popup-button" @click.native="$closePopup()" button-style="secondary">{{ $t('popup_move_item.cancel') }}
</ButtonBase>
<ButtonBase
class="popup-button"
@click.native="submitShareOptions"
button-style="theme"
:loading="isLoading"
:disabled="isLoading"
>{{ submitButtonText }}
<ButtonBase class="popup-button" @click.native="submitShareOptions" button-style="theme" :loading="isLoading" :disabled="isLoading">{{ submitButtonText }}
</ButtonBase>
</PopupActions>
</PopupWrapper>
</template>
<script>
import {ValidationProvider, ValidationObserver} from 'vee-validate/dist/vee-validate.full'
import SelectBoxInput from '@/components/Others/Forms/SelectBoxInput'
import PopupWrapper from '@/components/Others/Popup/PopupWrapper'
import PopupActions from '@/components/Others/Popup/PopupActions'
import PopupContent from '@/components/Others/Popup/PopupContent'
import PopupHeader from '@/components/Others/Popup/PopupHeader'
import SwitchInput from '@/components/Others/Forms/SwitchInput'
import SelectInput from '@/components/Others/Forms/SelectInput'
import ThumbnailItem from '@/components/Others/ThumbnailItem'
import ActionButton from '@/components/Others/ActionButton'
import CopyInput from '@/components/Others/Forms/CopyInput'
import ButtonBase from '@/components/FilesView/ButtonBase'
import {required} from 'vee-validate/dist/rules'
import {mapGetters} from 'vuex'
import {events} from '@/bus'
import axios from 'axios'
import { ValidationProvider, ValidationObserver } from 'vee-validate/dist/vee-validate.full'
import SelectBoxInput from '@/components/Others/Forms/SelectBoxInput'
import PopupWrapper from '@/components/Others/Popup/PopupWrapper'
import PopupActions from '@/components/Others/Popup/PopupActions'
import PopupContent from '@/components/Others/Popup/PopupContent'
import PopupHeader from '@/components/Others/Popup/PopupHeader'
import MultiEmailInput from '@/components/Others/Forms/MultiEmailInput'
import SwitchInput from '@/components/Others/Forms/SwitchInput'
import SelectInput from '@/components/Others/Forms/SelectInput'
import ThumbnailItem from '@/components/Others/ThumbnailItem'
import ActionButton from '@/components/Others/ActionButton'
import CopyInput from '@/components/Others/Forms/CopyInput'
import TabWrapper from '@/components/Others/TabWrapper'
import TabOption from '@/components/Others/TabOption'
import ButtonBase from '@/components/FilesView/ButtonBase'
import InfoBox from '@/components/Others/Forms/InfoBox'
import { LinkIcon, MailIcon } from 'vue-feather-icons'
import { required } from 'vee-validate/dist/rules'
import { mapGetters } from 'vuex'
import { events } from '@/bus'
import axios from 'axios'
export default {
name: 'ShareCreate',
components: {
ValidationProvider,
ValidationObserver,
SelectBoxInput,
ThumbnailItem,
ActionButton,
PopupWrapper,
PopupActions,
PopupContent,
PopupHeader,
SelectInput,
SwitchInput,
ButtonBase,
CopyInput,
required,
export default {
name: 'ShareCreate',
components: {
ValidationProvider,
ValidationObserver,
SelectBoxInput,
ThumbnailItem,
ActionButton,
PopupWrapper,
PopupActions,
TabWrapper,
TabOption,
PopupContent,
PopupHeader,
MultiEmailInput,
SelectInput,
SwitchInput,
ButtonBase,
CopyInput,
MailIcon,
required,
LinkIcon,
InfoBox
},
computed: {
...mapGetters([
'permissionOptions',
'expirationList'
]),
itemTypeTitle() {
return this.pickedItem && this.pickedItem.type === 'folder' ? this.$t('types.folder') : this.$t('types.file')
},
computed: {
...mapGetters([
'permissionOptions',
'expirationList',
]),
itemTypeTitle() {
return this.pickedItem && this.pickedItem.type === 'folder' ? this.$t('types.folder') : this.$t('types.file')
isFolder() {
return this.pickedItem && this.pickedItem.type === 'folder'
},
submitButtonText() {
return this.isGeneratedShared ? this.$t('shared_form.button_done') : this.$t('shared_form.button_generate')
},
moreOptionsTitle() {
return this.isMoreOptions ? this.$t('shared_form.button_close_options') : this.$t('shared_form.button_more_options')
}
},
data() {
return {
shareOptions: {
isPassword: false,
expiration: undefined,
password: undefined,
permission: undefined,
type: undefined,
unique_id: undefined,
emails: undefined
},
isFolder() {
return this.pickedItem && this.pickedItem.type === 'folder'
},
submitButtonText() {
return this.isGeneratedShared ? this.$t('shared_form.button_done') : this.$t('shared_form.button_generate')
},
moreOptionsTitle() {
return this.isMoreOptions ? this.$t('shared_form.button_close_options') : this.$t('shared_form.button_more_options')
pickedItem: undefined,
isGeneratedShared: false,
isLoading: false,
isMoreOptions: false,
sharedViaEmail: false
}
},
methods: {
moreOptions() {
this.isMoreOptions = !this.isMoreOptions
if (!this.isMoreOptions)
this.shareOptions.expiration = undefined
},
async submitShareOptions() {
// If shared was generated, then close popup
if (this.isGeneratedShared) {
events.$emit('popup:close')
return
}
},
data() {
return {
shareOptions: {
// Validate fields
const isValid = await this.$refs.shareForm.validate()
if (!isValid) return
this.isLoading = true
// Send request to get share link
axios
.post('/api/share', this.shareOptions)
.then(response => {
// Show infobox and reset emails container
if (this.shareOptions.emails)
this.sharedViaEmail = true
// End loading
this.isGeneratedShared = true
this.$store.commit('UPDATE_SHARED_ITEM', response.data.data.attributes)
})
.catch(() => {
events.$emit('alert:open', {
title: this.$t('popup_error.title'),
message: this.$t('popup_error.message'),
})
// End loading
this.isLoading = false
})
.finally(() => {
this.isLoading = false
})
}
},
mounted() {
events.$on('emailsInputValues', (emails) => this.shareOptions.emails = emails)
// Show popup
events.$on('popup:open', args => {
if (args.name !== 'share-create') return
// Store picked item
this.pickedItem = args.item
this.shareOptions.type = args.item.type
this.shareOptions.unique_id = args.item.unique_id
})
// Close popup
events.$on('popup:close', () => {
// Restore data
setTimeout(() => {
this.shareOptions = {
permission: undefined,
password: undefined,
isPassword: false,
expiration: undefined,
password: undefined,
permission: undefined,
type: undefined,
unique_id: undefined,
},
pickedItem: undefined,
shareLink: undefined,
isGeneratedShared: false,
isLoading: false,
isMoreOptions: false,
}
},
methods: {
moreOptions() {
this.isMoreOptions = ! this.isMoreOptions
if (! this.isMoreOptions)
this.shareOptions.expiration = undefined
},
async submitShareOptions() {
// If shared was generated, then close popup
if (this.isGeneratedShared) {
events.$emit('popup:close')
return;
emails: undefined
}
// Validate fields
const isValid = await this.$refs.shareForm.validate();
if (!isValid) return;
this.isLoading = true
// Send request to get share link
axios
.post('/api/share', this.shareOptions)
.then(response => {
// End loading
this.isLoading = false
this.shareLink = response.data.data.attributes.link
this.isGeneratedShared = true
this.$store.commit('UPDATE_SHARED_ITEM', response.data.data.attributes)
})
.catch(error => {
// todo: catch errors
// End loading
this.isLoading = false
})
},
},
mounted() {
// Show popup
events.$on('popup:open', args => {
if (args.name !== 'share-create') return
// Store picked item
this.pickedItem = args.item
this.shareOptions.type = args.item.type
this.shareOptions.unique_id = args.item.unique_id
})
// Close popup
events.$on('popup:close', () => {
// Restore data
setTimeout(() => {
this.shareOptions = {
permission: undefined,
password: undefined,
isPassword: false,
expiration: undefined,
type: undefined,
unique_id: undefined,
}
this.isGeneratedShared = false
this.isMoreOptions = false
this.shareLink = undefined
}, 150)
})
}
this.isGeneratedShared = false
this.isMoreOptions = false
this.sharedViaEmail = false
}, 150)
})
}
}
</script>
<style scoped lang="scss">
@import "@assets/vue-file-manager/_inapp-forms.scss";
@import '@assets/vue-file-manager/_forms';
@import "@assets/vue-file-manager/_inapp-forms.scss";
@import '@assets/vue-file-manager/_forms';
.more-options {
margin-bottom: 10px;
.more-options {
margin-bottom: 10px;
}
.input-wrapper {
&.password {
margin-top: -10px;
}
}
.input-wrapper {
.item-thumbnail {
margin-bottom: 20px;
}
&.password {
margin-top: -10px;
}
}
.item-thumbnail {
margin-bottom: 20px;
}
</style>

View File

@@ -9,14 +9,26 @@
<!--Item Thumbnail-->
<ThumbnailItem class="item-thumbnail" :item="pickedItem" info="metadata"/>
<!--Form to set sharing-->
<ValidationObserver ref="shareForm" v-slot="{ invalid }" tag="form" class="form-wrapper">
<!-- Infobox for successfull sended email -->
<InfoBox v-if="sendToRecipientsMenu && isEmailSended" class="info-box-wrapper">
<p v-html="$t('shared_form.email_successfully_send_message')"></p>
</InfoBox>
<!--Share link-->
<div class="input-wrapper">
<label class="input-label">{{ $t('shared_form.label_shared_url') }}:</label>
<CopyInput size="small" :value="pickedItem.shared.link" />
</div>
<div v-if="! sendToRecipientsMenu || (sendToRecipientsMenu && isEmailSended)" class="input-wrapper copy-input">
<label class="input-label">{{ $t('shared_form.label_share_vie_email') }}:</label>
<CopyInput size="small" :item="pickedItem" />
</div>
<ValidationObserver @submit.prevent v-if="sendToRecipientsMenu && !isEmailSended" v-slot="{ invalid }" ref="shareEmail" tag="form" class="form-wrapper">
<ValidationProvider tag="div" mode="passive" name="Email" rules="required" v-slot="{ errors }">
<MultiEmailInput rules="required" v-model="emails" :label="$t('shared_form.label_send_to_recipients')" :isError="errors[0]" />
</ValidationProvider>
</ValidationObserver>
<!--Form to set sharing-->
<ValidationObserver @submit.prevent v-if="! sendToRecipientsMenu" ref="shareForm" v-slot="{ invalid }" tag="form" class="form-wrapper">
<!--Permision Select-->
<ValidationProvider v-if="isFolder" tag="div" mode="passive" class="input-wrapper" name="Permission" rules="required" v-slot="{ errors }">
@@ -58,7 +70,7 @@
<!--Actions-->
<PopupActions>
<ButtonBase
<ButtonBase v-if="! sendToRecipientsMenu || (sendToRecipientsMenu && !isEmailSended)"
class="popup-button"
@click.native="destroySharing"
:button-style="destroyButtonStyle"
@@ -72,7 +84,7 @@
button-style="theme"
:loading="isLoading"
:disabled="isLoading"
>{{ $t('popup_share_edit.save') }}
>{{ secondButtonText }}
</ButtonBase>
</PopupActions>
</PopupWrapper>
@@ -87,10 +99,12 @@
import PopupHeader from '@/components/Others/Popup/PopupHeader'
import SwitchInput from '@/components/Others/Forms/SwitchInput'
import SelectInput from '@/components/Others/Forms/SelectInput'
import MultiEmailInput from '@/components/Others/Forms/MultiEmailInput'
import ThumbnailItem from '@/components/Others/ThumbnailItem'
import ActionButton from '@/components/Others/ActionButton'
import CopyInput from '@/components/Others/Forms/CopyInput'
import ButtonBase from '@/components/FilesView/ButtonBase'
import InfoBox from '@/components/Others/Forms/InfoBox'
import {required} from 'vee-validate/dist/rules'
import {mapGetters} from 'vuex'
import {events} from '@/bus'
@@ -109,10 +123,12 @@
PopupContent,
PopupHeader,
SelectInput,
MultiEmailInput,
SwitchInput,
ButtonBase,
CopyInput,
required,
InfoBox,
},
computed: {
...mapGetters([
@@ -125,10 +141,26 @@
return this.pickedItem && this.pickedItem.type === 'folder'
},
destroyButtonText() {
return this.isConfirmedDestroy ? this.$t('popup_share_edit.confirm') : this.$t('popup_share_edit.stop')
if(! this.sendToRecipientsMenu)
return this.isConfirmedDestroy ? this.$t('popup_share_edit.confirm') : this.$t('popup_share_edit.stop')
if(this.sendToRecipientsMenu)
return this.$t('popup_share_edit.go_back')
},
destroyButtonStyle() {
return this.isConfirmedDestroy ? 'danger-solid' : 'secondary'
if(! this.sendToRecipientsMenu)
return this.isConfirmedDestroy ? 'danger-solid' : 'secondary'
if(this.sendToRecipientsMenu)
return 'secondary'
},
secondButtonText(){
if(! this.sendToRecipientsMenu)
return this.$t('popup_share_edit.save')
if(this.sendToRecipientsMenu)
return this.isEmailSended ? this.$t('shared_form.button_done') : this.$t('popup_share_edit.send_to_recipients')
},
isSharedLocation() {
return this.currentFolder && this.currentFolder.location === 'shared'
@@ -139,11 +171,14 @@
},
data() {
return {
sendToRecipientsMenu: false,
isConfirmedDestroy: false,
canChangePassword: false,
shareOptions: undefined,
pickedItem: undefined,
emails:undefined,
isMoreOptions: false,
isEmailSended:false,
isDeleting: false,
isLoading: false,
}
@@ -158,8 +193,38 @@
changePassword() {
this.canChangePassword = false
},
async sendViaEmail() {
// Validate email field
const isValid = await this.$refs.shareEmail.validate();
if (!isValid) return;
this.isLoading = true
axios.
post(`/api/share/${this.shareOptions.token}/send-email`, {
emails: this.emails
})
.catch(() => {
this.$isSomethingWrong()
})
.finally(() => {
this.isEmailSended = true
// End loading
this.isLoading = false
})
},
async destroySharing() {
if(this.sendToRecipientsMenu) {
this.sendToRecipientsMenu = false
return
}
// Set confirm button
if (! this.isConfirmedDestroy) {
@@ -188,6 +253,18 @@
},
async updateShareOptions() {
// If is open send share via email
if(this.sendToRecipientsMenu && !this.isEmailSended) {
this.sendViaEmail()
return
}
// Is is open send share via email and email was already sended
if(this.sendToRecipientsMenu && this.isEmailSended){
events.$emit('popup:close')
return
}
// If shared was generated, then close popup
if (this.isGeneratedShared) {
@@ -219,18 +296,21 @@
events.$emit('popup:close')
})
.catch(() => {
this.$isSomethingWrong()
})
.finally(() => {
// End loading
this.isLoading = false
})
},
},
mounted() {
this.sendToRecipientsMenu = false
events.$on('emailsInputValues', (emails) => {
this.emails = emails
})
// Show popup
events.$on('popup:open', args => {
@@ -251,6 +331,9 @@
if (args.item.shared.expire_in)
this.isMoreOptions = true
if (args.sentToEmail)
this.sendToRecipientsMenu = true
this.canChangePassword = args.item.shared.protected
})
@@ -259,6 +342,8 @@
// Restore data
setTimeout(() => {
this.sendToRecipientsMenu = false
this.isEmailSended = false
this.isConfirmedDestroy = false
this.canChangePassword = false
this.pickedItem = undefined
@@ -278,6 +363,10 @@
&.password {
margin-top: -10px;
}
&.copy-input {
padding: 0px 20px;
}
}
.change-password {

View File

@@ -0,0 +1,22 @@
<template>
<div v-if="isActive">
<slot></slot>
</div>
</template>
<script>
export default {
name: "TabOption",
props: ['title', 'icon', 'selected'],
data () {
return {
isActive: false
}
},
mounted() {
this.isActive = this.selected
}
}
</script>

View File

@@ -0,0 +1,117 @@
<template>
<div>
<div class="tab-wrapper">
<div class="tab" :class="{ active: tab.isActive }" @click="selectTab(tab)" v-for="(tab, i) in tabs" :key="i">
<!--Icon-->
<mail-icon v-if="tab.icon === 'email'" class="tab-icon" size="17"/>
<link-icon v-if="tab.icon === 'link'" class="tab-icon" size="17"/>
<smile-icon v-if="tab.icon === 'emoji'" class="tab-icon" size="17"/>
<folder-icon v-if="tab.icon === 'folder'" class="tab-icon" size="17"/>
<!--Title-->
<b class="tab-title">{{tab.title}}</b>
</div>
</div>
<slot></slot>
</div>
</template>
<script>
import {
LinkIcon,
MailIcon,
SmileIcon,
FolderIcon } from 'vue-feather-icons'
export default {
name: "TabWrapper",
components: {
LinkIcon,
MailIcon,
SmileIcon,
FolderIcon
},
data () {
return {
tabs: []
}
},
methods: {
selectTab(selectedTab) {
this.tabs.forEach(tab => {
tab.isActive = tab.title == selectedTab.title
})
}
},
mounted () {
this.tabs = this.$children
}
}
</script>
<style scoped lang="scss">
@import "@assets/vue-file-manager/_inapp-forms.scss";
@import '@assets/vue-file-manager/_forms';
.tab-wrapper {
display: flex;
justify-content: center;
margin-bottom: 20px;
cursor: pointer;
align-items: center;
background: white;
color: $text;
border-radius: 8px;
overflow: hidden;
border: 1px solid #E8E9EB;
.tab-title {
@include font-size(14);
}
.tab {
width: 100%;
display: flex;
align-items: center;
justify-content: center;
padding: 8px;
&.active {
background: $light_background;
.tab-title {
color: $text;
}
}
}
.tab-icon {
margin-right: 10px;
path,
circle,
line,
polyline {
color: $theme !important;
}
}
}
@media (prefers-color-scheme: dark) {
.tab-wrapper {
background: $dark_mode_foreground;
border-color: transparent;
.tab.active {
background: rgba($theme, 0.1);
.tab-title {
color: $theme;
}
}
}
}
</style>

View File

@@ -13,8 +13,12 @@
<!--Image thumbnail-->
<img v-if="isImage && item.thumbnail" class="image" :src="item.thumbnail" :alt="item.name"/>
<!-- If folder have set emoji -->
<Emoji v-if="isFolder && folderIconHandle" :emoji="folderIconHandle" size="36"/>
<!--Else show only folder icon-->
<FontAwesomeIcon v-if="isFolder" class="folder-icon" icon="folder"/>
<FontAwesomeIcon ref="folderIcon" v-if="isFolder && !folderIconHandle" class="folder-icon" icon="folder"/>
</div>
<!--Name-->
@@ -41,12 +45,37 @@
<script>
import {mapGetters} from 'vuex'
import Emoji from '@/components/Others/Emoji'
export default {
name: 'ThumbnailItem',
props: ['item', 'info'],
props: ['item', 'info', 'setFolderIcon'],
components: {Emoji},
computed: {
...mapGetters(['currentFolder']),
folderIconHandle(){
// Set icon folder if set folder from rename popup
if(this.setFolderIcon){
return this.setFolderIcon.emoji
? this.setFolderIcon.emoji
: this.$nextTick(() => {
this.$refs.folderIcon.firstElementChild.style.fill = `${this.setFolderIcon.color}`
})
}
// If folder have already set some icon
if(!this.setFolderIcon && (this.item.icon_emoji || this.item.icon_color)){
return this.item.icon_emoji
? this.item.icon_emoji
: this.$nextTick(() => {
this.$refs.folderIcon.firstElementChild.style.fill = `${this.item.icon_color}`
})
}
},
isFolder() {
return this.item.type === 'folder'
},
@@ -103,9 +132,12 @@
.icon-item {
position: relative;
min-width: 52px;
display: flex;
text-align: center;
justify-content: center;
line-height: 0;
.file-icon {
@include font-size(35);

View File

@@ -12,10 +12,10 @@
name: 'Vignette',
computed: {
...mapGetters([
'isZippingFiles'
'processingPopup'
]),
isVisible() {
return this.isZippingFiles || this.isVisibleVignette
return this.processingPopup || this.isVisibleVignette
},
},
data() {
@@ -56,7 +56,7 @@
right: 0;
left: 0;
bottom: 0;
z-index: 19;
z-index: 18;
background: $light_mode_vignette;
}

View File

@@ -1,5 +1,5 @@
<template>
<section class="content-sidebar">
<section class="content-sidebar" id="content-sidebar">
<slot></slot>
</section>
</template>

View File

@@ -177,7 +177,7 @@ const Helpers = {
if (files.length == 0) return
if (!this.$checkFileMimetype(files)) return
if (!this.$checkFileMimetype(files) || !this.$checkUploadLimit(files)) return
this.$handleUploading(files, undefined)
}
@@ -351,7 +351,7 @@ const Helpers = {
let body = document.body
body.classList.add('windows')
}
}
}
}
}

View File

@@ -26,7 +26,7 @@
"users": "Users"
},
"admin_page_dashboard": {
"backer_button": "成为支持者",
"backer_button": "Help Us Improve",
"license": "执照",
"version": "版",
"w_latest_users": {
@@ -283,7 +283,8 @@
"share_edit": "编辑分享设定",
"upload": "上传",
"select": "Select",
"no_options": "No Options Available"
"no_options": "No Options Available",
"zip_folder": "Zip and Download"
},
"mobile_selecting": {
"select_all": "Select All",
@@ -597,7 +598,9 @@
"confirm": "确认",
"save": "保存更改",
"stop": "停止风险",
"title": "更新分享设定"
"title": "更新分享设定",
"go_back": "Go Back",
"send_to_recipients": "Send to Recipients"
},
"popup_signup_error": {
"message": "Please check your database connection if everything works correctly.",
@@ -689,10 +692,17 @@
"label_password_protection": "密码保护",
"label_permission": "权限",
"label_shared_url": "分享链接",
"label_share_vie_email": "Get your link",
"label_send_to_recipients": "Send to Recipients",
"label_expiration": "Link Expiration",
"expiration_hour": "{value}h.",
"expiration_day": "{value}d.",
"placeholder_permission": "请设置权限"
"placeholder_permission": "请设置权限",
"email_successfully_send_message": "Your item was <b>successfully sended</b> to recipients emails.",
"share_by_link": "Share by Link",
"share_by_email": "Share by Email",
"recipients_label": "Recipients",
"email_placeholder": "Type your emails"
},
"sidebar": {
"favourites": "收藏",
@@ -802,7 +812,9 @@
"state": "州",
"state_plac": "输入您的帐单状态",
"title_account": "帐户信息",
"title_billing": "账单信息"
"title_billing": "账单信息",
"timezone": "Timezone",
"timezone_plac" : "Select your timezone"
},
"user_subscription": {
"billed": "开票",

View File

@@ -28,7 +28,7 @@
"users": "Users"
},
"admin_page_dashboard": {
"backer_button": "Become a Backer",
"backer_button": "Help Us Improve",
"license": "License",
"version": "Version",
"w_latest_users": {
@@ -285,7 +285,8 @@
"share_edit": "Edit Sharing",
"upload": "Upload",
"select": "Select",
"no_options": "No Options Available"
"no_options": "No Options Available",
"zip_folder": "Zip and Download"
},
"mobile_selecting": {
"select_all": "Select All",
@@ -307,9 +308,9 @@
"paginate_info": "Showing 1 - {visible} from {total} records"
},
"empty_page": {
"call_to_action": "Upload File",
"description": "Upload some files here easily via upload button",
"title": "There is Nothing"
"call_to_action": "Upload Files",
"description": "Upload some files here easily via upload button.",
"title": "Upload Your First File"
},
"errors": {
"capacity_digit": "The storage capacity must be lower than 10 digit number."
@@ -579,7 +580,14 @@
"popup_rename": {
"title": "Rename Your {item}",
"label": "Edit Name",
"placeholder": "Type your title"
"placeholder": "Type your title",
"tab_emoji_title": "Emoji as an Icon",
"tab_color_title": "Folder Color",
"set_emoji_input_placeholder": "Emojis List...",
"search_emoji_input_placeholder": "Search your emoji...",
"emoji_list_not_found": "Not Found",
"color_pick_label": "Pick Your Color:"
},
"popup_create_folder": {
"folder_default_name": "New Folder",
@@ -598,8 +606,10 @@
"change_pass": "Change Password",
"confirm": "Confirm",
"save": "Save Changes",
"stop": "Stop Sharing",
"title": "Update sharing options"
"stop": "Cancel Sharing",
"title": "Update sharing options",
"go_back": "Go Back",
"send_to_recipients": "Send to Recipients"
},
"popup_signup_error": {
"message": "Please check your database connection if everything works correctly.",
@@ -686,15 +696,23 @@
"shared_form": {
"button_more_options": "Set Expiration",
"button_close_options": "Close Options",
"button_folder_icon_open": "Customize Folder Icon",
"button_done": "Awesome, Im done!",
"button_generate": "Generate Link",
"label_password_protection": "Password Protected",
"label_permission": "Permission",
"label_shared_url": "Share url",
"label_share_vie_email": "Get your link",
"label_send_to_recipients": "Send to Recipients",
"label_expiration": "Link Expiration",
"expiration_hour": "{value}h.",
"expiration_day": "{value}d.",
"placeholder_permission": "Select your permission"
"placeholder_permission": "Select your permission",
"email_successfully_send_message": "Your item was <b>successfully sended</b> to recipients emails.",
"share_by_link": "Share by Link",
"share_by_email": "Share by Email",
"recipients_label": "Recipients",
"email_placeholder": "Type your emails"
},
"sidebar": {
"favourites": "Favourites",
@@ -804,7 +822,9 @@
"state": "State",
"state_plac": "Type your billing state",
"title_account": "Account Information",
"title_billing": "Billing Information"
"title_billing": "Billing Information",
"timezone": "Timezone",
"timezone_plac" : "Select your timezone"
},
"user_subscription": {
"billed": "Billed",

View File

@@ -28,7 +28,7 @@
"users": "Uživatelia"
},
"admin_page_dashboard": {
"backer_button": "Staňte sa podporovateľom",
"backer_button": "Pomôžte nám zlepšiť sa",
"license": "Licencia",
"version": "Verzia",
"w_latest_users": {
@@ -285,7 +285,8 @@
"share_edit": "Upraviť zdieľanie",
"upload": "Nahrať",
"select": "Výber",
"no_options": "Nie sú k dispozícii žiadne možnosti"
"no_options": "Nie sú k dispozícii žiadne možnosti",
"zip_folder": "Zazipovať priečinok"
},
"mobile_selecting": {
"select_all": "Vybrať všetko",
@@ -599,7 +600,9 @@
"confirm": "Potvrdiť",
"save": "Uložiť zmeny",
"stop": "Zastaviť zdieľanie",
"title": "Upraviť nastavenia zdieľania"
"title": "Upraviť nastavenia zdieľania",
"go_back": "Spať",
"send_to_recipients": "Odoslať príjemcom"
},
"popup_signup_error": {
"message": "Prosím skontrolujte databázove spojenie, či všetko funguje správne.",
@@ -691,10 +694,17 @@
"label_password_protection": "Chrániť heslom",
"label_permission": "Oprávnenie",
"label_shared_url": "Zdieľací odkaz",
"label_share_vie_email": "Získajte odkaz",
"label_send_to_recipients": "Odoslať príjemcom",
"label_expiration": "Expirácia Linku",
"expiration_hour": "{value}h.",
"expiration_day": "{value}d.",
"placeholder_permission": "Zvoľte oprávnenia"
"placeholder_permission": "Zvoľte oprávnenia",
"email_successfully_send_message": "Vaša položka bola <b>úspešne odoslaná</b> na e-maily príjemcov.",
"share_by_link": "Zdieľať odkazom",
"share_by_email": "Zdieľať e-mailom",
"recipients_label": "Príjemcovia",
"email_placeholder": "Zadajte e-mailové adresy"
},
"sidebar": {
"favourites": "Obľúbené",
@@ -804,7 +814,9 @@
"state": "Štát",
"state_plac": "Zadajte Štát",
"title_account": "informácie o účte",
"title_billing": "Fakturačné údaje"
"title_billing": "Fakturačné údaje",
"timezone": "Časové pásmo",
"timezone_plac" : "Vyberte svoje časové pásmo"
},
"user_subscription": {
"billed": "Ůčtované",

View File

@@ -90,8 +90,9 @@ Vue.use(Helpers);
Vue.config.productionTip = false;
// Handle position of Drag & Drop Ghost
document.addEventListener('drag', (event) => {
document.addEventListener('drag', event => {
let multiSelect = document.getElementById('multi-select-ui')
multiSelect.style.top = event.clientY + 20 + 'px'
multiSelect.style.left = event.clientX + 'px'

View File

@@ -3,6 +3,7 @@ import Vue from 'vue'
import fileFunctions from './modules/fileFunctions'
import fileBrowser from './modules/fileBrowser'
import emojisList from './modules/emojisList'
import userAuth from './modules/userAuth'
import sharing from './modules/sharing'
import app from './modules/app'
@@ -13,6 +14,7 @@ export default new Vuex.Store({
modules: {
fileFunctions,
fileBrowser,
emojisList,
userAuth,
sharing,
app,

View File

@@ -1,7 +1,7 @@
import i18n from '@/i18n/index'
const defaultState = {
fileInfoPanelVisible: localStorage.getItem('file_info_visibility') == 'true' || false,
fileInfoPanelVisible: localStorage.getItem('file_info_visibility') == 'true' || true,
FilePreviewType: localStorage.getItem('preview_type') || 'list',
config: undefined,
index: undefined,
@@ -839,6 +839,132 @@ const defaultState = {
value: 'ZMW',
},
],
timezones : [
{
value: "-12.0",
label: "(GMT -12:00) Eniwetok, Kwajalein"
},
{
value: "-11.0",
label: "(GMT -11:00) Midway Island, Samoa"
},
{
value: "-10.0",
label: "(GMT -10:00) Hawaii"
},
{
value: "-9.0",
label: "(GMT -9:00) Alaska"
},
{
value: "-8.0",
label: "(GMT -8:00) Pacific Time (US & Canada)"
},
{
value: "-7.0",
label: "(GMT -7:00) Mountain Time (US & Canada)"
},
{
value: "-6.0",
label: "(GMT -6:00) Central Time (US & Canada), Mexico City"
},
{
value: "-5.0",
label: "(GMT -5:00) Eastern Time (US & Canada), Bogota, Lima"
},
{
value: "-4.0",
label: "(GMT -4:00) Atlantic Time (Canada), Caracas, La Paz"
},
{
value: "-3.5",
label: "(GMT -3:30) Newfoundland"
},
{
value: "-3.0",
label: "(GMT -3:00) Brazil, Buenos Aires, Georgetown"
},
{
value: "-2.0",
label: "(GMT -2:00) Mid-Atlantic"
},
{
value: "-1.0",
label: "(GMT -1:00) Azores, Cape Verde Islands"
},
{
value: "0.0",
label: "(GMT) Western Europe Time, London, Lisbon, Casablanca"
},
{
value: "1.0",
label: "(GMT +1:00) Brussels, Copenhagen, Madrid, Paris"
},
{
value: "2.0",
label: "(GMT +2:00) Kaliningrad, South Africa"
},
{
value: "3.0",
label: "(GMT +3:00) Baghdad, Riyadh, Moscow, St. Petersburg"
},
{
value: "3.5",
label: "(GMT +3:30) Tehran"
},
{
value: "4.0",
label: "(GMT +4:00) Abu Dhabi, Muscat, Baku, Tbilisi"
},
{
value: "4.5",
label: "(GMT +4:30) Kabul"
},
{
value: "5.0",
label: "(GMT +5:00) Ekaterinburg, Islamabad, Karachi, Tashkent"
},
{
value: "5.5",
label: "(GMT +5:30) Bombay, Calcutta, Madras, New Delhi"
},
{
value: "5.75",
label: "(GMT +5:45) Kathmandu"
},
{
value: "6.0",
label: "(GMT +6:00) Almaty, Dhaka, Colombo"
},
{
value: "7.0",
label: "(GMT +7:00) Bangkok, Hanoi, Jakarta"
},
{
value: "8.0",
label: "(GMT +8:00) Beijing, Perth, Singapore, Hong Kong"
},
{
value: "9.0",
label: "(GMT +9:00) Tokyo, Seoul, Osaka, Sapporo, Yakutsk"
},
{
value: "9.5",
label: "(GMT +9:30) Adelaide, Darwin"
},
{
value: "10.0",
label: "(GMT +10:00) Eastern Australia, Guam, Vladivostok"
},
{
value: "11.0",
label: "(GMT +11:00) Magadan, Solomon Islands, New Caledonia"
},
{
value: "12.0",
label: "(GMT +12:00) Auckland, Wellington, Fiji, Kamchatka"
}
]
}
const actions = {
changePreviewType: ({commit, state}, preview) => {
@@ -906,6 +1032,7 @@ const getters = {
requestedPlan: state => state.requestedPlan,
currencyList: state => state.currencyList,
countries: state => state.countries,
timezones: state=> state.timezones,
api: state => state.config.api,
config: state => state.config,
index: state => state.index,

14038
resources/js/store/modules/emojisList.js vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -80,7 +80,7 @@ const actions = {
})
axios
.get(getters.api + '/latest' + getters.sorting.URI)
.get(getters.api + '/latest' )
.then(response => {
commit('LOADING_STATE', {loading: false, data: response.data})
events.$emit('scrollTop')
@@ -209,6 +209,7 @@ const mutations = {
state.navigation = tree
},
LOADING_STATE(state, payload) {
state.fileInfoDetail= []
state.data = payload.data
state.isLoading = payload.loading
},
@@ -227,7 +228,6 @@ const mutations = {
state.browseHistory.pop()
},
CHANGE_ITEM_NAME(state, updatedFile) {
// Rename filename in file info detail
if (state.fileInfoDetail && state.fileInfoDetail.unique_id == updatedFile.unique_id) {
state.fileInfoDetail = updatedFile
@@ -235,7 +235,11 @@ const mutations = {
// Rename item name in data view
state.data.find(item => {
if (item.unique_id == updatedFile.unique_id) item.name = updatedFile.name
if (item.unique_id == updatedFile.unique_id) {
item.name = updatedFile.name
item.icon_color = updatedFile.icon_color ? updatedFile.icon_color : null
item.icon_emoji = updatedFile.icon_emoji ? updatedFile.icon_emoji : null
}
})
},
REMOVE_ITEM_FILEINFO_DETAIL(state,item) {

View File

@@ -6,10 +6,34 @@ import axios from 'axios'
import Vue from 'vue'
const defaultState = {
isZippingFiles: false,
processingPopup: undefined,
}
const actions = {
downloadFolder: ({commit, getters}, folder) => {
commit('PROCESSING_POPUP', {
title: i18n.t('popup_zipping.title'),
message: i18n.t('popup_zipping.message'),
})
// Get route
let route = getters.sharedDetail && !getters.sharedDetail.protected
? '/api/zip-folder/' + folder.unique_id + '/public/' + router.currentRoute.params.token
: '/api/zip-folder/' + folder.unique_id
axios.get(route)
.then(response => {
Vue.prototype.$downloadFile(response.data.url, response.data.name)
})
.catch(() => {
Vue.prototype.$isSomethingWrong()
})
.finally(() => {
commit('PROCESSING_POPUP', undefined)
})
},
downloadFiles: ({ commit, getters }) => {
let files = []
@@ -96,6 +120,11 @@ const actions = {
events.$emit('scrollTop')
//Set focus on new folder name
setTimeout(() => {
events.$emit('newFolder:focus', response.data.unique_id)
}, 10);
if (getters.currentFolder.location !== 'public')
dispatch('getAppData')
if (getters.currentFolder.location === 'public')
@@ -119,6 +148,7 @@ const actions = {
.post(route, {
name: data.name,
type: data.type,
folder_icon: data.folder_icon,
_method: 'patch'
})
.then(response => {
@@ -220,24 +250,36 @@ const actions = {
},
restoreItem: ({ commit, getters }, item) => {
let itemToRestore = []
let items = [item]
let restoreToHome = false
// If coming no selected item dont get items to restore from fileInfoDetail
if (!item)
items = getters.fileInfoDetail
// Check if file can be restored to home directory
if (getters.currentFolder.location === 'trash')
restoreToHome = true
// Remove file
commit('REMOVE_ITEM', item.unique_id)
items.forEach(data => itemToRestore.push({
'type': data.type,
'unique_id': data.unique_id,
}))
// Remove file preview
commit('CLEAR_FILEINFO_DETAIL')
axios
.post(getters.api + '/restore-item/' + item.unique_id, {
type: item.type,
.post(getters.api + '/restore-items' ,{
to_home: restoreToHome,
_method: 'patch'
data: itemToRestore,
})
.then(
// Remove file
items.forEach( data => commit('REMOVE_ITEM', data.unique_id) )
)
.catch(() => Vue.prototype.$isSomethingWrong())
},
deleteItem: ({ commit, getters, dispatch }, noSelectedItem) => {
@@ -340,13 +382,13 @@ const actions = {
}
const mutations = {
ZIPPING_FILE_STATUS(state, status) {
state.isZippingFiles = status
PROCESSING_POPUP(state, status) {
state.processingPopup = status
}
}
const getters = {
isZippingFiles: state => state.isZippingFiles
processingPopup: state => state.processingPopup
}
export default {

View File

@@ -39,11 +39,18 @@ const actions = {
})
},
logOut: ({getters, commit}) => {
let popup = setTimeout(() => {
commit('PROCESSING_POPUP', {
title: 'Logging Out',
message: 'Wait a second...',
})
}, 300)
axios
.get(getters.api + '/logout')
.then(() => {
// Commit Remove Access Token from vuex storage
clearTimeout(popup)
commit('DESTROY_DATA')
router.push({name: 'SignIn'})

View File

@@ -30,6 +30,14 @@
{{ $t('admin_menu.settings') }}
</div>
</router-link>
<router-link :to="{name: 'Pages'}" class="menu-list-item link">
<div class="icon">
<monitor-icon size="17"></monitor-icon>
</div>
<div class="label">
{{ $t('admin_menu.pages') }}
</div>
</router-link>
</div>
</ContentGroup>
@@ -52,14 +60,6 @@
{{ $t('admin_menu.invoices') }}
</div>
</router-link>
<router-link :to="{name: 'Pages'}" class="menu-list-item link">
<div class="icon">
<monitor-icon size="17"></monitor-icon>
</div>
<div class="label">
{{ $t('admin_menu.pages') }}
</div>
</router-link>
</div>
</ContentGroup>
</ContentSidebar>

View File

@@ -48,7 +48,7 @@
</div>
</router-link>
<router-link v-if="config.isSaaS" replace :to="{name: 'AppIndex'}" class="menu-list-item link">
<router-link replace :to="{name: 'AppIndex'}" class="menu-list-item link">
<div class="icon">
<home-icon size="17"></home-icon>
</div>

View File

@@ -1,9 +1,27 @@
<template>
<PageTab :is-loading="isLoading" class="form-fixed-width">
<PageTabGroup>
<PageTabGroup v-if="app">
<div class="form block-form">
<FormLabel>Home Page</FormLabel>
<div class="block-wrapper">
<div class="input-wrapper">
<div class="inline-wrapper">
<div class="switch-label">
<label class="input-label">
Allow Homepage
</label>
<small class="input-help">
When this is turned on, your visitors can visit your default homepage.
</small>
</div>
<SwitchInput @input="$updateText('/settings', 'allow_homepage', app.allow_homepage)" v-model="app.allow_homepage" class="switch" :state="app.allow_homepage"/>
</div>
</div>
</div>
<!--Header-->
<div>
<FormLabel>Header Title</FormLabel>
@@ -15,9 +33,7 @@
<div class="block-wrapper">
<label>Title:</label>
<ValidationProvider tag="div" mode="passive" class="input-wrapper" name="App Title" rules="required" v-slot="{ errors }">
<input @input="$updateText('/settings', 'header_title', header_title)" v-model="header_title"
type="text"
:class="{'is-error': errors[0]}"/>
<input @input="$updateText('/settings', 'header_title', app.header_title)" v-model="app.header_title" type="text" :class="{'is-error': errors[0]}"/>
<span class="error-message" v-if="errors[0]">{{ errors[0] }}</span>
</ValidationProvider>
</div>
@@ -25,8 +41,7 @@
<div class="block-wrapper">
<label>Description:</label>
<ValidationProvider tag="div" mode="passive" class="input-wrapper" name="App Description" rules="required" v-slot="{ errors }">
<textarea @input="$updateText('/settings', 'header_description', header_description)" rows="2" v-model="header_description"
:class="{'is-error': errors[0]}"></textarea>
<textarea @input="$updateText('/settings', 'header_description', app.header_description)" rows="2" v-model="app.header_description" :class="{'is-error': errors[0]}"></textarea>
<span class="error-message" v-if="errors[0]">{{ errors[0] }}</span>
</ValidationProvider>
</div>
@@ -44,18 +59,13 @@
Show section:
</label>
</div>
<SwitchInput
@input="$updateText('/settings', 'section_features', section_features)"
v-model="section_features"
class="switch"
:state="section_features"
/>
<SwitchInput @input="$updateText('/settings', 'section_features', app.section_features)" v-model="app.section_features" class="switch" :state="app.section_features"/>
</div>
</div>
</div>
<div v-if="section_features">
<div v-if="app.section_features">
<div class="block-wrapper">
<img src="/assets/images/admin/main-features.jpg" alt="Main Features" class="page-image">
</div>
@@ -63,9 +73,7 @@
<div class="block-wrapper">
<label>Title:</label>
<ValidationProvider tag="div" mode="passive" class="input-wrapper" name="App Title" rules="required" v-slot="{ errors }">
<input @input="$updateText('/settings', 'features_title', features_title)" v-model="features_title"
type="text"
:class="{'is-error': errors[0]}"/>
<input @input="$updateText('/settings', 'features_title', app.features_title)" v-model="app.features_title" type="text" :class="{'is-error': errors[0]}"/>
<span class="error-message" v-if="errors[0]">{{ errors[0] }}</span>
</ValidationProvider>
</div>
@@ -73,8 +81,7 @@
<div class="block-wrapper">
<label>Description:</label>
<ValidationProvider tag="div" mode="passive" class="input-wrapper" name="App Description" rules="required" v-slot="{ errors }">
<textarea @input="$updateText('/settings', 'features_description', features_description)" rows="2" v-model="features_description"
:class="{'is-error': errors[0]}"></textarea>
<textarea @input="$updateText('/settings', 'features_description', app.features_description)" rows="2" v-model="app.features_description" :class="{'is-error': errors[0]}"></textarea>
<span class="error-message" v-if="errors[0]">{{ errors[0] }}</span>
</ValidationProvider>
</div>
@@ -93,68 +100,54 @@
Show section:
</label>
</div>
<SwitchInput
@input="$updateText('/settings', 'section_feature_boxes', section_feature_boxes)"
v-model="section_feature_boxes"
class="switch"
:state="section_feature_boxes"
/>
<SwitchInput @input="$updateText('/settings', 'section_feature_boxes', app.section_feature_boxes)" v-model="app.section_feature_boxes" class="switch" :state="app.section_feature_boxes"/>
</div>
</div>
</div>
<div v-if="section_feature_boxes">
<div v-if="app.section_feature_boxes">
<div class="block-wrapper">
<img src="/assets/images/admin/feature-boxes.jpg" alt="Main Features" class="page-image">
</div>
<div class="block-wrapper">
<label>First Box Title:</label>
<ValidationProvider tag="div" mode="passive" class="input-wrapper" name="Feature Title 1" rules="required" v-slot="{ errors }">
<input @input="$updateText('/settings', 'feature_title_1', feature_title_1)" v-model="feature_title_1"
type="text"
:class="{'is-error': errors[0]}"/>
<input @input="$updateText('/settings', 'feature_title_1', app.feature_title_1)" v-model="app.feature_title_1" type="text" :class="{'is-error': errors[0]}"/>
<span class="error-message" v-if="errors[0]">{{ errors[0] }}</span>
</ValidationProvider>
</div>
<div class="block-wrapper">
<label>First Box Description:</label>
<ValidationProvider tag="div" mode="passive" class="input-wrapper" name="Feature Description 1" rules="required" v-slot="{ errors }">
<textarea @input="$updateText('/settings', 'feature_description_1', feature_description_1)" rows="2" v-model="feature_description_1"
:class="{'is-error': errors[0]}"></textarea>
<textarea @input="$updateText('/settings', 'feature_description_1', app.feature_description_1)" rows="2" v-model="app.feature_description_1" :class="{'is-error': errors[0]}"></textarea>
<span class="error-message" v-if="errors[0]">{{ errors[0] }}</span>
</ValidationProvider>
</div>
<div class="block-wrapper">
<label>Second Box Title:</label>
<ValidationProvider tag="div" mode="passive" class="input-wrapper" name="Feature Title 2" rules="required" v-slot="{ errors }">
<input @input="$updateText('/settings', 'feature_title_2', feature_title_2)" v-model="feature_title_2"
type="text"
:class="{'is-error': errors[0]}"/>
<input @input="$updateText('/settings', 'feature_title_2', app.feature_title_2)" v-model="app.feature_title_2" type="text" :class="{'is-error': errors[0]}"/>
<span class="error-message" v-if="errors[0]">{{ errors[0] }}</span>
</ValidationProvider>
</div>
<div class="block-wrapper">
<label>Second Box Description:</label>
<ValidationProvider tag="div" mode="passive" class="input-wrapper" name="Feature Description 2" rules="required" v-slot="{ errors }">
<textarea @input="$updateText('/settings', 'feature_description_2', feature_description_2)" rows="2" v-model="feature_description_2"
:class="{'is-error': errors[0]}"></textarea>
<textarea @input="$updateText('/settings', 'feature_description_2', app.feature_description_2)" rows="2" v-model="app.feature_description_2" :class="{'is-error': errors[0]}"></textarea>
<span class="error-message" v-if="errors[0]">{{ errors[0] }}</span>
</ValidationProvider>
</div>
<div class="block-wrapper">
<label>Third Box Title:</label>
<ValidationProvider tag="div" mode="passive" class="input-wrapper" name="Feature Title 3" rules="required" v-slot="{ errors }">
<input @input="$updateText('/settings', 'feature_title_3', feature_title_3)" v-model="feature_title_3"
type="text"
:class="{'is-error': errors[0]}"/>
<input @input="$updateText('/settings', 'feature_title_3', app.feature_title_3)" v-model="app.feature_title_3" type="text" :class="{'is-error': errors[0]}"/>
<span class="error-message" v-if="errors[0]">{{ errors[0] }}</span>
</ValidationProvider>
</div>
<div class="block-wrapper">
<label>Third Box Description:</label>
<ValidationProvider tag="div" mode="passive" class="input-wrapper" name="Feature Description 3" rules="required" v-slot="{ errors }">
<textarea @input="$updateText('/settings', 'feature_description_3', feature_description_3)" rows="2" v-model="feature_description_3"
:class="{'is-error': errors[0]}"></textarea>
<textarea @input="$updateText('/settings', 'feature_description_3', app.feature_description_3)" rows="2" v-model="app.feature_description_3" :class="{'is-error': errors[0]}"></textarea>
<span class="error-message" v-if="errors[0]">{{ errors[0] }}</span>
</ValidationProvider>
</div>
@@ -162,7 +155,7 @@
</div>
<!--Pricing Content-->
<div>
<div v-if="config.isSaaS">
<FormLabel class="mt-70">Pricing Content</FormLabel>
<div class="block-wrapper">
@@ -173,27 +166,19 @@
Show section:
</label>
</div>
<SwitchInput
@input="$updateText('/settings', 'section_pricing_content', section_pricing_content)"
v-model="section_pricing_content"
class="switch"
:state="section_pricing_content"
/>
<SwitchInput @input="$updateText('/settings', 'section_pricing_content', app.section_pricing_content)" v-model="app.section_pricing_content" class="switch" :state="app.section_pricing_content"/>
</div>
</div>
</div>
<div v-if="section_pricing_content">
<div v-if="app.section_pricing_content">
<div class="block-wrapper">
<img src="/assets/images/admin/pricing-content.jpg" alt="Main Features" class="page-image">
</div>
<div class="block-wrapper">
<label>Title:</label>
<ValidationProvider tag="div" mode="passive" class="input-wrapper" name="App Title" rules="required" v-slot="{ errors }">
<input @input="$updateText('/settings', 'pricing_title', pricing_title)" v-model="pricing_title"
type="text"
:class="{'is-error': errors[0]}"/>
<input @input="$updateText('/settings', 'pricing_title', app.pricing_title)" v-model="app.pricing_title" type="text" :class="{'is-error': errors[0]}"/>
<span class="error-message" v-if="errors[0]">{{ errors[0] }}</span>
</ValidationProvider>
</div>
@@ -201,8 +186,7 @@
<div class="block-wrapper">
<label>Description:</label>
<ValidationProvider tag="div" mode="passive" class="input-wrapper" name="App Description" rules="required" v-slot="{ errors }">
<textarea @input="$updateText('/settings', 'pricing_description', pricing_description)" rows="2" v-model="pricing_description"
:class="{'is-error': errors[0]}"></textarea>
<textarea @input="$updateText('/settings', 'pricing_description', app.pricing_description)" rows="2" v-model="app.pricing_description" :class="{'is-error': errors[0]}"></textarea>
<span class="error-message" v-if="errors[0]">{{ errors[0] }}</span>
</ValidationProvider>
</div>
@@ -221,27 +205,20 @@
Show section:
</label>
</div>
<SwitchInput
@input="$updateText('/settings', 'section_get_started', section_get_started)"
v-model="section_get_started"
class="switch"
:state="section_get_started"
/>
<SwitchInput @input="$updateText('/settings', 'section_get_started', app.section_get_started)" v-model="app.section_get_started" class="switch" :state="app.section_get_started"/>
</div>
</div>
</div>
<div v-if="section_get_started">
<div v-if="app.section_get_started">
<div class="block-wrapper">
<img src="/assets/images/admin/get-started-content.jpg" alt="Main Features" class="page-image">
</div>
<div class="block-wrapper">
<label>Title:</label>
<ValidationProvider tag="div" mode="passive" class="input-wrapper" name="App Title" rules="required" v-slot="{ errors }">
<input @input="$updateText('/settings', 'get_started_title', get_started_title)" v-model="get_started_title"
type="text"
:class="{'is-error': errors[0]}"/>
<input @input="$updateText('/settings', 'get_started_title', app.get_started_title)" v-model="app.get_started_title" type="text" :class="{'is-error': errors[0]}"/>
<span class="error-message" v-if="errors[0]">{{ errors[0] }}</span>
</ValidationProvider>
</div>
@@ -249,8 +226,7 @@
<div class="block-wrapper">
<label>Description:</label>
<ValidationProvider tag="div" mode="passive" class="input-wrapper" name="App Description" rules="required" v-slot="{ errors }">
<textarea @input="$updateText('/settings', 'get_started_description', get_started_description)" rows="2" v-model="get_started_description"
:class="{'is-error': errors[0]}"></textarea>
<textarea @input="$updateText('/settings', 'get_started_description', app.get_started_description)" rows="2" v-model="app.get_started_description" :class="{'is-error': errors[0]}"></textarea>
<span class="error-message" v-if="errors[0]">{{ errors[0] }}</span>
</ValidationProvider>
</div>
@@ -264,9 +240,7 @@
<div class="block-wrapper">
<label>Footer content:</label>
<ValidationProvider tag="div" mode="passive" class="input-wrapper" name="App Title" rules="required" v-slot="{ errors }">
<input @input="$updateText('/settings', 'footer_content', footer_content)" v-model="footer_content"
type="text"
:class="{'is-error': errors[0]}"/>
<input @input="$updateText('/settings', 'footer_content', app.footer_content)" v-model="app.footer_content" type="text" :class="{'is-error': errors[0]}"/>
<span class="error-message" v-if="errors[0]">{{ errors[0] }}</span>
</ValidationProvider>
</div>
@@ -277,125 +251,100 @@
</template>
<script>
import {ValidationProvider, ValidationObserver} from 'vee-validate/dist/vee-validate.full'
import StorageItemDetail from '@/components/Others/StorageItemDetail'
import PageTabGroup from '@/components/Others/Layout/PageTabGroup'
import SelectInput from '@/components/Others/Forms/SelectInput'
import SwitchInput from '@/components/Others/Forms/SwitchInput'
import ImageInput from '@/components/Others/Forms/ImageInput'
import FormLabel from '@/components/Others/Forms/FormLabel'
import ButtonBase from '@/components/FilesView/ButtonBase'
import SetupBox from '@/components/Others/Forms/SetupBox'
import PageTab from '@/components/Others/Layout/PageTab'
import InfoBox from '@/components/Others/Forms/InfoBox'
import {required} from 'vee-validate/dist/rules'
import axios from 'axios'
import { ValidationProvider, ValidationObserver } from 'vee-validate/dist/vee-validate.full'
import StorageItemDetail from '@/components/Others/StorageItemDetail'
import PageTabGroup from '@/components/Others/Layout/PageTabGroup'
import SelectInput from '@/components/Others/Forms/SelectInput'
import SwitchInput from '@/components/Others/Forms/SwitchInput'
import ImageInput from '@/components/Others/Forms/ImageInput'
import FormLabel from '@/components/Others/Forms/FormLabel'
import ButtonBase from '@/components/FilesView/ButtonBase'
import SetupBox from '@/components/Others/Forms/SetupBox'
import PageTab from '@/components/Others/Layout/PageTab'
import InfoBox from '@/components/Others/Forms/InfoBox'
import { required } from 'vee-validate/dist/rules'
import axios from 'axios'
import { mapGetters } from 'vuex'
export default {
name: 'AppIndex',
components: {
ValidationObserver,
ValidationProvider,
StorageItemDetail,
PageTabGroup,
SwitchInput,
SelectInput,
ImageInput,
ButtonBase,
FormLabel,
SetupBox,
required,
PageTab,
InfoBox,
},
data() {
return {
isLoading: true,
section_features: 1,
section_feature_boxes: 1,
section_pricing_content: 1,
section_get_started: 1,
header_title: '',
header_description: '',
features_title: '',
features_description: '',
feature_title_1: '',
feature_description_1: '',
feature_title_2: '',
feature_description_2: '',
feature_title_3: '',
feature_description_3: '',
pricing_title: '',
pricing_description: '',
get_started_title: '',
get_started_description: '',
footer_content: '',
}
},
mounted() {
axios.get('/api/settings', {
params: {
column: 'footer_content|get_started_description|get_started_title|pricing_description|pricing_title|feature_description_3|feature_title_3|feature_description_2|feature_title_2|feature_description_1|feature_title_1|features_description|features_title|header_description|header_title|section_get_started|section_pricing_content|section_feature_boxes|section_features'
}
})
.then(response => {
this.section_features = parseInt(response.data.section_features)
this.section_feature_boxes = parseInt(response.data.section_feature_boxes)
this.section_pricing_content = parseInt(response.data.section_pricing_content)
this.section_get_started = parseInt(response.data.section_get_started)
this.header_title = response.data.header_title
this.header_description = response.data.header_description
this.features_title = response.data.features_title
this.features_description = response.data.features_description
this.feature_title_1 = response.data.feature_title_1
this.feature_description_1 = response.data.feature_description_1
this.feature_title_2 = response.data.feature_title_2
this.feature_description_2 = response.data.feature_description_2
this.feature_title_3 = response.data.feature_title_3
this.feature_description_3 = response.data.feature_description_3
this.pricing_title = response.data.pricing_title
this.pricing_description = response.data.pricing_description
this.get_started_title = response.data.get_started_title
this.get_started_description = response.data.get_started_description
this.footer_content = response.data.footer_content
})
.finally(() => {
this.isLoading = false
})
export default {
name: 'AppIndex',
components: {
ValidationObserver,
ValidationProvider,
StorageItemDetail,
PageTabGroup,
SwitchInput,
SelectInput,
ImageInput,
ButtonBase,
FormLabel,
SetupBox,
required,
PageTab,
InfoBox
},
computed: {
...mapGetters(['config'])
},
data() {
return {
isLoading: true,
app: undefined
}
},
mounted() {
axios.get('/api/settings', {
params: {
column: 'allow_homepage|footer_content|get_started_description|get_started_title|pricing_description|pricing_title|feature_description_3|feature_title_3|feature_description_2|feature_title_2|feature_description_1|feature_title_1|features_description|features_title|header_description|header_title|section_get_started|section_pricing_content|section_feature_boxes|section_features'
}
})
.then(response => {
this.app = {
allow_homepage: parseInt(response.data.allow_homepage),
section_features: parseInt(response.data.section_features),
section_feature_boxes: parseInt(response.data.section_feature_boxes),
section_pricing_content: parseInt(response.data.section_pricing_content),
section_get_started: parseInt(response.data.section_get_started),
header_title: response.data.header_title,
header_description: response.data.header_description,
features_title: response.data.features_title,
features_description: response.data.features_description,
feature_title_1: response.data.feature_title_1,
feature_description_1: response.data.feature_description_1,
feature_title_2: response.data.feature_title_2,
feature_description_2: response.data.feature_description_2,
feature_title_3: response.data.feature_title_3,
feature_description_3: response.data.feature_description_3,
pricing_title: response.data.pricing_title,
pricing_description: response.data.pricing_description,
get_started_title: response.data.get_started_title,
get_started_description: response.data.get_started_description,
footer_content: response.data.footer_content
}
console.log(this.app);
})
.finally(() => {
this.isLoading = false
})
}
}
</script>
<style lang="scss" scoped>
@import '@assets/vue-file-manager/_variables';
@import '@assets/vue-file-manager/_mixins';
@import '@assets/vue-file-manager/_forms';
@import '@assets/vue-file-manager/_variables';
@import '@assets/vue-file-manager/_mixins';
@import '@assets/vue-file-manager/_forms';
.block-form {
max-width: 100%;
}
.block-form {
max-width: 100%;
}
.page-image {
width: 100%;
margin: 0 auto;
display: block;
border-radius: 8px;
border: 1px solid #ececec;
}
.page-image {
width: 100%;
margin: 0 auto;
display: block;
border-radius: 8px;
border: 1px solid #ececec;
}
</style>

View File

@@ -22,7 +22,7 @@
{{ data.license }}
</ColorLabel>
</a>
<a v-if="! config.isDemo" href="https://vuefilemanager.com/become-a-backer" target="_blank" class="became-backer">
<a href="https://bit.ly/VueFileManager-survey" target="_blank" class="became-backer">
<div class="icon">
<credit-card-icon size="15"></credit-card-icon>
</div>
@@ -177,7 +177,7 @@
}
.became-backer {
background: rgba($yellow, 0.1);
background: rgba($theme, 0.1);
display: inline-block;
padding: 5px 10px;
border-radius: 6px;
@@ -194,12 +194,12 @@
line-height: 0;
rect, line {
stroke: $yellow;
stroke: $theme;
}
}
.content {
color: $yellow;
color: $theme;
font-weight: 700;
@include font-size(14);
}

View File

@@ -208,6 +208,7 @@
},
created() {
this.$scrollTop()
this.$store.commit('PROCESSING_POPUP', undefined)
}
}
</script>

View File

@@ -53,7 +53,7 @@
</div>
<div>
<i18n v-if="config.isSaaS" path="page_registration.agreement" tag="p" class="legal-agreement">
<i18n path="page_registration.agreement" tag="p" class="legal-agreement">
<router-link :to="{name: 'DynamicPage', params: {slug: 'terms-of-service'}}" target="_blank">{{ termsOfService.title }}</router-link>
<router-link :to="{name: 'DynamicPage', params: {slug: 'privacy-policy'}}" target="_blank">{{ privacyPolicy.title }}</router-link>
</i18n>

View File

@@ -188,7 +188,7 @@ export default {
events.$on('drop', () => {
this.dragInProgress = false
})
},
}
}
</script>

View File

@@ -14,7 +14,7 @@
<MainFeatures />
<!--Pricing Tables-->
<PricingTables />
<PricingTables v-if="config.isSaaS" />
<!--Get Started Call To Action-->
<GetStarted />
@@ -60,18 +60,15 @@
isLoading: true,
}
},
beforeMount() {
if (! this.config.isSaaS) {
this.$router.push({name: 'SignIn'})
}
},
mounted() {
if (! this.config.isSaaS) return
if (! this.config.allowHomepage)
this.$router.push({name: 'SignIn'})
// Get page content
axios.get('/api/content', {
params: {
column: 'footer_content|get_started_description|get_started_title|pricing_description|pricing_title|feature_description_3|feature_title_3|feature_description_2|feature_title_2|feature_description_1|feature_title_1|features_description|features_title|header_description|header_title|section_get_started|section_pricing_content|section_feature_boxes|section_features'
column: 'allow_homepage|footer_content|get_started_description|get_started_title|pricing_description|pricing_title|feature_description_3|feature_title_3|feature_description_2|feature_title_2|feature_description_1|feature_title_1|features_description|features_title|header_description|header_title|section_get_started|section_pricing_content|section_feature_boxes|section_features'
}
})
.then(response => {

View File

@@ -9,12 +9,18 @@
<!--Move item setup-->
<MoveItem />
<!-- Processing popup for zip -->
<ProcessingPopup/>
<!-- Mobile Menu for Multi selected items -->
<MobileMultiSelectMenu/>
<!--Rename folder or file item-->
<RenameItem/>
<!--Create folder in mobile version-->
<CreateFolder/>
<!-- Drag & Drop UI -->
<DragUI/>
@@ -116,15 +122,16 @@
import MobileSortingAndPreview from '@/components/FilesView/MobileSortingAndPreview'
import MobileMultiSelectMenu from '@/components/FilesView/MobileMultiSelectMenu'
import DesktopSortingAndPreview from '@/components/FilesView/DesktopSortingAndPreview'
import ProcessingPopup from '@/components/FilesView/ProcessingPopup'
import TreeMenuNavigator from '@/components/Others/TreeMenuNavigator'
import FileFullPreview from '@/components/FilesView/FileFullPreview'
import DesktopToolbar from '@/components/FilesView/DesktopToolbar'
import ContentSidebar from '@/components/Sidebar/ContentSidebar'
import DragUI from '@/components/FilesView/DragUI'
import FileItemGrid from '@/components/FilesView/FileItemGrid'
import ContentGroup from '@/components/Sidebar/ContentGroup'
import FileBrowser from '@/components/FilesView/FileBrowser'
import ContextMenu from '@/components/FilesView/ContextMenu'
import CreateFolder from '@/components/Others/CreateFolder'
import ButtonBase from '@/components/FilesView/ButtonBase'
import MobileMenu from '@/components/FilesView/MobileMenu'
import AuthContent from '@/components/Auth/AuthContent'
@@ -133,6 +140,7 @@
import Spinner from '@/components/FilesView/Spinner'
import MoveItem from '@/components/Others/MoveItem'
import Vignette from '@/components/Others/Vignette'
import DragUI from '@/components/FilesView/DragUI'
import Alert from '@/components/FilesView/Alert'
import {required} from 'vee-validate/dist/rules'
import {mapGetters} from 'vuex'
@@ -152,9 +160,10 @@
ValidationObserver,
TreeMenuNavigator,
FileFullPreview,
ProcessingPopup,
DesktopToolbar,
ContentSidebar,
DragUI,
CreateFolder,
FileItemGrid,
ContentGroup,
AuthContent,
@@ -169,6 +178,7 @@
required,
Vignette,
Spinner,
DragUI,
Alert,
},
computed: {

View File

@@ -25,6 +25,23 @@
</div>
</div>
</PageTabGroup>
<PageTabGroup v-if="userInfo">
<div class="form block-form">
<FormLabel>{{$t('user_settings.timezone')}}</FormLabel>
<div class="block-wrapper">
<label>GMT:</label>
<div class="input-wrapper">
<SelectInput @input="$updateText('/user/relationships/settings', 'timezone', userTimezone)"
v-model="userTimezone"
:default="userTimezone"
:options="timezones"
:placeholder="$t('user_settings.timezone_plac')"/>
</div>
</div>
</div>
</PageTabGroup>
<PageTabGroup v-if="config.isSaaS && billingInfo">
<div class="form block-form">
<FormLabel>{{ $t('user_settings.title_billing') }}</FormLabel>
@@ -141,12 +158,13 @@
PageTab,
},
computed: {
...mapGetters(['config', 'countries']),
...mapGetters(['config', 'countries', 'timezones']),
},
data() {
return {
userInfo: undefined,
billingInfo: undefined,
userTimezone: undefined,
isLoading: false,
}
},
@@ -158,6 +176,8 @@
},
created() {
this.userTimezone = this.user.relationships.timezone.data.attributes.timezone
this.userInfo = {
name: this.user.data.attributes.name,
email: this.user.data.attributes.email

View File

@@ -7,6 +7,12 @@ return [
'time' => '%d. %B. %Y 于 %H:%M',
'home' => '首页',
//Shared link email message
'shared_link_email_subject' => '🙋 :user share some files with you. Look at it!',
'shared_link_email_greeting' => 'Hello!',
'shared_link_email_user' => ':user (:email) send you a link to shared files.',
'shared_link_email_link' => 'Open your files',
// Reset password email
'reset_password_greeting' => 'Hello!',
'reset_password_subject' => 'Reset password for your account on ',

View File

@@ -7,6 +7,12 @@ return [
'time' => '%d. %B. %Y at %H:%M',
'home' => 'Home',
//Shared link email message
'shared_link_email_subject' => '🙋 :user share some files with you. Look at it!',
'shared_link_email_greeting' => 'Hello!',
'shared_link_email_user' => ':user (:email) send you a link to shared files.',
'shared_link_email_link' => 'Open your files',
// Reset password email
'reset_password_greeting' => 'Hello!',
'reset_password_subject' => 'Reset password for your account on ',

View File

@@ -7,6 +7,12 @@ return [
'time' => '%d. %B. %Y o %H:%M',
'home' => 'Domov',
//Shared link email message
'shared_link_email_subject' => '🙋 :user vám posiela zdieľané súbory.',
'shared_link_email_greeting' => 'Ahoj!',
'shared_link_email_user' => ':user (:email) vám posiela odkaz pre zdieľané súbory.',
'shared_link_email_link' => 'Vaše súbory',
// Reset password email
'reset_password_greeting' => 'Ahoj!',
'reset_password_subject' => 'Resetujte svoje heslo v aplikácií ',

View File

@@ -442,7 +442,19 @@
}
.info-box-wrapper {
margin: 0px 20px;
}
.windows {
#content-sidebar {
scrollbar-width: none;
&::-webkit-scrollbar {
width: 0px;
}
}
::-webkit-scrollbar {
width: 18px;
height: 18px;

View File

@@ -11,6 +11,8 @@ $red: #FE6057;
$purple: #9D66FE;
$light_mode_border: #F8F8F8;
$light_mode_border_darken: #E8E9EB;
$danger: #fd397a;
$light_text: #A4ADB6;
$light_background: #f4f5f6;

View File

@@ -52,6 +52,7 @@
stripe_public_key: '{{ config('cashier.key') ? config('cashier.key') : null }}',
userRegistration: {{ isset($settings->registration) ? $settings->registration : 1 }},
allowHomepage: {{ isset($settings->allow_homepage) ? $settings->allow_homepage : 1 }},
storageLimit: {{ isset($settings->storage_limitation) ? $settings->storage_limitation : 1 }},
storageDefaultSpace: {{ isset($settings->storage_default) ? $settings->storage_default : 5 }},
storageDefaultSpaceFormatted: '{{ isset($settings->storage_default) ? format_gigabytes($settings->storage_default) : format_gigabytes(5) }}',