Compare commits

...

305 Commits
v1.4.2 ... v1.8

Author SHA1 Message Date
Peter Papp
26e0535407 - frontend build 2020-12-21 18:22:22 +01:00
Peter Papp
0f8a89bbad Merge remote-tracking branch 'origin/master'
# Conflicts:
#	public/chunks/app-others.js
#	public/js/main.js
#	public/mix-manifest.json
#	resources/js/components/FilesView/FileItemList.vue
#	resources/js/components/FilesView/FilePreview.vue
#	resources/js/helpers.js
#	resources/js/store/modules/fileFunctions.js
2020-12-21 18:20:10 +01:00
Peter Papp
2f89f6303a Merge branch 'upload-limit'
# Conflicts:
#	app/Http/Helpers/helpers.php
#	config/vuefilemanager.php
#	public/chunks/app-others.js
#	public/chunks/files~chunks/shared-files~chunks/shared-page~chunks/trash.js
#	public/js/main.js
#	public/mix-manifest.json
#	resources/js/components/FilesView/FileItemList.vue
#	resources/js/components/FilesView/FilePreview.vue
#	resources/js/helpers.js
#	resources/js/store/modules/fileFunctions.js
2020-12-21 18:19:37 +01:00
Peter Papp
4b3ca7e34b Merge remote-tracking branch 'origin/bulk-operations'
# Conflicts:
#	app/Http/Helpers/helpers.php
#	public/chunks/app-others.js
#	public/js/main.js
#	public/mix-manifest.json
#	resources/js/components/FilesView/FileItemList.vue
#	resources/js/components/FilesView/FilePreview.vue
#	resources/js/helpers.js
#	resources/js/store/modules/fileFunctions.js
2020-12-21 17:17:10 +01:00
Peter Papp
2c0cf43802 - frontend build
- merge upload limit
2020-12-21 17:15:12 +01:00
Peter Papp
d255b01c70 Merge branch 'upload-limit' into bulk-operations
# Conflicts:
#	app/Http/Helpers/helpers.php
#	config/vuefilemanager.php
#	public/chunks/app-others.js
#	public/chunks/files~chunks/shared-files~chunks/shared-page~chunks/trash.js
#	public/js/main.js
#	public/mix-manifest.json
#	resources/js/components/FilesView/FileItemList.vue
#	resources/js/components/FilesView/FilePreview.vue
#	resources/js/helpers.js
#	resources/js/store/modules/fileFunctions.js
2020-12-21 17:14:21 +01:00
Peter Papp
6a9edc49c0 Merge remote-tracking branch 'origin/bulk-operations'
# Conflicts:
#	app/Http/Helpers/helpers.php
#	config/vuefilemanager.php
#	public/chunks/app-others.js
#	public/chunks/files~chunks/shared-files~chunks/shared-page~chunks/trash.js
#	public/js/main.js
#	public/mix-manifest.json
#	resources/js/components/FilesView/FileItemList.vue
#	resources/js/components/FilesView/FilePreview.vue
#	resources/js/helpers.js
#	resources/js/store/modules/fileFunctions.js
2020-12-21 17:02:49 +01:00
Peter Papp
96faf2de0e - frontend build
- MobileActionButton enhancement
2020-12-21 17:00:41 +01:00
Peter Papp
5a2e6a5e53 - frontend build 2020-12-21 11:46:11 +01:00
Milos Holba
c509eeceb1 merge local with remote changes (Peter) 2020-12-21 11:31:50 +01:00
Milos Holba
299292a817 add actionButtons for mobile multiSelecting, refactoring sortingAndPreview icon 2020-12-21 11:23:18 +01:00
Peter Papp
b11bd0e521 - checkbox restyle in light mode 2020-12-21 11:05:12 +01:00
Peter Papp
8fb859b441 - checkbox restyle in dark mode 2020-12-21 10:57:07 +01:00
Peter Papp
28a5f58b80 - MobileAction buttons animation
- build
2020-12-21 10:47:06 +01:00
Peter Papp
7da4d1f64b - .multiselect-actions as position:fixed
- zipping popup when generate zip for multiple files download
2020-12-21 09:56:03 +01:00
Peter Papp
07086c7550 - turnOff delete in MobileActions.vue 2020-12-20 22:02:08 +01:00
Peter Papp
5246c47c86 - MobileActions.vue refactoring
- added select all / deselect all buttons to mobile multi select mode
2020-12-20 21:57:24 +01:00
Peter Papp
2975d1555d - frontend build 2020-12-20 19:22:35 +01:00
Milos Holba
bd946be0b0 merge local with remote 2020-12-20 16:53:32 +01:00
Milos Holba
e6ff90f6fc solved issues: FullFilePreview menu , MultiSelected move action ; refactoring contextMenu 2020-12-20 16:41:30 +01:00
Peter Papp
08dc813af9 - fixes 2020-12-19 17:04:52 +01:00
Peter Papp
9098634b99 - upload bar x margin fix 2020-12-19 16:31:14 +01:00
Milos Holba
00213824f1 brench merge 2020-12-19 15:18:58 +01:00
Milos Holba
65624326c7 solved issuis from Bulk Review v3,v4 2020-12-19 15:12:56 +01:00
Peter Papp
e92d9344b9 - App popup for creating folder in mobile version
- bug fixes
2020-12-19 13:47:14 +01:00
Peter Papp
55cf58fffd - local zip file 2020-12-19 11:50:48 +01:00
Peter Papp
a3c43af7ba - local zip file 2020-12-19 11:49:00 +01:00
Peter Papp
a10d394abb - download multiple files in public folder
- frontend build
2020-12-19 11:34:32 +01:00
Milos Holba
b16f22b0eb refactoring sorting and preview component 2020-12-17 12:25:08 +01:00
Milos Holba
5bc66d51be add close sortingAndPreview after click anywhere in application 2020-12-16 12:21:55 +01:00
Peter Papp
7b27fa70d8 - download multiple files in private folder
- sorting context menu changes
- download icon active only if files was selected
2020-12-15 18:05:08 +01:00
Peter Papp
372b3aa407 - frontend build 2020-12-14 18:53:06 +01:00
Peter Papp
bda293bc53 Merge remote-tracking branch 'origin/bulk-operations' into bulk-operations
# Conflicts:
#	public/mix-manifest.json
#	resources/js/components/FilesView/ContextMenu.vue
2020-12-13 17:52:24 +01:00
Peter Papp
11873d06ff - zip implementation for users 2020-12-13 17:49:44 +01:00
Milos Holba
89ea50b893 solved issues bulk review part.1 2020-12-13 16:33:28 +01:00
Peter Papp
874b4bb768 - multiple download test 2020-12-12 17:44:24 +01:00
Peter Papp
1e49f52abe - timeout changed in ContextMenu.vue 2020-12-12 10:44:13 +01:00
Peter Papp
94088e76ec - timeout changed in ContextMenu.vue 2020-12-12 10:38:25 +01:00
Peter Papp
c7abcce729 - code review
- frontend build
2020-12-12 10:31:22 +01:00
Milos Holba
e9d2f4bacc delete sorting icon 2020-12-11 18:26:57 +01:00
Milos Holba
7e7965f98e merge with sorting 2020-12-11 18:25:04 +01:00
Milos Holba
d823ae687b refactor getDataByLocation helper, fix moveItem popup from File info panel 2020-12-11 18:18:09 +01:00
Milos Holba
21a36c0965 add translation for preview and sorting, add sorting for moveItem tree and remove for favourites tree 2020-12-10 18:38:48 +01:00
Milos Holba
c3d162b991 add preview&Sorting menu for mobile version, refacoring sorting URI 2020-12-09 18:24:05 +01:00
Milos Holba
7e325c5101 add sorting for every getData and navigator, Favourites need to be fixed 2020-12-08 19:14:53 +01:00
Milos Holba
59ff07fb01 remove clear fileInfoDetail after load data , add clear fileInfoDetail after open another folder 2020-12-07 19:08:45 +01:00
Milos Holba
2ba92ed3b6 add aplhabet icon 2020-12-07 19:00:50 +01:00
Milos Holba
418c072414 optimalize UI for preview and sorting options 2020-12-07 18:57:40 +01:00
Milos Holba
397fe6b7e4 fixed record_upload for guest ,add clear fileInfoDetail after data change 2020-12-07 16:03:48 +01:00
Milos Holba
97d500028a grid and list view working 2020-12-07 14:23:29 +01:00
Milos Holba
527dfeef9f merge with bulk-operations 2020-12-07 11:32:43 +01:00
Milos Holba
1b3baab691 set timeout between multi download cycles 2020-12-05 18:17:45 +01:00
Milos Holba
5d512f7806 available create folder and upload files at multi selecting 2020-12-05 17:46:22 +01:00
Milos Holba
0da525f692 add the command key for selecting to FileItemGrid 2020-12-05 17:38:08 +01:00
Milos Holba
d076817c9e Merge branch 'bulk-operations' into sorting 2020-12-05 17:25:38 +01:00
Peter Papp
d2af7c1f5e version changed 2020-12-05 17:17:11 +01:00
Peter Papp
ef002f9186 frontend build 2020-12-05 17:15:27 +01:00
Peter Papp
f1006355c4 changed default sorting in navigation tree for move popup action 2020-12-05 17:09:44 +01:00
Milos Holba
ec6087a03c sorting first commit 2020-12-05 17:08:57 +01:00
Peter Papp
2e465f4d03 'Cancel Sharing' edit in language files 2020-12-05 16:56:17 +01:00
Peter Papp
08bf6654fc - Folder tree in navigator now has correct ordering with folder list in file view
- added translation to alert popup
- SCOUT_QUEUE default to true
2020-12-05 11:18:37 +01:00
Peter Papp
77e29852a5 SQLite indexer overload fix
- added jobs migration
- added queue:work to scheduler
- new setup in env.example
- frontend build
2020-12-05 10:22:50 +01:00
Milos Holba
6bc84d4b25 bulk-operations v0.3 fixed DragUI subtitle for single item , add handle for move item to the same parent 2020-12-04 15:47:32 +01:00
Milos Holba
d31e2ab98b bulk-operations v0.3 add multi shared cancel to contextMenu 2020-12-04 13:34:00 +01:00
Milos Holba
ee14c34671 bulk-operations fix desktopToolbar delete 2020-12-04 11:26:15 +01:00
Milos Holba
a8b290692e bulk-operations merge 2020-12-04 11:15:10 +01:00
Milos Holba
441f911560 bulk-operations v0.3 issues solved 2020-12-04 10:56:27 +01:00
Peter Papp
10090b474a - navigator in shared folder page fix
- fixed trash in MobileNavigation.vue
- MobileMultiSelectMenu.vue refactor
- frontend build
2020-12-03 20:19:21 +01:00
Milos Holba
aff2594ef2 bulk-operations fix drop of dragged item 2020-12-02 19:56:54 +01:00
Milos Holba
66aad7c488 merge 2020-12-02 17:09:35 +01:00
Milos Holba
896be9c9c9 bulk-operations change MultiSelect component 2020-12-02 17:06:35 +01:00
Peter Papp
8d1821e05d changed version 2020-12-01 17:55:20 +01:00
Peter Papp
b7014552d2 frontend build 2020-12-01 17:53:25 +01:00
Milos Holba
8561556f5c bulk-operations fixed moveItem popup Three, fixed multiSelect with Shift from bottom to top 2020-11-30 18:49:54 +01:00
Milos Holba
58f7479380 bulk-operations v0.1 add opcaity for draged items 2020-11-29 17:50:59 +01:00
Peter Papp
7b70c81d4d binding key delete 2020-11-29 09:55:58 +01:00
Peter Papp
1981f39f9e traffic fix
drag preview styling
2020-11-29 09:39:26 +01:00
Milos Holba
e86ea3e5c4 bulk-operations v0.1 disable rename if are multiple items selected , add Drag & Drop UI Ghost for share 2020-11-28 18:06:42 +01:00
Milos Holba
8e01b837a2 bulk-operations v0.1 add the Drag & Drop UI Ghost , styled scrollbar 2020-11-28 15:56:12 +01:00
Peter Papp
f33fe84350 traffic id fix 2020-11-28 14:46:59 +01:00
Milos Holba
dbbad817c7 bulk-operations multiSelect items UI v0.1 2020-11-26 12:28:59 +01:00
Milos Holba
0eb0939598 bulk-operations multiSelect items UI v0.1 2020-11-26 12:26:30 +01:00
Peter Papp
65f7855703 I killed that ghost beach! 2020-11-25 20:40:18 +01:00
Peter Papp
45d482d347 Merge remote-tracking branch 'origin/monitor' into bulk-operations
# Conflicts:
#	public/mix-manifest.json
2020-11-24 15:58:43 +01:00
Milos Holba
1654dc8678 bulk-operations draged item multiSelect UI 2020-11-24 11:19:40 +01:00
Peter Papp
cb417ea76d added basic traffic meter for upload/download 2020-11-23 18:29:23 +01:00
Milos Holba
17df4aea35 bulk-operations add the command key for selecting the items 2020-11-22 19:09:10 +01:00
Milos Holba
714429d8f4 bulk-operations merge with the improvements branch 2020-11-22 16:39:58 +01:00
Milos Holba
0df03f1d32 bulk-operations make new MobileMultiSelectMenu component, add multi dwonload 2020-11-22 16:21:51 +01:00
Peter Papp
fefc10afb8 Prevent browser 'Go Back' when you undesirably trying to go to previous page after sign in to your account 2020-11-22 10:30:06 +01:00
Peter Papp
1658627069 updated language files 2020-11-22 09:44:50 +01:00
Peter Papp
16ab540298 Ability to cancel uploading progress via x button 2020-11-22 09:42:44 +01:00
Peter Papp
4e5afa4747 Ability to remove uploaded logo and get back to text logo 2020-11-22 08:56:53 +01:00
Peter Papp
d76ff07bf4 Ability to collapse navigator or favourites widget in File page 2020-11-21 19:56:51 +01:00
Peter Papp
b38333bb64 Trash moved to file page 2020-11-21 18:02:46 +01:00
Peter Papp
f863537190 Integrated rename popup 2020-11-21 17:14:56 +01:00
Peter Papp
a72a672aaf Added navigator to shared page with folders when you have multiple folders within for better user experience
Title name of favourite folder overflowed sidebar when you have long title
Share button in image preview appear when you are in shared public folder
2020-11-21 10:33:27 +01:00
Milos Holba
a3dd817e7a bulk-operations v0.1 adding Drag & Drop for moving items to navigations folders, add folders to favourites 2020-11-20 17:54:39 +01:00
Milos Holba
9d9e07d0fa bulk-operations v0.1 mobileSeleting finished, change FileFullPreview code 2020-11-16 17:57:43 +01:00
Peter Papp
30783d0e5d frontend build 2020-11-16 17:19:59 +01:00
Peter Papp
3a296f0f9c Merge remote-tracking branch 'origin/upload-limit' into upload-limit 2020-11-16 17:17:46 +01:00
Peter Papp
c0d5a771a5 upload-limit branch review 2020-11-16 17:16:38 +01:00
Milos Holba
8fc7272e38 upload-limit v0.3 solved the multiple DB querys,uploadLimit in config 2020-11-16 17:16:38 +01:00
Milos Holba
8b8dc34ba6 upload-limit v0.2 solved uploadLimitFormate issues , add helper for format_megabytes 2020-11-16 17:16:38 +01:00
Milos Holba
966145d384 upload-limit v0.2 2020-11-16 17:16:38 +01:00
Milos Holba
2b4060cb7b upload-limit v0.1 2020-11-16 17:16:38 +01:00
Peter Papp
9455a361dc upload-limit branch review 2020-11-16 17:15:10 +01:00
Milos Holba
26132e23a3 bulk-operations v0.1 ContextMenu Add selecting for mobile version 2020-11-15 20:24:12 +01:00
Milos Holba
5acaadeaae bulk-version v0.1 contextMenu Mobile version selecting 2020-11-15 19:59:12 +01:00
Milos Holba
324f4b1ffa bulk-operations v0.1 setup bulk delete,move items and FileInfoPanel 2020-11-13 19:10:36 +01:00
Milos Holba
0c02952f7a bulk-operations v0.1 add selecting to FileItem List and Grid 2020-11-13 19:05:47 +01:00
Milos Holba
f76a4b37e4 bulk-operations v0.1 2020-11-13 19:03:13 +01:00
Milos Holba
f33f93006e bulk-operations v0.1 user_move, guest_move BE 2020-11-13 19:02:27 +01:00
Milos Holba
382756a6f0 rebase 2020-11-13 19:02:08 +01:00
Milos Holba
559bee6ca2 bulk-operations v0.1 setup bulk delete,move items and FileInfoPanel 2020-11-13 17:56:45 +01:00
Milos Holba
190fc651ae upload-limit v0.3 solved the multiple DB querys,uploadLimit in config 2020-11-12 17:46:23 +01:00
Peter Papp
1c62da4e7c fixed plan description validation in payment request 2020-11-12 17:16:37 +01:00
Milos Holba
8a9d2d7f9a bulk-operations add select for FileItemgrid 2020-11-12 17:12:09 +01:00
Peter Papp
ae3be12934 fixed plan description validation in payment request 2020-11-12 09:46:38 +01:00
Milos Holba
4683e0d084 bulk-operations select items v0.1 2020-11-11 18:03:46 +01:00
Milos Holba
c55d3f5350 upload-limit v0.2 solved uploadLimitFormate issues , add helper for format_megabytes 2020-11-07 15:19:03 +01:00
Milos Holba
7273d60154 upload-limit v0.2 2020-11-06 12:19:31 +01:00
Milos Holba
587daa5ffe upload-limit v0.1 2020-11-04 12:48:15 +01:00
Milos Holba
064e635d35 bulk-operations v0.1 user_move, guest_move BE 2020-11-03 15:36:08 +01:00
Milos Holba
06db522545 bulk-operations v0.1 user_delete,guest_delete BE 2020-11-01 19:52:33 +01:00
Peter Papp
e5335c2472 Merge remote-tracking branch 'origin/exifBlacklist' into exifBlacklist
# Conflicts:
#	public/mix-manifest.json
#	resources/js/helpers.js
#	resources/js/i18n/lang/cn.json
#	resources/js/i18n/lang/en.json
#	resources/js/i18n/lang/sk.json
#	resources/js/store/modules/fileFunctions.js
#	resources/js/views/Admin/AppSettings/AppSettingsTabs/Others.vue
2020-10-24 10:40:59 +02:00
Peter Papp
a2ec066c6d added metadata to UpgradeAppController.php
updated readme with upgrading step to 1.7.11
Frontend build
2020-10-24 10:39:39 +02:00
Miloš Holba
15d4ffc349 Add to metadata Longitude and Latitude 2020-10-24 09:44:23 +02:00
Peter Papp
9af9e45b8a night mode for ImageMetaData.vue 2020-10-24 09:44:23 +02:00
Peter Papp
f30dd38b96 migration column in file_manager_files renamed from meta_data to metadata
context menu feractoring
Editor.php typo changes
FileInfoPanel.vue small changes when to show metadata info
Added new exif information and reordering existing in ImageMetaData.vue
Added new translation strings to i18n files
2020-10-24 09:44:23 +02:00
Miloš Holba
d5f3599b9e v0.1 Add enable empty input in uploadText method 2020-10-24 09:44:23 +02:00
Miloš Holba
de62ab535c v0.1 Fix exif data function just for jpeg and Fix config in index.blade for mimetype_blacklist 2020-10-24 09:44:18 +02:00
Miloš Holba
8b8562916b v0.1 exifBlacklist 2020-10-24 09:44:05 +02:00
Miloš Holba
0d7b920cbe v0.1 add check mimetype if mimetype of file is undefined 2020-10-24 09:44:04 +02:00
Miloš Holba
f3254a380f v0.1 exif Blacklist add frontend validation 2020-10-24 09:44:02 +02:00
Miloš Holba
0e12029efc v0.1 Blacklist and Exif data 2020-10-24 09:43:58 +02:00
Miloš Holba
fd6aa5c6b2 Lazy loading (#41)
* lazy-loading and solved bugs

* lazy-loading for pull request on master

* Update vuefilemanager.php

Co-authored-by: Peter Papp <peterpapp@makingcg.com>
2020-10-24 09:40:05 +02:00
Miloš Holba
5be9db83f7 version change to v1.7.11 2020-10-23 16:14:34 +02:00
Peter Papp
12d6b0bf57 scheduler changed from hourly to every minute 2020-10-13 17:09:05 +02:00
nourisk
961462fe54 Update package.json (#32)
The "vue" and "vue-template[..]" versions not being the same, this has the effect of breaking the "npm run prod" command with error "version missmatch".
For people who have already installed it, just change the version then "npm install" and that should solve the problem.
2020-10-07 08:19:39 +02:00
nourisk
f0e0103d81 Update router.js (#31)
Capital i missing
2020-10-06 17:03:26 +02:00
Miloš Holba
755523e07b Add to metadata Longitude and Latitude 2020-09-25 09:31:31 +02:00
Peter Papp
c4ace0697b version change to v1.7.10.3 2020-09-20 12:09:07 +02:00
Peter Papp
69489c4ee2 - fixed simultaneously action with show and download file on doubleclick
- Changed download name for Backblaze
- Price plan can be defined in 12 digit number
2020-09-20 12:08:29 +02:00
Peter Papp
e05d7468b5 night mode for ImageMetaData.vue 2020-09-20 11:23:50 +02:00
Peter Papp
1a6a91335e migration column in file_manager_files renamed from meta_data to metadata
context menu feractoring
Editor.php typo changes
FileInfoPanel.vue small changes when to show metadata info
Added new exif information and reordering existing in ImageMetaData.vue
Added new translation strings to i18n files
2020-09-20 11:17:34 +02:00
Miloš Holba
80de069c7b v0.1 Add enable empty input in uploadText method 2020-09-15 13:20:39 +02:00
Miloš Holba
2de8bbe548 v0.1 Fix exif data function just for jpeg and Fix config in index.blade for mimetype_blacklist 2020-09-15 10:33:39 +02:00
Peter Papp
7ba2a3bec1 fixed backblaze broken download link when filename contain space (Content Disposition header wasn't read properly with space) 2020-09-14 09:53:38 +02:00
Miloš Holba
89fa2fce23 v0.1 exifBlacklist 2020-09-12 11:15:32 +02:00
Miloš Holba
84b47416d6 v0.1 add check mimetype if mimetype of file is undefined 2020-09-06 20:29:16 +02:00
Miloš Holba
55c72cddba v0.1 exif Blacklist add frontend validation 2020-09-05 18:35:27 +02:00
Miloš Holba
6effd4a7d9 v0.1 Blacklist and Exif data 2020-09-05 14:35:51 +02:00
Peter Papp
95f6695d00 Fixed when default value incorrectly showing in switches in app settings 2020-09-01 07:41:05 +02:00
Peter Papp
fde7f0f73b Fixed when default value incorrectly showing in switches in app settings 2020-09-01 07:10:56 +02:00
Peter Papp
dd1bffec09 typo error 2020-08-31 08:38:40 +02:00
Peter Papp
83a951b3af cache bursting support in webpack.mix.js 2020-08-31 08:22:51 +02:00
Peter Papp
6762ed25dc frontend build 2020-08-30 15:33:52 +02:00
Peter Papp
6ba869234e FileFullPreview functionality in file grid view 2020-08-30 15:33:20 +02:00
Peter Papp
2c04376a61 v1.7.10 2020-08-30 11:18:28 +02:00
Peter Papp
f180f1fff8 expand tap area for FilePreviewActions.vue arrows 2020-08-30 11:15:51 +02:00
Peter Papp
ab65ca7a13 hide subscription, payment cards and invoices in profile menu when is 'Allow subscription payment' option disabled
frontend build
2020-08-30 11:05:08 +02:00
Peter Papp
8895b5062a small design improves 2020-08-30 09:01:00 +02:00
Peter Papp
2f4aafb1b3 frontend build 2020-08-28 16:17:53 +02:00
Peter Papp
be08c8487a Merge remote-tracking branch 'origin/master' into filepreview
# Conflicts:
#	.env.example
#	package-lock.json
#	package.json
#	public/js/main.js
#	public/mix-manifest.json
#	resources/js/App.vue
#	resources/js/helpers.js
#	resources/js/i18n/lang/en.json
#	resources/js/i18n/lang/sk.json
#	resources/js/store/modules/fileFunctions.js
#	resources/js/views/Shared/SharedPage.vue
2020-08-28 16:14:28 +02:00
Peter Papp
0d5df91d2d frontend build 2020-08-27 14:48:48 +02:00
Peter Papp
72e4067beb logo fix in shared page
Renamed option title in share popup
2020-08-27 14:46:07 +02:00
Peter Papp
ba4f888826 Updated README.md and dark mode logo in Dashboard.vue page 2020-08-27 08:25:08 +02:00
Miloš Holba
82b43eb996 FilePreview solved v0.3 issues 2020-08-26 22:37:33 +02:00
Peter Papp
777132ec40 dark mode expiration buttons fix 2020-08-26 18:30:10 +02:00
Peter Papp
7f6f60227a Ability to set expiration for shared link
If user is logged in, after visit SignIn page will be redirected to files page
2020-08-26 08:17:49 +02:00
Peter Papp
ab6ff5dbfd force migration
added maintenance mode
2020-08-26 07:51:16 +02:00
Peter Papp
0d272bc9b7 version change 2020-08-26 07:30:48 +02:00
Peter Papp
2b08d7801b Shared link expiration backend
Redirect from sign in page when user is logged
Updated README.md
2020-08-26 07:29:28 +02:00
Peter Papp
cec4ff6cda shared link title fix 2020-08-25 15:33:16 +02:00
Peter Papp
6f300ba1d5 Link expiration frontend 2020-08-25 15:25:18 +02:00
Peter Papp
86813629ed updated readme 2020-08-25 12:53:46 +02:00
Peter Papp
73a728e606 change vuefilemanager version 2020-08-25 12:37:09 +02:00
Peter Papp
af1228e363 reading metadata in shared link RC 2020-08-25 12:36:21 +02:00
Peter Papp
f3a2758bcc reading metadata in shared link v0.1 2020-08-25 12:04:13 +02:00
Peter Papp
6f9f2f2d34 reading metadata in shared link 2020-08-25 11:03:53 +02:00
Peter Papp
9f4c21a1b1 UserSubscription page fix
PaymentMethods page fix
2020-08-24 09:30:38 +02:00
Peter Papp
88315e4a91 fixed paginated button 2020-08-24 07:20:45 +02:00
Peter Papp
25bb186c89 updated readme 2020-08-24 07:17:11 +02:00
Peter Papp
cca832a1c1 backend pagination and sorting from laravel database release 2020-08-24 06:35:42 +02:00
Miloš Holba
aac0aa755f fix arrow function when is just one file in MediaPreview 2020-08-22 17:55:03 +02:00
Miloš Holba
4be77c07ac remove addEventListener change the video sizing wrapper 2020-08-22 14:18:57 +02:00
Miloš Holba
9372906a3e open to click at audio for mobile 2020-08-22 12:19:23 +02:00
Miloš Holba
e12e521622 add audio mimetypes : mp4, flac 2020-08-21 17:32:36 +02:00
Peter Papp
6dd0b4f026 backend pagination and sorting from laravel database 2020-08-21 16:04:21 +02:00
Miloš Holba
0082c3a6a8 change arrows position 2020-08-20 20:31:47 +02:00
Miloš Holba
f79973e922 Fix issues from FilePreview v0.2 2020-08-20 18:50:07 +02:00
Peter Papp
3e6e046dfc folder delete fix 2020-08-19 06:10:07 +02:00
Miloš Holba
59ca45e9b1 update filePreview ContextMenu 2020-08-18 19:22:54 +02:00
Peter Papp
5f5caae9e7 updated language files, cache clear button loading 2020-08-18 15:12:34 +02:00
Peter Papp
93a7542502 - Moved chunk size option to .env
- Added ability into App Settings to flush application cache
- Renew password page logo fix
- removed put|patch|delete methods from axios and replaced by faking these methods to support incompatible shared hostings where you can't install php extension to support these methods.
- Getting unique_ids fix
2020-08-18 10:21:24 +02:00
Miloš Holba
bb22ec1e88 key binds fix 2020-08-17 20:10:30 +02:00
Peter Papp
5c2326e492 fixed non-vuefilemanager stripe plans loading 2020-08-17 10:04:47 +02:00
Peter Papp
47faadef51 strip non-asci characters from filename 2020-08-17 08:42:22 +02:00
Peter Papp
aa26501100 - added support for custom local storage path 2020-08-17 07:38:58 +02:00
Peter Papp
d333d53e9c - added eu-central to setup wizard for backblaze regions 2020-08-17 05:55:50 +02:00
Miloš Holba
ba315014fa split code to components, add arrow functions 2020-08-15 17:41:46 +02:00
Peter Papp
4a9f040c32 - added chunks 2020-08-14 10:22:41 +02:00
Peter Papp
13de58fdbd - relative chunks path 2020-08-14 10:21:57 +02:00
Peter Papp
40111b95c1 - frontend build with included chunks 2020-08-14 10:13:57 +02:00
Peter Papp
f03c6edec1 - code splitting with vue router 2020-08-14 10:02:24 +02:00
Peter Papp
840898ec9a - added region list to setup wizard
- dark mode disabled input fix
- fixed deprecated scopes
- updated screenshots for SaaS version
2020-08-14 08:39:11 +02:00
Peter Papp
a906a98cce frontend build 2020-08-13 10:58:43 +02:00
Peter Papp
962b76dd8b fix email setup 2020-08-13 10:57:29 +02:00
Peter Papp
23bbae8a74 meta description fix 2020-08-11 08:46:03 +02:00
Miloš Holba
8387f56048 Move item from gallery fix 2020-08-10 10:30:54 +02:00
Miloš Holba
68acf5f986 Add Gallery 2020-08-08 21:53:43 +02:00
Miloš Holba
785dade6b2 Add Gallery 2020-08-08 21:46:56 +02:00
carodej
8e93a92fcc updated readme 2020-08-07 16:33:30 +02:00
carodej
4393fc6f12 clear kernel 2020-08-07 10:57:04 +02:00
carodej
cba38b58c3 removed deprecated commands 2020-08-07 10:55:32 +02:00
carodej
1ff43b3557 added htaccess to redirect domain root to /public folder 2020-08-07 09:38:22 +02:00
carodej
bfc2253f4d set version 2020-08-05 16:54:26 +02:00
carodej
fb790200d4 bug fixes 2020-08-05 16:51:44 +02:00
carodej
fc2c4de50f additional text logo fix 2020-07-31 08:37:21 +02:00
carodej
a947882449 Merge branch 'master' of github.com:MakingCG/vue-filemanager-laravel
* 'master' of github.com:MakingCG/vue-filemanager-laravel:
  Update README.md
2020-07-30 09:01:34 +02:00
carodej
192b843a85 Merge branch 'dev'
* dev:
  v1.7.3 rc.1
  chunk upload and multipart upload beta.2
  chunk upload and multipart upload beta.1
  multipart upload build
  multipart upload
  chunk upload
  chunk upload
2020-07-30 08:58:19 +02:00
carodej
cd0627e8b8 v1.7.3 rc.1 2020-07-30 08:47:05 +02:00
carodej
46256f6332 chunk upload and multipart upload beta.2 2020-07-29 16:33:42 +02:00
Peter Papp
9935db4bb1 Update README.md 2020-07-29 16:33:05 +02:00
carodej
18761eb5b3 chunk upload and multipart upload beta.1 2020-07-29 12:29:30 +02:00
carodej
bf8db1be52 multipart upload build 2020-07-28 17:01:14 +02:00
carodej
9fea65bd52 multipart upload 2020-07-28 16:57:54 +02:00
carodej
fe8e329fea chunk upload 2020-07-27 17:40:16 +02:00
carodej
6ac543128e chunk upload 2020-07-27 17:28:55 +02:00
carodej
3a643da14e Updated readme 2020-07-24 09:07:41 +02:00
carodej
8cbc0d65dc download butten centered 2020-07-22 18:29:04 +02:00
carodej
4ac81f1ceb fixed urls when external storage is used 2020-07-22 18:12:26 +02:00
carodej
032f4f357a set smtp as default in setup wizard 2020-07-22 10:13:04 +02:00
carodej
35e1262676 Added some hints to setup wizard, new validation rules and readme update 2020-07-22 09:28:13 +02:00
carodej
5c86f4dbb3 SignUp button fixed and updated readme 2020-07-21 19:28:47 +02:00
carodej
a32b87334f Merge branch 'master' of github.com:MakingCG/vue-filemanager-laravel
* 'master' of github.com:MakingCG/vue-filemanager-laravel:
  Allow S3 compatible systems
2020-07-21 09:44:42 +02:00
carodej
4858d82ce3 Merge branch 'dev'
* dev: (38 commits)
  v1.7 RC.2
  v1.7 RC.1
  v1.7 beta.8
  README updated
  v1.7 beta.7
  v1.7 beta.6
  v1.7 beta.ť
  v1.7 beta.4
  v1.7 beta.3
  v1.7 beta.2
  v1.7 beta.1
  v1.7 alpha.4
  v1.7 alpha.3 build
  v1.7 alpha.3
  v1.7 alpha.2 build
  v1.7 alpha.2
  alpha v1.7 alpha.1
  bugfixes
  index options added
  translations update
  ...
2020-07-21 09:44:23 +02:00
carodej
6f6115d5cb v1.7 RC.2 2020-07-21 09:27:57 +02:00
carodej
b67297f160 v1.7 RC.1 2020-07-20 16:03:39 +02:00
carodej
6f95fc2565 v1.7 beta.8 2020-07-20 08:54:57 +02:00
carodej
827b34e1dd README updated 2020-07-19 17:27:24 +02:00
carodej
35543e6f06 v1.7 beta.7 2020-07-18 16:25:29 +02:00
carodej
6c96fe2f46 v1.7 beta.6 2020-07-18 09:15:19 +02:00
carodej
9ce64b2d58 v1.7 beta.ť 2020-07-17 08:39:01 +02:00
carodej
caa586ceb4 v1.7 beta.4 2020-07-17 08:34:28 +02:00
carodej
59b6dfb841 v1.7 beta.3 2020-07-16 16:51:27 +02:00
carodej
abe7f0dafd v1.7 beta.2 2020-07-16 11:02:58 +02:00
carodej
eeada5468b v1.7 beta.1 2020-07-16 10:26:41 +02:00
carodej
6b36480097 v1.7 alpha.4 2020-07-15 11:50:32 +02:00
carodej
25d15390ef v1.7 alpha.3 build 2020-07-15 09:40:15 +02:00
carodej
7ff7decbb6 v1.7 alpha.3 2020-07-15 09:36:38 +02:00
carodej
4cbdea4143 v1.7 alpha.2 build 2020-07-15 08:47:08 +02:00
carodej
38da639e26 v1.7 alpha.2 2020-07-15 08:34:52 +02:00
carodej
b1860eac21 alpha v1.7 alpha.1 2020-07-14 17:31:04 +02:00
carodej
c9d300769c bugfixes 2020-07-14 10:34:42 +02:00
carodej
2ae60003d6 index options added 2020-07-13 09:49:25 +02:00
carodej
a74c1c7b6e translations update 2020-07-12 11:02:21 +02:00
carodej
3c3a18cf6b translations update 2020-07-10 17:05:30 +02:00
carodej
5bd6455f7f responsivity update 2020-07-09 16:52:00 +02:00
carodej
5a9f5813c8 dark mode update 2020-07-09 10:56:17 +02:00
carodej
a43f0e6908 landing page 2020-07-08 09:07:11 +02:00
carodej
5a5125967f dashboard include 2020-07-05 09:14:17 +02:00
carodej
e1ebb70035 frontend update 2020-07-03 08:34:10 +02:00
carodej
05bc598405 frontend update 2020-07-02 18:04:10 +02:00
carodej
2764fd6dd5 setup wizard update 2020-07-02 12:31:14 +02:00
carodej
a98625876d Setup wizard update 2020-07-01 11:01:54 +02:00
carodej
aedc98cc8b setup wizard init 2020-06-29 10:09:42 +02:00
carodej
a2dfc627a7 frontend & backend update 2020-06-22 16:46:02 +02:00
carodej
a2cab6198e frontend & backend update 2020-06-19 08:03:29 +02:00
Carlos Ferreira
766368bace Allow S3 compatible systems
By specifying endpoint, one can use S3 compatible systems like Backblaze or Wasabi.
2020-06-12 15:00:22 +02:00
carodej
fee2cef980 fileview xscroller fix 2020-06-09 18:10:27 +02:00
carodej
95bc310def backend update 2020-06-09 18:06:04 +02:00
carodej
0b7bc27a5f Merge branch 'svg-fix' into dev
* svg-fix:
  fix with svg and scrolling
  v1.6.2 released
  v1.6.1
2020-06-05 17:39:00 +02:00
Peter Papp
bc7950b245 Merge pull request #14 from MakingCG/svg-fix
fix with svg and scrolling
2020-06-05 17:29:06 +02:00
carodej
321bac6c9f fix with svg and scrolling 2020-06-05 17:26:41 +02:00
carodej
cffdc3ced9 frontend/backend update 2020-06-05 16:48:11 +02:00
carodej
ca14838212 frontend update 2020-06-03 10:58:44 +02:00
carodej
331ee52ea3 frontend update 2020-06-02 07:13:44 +02:00
carodej
16c3625b0b frontend fix 2020-05-31 19:54:42 +02:00
carodej
6392ce1727 v1.6.2 released 2020-05-31 19:48:22 +02:00
carodej
181f090901 v1.6.1 2020-05-31 10:04:49 +02:00
carodej
61a8849e2d SaaS frontend update 2020-05-29 17:37:23 +02:00
carodej
252b6fd0bf v1.6 released 2020-05-28 13:00:54 +02:00
carodej
a76d1dec3b user management v1.6-alpha.1 2020-05-27 10:22:33 +02:00
carodej
143aca64dc Merge branch 'dev'
* dev:
  v1.5.2
2020-05-22 10:22:23 +02:00
carodej
3dc3f37cf6 v1.5.2 2020-05-22 10:20:55 +02:00
carodej
deff8d8741 Merge branch 'dev'
* dev:
  v1.5.1-beta.2
  v1.5.1-beta.1
2020-05-22 09:59:05 +02:00
carodej
beae4277ca v1.5.1-beta.2 2020-05-22 09:48:26 +02:00
carodej
be7d1bdc73 v1.5.1-beta.1 2020-05-22 09:40:34 +02:00
carodej
2eaf399441 Merge branch 'dev'
* dev:
  v1.5-beta.3
  v1.5-beta.2
  v1.5-beta.1
  v1.5-alpha.11
  v1.5-alpha.10
  v1.5-alpha.9
  v1.5-alpha.8
  v1.5-alpha.7
  v1.5-alpha.6
  v1.5-alpha.5
  v1.5-alpha.4
  v1.5-alpha.3
  v1.5-alpha.2
  v1.5-alpha.1
2020-05-19 17:52:07 +02:00
carodej
65f902fbcf v1.5-beta.3 2020-05-19 17:49:49 +02:00
carodej
5df0fa93b3 v1.5-beta.2 2020-05-19 11:27:47 +02:00
carodej
67b9416f64 v1.5-beta.1 2020-05-19 09:52:04 +02:00
carodej
8255597fd5 v1.5-alpha.11 2020-05-19 08:29:34 +02:00
carodej
633bef7660 v1.5-alpha.10 2020-05-18 12:37:33 +02:00
carodej
dfe4991177 v1.5-alpha.9 2020-05-18 09:32:34 +02:00
carodej
8daa05f710 v1.5-alpha.8 2020-05-16 17:52:24 +02:00
carodej
62434bcedb v1.5-alpha.7 2020-05-16 17:35:50 +02:00
carodej
6272f62e85 v1.5-alpha.6 2020-05-16 17:22:45 +02:00
carodej
4c8028696f v1.5-alpha.5 2020-05-16 17:12:38 +02:00
carodej
355f6a96ff v1.5-alpha.4 2020-05-16 12:18:37 +02:00
carodej
bdcfc26af7 v1.5-alpha.3 2020-05-16 12:11:17 +02:00
carodej
d2c4f2aa23 v1.5-alpha.2 2020-05-16 12:04:28 +02:00
carodej
41656235fc v1.5-alpha.1 2020-05-15 17:31:25 +02:00
458 changed files with 58368 additions and 12582 deletions

View File

@@ -1,12 +1,17 @@
APP_NAME=Laravel
APP_ENV=local
APP_KEY=
APP_NAME=VueFileManager
APP_ENV=production
APP_KEY=base64:sB1YuKsbWv7MdWugb9ZsYBqv2QZJ+QOuHZHEddOsUuo=
APP_DEBUG=true
APP_URL=http://localhost
APP_DEMO=false
LOG_CHANNEL=stack
SCOUT_DRIVER=tntsearch
FILESYSTEM_DRIVER=local
SCOUT_QUEUE=true
FILESYSTEM_DRIVER=
CHUNK_SIZE=128
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
@@ -17,7 +22,7 @@ DB_PASSWORD=
BROADCAST_DRIVER=log
CACHE_DRIVER=file
QUEUE_CONNECTION=sync
QUEUE_CONNECTION=database
SESSION_DRIVER=file
SESSION_LIFETIME=120
@@ -25,18 +30,18 @@ REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
MAIL_DRIVER=smtp
MAIL_HOST=smtp.mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS=null
MAIL_FROM_NAME="${APP_NAME}"
MAIL_DRIVER=
MAIL_HOST=
MAIL_PORT=
MAIL_USERNAME=
MAIL_PASSWORD=
MAIL_ENCRYPTION=
MAIL_FROM_ADDRESS="${MAIL_USERNAME}"
MAIL_FROM_NAME="${MAIL_USERNAME}"
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_DEFAULT_REGION=us-east-1
AWS_DEFAULT_REGION=
AWS_BUCKET=
DO_SPACES_KEY=
@@ -45,13 +50,26 @@ DO_SPACES_ENDPOINT=
DO_SPACES_REGION=
DO_SPACES_BUCKET=
PUSHER_APP_ID=
PUSHER_APP_KEY=
PUSHER_APP_SECRET=
PUSHER_APP_CLUSTER=mt1
WASABI_KEY=
WASABI_SECRET=
WASABI_ENDPOINT=
WASABI_REGION=
WASABI_BUCKET=
MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
BACKBLAZE_KEY=
BACKBLAZE_SECRET=
BACKBLAZE_ENDPOINT=
BACKBLAZE_REGION=
BACKBLAZE_BUCKET=
PASSPORT_CLIENT_ID=
PASSPORT_CLIENT_SECRET=
APP_DEPLOY_SECRET=
CASHIER_LOGGER=stack
CASHIER_CURRENCY=
STRIPE_KEY=
STRIPE_SECRET=
STRIPE_WEBHOOK_SECRET=
CASHIER_PAYMENT_NOTIFICATION=App\Notifications\ConfirmPayment

7
.gitignore vendored
View File

@@ -1,14 +1,21 @@
/app/Console/Commands/SetupDevelopmentEnvironment.php
/node_modules
/public/hot
/public/storage
/storage/*.key
/storage/*.index
/storage/framework/cache/*
/vendor
/resources/babel.babel
.idea
.env
.env.backup
.phpunit.result.cache
.phpstorm.meta.php
.vscode/
_ide_helper.php
Homestead.json
Homestead.yaml
npm-debug.log
yarn-error.log
prettier.json

3
.htaccess Normal file
View File

@@ -0,0 +1,3 @@
RewriteEngine on
RewriteCond %{REQUEST_URI} !^public
RewriteRule ^(.*)$ public/$1 [L]

File diff suppressed because it is too large Load Diff

BIN
.rnd

Binary file not shown.

380
README.md
View File

@@ -1,48 +1,380 @@
### Documentation
[Read online documentation](https://vuefilemanager.com/docs/)
![logo](https://vuefilemanager.com/assets/images/vuefilemanager-horizontal-logo.svg)
# Private Cloud Storage Build on Laravel & Vue.js
### Installation setup
## Contents
Run these commands to install vendors:
- [Installation](#installation)
- [Server Requirements](#server-requirements)
- [Installation](#installation)
- [PHP Configuration](#php-configuration)
- [Chunk Upload](#chunk-upload)
- [Upgrade Guide](#upgrade-guide)
- [Common Instructions](#common-instructions)
- [Update from 1.7.12 to 1.8](#update-from-1712-to-178)
- [Update from 1.7.10 to 1.7.11](#update-from-1710-to-1711)
- [Update from 1.7.8 to 1.7.9](#update-from-178-to-179)
- [Update from 1.7.x to 1.7.8](#update-from-17x-to-178)
- [Update from 1.6.x to 1.7](#update-from-16x-to-17)
- [Nginx Configuration](#nginx-configuration)
- [Apache Configuration](#apache-configuration)
- [Payments](#payments)
- [Get your active plans](#get-your-active-plans)
- [Manage Failed Payments](#manage-failed-payments)
- [Tax Rates](#tax-rates)
- [Developers](#developers)
- [Running development environment on your localhost](#running-development-environment-on-your-localhost)
- [Supported Storages](#supported-storages)
- [How to Create New Language](#how-to-create-new-language)
- [Others](#others)
- [Changelog](#changelog)
- [GitHub Repository](#github-repository)
- [Support](#support)
- [Security Vulnerabilities](#security-vulnerabilities)
# Installation
## Server Requirements
**For running app make sure you have installed:**
- PHP >= 7.2.5 version
- MySQL 5.6+
- Nginx or Apache
**These PHP Extensions are required:**
- GD
- BCMath
- PDO
- SQLite
- Ctype
- Fileinfo
- JSON
- Mbstring
- OpenSSL
- Tokenizer
- XML
- Exif
## Installation
#### 1. Upload files on your server
Upload project files to web root folder of your domain. It's mostly located in `html`, `www` or `public_html` folder name.
#### 2. Configure your web root folder
Configure your web server's document root to point to the public directory of the files you previously uploaded. For example, if you've uploaded the files in `html` folder, your domain root directory should be changed to `html/project_files/public` folder or anything else where domain root is in project `/public` directory.
Please don't try go to `yourdomain.com/public` URL address, you will have issue to verify your purchase code, this is not correct domain root setup, you must do this in your webhosting settings panel.
![Domain Root](https://vuefilemanager.com/assets/images/domain-root.jpg)
#### 3. Check your .env file
Make sure `.env` file was uploaded. This type of file can be hidden in default.
#### 3.1 When you install from GitHub
When you download repository from GitHub, you have to rename your `.env.example` file to `.env`. Then run command below in your terminal to install vendors. Composer is required.
```
composer install
```
#### 4. Set write permissions
Set `755` permission (CHMOD) to these file and folders directory within all children subdirectories:
- /bootstrap/cache
- /storage
- /.env
#### 5. Open your application in your web browser
Then open your application in web browser. If everything works fine, you will be redirect to setup wizard installation process.
At first step you have to verify your purchase code. **Subscription service with stripe payments is available only for Extended License.** If you can't verify your purchase code, check, if you did previously steps correctly.
#### 6. Follow setup wizard steps
That was the hardest part of installation proces. Please follow instructions in every step of Setup Wizard to successfully install VueFileManager.
#### 7. Set up Cron
Add the following Cron entry to your server. Just update your php path (if it's different) and project path:
```
npm install
* * * * * /usr/local/bin/php /path-to-your-project/artisan schedule:run >> /dev/null 2>&1
```
Setup your database in .env and run this command:
## PHP Configuration
There are several PHP settings good to know to setup before you try upload any file. Please set these values in your php.ini, we provide minimal setup for you. When you set `-1` then you set infinity limits.
```
php artisan setup:prod
memory_limit = 512M
upload_max_filesize = 128M
post_max_size = 128M
max_file_uploads = 50
max_execution_time = 3600
```
It automatically:
* Migrate database
* Generate Application key
* Create Passport Encryption keys
* Create Password grant client
* Create Personal access client
## Chunk & Multipart Upload
VueFileManager in default supporting chunk upload. Default chunk upload size is `128MB`. If you wish change this default value, go to your `.env` and change `CHUNK_SIZE` attribute.
Then, copy generated password grant client `Client ID`, `Client secret` and paste it to .env files here:
```
PASSPORT_CLIENT_ID=<your_passport_client_id>
PASSPORT_CLIENT_SECRET=<your_passport_client_secret>
```
For sending forgoten password request via email, fill your mail driver in .env
When you use external storage, and upload large files, to prevent failing upload process make sure you have enough space in your application space and set higher `max_execution_time` in your php.ini to move your files to external storage.
### Run Application
To start server on your localhost, run this command
## Upgrade Guide
### Common Instructions
`Don't forget create backup of your database before make any changes in your production application. If you serve your files in local storage driver pay attention and don't delete your /storage folder!`
These instructions is applicable for all updates. Please follow this step:
- Just rewrite all project files with new excluded `/.env` file and `/storage` folder. These items must be preserved!
### Update from 1.7.12 to 1.8
- Before upload new files to your hosting, log in to VueFileManager as Admin. After uploading new files on your webhosting, visit this url `your-domain.com/service/upgrade-database` for upgrading your database.
- Just rewrite all project files with new, excluded /.env file and /storage folder. These items must be preserved!
- set **QUEUE_CONNECTION** to **database** in your **.env** file
- Clear cache in your administration panel - Settings / Application / Clear Cache
If you are upgrading from GitHub, don't forget run `composer install` to install new vendors.
### Update from 1.7.10 to 1.7.11
Before upload new files to your hosting, log in to VueFileManager as Admin. After uploading new files on your webhosting, visit this url `your-domain.com/service/upgrade-database` for upgrading your database.
### Update from 1.7.8 to 1.7.9
After rewrited old files with new files, log in as admin to the app and go to `your-domain.com/service/upgrade-database`. This will upgrade your database on the background.
Add the following Cron entry to your server. Just update your php path (if it's different) and project path:
```
* * * * * /usr/local/bin/php /path-to-your-project/artisan schedule:run >> /dev/null 2>&1
```
### Update from 1.7.x to 1.7.8
For those who have installed VueFileManager via git or any other repository synchronization tool, dont't forget after updated code run `composer update` command to update your vendors.
### Update from 1.6.x to 1.7
For those, who purchase extended licence, place these lines at the end of your `/.env` file:
```
CASHIER_LOGGER=stack
CASHIER_CURRENCY=
STRIPE_KEY=
STRIPE_SECRET=
STRIPE_WEBHOOK_SECRET=
CASHIER_PAYMENT_NOTIFICATION=App\Notifications\ConfirmPayment
```
Then go to https://your-domain.com/upgrade and follow the setup wizard instructions.
## Nginx Configuration
If you running VueFileManager undex Nginx, don't forget set this value in your `nginx.conf` file:
```
http {
client_max_body_size 1024M;
}
```
And example Nginx config for your domain:
```
server {
listen 80;
listen [::]:80;
# Log files for Debugging
access_log /var/log/nginx/laravel-access.log;
error_log /var/log/nginx/laravel-error.log;
# Webroot Directory for Laravel project
root /var/www/vuefilemanager/public;
index index.php index.html index.htm;
# Your Domain Name
server_name example.com;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
# PHP-FPM Configuration Nginx
location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass unix:/run/php/php7.3-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
```
## Apache Configuration
Make sure you have enabled mod_rewrite. There is an example config for running VueFileManager under apache:
```
<VirtualHost example.com:80>
DocumentRoot /var/www/vuefilemanager/public
ServerName example.com
<Directory "/var/www/vuefilemanager/public">
AllowOverride All
allow from all
Options +Indexes
Require all granted
</Directory>
RewriteEngine on
RewriteCond %{SERVER_NAME} =example.com
RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent]
</VirtualHost>
```
# Payments
VueFileManager is packed with **Stripe** payment options. To configure Stripe, you will be asked in Setup Wizard to set up. Or, if you skip this installation process, you will find stripe set up in you admin `Dashboard / Settings / Payments`.
## Get your active plans
Would you like to get your subscription plans for your custom front-end page? Create GET request and get all your active plans:
```
GET /api/public/pricing
```
## Manage Failed Payments
VueFileManager manage failed payments with additional email notification. But, there is more you can do for better User Experience. There is some additionals option in Stripe, look on [prevent failed payments](https://dashboard.stripe.com/settings/billing/automatic).
## Tax Rates
You are able to manage tax rates. When adding a new tax rate, if no Region is specified, the tax rate will apply to everyone. Add a [ISO 3166-1 alpha-2 country code](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2#Officially_assigned_code_elements) to the Region field if you wish to apply taxes per country.
Just log in to your stripe dashboard, and you will find taxes under `Dashboard / Products / Tax Rates`.
# Developers
## Running development environment on your localhost
When you download repository from GitHub, you have to rename your `.env.example` file to `.env`. Then run command below in your terminal to install vendors. Composer is required.
```
composer install
```
Set your `APP_ENV` to local mode, in default, it's in production mode.
```
APP_ENV=local
```
Also, to debug application, set `APP_DEBUG` on true:
```
APP_DEBUG=true
```
To start server on your localhost, run command below. Then go to your generated localhost URL by terminal, and follow Setup Wizard steps to configure VueFileManager.
```
php artisan serve
```
To compiles and hot-reloads for development, run this command
After successfully installation via Setup Wizard, stop your artisan server, clear config cache and run your artisan server again:
```
php artisan config:clear
php artisan serve
```
*After any change in your .env you have to restart your artisan server to reload your config cache.*
To develop your Vue front-end, you have to install npm modules by this command:
```
npm install
```
To compiles and hot-reloads for front-end development. Then run this command:
```
npm run hot
```
To compiles for production, run this command
To compiles for production build, run this command
```
npm run prod
```
That's all, happy coding! :tada: :tada: :tada:
## Supported Storages
VueFileManager support these storages for your files:
- [Amazon Web Services S3](https://aws.amazon.com/s3/)
- [Digital Ocean Spaces](https://www.digitalocean.com/products/spaces/)
- [Object Cloud Storage by Wasabi](https://wasabi.com/)
- [Backblaze B2 Cloud Storage](https://www.backblaze.com/b2/cloud-storage.html)
- Your local disk
In case of installation process, you will be able to set storage driver and credentials. After this, you can change your credentials later in `/.env` file.
To set or change your storage driver, you have to edit `FILESYSTEM_DRIVER` in your `/.env` file. Supported drivers are `s3`, `spaces`, `wasabi`,`backblaze` or `local`:
```
FILESYSTEM_DRIVER=local
```
Then you can find corresponding credentials options for your storage driver like key, secret, region in `/.env` file.
## How to Create New Language
VueFileManager front-end support i18n standard for localization. This mean, you can translate app to any language
### How to create translation for Vue Front-End
Go to `/resources/js/i18n/lang` And make copy of `en.json` and rename it to your local name (eg: Slovak language has 'sk' shortcut, it means `sk.json`). If you have created your copy, then feel free to translate this file.
Open `/resources/js/i18n/index.js` import your new language and assign it to languages object:
```
import Vue from 'vue';
import VueI18n from 'vue-i18n';
import en from './lang/en.json'
import sk from './lang/sk.json'
Vue.use(VueI18n);
const i18n = new VueI18n({
locale: config.locale,
messages: Object.assign({
en,
sk
}),
});
export default i18n;
```
After this, you have to compile language to your application code via this command in your terminal:
```
npm run prod
```
### How to Create Translation for Laravel Back-End
Go to `/resources/lang/` And make copy of `en` folder and rename it to your local name (eg: Slovak language has 'sk' shortcut, it means `sk`). If you have created your copy, then feel free to translate this file.
### Set locale
To set your locale in app, go to `/config/app.php` and set your locale string in `locale` option:
```
'locale' => 'YOUR_LOCALE',
```
**Small hint:** We use for translating localizations this awesome software, [check it](https://www.codeandweb.com/babeledit).
# Others
## Changelog
Refer to the [Changelog](https://vuefilemanager.com/changelog) for a full history of the project.
## GitHub Repository
[Join our GitHub repository](https://vuefilemanager.com/github-access) to submit your issues or suggestions, track VueFileManager progress and get new updates as fast as possible.
## Support
The following support channels are available at your fingertips:
- [CodeCanyon support message](https://codecanyon.net/item/vue-file-manager-with-laravel-backend/25815986/support)
- [GitHub repository](https://vuefilemanager.com/github-access)
## Supporting VueFileManager
Hi, we are trying make the best experience with VueFileManager. There is a lot things to do, and a lot of features we can make.
But, it can't be done without you, development is more and more complicated and we have to hire new colleagues to help with it. There is couple way you can support us, and then, we support you with all great new features which can be. Thanks you for participating on this awesome software!
- [Buy me a Coffe](https://www.buymeacoffee.com/pepe)
- [Become a Patreon](https://www.patreon.com/vuefilemanager)
- [One-time donation via PayPal](https://www.paypal.me/peterpapp)
## Security Vulnerabilities
If you discover a security vulnerability within this project, please send an e-mail to [peterpapp@makingcg.com](peterpapp@makingcg.com). All security vulnerabilities will be promptly addressed.

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,66 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Log;
class Deploy extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'deploy:production';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Automatic deployment for production';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
// Start deployment
$this->info('Running auto deployment');
$this->call('down');
// Exec commands
exec('git pull origin ' . config('app.deploy_branch'));
//exec('composer update --no-interaction --prefer-dist');
$this->migrateDatabase();
// Stop deployment
$this->call('up');
$this->info('Everything is done, congratulations! 🥳🥳🥳');
Log::info('Application was updated!');
}
/**
* Migrate database
*/
public function migrateDatabase()
{
$this->call('migrate', [
'--force' => true,
]);
}
}

View File

@@ -1,117 +0,0 @@
<?php
namespace App\Console\Commands;
use App\User;
use Illuminate\Console\Command;
class SetupDevEnvironment extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'setup:dev';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Setting production environment';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$this->info('Setting up production environment');
$this->migrateDatabase();
$this->generateKey();
$this->createPassportKeys();
$this->createPassportClientPassword();
$this->createPassportClientPersonal();
$this->createDefaultUser();
$this->info('Everything is done, congratulations! 🥳🥳🥳');
}
/**
* Migrate database
*/
public function generateKey()
{
$this->call('key:generate');
}
/**
* Migrate database
*/
public function migrateDatabase()
{
$this->call('migrate:fresh');
}
/**
* Create Passport Encryption keys
*/
public function createPassportKeys()
{
$this->call('passport:keys', [
'--force' => true
]);
}
/**
* Create Password grant client
*/
public function createPassportClientPassword()
{
$this->call('passport:client', [
'--password' => true,
'--name' => 'vuefilemanager',
]);
$this->alert('Please copy these first password grant Client ID & Client secret above to your /.env file.');
}
/**
* Create Personal access client
*/
public function createPassportClientPersonal()
{
$this->call('passport:client', [
'--personal' => true,
'--name' => 'shared',
]);
}
/**
* Create Default User
*/
public function createDefaultUser()
{
$user = User::create([
'name' => 'Jane Doe',
'email' => 'howdy@hi5ve.digital',
'password' => \Hash::make('secret'),
]);
$this->info('Test user created. Email: ' . $user->email . ' Password: secret');
}
}

View File

@@ -1,101 +0,0 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
class SetupProductionEnvironment extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'setup:prod';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Setting production environment';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$this->info('Setting up production environment');
$this->migrateDatabase();
$this->generateKey();
$this->createPassportKeys();
$this->createPassportClientPassword();
$this->createPassportClientPersonal();
$this->info('Everything is done, congratulations! 🥳🥳🥳');
}
/**
* Migrate database
*/
public function generateKey()
{
$this->call('key:generate');
}
/**
* Migrate database
*/
public function migrateDatabase()
{
$this->call('migrate:fresh');
}
/**
* Create Passport Encryption keys
*/
public function createPassportKeys()
{
$this->call('passport:keys', [
'--force' => true
]);
}
/**
* Create Password grant client
*/
public function createPassportClientPassword()
{
$this->call('passport:client', [
'--password' => true,
'--name' => 'vuefilemanager',
]);
$this->alert('Please copy these first password grant Client ID & Client secret above to your /.env file.');
}
/**
* Create Personal access client
*/
public function createPassportClientPersonal()
{
$this->call('passport:client', [
'--personal' => true,
'--name' => 'shared',
]);
}
}

