diff --git a/modules/backend/assets/js/october-min.js b/modules/backend/assets/js/october-min.js index d960a8189..db2e33ae3 100644 --- a/modules/backend/assets/js/october-min.js +++ b/modules/backend/assets/js/october-min.js @@ -673,7 +673,8 @@ this.$modal=this.$container.modal({show:false,backdrop:false,keyboard:this.optio this.$container.data('oc.popup',this) this.$modal.on('hide.bs.modal',function(){self.isOpen=false self.setBackdrop(false)}) -this.$modal.on('hidden.bs.modal',function(){self.$container.remove() +this.$modal.on('hidden.bs.modal',function(){self.triggerEvent('hidden.oc.popup') +self.$container.remove() self.$el.data('oc.popup',null)}) this.$modal.on('show.bs.modal',function(){self.isOpen=true self.setBackdrop(true)}) diff --git a/modules/backend/assets/js/october.popup.js b/modules/backend/assets/js/october.popup.js index 161988f37..e17399757 100644 --- a/modules/backend/assets/js/october.popup.js +++ b/modules/backend/assets/js/october.popup.js @@ -54,6 +54,7 @@ }) this.$modal.on('hidden.bs.modal', function(){ + self.triggerEvent('hidden.oc.popup') self.$container.remove() self.$el.data('oc.popup', null) }) diff --git a/modules/cms/classes/MediaLibrary.php b/modules/cms/classes/MediaLibrary.php index d034a94b9..699137e38 100644 --- a/modules/cms/classes/MediaLibrary.php +++ b/modules/cms/classes/MediaLibrary.php @@ -186,6 +186,28 @@ class MediaLibrary return $this->getStorageDisk()->exists($fullPath); } + /** + * Determines if a folder with the specified path exists in the library. + * @param string $path Specifies the folder path relative the the Library root. + * @return boolean Returns TRUE if the folder exists. + */ + public function folderExists($path) + { + $folderName = basename($path); + $folderPath = dirname($path); + + $path = self::validatePath($folderPath); + $fullPath = $this->getMediaPath($path); + + $folders = $this->getStorageDisk()->directories($fullPath); + foreach ($folders as $folder) { + if (basename($folder) == $folderName) + return true; + } + + return false; + } + /** * Returns a file contents. * @param string $path Specifies the file path relative the the Library root. @@ -284,6 +306,19 @@ class MediaLibrary return true; } + /** + * Creates a folder. + * @param string $path Specifies the folder path. + * @return boolean + */ + public function makeFolder($path) + { + $path = self::validatePath($path); + $fullPath = $this->getMediaPath($path); + + return $this->getStorageDisk()->makeDirectory($fullPath); + } + /** * Resets the Library cache. * diff --git a/modules/cms/lang/en/lang.php b/modules/cms/lang/en/lang.php index f0003b24d..c61131926 100644 --- a/modules/cms/lang/en/lang.php +++ b/modules/cms/lang/en/lang.php @@ -261,7 +261,11 @@ return [ 'folder' => 'Folder', 'no_files_found' => 'No files found by your request.', 'delete_empty' => 'Please select files to delete.', - 'delete_confirm' => 'Do you really want to delete the selected file(s)?', - 'error_renaming_file' => 'Error renaming file.' + 'delete_confirm' => 'Do you really want to delete the selected item(s)?', + 'error_renaming_file' => 'Error renaming file.', + 'new_folder_title' => 'New folder', + 'folder_name' => 'Folder name', + 'error_creating_folder' => 'Error creating folder', + 'folder_or_file_exist' => 'A folder or file with the specified name already exists.' ] ]; diff --git a/modules/cms/widgets/MediaManager.php b/modules/cms/widgets/MediaManager.php index a14a33458..fa96a416e 100644 --- a/modules/cms/widgets/MediaManager.php +++ b/modules/cms/widgets/MediaManager.php @@ -228,7 +228,7 @@ class MediaManager extends WidgetBase $this->vars['name'] = basename($path); $this->vars['listId'] = Input::get('listId'); $this->vars['type'] = Input::get('type'); - return $this->makePartial('rename_form'); + return $this->makePartial('rename-form'); } public function onApplyName() @@ -255,6 +255,37 @@ class MediaManager extends WidgetBase MediaLibrary::instance()->resetCache(); } + public function onCreateFolder() + { + $name = trim(Input::get('name')); + if (!strlen($name)) + throw new ApplicationException(Lang::get('cms::lang.asset.name_cant_be_empty')); + + if (!$this->validateFileName($name)) + throw new ApplicationException(Lang::get('cms::lang.asset.invalid_name')); + + $path = Input::get('path'); + $path = MediaLibrary::validatePath($path); + + $newFolderPath = $path.'/'.$name; + + $library = MediaLibrary::instance(); + + if ($library->folderExists($newFolderPath)) + throw new ApplicationException(Lang::get('cms::lang.media.folder_or_file_exist')); + + if (!$library->makeFolder($newFolderPath)) + throw new ApplicationException(Lang::get('cms::lang.media.error_creating_folder')); + + $library->resetCache(); + + $this->prepareVars(); + + return [ + '#'.$this->getId('item-list') => $this->makePartial('item-list') + ]; + } + // // Methods for th internal use // diff --git a/modules/cms/widgets/mediamanager/assets/js/mediamanager.js b/modules/cms/widgets/mediamanager/assets/js/mediamanager.js index aa96522e0..db42595fa 100644 --- a/modules/cms/widgets/mediamanager/assets/js/mediamanager.js +++ b/modules/cms/widgets/mediamanager/assets/js/mediamanager.js @@ -24,6 +24,9 @@ this.listMouseMoveHandler = this.onListMouseMove.bind(this) this.sortingChangedHandler = this.onSortingChanged.bind(this) this.searchChangedHandler = this.onSearchChanged.bind(this) + this.folderPopupShownHandler = this.onFolderPopupShown.bind(this) + this.newFolderSubmitHandler = this.onNewFolderSubmit.bind(this) + this.folderPopupHiddenHandler = this.onFolderPopupHidden.bind(this) // Instance-bound methods this.updateSidebarPreviewBound = this.updateSidebarPreview.bind(this) @@ -41,6 +44,7 @@ this.releaseNavigationAjaxBound = this.releaseNavigationAjax.bind(this) this.deleteConfirmationBound = this.deleteConfirmation.bind(this) this.refreshBound = this.refresh.bind(this) + this.folderCreatedBound = this.folderCreated.bind(this) // State properties this.selectTimer = null @@ -85,6 +89,7 @@ this.releaseNavigationAjaxBound = null this.deleteConfirmationBound = null this.refreshBound = null + this.folderCreatedBound = null this.sidebarPreviewElement = null this.itemListElement = null @@ -114,6 +119,8 @@ this.$el.on('change', '[data-control="sorting"]', this.sortingChangedHandler) this.$el.on('keyup', '[data-control="search"]', this.searchChangedHandler) this.$el.on('mediarefresh', this.refreshBound) + this.$el.on('shown.oc.popup', '[data-command="create-folder"]', this.folderPopupShownHandler) + this.$el.on('hidden.oc.popup', '[data-command="create-folder"]', this.folderPopupHiddenHandler) if (this.itemListElement) this.itemListElement.addEventListener('mousedown', this.listMouseDownHandler) @@ -126,6 +133,8 @@ this.$el.off('click.item', this.itemClickHandler) this.$el.off('change', '[data-control="sorting"]', this.sortingChangedHandler) this.$el.off('keyup', '[data-control="search"]', this.searchChangedHandler) + this.$el.off('shown.oc.popup', '[data-command="create-folder"]', this.folderPopupShownHandler) + this.$el.off('hidden.oc.popup', '[data-command="create-folder"]', this.folderPopupHiddenHandler) if (this.itemListElement) { this.itemListElement.removeEventListener('mousedown', this.listMouseDownHandler) @@ -141,6 +150,9 @@ this.listMouseMoveHandler = null this.sortingChangedHandler = null this.searchChangedHandler = null + this.folderPopupShownHandler = null + this.folderPopupHiddenHandler = null + this.newFolderSubmitHandler = null } MediaManager.prototype.changeView = function(view) { @@ -323,13 +335,19 @@ this.sidebarPreviewElement.querySelector('[data-control="sidebar-labels"]').setAttribute('class', 'panel') // One item is selected - display the details - var item = items[0] + var item = items[0], + lastModified = item.getAttribute('data-last-modified') previewPanel.querySelector('[data-label="size"]').textContent = item.getAttribute('data-size') previewPanel.querySelector('[data-label="title"]').textContent = item.getAttribute('data-title') - previewPanel.querySelector('[data-label="last-modified"]').textContent = item.getAttribute('data-last-modified') + previewPanel.querySelector('[data-label="last-modified"]').textContent = lastModified previewPanel.querySelector('[data-label="public-url"]').setAttribute('href', item.getAttribute('data-public-url')) + if (lastModified) + previewPanel.querySelector('[data-control="last-modified"]').setAttribute('class', '') + else + previewPanel.querySelector('[data-control="last-modified"]').setAttribute('class', 'hide') + if (this.isSearchMode()) { previewPanel.querySelector('[data-control="item-folder"]').setAttribute('class', '') var folderNode = previewPanel.querySelector('[data-label="folder"]') @@ -675,8 +693,21 @@ this.$el.find('[data-control="search"]').val('') } + MediaManager.prototype.onSearchChanged = function(ev) { + var value = ev.currentTarget.value + + if (this.lastSearchValue !== undefined && this.lastSearchValue == value) + return + + this.lastSearchValue = value + + this.clearSearchTrackInputTimer() + + this.searchTrackInputTimer = window.setTimeout(this.updateSearchResultsBound, 300) + } + // - // File operations + // File and folder operations // MediaManager.prototype.deleteFiles = function() { @@ -724,6 +755,44 @@ }).done(this.afterNavigateBound) } + MediaManager.prototype.createFolder = function(ev) { + $(ev.target).popup({ + content: this.$el.find('[data-control="new-folder-template"]').html() + }) + } + + MediaManager.prototype.onFolderPopupShown = function(ev, button, popup) { + $(popup).find('input[name=name]').focus() + $(popup).on('submit.media', 'form', this.newFolderSubmitHandler) + } + + MediaManager.prototype.onFolderPopupHidden = function(ev, button, popup) { + $(popup).off('.media', 'form') + } + + MediaManager.prototype.onNewFolderSubmit = function(ev) { + var data = { + name: $(ev.target).find('input[name=name]').val(), + path: this.$el.find('[data-type="current-folder"]').val() + } + + $.oc.stripeLoadIndicator.show() + this.$form.request(this.options.alias+'::onCreateFolder', { + data: data + }).always(function() { + $.oc.stripeLoadIndicator.hide() + }).done(this.folderCreatedBound) + + ev.preventDefault() + return false + } + + MediaManager.prototype.folderCreated = function() { + this.$el.find('button[data-command="create-folder"]').popup('hide') + + this.afterNavigateBound + } + // EVENT HANDLERS // ============================ @@ -767,6 +836,9 @@ case 'delete': this.deleteFiles() break; + case 'create-folder': + this.createFolder(ev) + break; } return false @@ -774,7 +846,7 @@ MediaManager.prototype.onItemClick = function(ev) { // Don't select "Go up" folders and don't select items when the rename icon is clicked - if (ev.currentTarget.hasAttribute('data-root') || ev.target.tagName == 'I') + if (ev.currentTarget.hasAttribute('data-root') || (ev.target.tagName == 'I' && ev.target.hasAttribute('data-rename-control'))) return this.selectItem(ev.currentTarget, ev.shiftKey) @@ -881,19 +953,6 @@ this.execNavigationRequest('onSetSorting', data) } - MediaManager.prototype.onSearchChanged = function(ev) { - var value = ev.currentTarget.value - - if (this.lastSearchValue !== undefined && this.lastSearchValue == value) - return - - this.lastSearchValue = value - - this.clearSearchTrackInputTimer() - - this.searchTrackInputTimer = window.setTimeout(this.updateSearchResultsBound, 300) - } - // MEDIA MANAGER PLUGIN DEFINITION // ============================ diff --git a/modules/cms/widgets/mediamanager/partials/_body.htm b/modules/cms/widgets/mediamanager/partials/_body.htm index 0b19e06bd..70bbe13e6 100644 --- a/modules/cms/widgets/mediamanager/partials/_body.htm +++ b/modules/cms/widgets/mediamanager/partials/_body.htm @@ -50,4 +50,6 @@ + + = $this->makePartial('new-folder-form') ?> \ No newline at end of file diff --git a/modules/cms/widgets/mediamanager/partials/_generic-list.htm b/modules/cms/widgets/mediamanager/partials/_generic-list.htm index 4b7553bc8..ae1185bbe 100644 --- a/modules/cms/widgets/mediamanager/partials/_generic-list.htm +++ b/modules/cms/widgets/mediamanager/partials/_generic-list.htm @@ -41,7 +41,7 @@ data-control="popup" data-request-data="path: '= e($item->path) ?>', listId: '= $listElementId ?>', type: '= $item->type ?>'" data-handler="= $this->getEventHandler('onLoadRenamePopup') ?>" - > + >
= e($item->sizeToString()) ?>
diff --git a/modules/cms/widgets/mediamanager/partials/_list-grid.htm b/modules/cms/widgets/mediamanager/partials/_list-grid.htm index 366d1ee4d..43cf3a0d3 100644 --- a/modules/cms/widgets/mediamanager/partials/_list-grid.htm +++ b/modules/cms/widgets/mediamanager/partials/_list-grid.htm @@ -41,7 +41,7 @@ data-control="popup" data-request-data="path: '= e($item->path) ?>', listId: '= $listElementId ?>', type: '= $item->type ?>'" data-handler="= $this->getEventHandler('onLoadRenamePopup') ?>" - > + >