Merge pull request #18630 from owncloud/files-combineuploadbutton
Combine upload action into the "New" menuremotes/origin/db-empty-migrate
commit
004de1425e
@ -0,0 +1,235 @@ |
||||
/* |
||||
* Copyright (c) 2014 |
||||
* |
||||
* This file is licensed under the Affero General Public License version 3 |
||||
* or later. |
||||
* |
||||
* See the COPYING-README file. |
||||
* |
||||
*/ |
||||
|
||||
/* global Files */ |
||||
|
||||
(function() { |
||||
|
||||
var TEMPLATE_MENU = |
||||
'<ul>' + |
||||
'<li>' + |
||||
'<label for="file_upload_start" class="menuitem" data-action="upload" title="{{uploadMaxHumanFilesize}}"><span class="svg icon icon-upload"></span><span class="displayname">{{uploadLabel}}</span></label>' + |
||||
'</li>' + |
||||
'{{#each items}}' + |
||||
'<li>' + |
||||
'<a href="#" class="menuitem" data-templatename="{{templateName}}" data-filetype="{{fileType}}" data-action="{{id}}"><span class="icon {{iconClass}} svg"></span><span class="displayname">{{displayName}}</span></a>' + |
||||
'</li>' + |
||||
'{{/each}}' + |
||||
'</ul>'; |
||||
|
||||
var TEMPLATE_FILENAME_FORM = |
||||
'<form class="filenameform">' + |
||||
'<label class="hidden-visually" for="{{cid}}-input-{{fileType}}">{{fileName}}</label>' + |
||||
'<input id="{{cid}}-input-{{fileType}}" type="text" value="{{fileName}}">' + |
||||
'</form>'; |
||||
|
||||
/** |
||||
* Construct a new NewFileMenu instance |
||||
* @constructs NewFileMenu |
||||
* |
||||
* @memberof OCA.Files |
||||
*/ |
||||
var NewFileMenu = OC.Backbone.View.extend({ |
||||
tagName: 'div', |
||||
className: 'newFileMenu popovermenu bubble hidden open menu', |
||||
|
||||
events: { |
||||
'click .menuitem': '_onClickAction' |
||||
}, |
||||
|
||||
initialize: function(options) { |
||||
var self = this; |
||||
var $uploadEl = $('#file_upload_start'); |
||||
if ($uploadEl.length) { |
||||
$uploadEl.on('fileuploadstart', function() { |
||||
self.trigger('actionPerformed', 'upload'); |
||||
}); |
||||
} else { |
||||
console.warn('Missing upload element "file_upload_start"'); |
||||
} |
||||
|
||||
this._fileList = options && options.fileList; |
||||
}, |
||||
|
||||
template: function(data) { |
||||
if (!OCA.Files.NewFileMenu._TEMPLATE) { |
||||
OCA.Files.NewFileMenu._TEMPLATE = Handlebars.compile(TEMPLATE_MENU); |
||||
} |
||||
return OCA.Files.NewFileMenu._TEMPLATE(data); |
||||
}, |
||||
|
||||
/** |
||||
* Event handler whenever an action has been clicked within the menu |
||||
* |
||||
* @param {Object} event event object |
||||
*/ |
||||
_onClickAction: function(event) { |
||||
var $target = $(event.target); |
||||
if (!$target.hasClass('menuitem')) { |
||||
$target = $target.closest('.menuitem'); |
||||
} |
||||
var action = $target.attr('data-action'); |
||||
// note: clicking the upload label will automatically
|
||||
// set the focus on the "file_upload_start" hidden field
|
||||
// which itself triggers the upload dialog.
|
||||
// Currently the upload logic is still in file-upload.js and filelist.js
|
||||
if (action === 'upload') { |
||||
OC.hideMenus(); |
||||
} else { |
||||
event.preventDefault(); |
||||
this._promptFileName($target); |
||||
} |
||||
}, |
||||
|
||||
_promptFileName: function($target) { |
||||
var self = this; |
||||
if (!OCA.Files.NewFileMenu._TEMPLATE_FORM) { |
||||
OCA.Files.NewFileMenu._TEMPLATE_FORM = Handlebars.compile(TEMPLATE_FILENAME_FORM); |
||||
} |
||||
|
||||
if ($target.find('form').length) { |
||||
$target.find('input').focus(); |
||||
return; |
||||
} |
||||
|
||||
// discard other forms
|
||||
this.$el.find('form').remove(); |
||||
this.$el.find('.displayname').removeClass('hidden'); |
||||
|
||||
$target.find('.displayname').addClass('hidden'); |
||||
|
||||
var newName = $target.attr('data-templatename'); |
||||
var fileType = $target.attr('data-filetype'); |
||||
var $form = $(OCA.Files.NewFileMenu._TEMPLATE_FORM({ |
||||
fileName: newName, |
||||
cid: this.cid, |
||||
fileType: fileType |
||||
})); |
||||
|
||||
//this.trigger('actionPerformed', action);
|
||||
$target.append($form); |
||||
|
||||
// here comes the OLD code
|
||||
var $input = $form.find('input'); |
||||
|
||||
var lastPos; |
||||
var checkInput = function () { |
||||
var filename = $input.val(); |
||||
try { |
||||
if (!Files.isFileNameValid(filename)) { |
||||
// Files.isFileNameValid(filename) throws an exception itself
|
||||
} else if (self._fileList.inList(filename)) { |
||||
throw t('files', '{newname} already exists', {newname: filename}); |
||||
} else { |
||||
return true; |
||||
} |
||||
} catch (error) { |
||||
$input.attr('title', error); |
||||
$input.tooltip({placement: 'right', trigger: 'manual'}); |
||||
$input.tooltip('show'); |
||||
$input.addClass('error'); |
||||
} |
||||
return false; |
||||
}; |
||||
|
||||
// verify filename on typing
|
||||
$input.keyup(function() { |
||||
if (checkInput()) { |
||||
$input.tooltip('hide'); |
||||
$input.removeClass('error'); |
||||
} |
||||
}); |
||||
|
||||
$input.focus(); |
||||
// pre select name up to the extension
|
||||
lastPos = newName.lastIndexOf('.'); |
||||
if (lastPos === -1) { |
||||
lastPos = newName.length; |
||||
} |
||||
$input.selectRange(0, lastPos); |
||||
|
||||
$form.submit(function(event) { |
||||
event.stopPropagation(); |
||||
event.preventDefault(); |
||||
|
||||
if (checkInput()) { |
||||
var newname = $input.val(); |
||||
self._createFile(fileType, newname); |
||||
$form.remove(); |
||||
$target.find('.displayname').removeClass('hidden'); |
||||
OC.hideMenus(); |
||||
} |
||||
}); |
||||
}, |
||||
|
||||
/** |
||||
* Creates a file with the given type and name. |
||||
* This calls the matching methods on the attached file list. |
||||
* |
||||
* @param {string} fileType file type |
||||
* @param {string} name file name |
||||
*/ |
||||
_createFile: function(fileType, name) { |
||||
switch(fileType) { |
||||
case 'file': |
||||
this._fileList.createFile(name); |
||||
break; |
||||
case 'folder': |
||||
this._fileList.createDirectory(name); |
||||
break; |
||||
default: |
||||
console.warn('Unknown file type "' + fileType + '"'); |
||||
} |
||||
}, |
||||
|
||||
/** |
||||
* Renders the menu with the currently set items |
||||
*/ |
||||
render: function() { |
||||
this.$el.html(this.template({ |
||||
uploadMaxHumanFileSize: 'TODO', |
||||
uploadLabel: t('files', 'Upload'), |
||||
items: [{ |
||||
id: 'file', |
||||
displayName: t('files', 'Text file'), |
||||
templateName: t('files', 'New text file.txt'), |
||||
iconClass: 'icon-filetype-text', |
||||
fileType: 'file' |
||||
}, { |
||||
id: 'folder', |
||||
displayName: t('files', 'Folder'), |
||||
templateName: t('files', 'New folder'), |
||||
iconClass: 'icon-folder', |
||||
fileType: 'folder' |
||||
}] |
||||
})); |
||||
}, |
||||
|
||||
/** |
||||
* Displays the menu under the given element |
||||
* |
||||
* @param {Object} $target target element |
||||
*/ |
||||
showAt: function($target) { |
||||
this.render(); |
||||
var targetOffset = $target.offset(); |
||||
this.$el.css({ |
||||
left: targetOffset.left, |
||||
top: targetOffset.top + $target.height() |
||||
}); |
||||
this.$el.removeClass('hidden'); |
||||
|
||||
OC.showMenu(null, this.$el); |
||||
} |
||||
}); |
||||
|
||||
OCA.Files.NewFileMenu = NewFileMenu; |
||||
|
||||
})(); |
||||
@ -0,0 +1,119 @@ |
||||
/** |
||||
* ownCloud |
||||
* |
||||
* @author Vincent Petry |
||||
* @copyright 2015 Vincent Petry <pvince81@owncloud.com> |
||||
* |
||||
* This library is free software; you can redistribute it and/or |
||||
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE |
||||
* License as published by the Free Software Foundation; either |
||||
* version 3 of the License, or any later version. |
||||
* |
||||
* This library is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU AFFERO GENERAL PUBLIC LICENSE for more details. |
||||
* |
||||
* You should have received a copy of the GNU Affero General Public |
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
* |
||||
*/ |
||||
|
||||
describe('OCA.Files.NewFileMenu', function() { |
||||
var FileList = OCA.Files.FileList; |
||||
var menu, fileList, $uploadField, $trigger; |
||||
|
||||
beforeEach(function() { |
||||
// dummy upload button
|
||||
var $container = $('<div id="app-content-files"></div>'); |
||||
$uploadField = $('<input id="file_upload_start"></input>'); |
||||
$trigger = $('<a href="#">Menu</a>'); |
||||
$container.append($uploadField).append($trigger); |
||||
$('#testArea').append($container); |
||||
|
||||
fileList = new FileList($container); |
||||
menu = new OCA.Files.NewFileMenu({ |
||||
fileList: fileList |
||||
}); |
||||
menu.showAt($trigger); |
||||
}); |
||||
afterEach(function() { |
||||
OC.hideMenus(); |
||||
fileList = null; |
||||
menu = null; |
||||
}); |
||||
|
||||
describe('rendering', function() { |
||||
it('renders menu items', function() { |
||||
var $items = menu.$el.find('.menuitem'); |
||||
expect($items.length).toEqual(3); |
||||
// label points to the file_upload_start item
|
||||
var $item = $items.eq(0); |
||||
expect($item.is('label')).toEqual(true); |
||||
expect($item.attr('for')).toEqual('file_upload_start'); |
||||
}); |
||||
}); |
||||
describe('New file/folder', function() { |
||||
var $input; |
||||
var createFileStub; |
||||
var createDirectoryStub; |
||||
|
||||
beforeEach(function() { |
||||
createFileStub = sinon.stub(FileList.prototype, 'createFile'); |
||||
createDirectoryStub = sinon.stub(FileList.prototype, 'createDirectory'); |
||||
menu.$el.find('.menuitem').eq(1).click(); |
||||
$input = menu.$el.find('form.filenameform input'); |
||||
}); |
||||
afterEach(function() { |
||||
createFileStub.restore(); |
||||
createDirectoryStub.restore(); |
||||
}); |
||||
|
||||
it('sets default text in field', function() { |
||||
expect($input.length).toEqual(1); |
||||
expect($input.val()).toEqual('New text file.txt'); |
||||
}); |
||||
it('creates file when enter is pressed', function() { |
||||
$input.val('somefile.txt'); |
||||
$input.trigger(new $.Event('keyup', {keyCode: 13})); |
||||
$input.parent('form').submit(); |
||||
|
||||
expect(createFileStub.calledOnce).toEqual(true); |
||||
expect(createFileStub.getCall(0).args[0]).toEqual('somefile.txt'); |
||||
expect(createDirectoryStub.notCalled).toEqual(true); |
||||
}); |
||||
it('prevents entering invalid file names', function() { |
||||
$input.val('..'); |
||||
$input.trigger(new $.Event('keyup', {keyCode: 13})); |
||||
$input.closest('form').submit(); |
||||
|
||||
expect(createFileStub.notCalled).toEqual(true); |
||||
expect(createDirectoryStub.notCalled).toEqual(true); |
||||
}); |
||||
it('prevents entering file names that already exist', function() { |
||||
var inListStub = sinon.stub(fileList, 'inList').returns(true); |
||||
$input.val('existing.txt'); |
||||
$input.trigger(new $.Event('keyup', {keyCode: 13})); |
||||
$input.closest('form').submit(); |
||||
|
||||
expect(createFileStub.notCalled).toEqual(true); |
||||
expect(createDirectoryStub.notCalled).toEqual(true); |
||||
inListStub.restore(); |
||||
}); |
||||
it('switching fields removes the previous form', function() { |
||||
menu.$el.find('.menuitem').eq(2).click(); |
||||
expect(menu.$el.find('form').length).toEqual(1); |
||||
}); |
||||
it('creates directory when clicking on create directory field', function() { |
||||
menu.$el.find('.menuitem').eq(2).click(); |
||||
$input = menu.$el.find('form.filenameform input'); |
||||
$input.val('some folder'); |
||||
$input.trigger(new $.Event('keyup', {keyCode: 13})); |
||||
$input.closest('form').submit(); |
||||
|
||||
expect(createDirectoryStub.calledOnce).toEqual(true); |
||||
expect(createDirectoryStub.getCall(0).args[0]).toEqual('some folder'); |
||||
expect(createFileStub.notCalled).toEqual(true); |
||||
}); |
||||
}); |
||||
}); |
||||
Loading…
Reference in new issue