View File

@@ -2,8 +2,15 @@
namespace App\Console;
use App\Console\Commands\Deploy;
// use App\Console\Commands\SetupDevelopmentEnvironment;
use App\Console\Commands\SetupDevEnvironment;
use App\Console\Commands\SetupProductionEnvironment;
use App\Console\Commands\UpgradeApp;
use App\Share;
use App\Zip;
use Carbon\Carbon;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
@@ -15,20 +22,30 @@ class Kernel extends ConsoleKernel
* @var array
*/
protected $commands = [
SetupProductionEnvironment::class,
SetupDevEnvironment::class,
Deploy::class,
// SetupDevelopmentEnvironment::class,
];
/**
* Define the application's command schedule.
*
* @param \Illuminate\Console\Scheduling\Schedule $schedule
* @param \Illuminate\Console\Scheduling\Schedule $schedule
* @return void
*/
protected function schedule(Schedule $schedule)
{
// $schedule->command('inspire')
// ->hourly();
$schedule->call(function () {
$this->delete_expired_shared_links();
})->everyMinute();
$schedule->call(function () {
$this->delete_old_zips();
})->everySixHours();
// Run queue jobs every minute
$schedule->command('queue:work --tries=3')
->everyMinute()
->withoutOverlapping();
}
/**
@@ -38,8 +55,46 @@ class Kernel extends ConsoleKernel
*/
protected function commands()
{
$this->load(__DIR__.'/Commands');
$this->load(__DIR__ . '/Commands');
require base_path('routes/console.php');
}
/**
* Delete old zips
*/
protected function delete_old_zips(): void
{
// Get all zips
$zips = Zip::where('created_at', '<=', Carbon::now()->subDay()->toDateTimeString())->get();
$zips->each(function ($zip) {
// Delete zip file
\Storage::disk('local')->delete('zip/' . $zip->basename);
// Delete zip record
$zip->delete();
});
}
/**
* Get and delete expired shared links
*/
protected function delete_expired_shared_links(): void
{
// Get all shares with expiration time
$shares = Share::whereNotNull('expire_in')->get();
$shares->each(function ($share) {
// Get dates
$created_at = Carbon::parse($share->created_at);
// If time was over, then delete share record
if ($created_at->diffInHours(Carbon::now()) >= $share->expire_in) {
$share->delete();
}
});
}
}

