diff --git a/README.md b/README.md index 1b41c1e..ae3e5be 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,7 @@ new Vue({ - multi - upload - - move + - move/copy ***"toggle between"*** - delete - bulk selection - restrict access to [folders](https://github.com/ctf0/Laravel-Media-Manager/wiki/Folder-Restriction) @@ -88,7 +88,7 @@ new Vue({ + all + selected + search found -- protection against overwriting (files / folders) +- protection against overwriting (files/folders) - autoplay media files ***"if selected filter is audio/video"*** - file name sanitization for + upload @@ -97,46 +97,50 @@ new Vue({ - disable/enable buttons depend on the usage to avoid noise & keep the user focused - shortcuts - | navigation | button | keyboard | mouse (click) | touch | - |----------------|------------------------------------|--------------|---------------|-------------| - | | upload *(toolbar)* | u | * | | - | | refresh *(toolbar)* | r | * | | - | | move *(toolbar)* | m | * | swipe up | - | | delete *(toolbar)* | d/del | * | swipe down | - | | lock/unlock *(toolbar)* | l | * | | - | | bulk select *(toolbar)* | b | * | | - | | bulk select all *(toolbar)* | a | * | | - | |   | | | | - | | confirm rename *(modal)* | enter | * | | - | | confirm delete *(modal)* | enter | * | | - | | confirm move *(modal)* | enter | * | | - | | create new folder *(modal)* | enter | * | | - | |   | | | | - | | toggle *(info panel)* | t | * | | - | | play/pause media *(sidebar)* | space | * | | - | | preview image/pdf/text *(sidebar)* | space | * | tap | - | | preview image/pdf/text | space | 2x click | 2x tap | - | | hide image *(light-box)* | space/esc | * | | - | select next | | right / down | * | swipe left | - | select prev | | left / up | * | swipe right | - | select first | | home | * | | - | select last | | end | * | | - | open folder | | enter | 2x click | 2x tap | - | go to prev dir | folderName *(breadcrumb)* | backspace | * | swipe right | + | navigation | button | keyboard | mouse (click) | touch | + |----------------|--------------------------------------------|---------------|---------------|---------------------------| + | | upload *(toolbar)* | u | * | | + | | refresh *(toolbar)* | r | * | | + | | move *(toolbar)* | m | * | swipe up | + | | delete *(toolbar)* | d/del | * | swipe down | + | | lock/unlock *(toolbar)* | l | * | | + | | (reset) bulk select *(toolbar)* | b | * | | + | | (reset) bulk select all *(toolbar)* | a | * | | + | | cancel bulk selection | esc | | | + | | cancel search *(toolbar)* | esc | * | | + | |   | | | | + | | toggle *(sidebar)* | t | * | | + | | play/pause media *(sidebar)* | space | * | | + | | preview image/pdf/text *(sidebar)* | space | * | tap | + | |   | | | | + | | confirm rename *(modal)* | enter | * | | + | | confirm delete *(modal)* | enter | * | | + | | confirm move *(modal)* | enter | * | | + | | create new folder *(modal)* | enter | * | | + | |   | | | | + | | limit bulk select *(files container)* | shift + click | | | + | | preview image/pdf/text *(files container)* | space | ** | 2x tap | + | | hide light-box | space/esc | * | | + | select next | | right / down | * | swipe left *(light-box)* | + | select prev | | left / up | * | swipe right *(light-box)* | + | select first | | home | * | | + | select last | | end | * | | + | open folder | | enter | ** | 2x tap | + | go to prev dir | folderName *(breadcrumb)* | backspace | * | swipe right | - events - | type | event-name | description | - |---------|---------------------------------------|------------------------------------------| - | [JS](https://github.com/gocanto/vuemit) | | | - | | modal-show | when modal is showen | - | | modal-hide | when modal is hidden | - | | file_selected *(when inside modal)* | get selected file url | - | [Laravel](https://laravel.com/docs/5.5/events#manually-registering-events) | | | - | | MMFileUploaded($file_path) | get uploaded file full path | - | | MMFileDeleted($file_path, $is_folder) | get deleted file/folder full path | - | | MMFileRenamed($old_path, $new_path) | get renamed file/folder "old & new" path | - | | MMFileMoved($old_path, $new_path) | get moved file/folder "old & new" path | + | type | event-name | description | + |---------|---------------------------------------|------------------------------------------| + | [JS](https://github.com/gocanto/vuemit) | + | | modal-show | when modal is showen | + | | modal-hide | when modal is hidden | + | | file_selected *(when inside modal)* | get selected file url | + | [Laravel](https://laravel.com/docs/5.5/events#manually-registering-events) | + | | MMFileUploaded($file_path) | get uploaded file full path | + | | MMFileDeleted($file_path, $is_folder) | get deleted file/folder full path | + | | MMFileRenamed($old_path, $new_path) | get renamed file/folder "old & new" path | + | | MMFileMoved($old_path, $new_path) | get moved file/folder "old & new" path |
@@ -194,3 +198,5 @@ return [ ## Usage - visit `localhost:8000/media` +- [Wiki](https://github.com/ctf0/Laravel-Media-Manager/wiki) + diff --git a/logs/v2.3.6.txt b/logs/v2.3.6.txt deleted file mode 100644 index 2aaba76..0000000 --- a/logs/v2.3.6.txt +++ /dev/null @@ -1,25 +0,0 @@ -# Fix - -- show correct total size in delete modal -- add some sense to css classes -- move dropzone file input inside its own form so you don’t have a bazillion inputs at the end of the DOM on each toggle of the modal -- some cleanup to the media-controller - -# New - -- add support for WYSIWYG Editors - https://github.com/ctf0/Laravel-Media-Manager/wiki/Use-The-Manager-With-Any-WYSIWYG-Editor -- sidebar no selection icon is now a btn to reset “bulk select & search” - -# Zip - -- you can now download folders as a zip as well -- the zipping is now done on server & downloaded as a stream, so no worries about memory issues nor you need to install any extra js packages -- sadly no more progress for zipping as the form submit is not being handled by js, so any ideas/PRs are welcome - - -# Update - -- update readme -- update assets -- update wiki \ No newline at end of file diff --git a/logs/v2.3.7.txt b/logs/v2.3.7.txt new file mode 100644 index 0000000..ac35c40 --- /dev/null +++ b/logs/v2.3.7.txt @@ -0,0 +1,30 @@ +## New + +- you can now use “shift + click” to limit bulk selection to specific files. +- added new shortcuts +- you can now copy files/folders instead of moving as well. +- add new option for path breadcrumb for mobile to make it easier to navigate +- add audio for success & alert ops +- don't reset the selection if (deleting/moving/copying) an item has returned an error. + +## Fix + +- some fixes & cleanups here and there +- fixed issue with local-storage overriding the original keys +- fixed update folder info after moving +- fixed not selecting an item after getting the files from server “rare” +- fixed refreshing the manager when using “key modifier + r” +- fixed the hardcoded translation & now all handled through laravel. +- fixed pre-mature selection of first item after delete/move form submit +- fixed showing an empty notification +- fixed axios showing vague error message + +## Update + +- cleanup & optimization “js, controller” +- slightly better scroll to item +- change view files structure +- make file-box smaller on mobile +- update read me +- update wiki +- update resources diff --git a/src/Controllers/MediaController.php b/src/Controllers/MediaController.php index b76089f..bbc86d4 100755 --- a/src/Controllers/MediaController.php +++ b/src/Controllers/MediaController.php @@ -18,24 +18,30 @@ class MediaController extends Controller protected $folderChars; protected $sanitizedText; protected $unallowed_mimes; - protected $locked_files_list; protected $LMF; + protected $locked_files_list; + protected $disks; + public function __construct() { - $this->fileSystem = config('mediaManager.storage_disk'); - $this->storageDisk = app('filesystem')->disk($this->fileSystem); - $this->ignoreFiles = config('mediaManager.ignore_files'); - $this->fileChars = config('mediaManager.allowed_fileNames_chars'); - $this->folderChars = config('mediaManager.allowed_folderNames_chars'); - $this->sanitizedText = config('mediaManager.sanitized_text'); - $this->unallowed_mimes = config('mediaManager.unallowed_mimes'); - $this->LMF = config('mediaManager.last_modified_format'); - $this->locked_files_list = config('mediaManager.locked_files_list'); + $config = config('mediaManager'); + + $this->fileSystem = array_get($config, 'storage_disk'); + $this->storageDisk = app('filesystem')->disk($this->fileSystem); + $this->ignoreFiles = array_get($config, 'ignore_files'); + $this->fileChars = array_get($config, 'allowed_fileNames_chars'); + $this->folderChars = array_get($config, 'allowed_folderNames_chars'); + $this->sanitizedText = array_get($config, 'sanitized_text'); + $this->unallowed_mimes = array_get($config, 'unallowed_mimes'); + $this->LMF = array_get($config, 'last_modified_format'); + + $this->locked_files_list = array_get($config, 'locked_files_list'); + $this->disks = config("filesystems.disks.{$this->fileSystem}"); } /** - * [index description]. + * main view. * * @return [type] [description] */ @@ -45,7 +51,7 @@ public function index() } /** - * [files description]. + * get files in path. * * @param Request $request [description] * @@ -71,7 +77,7 @@ public function get_files(Request $request) } /** - * [get_all_dirs description]. + * get all directories in path. * * @param Request $request [description] * @@ -91,7 +97,7 @@ public function get_dirs(Request $request) } /** - * [upload description]. + * upload new files. * * @param Request $request [description] * @@ -128,7 +134,7 @@ public function upload(Request $request) $saved_name = $one->storeAs($upload_path, $file_name, $this->fileSystem); // fire event - event('MMFileUploaded', $this->getFilePath($this->fileSystem, $saved_name)); + event('MMFileUploaded', $this->getFilePath($saved_name)); $result[] = [ 'path' => preg_replace('/^public\//', '', $saved_name), @@ -148,7 +154,7 @@ public function upload(Request $request) } /** - * [new_folder description]. + * create new folder. * * @param Request $request [description] * @@ -174,7 +180,7 @@ public function new_folder(Request $request) } /** - * [delete_file description]. + * delete files/folders. * * @param Request $request [description] * @@ -209,7 +215,7 @@ public function delete_file(Request $request) } else { // fire event event('MMFileDeleted', [ - 'file_path' => $this->getFilePath($this->fileSystem, $file_name), + 'file_path' => $this->getFilePath($file_name), 'is_folder' => true, ]); } @@ -222,7 +228,7 @@ public function delete_file(Request $request) } else { // fire event event('MMFileDeleted', [ - 'file_path' => $this->getFilePath($this->fileSystem, $file_name), + 'file_path' => $this->getFilePath($file_name), 'is_folder' => false, ]); } @@ -233,7 +239,7 @@ public function delete_file(Request $request) } /** - * [move_file description]. + * move files/folders. * * @param Request $request [description] * @@ -242,6 +248,7 @@ public function delete_file(Request $request) public function move_file(Request $request) { $folderLocation = $request->folder_location; + $copy = $request->use_copy; $result = []; if (is_array($folderLocation)) { @@ -268,22 +275,71 @@ public function move_file(Request $request) try { if (!file_exists($new_path)) { - if ($this->storageDisk->move($old_path, $new_path)) { - $result[] = [ - 'success' => true, - 'name' => $one['name'], - 'items' => $file_items, - 'type' => $file_type, - 'size' => $file_size, - ]; - - // fire event - event('MMFileMoved', [ - 'old_path' => $this->getFilePath($this->fileSystem, $old_path), - 'new_path' => $this->getFilePath($this->fileSystem, $new_path), - ]); - } else { - throw new Exception(trans('MediaManager::messages.error_moving')); + // copy + if ($copy) { + // folders + if ('folder' == $one['type']) { + $old = $this->getFilePath($old_path); + $new = $this->getFilePath($new_path); + + if (app('files')->copyDirectory($old, $new)) { + $result[] = [ + 'success' => true, + 'name' => $one['name'], + 'items' => $file_items, + 'type' => $file_type, + 'size' => $file_size, + ]; + } else { + $exc = array_get($this->disks, 'root') + ? trans('MediaManager::messages.error_moving') + : trans('MediaManager::messages.error_moving_cloud'); + + throw new Exception($exc); + } + } + + // files + else { + if ($this->storageDisk->copy($old_path, $new_path)) { + $result[] = [ + 'success' => true, + 'name' => $one['name'], + 'items' => $file_items, + 'type' => $file_type, + 'size' => $file_size, + ]; + } else { + throw new Exception(trans('MediaManager::messages.error_moving')); + } + } + } + + // move + else { + if ($this->storageDisk->move($old_path, $new_path)) { + $result[] = [ + 'success' => true, + 'name' => $one['name'], + 'items' => $file_items, + 'type' => $file_type, + 'size' => $file_size, + ]; + + // fire event + event('MMFileMoved', [ + 'old_path' => $this->getFilePath($old_path), + 'new_path' => $this->getFilePath($new_path), + ]); + } else { + $exc = trans('MediaManager::messages.error_moving'); + + if ('folder' == $one['type'] && !array_get($this->disks, 'root')) { + $exc = trans('MediaManager::messages.error_moving_cloud'); + } + + throw new Exception($exc); + } } } else { throw new Exception(trans('MediaManager::messages.error_already_exists')); @@ -300,7 +356,7 @@ public function move_file(Request $request) } /** - * [rename_file description]. + * rename item. * * @param Request $request [description] * @@ -317,28 +373,34 @@ public function rename_file(Request $request) $folderLocation = rtrim(implode('/', $folderLocation), '/'); } - if (!$this->storageDisk->exists("$folderLocation/$new_filename")) { - if ($this->storageDisk->move("$folderLocation/$filename", "$folderLocation/$new_filename")) { - $message = ''; - $success = true; + $old_path = "$folderLocation/$filename"; + $new_path = "$folderLocation/$new_filename"; - // fire event - event('MMFileRenamed', [ - 'old_path' => $this->getFilePath($this->fileSystem, "$folderLocation/$filename"), - 'new_path' => $this->getFilePath($this->fileSystem, "$folderLocation/$new_filename"), - ]); + try { + if (!$this->storageDisk->exists($new_path)) { + if ($this->storageDisk->move($old_path, $new_path)) { + $success = true; + + // fire event + event('MMFileRenamed', [ + 'old_path' => $this->getFilePath($old_path), + 'new_path' => $this->getFilePath($new_path), + ]); + } else { + throw new Exception(trans('MediaManager::messages.error_moving')); + } } else { - $message = trans('MediaManager::messages.error_moving'); + throw new Exception(trans('MediaManager::messages.error_already_exists')); } - } else { - $message = trans('MediaManager::messages.error_already_exists'); + } catch (Exception $e) { + $message = $e->getMessage(); } return compact('success', 'message', 'new_filename'); } /** - * [lock_file description]. + * lock/unlock files/folders. * * @param Request $request [description] * @@ -358,7 +420,7 @@ public function lock_file(Request $request) } /** - * zip folder. + * zip folders. * * @param Request $request [description] * @@ -375,10 +437,11 @@ public function folder_download(Request $request) ]); foreach ($this->storageDisk->allFiles($dir) as $file) { - $name = pathinfo($file, PATHINFO_BASENAME); - $stream = $this->storageDisk->readStream($file); - - $zip->addFileFromStream($name, $stream); + if ($streamRead = $this->storageDisk->readStream($file)) { + $zip->addFileFromStream(pathinfo($file, PATHINFO_BASENAME), $streamRead); + } else { + die('Could not open stream for reading'); + } } $zip->finish(); @@ -394,8 +457,8 @@ public function folder_download(Request $request) */ public function files_download(Request $request) { - $name = $request->name; - $list = json_decode($request->list, true); + $name = $request->name; + $list = json_decode($request->list, true); return response()->stream(function () use ($name, $list) { $zip = new ZipStream("$name.zip", [ @@ -403,8 +466,11 @@ public function files_download(Request $request) ]); foreach ($list as $file) { - $stream = fopen($file['path'], 'r'); - $zip->addFileFromStream($file['name'], $stream); + if ($streamRead = fopen($file['path'], 'r')) { + $zip->addFileFromStream($file['name'], $streamRead); + } else { + die('Could not open stream for reading'); + } } $zip->finish(); diff --git a/src/Controllers/OpsTrait.php b/src/Controllers/OpsTrait.php index d17d046..7998fef 100644 --- a/src/Controllers/OpsTrait.php +++ b/src/Controllers/OpsTrait.php @@ -18,8 +18,7 @@ protected function getData($dir) $files = []; $storageFiles = $this->storageDisk->files($dir); $storageFolders = $this->storageDisk->directories($dir); - - $pattern = $this->ignoreFiles; + $pattern = $this->ignoreFiles; foreach ($storageFolders as $folder) { if (!preg_grep($pattern, [$folder])) { @@ -74,9 +73,9 @@ protected function cleanName($text, $folder = null) return '' == $text ? $this->sanitizedText : $text; } - protected function filePattern($file) + protected function filePattern($item) { - return '/(script.*?\/script)|[^(' . $file . ')a-zA-Z0-9]|\(|\)+/ius'; + return '/(script.*?\/script)|[^(' . $item . ')a-zA-Z0-9]|\(|\)+/ius'; } /** @@ -88,7 +87,10 @@ protected function filePattern($file) */ protected function folderCount($folder) { + // files + directories count // return count($this->folderFiles($folder)) + count($this->storageDisk->allDirectories($folder)); + + // files only return count($this->folderFiles($folder)); } @@ -116,12 +118,12 @@ protected function folderFiles($folder) * * @return [type] [description] */ - protected function getFilePath($disk, $name) + protected function getFilePath($name) { - $config = config("filesystems.disks.$disk"); - $url = app('filesystem')->disk($disk)->url($name); - $dir = str_replace(array_get($config, 'url'), '', $url); - $root = array_get($config, 'root'); + $disks = $this->disks; + $url = $this->storageDisk->url($name); + $dir = str_replace(array_get($disks, 'url'), '', $url); + $root = array_get($disks, 'root'); // for other disks without root ex."cloud" if (!$root) { diff --git a/src/dist/audio/alert.mp3 b/src/dist/audio/alert.mp3 new file mode 100644 index 0000000..9a0e8b3 Binary files /dev/null and b/src/dist/audio/alert.mp3 differ diff --git a/src/dist/audio/success.mp3 b/src/dist/audio/success.mp3 new file mode 100644 index 0000000..f8a58cb Binary files /dev/null and b/src/dist/audio/success.mp3 differ diff --git a/src/resources/assets/js/components/media.vue b/src/resources/assets/js/components/media.vue index e674375..08295c0 100644 --- a/src/resources/assets/js/components/media.vue +++ b/src/resources/assets/js/components/media.vue @@ -56,6 +56,7 @@ export default { folderWarning: false, checkForFolders: false, randomNames: false, + useCopy: false, toolBar: true, files: [], @@ -117,10 +118,10 @@ export default { let ls = this.$ls.get('mediamanager') if (ls) { - this.randomNames = ls.randomNames ? ls.randomNames : false - this.folders = ls.folders ? ls.folders : [] - this.toolBar = ls.toolBar ? ls.toolBar : true - this.selectedFile = ls.selectedFileName ? ls.selectedFileName : null + this.randomNames = ls.randomNames == 'undefined' ? false : ls.randomNames + this.folders = ls.folders == 'undefined' ? [] : ls.folders + this.toolBar = ls.toolBar == 'undefined' ? true : ls.toolBar + this.selectedFile = ls.selectedFileName == 'undefined' ? null : ls.selectedFileName } }, @@ -171,11 +172,11 @@ export default { manager.progressCounter = 0 manager.showProgress = false - setTimeout(() => { - last - ? manager.getFiles(manager.folders, null, last) - : manager.getFiles(manager.folders) - }, 500) + manager.$refs['success-audio'].play() + + last + ? manager.getFiles(manager.folders, null, last) + : manager.getFiles(manager.folders) } }, errormultiple(files, res) { @@ -186,7 +187,7 @@ export default { /* end of upload */ shortCuts(e) { - if (!this.isLoading) { + if (!(this.isLoading || e.altKey || e.ctrlKey || e.metaKey)) { // when modal isnt visible if (!this.active_modal) { // when search is not focused @@ -264,10 +265,19 @@ export default { } } - // add all to bulk list - if (this.isBulkSelecting() && keycode(e) == 'a') { - if (this.$refs.bulkSelectAll) { - this.$refs.bulkSelectAll.click() + if (this.isBulkSelecting()) { + // add all to bulk list + if (keycode(e) == 'a') { + if (this.$refs.bulkSelectAll) { + this.$refs.bulkSelectAll.click() + } + } + + // cancel bulk selection + if (keycode(e) == 'esc') { + if (this.$refs.bulkSelect) { + this.$refs.bulkSelect.click() + } } } @@ -300,6 +310,11 @@ export default { } } /* end of search is not focused */ + + // cancel search + else if (keycode(e) == 'esc') { + this.resetInput('searchFor') + } } /* end of modal isnt visible */ diff --git a/src/resources/assets/js/components/mixins/form.js b/src/resources/assets/js/components/mixins/form.js index 5deedc0..9ca59e8 100644 --- a/src/resources/assets/js/components/mixins/form.js +++ b/src/resources/assets/js/components/mixins/form.js @@ -53,6 +53,11 @@ export default { }) } + // we have files + if (this.allItemsCount) { + this.selectFirst() + } + // check for prev opened folder if (prev_folder) { this.$nextTick(() => { @@ -77,11 +82,6 @@ export default { // we have files if (this.allItemsCount) { - // now prev files / folders - if (!prev_folder && !prev_file) { - this.selectFirst() - } - this.toggleLoading() this.loadingFiles('hide') this.toggleInfo = true @@ -93,6 +93,13 @@ export default { } }, 800) + // scroll to breadcrumb item + setTimeout(() => { + let name = folders.split('/').pop() + let count = document.getElementById(`${name ? name : 'home'}-bc`).offsetLeft + this.$refs.bc.scrollBy({top: 0, left: count, behavior: 'smooth'}) + }, 100) + return this.updateDirsList() } @@ -165,7 +172,7 @@ export default { return this.showNotif(data.message, 'danger') } - this.showNotif(`Successfully Created "${data.new_folder_name}" at "${data.full_path}"`) + this.showNotif(`${this.trans('create_success')} "${data.new_folder_name}" at "${data.full_path}"`) this.getFiles(this.folders, data.new_folder_name) }).catch((err) => { @@ -204,7 +211,7 @@ export default { return this.showNotif(data.message, 'danger') } - this.showNotif(`Successfully Renamed "${filename}" to "${data.new_filename}"`) + this.showNotif(`${this.trans('rename_success')} "${filename}" to "${data.new_filename}"`) this.updateItemName(this.selectedFile, filename, data.new_filename) if (this.selectedFileIs('folder')) { @@ -220,55 +227,65 @@ export default { // move MoveFileForm(event) { if (this.checkForFolders) { - if (this.bulkItemsCount) { - this.move_file(this.bulkListFilter, event.target.action) + let list = this.bulkItemsCount + ? this.bulkListFilter + : [this.selectedFile] - setTimeout(() => { - this.blkSlct() - }, 100) - } else { - this.move_file([this.selectedFile], event.target.action) - } + this.move_file(list, event.target.action) } }, move_file(files, routeUrl) { this.toggleLoading() let destination = this.moveToPath + let copy = this.useCopy + let error = false axios.post(routeUrl, { folder_location: this.files.path, destination: destination, - moved_files: files + moved_files: files, + use_copy: copy }).then(({data}) => { this.toggleLoading() data.data.map((item) => { if (!item.success) { + error = true return this.showNotif(item.message, 'danger') } - this.showNotif(`Successfully moved "${item.name}" to "${destination}"`) - this.removeFromLists(item.name, item.type) + // copy + if (copy) { + this.showNotif(`${this.trans('copy_success')} "${item.name}" to "${destination}"`) + } - // update folder count when folder is moved into another - if (this.fileTypeIs(item, 'folder')) { - if (item.items > 0) { - this.updateFolderCount(destination, item.items, item.size) - } + // move + else { + this.showNotif(`${this.trans('move_success')} "${item.name}" to "${destination}"`) + this.removeFromLists(item.name, item.type) // update dirs list after move this.updateDirsList() - } else { - this.updateFolderCount(destination, 1, item.size) + + // update search count + if (this.searchFor) { + this.searchItemsCount = this.filesList.length + } } + + // update folder count when folder is moved/copied into another + this.fileTypeIs(item, 'folder') + ? this.updateFolderCount(destination, item.items, item.size) + : this.updateFolderCount(destination, 1, item.size) }) + this.$refs['success-audio'].play() this.toggleModal() - this.selectFirst() - if (this.searchFor) { - this.searchItemsCount = this.filesList.length - } + + this.isBulkSelecting() + ? this.blkSlct() + : error ? false : this.selectFirst() }).catch((err) => { console.error(err) @@ -278,15 +295,11 @@ export default { // delete DeleteFileForm(event) { - if (this.bulkItemsCount) { - this.delete_file(this.bulkListFilter, event.target.action) - - setTimeout(() => { - this.blkSlct() - }, 100) - } else { - this.delete_file([this.selectedFile], event.target.action) - } + let list = this.bulkItemsCount + ? this.bulkListFilter + : [this.selectedFile] + + this.delete_file(list, event.target.action) }, delete_file(files, routeUrl) { this.toggleLoading() @@ -302,12 +315,13 @@ export default { return this.showNotif(item.message, 'danger') } - this.showNotif(`Successfully Deleted "${item.name}"`, 'warning') + this.showNotif(`${this.trans('delete_success')} "${item.name}"`, 'warning') this.removeFromLists(item.name, item.type) }) + this.$refs['success-audio'].play() this.toggleModal() - this.selectFirst() + this.isBulkSelecting() ? this.blkSlct() : this.selectFirst() if (this.searchFor) { this.searchItemsCount = this.filesList.length } @@ -367,12 +381,15 @@ export default { if (destination !== '../') { if (destination.includes('/')) { - destination = destination.split('/').shift() + // get the first dir in the path + // because this is what the user is currently viewing + destination = destination.split('/') + destination = destination[0] == '' ? destination[1] : destination[0] } if (this.filteredItemsCount) { - this.filterdList.map((e) => { - if (e.name.includes(destination)) { + this.filterdList.some((e) => { + if (e.name == destination) { e.items += parseInt(count) e.size += parseInt(weight) } @@ -380,7 +397,7 @@ export default { } this.files.items.some((e) => { - if (e.name.includes(destination)) { + if (e.name == destination) { e.items += parseInt(count) e.size += parseInt(weight) } diff --git a/src/resources/assets/js/components/mixins/selected.js b/src/resources/assets/js/components/mixins/selected.js index 2541d6d..68e71fe 100644 --- a/src/resources/assets/js/components/mixins/selected.js +++ b/src/resources/assets/js/components/mixins/selected.js @@ -10,10 +10,29 @@ export default { } }) }, - setSelected(file, index) { + setSelected(file, index, e = null) { + // select with shift + if (e && e.shiftKey) { + this.bulkSelect = true + + // normal + let begin = this.currentFileIndex + let end = index + 1 + + // reverse + if (begin > index) { + begin = index + end = this.currentFileIndex + 1 + } + + return this.bulkList = this.allFiles.slice(begin, end) + } + + // normal selection this.selectedFile = file this.currentFileIndex = index + // bulk selection if (this.isBulkSelecting()) { this.pushtoBulkList(file) } @@ -131,7 +150,15 @@ export default { scrollToFile(file) { file = file[0] file.click() - file.scrollIntoView({behavior: 'smooth', block: 'end', inline: 'end'}) + + let container = this.$refs['__stack-files'].$el + let count = file.offsetTop - container.scrollTop - 20 + container.scrollBy({top: count, left: 0, behavior: 'smooth'}) + + // when scrollBy() doesnt work + if (!(container.scrollHeight > container.clientHeight)) { + file.scrollIntoView({behavior: 'smooth', block: 'end', inline: 'end'}) + } } } } diff --git a/src/resources/assets/js/components/mixins/utils.js b/src/resources/assets/js/components/mixins/utils.js index a0d3fd5..66bb14f 100644 --- a/src/resources/assets/js/components/mixins/utils.js +++ b/src/resources/assets/js/components/mixins/utils.js @@ -9,29 +9,34 @@ export default { return this.active_modal == el }, showNotif(msg, s = 'success') { - - let title - let duration = 3 - - switch (s) { - case 'black': - case 'danger': - title = 'Error' - duration = null - break - case 'warning': - title = 'Warning' - break - default: - title = 'Success' + if (msg) { + if (s == 'danger') { + this.$refs['alert-audio'].play() + } + + let title + let duration = 3 + + switch (s) { + case 'black': + case 'danger': + title = 'Error' + duration = null + break + case 'warning': + title = 'Warning' + break + default: + title = 'Success' + } + + EventHub.fire('showNotif', { + title: title, + body: msg, + type: s, + duration: duration + }) } - - EventHub.fire('showNotif', { - title: title, - body: msg, - type: s, - duration: duration - }) }, resetInput(input, val = null) { if (Array.isArray(input)) { @@ -64,7 +69,8 @@ export default { this.bulkSelect = false this.bulkSelectAll = false this.resetInput('bulkList', []) - this.resetInput(['selectedFile', 'currentFileIndex', 'searchFor']) + this.resetInput('searchFor') + this.selectFirst() }, /* Resolve */ @@ -161,9 +167,9 @@ export default { EventHub.fire('loading-files-hide') }, ajaxError() { - this.toggleInfoPanel() this.toggleInfo = false this.toggleLoader('ajax_error', true) + this.$refs['alert-audio'].play() EventHub.fire('ajax-error-show') }, @@ -180,6 +186,7 @@ export default { downloadFile(e.path) }) + manager.$refs['success-audio'].play() return this.showNotif('All Done') } diff --git a/src/resources/assets/js/components/mixins/watch.js b/src/resources/assets/js/components/mixins/watch.js index 62871b4..3e29618 100644 --- a/src/resources/assets/js/components/mixins/watch.js +++ b/src/resources/assets/js/components/mixins/watch.js @@ -28,6 +28,8 @@ export default { checkForFolders(val) { if (!val) { this.resetInput('moveToPath') + } else { + this.moveToPath = this.$refs.move_folder_dropdown.options[0].value } }, bulkItemsCount(val) { diff --git a/src/resources/assets/js/manager.js b/src/resources/assets/js/manager.js index 3a07aea..1c1a1de 100644 --- a/src/resources/assets/js/manager.js +++ b/src/resources/assets/js/manager.js @@ -43,6 +43,11 @@ axios.defaults.headers.common = { 'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content'), 'X-Requested-With': 'XMLHttpRequest' } +axios.interceptors.response.use((response) => { + return response +}, (error) => { + return Promise.reject(error.response) +}) // vue-awesome import 'vue-awesome/icons/shopping-basket' diff --git a/src/resources/assets/sass/extra/vars.scss b/src/resources/assets/sass/extra/vars.scss index c2ab2fa..6a01529 100644 --- a/src/resources/assets/sass/extra/vars.scss +++ b/src/resources/assets/sass/extra/vars.scss @@ -18,4 +18,5 @@ $yellow: hsl(48, 100%, 67%); $red: hsl(348, 100%, 61%); $blue: hsl(217, 71%, 53%); $shadow_1: 0 10px 45px 0 rgba($black, 0.2); -$shadow_2: 0 5px 25px 0 rgba($black, 0.2); +$shadow_2: 0 6px 25px 0 rgba($black, 0.3); +$shadow_3: 0 6px 20px rgba($black, 0.08); diff --git a/src/resources/assets/sass/media.scss b/src/resources/assets/sass/media.scss index fa8602e..eb39e0e 100644 --- a/src/resources/assets/sass/media.scss +++ b/src/resources/assets/sass/media.scss @@ -12,7 +12,7 @@ /* toolbar */ .media-manager__toolbar { flex-wrap: wrap; - margin: 0 0 -1px!important; + margin: 0 0 -1px !important; padding: 1rem; border: 1px solid darken($active_theme, 5%); border-radius: 4px 4px 0 0; @@ -118,9 +118,55 @@ } /* breadcrump */ +// mobile +.__stack-breadcrumb-mobile { + display: flex; + overflow: scroll; + align-items: center; + width: 100%; + padding: 1rem 0; + list-style: none; + + li { + flex: 1; + margin-right: 1rem; + text-align: center; + + a { + display: block; + } + + a, + &:only-of-type { + padding: 1rem; + transition: all 0.3s ease-in-out; + white-space: nowrap; + color: $blue; + border: 1px solid lighten($active_theme, 2%); + border-radius: 5px; + background: $white; + + &:hover { + border-color: transparent; + box-shadow: $shadow_3; + } + } + + &:only-of-type { + display: none; + } + + &:last-of-type { + margin-right: 0; + padding-right: 3rem; + } + } +} + +// default .media-manager__stack-breadcrumb { margin: 0 !important; - padding: 0.5rem 1rem; + padding: 0.7rem; border-top: 1px solid darken($active_theme, 5%); background: $active_theme; @@ -320,10 +366,10 @@ .__files-boxs { li { - min-width: 250px; - max-width: 250px; - min-height: 250px; - max-height: 250px; + min-width: 200px; + max-width: 200px; + min-height: 200px; + max-height: 200px; } } @@ -362,7 +408,7 @@ .__box-info { width: 100%; - padding: 1rem; + padding: 0.7rem; } } } @@ -420,27 +466,33 @@ display: flex; flex: 1 0 auto; flex-direction: column; - padding: 0.75rem; transition: background 0.8s ease-in-out; word-break: break-all; - border-top: 1px solid darken($active_theme, 5%); - > div:not(.__sidebar-count) { - flex: 1; + > div { + padding: 0.75rem; - h4 { - font-size: 1rem; - margin-bottom: 0.1em; - color: darken($active_theme, 20%); - } + &:not(.__sidebar-count) { + flex: 1; - span { - word-break: break-word; - color: darken($active_theme, 70%); - } + &:not(:empty) { + border-top: 1px solid darken($active_theme, 5%); + } + + h4 { + font-size: 1rem; + margin-bottom: 0.1em; + color: darken($active_theme, 20%); + } + + span { + word-break: break-word; + color: darken($active_theme, 70%); + } - a { - color: $blue; + a { + color: $blue; + } } } } @@ -509,7 +561,7 @@ } .subtitle { - color: darken($active_theme, 50%);; + color: darken($active_theme, 50%); } .modal-content, @@ -658,6 +710,10 @@ padding-right: 0 !important; } +.p-t-10 { + padding-top: 10px !important; +} + .m-v-15 { margin-top: 15px !important; margin-bottom: 15px !important; @@ -684,7 +740,7 @@ background: transparent; * { - color: darken($active_theme, 70%);; + color: darken($active_theme, 70%); } &:focus { @@ -768,7 +824,7 @@ a[target='_blank'] { } .modal-content { - box-shadow: $shadow_2; + box-shadow: $shadow_1; } .modal-card-foot { diff --git a/src/resources/lang/en/messages.php b/src/resources/lang/en/messages.php index aa346fe..d489609 100644 --- a/src/resources/lang/en/messages.php +++ b/src/resources/lang/en/messages.php @@ -11,13 +11,19 @@ 'clear' => 'Clear :attr', 'close' => 'Close', 'copied' => 'Copied', + 'copy_file_folder' => 'Copy File/Folder', + 'copy_files' => 'Copy Files Instead ?', + 'copy_success' => 'Successfully Copied', 'copy_to_cp' => 'Copy Link To Clipboard', 'create_new_folder' => 'Create New Folder', + 'create_success' => 'Successfully Created', 'delete' => 'Delete', 'delete_confirm' => 'Yes, Delete it!', 'delete_folder' => 'Deleting a folder will remove all files and folders contained inside.', + 'delete_success' => 'Successfully Deleted', 'destination_folder' => 'Destination Folder', 'download_file' => 'Download File', + 'download_folder' => 'Download Folder', 'downloaded' => 'Downloaded', 'error_already_exists' => 'A File/Folder already exists with that name.', 'error_creating_dir' => 'Something seems to have gone wrong with creating the directory, please check your permissions,', @@ -29,11 +35,12 @@ 'found' => 'Found', 'items' => 'Item(s)', 'last_modified' => 'Last Modified', - 'library' => 'Media Library', + 'library' => 'Home', 'loading' => 'Loading Files', 'lock' => 'Lock Item', 'move' => 'Move', 'move_file_folder' => 'Move File/Folder', + 'move_success' => 'Successfully Moved', 'new_file_folder' => 'New File/Folder Name', 'new_folder_name' => 'New Folder Name', 'no_files_in_folder' => 'No files in this folder.', @@ -48,6 +55,7 @@ 'public_url' => 'Public URL', 'rename' => 'Rename', 'rename_file_folder' => 'Rename File/Folder', + 'rename_success' => 'Successfully Renamed', 'select_all' => 'Select All', 'select_non' => 'Select Non', 'selected' => 'Selected', @@ -61,10 +69,11 @@ 'type' => 'Type', 'unlock' => 'UnLock Item', 'upload' => 'Upload', - 'upload_success' => 'Successfully Uploaded.', + 'upload_success' => 'Successfully Uploaded', 'upload_text' => 'Drag & Drop Files
Or
Click To Upload', 'url' => 'URL', 'use_random_names' => 'Use Random Names ?', 'video_support' => 'Your browser does not support the video tag.', - 'download_folder' => 'Download Folder', + 'copy' => 'Copy', + 'error_moving_cloud' => 'Cloud Folders can\'t be "Renamed / Moved or Copied".', ); \ No newline at end of file diff --git a/src/resources/lang/fr/messages.php b/src/resources/lang/fr/messages.php deleted file mode 100644 index 39c7f40..0000000 --- a/src/resources/lang/fr/messages.php +++ /dev/null @@ -1,70 +0,0 @@ - 'Ajouter le dossier', - 'add_new_folder' => 'Ajouter un nouveau dossier', - 'ajax_error' => 'Oh NO! Something Went Wrong', - 'are_you_sure_delete' => 'Êtes-vous sûre de vouloir supprimer ?', - 'audio_support' => NULL, - 'bulk_select' => 'Sélection en vrac', - 'cancel' => 'Annuler', - 'clear' => NULL, - 'close' => NULL, - 'copied' => NULL, - 'copy_to_cp' => NULL, - 'create_new_folder' => 'Créer un nouveau dossier', - 'delete' => 'Supprimer', - 'delete_confirm' => 'Oui, Supprimez-le', - 'delete_folder' => 'La suppression d\'un dossier supprimera tous les fichiers et dossiers contenus à l\'intérieur', - 'destination_folder' => 'Dossier de destination', - 'download_file' => NULL, - 'downloaded' => NULL, - 'error_already_exists' => 'Le fichier ou le dossier semble déjà exister avec ce nom. Choisissez un autre nom ou supprimez l\'autre fichier.', - 'error_creating_dir' => 'Quelque chose semble avoir mal tourné avec la création du répertoire. Veuillez vérifier vos autorisations', - 'error_deleting_file' => 'Quelque chose semble avoir mal retourné ce fichier. Veuillez vérifiez vos autorisations', - 'error_doesnt_exist' => NULL, - 'error_moving' => 'Il semble y avoir un problème que le déplacement fichier / dossier, assurez-vous que vous disposez des autorisations appropriées.', - 'filter_by' => NULL, - 'find' => NULL, - 'found' => NULL, - 'items' => NULL, - 'last_modified' => NULL, - 'library' => 'Médiathèque', - 'loading' => NULL, - 'lock' => NULL, - 'move' => 'Déplacer', - 'move_file_folder' => 'Déplacer le fichier/dossier', - 'new_file_folder' => 'Nouveau nom de fichier/dossier', - 'new_folder_name' => 'Nom du nouveau dossier', - 'no_files_in_folder' => 'Pas de fichiers dans ce dossier.', - 'no_val' => NULL, - 'non' => NULL, - 'not_allowed_file_ext' => 'Les fichiers du type ":attr" ne sont pas autorisés', - 'nothing_found' => NULL, - 'nothing_selected' => 'Aucun fichier ou dossier sélectionné', - 'open' => NULL, - 'pdf' => NULL, - 'preview' => NULL, - 'public_url' => NULL, - 'rename' => 'Renommer', - 'rename_file_folder' => 'Renommer Fichier/Dossier', - 'select_all' => NULL, - 'select_non' => NULL, - 'selected' => NULL, - 'single_char_folder' => NULL, - 'size' => NULL, - 'sort_by' => NULL, - 'stand_by' => NULL, - 'title' => NULL, - 'too_many_files' => NULL, - 'total' => NULL, - 'type' => NULL, - 'unlock' => NULL, - 'upload' => 'Téléverser', - 'upload_success' => NULL, - 'upload_text' => 'Faites glisser & déposez des fichiers
ou
cliquez pour télécharger', - 'url' => NULL, - 'use_random_names' => NULL, - 'video_support' => NULL, - 'download_folder' => NULL, -); \ No newline at end of file diff --git a/src/resources/views/_manager.blade.php b/src/resources/views/_manager.blade.php index 450c73b..a0cdfe1 100644 --- a/src/resources/views/_manager.blade.php +++ b/src/resources/views/_manager.blade.php @@ -2,6 +2,11 @@ {{-- component --}} +@php + // mobile breadCrumb + $alt_breadcrumb = true; +@endphp + trans('MediaManager::messages.no_val'), 'single_char_folder' => trans('MediaManager::messages.single_char_folder'), 'downloaded' => trans('MediaManager::messages.downloaded'), - 'upload_success' => trans('MediaManager::messages.upload_success') + 'upload_success' => trans('MediaManager::messages.upload_success'), + 'create_success' => trans('MediaManager::messages.create_success'), + 'rename_success' => trans('MediaManager::messages.rename_success'), + 'move_success' => trans('MediaManager::messages.move_success'), + 'delete_success' => trans('MediaManager::messages.delete_success'), + 'copy_success' => trans('MediaManager::messages.copy_success') ]) }}" :upload-panel-img-list="{{ $patterns }}" files-route="{{ route('media.files') }}" @@ -22,6 +32,10 @@
+ {{-- notif-audio --}} + + + {{-- top toolbar --}}
+ {{-- mobile breadCrumb --}} + @if ($alt_breadcrumb) + @include('MediaManager::extras._breadcrumb') + @endif + {{-- ====================================================================== --}} {{-- dropzone --}} @@ -307,10 +326,10 @@ class="button"
- -

{{ trans('MediaManager::messages.stand_by') }}

-

{{ trans('MediaManager::messages.loading') }}

-
+ +

{{ trans('MediaManager::messages.stand_by') }}

+

{{ trans('MediaManager::messages.loading') }}

+
{{-- ajax error --}} @@ -330,8 +349,8 @@ class="button" {{-- files box --}}
  • + @click="setSelected(file, index, $event)">
    @@ -447,8 +466,6 @@ class="link image" :key="selectedFile.name" v-tippy="{arrow: true, position: 'left'}" title="space"> - - {{ trans('MediaManager::messages.video_support') }} @@ -459,7 +476,6 @@ class="link image" ref="player" :key="selectedFile.name" v-tippy="{arrow: true, position: 'left'}" title="space"> - {{ trans('MediaManager::messages.audio_support') }} @@ -503,7 +519,7 @@ class="link image"
    @@ -591,7 +607,7 @@ class="title is-6 has-text-weight-semibold">
    {{-- directories breadCrumb --}} -