From b4887cea0e353b2b0f3a12de5d63f34da571aaee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=8Carodej?= Date: Wed, 5 Jan 2022 12:48:07 +0100 Subject: [PATCH] - Restriction UI warning - create folder restriction - fixed UI bugs --- public/mix-manifest.json | 91 ++++++++++++++++++- resources/js/App.vue | 11 ++- .../Others/TwoFactorRecoveryCodesPopup.vue | 3 - resources/js/helpers/functionHelpers.js | 2 +- resources/js/helpers/itemHelpers.js | 44 ++++----- resources/js/store/modules/fileFunctions.js | 14 ++- resources/js/store/modules/userAuth.js | 1 + resources/js/views/Platform.vue | 3 +- .../Engines/FixedBillingLimitationEngine.php | 3 +- .../MeteredBillingLimitationEngine.php | 13 +-- .../Exceptions/InvalidUserActionException.php | 10 ++ src/App/Users/Models/User.php | 22 ++--- src/Domain/Files/Actions/UploadFileAction.php | 5 +- .../Controllers/UploadFileController.php | 12 ++- .../Folders/Actions/CreateFolderAction.php | 23 +++-- .../Controllers/CreateFolderController.php | 14 ++- .../VisitorCreateFolderController.php | 23 ++++- .../App/Limitations/DefaultLimitationTest.php | 20 ++++ .../FixedBillingLimitationTest.php | 20 ++++ .../MeteredBillingLimitationTest.php | 41 +++++++++ tests/Domain/Files/FileTest.php | 2 +- 21 files changed, 306 insertions(+), 71 deletions(-) create mode 100644 src/App/Users/Exceptions/InvalidUserActionException.php diff --git a/public/mix-manifest.json b/public/mix-manifest.json index 958f7d06..2f8ffa23 100644 --- a/public/mix-manifest.json +++ b/public/mix-manifest.json @@ -63,14 +63,14 @@ "/chunks/plan-settings.js": "/chunks/plan-settings.js?id=8a959e196d628d0efc4b", "/chunks/plan-subscribers.js": "/chunks/plan-subscribers.js?id=7eff560fea52770a642f", "/chunks/plans.js": "/chunks/plans.js?id=854c232c29d88c5b541a", - "/chunks/platform.js": "/chunks/platform.js?id=d6b426d84871fc636e9e", + "/chunks/platform.js": "/chunks/platform.js?id=c3427a1bceb2bbf9f738", "/chunks/platform~chunks/shared.js": "/chunks/platform~chunks/shared.js?id=440568dcf151d51d2486", "/chunks/platform~chunks/shared~chunks/shared-with-me~chunks/team-folders.js": "/chunks/platform~chunks/shared~chunks/shared-with-me~chunks/team-folders.js?id=7d983dfdc91de607d737", "/chunks/profile.js": "/chunks/profile.js?id=8688d9c7ff850e6989e6", "/chunks/profile~chunks/settings-password.js": "/chunks/profile~chunks/settings-password.js?id=dfa4128d68360d5e1b3b", "/chunks/purchase-code.js": "/chunks/purchase-code.js?id=9e948882ae2315eb6132", "/chunks/recent-uploads.js": "/chunks/recent-uploads.js?id=8577d4c771602671b38a", - "/chunks/settings.js": "/chunks/settings.js?id=548f24fc6d666c9db1a8", + "/chunks/settings.js": "/chunks/settings.js?id=4466d97a0211f77d9317", "/chunks/settings-password.js": "/chunks/settings-password.js?id=b6eb94764cc7b47f835e", "/chunks/settings-storage.js": "/chunks/settings-storage.js?id=76b45c336e8e12b23e81", "/chunks/settings~chunks/settings-password.js": "/chunks/settings~chunks/settings-password.js?id=aafc9cd6aa47b01bc25a", @@ -113,5 +113,90 @@ "/chunks/settings.35f000715b5a03522346.hot-update.js": "/chunks/settings.35f000715b5a03522346.hot-update.js", "/chunks/settings.51d3c0571252e4f23ff1.hot-update.js": "/chunks/settings.51d3c0571252e4f23ff1.hot-update.js", "/chunks/settings.255054593b1cd7f654d3.hot-update.js": "/chunks/settings.255054593b1cd7f654d3.hot-update.js", - "/chunks/settings.2438d1c93de4d0c45c67.hot-update.js": "/chunks/settings.2438d1c93de4d0c45c67.hot-update.js" + "/chunks/settings.2438d1c93de4d0c45c67.hot-update.js": "/chunks/settings.2438d1c93de4d0c45c67.hot-update.js", + "/chunks/platform.5811795aa26f29d23f5b.hot-update.js": "/chunks/platform.5811795aa26f29d23f5b.hot-update.js", + "/chunks/platform.8c481bcdc638d27a4a84.hot-update.js": "/chunks/platform.8c481bcdc638d27a4a84.hot-update.js", + "/chunks/platform.2c801c878d2302c04b04.hot-update.js": "/chunks/platform.2c801c878d2302c04b04.hot-update.js", + "/chunks/platform.6ae437c53d3355af934e.hot-update.js": "/chunks/platform.6ae437c53d3355af934e.hot-update.js", + "/chunks/platform.8a1132d7879b623a0ee4.hot-update.js": "/chunks/platform.8a1132d7879b623a0ee4.hot-update.js", + "/js/main.34827559ca00fbb7f164.hot-update.js": "/js/main.34827559ca00fbb7f164.hot-update.js", + "/js/main.11ddf2d8a48a946eb9b9.hot-update.js": "/js/main.11ddf2d8a48a946eb9b9.hot-update.js", + "/js/main.c81f24a0fd629af6c208.hot-update.js": "/js/main.c81f24a0fd629af6c208.hot-update.js", + "/js/main.07d32454f0bd3299c3bb.hot-update.js": "/js/main.07d32454f0bd3299c3bb.hot-update.js", + "/js/main.deef2a5be87c2f0386e1.hot-update.js": "/js/main.deef2a5be87c2f0386e1.hot-update.js", + "/js/main.7e7cfb75cc4b9255a594.hot-update.js": "/js/main.7e7cfb75cc4b9255a594.hot-update.js", + "/js/main.e31064a188136858221e.hot-update.js": "/js/main.e31064a188136858221e.hot-update.js", + "/js/main.5295702a8639b61805ac.hot-update.js": "/js/main.5295702a8639b61805ac.hot-update.js", + "/js/main.1663362fc2afa6e6c7fd.hot-update.js": "/js/main.1663362fc2afa6e6c7fd.hot-update.js", + "/js/main.b7f439a45efadc0d09c9.hot-update.js": "/js/main.b7f439a45efadc0d09c9.hot-update.js", + "/js/main.ab2d94da6e88e19834fc.hot-update.js": "/js/main.ab2d94da6e88e19834fc.hot-update.js", + "/js/main.d1bac7303de2752f8f2a.hot-update.js": "/js/main.d1bac7303de2752f8f2a.hot-update.js", + "/js/main.fa4020861f2256c1a814.hot-update.js": "/js/main.fa4020861f2256c1a814.hot-update.js", + "/js/main.49e61ab55a7999ac2d7c.hot-update.js": "/js/main.49e61ab55a7999ac2d7c.hot-update.js", + "/js/main.9a3d34d1573f5a360549.hot-update.js": "/js/main.9a3d34d1573f5a360549.hot-update.js", + "/js/main.a09414329bcddae0193d.hot-update.js": "/js/main.a09414329bcddae0193d.hot-update.js", + "/js/main.9af992eb88a9c439a7ab.hot-update.js": "/js/main.9af992eb88a9c439a7ab.hot-update.js", + "/js/main.583d206005a97f6eaede.hot-update.js": "/js/main.583d206005a97f6eaede.hot-update.js", + "/js/main.1a94a099a196d5dd94a7.hot-update.js": "/js/main.1a94a099a196d5dd94a7.hot-update.js", + "/vendors~chunks/admin~chunks/admin-account~chunks/app-appearance~chunks/app-billings~chunks/app-email~29ecd2dd.js": "/vendors~chunks/admin~chunks/admin-account~chunks/app-appearance~chunks/app-billings~chunks/app-email~29ecd2dd.js?id=6a9136010589fefae8c1", + "/js/main.4280084333a6f807e55e.hot-update.js": "/js/main.4280084333a6f807e55e.hot-update.js", + "/chunks/admin~chunks/app-language~chunks/dashboard~chunks/files~chunks/invoices~chunks/my-shared-item~3b21240c.4280084333a6f807e55e.hot-update.js": "/chunks/admin~chunks/app-language~chunks/dashboard~chunks/files~chunks/invoices~chunks/my-shared-item~3b21240c.4280084333a6f807e55e.hot-update.js", + "/chunks/admin~chunks/platform~chunks/settings~chunks/shared.4280084333a6f807e55e.hot-update.js": "/chunks/admin~chunks/platform~chunks/settings~chunks/shared.4280084333a6f807e55e.hot-update.js", + "/chunks/app-appearance~chunks/app-billings~chunks/app-email~chunks/app-index~chunks/app-others~chunks~217928f6.4280084333a6f807e55e.hot-update.js": "/chunks/app-appearance~chunks/app-billings~chunks/app-email~chunks/app-index~chunks/app-others~chunks~217928f6.4280084333a6f807e55e.hot-update.js", + "/chunks/app-appearance~chunks/app-billings~chunks/app-email~chunks/app-index~chunks/app-others~chunks~5acee76d.4280084333a6f807e55e.hot-update.js": "/chunks/app-appearance~chunks/app-billings~chunks/app-email~chunks/app-index~chunks/app-others~chunks~5acee76d.4280084333a6f807e55e.hot-update.js", + "/chunks/app-language.4280084333a6f807e55e.hot-update.js": "/chunks/app-language.4280084333a6f807e55e.hot-update.js", + "/chunks/billing.4280084333a6f807e55e.hot-update.js": "/chunks/billing.4280084333a6f807e55e.hot-update.js", + "/chunks/dashboard~chunks/invoices~chunks/pages~chunks/plans~chunks/subscriptions.4280084333a6f807e55e.hot-update.js": "/chunks/dashboard~chunks/invoices~chunks/pages~chunks/plans~chunks/subscriptions.4280084333a6f807e55e.hot-update.js", + "/chunks/homepage.4280084333a6f807e55e.hot-update.js": "/chunks/homepage.4280084333a6f807e55e.hot-update.js", + "/chunks/installation-disclaimer.4280084333a6f807e55e.hot-update.js": "/chunks/installation-disclaimer.4280084333a6f807e55e.hot-update.js", + "/chunks/invitation.4280084333a6f807e55e.hot-update.js": "/chunks/invitation.4280084333a6f807e55e.hot-update.js", + "/chunks/page-edit.4280084333a6f807e55e.hot-update.js": "/chunks/page-edit.4280084333a6f807e55e.hot-update.js", + "/chunks/plan.4280084333a6f807e55e.hot-update.js": "/chunks/plan.4280084333a6f807e55e.hot-update.js", + "/chunks/plan-create/fixed.4280084333a6f807e55e.hot-update.js": "/chunks/plan-create/fixed.4280084333a6f807e55e.hot-update.js", + "/chunks/plan-create/metered.4280084333a6f807e55e.hot-update.js": "/chunks/plan-create/metered.4280084333a6f807e55e.hot-update.js", + "/chunks/plan-delete.4280084333a6f807e55e.hot-update.js": "/chunks/plan-delete.4280084333a6f807e55e.hot-update.js", + "/chunks/profile~chunks/settings-password.4280084333a6f807e55e.hot-update.js": "/chunks/profile~chunks/settings-password.4280084333a6f807e55e.hot-update.js", + "/chunks/settings-storage.4280084333a6f807e55e.hot-update.js": "/chunks/settings-storage.4280084333a6f807e55e.hot-update.js", + "/chunks/shared.4280084333a6f807e55e.hot-update.js": "/chunks/shared.4280084333a6f807e55e.hot-update.js", + "/chunks/shared-with-me~chunks/team-folders.4280084333a6f807e55e.hot-update.js": "/chunks/shared-with-me~chunks/team-folders.4280084333a6f807e55e.hot-update.js", + "/chunks/shared/single-file.4280084333a6f807e55e.hot-update.js": "/chunks/shared/single-file.4280084333a6f807e55e.hot-update.js", + "/chunks/sign-in.4280084333a6f807e55e.hot-update.js": "/chunks/sign-in.4280084333a6f807e55e.hot-update.js", + "/chunks/subscription-plans.4280084333a6f807e55e.hot-update.js": "/chunks/subscription-plans.4280084333a6f807e55e.hot-update.js", + "/chunks/subscriptions.4280084333a6f807e55e.hot-update.js": "/chunks/subscriptions.4280084333a6f807e55e.hot-update.js", + "/chunks/upgrade-billing~chunks/upgrade-plan.4280084333a6f807e55e.hot-update.js": "/chunks/upgrade-billing~chunks/upgrade-plan.4280084333a6f807e55e.hot-update.js", + "/chunks/user.4280084333a6f807e55e.hot-update.js": "/chunks/user.4280084333a6f807e55e.hot-update.js", + "/chunks/user-create.4280084333a6f807e55e.hot-update.js": "/chunks/user-create.4280084333a6f807e55e.hot-update.js", + "/chunks/user-subscription.4280084333a6f807e55e.hot-update.js": "/chunks/user-subscription.4280084333a6f807e55e.hot-update.js", + "/chunks/users.4280084333a6f807e55e.hot-update.js": "/chunks/users.4280084333a6f807e55e.hot-update.js", + "/js/main.759fcc4e53d2dc10eef7.hot-update.js": "/js/main.759fcc4e53d2dc10eef7.hot-update.js", + "/js/main.8c791a358db3536d409d.hot-update.js": "/js/main.8c791a358db3536d409d.hot-update.js", + "/js/main.56620b989bbce3ae0875.hot-update.js": "/js/main.56620b989bbce3ae0875.hot-update.js", + "/chunks/platform.68f9a7490063bdd5f3ab.hot-update.js": "/chunks/platform.68f9a7490063bdd5f3ab.hot-update.js", + "/chunks/platform.9fc7bcf362c1d4bbfc81.hot-update.js": "/chunks/platform.9fc7bcf362c1d4bbfc81.hot-update.js", + "/chunks/platform.ee93be9965a1cfa152fc.hot-update.js": "/chunks/platform.ee93be9965a1cfa152fc.hot-update.js", + "/chunks/platform.133e1026a756d3035944.hot-update.js": "/chunks/platform.133e1026a756d3035944.hot-update.js", + "/chunks/platform.6fcf086c7a4c1331a53c.hot-update.js": "/chunks/platform.6fcf086c7a4c1331a53c.hot-update.js", + "/js/main.f16a33ff7d5847b98266.hot-update.js": "/js/main.f16a33ff7d5847b98266.hot-update.js", + "/js/main.5f3afd688212022fb58a.hot-update.js": "/js/main.5f3afd688212022fb58a.hot-update.js", + "/js/main.007eb43910450b32c805.hot-update.js": "/js/main.007eb43910450b32c805.hot-update.js", + "/js/main.76d40e551720ac111fd9.hot-update.js": "/js/main.76d40e551720ac111fd9.hot-update.js", + "/js/main.b7babb7b19f9b09ca98d.hot-update.js": "/js/main.b7babb7b19f9b09ca98d.hot-update.js", + "/js/main.08e4f30c97bcc3a942e8.hot-update.js": "/js/main.08e4f30c97bcc3a942e8.hot-update.js", + "/js/main.fea58478d3dfe9206fed.hot-update.js": "/js/main.fea58478d3dfe9206fed.hot-update.js", + "/js/main.8881265f32669ed95bfb.hot-update.js": "/js/main.8881265f32669ed95bfb.hot-update.js", + "/chunks/settings.1eda8247e148c5d51d4d.hot-update.js": "/chunks/settings.1eda8247e148c5d51d4d.hot-update.js", + "/chunks/settings.d9d981a7197ff398ee22.hot-update.js": "/chunks/settings.d9d981a7197ff398ee22.hot-update.js", + "/chunks/platform.a7c645c2c8ceda4ae3f7.hot-update.js": "/chunks/platform.a7c645c2c8ceda4ae3f7.hot-update.js", + "/js/main.9fbff94db8eb20332da7.hot-update.js": "/js/main.9fbff94db8eb20332da7.hot-update.js", + "/js/main.959620b85c3df6282a5f.hot-update.js": "/js/main.959620b85c3df6282a5f.hot-update.js", + "/js/main.b8fe9507703cfb75e859.hot-update.js": "/js/main.b8fe9507703cfb75e859.hot-update.js", + "/js/main.a48539de27bb23dd5904.hot-update.js": "/js/main.a48539de27bb23dd5904.hot-update.js", + "/js/main.c2d0f57bdad53abf3859.hot-update.js": "/js/main.c2d0f57bdad53abf3859.hot-update.js", + "/js/main.9eb9d2d7d97cc33f46f7.hot-update.js": "/js/main.9eb9d2d7d97cc33f46f7.hot-update.js", + "/js/main.272640c4f5959835aa61.hot-update.js": "/js/main.272640c4f5959835aa61.hot-update.js", + "/js/main.287b5ae535954f8ed89c.hot-update.js": "/js/main.287b5ae535954f8ed89c.hot-update.js", + "/js/main.51906e2db8a6c36f5304.hot-update.js": "/js/main.51906e2db8a6c36f5304.hot-update.js", + "/js/main.c09b0f39fe6d0b43515b.hot-update.js": "/js/main.c09b0f39fe6d0b43515b.hot-update.js", + "/js/main.de28ca938da3825e62b3.hot-update.js": "/js/main.de28ca938da3825e62b3.hot-update.js", + "/js/main.be91e0ed3d68ecb1af06.hot-update.js": "/js/main.be91e0ed3d68ecb1af06.hot-update.js" } diff --git a/resources/js/App.vue b/resources/js/App.vue index 1bd33963..1bd2d408 100644 --- a/resources/js/App.vue +++ b/resources/js/App.vue @@ -9,6 +9,13 @@ + +
+ + {{ $t('Your functionality is restricted. Please review your billing settings.') }} + +
+ @@ -41,7 +48,9 @@ export default { }, computed: { ...mapGetters([ - 'isDarkMode' + 'isLimitedUser', + 'isDarkMode', + 'user', ]), }, watch: { diff --git a/resources/js/components/Others/TwoFactorRecoveryCodesPopup.vue b/resources/js/components/Others/TwoFactorRecoveryCodesPopup.vue index c38d8cf1..8536e3c5 100644 --- a/resources/js/components/Others/TwoFactorRecoveryCodesPopup.vue +++ b/resources/js/components/Others/TwoFactorRecoveryCodesPopup.vue @@ -143,9 +143,6 @@ export default { if ('two-factor-recovery-codes' === name) this.getRecoveryCodes() }) - }, - destroyed() { - events.$off('popup:open') } } diff --git a/resources/js/helpers/functionHelpers.js b/resources/js/helpers/functionHelpers.js index d0ce9999..cdfb1747 100644 --- a/resources/js/helpers/functionHelpers.js +++ b/resources/js/helpers/functionHelpers.js @@ -1,6 +1,6 @@ +import {debounce, isArray, orderBy} from "lodash" import i18n from '/resources/js/i18n/index' import store from '../store/index' -import {debounce, isArray, orderBy} from "lodash"; import {events} from '../bus' import axios from 'axios' diff --git a/resources/js/helpers/itemHelpers.js b/resources/js/helpers/itemHelpers.js index 1f7b9564..eef0e6a4 100644 --- a/resources/js/helpers/itemHelpers.js +++ b/resources/js/helpers/itemHelpers.js @@ -1,16 +1,18 @@ -import {events} from "../bus"; +import i18n from '/resources/js/i18n/index' +import store from '../store/index' +import {events} from '../bus' const itemHelpers = { install(Vue) { Vue.prototype.$emptyTrash = function () { - this.$store.dispatch('emptyTrash') + store.dispatch('emptyTrash') } Vue.prototype.$emptyTrashQuietly = function () { - this.$store.dispatch('emptyTrashQuietly') + store.dispatch('emptyTrashQuietly') } Vue.prototype.$shareCancel = function () { - this.$store.dispatch('shareCancel') + store.dispatch('shareCancel') } Vue.prototype.$renameFileOrFolder = function (entry) { @@ -22,16 +24,16 @@ const itemHelpers = { } Vue.prototype.$createFolder = function () { - this.$store.dispatch('createFolder', { - name: this.$t('popup_create_folder.folder_default_name') + store.dispatch('createFolder', { + name: i18n.t('popup_create_folder.folder_default_name') }) } Vue.prototype.$downloadSelection = function (item) { - let clipboard = this.$store.getters.clipboard + let clipboard = store.getters.clipboard if (clipboard.length > 1 || (clipboard.length === 1 && clipboard[0].data.type === 'folder')) - this.$store.dispatch('downloadZip') + store.dispatch('downloadZip') else { Vue.prototype.$downloadFile(item.data.attributes.file_url, item.data.attributes.name + '.' + item.data.attributes.mimetype) } @@ -39,8 +41,8 @@ const itemHelpers = { Vue.prototype.$dissolveTeamFolder = function (folder) { events.$emit('confirm:open', { - title: this.$t('Are you sure you want to dissolve this team?'), - message: this.$t('All team members will lose access to your files and existing folder will be moved into your "Files" section.'), + title: i18n.t('Are you sure you want to dissolve this team?'), + message: i18n.t('All team members will lose access to your files and existing folder will be moved into your "Files" section.'), action: { id: folder.data.id, operation: 'dissolve-team-folder', @@ -50,8 +52,8 @@ const itemHelpers = { Vue.prototype.$detachMeFromTeamFolder = function (folder) { events.$emit('confirm:open', { - title: this.$t('Are you sure you want to leave this team?'), - message: this.$t("You will don't have access to the files and all your previously uploaded content will be part of this Team Folder you are leaving."), + title: i18n.t('Are you sure you want to leave this team?'), + message: i18n.t("You will don't have access to the files and all your previously uploaded content will be part of this Team Folder you are leaving."), action: { id: folder.data.id, operation: 'leave-team-folder', @@ -72,25 +74,25 @@ const itemHelpers = { } Vue.prototype.$removeFavourite = function (folder) { - this.$store.dispatch('removeFromFavourites', folder) + store.dispatch('removeFromFavourites', folder) } Vue.prototype.$deleteFileOrFolder = function (entry) { - if (!this.$store.getters.clipboard.includes(entry)) { - this.$store.dispatch('deleteItem', entry) + if (!store.getters.clipboard.includes(entry)) { + store.dispatch('deleteItem', entry) } - if (this.$store.getters.clipboard.includes(entry)) { - this.$store.dispatch('deleteItem') + if (store.getters.clipboard.includes(entry)) { + store.dispatch('deleteItem') } } Vue.prototype.$restoreFileOrFolder = function (entry) { - if (!this.$store.getters.clipboard.includes(entry)) - this.$store.dispatch('restoreItem', entry) + if (!store.getters.clipboard.includes(entry)) + store.dispatch('restoreItem', entry) - if (this.$store.getters.clipboard.includes(entry)) - this.$store.dispatch('restoreItem', null) + if (store.getters.clipboard.includes(entry)) + store.dispatch('restoreItem', null) } Vue.prototype.$shareFileOrFolder = function (entry) { diff --git a/resources/js/store/modules/fileFunctions.js b/resources/js/store/modules/fileFunctions.js index 35b6d95c..0324f798 100644 --- a/resources/js/store/modules/fileFunctions.js +++ b/resources/js/store/modules/fileFunctions.js @@ -117,7 +117,12 @@ const actions = { dispatch('getAppData') }) - .catch(() => Vue.prototype.$isSomethingWrong()) + .catch(error => { + events.$emit('alert:open', { + title: error.response.data.message, + message: i18n.t('popup_error.message') + }) + }) }, renameItem: ({commit, getters, dispatch}, data) => { @@ -222,13 +227,16 @@ const actions = { '413': { title: i18n.t('popup_paylod_error.title'), message: i18n.t('popup_paylod_error.message') - } + }, + '401': { + title: error.response.data.message, + }, } events.$emit('alert:open', { emoji: '😬😬😬', title: messages[error.response.status]['title'], - message: messages[error.response.status]['message'] + message: messages[error.response.status]['message'] || null }) commit('PROCESSING_FILE', false) diff --git a/resources/js/store/modules/userAuth.js b/resources/js/store/modules/userAuth.js index 2f5fbd66..8d7ef4a4 100644 --- a/resources/js/store/modules/userAuth.js +++ b/resources/js/store/modules/userAuth.js @@ -143,6 +143,7 @@ const mutations = { } const getters = { + isLimitedUser: state => state.user && state.user.data.relationships.failedPayments && state.user.data.relationships.failedPayments.data.length === 3, permission: state => state.permission, user: state => state.user, } diff --git a/resources/js/views/Platform.vue b/resources/js/views/Platform.vue index 972499e7..52dc42d3 100644 --- a/resources/js/views/Platform.vue +++ b/resources/js/views/Platform.vue @@ -110,7 +110,8 @@ }, computed: { ...mapGetters([ - 'isVisibleSidebar' + 'isVisibleSidebar', + 'isLimitedUser', ]) }, data() { diff --git a/src/App/Limitations/Engines/FixedBillingLimitationEngine.php b/src/App/Limitations/Engines/FixedBillingLimitationEngine.php index 694250b6..27603078 100644 --- a/src/App/Limitations/Engines/FixedBillingLimitationEngine.php +++ b/src/App/Limitations/Engines/FixedBillingLimitationEngine.php @@ -15,8 +15,7 @@ class FixedBillingLimitationEngine implements LimitationEngine ); // Check if storage usage exceed predefined capacity - return ! ($usedPercentage >= 100) - ; + return ! ($usedPercentage >= 100); } public function canDownload(User $user): bool diff --git a/src/App/Limitations/Engines/MeteredBillingLimitationEngine.php b/src/App/Limitations/Engines/MeteredBillingLimitationEngine.php index 5a0c1142..b3cc26b3 100644 --- a/src/App/Limitations/Engines/MeteredBillingLimitationEngine.php +++ b/src/App/Limitations/Engines/MeteredBillingLimitationEngine.php @@ -9,21 +9,18 @@ class MeteredBillingLimitationEngine implements LimitationEngine public function canUpload(User $user, int $fileSize = 0): bool { // Disable upload when user has more than 3 failed payments - return ! ($user->failedPayments()->count() >= 3) - ; + return ! ($user->failedPayments()->count() >= 3); } public function canDownload(User $user): bool { - // Disable upload when user has more than 3 failed payments - return ! ($user->failedPayments()->count() >= 3) - ; + // Disable download when user has more than 3 failed payments + return ! ($user->failedPayments()->count() >= 3); } public function canCreateFolder(User $user): bool { - // Disable upload when user has more than 3 failed payments - return ! ($user->failedPayments()->count() >= 3) - ; + // Disable create folder when user has more than 3 failed payments + return ! ($user->failedPayments()->count() >= 3); } } diff --git a/src/App/Users/Exceptions/InvalidUserActionException.php b/src/App/Users/Exceptions/InvalidUserActionException.php new file mode 100644 index 00000000..ce69f62e --- /dev/null +++ b/src/App/Users/Exceptions/InvalidUserActionException.php @@ -0,0 +1,10 @@ +driver() - ->$method($this, ...$parameters); - } - - return parent::__call($method, $parameters); - } - /** * Get user used storage details */ @@ -211,6 +200,17 @@ class User extends Authenticatable implements MustVerifyEmail $this->notify(new ResetPassword($token)); } + public function __call($method, $parameters) + { + if (str_starts_with($method, 'can')) { + return resolve(LimitationManager::class) + ->driver() + ->$method($this, ...$parameters); + } + + return parent::__call($method, $parameters); + } + protected static function boot() { parent::boot(); diff --git a/src/Domain/Files/Actions/UploadFileAction.php b/src/Domain/Files/Actions/UploadFileAction.php index 32774bad..c52d6760 100644 --- a/src/Domain/Files/Actions/UploadFileAction.php +++ b/src/Domain/Files/Actions/UploadFileAction.php @@ -1,6 +1,7 @@ canUpload($fileSize)) { Storage::disk('local')->delete("chunks/$chunkName"); - abort(423, 'You exceed your storage limit!'); + throw new InvalidUserActionException(); } // Move finished file from chunk to file-manager directory diff --git a/src/Domain/Files/Controllers/UploadFileController.php b/src/Domain/Files/Controllers/UploadFileController.php index ef250dc9..602794b8 100644 --- a/src/Domain/Files/Controllers/UploadFileController.php +++ b/src/Domain/Files/Controllers/UploadFileController.php @@ -1,6 +1,7 @@ fakeUploadFile)($request); } - $file = ($this->uploadFiles)($request); + try { + $file = ($this->uploadFiles)($request); - return response(new FileResource($file), 201); + return response(new FileResource($file), 201); + } catch (InvalidUserActionException $e) { + return response([ + 'type' => 'error', + 'message' => $e->getMessage(), + ], 401); + } } } diff --git a/src/Domain/Folders/Actions/CreateFolderAction.php b/src/Domain/Folders/Actions/CreateFolderAction.php index 38944630..5edd2d58 100644 --- a/src/Domain/Folders/Actions/CreateFolderAction.php +++ b/src/Domain/Folders/Actions/CreateFolderAction.php @@ -5,24 +5,35 @@ use Domain\Sharing\Models\Share; use Domain\Folders\Models\Folder; use Illuminate\Support\Facades\Auth; use Domain\Folders\Requests\CreateFolderRequest; +use App\Users\Exceptions\InvalidUserActionException; class CreateFolderAction { /** * Create new folder + * + * @throws InvalidUserActionException */ public function __invoke( CreateFolderRequest $request, ?Share $shared = null, ): Folder|array { + // Get user model + $user = $shared + ? $shared->user + : Auth::user(); + + // Check if user can create folder + if (! $user->canCreateFolder()) { + throw new InvalidUserActionException(); + } + /* - * check if exist parent team folder, if yes, - * then get folder to detect whether it is team_folder + * Check if exist parent team folder, if yes, + * then get the latest parent folder to detect whether it is team_folder */ if ($request->has('parent_id')) { - $isTeamFolder = Folder::find( - $request->input('parent_id') - ) + $isTeamFolder = Folder::find($request->input('parent_id')) ->getLatestParent() ->team_folder; } @@ -34,7 +45,7 @@ class CreateFolderAction 'color' => $request->input('color') ?? null, 'emoji' => $request->input('emoji') ?? null, 'author' => $shared ? 'visitor' : 'user', - 'user_id' => $shared ? $shared->user_id : Auth::id(), + 'user_id' => $user->id, 'team_folder' => $isTeamFolder ?? false, ]); } diff --git a/src/Domain/Folders/Controllers/CreateFolderController.php b/src/Domain/Folders/Controllers/CreateFolderController.php index 17a675ca..34374fda 100644 --- a/src/Domain/Folders/Controllers/CreateFolderController.php +++ b/src/Domain/Folders/Controllers/CreateFolderController.php @@ -7,6 +7,7 @@ use Domain\Folders\Resources\FolderResource; use Domain\Folders\Actions\CreateFolderAction; use Domain\Folders\Requests\CreateFolderRequest; use Support\Demo\Actions\FakeCreateFolderAction; +use App\Users\Exceptions\InvalidUserActionException; class CreateFolderController extends Controller { @@ -28,8 +29,17 @@ class CreateFolderController extends Controller return response(new FolderResource($fakeFolder), 201); } - $folder = ($this->createFolder)($request); + try { + // Create new folder + $folder = ($this->createFolder)($request); - return response(new FolderResource($folder), 201); + // Return new folder + return response(new FolderResource($folder), 201); + } catch (InvalidUserActionException $e) { + return response([ + 'type' => 'error', + 'message' => $e->getMessage(), + ], 401); + } } } diff --git a/src/Domain/Folders/Controllers/VisitorCreateFolderController.php b/src/Domain/Folders/Controllers/VisitorCreateFolderController.php index 75ea94d9..d4dee71f 100644 --- a/src/Domain/Folders/Controllers/VisitorCreateFolderController.php +++ b/src/Domain/Folders/Controllers/VisitorCreateFolderController.php @@ -4,9 +4,11 @@ namespace Domain\Folders\Controllers; use Illuminate\Http\Response; use Domain\Sharing\Models\Share; use App\Http\Controllers\Controller; +use Domain\Folders\Resources\FolderResource; use Domain\Folders\Actions\CreateFolderAction; use Domain\Folders\Requests\CreateFolderRequest; use Support\Demo\Actions\FakeCreateFolderAction; +use App\Users\Exceptions\InvalidUserActionException; use Domain\Sharing\Actions\ProtectShareRecordAction; use Domain\Sharing\Actions\VerifyAccessToItemAction; @@ -19,7 +21,7 @@ class VisitorCreateFolderController extends Controller private CreateFolderAction $createFolder, private ProtectShareRecordAction $protectShareRecord, private VerifyAccessToItemAction $verifyAccessToItem, - private FakeCreateFolderAction $fakeCreateFolderAction, + private FakeCreateFolderAction $fakeCreateFolder, ) { } @@ -28,7 +30,9 @@ class VisitorCreateFolderController extends Controller Share $shared, ): Response | array { if (is_demo_account()) { - return ($this->fakeCreateFolderAction)($request); + $fakeFolder = ($this->fakeCreateFolder)($request); + + return response(new FolderResource($fakeFolder), 201); } // Check ability to access protected share record @@ -42,9 +46,18 @@ class VisitorCreateFolderController extends Controller // Check access to requested directory ($this->verifyAccessToItem)($request->parent_id, $shared); - // Create folder - $folder = ($this->createFolder)($request, $shared); + try { + // Create new folder + $folder = ($this->createFolder)($request, $shared); - return response($folder, 201); + // Return new folder + return response(new FolderResource($folder), 201); + } catch (InvalidUserActionException $e) { + // Return error response + return response([ + 'type' => 'error', + 'message' => $e->getMessage(), + ], 401); + } } } diff --git a/tests/App/Limitations/DefaultLimitationTest.php b/tests/App/Limitations/DefaultLimitationTest.php index 74e3113d..69461224 100644 --- a/tests/App/Limitations/DefaultLimitationTest.php +++ b/tests/App/Limitations/DefaultLimitationTest.php @@ -85,4 +85,24 @@ class DefaultLimitationTest extends TestCase $this->assertEquals(1, $user->limitations->max_storage_amount); $this->assertEquals(false, $user->canUpload(999999999)); } + + /** + * @test + */ + public function it_can_create_new_folder() + { + $user = User::factory() + ->create(); + + $this + ->actingAs($user) + ->postJson('/api/create-folder', [ + 'name' => 'New Folder', + ]) + ->assertStatus(201); + + $this->assertDatabaseHas('folders', [ + 'name' => 'New Folder', + ]); + } } diff --git a/tests/App/Limitations/FixedBillingLimitationTest.php b/tests/App/Limitations/FixedBillingLimitationTest.php index e4024042..e6903b38 100644 --- a/tests/App/Limitations/FixedBillingLimitationTest.php +++ b/tests/App/Limitations/FixedBillingLimitationTest.php @@ -46,4 +46,24 @@ class FixedBillingLimitationTest extends TestCase $this->assertEquals(false, $user->canUpload(999999999)); } + + /** + * @test + */ + public function it_can_create_new_folder() + { + $user = User::factory() + ->create(); + + $this + ->actingAs($user) + ->postJson('/api/create-folder', [ + 'name' => 'New Folder', + ]) + ->assertStatus(201); + + $this->assertDatabaseHas('folders', [ + 'name' => 'New Folder', + ]); + } } diff --git a/tests/App/Limitations/MeteredBillingLimitationTest.php b/tests/App/Limitations/MeteredBillingLimitationTest.php index a7826217..9af044d0 100644 --- a/tests/App/Limitations/MeteredBillingLimitationTest.php +++ b/tests/App/Limitations/MeteredBillingLimitationTest.php @@ -40,4 +40,45 @@ class MeteredBillingLimitationTest extends TestCase $this->assertEquals(false, $user->canUpload()); } + + /** + * @test + */ + public function it_can_create_new_folder() + { + $user = User::factory() + ->create(); + + $this + ->actingAs($user) + ->postJson('/api/create-folder', [ + 'name' => 'New Folder', + ]) + ->assertStatus(201); + + $this->assertDatabaseHas('folders', [ + 'name' => 'New Folder', + ]); + } + + /** + * @test + */ + public function it_cant_create_new_folder_because_user_has_3_failed_payments() + { + $user = User::factory() + ->hasFailedpayments(3) + ->create(); + + $this + ->actingAs($user) + ->postJson('/api/create-folder', [ + 'name' => 'New Folder', + ]) + ->assertStatus(401); + + $this->assertDatabaseMissing('folders', [ + 'name' => 'New Folder', + ]); + } } diff --git a/tests/Domain/Files/FileTest.php b/tests/Domain/Files/FileTest.php index 66030bee..e24aefc3 100644 --- a/tests/Domain/Files/FileTest.php +++ b/tests/Domain/Files/FileTest.php @@ -126,7 +126,7 @@ class FileTest extends TestCase 'file' => $file, 'parent_id' => null, 'is_last' => 'true', - ])->assertStatus(423); + ])->assertStatus(401); Storage::disk('local')->assertMissing( "files/$user->id/fake-file.jpeg"