View File

@@ -2,8 +2,8 @@
namespace App\Exceptions;
use Exception;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Throwable;
class Handler extends ExceptionHandler
{
@@ -29,12 +29,12 @@ class Handler extends ExceptionHandler
/**
* Report or log an exception.
*
* @param \Exception $exception
* @param \Throwable $exception
* @return void
*
* @throws \Exception
*/
public function report(Exception $exception)
public function report(Throwable $exception)
{
parent::report($exception);
}
@@ -43,13 +43,13 @@ class Handler extends ExceptionHandler
* Render an exception into an HTTP response.
*
* @param \Illuminate\Http\Request $request
* @param \Exception $exception
* @param \Throwable $exception
* @return \Symfony\Component\HttpFoundation\Response
*
* @throws \Exception
* @throws \Throwable
*/
public function render($request, Exception $exception)
public function render($request, Throwable $exception)
{
return parent::render($request, $exception);
}
}
}

View File

@@ -9,6 +9,7 @@ use Illuminate\Support\Str;
use Laravel\Scout\Searchable;
use TeamTNT\TNTSearch\Indexer\TNTIndexer;
use \Illuminate\Database\Eloquent\SoftDeletes;
use Kyslik\ColumnSortable\Sortable;
/**
* App\FileManagerFile
@@ -52,10 +53,13 @@ use \Illuminate\Database\Eloquent\SoftDeletes;
* @method static \Illuminate\Database\Query\Builder|\App\FileManagerFile withTrashed()
* @method static \Illuminate\Database\Query\Builder|\App\FileManagerFile withoutTrashed()
* @mixin \Eloquent
* @property array|null $metadata
* @method static \Illuminate\Database\Eloquent\Builder|FileManagerFile sortable($defaultParameters = null)
* @method static \Illuminate\Database\Eloquent\Builder|FileManagerFile whereMetadata($value)
*/
class FileManagerFile extends Model
{
use Searchable, SoftDeletes;
use Searchable, SoftDeletes , Sortable;
public $public_access = null;
@@ -67,6 +71,20 @@ class FileManagerFile extends Model
'file_url'
];
protected $casts = [
'metadata' => 'array',
];
/**
* Sortable columns
*
* @var string[]
*/
public $sortable = [
'name',
'created_at',
];
/**
* Set routes with public access
*
@@ -116,14 +134,14 @@ class FileManagerFile extends Model
*/
public function getThumbnailAttribute()
{
// Get thumbnail from s3
if ($this->attributes['thumbnail'] && is_storage_driver(['s3', 'spaces'])) {
// Get thumbnail from external storage
if ($this->attributes['thumbnail'] && is_storage_driver(['s3', 'spaces', 'wasabi', 'backblaze'])) {
return Storage::temporaryUrl('file-manager/' . $this->attributes['thumbnail'], now()->addDay());
return Storage::temporaryUrl('file-manager/' . $this->attributes['thumbnail'], now()->addHour());
}
// Get thumbnail from local storage
if ($this->attributes['thumbnail'] && is_storage_driver('local')) {
if ($this->attributes['thumbnail']) {
// Thumbnail route
$route = route('thumbnail', ['name' => $this->attributes['thumbnail']]);
@@ -145,31 +163,32 @@ class FileManagerFile extends Model
*/
public function getFileUrlAttribute()
{
// Get file from s3
if (is_storage_driver(['s3', 'spaces'])) {
// Get file from external storage
if (is_storage_driver(['s3', 'spaces', 'wasabi', 'backblaze'])) {
$file_pretty_name = is_storage_driver('backblaze')
? Str::snake(mb_strtolower($this->attributes['name']))
: get_pretty_name($this->attributes['basename'], $this->attributes['name'], $this->attributes['mimetype']);
$header = [
"ResponseAcceptRanges" => "bytes",
"ResponseContentType" => $this->attributes['mimetype'],
"ResponseContentLength" => $this->attributes['filesize'],
"ResponseContentRange" => "bytes 0-600/" . $this->attributes['filesize'],
'ResponseContentDisposition' => 'attachment; filename=' . $this->attributes['name'] . '.' . $this->attributes['mimetype'],
'ResponseContentDisposition' => 'attachment; filename=' . $file_pretty_name,
];
return Storage::temporaryUrl('file-manager/' . $this->attributes['basename'], now()->addDay(), $header);
}
// Get thumbnail from local storage
if (is_storage_driver('local')) {
$route = route('file', ['name' => $this->attributes['basename']]);
$route = route('file', ['name' => $this->attributes['basename']]);
if ($this->public_access) {
return $route . '/public/' . $this->public_access;
}
return $route;
if ($this->public_access) {
return $route . '/public/' . $this->public_access;
}
return $route;
}
/**

View File

@@ -11,6 +11,7 @@ use RecursiveArrayIterator;
use RecursiveIteratorIterator;
use TeamTNT\TNTSearch\Indexer\TNTIndexer;
use \Illuminate\Database\Eloquent\SoftDeletes;
use Kyslik\ColumnSortable\Sortable;
/**
* App\FileManagerFolder
@@ -58,10 +59,11 @@ use \Illuminate\Database\Eloquent\SoftDeletes;
* @method static \Illuminate\Database\Query\Builder|\App\FileManagerFolder withTrashed()
* @method static \Illuminate\Database\Query\Builder|\App\FileManagerFolder withoutTrashed()
* @mixin \Eloquent
* @method static \Illuminate\Database\Eloquent\Builder|FileManagerFolder sortable($defaultParameters = null)
*/
class FileManagerFolder extends Model
{
use Searchable, SoftDeletes;
use Searchable, SoftDeletes , Sortable;
protected $guarded = [
'id'
@@ -71,6 +73,16 @@ class FileManagerFolder extends Model
'items', 'trashed_items'
];
/**
* Sortable columns
*
* @var string[]
*/
public $sortable = [
'name',
'created_at',
];
/**
* Index folder
*

View File

@@ -0,0 +1,66 @@
<?php
namespace App\Http\Controllers\Admin;
use App\FileManagerFile;
use App\Http\Controllers\Controller;
use App\Http\Resources\UsersCollection;
use App\Services\StripeService;
use App\Setting;
use App\User;
use ByteUnits\Metric;
use Illuminate\Http\Request;
use Laravel\Cashier\Subscription;
class DashboardController extends Controller
{
/**
* DashboardController constructor.
*/
public function __construct(StripeService $stripe)
{
$this->stripe = $stripe;
}
/**
* Get data for dashboard
*
* @return array
*/
public function index()
{
// Get total users
$total_users = User::all()->count();
// Get total used space
$total_used_space = FileManagerFile::all()->map(function ($item) {
return (int)$item->getRawOriginal('filesize');
})->sum();
// Get total premium users
$total_premium_users = Subscription::where('stripe_status', 'active')->get()->count();
// Get License
$license = Setting::where('name', 'license')->first();
return [
'license' => $license ? $license->value : null,
'app_version' => config('vuefilemanager.version'),
'total_users' => $total_users,
'total_used_space' => Metric::bytes($total_used_space)->format(),
'total_premium_users' => $total_premium_users,
];
}
/**
* Get the newest users
*
* @return UsersCollection
*/
public function new_registrations()
{
return new UsersCollection(
User::sortable(['created_at' => 'desc'])->paginate(10)
);
}
}

View File

@@ -0,0 +1,52 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Http\Resources\InvoiceAdminCollection;
use App\Http\Resources\InvoiceResource;
use App\Invoice;
use App\Services\StripeService;
use App\Setting;
use Illuminate\Http\Request;
class InvoiceController extends Controller
{
/**
* PlanController constructor.
*/
public function __construct(StripeService $stripe)
{
$this->stripe = $stripe;
}
/**
* Get all invoices
*
* @return InvoiceAdminCollection
*/
public function index()
{
return new InvoiceAdminCollection(
$this->stripe->getInvoices()['data']
);
}
/**
* Get single invoice by $token
*
* @param $customer
* @param $token
* @return InvoiceResource
*/
public function show($customer, $token)
{
$settings = json_decode(Setting::all()->pluck('value', 'name')->toJson());
$invoice = $this->stripe->getUserInvoice($customer, $token);
return view('vuefilemanager.invoice')
->with('settings', $settings)
->with('invoice', $invoice);
}
}

View File

@@ -0,0 +1,61 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Http\Resources\PageCollection;
use App\Http\Resources\PageResource;
use App\Http\Tools\Demo;
use App\Page;
use Illuminate\Http\Request;
class PagesController extends Controller
{
/**
* Get all pages
*
* @return PageCollection
*/
public function index()
{
return new PageCollection(
Page::sortable()->paginate(10)
);
}
/**
* Get page resource
*
* @param $slug
* @return PageResource
*/
public function show($slug)
{
return new PageResource(
Page::where('slug', $slug)->first()
);
}
/**
* Update page content
*
* @param Request $request
* @param $slug
* @return \Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\Response
*/
public function update(Request $request, $slug)
{
// Check if is demo
if (env('APP_DEMO')) {
return Demo::response_204();
}
// Get page
$page = Page::where('slug', $slug)->first();
// Update page
$page->update(make_single_input($request));
return response('Done', 204);
}
}

View File

@@ -0,0 +1,159 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Http\Resources\PlanCollection;
use App\Http\Resources\PlanResource;
use App\Http\Resources\UserResource;
use App\Http\Resources\UsersCollection;
use App\Http\Tools\Demo;
use App\Plan;
use App\Services\StripeService;
use App\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
use Laravel\Cashier\Subscription;
use Rinvex\Subscriptions\Models\PlanFeature;
class PlanController extends Controller
{
/**
* PlanController constructor.
*/
public function __construct(StripeService $stripe)
{
$this->stripe = $stripe;
}
/**
* Get all plans
*
* @return PlanCollection
*/
public function index()
{
// Store or Get plans to cache
if (Cache::has('plans')) {
$plans = Cache::get('plans');
} else {
$plans = Cache::rememberForever('plans', function () {
return $this->stripe->getPlans();
});
}
return new PlanCollection($plans);
}
/**
* Get plan record
*
* @param $id
* @return PlanResource
*/
public function show($id)
{
// Store or Get plan to cache
if (Cache::has('plan-' . $id)) {
$plan = Cache::get('plan-' . $id);
} else {
$plan = Cache::rememberForever('plan-' . $id, function () use ($id) {
return $this->stripe->getPlan($id);
});
}
return new PlanResource($plan);
}
/**
* Create new plan
*
* @param Request $request
* @return PlanResource
*/
public function store(Request $request)
{
// Check if is demo
if (env('APP_DEMO')) {
if (Cache::has('plan-starter-pack')) {
$plan = Cache::get('plan-starter-pack');
} else {
$plan = Cache::rememberForever('plan-starter-pack', function () {
return $this->stripe->getPlan('starter-pack');
});
}
return new PlanResource($plan);
}
$plan = new PlanResource(
$this->stripe->createPlan($request)
);
// Clear cached plans
cache_forget_many(['plans', 'pricing']);
return $plan;
}
/**
* Update plan attribute
*
* @param Request $request
* @param $id
* @return \Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\Response
*/
public function update(Request $request, $id)
{
// Check if is demo
if (env('APP_DEMO')) {
return Demo::response_204();
}
// Update plan
$this->stripe->updatePlan($request, $id);
// Clear cached plans
cache_forget_many(['plans', 'pricing', 'plan-' . $id]);
return response('Saved!', 204);
}
/**
* Delete plan
*
* @param $id
* @return \Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\Response
*/
public function delete($id)
{
// Check if is demo
if (env('APP_DEMO')) {
return Demo::response_204();
}
// Delete plan
$this->stripe->deletePlan($id);
// Clear cached plans
cache_forget_many(['plans', 'pricing']);
return response('Done!', 204);
}
/**
* Get subscriptions
*
* @param $id
* @return mixed
*/
public function subscribers($id)
{
$subscribers = Subscription::where('stripe_plan', $id)->pluck('user_id');
return new UsersCollection(
User::sortable()->findMany($subscribers)
);
}
}

View File

@@ -0,0 +1,274 @@
<?php
namespace App\Http\Controllers\Admin;
use App\FileManagerFile;
use App\FileManagerFolder;
use App\Http\Controllers\Controller;
use App\Http\Requests\Admin\ChangeRoleRequest;
use App\Http\Requests\Admin\ChangeStorageCapacityRequest;
use App\Http\Requests\Admin\CreateUserByAdmin;
use App\Http\Requests\Admin\DeleteUserRequest;
use App\Http\Resources\InvoiceCollection;
use App\Http\Resources\UsersCollection;
use App\Http\Resources\UserResource;
use App\Http\Resources\UserStorageResource;
use App\Http\Resources\UserSubscription;
use App\Http\Tools\Demo;
use App\Services\StripeService;
use App\Share;
use App\User;
use App\UserSettings;
use Illuminate\Contracts\Routing\ResponseFactory;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Password;
use Illuminate\Support\Str;
use Storage;
class UserController extends Controller
{
public function __construct(StripeService $stripe)
{
$this->stripe = $stripe;
}
/**
* Get user details
*
* @param $id
* @return UserResource
*/
public function details($id)
{
return new UserResource(
User::findOrFail($id)
);
}
/**
* Get user storage details
*
* @param $id
* @return UserStorageResource
*/
public function storage($id)
{
return new UserStorageResource(
User::findOrFail($id)
);
}
/**
* Get user storage details
*
* @return InvoiceCollection
*/
public function invoices($id)
{
$user = User::find($id);
return new InvoiceCollection(
$this->stripe->getUserInvoices($user)
);
}
/**
* Get user subscription details
*
* @param $id
* @return UserSubscription
*/
public function subscription($id)
{
$user = User::find($id);
if (! $user->stripeId() || ! $user->subscription('main')) {
return response('User doesn\'t have any subscription.', 404);
}
return new UserSubscription(
User::find($id)
);
}
/**
* Get all users
*
* @return UsersCollection
*/
public function users()
{
return new UsersCollection(
User::sortable(['created_at', 'DESC'])->paginate('20')
);
}
/**
* Change user role
*
* @param ChangeRoleRequest $request
* @param $id
* @return UserResource
*/
public function change_role(ChangeRoleRequest $request, $id)
{
$user = User::findOrFail($id);
// Demo preview
if (env('APP_DEMO') && $id == 1) {
return new UserResource($user);
}
// Update user role
$user->role = $request->input('attributes.role');
$user->save();
return new UserResource($user);
}
/**
* Change user storage capacity
*
* @param ChangeStorageCapacityRequest $request
* @param $id
* @return UserStorageResource
*/
public function change_storage_capacity(ChangeStorageCapacityRequest $request, $id)
{
$user = User::findOrFail($id);
$user->settings()->update($request->input('attributes'));
return new UserStorageResource($user);
}
/**
* Send user password reset link
*
* @param $id
* @return ResponseFactory|\Illuminate\Http\Response
*/
public function send_password_reset_email($id)
{
$user = User::findOrFail($id);
// Demo preview
if (env('APP_DEMO')) {
return response('Done!', 204);
}
// Get password token
$token = Password::getRepository()->create($user);
// Send user email
$user->sendPasswordResetNotification($token);
return response('Done!', 204);
}
/**
* Create new user by admin
*
* @param CreateUserByAdmin $request
* @return UserResource
*/
public function create_user(CreateUserByAdmin $request)
{
// Store avatar
if ($request->hasFile('avatar')) {
$avatar = store_avatar($request->file('avatar'), 'avatars');
}
// Create user
$user = User::forceCreate([
'avatar' => $request->hasFile('avatar') ? $avatar : null,
'name' => $request->name,
'role' => $request->role,
'email' => $request->email,
'password' => Hash::make($request->password),
]);
// Create settings
UserSettings::forceCreate([
'user_id' => $user->id,
'storage_capacity' => $request->storage_capacity,
]);
return new UserResource($user);
}
/**
* Delete user with all user data
*
* @param DeleteUserRequest $request
* @param $id
* @return ResponseFactory|\Illuminate\Http\Response
* @throws \Exception
*/
public function delete_user(DeleteUserRequest $request, $id)
{
$user = User::findOrFail($id);
if ($user->subscribed('main')) {
abort(202, 'You can\'t delete this account while user have active subscription.');
}
// Demo preview
if (env('APP_DEMO')) {
return response('Done!', 204);
}
// Check for self deleted account
if ($user->id === Auth::id()) {
abort(406, 'You can\'t delete your account');
}
// Validate user name
if ($user->name !== $request->input('data.name')) abort(403);
$shares = Share::where('user_id', $user->id)->get();
$files = FileManagerFile::withTrashed()
->where('user_id', $user->id)
->get();
$folders = FileManagerFolder::withTrashed()
->where('user_id', $user->id)
->get();
// Remove all files and thumbnails
$files->each(function ($file) {
// Delete file
Storage::delete('/file-manager/' . $file->basename);
// Delete thumbnail if exist
if (!is_null($file->thumbnail)) {
Storage::delete('/file-manager/' . $file->getRawOriginal('thumbnail'));
}
// Delete file permanently
$file->forceDelete();
});
// Remove avatar
if ($user->avatar) {
Storage::delete('/avatars/' . $user->avatar);
}
// Remove folders & shares
$folders->each->forceDelete();
$shares->each->forceDelete();
// Remove favourites
$user->settings->delete();
$user->favourite_folders()->sync([]);
// Delete user
$user->delete();
return response('Done!', 204);
}
}

View File

@@ -2,13 +2,51 @@
namespace App\Http\Controllers;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Auth;
use App\Content;
use App\FileManagerFile;
use App\FileManagerFolder;
use App\Http\Requests\PublicPages\SendMessageRequest;
use App\Http\Resources\PageResource;
use App\Http\Tools\Demo;
use App\Mail\SendSupportForm;
use App\Page;
use App\Setting;
use App\User;
use Artisan;
use Doctrine\DBAL\Driver\PDOException;
use Illuminate\Http\Request;
use Response;
use Illuminate\Support\Facades\Mail;
use Schema;
class AppFunctionsController extends Controller
{
/**
* List of allowed settings to get from public request
*
* @var array
*/
private $whitelist = [
'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',
];
/**
* Show index page
*
@@ -16,6 +54,195 @@ class AppFunctionsController extends Controller
*/
public function index()
{
return view("index");
try {
// Try to connect to database
\DB::getPdo();
// Check settings table
$settings_table = Schema::hasTable('settings');
$users_table = Schema::hasTable('users');
// If settings table don't exist, then run migrations
if ($users_table && !$settings_table) {
Artisan::call('migrate', [
'--force' => true
]);
}
// Get settings
$upgraded = Setting::where('name', 'latest_upgrade')->first();
// Get connection string
if ($upgraded && $upgraded->value !== '1.7') {
$connection = 'quiet-update';
} else if (!$upgraded) {
$connection = 'quiet-update';
} else {
$connection = $this->get_setup_status();
}
// Get all settings
$settings = Setting::all();
// Get legal pages
$legal = Page::whereIn('slug', ['terms-of-service', 'privacy-policy', 'cookie-policy'])
->get(['visibility', 'title', 'slug']);
} catch (PDOException $e) {
$connection = 'setup-database';
$settings = null;
}
return view("index")
->with('settings', $settings ? json_decode($settings->pluck('value', 'name')->toJson()) : null)
->with('legal', isset($legal) ? $legal : null)
->with('installation', $connection);
}
/**
* Get og site for web crawlers
*
* @param $token
*/
public function og_site($token)
{
// Get all settings
$settings = Setting::all();
// Get shared token
$shared = get_shared($token);
// Get user
$user = User::findOrFail($shared->user_id);
// Handle single file
if ($shared->type === 'file') {
// Get file record
$file = FileManagerFile::where('user_id', $shared->user_id)
->where('unique_id', $shared->item_id)
->first();
if ($file->thumbnail) {
$file->setPublicUrl($token);
}
$metadata = [
'is_protected' => $shared->protected,
'url' => url('/shared', ['token' => $token]),
'user' => $user->name,
'name' => $file->name,
'size' => $file->filesize,
'thumbnail' => $file->thumbnail ? $file->thumbnail : null,
];
}
// Handle single file
if ($shared->type === 'folder') {
// Get file record
$folder = FileManagerFolder::where('user_id', $shared->user_id)
->where('unique_id', $shared->item_id)
->first();
$metadata = [
'is_protected' => $shared->protected,
'url' => url('/shared', ['token' => $token]),
'user' => $user->name,
'name' => $folder->name,
'size' => $folder->items,
'thumbnail' => null,
];
}
// Return view
return view("og-view")
->with('settings', json_decode($settings->pluck('value', 'name')->toJson()))
->with('metadata', $metadata);
}
/**
* Check if setup wizard was passed
*
* @return string
*/
private function get_setup_status(): string
{
$setup_success = get_setting('setup_wizard_success');
$connection = boolval($setup_success) ? 'setup-done' : 'setup-disclaimer';
return $connection;
}
/**
* Send contact message from pages
*
* @param SendMessageRequest $request
* @return \Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\Response
*/
public function contact_form(SendMessageRequest $request)
{
// Get receiver email
$receiver = Setting::where('name', 'contact_email')->first();
// Send message
Mail::to($receiver->value)->send(new SendSupportForm($request->all()));
return response('Done', 200);
}
/**
* Get single page content
*
* @param $slug
* @return PageResource
*/
public function get_page($slug)
{
return new PageResource(
Page::where('slug', $slug)->first()
);
}
/**
* Get selected settings from public route
*
* @param Request $request
* @return mixed
*/
public function get_settings(Request $request)
{
$column = $request->get('column');
if (strpos($column, '|') !== false) {
$columns = collect(explode('|', $column));
$columns->each(function ($column) {
if (!in_array($column, $this->whitelist)) abort(401);
});
return Setting::whereIn('name', $columns)->pluck('value', 'name');
}
if (!in_array($column, $this->whitelist)) abort(401);
return Setting::where('name', $column)->pluck('value', 'name');
}
/**
* Clear application cache
*/
public function flush_cache()
{
// Check if is demo
if (env('APP_DEMO')) {
return Demo::response_204();
}
Artisan::call('cache:clear');
Artisan::call('config:clear');
Artisan::call('config:cache');
}
}

View File

@@ -3,7 +3,9 @@
namespace App\Http\Controllers\Auth;
use App\Http\Requests\Auth\CheckAccountRequest;
use App\Setting;
use App\User;
use App\UserSettings;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Auth;
@@ -19,20 +21,21 @@ class AuthController extends Controller
* @param Request $request
* @return mixed
*/
public function check_account(CheckAccountRequest $request) {
public function check_account(CheckAccountRequest $request)
{
// Get User
$user = User::where('email', $request->input('email'))->select(['name', 'avatar'])->first();
// Return user info
if ($user) return [
'name' => $user->name,
'name' => $user->name,
'avatar' => $user->avatar,
];
// Abort with 404, user not found
return abort('404', __('vuefilemanager.user_not_fount'));
}
/**
* Login user
*
@@ -41,17 +44,16 @@ class AuthController extends Controller
*/
public function login(Request $request)
{
$response = Route::dispatch(self::make_request($request));
$response = Route::dispatch(self::make_login_request($request));
if ($response->isSuccessful()) {
$data = json_decode($response->content(), true);
return response('Login Successfull!', 200)->cookie('access_token', $data['access_token'], 43200);
} else {
return $response;
}
return $response;
}
/**
@@ -62,34 +64,41 @@ class AuthController extends Controller
*/
public function register(Request $request)
{
$settings = Setting::whereIn('name', ['storage_default', 'registration'])->pluck('value', 'name');
// Check if account registration is enabled
if (! config('vuefilemanager.registration') ) abort(401);
if (! intval($settings['registration'])) abort(401);
// Validate request
$request->validate([
'name' => ['required', 'string', 'max:255'],
'name' => ['required', 'string', 'max:255'],
'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
'password' => ['required', 'string', 'min:6', 'confirmed'],
]);
// Create user
User::create([
$user = User::create([
'name' => $request->name,
'email' => $request->email,
'password' => Hash::make($request->password),
]);
$response = Route::dispatch(self::make_request($request));
// Create settings
UserSettings::forceCreate([
'user_id' => $user->id,
'storage_capacity' => $settings['storage_default'],
]);
$response = Route::dispatch(self::make_login_request($request));
if ($response->isSuccessful()) {
$data = json_decode($response->content(), true);
return response('Register Successfull!', 200)->cookie('access_token', $data['access_token'], 43200);
} else {
return $response;
}
return $response;
}
/**
@@ -100,7 +109,7 @@ class AuthController extends Controller
public function logout()
{
// Demo preview
if (is_demo( Auth::id())) {
if (is_demo(Auth::id())) {
return response('Logout successfull', 204)
->cookie('access_token', '', -1);
}
@@ -112,18 +121,17 @@ class AuthController extends Controller
$token->delete();
});
return response('Logout successfull', 204)
return response('Logout successful', 204)
->cookie('access_token', '', -1);
}
/**
* Make request for get user token
* Make login request for get access token
*
* @param Request $request
* @param string $provider
* @return Request
*/
private static function make_request($request)
private static function make_login_request($request)
{
$request->request->add([
'grant_type' => 'password',

View File

@@ -0,0 +1,44 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Artisan;
use Illuminate\Support\Facades\Log;
use Illuminate\Validation\UnauthorizedException;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
class DeployController extends Controller
{
/**
* Get web hook payload and verify request
*
* @param Request $request
* @return \Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\Response
*/
public function github(Request $request) {
if (($signature = $request->headers->get('X-Hub-Signature')) == null) {
throw new BadRequestHttpException('Header not set');
}
$signature_parts = explode('=', $signature);
if (count($signature_parts) != 2) {
throw new BadRequestHttpException('signature has invalid format');
}
$known_signature = hash_hmac('sha1', $request->getContent(), config('app.deploy_secret'));
if (! hash_equals($known_signature, $signature_parts[1])) {
throw new UnauthorizedException('Could not verify request signature ' . $signature_parts[1]);
}
// Run deploying
Artisan::call('deploy:production');
Log::info('The GitHub webhook was accepted');
return response('The GitHub webhook was accepted', 202);
}
}

View File

@@ -3,8 +3,11 @@
namespace App\Http\Controllers;
use App\FileManagerFolder;
use App\Http\Tools\Editor;
use App\Http\Tools\Guardian;
use App\Share;
use App\User;
use App\Zip;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
@@ -13,7 +16,11 @@ use Illuminate\Http\Request;
use App\FileManagerFile;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use Illuminate\Http\Exceptions\HttpResponseException;
use Madnest\Madzipper\Facades\Madzipper;
use Response;
use League\Flysystem\FileNotFoundException;
use Symfony\Component\HttpKernel\Exception\HttpException;
class FileAccessController extends Controller
{
@@ -36,6 +43,25 @@ class FileAccessController extends Controller
return Storage::download($path, $basename);
}
/**
* Get system image
*
* @param $basename
* @return mixed
* @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
*/
public function get_system_image($basename)
{
// Get file path
$path = '/system/' . $basename;
// Check if file exist
if (!Storage::exists($path)) abort(404);
// Return avatar
return Storage::download($path, $basename);
}
/**
* Get file
*
@@ -65,9 +91,63 @@ class FileAccessController extends Controller
$this->check_file_access($shared, $file);
}
// Store user download size
$request->user()->record_download((int)$file->getRawOriginal('filesize'));
return $this->download_file($file);
}
/**
* Get generated zip for user
*
* @param $id
* @return \Symfony\Component\HttpFoundation\StreamedResponse
*/
public function get_zip($id)
{
$zip = Zip::where('id', $id)
->where('user_id', Auth::id())
->first();
$zip_path = 'zip/' . $zip->basename;
$header = [
"Content-Type" => 'application/zip',
"Content-Length" => Storage::disk('local')->size($zip_path),
"Accept-Ranges" => "bytes",
"Content-Range" => "bytes 0-600/" . Storage::disk('local')->size($zip_path),
"Content-Disposition" => "attachment; filename=" . $zip->basename,
];
return Storage::disk('local')->download($zip_path, $zip->basename, $header);
}
/**
* Get generated zip for guest
*
* @param $id
* @param $token
* @return \Symfony\Component\HttpFoundation\StreamedResponse
*/
public function get_zip_public($id, $token)
{
$zip = Zip::where('id', $id)
->where('shared_token', $token)
->first();
$zip_path = 'zip/' . $zip->basename;
$header = [
"Content-Type" => 'application/zip',
"Content-Length" => Storage::disk('local')->size($zip_path),
"Accept-Ranges" => "bytes",
"Content-Range" => "bytes 0-600/" . Storage::disk('local')->size($zip_path),
"Content-Disposition" => "attachment; filename=" . $zip->basename,
];
return Storage::disk('local')->download($zip_path, $zip->basename, $header);
}
/**
* Get file public
*
@@ -82,7 +162,7 @@ class FileAccessController extends Controller
$shared = get_shared($token);
// Abort if shared is protected
if ($shared->protected) {
if ((int)$shared->protected) {
abort(403, "Sorry, you don't have permission");
}
@@ -94,6 +174,9 @@ class FileAccessController extends Controller
// Check file access
$this->check_file_access($shared, $file);
// Store user download size
User::find($shared->user_id)->record_download((int)$file->getRawOriginal('filesize'));
return $this->download_file($file);
}
@@ -135,7 +218,7 @@ class FileAccessController extends Controller
$shared = get_shared($token);
// Abort if thumbnail is protected
if ($shared->protected) {
if ((int)$shared->protected) {
abort(403, "Sorry, you don't have permission");
}
@@ -178,8 +261,7 @@ class FileAccessController extends Controller
*/
private function download_file($file)
{
// Format pretty filename
$file_pretty_name = $file->name . '.' . $file->mimetype;
$file_pretty_name = get_pretty_name($file->basename, $file->name, $file->mimetype);
// Get file path
$path = '/file-manager/' . $file->basename;
@@ -206,12 +288,12 @@ class FileAccessController extends Controller
private function thumbnail_file($file)
{
// Get file path
$path = '/file-manager/' . $file->getOriginal('thumbnail');
$path = '/file-manager/' . $file->getRawOriginal('thumbnail');
// Check if file exist
if (!Storage::exists($path)) abort(404);
// Return image thumbnail
return Storage::download($path, $file->getOriginal('thumbnail'));
return Storage::download($path, $file->getRawOriginal('thumbnail'));
}
}

View File

@@ -3,6 +3,7 @@
namespace App\Http\Controllers\FileBrowser;
use App\Http\Requests\FileBrowser\SearchRequest;
use App\User;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Facades\Auth;
use App\Http\Controllers\Controller;
@@ -27,19 +28,23 @@ class BrowseController extends Controller
// Get folders and files
$folders_trashed = FileManagerFolder::onlyTrashed()
->with(['trashed_folders'])
->with(['trashed_folders', 'parent'])
->where('user_id', $user_id)
->get(['parent_id', 'unique_id', 'name']);
$folders = FileManagerFolder::onlyTrashed()
->with(['parent'])
->where('user_id', $user_id)
->whereIn('unique_id', filter_folders_ids($folders_trashed))
->sortable()
->get();
// Get files trashed
$files_trashed = FileManagerFile::onlyTrashed()
->with(['parent'])
->where('user_id', $user_id)
->whereNotIn('folder_id', array_values(array_unique(recursiveFind($folders_trashed->toArray(), 'unique_id'))))
->sortable()
->get();
// Collect folders and files to single array
@@ -66,20 +71,56 @@ class BrowseController extends Controller
->pluck('item_id');
// Get folders and files
$folders = FileManagerFolder::with(['parent', 'shared:token,id,item_id,permission,protected'])
$folders = FileManagerFolder::with(['parent', 'shared:token,id,item_id,permission,protected,expire_in'])
->where('user_id', $user_id)
->whereIn('unique_id', $folder_ids)
->sortable()
->get();
$files = FileManagerFile::with(['parent', 'shared:token,id,item_id,permission,protected'])
$files = FileManagerFile::with(['parent', 'shared:token,id,item_id,permission,protected,expire_in'])
->where('user_id', $user_id)
->whereIn('unique_id', $file_ids)
->sortable()
->get();
// Collect folders and files to single array
return collect([$folders, $files])->collapse();
}
/**
* Get latest user uploads
*
* @return mixed
*/
public function latest() {
// Get User
$user = User::with(['latest_uploads' => function($query) {
$query->sortable();
}])
->where('id', Auth::id())
->first();
return $user->latest_uploads->makeHidden(['user_id', 'basename']);
}
/**
* Get participant uploads
*
* @return mixed
*/
public function participant_uploads() {
// Get User
$uploads = FileManagerFile::with(['parent'])
->where('user_id', Auth::id())
->whereUserScope('editor')
->sortable()
->get();
return $uploads;
}
/**
* Get directory with files
*
@@ -97,15 +138,17 @@ class BrowseController extends Controller
// Get folders and files
$folders = FileManagerFolder::onlyTrashed()
->where('user_id', $user_id)
->with('parent')
->where('user_id', $user_id)
->where('parent_id', $unique_id)
->sortable()
->get();
$files = FileManagerFile::onlyTrashed()
->where('user_id', $user_id)
->with('parent')
->where('user_id', $user_id)
->where('folder_id', $unique_id)
->sortable()
->get();
// Collect folders and files to single array
@@ -113,14 +156,16 @@ class BrowseController extends Controller
}
// Get folders and files
$folders = FileManagerFolder::with(['parent', 'shared:token,id,item_id,permission,protected'])
$folders = FileManagerFolder::with(['parent', 'shared:token,id,item_id,permission,protected,expire_in'])
->where('user_id', $user_id)
->where('parent_id', $unique_id)
->sortable()
->get();
$files = FileManagerFile::with(['parent', 'shared:token,id,item_id,permission,protected'])
$files = FileManagerFile::with(['parent', 'shared:token,id,item_id,permission,protected,expire_in'])
->where('user_id', $user_id)
->where('folder_id', $unique_id)
->sortable()
->get();
// Collect folders and files to single array
@@ -137,6 +182,7 @@ class BrowseController extends Controller
$folders = FileManagerFolder::with('folders:id,parent_id,unique_id,name')
->where('parent_id', 0)
->where('user_id', Auth::id())
->sortable()
->get(['id', 'parent_id', 'unique_id', 'name']);
return [
@@ -159,33 +205,17 @@ class BrowseController extends Controller
{
// Get user
$user_id = Auth::id();
$query = remove_accents($request->input('query'));
// Search files id db
$searched_files = FileManagerFile::search($request->input('query'))
$searched_files = FileManagerFile::search($query)
->where('user_id', $user_id)
->get();
$searched_folders = FileManagerFolder::search($request->input('query'))
$searched_folders = FileManagerFolder::search($query)
->where('user_id', $user_id)
->get();
// Collect folders and files to single array
return collect([$searched_folders, $searched_files])->collapse();
}
/**
* Get file record
*
* @param $unique_id
* @return mixed
*/
public function file_detail($unique_id)
{
// Get user id
$user_id = Auth::id();
return FileManagerFile::with(['shared:token,id,item_id,permission,protected'])
->where('user_id', $user_id)
->where('unique_id', $unique_id)
->firstOrFail();
}
}

View File

@@ -10,6 +10,7 @@ use App\Http\Requests\FileFunctions\UploadRequest;
use App\Http\Tools\Demo;
use Illuminate\Contracts\Routing\ResponseFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use App\Http\Controllers\Controller;
use App\Http\Tools\Guardian;
@@ -168,40 +169,43 @@ class EditItemsController extends Controller
* @return ResponseFactory|\Illuminate\Http\Response
* @throws Exception
*/
public function user_delete_item(DeleteItemRequest $request, $unique_id)
public function user_delete_item(DeleteItemRequest $request)
{
// Demo preview
if (is_demo(Auth::id())) {
return Demo::response_204();
}
// Check permission to delete item for authenticated editor
if ($request->user()->tokenCan('editor')) {
foreach ($request->input('data') as $file) {
$unique_id = $file['unique_id'];
// Prevent force delete for non-master users
if ($request->force_delete) abort('401');
// Check permission to delete item for authenticated editor
if ($request->user()->tokenCan('editor')) {
// check if shared_token cookie exist
if (!$request->hasCookie('shared_token')) abort('401');
// Prevent force delete for non-master users
if ($file['force_delete']) abort('401');
// Get shared token
$shared = get_shared($request->cookie('shared_token'));
// check if shared_token cookie exist
if (!$request->hasCookie('shared_token')) abort('401');
// Get file|folder item
$item = get_item($request->type, $unique_id, Auth::id());
// Get shared token
$shared = get_shared($request->cookie('shared_token'));
// Check access to requested directory
if ($request->type === 'folder') {
Guardian::check_item_access($item->unique_id, $shared);
} else {
Guardian::check_item_access($item->folder_id, $shared);
// Get file|folder item
$item = get_item($file['type'], $unique_id, Auth::id());
// Check access to requested directory
if ($file['type'] === 'folder') {
Guardian::check_item_access($item->unique_id, $shared);
} else {
Guardian::check_item_access($item->folder_id, $shared);
}
}
// Delete item
Editor::delete_item($file, $unique_id);
}
// Delete item
Editor::delete_item($request, $unique_id);
// Return response
return response(null, 204);
}
@@ -214,7 +218,7 @@ class EditItemsController extends Controller
* @return ResponseFactory|\Illuminate\Http\Response
* @throws Exception
*/
public function guest_delete_item(DeleteItemRequest $request, $unique_id, $token)
public function guest_delete_item(DeleteItemRequest $request, $token)
{
// Get shared record
$shared = get_shared($token);
@@ -227,25 +231,28 @@ class EditItemsController extends Controller
// Check shared permission
if (!is_editor($shared)) abort(403);
// Get file|folder item
$item = get_item($request->type, $unique_id, $shared->user_id);
foreach ($request->input('data') as $file) {
$unique_id = $file['unique_id'];
// Check access to requested item
if ($request->type === 'folder') {
Guardian::check_item_access($item->unique_id, $shared);
} else {
Guardian::check_item_access($item->folder_id, $shared);
// Get file|folder item
$item = get_item($file['type'], $unique_id, $shared->user_id);
// Check access to requested item
if ($file['type'] === 'folder') {
Guardian::check_item_access($item->unique_id, $shared);
} else {
Guardian::check_item_access($item->folder_id, $shared);
}
// Delete item
Editor::delete_item($file, $unique_id, $shared);
}
// Delete item
Editor::delete_item($request, $unique_id, $shared);
// Return response
return response(null, 204);
}
/**
* Delete file for authenticated master|editor user
* Upload file for authenticated master|editor user
*
* @param UploadRequest $request
* @return FileManagerFile|Model
@@ -258,11 +265,6 @@ class EditItemsController extends Controller
return Demo::upload($request);
}
// Check if user can upload
if (config('vuefilemanager.limit_storage_by_capacity') && user_storage_percentage() >= 100) {
abort(423, 'You exceed your storage limit!');
}
// Check permission to upload for authenticated editor
if ($request->user()->tokenCan('editor')) {
@@ -314,19 +316,13 @@ class EditItemsController extends Controller
}
/**
* Move item for authenticated master|editor user
* User download multiple files via zip
*
* @param MoveItemRequest $request
* @param $unique_id
* @return ResponseFactory|\Illuminate\Http\Response
* @param Request $request
* @return string
*/
public function user_move(MoveItemRequest $request, $unique_id)
public function user_zip_multiple_files(Request $request)
{
// Demo preview
if (is_demo(Auth::id())) {
return Demo::response_204();
}
// Check permission to upload for authenticated editor
if ($request->user()->tokenCan('editor')) {
@@ -336,12 +332,98 @@ class EditItemsController extends Controller
// Get shared token
$shared = get_shared($request->cookie('shared_token'));
$file_parent_folders = FileManagerFile::whereUserId(Auth::id())
->whereIn('unique_id', $request->input('files'))
->get()
->pluck('folder_id')
->toArray();
// Check access to requested directory
Guardian::check_item_access($request->to_unique_id, $shared);
Guardian::check_item_access($file_parent_folders, $shared);
}
// Get requested files
$files = FileManagerFile::whereUserId(Auth::id())
->whereIn('unique_id', $request->input('files'))
->get();
$zip = Editor::zip_files($files);
// Get file
return response([
'url' => route('zip', $zip->id),
'name' => $zip->basename,
], 200);
}
/**
* Guest download multiple files via zip
*
* @param Request $request
* @param $token
* @return string
*/
public function guest_zip_multiple_files(Request $request, $token)
{
// Get shared record
$shared = get_shared($token);
$file_parent_folders = FileManagerFile::whereUserId($shared->user_id)
->whereIn('unique_id', $request->input('files'))
->get()
->pluck('folder_id')
->toArray();
// Check access to requested directory
Guardian::check_item_access($file_parent_folders, $shared);
// Get requested files
$files = FileManagerFile::whereUserId($shared->user_id)
->whereIn('unique_id', $request->input('files'))
->get();
$zip = Editor::zip_files($files, $shared);
// Get file
return response([
'url' => route('zip_public', [
'id' => $zip->id,
'token' => $shared->token,
]),
'name' => $zip->basename,
], 200);
}
/**
* Move item for authenticated master|editor user
*
* @param MoveItemRequest $request
* @param $unique_id
* @return ResponseFactory|\Illuminate\Http\Response
*/
public function user_move(MoveItemRequest $request)
{
// Demo preview
if (is_demo(Auth::id())) {
return Demo::response_204();
}
$to_unique_id = $request->input('to_unique_id');
// Check permission to upload for authenticated editor
if ($request->user()->tokenCan('editor')) {
// check if shared_token cookie exist
if (!$request->hasCookie('shared_token')) abort('401');
// Get shared token
$shared = get_shared($request->cookie('shared_token'));
// Check access to requested directory
Guardian::check_item_access($to_unique_id, $shared);
}
// Move item
Editor::move($request, $unique_id);
Editor::move($request, $to_unique_id);
return response('Done!', 204);
}
@@ -354,11 +436,14 @@ class EditItemsController extends Controller
* @param $token
* @return ResponseFactory|\Illuminate\Http\Response
*/
public function guest_move(MoveItemRequest $request, $unique_id, $token)
public function guest_move(MoveItemRequest $request, $token)
{
// Get shared record
$shared = get_shared($token);
//Unique id of Folder where move
$to_unique_id = $request->input('to_unique_id');
// Demo preview
if (is_demo(Auth::id())) {
return Demo::response_204();
@@ -367,23 +452,28 @@ class EditItemsController extends Controller
// Check shared permission
if (!is_editor($shared)) abort(403);
$moving_unique_id = $unique_id;
foreach ($request->input('items') as $item) {
if ($request->from_type !== 'folder') {
$file = FileManagerFile::where('unique_id', $unique_id)
->where('user_id', $shared->user_id)
->firstOrFail();
$unique_id = $item['unique_id'];
$moving_unique_id = $unique_id;
$moving_unique_id = $file->folder_id;
if ($item['type'] !== 'folder') {
$file = FileManagerFile::where('unique_id', $unique_id)
->where('user_id', $shared->user_id)
->firstOrFail();
$moving_unique_id = $file->folder_id;
}
// Check access to requested item
Guardian::check_item_access([
$to_unique_id, $moving_unique_id
], $shared);
}
// Check access to requested item
Guardian::check_item_access([
$request->to_unique_id, $moving_unique_id
], $shared);
// Move item
Editor::move($request, $unique_id, $shared);
Editor::move($request, $to_unique_id, $shared);
return response('Done!', 204);
}

View File

@@ -20,16 +20,18 @@ class FavouriteController extends Controller
public function store(Request $request)
{
// Validate request
$validator = Validator::make($request->all(), [
'unique_id' => 'required|integer',
$validator = Validator::make($request->input('folders'), [
'*.unique_id' => 'required|integer',
]);
// Return error
if ($validator->fails()) abort(400, 'Bad input');
foreach($request->input('folders') as $item) {
// Get user & folder
$user = Auth::user();
$folder = FileManagerFolder::where('unique_id', $request->unique_id)->first();
$folder = FileManagerFolder::where('unique_id', $item['unique_id'])->first();
if (is_demo($user->id)) {
return Demo::favourites($user);
@@ -39,10 +41,11 @@ class FavouriteController extends Controller
if ($folder->user_id !== $user->id) abort(403);
// Add folder to user favourites
$user->favourites()->syncWithoutDetaching($request->unique_id);
$user->favourite_folders()->syncWithoutDetaching($item['unique_id']);
}
// Return updated favourites
return $user->favourites->makeHidden(['pivot']);
return $user->favourite_folders;
}
/**
@@ -61,9 +64,9 @@ class FavouriteController extends Controller
}
// Remove folder from user favourites
$user->favourites()->detach($unique_id);
$user->favourite_folders()->detach($unique_id);
// Return updated favourites
return $user->favourites->makeHidden(['pivot']);
return $user->favourite_folders;
}
}

View File

@@ -5,6 +5,7 @@ namespace App\Http\Controllers\FileFunctions;
use App\Http\Requests\Share\CreateShareRequest;
use App\Http\Requests\Share\UpdateShareRequest;
use App\Http\Resources\ShareResource;
use App\Zip;
use Illuminate\Contracts\Routing\ResponseFactory;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
@@ -51,6 +52,7 @@ class ShareController extends Controller
'protected' => $request->isPassword,
'permission' => $request->permission,
'item_id' => $request->unique_id,
'expire_in' => $request->expiration,
'user_id' => Auth::id(),
'token' => $token,
];
@@ -77,6 +79,7 @@ class ShareController extends Controller
$shared->update([
'permission' => $request->permission,
'protected' => $request->protected,
'expire_in' => $request->expiration,
'password' => $request->password ? Hash::make($request->password) : $shared->password,
]);
@@ -91,15 +94,25 @@ class ShareController extends Controller
* @return ResponseFactory|\Illuminate\Http\Response
* @throws \Exception
*/
public function destroy($token)
public function destroy(Request $request)
{
// Get sharing record
$shared = Share::where('token', $token)
->where('user_id', Auth::id())
->firstOrFail();
foreach($request->input('tokens') as $token) {
// Delete shared record
$shared->delete();
// Get sharing record
Share::where('token', $token)
->where('user_id', Auth::id())
->firstOrFail()
->delete();
// Get zip record
$zip = Zip::where('shared_token', $token)
->where('user_id', Auth::id())
->first();
if ($zip) {
$zip->delete();
}
}
// Done
return response('Done!', 204);

View File

@@ -42,7 +42,7 @@ class TrashController extends Controller
Storage::delete('/file-manager/' . $file->basename);
// Delete thumbnail if exist
if ($file->thumbnail) Storage::delete('/file-manager/' . $file->getOriginal('thumbnail'));
if ($file->thumbnail) Storage::delete('/file-manager/' . $file->getRawOriginal('thumbnail'));
// Delete file permanently
$file->forceDelete();

View File

@@ -0,0 +1,48 @@
<?php
namespace App\Http\Controllers\General;
use App\Http\Controllers\Controller;
use App\Http\Resources\PricingCollection;
use App\Services\StripeService;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
class PricingController extends Controller
{
/**
* PlanController constructor.
*/
public function __construct(StripeService $stripe)
{
$this->stripe = $stripe;
}
/**
* Get all active plans
*
* @return PricingCollection
*/
public function index()
{
if (Cache::has('pricing')) {
// Get pricing from cache
$pricing = Cache::get('pricing');
} else {
// Store pricing to cache
$pricing = Cache::rememberForever('pricing', function () {
return $this->stripe->getActivePlans();
});
}
// Format pricing to collection
$collection = new PricingCollection($pricing);
// Sort and return pricing
return $collection->sortBy('product.metadata.capacity')
->values()
->all();
}
}

View File

@@ -0,0 +1,578 @@
<?php
namespace App\Http\Controllers\General;
use App\Http\Controllers\Controller;
use App\Http\Requests\SetupWizard\CreateAdminRequest;
use App\Http\Requests\SetupWizard\StoreAppSetupRequest;
use App\Http\Requests\SetupWizard\StoreDatabaseCredentialsRequest;
use App\Http\Requests\SetupWizard\StoreEnvironmentSetupRequest;
use App\Http\Requests\SetupWizard\StoreStripeBillingRequest;
use App\Http\Requests\SetupWizard\StoreStripeCredentialsRequest;
use App\Http\Requests\SetupWizard\StoreStripePlansRequest;
use App\Page;
use App\Services\StripeService;
use App\Setting;
use App\User;
use App\UserSettings;
use Artisan;
use Cartalyst\Stripe\Exception\UnauthorizedException;
use Doctrine\DBAL\Driver\PDOException;
use Illuminate\Contracts\Routing\ResponseFactory;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Str;
use Schema;
use Stripe;
use Symfony\Component\HttpKernel\Exception\HttpException;
class SetupWizardController extends Controller
{
/**
* Inject Stripe Service
*/
public function __construct(StripeService $stripe)
{
$this->stripe = $stripe;
}
/**
* Verify Envato purchase code
*
* @param Request $request
* @return ResponseFactory|\Illuminate\Http\Response|mixed
*/
public function verify_purchase_code(Request $request)
{
// Check setup status
if ($this->get_setup_status()) abort(410, 'Gone');
// Verify purchase code
$response = Http::get('https://verify.vuefilemanager.com/api/verify-code/' . $request->purchaseCode);
if ($response->successful()) {
return $response;
}
return response('Purchase code is invalid.', 400);
}
/**
* Set up database credentials
*
* @param StoreDatabaseCredentialsRequest $request
* @return ResponseFactory|\Illuminate\Http\Response
*/
public function setup_database(StoreDatabaseCredentialsRequest $request)
{
// Check setup status
if ($this->get_setup_status()) abort(410, 'Gone');
try {
// Set temporary database connection
config(['database.connections.test.driver' => $request->connection]);
config(['database.connections.test.host' => $request->host]);
config(['database.connections.test.port' => $request->port]);
config(['database.connections.test.database' => $request->name]);
config(['database.connections.test.username' => $request->username]);
config(['database.connections.test.password' => $request->password]);
// Test connection
\DB::connection('test')->getPdo();
} catch (PDOException $e) {
throw new HttpException(500, $e->getMessage());
}
setEnvironmentValue([
'DB_CONNECTION' => $request->connection,
'DB_HOST' => $request->host,
'DB_PORT' => $request->port,
'DB_DATABASE' => $request->name,
'DB_USERNAME' => $request->username,
'DB_PASSWORD' => $request->password,
]);
// Clear cache
Artisan::call('config:cache');
// Set up application
$this->set_up_application();
// Store setup wizard progress
Setting::create([
'name' => 'setup_wizard_database',
'value' => 1,
]);
return response('Done', 200);
}
/**
* Store and test stripe credentials
*
* @param StoreStripeCredentialsRequest $request
* @return ResponseFactory|\Illuminate\Http\Response
*/
public function store_stripe_credentials(StoreStripeCredentialsRequest $request)
{
// Check setup status
if ($this->get_setup_status()) abort(410, 'Gone');
// Create stripe instance
$stripe = Stripe::make($request->secret, '2020-03-02');
// Try to get stripe account details
try {
$stripe->account()->details();
} catch (UnauthorizedException $e) {
throw new HttpException(401, $e->getMessage());
}
// Get options
$settings = collect([
[
'name' => 'stripe_currency',
'value' => $request->currency,
],
[
'name' => 'payments_configured',
'value' => 1,
],
[
'name' => 'payments_active',
'value' => 1,
],
]);
// Store options
$settings->each(function ($col) {
Setting::updateOrCreate(['name' => $col['name']], $col);
});
// Set stripe credentials to .env
setEnvironmentValue([
'CASHIER_CURRENCY' => $request->currency,
'STRIPE_KEY' => $request->key,
'STRIPE_SECRET' => $request->secret,
'STRIPE_WEBHOOK_SECRET' => $request->webhookSecret,
]);
// Clear cache
Artisan::call('config:cache');
return response('Done', 200);
}
/**
* Store Stripe billings
*
* @param StoreStripeBillingRequest $request
* @return ResponseFactory|\Illuminate\Http\Response
*/
public function store_stripe_billings(StoreStripeBillingRequest $request)
{
// Check setup status
if ($this->get_setup_status()) abort(410, 'Gone');
// Get options
$settings = collect([
[
'name' => 'billing_phone_number',
'value' => $request->billing_phone_number,
],
[
'name' => 'billing_postal_code',
'value' => $request->billing_postal_code,
],
[
'name' => 'billing_vat_number',
'value' => $request->billing_vat_number,
],
[
'name' => 'billing_address',
'value' => $request->billing_address,
],
[
'name' => 'billing_country',
'value' => $request->billing_country,
],
[
'name' => 'billing_state',
'value' => $request->billing_state,
],
[
'name' => 'billing_city',
'value' => $request->billing_city,
],
[
'name' => 'billing_name',
'value' => $request->billing_name,
],
]);
// Store options
$settings->each(function ($col) {
Setting::updateOrCreate(['name' => $col['name']], $col);
});
// Clear cache
Artisan::call('config:cache');
return response('Done', 200);
}
/**
* Create Stripe subscription plan
*
* @param StoreStripePlansRequest $request
*/
public function store_stripe_plans(StoreStripePlansRequest $request)
{
// Check setup status
if ($this->get_setup_status()) abort(410, 'Gone');
foreach ($request->input('plans') as $plan) {
$this->stripe->createPlan($plan);
}
}
/**
* Store environment setup
*
* @param StoreEnvironmentSetupRequest $request
* @return string
*/
public function store_environment_setup(StoreEnvironmentSetupRequest $request)
{
// Check setup status
if ($this->get_setup_status()) abort(410, 'Gone');
$storage_driver = $request->input('storage.driver');
if ($storage_driver === 'local') {
setEnvironmentValue([
'FILESYSTEM_DRIVER' => 'local',
]);
}
if ($storage_driver === 's3') {
setEnvironmentValue([
'FILESYSTEM_DRIVER' => $request->input('storage.driver'),
'AWS_ACCESS_KEY_ID' => $request->input('storage.key'),
'AWS_SECRET_ACCESS_KEY' => $request->input('storage.secret'),
'AWS_DEFAULT_REGION' => $request->input('storage.region'),
'AWS_BUCKET' => $request->input('storage.bucket'),
]);
}
if ($storage_driver === 'spaces') {
setEnvironmentValue([
'FILESYSTEM_DRIVER' => $request->input('storage.driver'),
'DO_SPACES_KEY' => $request->input('storage.key'),
'DO_SPACES_SECRET' => $request->input('storage.secret'),
'DO_SPACES_ENDPOINT' => $request->input('storage.endpoint'),
'DO_SPACES_REGION' => $request->input('storage.region'),
'DO_SPACES_BUCKET' => $request->input('storage.bucket'),
]);
}
if ($storage_driver === 'wasabi') {
setEnvironmentValue([
'FILESYSTEM_DRIVER' => $request->input('storage.driver'),
'WASABI_KEY' => $request->input('storage.key'),
'WASABI_SECRET' => $request->input('storage.secret'),
'WASABI_ENDPOINT' => $request->input('storage.endpoint'),
'WASABI_REGION' => $request->input('storage.region'),
'WASABI_BUCKET' => $request->input('storage.bucket'),
]);
}
if ($storage_driver === 'backblaze') {
setEnvironmentValue([
'FILESYSTEM_DRIVER' => $request->input('storage.driver'),
'BACKBLAZE_KEY' => $request->input('storage.key'),
'BACKBLAZE_SECRET' => $request->input('storage.secret'),
'BACKBLAZE_ENDPOINT' => $request->input('storage.endpoint'),
'BACKBLAZE_REGION' => $request->input('storage.region'),
'BACKBLAZE_BUCKET' => $request->input('storage.bucket'),
]);
}
setEnvironmentValue([
'MAIL_DRIVER' => $request->input('mail.driver'),
'MAIL_HOST' => $request->input('mail.host'),
'MAIL_PORT' => $request->input('mail.port'),
'MAIL_USERNAME' => $request->input('mail.username'),
'MAIL_PASSWORD' => $request->input('mail.password'),
'MAIL_ENCRYPTION' => $request->input('mail.encryption'),
]);
// Clear cache
Artisan::call('config:cache');
return response('Done', 200);
}
/**
* Store app settings
* @param StoreAppSetupRequest $request
* @return ResponseFactory|\Illuminate\Http\Response
*/
public function store_app_settings(StoreAppSetupRequest $request)
{
// Check setup status
if ($this->get_setup_status()) abort(410, 'Gone');
// Store Logo
if ($request->hasFile('logo')) {
$logo = store_system_image($request->file('logo'), 'system');
}
// Store Logo horizontal
if ($request->hasFile('logo_horizontal')) {
$logo_horizontal = store_system_image($request->file('logo_horizontal'), 'system');
}
// Store favicon
if ($request->hasFile('favicon')) {
$favicon = store_system_image($request->file('favicon'), 'system');
}
// Get options
$settings = collect([
[
'name' => 'app_title',
'value' => $request->title,
],
[
'name' => 'app_description',
'value' => $request->description,
],
[
'name' => 'app_logo',
'value' => $request->hasFile('logo') ? $logo : null,
],
[
'name' => 'app_logo_horizontal',
'value' => $request->hasFile('logo_horizontal') ? $logo_horizontal : null,
],
[
'name' => 'app_favicon',
'value' => $request->hasFile('favicon') ? $favicon : null,
],
[
'name' => 'google_analytics',
'value' => $request->googleAnalytics,
],
[
'name' => 'contact_email',
'value' => $request->contactMail,
],
[
'name' => 'registration',
'value' => $request->userRegistration,
],
[
'name' => 'storage_limitation',
'value' => $request->storageLimitation,
],
[
'name' => 'storage_default',
'value' => $request->defaultStorage ? $request->defaultStorage : 5,
],
]);
// Store options
$settings->each(function ($col) {
Setting::updateOrCreate(['name' => $col['name']], $col);
});
setEnvironmentValue([
'APP_NAME' => Str::camel($request->title),
]);
return response('Done', 200);
}
/**
* Create and login admin account
*
* @param Request $request
* @return ResponseFactory|\Illuminate\Http\Response|\Symfony\Component\HttpFoundation\Response
*/
public function create_admin_account(Request $request)
{
// Check setup status
if ($this->get_setup_status()) abort(410, 'Gone');
// Validate request
$request->validate([
'email' => 'required|string|email|unique:users',
'password' => 'required|string|min:6|confirmed',
'name' => 'required|string',
'purchase_code' => 'required|string',
'license' => 'required|string',
'avatar' => 'sometimes|file',
]);
// Store avatar
if ($request->hasFile('avatar')) {
$avatar = store_avatar($request->file('avatar'), 'avatars');
}
// Create user
$user = User::forceCreate([
'avatar' => $request->hasFile('avatar') ? $avatar : null,
'name' => $request->name,
'role' => 'admin',
'email' => $request->email,
'password' => Hash::make($request->password),
]);
// Get default storage capacity
$storage_capacity = Setting::where('name', 'storage_default')->first();
// Create settings
UserSettings::forceCreate([
'user_id' => $user->id,
'storage_capacity' => $storage_capacity->value,
]);
// Store setup wizard progress
Setting::updateOrCreate([
'name' => 'setup_wizard_success',
'value' => 1,
]);
// Store License
Setting::updateOrCreate([
'name' => 'license',
'value' => $request->license,
]);
// Store Purchase Code
Setting::updateOrCreate([
'name' => 'purchase_code',
'value' => $request->purchase_code,
]);
// Create legal pages and index content
if ($request->license === 'Extended') {
$pages = collect(config('content.pages'));
$content = collect(config('content.content'));
$content->each(function ($content) {
Setting::updateOrCreate($content);
});
$pages->each(function ($page) {
Page::updateOrCreate($page);
});
}
// Retrieve access token
$response = Route::dispatch(self::make_login_request($request));
// Send access token to user if request is successful
if ($response->isSuccessful()) {
$data = json_decode($response->content(), true);
return response('Admin was created', 200)->cookie('access_token', $data['access_token'], 43200);
}
return $response;
}
/**
* Migrate database and generate necessary things
*/
private function set_up_application()
{
// Generate app key
Artisan::call('key:generate', [
'--force' => true
]);
// Migrate database
Artisan::call('migrate:fresh', [
'--force' => true
]);
// Create Passport Keys
Artisan::call('passport:keys', [
'--force' => true
]);
// Create Password grant client
Artisan::call('passport:client', [
'--password' => true,
'--name' => 'vuefilemanager',
]);
// Create Personal access client
Artisan::call('passport:client', [
'--personal' => true,
'--name' => 'shared',
]);
// Get generated client
$client = \DB::table('oauth_clients')->where('name', '=', 'vuefilemanager')->first();
// Set passport client to .env
setEnvironmentValue([
'PASSPORT_CLIENT_ID' => $client->id,
'PASSPORT_CLIENT_SECRET' => $client->secret,
]);
}
/**
* Make login request for get access token
*
* @param Request $request
* @return Request
*/
private static function make_login_request($request)
{
$request->request->add([
'grant_type' => 'password',
'client_id' => config('services.passport.client_id'),
'client_secret' => config('services.passport.client_secret'),
'username' => $request->email,
'password' => $request->password,
'scope' => 'master',
]);
return Request::create(url('/oauth/token'), 'POST', $request->all());
}
/**
* Get setup wizard status
*
* @return |null
*/
private function get_setup_status()
{
try {
// Check database connections
DB::getPdo();
// Get setup_wizard status
return Schema::hasTable('settings') ? Setting::where('name', 'setup_wizard_success')->first() : false;
} catch (PDOException $e) {
return false;
}
}
}

View File

@@ -0,0 +1,195 @@
<?php
namespace App\Http\Controllers\General;
use App\Http\Controllers\Controller;
use App\Page;
use App\Setting;
use Artisan;
use Illuminate\Http\Request;
use Schema;
class UpgradeAppController extends Controller
{
/**
* Upgrade account from 1.6 to 1.7
*
* @param Request $request
* @return \Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\Response
*/
public function upgrade(Request $request)
{
$upgraded = Setting::where('name', 'latest_upgrade')->first();
if ($upgraded && $upgraded->value === '1.7') abort(401);
// Create legal pages and index content
if ($request->license === 'Extended') {
$pages = collect(config('content.pages'));
$content = collect(config('content.content'));
$content->each(function ($content) {
Setting::updateOrCreate($content);
});
$pages->each(function ($page) {
Page::updateOrCreate($page);
});
}
// Store Logo
if ($request->hasFile('logo')) {
$logo = store_system_image($request->file('logo'), 'system');
}
// Store Logo horizontal
if ($request->hasFile('logo_horizontal')) {
$logo_horizontal = store_system_image($request->file('logo_horizontal'), 'system');
}
// Store favicon
if ($request->hasFile('favicon')) {
$favicon = store_system_image($request->file('favicon'), 'system');
}
// Get options
$settings = collect([
[
'name' => 'setup_wizard_database',
'value' => 1,
],
[
'name' => 'setup_wizard_success',
'value' => 1,
],
[
'name' => 'license',
'value' => $request->license,
],
[
'name' => 'purchase_code',
'value' => $request->purchase_code,
],
[
'name' => 'app_title',
'value' => $request->title,
],
[
'name' => 'app_description',
'value' => $request->description,
],
[
'name' => 'app_logo',
'value' => $request->hasFile('logo') ? $logo : null,
],
[
'name' => 'app_logo_horizontal',
'value' => $request->hasFile('logo_horizontal') ? $logo_horizontal : null,
],
[
'name' => 'app_favicon',
'value' => $request->hasFile('favicon') ? $favicon : null,
],
[
'name' => 'google_analytics',
'value' => $request->googleAnalytics,
],
[
'name' => 'contact_email',
'value' => $request->contactMail,
],
[
'name' => 'registration',
'value' => $request->userRegistration,
],
[
'name' => 'storage_limitation',
'value' => $request->storageLimitation,
],
[
'name' => 'storage_default',
'value' => $request->defaultStorage ? $request->defaultStorage : 5,
],
[
'name' => 'latest_upgrade',
'value' => '1.7',
],
]);
// Store options
$settings->each(function ($col) {
Setting::updateOrCreate(['name' => $col['name']], $col);
});
return response('Done', 200);
}
/**
* Start maintenance mode
*/
public function up() {
$command = Artisan::call('up');
if ($command === 0) {
echo 'System is in production mode';
}
}
/**
* End maintenance mode
*/
public function down() {
$command = Artisan::call('down');
if ($command === 0) {
echo 'System is in maintenance mode';
}
}
/**
* Upgrade database
*/
public function upgrade_database()
{
/*
* Upgrade expire_in in shares table
*
* @since v1.7.9
*/
if (! Schema::hasColumn('shares', 'expire_in')) {
$command = Artisan::call('migrate', [
'--force' => true
]);
if ($command === 0) {
echo 'Operation was successful.';
}
if ($command === 1) {
echo 'Operation failed.';
}
}
/*
* Upgrade expire_in in shares table
*
* @since v1.7.11
*/
if (! Schema::hasColumn('file_manager_files', 'metadata')) {
$command = Artisan::call('migrate', [
'--force' => true
]);
if ($command === 0) {
echo 'Operation was successful.';
}
if ($command === 1) {
echo 'Operation failed.';
}
}
}
}

View File

@@ -0,0 +1,156 @@
<?php
namespace App\Http\Controllers;
use App\Http\Tools\Demo;
use App\Setting;
use Artisan;
use Stripe;
use Cartalyst\Stripe\Exception\UnauthorizedException;
use Illuminate\Http\Request;
use Symfony\Component\HttpKernel\Exception\HttpException;
class SettingController extends Controller
{
/**
* Get table content
*
* @param Request $request
* @return mixed
*/
public function show(Request $request)
{
$column = $request->get('column');
if (strpos($column, '|') !== false) {
$columns = explode('|', $column);
return Setting::whereIn('name', $columns)->pluck('value', 'name');
}
return Setting::where('name', $column)->pluck('value', 'name');
}
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function update(Request $request)
{
// Check if is demo
if (env('APP_DEMO')) {
return Demo::response_204();
}
// Store image if exist
if ($request->hasFile($request->name)) {
// Store image
$image_path = store_system_image($request->file($request->name), 'system');
// Find and update image path
Setting::updateOrCreate(['name' => $request->name], [
'value' => $image_path
]);
return response('Done', 204);
}
// Find and update variable
Setting::updateOrCreate(['name' => $request->name], [
'value' => $request->value
]);
return response('Done', 204);
}
/**
* Set new email credentials to .env file
*
* @param Request $request
* @return \Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\Response
*/
public function set_email(Request $request)
{
// Check if is demo
if (env('APP_DEMO')) {
return Demo::response_204();
}
setEnvironmentValue([
'MAIL_DRIVER' => $request->input('driver'),
'MAIL_HOST' => $request->input('host'),
'MAIL_PORT' => $request->input('port'),
'MAIL_USERNAME' => $request->input('username'),
'MAIL_PASSWORD' => $request->input('password'),
'MAIL_ENCRYPTION' => $request->input('encryption'),
]);
// Clear config cache
Artisan::call('config:clear');
Artisan::call('config:cache');
return response('Done', 204);
}
/**
* Configure stripe additionally
*
* @param Request $request
*/
public function set_stripe(Request $request)
{
// Get stripe status
$is_stripe = get_setting('payments_configured');
// Check setup status
if ($is_stripe) abort(401, 'Gone');
// Create stripe instance
$stripe = Stripe::make($request->secret, '2020-03-02');
// Try to get stripe account details
try {
$stripe->account()->details();
} catch (UnauthorizedException $e) {
throw new HttpException(401, $e->getMessage());
}
// Get options
$settings = collect([
[
'name' => 'stripe_currency',
'value' => $request->currency,
],
[
'name' => 'payments_configured',
'value' => 1,
],
[
'name' => 'payments_active',
'value' => 1,
],
]);
// Store options
$settings->each(function ($col) {
Setting::updateOrCreate(['name' => $col['name']], $col);
});
// Set stripe credentials to .env
setEnvironmentValue([
'CASHIER_CURRENCY' => $request->currency,
'STRIPE_KEY' => $request->key,
'STRIPE_SECRET' => $request->secret,
'STRIPE_WEBHOOK_SECRET' => $request->webhookSecret,
]);
// Clear cache
Artisan::call('cache:clear');
Artisan::call('config:clear');
Artisan::call('config:cache');
}
}

View File

@@ -6,6 +6,8 @@ use App\Http\Controllers\Controller;
use App\Http\Requests\Share\AuthenticateShareRequest;
use App\Http\Resources\ShareResource;
use App\Http\Tools\Guardian;
use App\Setting;
use http\Env\Response;
use Illuminate\Contracts\View\Factory;
use Illuminate\Support\Facades\Cookie;
use Illuminate\Support\Facades\DB;
@@ -17,6 +19,7 @@ use App\FileManagerFolder;
use App\FileManagerFile;
use App\User;
use App\Share;
use Illuminate\Support\Facades\Storage;
class FileSharingController extends Controller
{
@@ -32,7 +35,7 @@ class FileSharingController extends Controller
$shared = Share::where(\DB::raw('BINARY `token`'), $token)
->first();
if (!$shared) {
if (! $shared) {
return view("index");
}
@@ -40,14 +43,63 @@ class FileSharingController extends Controller
Cookie::queue('shared_access_token', '', -1);
// Set cookies
if ($shared->protected) {
if ((int) $shared->protected) {
// Set shared token
Cookie::queue('shared_token', $token, 43200);
}
// Check if shared is image file and then show it
if ($shared->type === 'file' && ! (int) $shared->protected) {
$image = FileManagerFile::where('user_id', $shared->user_id)
->where('type', 'image')
->where('unique_id', $shared->item_id)
->first();
if ($image) {
// Store user download size
User::find($shared->user_id)->record_download((int) $image->getRawOriginal('filesize'));
return $this->show_image($image);
}
}
// Get all settings
$settings = Setting::all();
// Return page index
return view("index");
return view("index")
->with('settings', $settings ? json_decode($settings->pluck('value', 'name')->toJson()) : null);
}
/**
* Get image from storage and show it
*
* @param $file
* @return \Symfony\Component\HttpFoundation\StreamedResponse
*/
private function show_image($file)
{
// Format pretty filename
$file_pretty_name = $file->name . '.' . $file->mimetype;
// Get file path
$path = '/file-manager/' . $file->basename;
// Check if file exist
if (!Storage::exists($path)) abort(404);
$header = [
"Content-Type" => Storage::mimeType($path),
"Content-Length" => Storage::size($path),
"Accept-Ranges" => "bytes",
"Content-Range" => "bytes 0-600/" . Storage::size($path),
];
// Get file
return Storage::response($path, $file_pretty_name, $header);
}
/**
@@ -117,7 +169,7 @@ class FileSharingController extends Controller
$shared = Share::where(DB::raw('BINARY `token`'), $token)->firstOrFail();
// Abort if folder is protected
if ($shared->protected) {
if ((int) $shared->protected) {
abort(403, "Sorry, you don't have permission");
}
@@ -148,7 +200,7 @@ class FileSharingController extends Controller
$shared = Share::where(DB::raw('BINARY `token`'), $token)->firstOrFail();
// Abort if file is protected
if ($shared->protected) {
if ((int) $shared->protected) {
abort(403, "Sorry, you don't have permission");
}
@@ -199,6 +251,7 @@ class FileSharingController extends Controller
$folders = FileManagerFolder::with('folders:id,parent_id,unique_id,name')
->where('parent_id', $shared->item_id)
->where('user_id', $shared->user_id)
->sortable()
->get(['id', 'parent_id', 'unique_id', 'name']);
// Return folder tree
@@ -229,6 +282,7 @@ class FileSharingController extends Controller
$folders = FileManagerFolder::with('folders:id,parent_id,unique_id,name')
->where('parent_id', $shared->item_id)
->where('user_id', $shared->user_id)
->sortable()
->get(['id', 'parent_id', 'unique_id', 'name']);
// Return folder tree
@@ -298,7 +352,7 @@ class FileSharingController extends Controller
$shared = get_shared($token);
// Abort if folder is protected
if ($shared->protected) {
if ((int) $shared->protected) {
abort(403, "Sorry, you don't have permission");
}
@@ -351,10 +405,12 @@ class FileSharingController extends Controller
{
$folders = FileManagerFolder::where('user_id', $shared->user_id)
->where('parent_id', $unique_id)
->sortable()
->get();
$files = FileManagerFile::where('user_id', $shared->user_id)
->where('folder_id', $unique_id)
->sortable()
->get();
return [$folders, $files];

View File

@@ -2,6 +2,12 @@
namespace App\Http\Controllers\User;
use App\FileManagerFile;
use App\FileManagerFolder;
use App\Http\Resources\InvoiceCollection;
use App\Http\Resources\StorageDetailResource;
use App\Http\Resources\UserResource;
use App\Http\Resources\UserStorageResource;
use App\Http\Tools\Demo;
use Illuminate\Contracts\Routing\ResponseFactory;
use Illuminate\Support\Facades\Validator;
@@ -17,25 +23,36 @@ class AccountController extends Controller
/**
* Get all user data to frontend
*
* @return array
* @return UserResource
*/
public function user()
{
// Get User
$user = User::with(['favourites', 'latest_uploads'])
->where('id', Auth::id())
->first();
return new UserResource(
Auth::user()
);
}
return [
'user' => $user->only(['name', 'email', 'avatar']),
'favourites' => $user->favourites->makeHidden(['pivot']),
'latest_uploads' => $user->latest_uploads->makeHidden(['user_id', 'basename']),
'storage' => [
'used' => Metric::bytes($user->used_capacity)->format(),
'capacity' => format_gigabytes(config('vuefilemanager.user_storage_capacity')),
'percentage' => get_storage_fill_percentage($user->used_capacity, config('vuefilemanager.user_storage_capacity')),
],
];
/**
* Get storage details
*
* @return UserStorageResource
*/
public function storage()
{
return new UserStorageResource(
Auth::user()
);
}
/**
* Get user invoices
*
* @return InvoiceCollection
*/
public function invoices() {
return new InvoiceCollection(
Auth::user()->invoices()
);
}
/**
@@ -48,9 +65,9 @@ class AccountController extends Controller
{
// Validate request
$validator = Validator::make($request->all(), [
'avatar' => 'file',
'name' => 'string',
'value' => 'string',
'avatar' => 'file',
'name' => 'string',
'value' => 'string',
]);
// Return error
@@ -59,10 +76,12 @@ class AccountController extends Controller
// Get user
$user = Auth::user();
// Check if is demo
if (is_demo($user->id)) {
return Demo::response_204();
}
// Update data
if ($request->hasFile('avatar')) {
// Update avatar
@@ -80,6 +99,37 @@ class AccountController extends Controller
return response('Saved!', 204);
}
/**
* Update user settings relationship
*
* @param Request $request
* @return ResponseFactory|\Illuminate\Http\Response
*/
public function update_user_settings(Request $request)
{
// Validate request
$validator = Validator::make($request->all(), [
'name' => 'string',
'value' => 'string',
]);
// Return error
if ($validator->fails()) abort(400, 'Bad input');
// Get user
$user = Auth::user();
// Check if is demo
if (is_demo($user->id)) {
return Demo::response_204();
}
// Update text data
$user->settings->update(make_single_input($request));
return response('Saved!', 204);
}
/**
* Change user password
*

View File

@@ -0,0 +1,172 @@
<?php
namespace App\Http\Controllers\User;
use App\Http\Controllers\Controller;
use App\Http\Requests\Payments\RegisterNewPaymentMethodRequest;
use App\Http\Resources\PaymentCardCollection;
use App\Http\Resources\PaymentCardResource;
use App\Http\Resources\PaymentDefaultCardResource;
use App\Http\Tools\Demo;
use App\Services\StripeService;
use Auth;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
use Laravel\Cashier\PaymentMethod;
class PaymentMethodsController extends Controller
{
/**
* PaymentMethodsController constructor.
*/
public function __construct(StripeService $stripe)
{
$this->stripe = $stripe;
}
/**
* Get user payment methods grouped by default and others
*
* @return array
*/
public function index()
{
$user = Auth::user();
if (!$user->hasPaymentMethod()) {
return abort(204, 'User don\'t have any payment methods');
}
$slug_payment_methods = 'payment-methods-user-' . $user->id;
$slug_default_payment_method = 'default-payment-methods-user-' . $user->id;
if (Cache::has($slug_payment_methods) && Cache::has($slug_default_payment_method)) {
$defaultPaymentMethod = Cache::get($slug_default_payment_method);
$paymentMethodsMapped = Cache::get($slug_payment_methods);
} else {
// Get default payment method
$defaultPaymentMethod = Cache::rememberForever($slug_default_payment_method, function () use ($user) {
$defaultPaymentMethodObject = $user->defaultPaymentMethod();
return $defaultPaymentMethodObject instanceof PaymentMethod
? $defaultPaymentMethodObject->asStripePaymentMethod()
: $defaultPaymentMethodObject;
});
// filter payment methods without default payment
$paymentMethodsMapped = Cache::rememberForever($slug_payment_methods, function () use ($defaultPaymentMethod, $user) {
$paymentMethods = $user->paymentMethods()->filter(function ($paymentMethod) use ($defaultPaymentMethod) {
return $paymentMethod->id !== $defaultPaymentMethod->id;
});
// Get payment methods
return $paymentMethods->map(function ($paymentMethod) {
return $paymentMethod->asStripePaymentMethod();
})->values()->all();
});
}
if (!$user->card_brand || !$user->stripe_id || is_null($paymentMethodsMapped) && is_null($paymentMethodsMapped)) {
return [
'default' => null,
'others' => [],
];
}
return [
'default' => $defaultPaymentMethod instanceof PaymentMethod
? new PaymentCardResource($defaultPaymentMethod)
: new PaymentDefaultCardResource($defaultPaymentMethod),
'others' => new PaymentCardCollection($paymentMethodsMapped),
];
}
/**
* Update default payment method
*
* @param Request $request
* @param $id
* @return \Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\Response
*/
public function update($id)
{
$user = Auth::user();
// Check if is demo
if (is_demo($user->id)) {
return Demo::response_204();
}
// Update DefaultPayment Method
$user->updateDefaultPaymentMethod($id);
// Sync default payment method
$user->updateDefaultPaymentMethodFromStripe();
// Clear cached payment methods
cache_forget_many([
'payment-methods-user-' . $user->id,
'default-payment-methods-user-' . $user->id
]);
return response('Done', 204);
}
/**
* Register new payment method for user
*
* @param Request $request
* @return \Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\Response
*/
public function store(RegisterNewPaymentMethodRequest $request)
{
// Get user
$user = Auth::user();
// Check if is demo
if (is_demo($user->id)) {
return response('Done', 201);
}
// Register new payment method
$this->stripe->registerNewPaymentMethod($request, $user);
return response('Done', 201);
}
/**
* Delete user payment method
*
*/
public function delete($id)
{
$user = Auth::user();
// Check if is demo
if (is_demo($user->id)) {
return Demo::response_204();
}
// Get payment method
$paymentMethod = $user->findPaymentMethod($id);
// Delete payment method
$paymentMethod->delete();
// Sync default payment method
$user->updateDefaultPaymentMethodFromStripe();
// Clear cached payment methods
cache_forget_many([
'payment-methods-user-' . $user->id,
'default-payment-methods-user-' . $user->id
]);
return response('Done!', 204);
}
}

View File

@@ -0,0 +1,154 @@
<?php
namespace App\Http\Controllers\User;
use App\Http\Controllers\Controller;
use App\Http\Requests\Subscription\StoreUpgradeAccountRequest;
use App\Http\Resources\UserSubscription;
use App\Http\Tools\Demo;
use App\Invoice;
use App\Services\StripeService;
use Auth;
use Cartalyst\Stripe\Exception\CardErrorException;
use Illuminate\Contracts\Routing\ResponseFactory;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
use Laravel\Cashier\Exceptions\IncompletePayment;
use Symfony\Component\HttpKernel\Exception\HttpException;
class SubscriptionController extends Controller
{
private $stripe;
/**
* SubscriptionController constructor.
* @param $payment
*/
public function __construct(StripeService $stripe)
{
$this->stripe = $stripe;
}
/**
* Generate setup intent
*
* @return \Stripe\SetupIntent
*/
public function stripe_setup_intent()
{
$user = Auth::user();
return $this->stripe->getSetupIntent($user);
}
/**
* Get user subscription detail
*
* @return array
*/
public function show()
{
$user = Auth::user();
if (! $user->subscription('main')) {
return abort(204, 'User don\'t have any subscription');
}
$slug_user_subscription = 'subscription-user-' . $user->id;
if (Cache::has($slug_user_subscription)) {
return Cache::get($slug_user_subscription);
}
return Cache::rememberForever($slug_user_subscription, function () {
return new UserSubscription(
Auth::user()
);
});
}
/**
* Upgrade account to subscription
*
* @param StoreUpgradeAccountRequest $request
* @return ResponseFactory|\Illuminate\Http\Response
*/
public function upgrade(StoreUpgradeAccountRequest $request)
{
// Get user
$user = Auth::user();
// Check if is demo
if (is_demo($user->id)) {
return Demo::response_204();
}
// Forget user subscription
Cache::forget('subscription-user-' . $user->id);
// Get requested plan
$plan = $this->stripe->getPlan($request->input('plan.data.id'));
// Set user billing
$user->setBilling($request->input('billing'));
// Update stripe customer billing info
$this->stripe->updateCustomerDetails($user);
// Make subscription
$this->stripe->createOrReplaceSubscription($request, $user);
// Update user storage limit
$user->settings()->update([
'storage_capacity' => $plan['product']['metadata']['capacity']
]);
return response('Done!', 204);
}
/**
* Cancel Subscription
*
* @return ResponseFactory|\Illuminate\Http\Response
*/
public function cancel()
{
$user = Auth::user();
// Check if is demo
if (is_demo($user->id)) {
return Demo::response_204();
}
// Cancel subscription
$user->subscription('main')->cancel();
// Forget user subscription
Cache::forget('subscription-user-' . $user->id);
return response('Done!', 204);
}
/**
* Resume Subscription
*
* @return ResponseFactory|\Illuminate\Http\Response
*/
public function resume()
{
$user = Auth::user();
// Check if is demo
if (is_demo($user->id)) {
return Demo::response_204();
}
// Resume subscription
$user->subscription('main')->resume();
// Forget user subscription
Cache::forget('subscription-user-' . $user->id);
return response('Done!', 204);
}
}

View File

@@ -0,0 +1,68 @@
<?php
namespace App\Http\Controllers;
use App\Services\StripeService;
use App\Setting;
use App\User;
use Illuminate\Http\Request;
use Laravel\Cashier\Http\Controllers\WebhookController as CashierController;
class WebhookController extends CashierController
{
public function __construct(StripeService $stripe)
{
$this->stripe = $stripe;
}
/**
* Handle a cancelled customer from a Stripe subscription.
*
* @param array $payload
* @return \Symfony\Component\HttpFoundation\Response
*/
public function handleCustomerSubscriptionDeleted($payload)
{
if ($user = $this->getUserByStripeId($payload['data']['object']['customer'])) {
$user->subscriptions->filter(function ($subscription) use ($payload) {
return $subscription->stripe_id === $payload['data']['object']['id'];
})->each(function ($subscription) {
$subscription->markAsCancelled();
});
}
// Get user
$user = User::where('stripe_id', $payload['data']['object']['customer'])->firstOrFail();
// Get default storage capacity
$default_storage = Setting::where('name', 'storage_default')->first();
// Update storage capacity
$user->settings()->update(['storage_capacity' => $default_storage->value]);
return $this->successMethod();
}
/**
* Handle Invoice Payment Succeeded
*
* @param $payload
* @return \Symfony\Component\HttpFoundation\Response
*/
public function handleInvoicePaymentSucceeded($payload)
{
// Get user
$user = User::where('stripe_id', $payload['data']['object']['customer'])->firstOrFail();
// Get requested plan
$plan = $this->stripe->getPlan($user->subscription('main')->stripe_plan);
// Update user storage limit
$user->settings()->update([
'storage_capacity' => $plan['product']['metadata']['capacity']
]);
return $this->successMethod();
}
}

View File

@@ -0,0 +1,735 @@
<?php
use App\FileManagerFile;
use App\FileManagerFolder;
use App\Setting;
use App\Share;
use ByteUnits\Metric;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use Intervention\Image\ImageManagerStatic as Image;
/**
* Obfuscate email
*
* @param $email
* @return string
*/
function obfuscate_email($email)
{
$em = explode("@", $email);
$name = implode('@', array_slice($em, 0, count($em) - 1));
$len = floor(strlen($name) / 2);
return substr($name, 0, $len) . str_repeat('*', $len) . "@" . end($em);
}
/**
* Get single value from settings table
*
* @param $setting
* @return |null
*/
function get_setting($setting)
{
$row = Setting::where('name', $setting)->first();
return $row ? $row->value : null;
}
/**
* Create paragraph from text
*
* @param $str
* @return mixed|null|string|string[]
*/
function add_paragraphs($str)
{
// Trim whitespace
if (($str = trim($str)) === '') return '';
// Standardize newlines
$str = str_replace(array("\r\n", "\r"), "\n", $str);
// Trim whitespace on each line
$str = preg_replace('~^[ \t]+~m', '', $str);
$str = preg_replace('~[ \t]+$~m', '', $str);
// The following regexes only need to be executed if the string contains html
if ($html_found = (strpos($str, '<') !== FALSE)) {
// Elements that should not be surrounded by p tags
$no_p = '(?:p|div|article|header|aside|hgroup|canvas|output|progress|section|figcaption|audio|video|nav|figure|footer|video|details|main|menu|summary|h[1-6r]|ul|ol|li|blockquote|d[dlt]|pre|t[dhr]|t(?:able|body|foot|head)|c(?:aption|olgroup)|form|s(?:elect|tyle)|a(?:ddress|rea)|ma(?:p|th))';
// Put at least two linebreaks before and after $no_p elements
$str = preg_replace('~^<' . $no_p . '[^>]*+>~im', "\n$0", $str);
$str = preg_replace('~</' . $no_p . '\s*+>$~im', "$0\n", $str);
}
// Do the <p> magic!
$str = '<p>' . trim($str) . '</p>';
$str = preg_replace('~\n{2,}~', "</p>\n\n<p>", $str);
// The following regexes only need to be executed if the string contains html
if ($html_found !== FALSE) {
// Remove p tags around $no_p elements
$str = preg_replace('~<p>(?=</?' . $no_p . '[^>]*+>)~i', '', $str);
$str = preg_replace('~(</?' . $no_p . '[^>]*+>)</p>~i', '$1', $str);
}
// Convert single linebreaks to <br />
$str = preg_replace('~(?<!\n)\n(?!\n)~', "<br>\n", $str);
return $str;
}
/**
* Set environment value
*
* @param $key
* @param $value
* @return bool
*/
function setEnvironmentValue(array $values)
{
$envFile = app()->environmentFilePath();
$str = file_get_contents($envFile);
if (count($values) > 0) {
foreach ($values as $envKey => $envValue) {
$str .= "\n"; // In case the searched variable is in the last line without \n
$keyPosition = strpos($str, "{$envKey}=");
$endOfLinePosition = strpos($str, "\n", $keyPosition);
$oldLine = substr($str, $keyPosition, $endOfLinePosition - $keyPosition);
// If key does not exist, add it
$str = str_replace($oldLine, "{$envKey}={$envValue}", $str);
}
}
$str = substr($str, 0, -1);
if (!file_put_contents($envFile, $str)) return false;
return true;
}
/**
* Get invoice number
*
* @return string
*/
function get_invoice_number()
{
$invoices = \App\Invoice::all();
if ($invoices->isEmpty()) {
return Carbon::now()->year . '001';
} else {
return (int)$invoices->last()->order + 1;
}
}
/**
* Forget many cache keys at once
* @param $cache
*/
function cache_forget_many($cache)
{
foreach ($cache as $item) {
\Illuminate\Support\Facades\Cache::forget($item);
}
}
/**
* Get app version from config
*
* @return \Illuminate\Config\Repository|mixed
*/
function get_storage()
{
return env('FILESYSTEM_DRIVER');
}
/**
* Check if is running AWS s3 as storage
*
* @return bool
*/
function is_storage_driver($driver)
{
if (is_array($driver)) {
return in_array(config('filesystems.default'), $driver);
}
return config('filesystems.default') === $driver;
}
/**
* Get app version from config
*
* @return \Illuminate\Config\Repository|mixed
*/
function get_version()
{
return config('vuefilemanager.version');
}
/**
* Check if is demo
*
* @return mixed
*/
function is_demo($user_id)
{
return env('APP_DEMO', false) && $user_id === 1;
}
/**
* Get folder or file item
*
* @param $type
* @param $unique_id
* @param $user_id
* @return \Illuminate\Database\Eloquent\Builder|Model
*/
function get_item($type, $unique_id, $user_id)
{
if ($type === 'folder') {
// Return folder item
return FileManagerFolder::where('unique_id', $unique_id)
->where('user_id', $user_id)
->firstOrFail();
}
// Return file item
return FileManagerFile::where('unique_id', $unique_id)
->where('user_id', $user_id)
->firstOrFail();
}
/**
* Get shared token
*
* @param $token
* @return \Illuminate\Database\Eloquent\Builder|Model
*/
function get_shared($token)
{
return Share::where(DB::raw('BINARY `token`'), $token)
->firstOrFail();
}
/**
* Check if shared permission is editor
*
* @param $shared
* @return bool
*/
function is_editor($shared)
{
return $shared->permission === 'editor';
}
/**
* Get unique id
*
* @return int
*/
function get_unique_id(): int
{
// Get files and folders
$folders = FileManagerFolder::withTrashed()->get();
$files = FileManagerFile::withTrashed()->get();
// Get last ids
$folders_unique = $folders->isEmpty() ? 0 : $folders->last()->unique_id;
$files_unique = $files->isEmpty() ? 0 : $files->last()->unique_id;
// Count new unique id
$unique_id = $folders_unique > $files_unique ? $folders_unique + 1 : $files_unique + 1;
return $unique_id;
}
/**
* Store user avatar to storage
*
* @param $image
* @param $path
* @return string
*/
function store_avatar($image, $path)
{
// Get directory
$path = check_directory($path);
// Store avatar
$image_path = Str::random(8) . '-' . $image->getClientOriginalName();
// Create intervention image
$img = Image::make($image->getRealPath());
// Generate thumbnail
$img->fit('150', '150')->stream();
// Store thumbnail to disk
Storage::put($path . '/' . $image_path, $img);
// Return path to image
return $path . '/' . $image_path;
}
/**
* Store system image
*
* @param $image
* @param $path
* @return string
*/
function store_system_image($image, $path)
{
// Get directory
$path = check_directory($path);
// Store avatar
$image_path = Str::random(8) . '-' . str_replace(' ', '', $image->getClientOriginalName());
// Store image to disk
Storage::putFileAs($path, $image, $image_path);
// Return path to image
return $path . '/' . $image_path;
}
/**
* Check if directory exist, if no, then create it
*
* @param $directory
* @return mixed
*/
function check_directory($directory)
{
if (!Storage::exists($directory)) {
Storage::makeDirectory($directory);
}
return $directory;
}
/**
* Make input from request
*
* @param $request
* @return array
*/
function make_single_input($request)
{
// Create container
$data = [];
// Add data to array
$data[$request->name] = $request->value;
// Return input
return $data;
}
/**
* Format integer to gigabytes
*
* @param $gigabytes
* @return string
*/
function format_gigabytes($gigabytes)
{
if ($gigabytes >= 1000) {
return Metric::gigabytes($gigabytes)->format('Tb/');
} else {
return Metric::gigabytes($gigabytes)->format('GB/');
}
}
/**
* Format string to formated megabytes string
*
* @param $megabytes
* @return string
*/
function format_megabytes($megabytes)
{
if ($megabytes >= 1000) {
return $megabytes / 1000 . 'GB';
}
if ($megabytes >= 1000000) {
return $megabytes / 1000000 . 'TB';
}
return $megabytes . 'MB';
}
/**
* Convert megabytes to bytes
*
* @param $megabytes
* @return int|string
*/
function format_bytes($megabytes)
{
return Metric::megabytes($megabytes)->numberOfBytes();
}
/**
* Get storage usage in percent
*
* @param $used
* @param $capacity
* @return string
*/
function get_storage_fill_percentage($used, $capacity)
{
// Format gigabytes to bytes
$total = intval(Metric::gigabytes($capacity)->numberOfBytes());
// Count progress
$progress = ($used * 100) / $total;
// Return in 2 decimal
return number_format((float)$progress, 2, '.', '');
}
/**
* Get user capacity fill by percentage
*
* @return string
*/
function user_storage_percentage($id, $additionals = null)
{
$user = \App\User::findOrFail($id);
$used = $user->used_capacity;
if ($additionals) {
$used = $user->used_capacity + $additionals;
}
return get_storage_fill_percentage($used, $user->settings->storage_capacity);
}
/**
* Find all key values in recursive array
*
* @param array $array
* @param $needle
* @return array
*/
function recursiveFind(array $array, $needle)
{
$iterator = new RecursiveArrayIterator($array);
$recursive = new RecursiveIteratorIterator($iterator, RecursiveIteratorIterator::SELF_FIRST);
$aHitList = array();
foreach ($recursive as $key => $value) {
if ($key === $needle) {
array_push($aHitList, $value);
}
}
return $aHitList;
}
/**
* Get values which appears only once in array
* @param $arr
* @return array
*/
function appeared_once($arr)
{
$array_count_values = array_count_values($arr);
$single_time_comming_values_array = [];
foreach ($array_count_values as $key => $val) {
if ($val == 1) {
$single_time_comming_values_array[] = $key;
}
}
return $single_time_comming_values_array;
}
/**
* @param $folders
* @return array
*/
function filter_folders_ids($folders, $by_column = 'unique_id')
{
$folder_unique_ids = recursiveFind($folders->toArray(), $by_column);
return appeared_once($folder_unique_ids);
}
/**
* Format localized date
*
* @param $date
* @param string $format
* @return string
*/
function format_date($date, $format = '%d. %B. %Y, %H:%M')
{
$start = Carbon::parse($date);
return $start->formatLocalized($format);
}
/**
* Get file type from mimetype
*
* @param $file
* @return string
*/
function get_file_type($file_mimetype)
{
// Get mimetype from file
$mimetype = explode('/', $file_mimetype);
switch ($mimetype[0]) {
case 'image':
return 'image';
break;
case 'video':
return 'video';
break;
case 'audio':
return 'audio';
break;
default:
return 'file';
}
}
/**
* Get file type from mimetype
*
* @param $mimetype
* @return mixed
*/
function get_file_type_from_mimetype($mimetype)
{
return explode('/', $mimetype)[1];
}
/**
* Format pretty name file
*
* @param $basename
* @param $name
* @param $mimetype
* @return string
*/
function get_pretty_name($basename, $name, $mimetype)
{
$file_extension = substr(strrchr($basename, '.'), 1);
if (strpos($name, $file_extension) !== false) {
return $name;
}
if ($file_extension) {
return $name . '.' . $file_extension;
}
return $name . '.' . $mimetype;
}
/**
* Get exif data from jpeg image
*
* @param $file
* @return array
*/
function get_image_meta_data($file)
{
if (get_file_type_from_mimetype($file->getMimeType()) === 'jpeg') {
return exif_read_data($file);
}
}
/**
* Check if app is in dev mode
*
* @return bool
*/
function is_dev()
{
return env('APP_ENV') === 'local' ? true : false;
}
/**
* @param $str
* @return bool
*/
function seems_utf8($str)
{
$length = strlen($str);
for ($i=0; $i < $length; $i++) {
$c = ord($str[$i]);
if ($c < 0x80) $n = 0; # 0bbbbbbb
elseif (($c & 0xE0) == 0xC0) $n=1; # 110bbbbb
elseif (($c & 0xF0) == 0xE0) $n=2; # 1110bbbb
elseif (($c & 0xF8) == 0xF0) $n=3; # 11110bbb
elseif (($c & 0xFC) == 0xF8) $n=4; # 111110bb
elseif (($c & 0xFE) == 0xFC) $n=5; # 1111110b
else return false; # Does not match any model
for ($j=0; $j<$n; $j++) { # n bytes matching 10bbbbbb follow ?
if ((++$i == $length) || ((ord($str[$i]) & 0xC0) != 0x80))
return false;
}
}
return true;
}
/**
* Converts all accent characters to ASCII characters.
*
* If there are no accent characters, then the string given is just returned.
*
* @param string $string Text that might have accent characters
* @return string Filtered string with replaced "nice" characters.
*/
function remove_accents($string) {
if ( !preg_match('/[\x80-\xff]/', $string) )
return $string;
if (seems_utf8($string)) {
$chars = array(
// Decompositions for Latin-1 Supplement
chr(195).chr(128) => 'A', chr(195).chr(129) => 'A',
chr(195).chr(130) => 'A', chr(195).chr(131) => 'A',
chr(195).chr(132) => 'A', chr(195).chr(133) => 'A',
chr(195).chr(135) => 'C', chr(195).chr(136) => 'E',
chr(195).chr(137) => 'E', chr(195).chr(138) => 'E',
chr(195).chr(139) => 'E', chr(195).chr(140) => 'I',
chr(195).chr(141) => 'I', chr(195).chr(142) => 'I',
chr(195).chr(143) => 'I', chr(195).chr(145) => 'N',
chr(195).chr(146) => 'O', chr(195).chr(147) => 'O',
chr(195).chr(148) => 'O', chr(195).chr(149) => 'O',
chr(195).chr(150) => 'O', chr(195).chr(153) => 'U',
chr(195).chr(154) => 'U', chr(195).chr(155) => 'U',
chr(195).chr(156) => 'U', chr(195).chr(157) => 'Y',
chr(195).chr(159) => 's', chr(195).chr(160) => 'a',
chr(195).chr(161) => 'a', chr(195).chr(162) => 'a',
chr(195).chr(163) => 'a', chr(195).chr(164) => 'a',
chr(195).chr(165) => 'a', chr(195).chr(167) => 'c',
chr(195).chr(168) => 'e', chr(195).chr(169) => 'e',
chr(195).chr(170) => 'e', chr(195).chr(171) => 'e',
chr(195).chr(172) => 'i', chr(195).chr(173) => 'i',
chr(195).chr(174) => 'i', chr(195).chr(175) => 'i',
chr(195).chr(177) => 'n', chr(195).chr(178) => 'o',
chr(195).chr(179) => 'o', chr(195).chr(180) => 'o',
chr(195).chr(181) => 'o', chr(195).chr(182) => 'o',
chr(195).chr(182) => 'o', chr(195).chr(185) => 'u',
chr(195).chr(186) => 'u', chr(195).chr(187) => 'u',
chr(195).chr(188) => 'u', chr(195).chr(189) => 'y',
chr(195).chr(191) => 'y',
// Decompositions for Latin Extended-A
chr(196).chr(128) => 'A', chr(196).chr(129) => 'a',
chr(196).chr(130) => 'A', chr(196).chr(131) => 'a',
chr(196).chr(132) => 'A', chr(196).chr(133) => 'a',
chr(196).chr(134) => 'C', chr(196).chr(135) => 'c',
chr(196).chr(136) => 'C', chr(196).chr(137) => 'c',
chr(196).chr(138) => 'C', chr(196).chr(139) => 'c',
chr(196).chr(140) => 'C', chr(196).chr(141) => 'c',
chr(196).chr(142) => 'D', chr(196).chr(143) => 'd',
chr(196).chr(144) => 'D', chr(196).chr(145) => 'd',
chr(196).chr(146) => 'E', chr(196).chr(147) => 'e',
chr(196).chr(148) => 'E', chr(196).chr(149) => 'e',
chr(196).chr(150) => 'E', chr(196).chr(151) => 'e',
chr(196).chr(152) => 'E', chr(196).chr(153) => 'e',
chr(196).chr(154) => 'E', chr(196).chr(155) => 'e',
chr(196).chr(156) => 'G', chr(196).chr(157) => 'g',
chr(196).chr(158) => 'G', chr(196).chr(159) => 'g',
chr(196).chr(160) => 'G', chr(196).chr(161) => 'g',
chr(196).chr(162) => 'G', chr(196).chr(163) => 'g',
chr(196).chr(164) => 'H', chr(196).chr(165) => 'h',
chr(196).chr(166) => 'H', chr(196).chr(167) => 'h',
chr(196).chr(168) => 'I', chr(196).chr(169) => 'i',
chr(196).chr(170) => 'I', chr(196).chr(171) => 'i',
chr(196).chr(172) => 'I', chr(196).chr(173) => 'i',
chr(196).chr(174) => 'I', chr(196).chr(175) => 'i',
chr(196).chr(176) => 'I', chr(196).chr(177) => 'i',
chr(196).chr(178) => 'IJ',chr(196).chr(179) => 'ij',
chr(196).chr(180) => 'J', chr(196).chr(181) => 'j',
chr(196).chr(182) => 'K', chr(196).chr(183) => 'k',
chr(196).chr(184) => 'k', chr(196).chr(185) => 'L',
chr(196).chr(186) => 'l', chr(196).chr(187) => 'L',
chr(196).chr(188) => 'l', chr(196).chr(189) => 'L',
chr(196).chr(190) => 'l', chr(196).chr(191) => 'L',
chr(197).chr(128) => 'l', chr(197).chr(129) => 'L',
chr(197).chr(130) => 'l', chr(197).chr(131) => 'N',
chr(197).chr(132) => 'n', chr(197).chr(133) => 'N',
chr(197).chr(134) => 'n', chr(197).chr(135) => 'N',
chr(197).chr(136) => 'n', chr(197).chr(137) => 'N',
chr(197).chr(138) => 'n', chr(197).chr(139) => 'N',
chr(197).chr(140) => 'O', chr(197).chr(141) => 'o',
chr(197).chr(142) => 'O', chr(197).chr(143) => 'o',
chr(197).chr(144) => 'O', chr(197).chr(145) => 'o',
chr(197).chr(146) => 'OE',chr(197).chr(147) => 'oe',
chr(197).chr(148) => 'R',chr(197).chr(149) => 'r',
chr(197).chr(150) => 'R',chr(197).chr(151) => 'r',
chr(197).chr(152) => 'R',chr(197).chr(153) => 'r',
chr(197).chr(154) => 'S',chr(197).chr(155) => 's',
chr(197).chr(156) => 'S',chr(197).chr(157) => 's',
chr(197).chr(158) => 'S',chr(197).chr(159) => 's',
chr(197).chr(160) => 'S', chr(197).chr(161) => 's',
chr(197).chr(162) => 'T', chr(197).chr(163) => 't',
chr(197).chr(164) => 'T', chr(197).chr(165) => 't',
chr(197).chr(166) => 'T', chr(197).chr(167) => 't',
chr(197).chr(168) => 'U', chr(197).chr(169) => 'u',
chr(197).chr(170) => 'U', chr(197).chr(171) => 'u',
chr(197).chr(172) => 'U', chr(197).chr(173) => 'u',
chr(197).chr(174) => 'U', chr(197).chr(175) => 'u',
chr(197).chr(176) => 'U', chr(197).chr(177) => 'u',
chr(197).chr(178) => 'U', chr(197).chr(179) => 'u',
chr(197).chr(180) => 'W', chr(197).chr(181) => 'w',
chr(197).chr(182) => 'Y', chr(197).chr(183) => 'y',
chr(197).chr(184) => 'Y', chr(197).chr(185) => 'Z',
chr(197).chr(186) => 'z', chr(197).chr(187) => 'Z',
chr(197).chr(188) => 'z', chr(197).chr(189) => 'Z',
chr(197).chr(190) => 'z', chr(197).chr(191) => 's',
// Euro Sign
chr(226).chr(130).chr(172) => 'E',
// GBP (Pound) Sign
chr(194).chr(163) => '');
$string = strtr($string, $chars);
} else {
// Assume ISO-8859-1 if not UTF-8
$chars['in'] = chr(128).chr(131).chr(138).chr(142).chr(154).chr(158)
.chr(159).chr(162).chr(165).chr(181).chr(192).chr(193).chr(194)
.chr(195).chr(196).chr(197).chr(199).chr(200).chr(201).chr(202)
.chr(203).chr(204).chr(205).chr(206).chr(207).chr(209).chr(210)
.chr(211).chr(212).chr(213).chr(214).chr(216).chr(217).chr(218)
.chr(219).chr(220).chr(221).chr(224).chr(225).chr(226).chr(227)
.chr(228).chr(229).chr(231).chr(232).chr(233).chr(234).chr(235)
.chr(236).chr(237).chr(238).chr(239).chr(241).chr(242).chr(243)
.chr(244).chr(245).chr(246).chr(248).chr(249).chr(250).chr(251)
.chr(252).chr(253).chr(255);
$chars['out'] = "EfSZszYcYuAAAAAACEEEEIIIINOOOOOOUUUUYaaaaaaceeeeiiiinoooooouuuuyy";
$string = strtr($string, $chars['in'], $chars['out']);
$double_chars['in'] = array(chr(140), chr(156), chr(198), chr(208), chr(222), chr(223), chr(230), chr(240), chr(254));
$double_chars['out'] = array('OE', 'oe', 'AE', 'DH', 'TH', 'ss', 'ae', 'dh', 'th');
$string = str_replace($double_chars['in'], $double_chars['out'], $string);
}
return $string;
}

View File

@@ -0,0 +1,23 @@
<?php
/**
* Check if current user subscribed plan is highest
*
* @param $id
* @param $subscribed_capacity
* @return int
*/
function is_highest_plan($plan)
{
$plans = app('rinvex.subscriptions.plan')->all();
$unsubscribed = $plans->filter(function ($item) use ($plan) {
return $item->id !== $plan->id;
});
$capacities = $unsubscribed->map(function ($item) {
return $item->features->first()->value;
});
return max(Arr::flatten($capacities)) < $plan->features->first()->value ? 1 : 0;
}

View File

@@ -2,6 +2,7 @@
namespace App\Http;
use App\Http\Middleware\AdminCheck;
use App\Http\Middleware\CookieAuth;
use App\Http\Middleware\LastCheck;
use App\Http\Middleware\SharedAuth;
@@ -58,6 +59,7 @@ class Kernel extends HttpKernel
protected $routeMiddleware = [
'auth.master' => CookieAuth::class,
'auth.shared' => SharedAuth::class,
'auth.admin' => AdminCheck::class,
'auth' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,

View File

@@ -0,0 +1,40 @@
<?php
namespace App\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
class SendSupportForm extends Mailable
{
use Queueable, SerializesModels;
private $request;
/**
* Create a new message instance.
*
* @return void
*/
public function __construct($request)
{
$this->request = $request;
}
/**
* Build the message.
*
* @return $this
*/
public function build()
{
$from = config('mail.from')['address'];
return $this->from($from)
->replyTo($this->request['email'])
->subject('New Contact Message from ' . $this->request['email'])
->view('mails.contact-message')
->with('request', $this->request);
}
}

View File

@@ -0,0 +1,26 @@
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\Gate;
class AdminCheck
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
// Check if user have access to administration settings
if ( ! Gate::allows('admin-settings')) {
abort(403, 'You don\'t have access for this operation!');
}
return $next($request);
}
}

View File

@@ -12,6 +12,8 @@ class CheckForMaintenanceMode extends Middleware
* @var array
*/
protected $except = [
//
'/service/upgrade-database',
'/service/down',
'/service/up',
];
}

View File

@@ -19,6 +19,7 @@ class VerifyCsrfToken extends Middleware
* @var array
*/
protected $except = [
//
'/deploy',
'/stripe/*',
];
}

View File

@@ -0,0 +1,68 @@
<?php
namespace App\Notifications;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;
use Laravel\Cashier\Payment;
class ConfirmPayment extends Notification implements ShouldQueue
{
use Queueable;
/**
* The PaymentIntent identifier.
*
* @var string
*/
public $paymentId;
/**
* The payment amount.
*
* @var string
*/
public $amount;
/**
* Create a new payment confirmation notification.
*
* @param \Laravel\Cashier\Payment $payment
* @return void
*/
public function __construct(Payment $payment)
{
$this->paymentId = $payment->id;
$this->amount = $payment->amount();
}
/**
* Get the notification's delivery channels.
*
* @param mixed $notifiable
* @return array
*/
public function via($notifiable)
{
return ['mail'];
}
/**
* Get the mail representation of the notification.
*
* @param mixed $notifiable
* @return \Illuminate\Notifications\Messages\MailMessage
*/
public function toMail($notifiable)
{
$url = route('cashier.payment', ['id' => $this->paymentId]);
return (new MailMessage)
->subject(__('cashier.confirm_payment'))
->greeting(__('cashier.confirm_amount', ['amount' => $this->amount]))
->line(__('cashier.confirm_description'))
->action(__('cashier.confirm_button'), $url);
}
}

View File

@@ -0,0 +1,31 @@
<?php
namespace App\Http\Requests\Admin;
use Illuminate\Foundation\Http\FormRequest;
class ChangeRoleRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'attributes' => 'required|array',
'attributes.role' => 'required|string'
];
}
}

View File

@@ -0,0 +1,31 @@
<?php
namespace App\Http\Requests\Admin;
use Illuminate\Foundation\Http\FormRequest;
class ChangeStorageCapacityRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'attributes' => 'required|array',
'attributes.storage_capacity' => 'required|digits_between:1,9'
];
}
}

View File

@@ -0,0 +1,35 @@
<?php
namespace App\Http\Requests\Admin;
use Illuminate\Foundation\Http\FormRequest;
class CreateUserByAdmin extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'email' => 'required|string|email|max:255|unique:users',
'password' => 'required|string|min:6|confirmed',
'name' => 'required|string|max:255',
'storage_capacity' => 'required|digits_between:1,9',
'role' => 'required|string',
'avatar' => 'sometimes|file',
];
}
}

View File

@@ -0,0 +1,30 @@
<?php
namespace App\Http\Requests\Admin;
use Illuminate\Foundation\Http\FormRequest;
class DeleteUserRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'data.name' => 'required|string|max:255',
];
}
}

View File

@@ -25,8 +25,9 @@ class DeleteItemRequest extends FormRequest
public function rules()
{
return [
'type' => 'required|string',
'force_delete' => 'required|boolean',
'data[*].force_delete' => 'required|boolean',
'data[*].type' => 'required|string',
'data[*].unique_id' => 'required|integer'
];
}
}

View File

@@ -25,8 +25,9 @@ class MoveItemRequest extends FormRequest
public function rules()
{
return [
'to_unique_id' => 'required|integer',
'from_type' => 'required|string',
'to_unique_id' => 'required|integer',
'items[*].type' => 'required|string',
'items[*].unique_id' => 'required|integer',
];
}
}

View File

@@ -2,6 +2,7 @@
namespace App\Http\Requests\FileFunctions;
use App\Rules\MimetypeBlacklistValidation;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Auth;
@@ -26,7 +27,7 @@ class UploadRequest extends FormRequest
{
return [
'parent_id' => 'required|integer',
'file' => 'required|file',
'file' => ['required','file' , new MimetypeBlacklistValidation]
];
}
}

View File

@@ -0,0 +1,31 @@
<?php
namespace App\Http\Requests\Payments;
use Illuminate\Foundation\Http\FormRequest;
class RegisterNewPaymentMethodRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'token' => 'required|string',
'default' => 'required|boolean'
];
}
}

View File

@@ -0,0 +1,31 @@
<?php
namespace App\Http\Requests\PublicPages;
use Illuminate\Foundation\Http\FormRequest;
class SendMessageRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'email' => 'required|email',
'message' => 'required|string',
];
}
}

View File

@@ -0,0 +1,38 @@
<?php
namespace App\Http\Requests\SetupWizard;
use Illuminate\Foundation\Http\FormRequest;
class StoreAppSetupRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'title' => 'required|string',
'description' => 'required|string',
'logo' => 'sometimes|file',
'favicon' => 'sometimes|file',
'contactMail' => 'required|email',
'googleAnalytics' => 'sometimes|string',
'defaultStorage' => 'sometimes|digits_between:1,9',
'userRegistration' => 'required|boolean',
'storageLimitation' => 'required|boolean',
];
}
}

View File

@@ -0,0 +1,35 @@
<?php
namespace App\Http\Requests\SetupWizard;
use Illuminate\Foundation\Http\FormRequest;
class StoreDatabaseCredentialsRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'connection' => 'required|string',
'host' => 'required|string',
'port' => 'required|string',
'name' => 'required|string',
'username' => 'required|string',
'password' => 'required|string',
];
}
}

View File

@@ -0,0 +1,43 @@
<?php
namespace App\Http\Requests\SetupWizard;
use Illuminate\Foundation\Http\FormRequest;
class StoreEnvironmentSetupRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'storage' => 'required|array',
'storage.driver' => 'required|string',
'storage.key' => 'sometimes|nullable|string',
'storage.secret' => 'sometimes|nullable|string',
'storage.endpoint' => 'sometimes|nullable|string',
'storage.region' => 'sometimes|nullable|string',
'storage.bucket' => 'sometimes|nullable|string',
'mail' => 'required|array',
'mail.driver' => 'required|string',
'mail.host' => 'required|string',
'mail.port' => 'required|string',
'mail.username' => 'required|string',
'mail.password' => 'required|string',
'mail.encryption' => 'required|string',
];
}
}

View File

@@ -0,0 +1,37 @@
<?php
namespace App\Http\Requests\SetupWizard;
use Illuminate\Foundation\Http\FormRequest;
class StoreStripeBillingRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'billing_phone_number' => 'sometimes|nullable|string',
'billing_postal_code' => 'required|string',
'billing_vat_number' => 'required|string',
'billing_address' => 'required|string',
'billing_country' => 'required|string',
'billing_state' => 'required|string',
'billing_city' => 'required|string',
'billing_name' => 'required|string',
];
}
}

View File

@@ -0,0 +1,33 @@
<?php
namespace App\Http\Requests\SetupWizard;
use Illuminate\Foundation\Http\FormRequest;
class StoreStripeCredentialsRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'currency' => 'required|string',
'webhookSecret' => 'required|string',
'secret' => 'required|string',
'key' => 'required|string',
];
}
}

View File

@@ -0,0 +1,35 @@
<?php
namespace App\Http\Requests\SetupWizard;
use Illuminate\Foundation\Http\FormRequest;
class StoreStripePlansRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'plans' => 'required|array',
'plans.*.type' => 'required|string',
'plans.*.attributes.name' => 'required|string',
'plans.*.attributes.price' => 'required|string',
'plans.*.attributes.description' => 'sometimes|nullable|string',
'plans.*.attributes.capacity' => 'required|digits_between:1,9',
];
}
}

View File

@@ -28,6 +28,7 @@ class CreateShareRequest extends FormRequest
'isPassword' => 'required|boolean',
'unique_id' => 'required|integer',
'type' => 'required|string',
'expiration' => 'integer|nullable',
'permission' => 'string',
'password' => 'string',
];

View File

@@ -27,6 +27,7 @@ class UpdateShareRequest extends FormRequest
return [
'protected' => 'required|boolean',
'permission' => 'nullable|string',
'expiration' => 'integer|nullable',
'password' => 'string',
];
}

View File

@@ -0,0 +1,56 @@
<?php
namespace App\Http\Requests\Subscription;
use Illuminate\Foundation\Http\FormRequest;
class StoreUpgradeAccountRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
// Billings
'billing' => 'required|array',
'billing.billing_address' => 'required|string',
'billing.billing_city' => 'required|string',
'billing.billing_country' => 'required|string',
'billing.billing_name' => 'required|string',
'billing.billing_phone_number' => 'required|string',
'billing.billing_postal_code' => 'required|string',
'billing.billing_state' => 'required|string',
// Payment
'payment' => 'required|array',
'payment.type' => 'required|string',
'payment.meta' => 'required|sometimes|array',
'payment.meta.pm' => 'required|sometimes|string',
// Plan
'plan.data' => 'required|array',
'plan.data.attributes' => 'required|array',
'plan.data.attributes.capacity' => 'required|digits_between:1,9',
'plan.data.attributes.capacity_formatted' => 'required|string',
'plan.data.attributes.currency' => 'required|string',
'plan.data.attributes.description' => 'sometimes|string|nullable',
'plan.data.attributes.name' => 'required|string',
'plan.data.attributes.price' => 'required|string',
'plan.data.id' => 'required|string',
'plan.data.type' => 'required|string',
];
}
}

View File

@@ -0,0 +1,23 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\ResourceCollection;
class GatewayCollection extends ResourceCollection
{
public $collects = GatewayResource::class;
/**
* Transform the resource collection into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
return [
'data' => $this->collection,
];
}
}

View File

@@ -0,0 +1,36 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class GatewayResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
return [
'data' => [
'id' => (string)$this->id,
'type' => 'gateways',
'attributes' => [
'status' => $this->status,
'sandbox' => $this->sandbox,
'name' => $this->name,
'slug' => $this->slug,
'logo' => $this->logo,
'client_id' => $this->client_id,
'secret' => $this->secret,
'webhook' => $this->webhook,
'payment_processed' => $this->payment_processed,
'optional' => $this->optional,
]
]
];
}
}

View File

@@ -0,0 +1,23 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\ResourceCollection;
class InvoiceAdminCollection extends ResourceCollection
{
public $collects = InvoiceAdminResource::class;
/**
* Transform the resource collection into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
return [
'data' => $this->collection,
];
}
}

View File

@@ -0,0 +1,65 @@
<?php
namespace App\Http\Resources;
use App\User;
use Illuminate\Http\Resources\Json\JsonResource;
use Laravel\Cashier\Cashier;
class InvoiceAdminResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
$user = User::where('stripe_id', $this['customer'])->first();
return [
'data' => [
'id' => $this['id'],
'type' => 'invoices',
'attributes' => [
'customer' => $this['customer'],
'total' => Cashier::formatAmount($this['total']),
'currency' => $this['currency'],
'created_at_formatted' => format_date($this['created']),
'created_at' => $this['created'],
'order' => $this['number'],
'user_id' => $user ? $user->id : null,
'client' => [
'billing_address' => $this['customer_address'],
'billing_name' => $this['customer_name'],
'billing_phone_number' => $this['customer_phone'],
],
'bag' => [
'amount' => $this['lines']['data'][0]['amount'],
'currency' => $this['lines']['data'][0]['currency'],
'type' => $this['lines']['data'][0]['type'],
'description' => $this['lines']['data'][0]['description'],
],
'seller' => null,
]
],
$this->mergeWhen($user, function () use ($user) {
return [
'relationships' => [
'user' => [
'data' => [
'id' => (string)$user->id,
'type' => 'user',
'attributes' => [
'name' => $user->name,
'avatar' => $user->avatar,
]
]
]
]
];
}),
];
}
}

View File

@@ -0,0 +1,23 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\ResourceCollection;
class InvoiceCollection extends ResourceCollection
{
public $collects = InvoiceResource::class;
/**
* Transform the resource collection into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
return [
'data' => $this->collection,
];
}
}

View File

@@ -0,0 +1,80 @@
<?php
namespace App\Http\Resources;
use App\User;
use Illuminate\Http\Resources\Json\JsonResource;
class InvoiceResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
$user = User::where('stripe_id', $this->customer)->first();
$invoice_items = [];
$invoice_subscriptions = [];
// Format bag
foreach ($this->invoiceItems() as $item) {
array_push($invoice_items, [
'amount' => $item->total(),
'description' => $item->description,
'currency' => $item->currency,
'type' => $item->type,
]);
}
// Format bag
foreach ($this->subscriptions() as $item) {
array_push($invoice_subscriptions, [
'amount' => $item->total(),
'description' => $item->description,
'currency' => $item->currency,
'type' => $item->type,
]);
}
return [
'data' => [
'id' => $this->id,
'type' => 'invoices',
'attributes' => [
'customer' => $this->customer,
'total' => $this->total(),
'currency' => $this->currency,
'created_at_formatted' => format_date($this->date(), '%d. %B. %Y'),
'created_at' => $this->created,
'order' => $this->number,
'user_id' => $user ? $user->id : null,
'client' => [
'billing_address' => $this->customer_address,
'billing_name' => $this->customer_name,
'billing_phone_number' => $this->customer_phone,
],
'seller' => null,
'invoice_items' => $invoice_items,
'invoice_subscriptions' => $invoice_subscriptions,
]
],
$this->mergeWhen($user, [
'relationships' => [
'user' => [
'data' => [
'id' => (string)$user->id,
'type' => 'user',
'attributes' => [
'name' => $user->name,
'avatar' => $user->avatar,
]
]
]
]
]),
];
}
}

View File

@@ -0,0 +1,23 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\ResourceCollection;
class PageCollection extends ResourceCollection
{
public $collects = PageResource::class;
/**
* Transform the resource collection into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
return [
'data' => $this->collection,
];
}
}

View File

@@ -0,0 +1,31 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class PageResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
return [
'data' => [
'id' => $this->id,
'type' => 'pages',
'attributes' => [
'visibility' => $this->visibility,
'title' => $this->title,
'slug' => $this->slug,
'content' => $this->content,
'content_formatted' => add_paragraphs($this->content),
]
],
];
}
}

View File

@@ -0,0 +1,23 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\ResourceCollection;
class PaymentCardCollection extends ResourceCollection
{
public $collects = PaymentCardResource::class;
/**
* Transform the resource collection into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
return [
'data' => $this->collection,
];
}
}

View File

@@ -0,0 +1,35 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class PaymentCardResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
return [
'data' => [
'id' => (string)$this['id'],
'type' => 'payment_method',
'attributes' => [
'provider' => 'stripe',
'card_id' => $this['id'],
'brand' => strtolower($this['card']['brand']),
'last4' => $this['card']['last4'],
'exp_month' => $this['card']['exp_month'],
'exp_year' => $this['card']['exp_year'],
'created_at' => format_date($this['created_at'], '%d. %B. %Y'),
'status' => 'active',
'default' => 0,
]
]
];
}
}

View File

@@ -0,0 +1,35 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class PaymentDefaultCardResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
return [
'data' => [
'id' => (string)$this['id'],
'type' => 'payment_method',
'attributes' => [
'provider' => 'stripe',
'card_id' => $this['id'],
'brand' => isset($this['brand']) ? strtolower($this['brand']) : strtolower($this['card']['brand']),
'last4' => isset($this['last4']) ? $this['last4'] : $this['card']['last4'],
'exp_month' => isset($this['exp_month']) ? $this['exp_month'] : $this['card']['exp_month'],
'exp_year' => isset($this['exp_year']) ? $this['exp_year'] : $this['card']['exp_year'],
'created_at' => format_date($this['created_at'], '%d. %B. %Y'),
'status' => 'active',
'default' => 0,
]
]
];
}
}

View File

@@ -0,0 +1,23 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\ResourceCollection;
class PlanCollection extends ResourceCollection
{
public $collects = PlanResource::class;
/**
* Transform the resource collection into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
return [
'data' => $this->collection,
];
}
}

View File

@@ -0,0 +1,41 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
use Laravel\Cashier\Cashier;
use Laravel\Cashier\Subscription;
class PlanResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
// Get subscribers
$subscriber_count = Subscription::where('stripe_plan', $this['plan']['id'])->where('stripe_status', 'active')->get();
return [
'data' => [
'id' => $this['plan']['id'],
'type' => 'plans',
'attributes' => [
'subscribers' => $subscriber_count->count(),
'status' => $this['plan']['active'] ? 1 : 0,
'name' => $this['product']['name'],
'description' => $this['product']['description'],
'price' => $this['plan']['amount'],
'price_formatted' => Cashier::formatAmount($this['plan']['amount']),
'capacity_formatted' => format_gigabytes($this['product']['metadata']['capacity']),
'capacity' => (int) $this['product']['metadata']['capacity'],
'created_at_formatted' => format_date($this['plan']['created']),
'created_at' => $this['plan']['created'],
]
]
];
}
}

View File

@@ -0,0 +1,23 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\ResourceCollection;
class PricingCollection extends ResourceCollection
{
public $collects = PricingResource::class;
/**
* Transform the resource collection into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
return [
'data' => $this->collection,
];
}
}

View File

@@ -0,0 +1,68 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
use Laravel\Cashier\Cashier;
class PricingResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
return [
'data' => [
'id' => $this['plan']['id'],
'type' => 'plans',
'attributes' => [
'name' => $this['product']['name'],
'description' => $this['product']['description'],
'price' => Cashier::formatAmount($this['plan']['amount']),
'capacity_formatted' => format_gigabytes($this['product']['metadata']['capacity']),
'capacity' => (int)$this['product']['metadata']['capacity'],
'currency' => config('cashier.currency'),
'tax_rates' => $this->get_tax_rates(),
]
]
];
}
/**
* Get plan tax rates
*
* @return array
*/
private function get_tax_rates(): array
{
$stripe = resolve('App\Services\StripeService');
$rates_puplic = [];
// Get tax rates
$rates = $stripe->getTaxRates();
foreach ($rates as $rate) {
// Continue when is not active
if (!$rate['active']) continue;
// Calculate tax
$tax = $this['plan']['amount'] * ($rate['percentage'] / 100);
array_push($rates_puplic, [
'id' => $rate['id'],
'active' => $rate['active'],
'jurisdiction' => $rate['jurisdiction'],
'percentage' => $rate['percentage'],
'plan_price_formatted' => Cashier::formatAmount(round($this['plan']['amount'] + $tax)),
]);
}
return $rates_puplic;
}
}

View File

@@ -20,8 +20,9 @@ class ShareResource extends JsonResource
'type' => 'shares',
'attributes' => [
'permission' => $this->permission,
'protected' => $this->protected,
'item_id' => $this->item_id,
'protected' => (int) $this->protected,
'item_id' => (int) $this->item_id,
'expire_in' => (int) $this->expire_in,
'token' => $this->token,
'link' => $this->link,
'type' => $this->type,

View File

@@ -0,0 +1,83 @@
<?php
namespace App\Http\Resources;
use App\Services\StripeService;
use App\User;
use Cartalyst\Stripe\Api\PaymentMethods;
use Faker\Factory;
use Illuminate\Http\Resources\Json\JsonResource;
class UserResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
return [
'data' => [
'id' => (string)$this->id,
'type' => 'user',
'attributes' => [
'storage_capacity' => $this->settings->storage_capacity,
'subscription' => $this->subscribed('main'),
'incomplete_payment' => $this->hasIncompletePayment('main') ? route('cashier.payment', $this->subscription('main')->latestPayment()->id) : null,
'stripe_customer' => is_null($this->stripe_id) ? false : true,
'name' => $this->name,
'email' => env('APP_DEMO') ? obfuscate_email($this->email) : $this->email,
'avatar' => $this->avatar,
'role' => $this->role,
'created_at_formatted' => format_date($this->created_at, '%d. %B. %Y'),
'created_at' => $this->created_at,
'updated_at' => $this->updated_at,
]
],
'relationships' => [
'settings' => [
'data' => [
'id' => (string)$this->settings->id,
'type' => 'settings',
'attributes' => [
'billing_name' => $this->settings->billing_name,
'billing_address' => $this->settings->billing_address,
'billing_state' => $this->settings->billing_state,
'billing_city' => $this->settings->billing_city,
'billing_postal_code' => $this->settings->billing_postal_code,
'billing_country' => $this->settings->billing_country,
'billing_phone_number' => $this->settings->billing_phone_number,
]
]
],
'storage' => [
'data' => [
'id' => '1',
'type' => 'storage',
'attributes' => $this->storage
]
],
'favourites' => [
'data' => [
'id' => '1',
'type' => 'folders_favourite',
'attributes' => [
'folders' => $this->favourite_folders->makeHidden(['pivot'])
],
],
],
'tree' => [
'data' => [
'id' => '1',
'type' => 'folders_tree',
'attributes' => [
'folders' => $this->folder_tree
],
],
]
]
];
}
}

View File

@@ -0,0 +1,89 @@
<?php
namespace App\Http\Resources;
use App\FileManagerFile;
use ByteUnits\Metric;
use Illuminate\Http\Resources\Json\JsonResource;
class UserStorageResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
$document_mimetypes = [
'pdf', 'numbers', 'xlsx', 'xls', 'txt', 'md', 'rtf', 'pptx', 'ppt', 'odt', 'ods', 'odp', 'epub', 'docx', 'doc', 'csv', 'pages'
];
// Get all images
$images = FileManagerFile::where('user_id', $this->id)
->where('type', 'image')->get()->map(function ($item) {
return (int)$item->getRawOriginal('filesize');
})->sum();
// Get all audios
$audios = FileManagerFile::where('user_id', $this->id)
->where('type', 'audio')->get()->map(function ($item) {
return (int)$item->getRawOriginal('filesize');
})->sum();
// Get all videos
$videos = FileManagerFile::where('user_id', $this->id)
->where('type', 'video')->get()->map(function ($item) {
return (int)$item->getRawOriginal('filesize');
})->sum();
// Get all documents
$documents = FileManagerFile::where('user_id', $this->id)
->whereIn('mimetype', $document_mimetypes)->get()->map(function ($item) {
return (int)$item->getRawOriginal('filesize');
})->sum();
// Get all other files
$others = FileManagerFile::where('user_id', $this->id)
->whereNotIn('mimetype', $document_mimetypes)
->whereNotIn('type', ['audio', 'video', 'image'])
->get()->map(function ($item) {
return (int)$item->getRawOriginal('filesize');
})->sum();
return [
'data' => [
'id' => (string)$this->id,
'type' => 'storage',
'attributes' => [
'used' => Metric::bytes($this->used_capacity)->format(),
'capacity' => format_gigabytes($this->settings->storage_capacity),
'percentage' => (float)get_storage_fill_percentage($this->used_capacity, $this->settings->storage_capacity),
],
'meta' => [
'images' => [
'used' => Metric::bytes($images)->format(),
'percentage' => (float)get_storage_fill_percentage($images, $this->settings->storage_capacity),
],
'audios' => [
'used' => Metric::bytes($audios)->format(),
'percentage' => (float)get_storage_fill_percentage($audios, $this->settings->storage_capacity),
],
'videos' => [
'used' => Metric::bytes($videos)->format(),
'percentage' => (float)get_storage_fill_percentage($videos, $this->settings->storage_capacity),
],
'documents' => [
'used' => Metric::bytes($documents)->format(),
'percentage' => (float)get_storage_fill_percentage($documents, $this->settings->storage_capacity),
],
'others' => [
'used' => Metric::bytes($others)->format(),
'percentage' => (float)get_storage_fill_percentage($others, $this->settings->storage_capacity),
],
]
]
];
}
}

View File

@@ -0,0 +1,48 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class UserSubscription extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
$stripe = resolve('App\Services\StripeService');
$active_subscription = $this->subscription('main')->asStripeSubscription();
// Get subscription details
$subscription = $stripe->getPlan($this->subscription('main')->stripe_plan);
// Retrieve the timestamp from Stripe
$current_period_end = $active_subscription["current_period_end"];
$current_period_start = $active_subscription["current_period_start"];
$canceled_at = $active_subscription["canceled_at"];
return [
'data' => [
'id' => $subscription['plan']['id'],
'type' => 'subscription',
'attributes' => [
'incomplete' => $this->subscription('main')->incomplete(),
'active' => $this->subscription('main')->active(),
'canceled' => $this->subscription('main')->cancelled(),
'name' => $subscription['product']['name'],
'capacity' => (int)$subscription['product']['metadata']['capacity'],
'capacity_formatted' => format_gigabytes($subscription['product']['metadata']['capacity']),
'slug' => $subscription['plan']['id'],
'canceled_at' => format_date($canceled_at, '%d. %B. %Y'),
'created_at' => format_date($current_period_start, '%d. %B. %Y'),
'ends_at' => format_date($current_period_end, '%d. %B. %Y'),
]
]
];
}
}

View File

@@ -0,0 +1,23 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\ResourceCollection;
class UsersCollection extends ResourceCollection
{
public $collects = UserResource::class;
/**
* Transform the resource collection into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
return [
'data' => $this->collection,
];
}
}

View File

@@ -102,7 +102,7 @@ class Demo
$filename = Str::random() . '-' . str_replace(' ', '', $file->getClientOriginalName());
$thumbnail = null;
$filesize = $file->getSize();
$filetype = get_file_type($file);
$filetype = get_file_type($file->getMimeType());
return [
'id' => random_int(1000, 9999),
@@ -138,6 +138,6 @@ class Demo
*/
public static function favourites($user) {
return $user->favourites->makeHidden(['pivot']);
return $user->favourite_folders->makeHidden(['pivot']);
}
}

View File

@@ -8,15 +8,94 @@ use App\FileManagerFile;
use App\FileManagerFolder;
use App\Http\Requests\FileFunctions\RenameItemRequest;
use App\User;
use App\Zip;
use Aws\Exception\MultipartUploadException;
use Aws\S3\MultipartUploader;
use Carbon\Carbon;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use Intervention\Image\ImageManagerStatic as Image;
use League\Flysystem\FileNotFoundException;
use Madnest\Madzipper\Facades\Madzipper;
use Symfony\Component\HttpKernel\Exception\HttpException;
class Editor
{
/**
* Zip selected files, store it in /zip folder and retrieve zip record
*
* @param $files
* @param null $shared
* @return mixed
* @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
*/
public static function zip_files($files, $shared = null)
{
// Local storage instance
$disk_local = Storage::disk('local');
// Create zip directory
if (!$disk_local->exists('zip')) {
$disk_local->makeDirectory('zip');
}
// Move file to local storage from external storage service
if (!is_storage_driver('local')) {
// Create temp directory
if (!$disk_local->exists('temp')) {
$disk_local->makeDirectory('temp');
}
foreach ($files as $file) {
try {
$disk_local->put('temp/' . $file['basename'], Storage::get('file-manager/' . $file['basename']));
} catch (FileNotFoundException $e) {
throw new HttpException(404, 'File not found');
}
}
}
// Get zip path
$zip_name = Str::random(16) . '.zip';
$zip_path = 'zip/' . $zip_name;
// Create zip
$zip = Madzipper::make(storage_path() . '/app/' . $zip_path);
// Get files folder on local storage drive
$files_directory = is_storage_driver('local') ? 'file-manager' : 'temp';
// Add files to zip
$files->each(function ($file) use ($zip, $files_directory) {
$zip->addString($file['name'], File::get(storage_path() . '/app/' . $files_directory . '/' . $file['basename']));
});
// Close zip
$zip->close();
// Delete temporary files
if (!is_storage_driver('local')) {
$files->each(function ($file) use ($disk_local) {
$disk_local->delete('temp/' . $file['basename']);
});
}
// Store zip record
return Zip::create([
'user_id' => $shared->user_id ?? Auth::id(),
'shared_token' => $shared->token ?? null,
'basename' => $zip_name,
]);
}
/**
* Create new directory
*
@@ -30,11 +109,12 @@ class Editor
$user_scope = is_null($shared) ? $request->user()->token()->scopes[0] : 'editor';
$name = $request->has('name') ? $request->input('name') : 'New Folder';
$user_id = is_null($shared) ? Auth::id() : $shared->user_id;
$unique_id = get_unique_id();
// Create folder
$folder = FileManagerFolder::create([
'parent_id' => $request->parent_id,
'unique_id' => get_unique_id(),
'unique_id' => $unique_id,
'user_scope' => $user_scope,
'user_id' => $user_id,
'type' => 'folder',
@@ -79,13 +159,13 @@ class Editor
* @param null $shared
* @throws \Exception
*/
public static function delete_item($request, $unique_id, $shared = null)
public static function delete_item($file, $unique_id, $shared = null)
{
// Get user id
$user = is_null($shared) ? Auth::user() : User::findOrFail($shared->user_id);
// Delete folder
if ($request->type === 'folder') {
if ($file['type'] === 'folder') {
// Get folder
$folder = FileManagerFolder::withTrashed()
@@ -106,7 +186,7 @@ class Editor
}
// Force delete children files
if ($request->force_delete) {
if ($file['force_delete']) {
// Get children folder ids
$child_folders = filter_folders_ids($folder->trashed_folders, 'unique_id');
@@ -124,7 +204,7 @@ class Editor
Storage::delete('/file-manager/' . $file->basename);
// Delete thumbnail if exist
if (!is_null($file->thumbnail)) Storage::delete('/file-manager/' . $file->getOriginal('thumbnail'));
if (!is_null($file->thumbnail)) Storage::delete('/file-manager/' . $file->getRawOriginal('thumbnail'));
// Delete file permanently
$file->forceDelete();
@@ -135,10 +215,10 @@ class Editor
}
// Soft delete items
if (!$request->force_delete) {
if (!$file['force_delete']) {
// Remove folder from user favourites
$user->favourites()->detach($unique_id);
$user->favourite_folders()->detach($unique_id);
// Soft delete folder record
$folder->delete();
@@ -146,10 +226,10 @@ class Editor
}
// Delete item
if ($request->type !== 'folder') {
if ($file['type'] !== 'folder') {
// Get file
$file = FileManagerFile::withTrashed()
$item = FileManagerFile::withTrashed()
->where('user_id', $user->id)
->where('unique_id', $unique_id)
->first();
@@ -166,23 +246,63 @@ class Editor
}
// Force delete file
if ($request->force_delete) {
if ($file['force_delete']) {
// Delete file
Storage::delete('/file-manager/' . $file->basename);
Storage::delete('/file-manager/' . $item->basename);
// Delete thumbnail if exist
if ($file->thumbnail) Storage::delete('/file-manager/' . $file->getOriginal('thumbnail'));
if ($item->thumbnail) Storage::delete('/file-manager/' . $item->getRawOriginal('thumbnail'));
// Delete file permanently
$file->forceDelete();
$item->forceDelete();
}
// Soft delete file
if (!$request->force_delete) {
if (!$file['force_delete']) {
// Soft delete file
$file->delete();
$item->delete();
}
}
}
/**
* Move folder or file to new location
*
* @param $request
* @param $unique_id
* @param null $shared
*/
public static function move($request, $to_unique_id, $shared = null)
{
// Get user id
$user_id = is_null($shared) ? Auth::id() : $shared->user_id;
foreach ($request->input('items') as $item) {
$unique_id = $item['unique_id'];
if ($item['type'] === 'folder') {
// Move folder
$item = FileManagerFolder::where('user_id', $user_id)
->where('unique_id', $unique_id)
->firstOrFail();
$item->update([
'parent_id' => $to_unique_id
]);
} else {
// Move file under new folder
$item = FileManagerFile::where('user_id', $user_id)
->where('unique_id', $unique_id)
->firstOrFail();
$item->update([
'folder_id' => $to_unique_id
]);
}
}
}
@@ -197,98 +317,276 @@ class Editor
*/
public static function upload($request, $shared = null)
{
// Get user data
$user_scope = is_null($shared) ? $request->user()->token()->scopes[0] : 'editor';
$user_id = is_null($shared) ? Auth::id() : $shared->user_id;
// Get parent_id from request
$folder_id = $request->parent_id === 0 ? 0 : $request->parent_id;
$file = $request->file('file');
// File
$filename = Str::random() . '-' . str_replace(' ', '', $file->getClientOriginalName());
$filetype = get_file_type($file);
$filesize = $file->getSize();
$directory = 'file-manager';
$thumbnail = null;
// Check or create directories
self::check_directories(['chunks', 'file-manager']);
// create directory if not exist
if (!Storage::exists($directory)) {
Storage::makeDirectory($directory);
// File name
$user_file_name = basename('chunks/' . substr($file->getClientOriginalName(), 17), '.part');
$disk_file_name = basename('chunks/' . $file->getClientOriginalName(), '.part');
$temp_filename = $file->getClientOriginalName();
// File Path
$file_path = config('filesystems.disks.local.root') . '/chunks/' . $temp_filename;\
// Generate file
File::append($file_path, $file->get());
// Size of file
$file_size = File::size($file_path);
// Size of limit
$limit = get_setting('upload_limit');
// File size handling
if( $limit && $file_size > format_bytes($limit)) abort(413);
// If last then process file
if ($request->boolean('is_last')) {
$metadata = get_image_meta_data($file);
$disk_local = Storage::disk('local');
$unique_id = get_unique_id();
// Get user data
$user_scope = is_null($shared) ? $request->user()->token()->scopes[0] : 'editor';
$user_id = is_null($shared) ? Auth::id() : $shared->user_id;
// File Info
$file_size = $disk_local->size('chunks/' . $temp_filename);
$file_mimetype = $disk_local->mimeType('chunks/' . $temp_filename);
// Check if user has enough space to upload file
self::check_user_storage_capacity($user_id, $file_size, $temp_filename);
// Create thumbnail
$thumbnail = self::get_image_thumbnail('chunks/' . $temp_filename, $disk_file_name);
// Move finished file from chunk to file-manager directory
$disk_local->move('chunks/' . $temp_filename, 'file-manager/' . $disk_file_name);
// Move files to external storage
if (!is_storage_driver(['local'])) {
// Clear failed uploads if exists
self::clear_failed_files();
// Move file to external storage service
self::move_to_external_storage($disk_file_name, $thumbnail);
}
// Store file
$options = [
'mimetype' => get_file_type_from_mimetype($file_mimetype),
'type' => get_file_type($file_mimetype),
'folder_id' => $request->parent_id,
'metadata' => $metadata,
'name' => $user_file_name,
'unique_id' => $unique_id,
'basename' => $disk_file_name,
'user_scope' => $user_scope,
'thumbnail' => $thumbnail,
'filesize' => $file_size,
'user_id' => $user_id,
];
// Store user upload size
if ($request->user()) {
// If upload a loged user
$request->user()->record_upload($file_size);
} else {
// If upload guest
User::find($shared->user_id)->record_upload($file_size);
}
// Return new file
return FileManagerFile::create($options);
}
}
// Store to disk
Storage::putFileAs($directory, $file, $filename, 'private');
/**
* Clear failed files
*/
private static function clear_failed_files()
{
$local_disk = Storage::disk('local');
// Create image thumbnail
if ($filetype == 'image') {
// Get all files from storage
$files = collect([
$local_disk->allFiles('file-manager'),
$local_disk->allFiles('chunks')
])->collapse();
$files->each(function ($file) use ($local_disk) {
// Get the file's last modification time.
$last_modified = $local_disk->lastModified($file);
// Get diffInHours
$diff = Carbon::parse($last_modified)->diffInHours(Carbon::now());
// Delete if file is in local storage more than 24 hours
if ($diff > 24) {
Log::info('Failed file or chunk ' . $file . ' deleted.');
// Delete file from local storage
$local_disk->delete($file);
}
});
}
/**
* Move file to external storage if is set
*
* @param string $filename
* @param string|null $thumbnail
*/
private static function move_to_external_storage(string $filename, ?string $thumbnail): void
{
$disk_local = Storage::disk('local');
foreach ([$filename, $thumbnail] as $file) {
// Check if file exist
if (!$file) continue;
// Get file size
$filesize = $disk_local->size('file-manager/' . $file);
// If file is bigger than 5.2MB then run multipart upload
if ($filesize > 5242880) {
// Get driver
$driver = \Storage::getDriver();
// Get adapter
$adapter = $driver->getAdapter();
// Get client
$client = $adapter->getClient();
// Prepare the upload parameters.
$uploader = new MultipartUploader($client, config('filesystems.disks.local.root') . '/file-manager/' . $file, [
'bucket' => $adapter->getBucket(),
'key' => 'file-manager/' . $file
]);
try {
// Upload content
$uploader->upload();
} catch (MultipartUploadException $e) {
// Write error log
Log::error($e->getMessage());
// Delete file after error
$disk_local->delete('file-manager/' . $file);
throw new HttpException(409, $e->getMessage());
}
} else {
// Stream file object to s3
Storage::putFileAs('file-manager', config('filesystems.disks.local.root') . '/file-manager/' . $file, $file, 'private');
}
// Delete file after upload
$disk_local->delete('file-manager/' . $file);
}
}
/**
* Check if directories 'chunks' and 'file-manager exist', if no, then create
*
* @param $directories
*/
private static function check_directories($directories): void
{
foreach ($directories as $directory) {
if (!Storage::disk('local')->exists($directory)) {
Storage::disk('local')->makeDirectory($directory);
}
if (!is_storage_driver(['local'])) {
if (!Storage::exists($directory)) {
Storage::makeDirectory($directory);
}
}
}
}
/**
* Create thumbnail for images
*
* @param string $file_path
* @param string $filename
* @param $file
* @return string|null
*/
private static function get_image_thumbnail(string $file_path, string $filename)
{
$local_disk = Storage::disk('local');
// Create thumbnail from image
if (in_array($local_disk->mimeType($file_path), ['image/gif', 'image/jpeg', 'image/jpg', 'image/png', 'image/webp'])) {
// Get thumbnail name
$thumbnail = 'thumbnail-' . $filename;
// Create intervention image
$image = Image::make($file->getRealPath())->orientate();
$image = Image::make(config('filesystems.disks.local.root') . '/' . $file_path)->orientate();
// Resize image
$image->resize(256, null, function ($constraint) {
$image->resize(512, null, function ($constraint) {
$constraint->aspectRatio();
})->stream();
// Store thumbnail to disk
Storage::put($directory . '/' . $thumbnail, $image);
$local_disk->put('file-manager/' . $thumbnail, $image);
}
// Store file
$options = [
'name' => pathinfo($file->getClientOriginalName())['filename'],
'mimetype' => $file->getClientOriginalExtension(),
'unique_id' => get_unique_id(),
'user_scope' => $user_scope,
'folder_id' => $folder_id,
'thumbnail' => $thumbnail,
'basename' => $filename,
'filesize' => $filesize,
'type' => $filetype,
'user_id' => $user_id,
];
// Return thumbnail as svg file
if ($local_disk->mimeType($file_path) === 'image/svg+xml') {
// Return new file
return FileManagerFile::create($options);
$thumbnail = $filename;
}
return $thumbnail ?? null;
}
/**
* Move folder or file to new location
* Check if user has enough space to upload file
*
* @param $request
* @param $unique_id
* @param null $shared
* @param $user_id
* @param int $file_size
* @param $temp_filename
*/
public static function move($request, $unique_id, $shared = null)
private static function check_user_storage_capacity($user_id, int $file_size, $temp_filename): void
{
// Get user id
$user_id = is_null($shared) ? Auth::id() : $shared->user_id;
// Get user storage percentage and get storage_limitation setting
$user_storage_used = user_storage_percentage($user_id, $file_size);
$storage_limitation = get_setting('storage_limitation');
if ($request->from_type === 'folder') {
// Check if user can upload
if ($storage_limitation && $user_storage_used >= 100) {
// Move folder
$item = FileManagerFolder::where('user_id', $user_id)
->where('unique_id', $unique_id)
->firstOrFail();
// Delete file
Storage::disk('local')->delete('chunks/' . $temp_filename);
$item->update([
'parent_id' => $request->to_unique_id
]);
} else {
// Move file under new folder
$item = FileManagerFile::where('user_id', $user_id)
->where('unique_id', $unique_id)
->firstOrFail();
$item->update([
'folder_id' => $request->to_unique_id
]);
// Abort uploading
abort(423, 'You exceed your storage limit!');
}
}
}

View File

@@ -1,317 +0,0 @@
<?php
use App\FileManagerFile;
use App\FileManagerFolder;
use App\Share;
use ByteUnits\Metric;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use Intervention\Image\ImageManagerStatic as Image;
/**
* Get app version from config
*
* @return \Illuminate\Config\Repository|mixed
*/
function get_storage() {
return env('FILESYSTEM_DRIVER');
}
/**
* Check if is running AWS s3 as storage
*
* @return bool
*/
function is_storage_driver($driver) {
if (is_array($driver)) {
return in_array(env('FILESYSTEM_DRIVER'), $driver);
}
return env('FILESYSTEM_DRIVER') === $driver;
}
/**
* Get app version from config
*
* @return \Illuminate\Config\Repository|mixed
*/
function get_version() {
return config('vuefilemanager.version');
}
/**
* Check if is demo
*
* @return mixed
*/
function is_demo($user_id) {
return env('APP_DEMO', false) && $user_id === 1;
}
/**
* Get folder or file item
*
* @param $type
* @param $unique_id
* @param $user_id
* @return \Illuminate\Database\Eloquent\Builder|Model
*/
function get_item($type, $unique_id, $user_id) {
if ($type === 'folder') {
// Return folder item
return FileManagerFolder::where('unique_id', $unique_id)
->where('user_id', $user_id)
->firstOrFail();
}
// Return file item
return FileManagerFile::where('unique_id', $unique_id)
->where('user_id', $user_id)
->firstOrFail();
}
/**
* Get shared token
*
* @param $token
* @return \Illuminate\Database\Eloquent\Builder|Model
*/
function get_shared($token) {
return Share::where(DB::raw('BINARY `token`'), $token)
->firstOrFail();
}
/**
* Check if shared permission is editor
*
* @param $shared
* @return bool
*/
function is_editor($shared) {
return $shared->permission === 'editor';
}
/**
* Get unique id
*
* @return int
*/
function get_unique_id(): int
{
// Get files and folders
$folders = FileManagerFolder::withTrashed()->get();
$files = FileManagerFile::withTrashed()->get();
// Get last ids
$folders_unique = $folders->isEmpty() ? 0 : $folders->last()->unique_id;
$files_unique = $files->isEmpty() ? 0 : $files->last()->unique_id;
// Count new unique id
$unique_id = $folders_unique > $files_unique ? $folders_unique + 1 : $files_unique + 1;
return $unique_id;
}
/**
* Store user avatar to storage
*
* @param $image
* @param $path
* @return string
*/
function store_avatar($image, $path)
{
// Get directory
$path = check_directory($path);
// Store avatar
$image_path = Str::random(8) . '-' . $image->getClientOriginalName();
// Create intervention image
$img = Image::make($image->getRealPath());
// Generate thumbnail
$img->fit('150', '150')->stream();
// Store thumbnail to disk
Storage::put($path . '/' . $image_path, $img);
// Return path to image
return $path . '/' . $image_path;
}
/**
* Check if directory exist, if no, then create it
*
* @param $directory
* @return mixed
*/
function check_directory($directory)
{
if (!Storage::exists($directory)) {
Storage::makeDirectory($directory);
}
return $directory;
}
/**
* Make input from request
*
* @param $request
* @return array
*/
function make_single_input($request)
{
// Create container
$data = [];
// Add data to array
$data[$request->name] = $request->value;
// Return input
return $data;
}
/**
* Format integer to gigabytes
*
* @param $gigabytes
* @return string
*/
function format_gigabytes($megabytes)
{
return Metric::megabytes($megabytes)->format();
}
/**
* Get storage usage in percent
*
* @param $used
* @param $capacity
* @return string
*/
function get_storage_fill_percentage($used, $capacity)
{
// Format gigabytes to bytes
$total = intval(Metric::megabytes($capacity)->numberOfBytes());
// Count progress
$progress = ($used * 100) / $total;
// Return in 2 decimal
return number_format((float)$progress, 2, '.', '');
}
/**
* Get user capacity fill percentage
*
* @return string
*/
function user_storage_percentage()
{
$user = Auth::user();
return get_storage_fill_percentage($user->used_capacity, config('vuefilemanager.user_storage_capacity'));
}
/**
* Find all key values in recursive array
*
* @param array $array
* @param $needle
* @return array
*/
function recursiveFind(array $array, $needle)
{
$iterator = new RecursiveArrayIterator($array);
$recursive = new RecursiveIteratorIterator($iterator, RecursiveIteratorIterator::SELF_FIRST);
$aHitList = array();
foreach ($recursive as $key => $value) {
if ($key === $needle) {
array_push($aHitList, $value);
}
}
return $aHitList;
}
/**
* Get values which appears only once in array
* @param $arr
* @return array
*/
function appeared_once($arr)
{
$array_count_values = array_count_values($arr);
$single_time_comming_values_array = [];
foreach ($array_count_values as $key => $val) {
if ($val == 1) {
$single_time_comming_values_array[] = $key;
}
}
return $single_time_comming_values_array;
}
/**
* @param $folders
* @return array
*/
function filter_folders_ids($folders, $by_column = 'unique_id')
{
$folder_unique_ids = recursiveFind($folders->toArray(), $by_column);
return appeared_once($folder_unique_ids);
}
/**
* Format localized date
*
* @param $date
* @param string $format
* @return string
*/
function format_date($date, $format = '%d. %B. %Y, %H:%M')
{
$start = Carbon::parse($date);
return $start->formatLocalized($format);
}
/**
* Get file type from mimetype
*
* @param $file
* @return string
*/
function get_file_type($file)
{
// Get mimetype from file
$mimetype = explode('/', $file->getMimeType());
switch ($mimetype[0]) {
case 'image':
return 'image';
break;
case 'video':
return 'video';
break;
case 'audio':
return 'audio';
break;
default:
return 'file';
}
}

64
app/Invoice.php Normal file
View File

@@ -0,0 +1,64 @@
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
/**
* App\Invoice
*
* @property int $id
* @property string $token
* @property string $order
* @property string|null $provider
* @property string $user_id
* @property string $plan_id
* @property array $seller
* @property array $client
* @property array $bag
* @property string|null $notes
* @property string $total
* @property string $currency
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property-read \App\User|null $user
* @method static \Illuminate\Database\Eloquent\Builder|\App\Invoice newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|\App\Invoice newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|\App\Invoice query()
* @method static \Illuminate\Database\Eloquent\Builder|\App\Invoice whereBag($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\Invoice whereClient($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\Invoice whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\Invoice whereCurrency($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\Invoice whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\Invoice whereNotes($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\Invoice whereOrder($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\Invoice wherePlanId($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\Invoice whereProvider($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\Invoice whereSeller($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\Invoice whereToken($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\Invoice whereTotal($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\Invoice whereUpdatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\Invoice whereUserId($value)
* @mixin \Eloquent
*/
class Invoice extends Model
{
protected $guarded = [
'id'
];
protected $casts = [
'seller' => 'array',
'client' => 'array',
'bag' => 'array',
];
/**
* Get user instance
*
* @return \Illuminate\Database\Eloquent\Relations\HasOne
*/
public function user() {
return $this->hasOne(User::class, 'id', 'user_id');
}
}

View File

@@ -43,10 +43,12 @@ class ResetPassword extends Notification
$reset_url = url('/create-new-password?token=' . $this->token);
return (new MailMessage)
->subject('Reset password for your account on ' . config('vuefilemanager.app_name'))
->line('You are receiving this email because we received a password reset request for your account.')
->action('Reset Password', $reset_url)
->line('If you did not request a password reset, no further action is required.');
->subject(__('vuefilemanager.reset_password_subject') . config('vuefilemanager.app_name'))
->greeting(__('vuefilemanager.reset_password_greeting'))
->line(__('vuefilemanager.reset_password_line_1'))
->action(__('vuefilemanager.reset_password_action'), $reset_url)
->line(__('vuefilemanager.reset_password_line_2'))
->salutation(__('vuefilemanager.salutation') . ', ' . config('vuefilemanager.app_name'));
}
/**

45
app/Page.php Normal file
View File

@@ -0,0 +1,45 @@
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use Kyslik\ColumnSortable\Sortable;
/**
* App\Page
*
* @property int $id
* @property int $visibility
* @property string $title
* @property string $slug
* @property string $content
* @method static \Illuminate\Database\Eloquent\Builder|Page newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|Page newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|Page query()
* @method static \Illuminate\Database\Eloquent\Builder|Page sortable($defaultParameters = null)
* @method static \Illuminate\Database\Eloquent\Builder|Page whereContent($value)
* @method static \Illuminate\Database\Eloquent\Builder|Page whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder|Page whereSlug($value)
* @method static \Illuminate\Database\Eloquent\Builder|Page whereTitle($value)
* @method static \Illuminate\Database\Eloquent\Builder|Page whereVisibility($value)
* @mixin \Eloquent
*/
class Page extends Model
{
use Sortable;
/**
* Sortable columns
*
* @var string[]
*/
public $sortable = [
'title',
'slug',
'visibility',
];
public $timestamps = false;
protected $guarded = ['id'];
}

42
app/PaymentGateway.php Normal file
View File

@@ -0,0 +1,42 @@
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
/**
* App\PaymentGateway
*
* @property int $id
* @property int $status
* @property int $sandbox
* @property string $name
* @property string $slug
* @property string $logo
* @property string|null $client_id
* @property string|null $secret
* @property string|null $webhook
* @property string|null $optional
* @property int|null $payment_processed
* @method static \Illuminate\Database\Eloquent\Builder|\App\PaymentGateway newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|\App\PaymentGateway newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|\App\PaymentGateway query()
* @method static \Illuminate\Database\Eloquent\Builder|\App\PaymentGateway whereClientId($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\PaymentGateway whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\PaymentGateway whereLogo($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\PaymentGateway whereName($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\PaymentGateway whereOptional($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\PaymentGateway wherePaymentProcessed($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\PaymentGateway whereSandbox($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\PaymentGateway whereSecret($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\PaymentGateway whereSlug($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\PaymentGateway whereStatus($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\PaymentGateway whereWebhook($value)
* @mixin \Eloquent
*/
class PaymentGateway extends Model
{
protected $guarded = ['id'];
public $timestamps = false;
}

View File

@@ -5,6 +5,9 @@ namespace App\Providers;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\ServiceProvider;
use Laravel\Passport\Console\ClientCommand;
use Laravel\Passport\Console\InstallCommand;
use Laravel\Passport\Console\KeysCommand;
class AppServiceProvider extends ServiceProvider
{
@@ -31,5 +34,12 @@ class AppServiceProvider extends ServiceProvider
// Set locale for carbon dates
setlocale(LC_TIME, $get_time_locale);
// Install passport commands
$this->commands([
InstallCommand::class,
ClientCommand::class,
KeysCommand::class,
]);
}
}

View File

@@ -26,6 +26,11 @@ class AuthServiceProvider extends ServiceProvider
{
$this->registerPolicies();
// Define admin settings gate
Gate::define('admin-settings', function ($user) {
return $user->role === 'admin';
});
Passport::routes();
Passport::tokensCan([

View File

@@ -0,0 +1,42 @@
<?php
namespace App\Rules;
use Illuminate\Contracts\Validation\Rule;
class MimetypeBlacklistValidation implements Rule
{
/**
* Create a new rule instance.
*
* @return void
*/
public function __construct()
{
//
}
/**
* Determine if the validation rule passes.
*
* @param string $attribute
* @param mixed $value
* @return bool
*/
public function passes($attribute, $value)
{
$mimetype_blacklist = explode(',' ,get_setting('mimetypes_blacklist'));
$file_mimetype = explode('/' ,$value->getMimeType());
return !array_intersect($file_mimetype , $mimetype_blacklist);
}
/**
* Get the validation error message.
*
* @return string
*/
public function message()
{
abort (415,'Type of this mime type is not allowed.');
}
}

View File

@@ -0,0 +1,387 @@
<?php
namespace App\Services;
use App\User;
use Artisan;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
use Laravel\Cashier\Exceptions\IncompletePayment;
use Laravel\Cashier\Exceptions\PaymentActionRequired;
use Stripe;
use Symfony\Component\HttpKernel\Exception\HttpException;
class StripeService
{
/**
* Stripe Service constructor.
*/
public function __construct()
{
$this->stripe = Stripe::make(config('cashier.secret'), '2020-03-02');
}
/**
* Get Stripe account details
*
* @return mixed
*/
public function getAccountDetails()
{
$account = $this->stripe->account()->details();
return $account;
}
/**
* Get setup intent
*
* @param $user
* @return mixed
*/
public function getSetupIntent($user)
{
// Create stripe customer if not exist
$user->createOrGetStripeCustomer();
// Return setup intent
return $user->createSetupIntent();
}
/**
* Get tax rate ids
* @return array
*/
public function getTaxRates()
{
$tax_rates = $this->stripe->taxRates()->all();
return $tax_rates['data'];
}
/**
* Get default payment option or set new default payment
*
* @param $request
* @param $user
* @return mixed
*/
public function getOrSetDefaultPaymentMethod($request, $user)
{
// Check payment method
if (!$request->has('payment.meta.pm') && $user->hasDefaultPaymentMethod()) {
// Get default payment
return $user->defaultPaymentMethod()->paymentMethod;
}
// Clear cached payment methods
cache_forget_many([
'payment-methods-user-' . $user->id,
'default-payment-methods-user-' . $user->id
]);
if ($request->has('payment.meta.pm') && $user->hasDefaultPaymentMethod()) {
// Set new payment
return $user->addPaymentMethod($request->input('payment.meta.pm'))->paymentMethod;
} else if ($request->has('payment.meta.pm') && !$user->hasDefaultPaymentMethod()) {
// Set new payment
return $user->updateDefaultPaymentMethod($request->input('payment.meta.pm'))->paymentMethod;
} else {
throw new HttpException(400, 'Something went wrong.');
}
}
/**
* Register new payment method
*
* @param $request
* @param $user
* @return mixed
*/
public function registerNewPaymentMethod($request, $user)
{
// Clear cached payment methods
cache_forget_many([
'payment-methods-user-' . $user->id,
'default-payment-methods-user-' . $user->id
]);
// Set new payment method
$user->addPaymentMethod($request->token)->paymentMethod;
// Set new default payment
if ($request->default) {
$user->updateDefaultPaymentMethod($request->token)->paymentMethod;
}
}
/**
* Create new subscription or replace by new subscription
*
* @param $request
* @param $user
* @param $paymentMethod
* @return \Illuminate\Http\RedirectResponse
*/
public function createOrReplaceSubscription($request, $user)
{
try {
// Get payment method
$paymentMethod = $this->getOrSetDefaultPaymentMethod($request, $user);
// Check if user have subscription
if ($user->subscribed('main')) {
// Change subscription plan
$user->subscription('main')->skipTrial()->swap($request->input('plan.data.id'));
} else {
// Create subscription
$user->newSubscription('main', $request->input('plan.data.id'))->create($paymentMethod);
}
} catch (IncompletePayment $exception) {
if ($exception instanceof PaymentActionRequired) {
$cashier_route = route('cashier.payment', [$exception->payment->id, 'redirect' => url('/settings/subscription')]);
throw new HttpException(402, $cashier_route);
} else {
throw new HttpException(400, $exception->getMessage());
}
}
}
/**
* Update customer details
*
* @param $user
*/
public function updateCustomerDetails($user)
{
$user->updateStripeCustomer([
'name' => $user->settings->billing_name,
'phone' => $user->settings->billing_phone_number,
'address' => [
'line1' => $user->settings->billing_address,
'city' => $user->settings->billing_city,
'country' => $user->settings->billing_country,
'postal_code' => $user->settings->billing_postal_code,
'state' => $user->settings->billing_state,
]
]);
}
/**
* Get all plans
*
* @return mixed
*/
public function getPlans()
{
// Get stripe plans
$stripe_plans = $this->stripe->plans()->all();
// Plans container
$plans = [];
foreach ($stripe_plans['data'] as $plan) {
// Get stripe product
$product = $this->stripe->products()->find($plan['product']);
// Push data to $plan container
if ($product['active'] && isset($product['metadata']['capacity'])) {
array_push($plans, [
'plan' => $plan,
'product' => $product,
]);
}
}
return $plans;
}
/**
* Get all active plans
*
* @return mixed
*/
public function getActivePlans()
{
// Get stripe plans
$stripe_plans = $this->stripe->plans()->all();
// Plans container
$plans = [];
foreach ($stripe_plans['data'] as $plan) {
if ($plan['active']) {
// Get stripe product
$product = $this->stripe->products()->find($plan['product']);
// Push data to $plan container
if ($product['active'] && isset($product['metadata']['capacity'])) {
array_push($plans, [
'plan' => $plan,
'product' => $product,
]);
}
}
}
return $plans;
}
/**
* Get plan details
*
* @param $id
* @return mixed
*/
public function getPlan($id)
{
$plan = $this->stripe->plans()->find($id);
$product = $this->stripe->products()->find($plan['product']);
return compact('plan', 'product');
}
/**
* Create plan
*
* @param $data
* @return mixed
*/
public function createPlan($data)
{
if ($data instanceof Request) {
$plan = [
'name' => $data->input('attributes.name'),
'description' => $data->input('attributes.description'),
'price' => $data->input('attributes.price'),
'capacity' => $data->input('attributes.capacity'),
];
} else {
$plan = [
'name' => $data['attributes']['name'],
'description' => $data['attributes']['description'],
'price' => $data['attributes']['price'],
'capacity' => $data['attributes']['capacity'],
];
}
$product = $this->stripe->products()->create([
'name' => $plan['name'],
'description' => $plan['description'],
'metadata' => [
'capacity' => $plan['capacity']
]
]);
$plan = $this->stripe->plans()->create([
'id' => Str::slug($plan['name']),
'amount' => $plan['price'],
'currency' => config('cashier.currency'),
'interval' => 'month',
'product' => $product['id'],
]);
return compact('plan', 'product');
}
/**
* Update plan
*
* @param $request
* @param $id
*/
public function updatePlan($request, $id)
{
$plan_colls = ['is_active', 'price'];
$product_colls = ['name', 'description', 'capacity'];
$plan = $this->stripe->plans()->find($id);
// Update product
if (in_array($request->name, $product_colls)) {
if ($request->name === 'capacity') {
$this->stripe->products()->update($plan['product'], ['metadata' => ['capacity' => $request->value]]);
}
if ($request->name === 'name') {
$this->stripe->products()->update($plan['product'], ['name' => $request->value]);
}
if ($request->name === 'description') {
$this->stripe->products()->update($plan['product'], ['description' => $request->value]);
}
}
// Update plan
if (in_array($request->name, $plan_colls)) {
if ($request->name === 'is_active') {
$this->stripe->plans()->update($id, ['active' => $request->value]);
}
}
}
/**
* Delete plan
*
* @param $slug
*/
public function deletePlan($slug)
{
$this->stripe->plans()->delete($slug);
}
/**
* Get all user invoices
*
* @param $user
* @return mixed
*/
public function getUserInvoices($user)
{
return $user->invoices();
}
/**
* Get user invoice by id
*
* @param $id
* @return \Laravel\Cashier\Invoice|null
*/
public function getUserInvoice($customer, $id)
{
$user = User::where('stripe_id', $customer)->firstOrFail();
return $user->findInvoice($id);
}
/**
* Get all invoices
*
* @return mixed
*/
public function getInvoices()
{
return $this->stripe->invoices()->all([
'limit' => 20
]);
}
}

26
app/Setting.php Normal file
View File

@@ -0,0 +1,26 @@
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
/**
* App\Setting
*
* @property int $id
* @property string $name
* @property string|null $value
* @method static \Illuminate\Database\Eloquent\Builder|Setting newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|Setting newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|Setting query()
* @method static \Illuminate\Database\Eloquent\Builder|Setting whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder|Setting whereName($value)
* @method static \Illuminate\Database\Eloquent\Builder|Setting whereValue($value)
* @mixin \Eloquent
*/
class Setting extends Model
{
public $timestamps = false;
protected $guarded = ['id'];
}

View File

@@ -32,6 +32,8 @@ use Illuminate\Database\Eloquent\Model;
* @method static \Illuminate\Database\Eloquent\Builder|\App\Share whereUpdatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\Share whereUserId($value)
* @mixin \Eloquent
* @property int|null $expire_in
* @method static \Illuminate\Database\Eloquent\Builder|Share whereExpireIn($value)
*/
class Share extends Model
{

30
app/Traffic.php Normal file
View File

@@ -0,0 +1,30 @@
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
/**
* App\Traffic
*
* @property int $id
* @property int $user_id
* @property int $upload
* @property int $download
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @method static \Illuminate\Database\Eloquent\Builder|Traffic newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|Traffic newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|Traffic query()
* @method static \Illuminate\Database\Eloquent\Builder|Traffic whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|Traffic whereDownload($value)
* @method static \Illuminate\Database\Eloquent\Builder|Traffic whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder|Traffic whereUpdatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|Traffic whereUpload($value)
* @method static \Illuminate\Database\Eloquent\Builder|Traffic whereUserId($value)
* @mixin \Eloquent
*/
class Traffic extends Model
{
protected $fillable = ['user_id', 'upload', 'download'];
}

View File

@@ -5,12 +5,14 @@ namespace App;
use App\Notifications\ResetPassword;
use App\Notifications\ResetUserPasswordNotification;
use ByteUnits\Metric;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Carbon\Carbon;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Notification;
use Illuminate\Support\Facades\Storage;
use Laravel\Cashier\Billable;
use Laravel\Passport\HasApiTokens;
use Kyslik\ColumnSortable\Sortable;
use Rinvex\Subscriptions\Traits\HasSubscriptions;
/**
* App\User
@@ -52,10 +54,33 @@ use Laravel\Passport\HasApiTokens;
* @method static \Illuminate\Database\Eloquent\Builder|\App\User whereRememberToken($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\User whereUpdatedAt($value)
* @mixin \Eloquent
* @property string $role
* @property string|null $stripe_id
* @property string|null $card_brand
* @property string|null $card_last_four
* @property string|null $trial_ends_at
* @property-read \Illuminate\Database\Eloquent\Collection|\App\FileManagerFolder[] $favourite_folders
* @property-read int|null $favourite_folders_count
* @property-read mixed $folder_tree
* @property-read mixed $storage
* @property-read \Illuminate\Database\Eloquent\Collection|\App\Invoice[] $invoices
* @property-read int|null $invoices_count
* @property-read int|null $payment_cards_count
* @property-read \App\UserSettings|null $settings
* @property-read \Illuminate\Database\Eloquent\Collection|\Laravel\Cashier\Subscription[] $subscriptions
* @property-read int|null $subscriptions_count
* @method static \Illuminate\Database\Eloquent\Builder|\App\User whereCardBrand($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\User whereCardLastFour($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\User whereRole($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\User whereStripeId($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\User whereTrialEndsAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|User sortable($defaultParameters = null)
*/
class User extends Authenticatable
{
use HasApiTokens, Notifiable;
use HasApiTokens, Notifiable, Billable, Sortable;
protected $guarded = ['id', 'role'];
/**
* The attributes that are mass assignable.
@@ -82,27 +107,103 @@ class User extends Authenticatable
*/
protected $casts = [
'email_verified_at' => 'datetime',
'favourites' => 'array',
];
protected $appends = [
'used_capacity'
'used_capacity', 'storage'
];
/**
* Sortable columns
*
* @var string[]
*/
public $sortable = [
'id',
'name',
'role',
'created_at',
'storage_capacity',
];
/**
* Get tax rate id for user
*
* @return array
*/
public function taxRates()
{
$stripe = resolve('App\Services\StripeService');
// Get tax rates
$rates = collect($stripe->getTaxRates());
// Find tax rate
$user_tax_rate = $rates->first(function ($item) {
return $item['jurisdiction'] === $this->settings->billing_country && $item['active'];
});
return $user_tax_rate ? [$user_tax_rate['id']] : [];
}
/**
* Get user used storage details
*
* @return mixed
*/
public function getStorageAttribute()
{
// Get storage limitation setup
$storage_limitation = get_setting('storage_limitation');
$is_storage_limit = $storage_limitation ? $storage_limitation : 1;
// Get user storage usage
if (!$is_storage_limit) {
return [
'used' => $this->used_capacity,
'used_formatted' => Metric::bytes($this->used_capacity)->format(),
];
}
return [
'used' => (float)get_storage_fill_percentage($this->used_capacity, $this->settings->storage_capacity),
'used_formatted' => get_storage_fill_percentage($this->used_capacity, $this->settings->storage_capacity) . '%',
'capacity' => $this->settings->storage_capacity,
'capacity_formatted' => format_gigabytes($this->settings->storage_capacity),
];
}
/**
* Get user used storage capacity in bytes
*
* @return mixed
*/
public function getUsedCapacityAttribute() {
public function getUsedCapacityAttribute()
{
$user_capacity = $this->files_with_trashed->map(function ($item) {
return $item->getOriginal();
return $item->getRawOriginal();
})->sum('filesize');
return $user_capacity;
}
/**
* Get user full folder tree
*
* @return \Illuminate\Database\Eloquent\Builder[]|\Illuminate\Database\Eloquent\Collection
*/
public function getFolderTreeAttribute()
{
// Get sorting setup
return FileManagerFolder::with(['folders.shared', 'shared:token,id,item_id,permission,protected,expire_in'])
->where('parent_id', 0)
->where('user_id', $this->id)
->sortable()
->get();
}
/**
* Format avatar to full url
*
@@ -110,6 +211,12 @@ class User extends Authenticatable
*/
public function getAvatarAttribute()
{
// Get avatar from external storage
if ($this->attributes['avatar'] && is_storage_driver(['s3', 'spaces', 'wasabi', 'backblaze'])) {
return Storage::temporaryUrl($this->attributes['avatar'], now()->addDay());
}
// Get avatar from local storage
if ($this->attributes['avatar']) {
return url('/' . $this->attributes['avatar']);
}
@@ -117,6 +224,27 @@ class User extends Authenticatable
return url('/assets/images/' . 'default-avatar.png');
}
/**
* Set user billing info
*
* @param $billing
* @return UserSettings
*/
public function setBilling($billing)
{
$this->settings()->update([
'billing_address' => $billing['billing_address'],
'billing_city' => $billing['billing_city'],
'billing_country' => $billing['billing_country'],
'billing_name' => $billing['billing_name'],
'billing_phone_number' => $billing['billing_phone_number'],
'billing_postal_code' => $billing['billing_postal_code'],
'billing_state' => $billing['billing_state'],
]);
return $this->settings;
}
/**
* Send the password reset notification.
*
@@ -128,14 +256,54 @@ class User extends Authenticatable
$this->notify(new ResetPassword($token));
}
/**
* Record user upload filesize
*
* @param $file_size
*/
public function record_upload($file_size)
{
$now = Carbon::now();
$record = Traffic::whereYear('created_at', '=', $now->year)
->whereMonth('created_at', '=', $now->month)
->firstOrCreate([
'user_id' => $this->id,
]);
$record->update([
'upload' => $record->upload + $file_size
]);
}
/**
* Record user download filesize
*
* @param $file_size
*/
public function record_download($file_size)
{
$now = Carbon::now();
$record = Traffic::whereYear('created_at', '=', $now->year)
->whereMonth('created_at', '=', $now->month)
->firstOrCreate([
'user_id' => $this->id,
]);
$record->update([
'download' => $record->download + $file_size
]);
}
/**
* Get user favourites folder
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function favourites()
public function favourite_folders()
{
return $this->belongsToMany(FileManagerFolder::class, 'favourite_folder', 'user_id', 'folder_unique_id', 'id', 'unique_id')->select(['unique_id', 'name', 'type']);
return $this->belongsToMany(FileManagerFolder::class, 'favourite_folder', 'user_id', 'folder_unique_id', 'id', 'unique_id')->with('shared:token,id,item_id,permission,protected,expire_in');
}
/**
@@ -143,9 +311,9 @@ class User extends Authenticatable
*
* @return \Illuminate\Database\Eloquent\Relations\HasMany|\Illuminate\Database\Query\Builder
*/
public function latest_uploads() {
return $this->hasMany(FileManagerFile::class)->orderBy('created_at', 'DESC')->take(7);
public function latest_uploads()
{
return $this->hasMany(FileManagerFile::class)->with(['parent'])->take(40);
}
/**
@@ -153,8 +321,8 @@ class User extends Authenticatable
*
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function files() {
public function files()
{
return $this->hasMany(FileManagerFile::class);
}
@@ -163,8 +331,18 @@ class User extends Authenticatable
*
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function files_with_trashed() {
public function files_with_trashed()
{
return $this->hasMany(FileManagerFile::class)->withTrashed();
}
/**
* Get user attributes
*
* @return \Illuminate\Database\Eloquent\Relations\HasOne
*/
public function settings()
{
return $this->hasOne(UserSettings::class);
}
}

40
app/UserSettings.php Normal file
View File

@@ -0,0 +1,40 @@
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
/**
* App\UserSettings
*
* @property int $id
* @property int $user_id
* @property int $storage_capacity
* @property string|null $billing_name
* @property string|null $billing_address
* @property string|null $billing_state
* @property string|null $billing_city
* @property string|null $billing_postal_code
* @property string|null $billing_country
* @property string|null $billing_phone_number
* @method static \Illuminate\Database\Eloquent\Builder|UserSettings newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|UserSettings newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|UserSettings query()
* @method static \Illuminate\Database\Eloquent\Builder|UserSettings whereBillingAddress($value)
* @method static \Illuminate\Database\Eloquent\Builder|UserSettings whereBillingCity($value)
* @method static \Illuminate\Database\Eloquent\Builder|UserSettings whereBillingCountry($value)
* @method static \Illuminate\Database\Eloquent\Builder|UserSettings whereBillingName($value)
* @method static \Illuminate\Database\Eloquent\Builder|UserSettings whereBillingPhoneNumber($value)
* @method static \Illuminate\Database\Eloquent\Builder|UserSettings whereBillingPostalCode($value)
* @method static \Illuminate\Database\Eloquent\Builder|UserSettings whereBillingState($value)
* @method static \Illuminate\Database\Eloquent\Builder|UserSettings whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder|UserSettings whereStorageCapacity($value)
* @method static \Illuminate\Database\Eloquent\Builder|UserSettings whereUserId($value)
* @mixin \Eloquent
*/
class UserSettings extends Model
{
public $timestamps = false;
protected $guarded = ['id', 'storage_capacity'];
}

27
app/Zip.php Normal file
View File

@@ -0,0 +1,27 @@
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Str;
class Zip extends Model
{
protected $guarded = ['id'];
public $incrementing = false;
protected $keyType = 'string';
/**
* Generate uuid
*/
protected static function boot()
{
parent::boot();
static::creating(function ($model) {
$model->id = (string)Str::uuid();
});
}
}

Some files were not shown because too many files have changed in this diff Show More