AMBARI-7623. View: Files cleanup, enhancements and bugs. (jaimin)
Project: http://git-wip-us.apache.org/repos/asf/ambari/repo Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/d5b9af74 Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/d5b9af74 Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/d5b9af74 Branch: refs/heads/branch-alerts-dev Commit: d5b9af74587ad918e82fc418aa9c77716bb6f299 Parents: ac70302 Author: Jaimin Jetly <[email protected]> Authored: Fri Oct 3 09:54:34 2014 -0700 Committer: Jaimin Jetly <[email protected]> Committed: Fri Oct 3 09:54:34 2014 -0700 ---------------------------------------------------------------------- .../view/filebrowser/FileOperationService.java | 37 +++ .../apache/ambari/view/filebrowser/HdfsApi.java | 21 ++ .../files/src/main/resources/ui/app/adapter.js | 59 +++-- .../resources/ui/app/components/breadCrumbs.js | 47 ++++ .../resources/ui/app/components/bsPopover.js | 21 ++ .../resources/ui/app/components/bulkCheckbox.js | 40 ++++ .../resources/ui/app/components/chmodInput.js | 79 +++++++ .../ui/app/components/confirmDelete.js | 59 +++++ .../resources/ui/app/components/contextMenu.js | 36 +-- .../resources/ui/app/components/mkdirInput.js | 45 ++++ .../ui/app/components/popoverDelete.js | 50 ++++ .../resources/ui/app/components/renameInput.js | 7 +- .../resources/ui/app/components/sortArrow.js | 39 ++++ .../ui/app/components/toggleContext.js | 56 +++++ .../resources/ui/app/components/uploader.js | 13 +- .../main/resources/ui/app/controllers/error.js | 5 +- .../main/resources/ui/app/controllers/file.js | 104 +++++---- .../main/resources/ui/app/controllers/files.js | 103 ++++----- .../resources/ui/app/controllers/filesAlert.js | 10 + .../src/main/resources/ui/app/initialize.js | 24 +- .../src/main/resources/ui/app/routes/file.js | 3 + .../resources/ui/app/styles/application.less | 67 +++++- .../ui/app/templates/components/chmodInput.hbs | 100 ++++++++ .../ui/app/templates/components/contextMenu.hbs | 43 ++++ .../ui/app/templates/components/deleteBulk.hbs | 46 ++++ .../app/templates/components/deletePopover.hbs | 38 +++ .../ui/app/templates/components/mkdirInput.hbs | 37 +++ .../ui/app/templates/components/renameInput.hbs | 38 +++ .../ui/app/templates/components/uploader.hbs | 35 +++ .../main/resources/ui/app/templates/files.hbs | 229 ++++++------------- .../ui/app/templates/util/contextMenu.hbs | 56 ----- .../ui/app/templates/util/deleteBulk.hbs | 38 --- .../ui/app/templates/util/deletePopover.hbs | 38 --- .../resources/ui/app/templates/util/fileRow.hbs | 84 +++++++ .../ui/app/templates/util/renameInput.hbs | 38 --- .../ui/app/templates/util/uploader.hbs | 35 --- .../src/main/resources/ui/app/views/file.js | 182 +-------------- .../main/resources/ui/app/views/filesAlert.js | 23 ++ .../files/src/main/resources/ui/bower.json | 10 +- .../main/resources/ui/vendor/js/bsPopover.js | 190 +++++++++++++++ contrib/views/files/src/main/resources/view.xml | 2 +- 41 files changed, 1478 insertions(+), 709 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ambari/blob/d5b9af74/contrib/views/files/src/main/java/org/apache/ambari/view/filebrowser/FileOperationService.java ---------------------------------------------------------------------- diff --git a/contrib/views/files/src/main/java/org/apache/ambari/view/filebrowser/FileOperationService.java b/contrib/views/files/src/main/java/org/apache/ambari/view/filebrowser/FileOperationService.java index 7b6a5fa..d043917 100644 --- a/contrib/views/files/src/main/java/org/apache/ambari/view/filebrowser/FileOperationService.java +++ b/contrib/views/files/src/main/java/org/apache/ambari/view/filebrowser/FileOperationService.java @@ -96,6 +96,33 @@ public class FileOperationService extends HdfsService { } /** + * Chmod + * @param request chmod request + * @return response with success + */ + @POST + @Path("/chmod") + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Response chmod(final ChmodRequest request) { + try { + HdfsApi api = getApi(context); + ResponseBuilder result; + if (api.chmod(request.path, request.mode)) { + result = Response.ok(HdfsApi.fileStatusToJSON(api + .getFileStatus(request.path))); + } else { + result = Response.ok(new BoolResult(false)).status(422); + } + return result.build(); + } catch (WebApplicationException ex) { + throw ex; + } catch (Exception ex) { + throw new ServiceFormattedException(ex.getMessage(), ex); + } + } + + /** * Copy file * @param request source and destination request * @return response with success @@ -229,6 +256,16 @@ public class FileOperationService extends HdfsService { public String path; } + /** + * Wrapper for json mapping of chmod request + */ + @XmlRootElement + public static class ChmodRequest { + @XmlElement(nillable = false, required = true) + public String path; + @XmlElement(nillable = false, required = true) + public String mode; + } /** * Wrapper for json mapping of request with http://git-wip-us.apache.org/repos/asf/ambari/blob/d5b9af74/contrib/views/files/src/main/java/org/apache/ambari/view/filebrowser/HdfsApi.java ---------------------------------------------------------------------- diff --git a/contrib/views/files/src/main/java/org/apache/ambari/view/filebrowser/HdfsApi.java b/contrib/views/files/src/main/java/org/apache/ambari/view/filebrowser/HdfsApi.java index 79ea0d9..cd88c54 100644 --- a/contrib/views/files/src/main/java/org/apache/ambari/view/filebrowser/HdfsApi.java +++ b/contrib/views/files/src/main/java/org/apache/ambari/view/filebrowser/HdfsApi.java @@ -250,6 +250,27 @@ public class HdfsApi { } /** + * Change permissions + * @param path path + * @param permissions permissions in format rwxrwxrwx + * @throws IOException + * @throws InterruptedException + */ + public boolean chmod(final String path, final String permissions) throws IOException, + InterruptedException { + return ugi.doAs(new PrivilegedExceptionAction<Boolean>() { + public Boolean run() throws Exception { + try { + fs.setPermission(new Path(path), new FsPermission(permissions)); + } catch (Exception ex) { + return false; + } + return true; + } + }); + } + + /** * Copy file * @param src source path * @param dest destination path http://git-wip-us.apache.org/repos/asf/ambari/blob/d5b9af74/contrib/views/files/src/main/resources/ui/app/adapter.js ---------------------------------------------------------------------- diff --git a/contrib/views/files/src/main/resources/ui/app/adapter.js b/contrib/views/files/src/main/resources/ui/app/adapter.js index b8e127b..a6e471f 100644 --- a/contrib/views/files/src/main/resources/ui/app/adapter.js +++ b/contrib/views/files/src/main/resources/ui/app/adapter.js @@ -87,11 +87,6 @@ function _move(adapter, store, record, query) { return store.push('file', payload); }, function(reason) { - if (reason instanceof DS.InvalidError) { - store.recordWasInvalid(record, reason.errors); - } else { - store.recordWasError(record, reason); - } throw reason; }, label); @@ -113,19 +108,14 @@ function _mkdir(adapter, store, type, query) { return store.push('file', payload); }, function(reason) { - if (reason instanceof DS.InvalidError) { - store.recordWasInvalid(record, reason.errors); - } else { - store.recordWasError(record, reason); - } - throw reason; }, label); } -function _remove(adapter, store, record, query) { +function _remove(adapter, store, record, query, toTrash) { var type = record.constructor; - var promise = adapter.remove(store, type, query), + var method = (toTrash)?'moveToTrash':'remove'; + var promise = adapter[method](store, type, query), serializer = serializerForAdapter(adapter, type), label = ""; @@ -136,7 +126,8 @@ function _remove(adapter, store, record, query) { if (reason instanceof DS.InvalidError) { store.recordWasInvalid(record, reason.errors); } else { - store.recordWasError(record, reason); + record.rollback(); + //store.recordWasError(record, reason); } throw reason; @@ -172,12 +163,22 @@ App.Store = DS.Store.extend({ move:function (store, type, record, query) { return this.ajax(this.buildURL('fileops','rename'), 'POST', { data: query }); }, + updateRecord:function (store, type, record) { + var query = { + "path":record.get('path'), + "mode":record.get('permission') + }; + return this.ajax(this.buildURL('fileops','chmod'), 'POST', { data: query }); + }, mkdir:function (store, type, query) { return this.ajax(this.buildURL('fileops','mkdir'), 'PUT', { data: query }); }, remove:function (store, type, query) { return this.ajax(this.buildURL('fileops','remove'), 'DELETE', { data: query }); }, + moveToTrash:function (store, type, query) { + return this.ajax(this.buildURL('fileops','moveToTrash'), 'DELETE', { data: query }); + }, downloadUrl:function (option, query) { return [this.buildURL('download',option),Em.$.param(query)].join('?'); }, @@ -218,6 +219,9 @@ App.Store = DS.Store.extend({ return DS.PromiseObject.create({ promise: resolver.promise }); }, + chmod:function (record, path) { + return record.save(); + }, mkdir:function (path) { var query = { "path":path @@ -231,7 +235,7 @@ App.Store = DS.Store.extend({ return DS.PromiseObject.create({ promise: resolver.promise }); }, - remove:function (record) { + remove:function (record, toTrash) { var query = { "path":record.get('path'), "recursive":true @@ -242,7 +246,7 @@ App.Store = DS.Store.extend({ var adapter = this.adapterFor(type); record.deleteRecord(); - resolver.resolve(_remove(adapter, this, record, query)); + resolver.resolve(_remove(adapter, this, record, query, toTrash)); return DS.PromiseObject.create({ promise: resolver.promise }); }, @@ -279,7 +283,7 @@ App.Store = DS.Store.extend({ return resolver.promise.then(function(response) { return adapter.downloadUrl(option,response); }, function(reason) { - //TODO reject + throw reason; }); } }) @@ -293,7 +297,10 @@ App.FileSerializer = DS.RESTSerializer.extend({ extractSingle: function(store, type, payload, id, requestType) { payload = {'files': payload}; return this._super(store, type, payload, id, requestType); - } + }, + extractChmod:function(store, type, payload, id, requestType) { + return this.extractSingle(store, type, payload, id, requestType); + }, }); App.Uploader = Ember.Uploader.create({ @@ -307,11 +314,19 @@ App.Uploader = Ember.Uploader.create({ this.set('isUploading', true); - return this.ajax(url, data, type).then(function(respData) { - self.didUpload(respData); - return respData; - }); + return this.ajax(url, data, type) + .then(Em.run.bind(this,this.uploadSuccess),Em.run.bind(this,this.uploadFailed)); + }, + uploadSuccess:function(respData) { + this.didUpload(respData); + return respData; + }, + uploadFailed:function (error) { + this.set('isUploading', false); + this.sendAlert(error); + return error; }, + sendAlert: Em.K, ajax: function(url, params, method) { var self = this; var settings = { http://git-wip-us.apache.org/repos/asf/ambari/blob/d5b9af74/contrib/views/files/src/main/resources/ui/app/components/breadCrumbs.js ---------------------------------------------------------------------- diff --git a/contrib/views/files/src/main/resources/ui/app/components/breadCrumbs.js b/contrib/views/files/src/main/resources/ui/app/components/breadCrumbs.js new file mode 100644 index 0000000..8448f05 --- /dev/null +++ b/contrib/views/files/src/main/resources/ui/app/components/breadCrumbs.js @@ -0,0 +1,47 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var App = require('app'); + +App.BreadCrumbsComponent = Ember.CollectionView.extend({ + classNames: ['breadcrumb pull-left'], + tagName: 'ul', + path:'', + content: function (argument) { + var crumbs = []; + var currentPath = this.get('path').match(/((?!\/)\S)+/g)||[]; + currentPath.forEach(function (cur,i,array) { + return crumbs.push({name:cur,path:'/'+array.slice(0,i+1).join('/')}); + }); + crumbs.unshift({name:'/',path:'/'}); + crumbs.get('lastObject').last = 'true'; + return crumbs; + }.property('path'), + itemViewClass: Ember.View.extend({ + classNameBindings: ['isActive:active'], + template: Ember.Handlebars.compile("{{#link-to 'files' (query-params path=view.content.path)}}{{view.content.name}}{{/link-to}}"), + isActive: function () { + return this.get('content.last'); + }.property('content'), + click:function () { + if (this.get('isActive')) { + this.get('controller').send('refreshDir'); + } + } + }) +}); http://git-wip-us.apache.org/repos/asf/ambari/blob/d5b9af74/contrib/views/files/src/main/resources/ui/app/components/bsPopover.js ---------------------------------------------------------------------- diff --git a/contrib/views/files/src/main/resources/ui/app/components/bsPopover.js b/contrib/views/files/src/main/resources/ui/app/components/bsPopover.js new file mode 100644 index 0000000..dbc9785 --- /dev/null +++ b/contrib/views/files/src/main/resources/ui/app/components/bsPopover.js @@ -0,0 +1,21 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var App = require('app'); + +App.BsPopoverComponent = Ember.BsPopoverComponent.extend({}); http://git-wip-us.apache.org/repos/asf/ambari/blob/d5b9af74/contrib/views/files/src/main/resources/ui/app/components/bulkCheckbox.js ---------------------------------------------------------------------- diff --git a/contrib/views/files/src/main/resources/ui/app/components/bulkCheckbox.js b/contrib/views/files/src/main/resources/ui/app/components/bulkCheckbox.js new file mode 100644 index 0000000..34c9a1f --- /dev/null +++ b/contrib/views/files/src/main/resources/ui/app/components/bulkCheckbox.js @@ -0,0 +1,40 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var App = require('app'); + +App.BulkCheckboxComponent = Em.Checkbox.extend({ + changeBinding:'selectAll', + checkedBinding:'selectedAll', + selectedAll:false, + selectAll:function () { + var checked = this.get('checked'); + var items = this.get('content'); + return items.forEach(function (item) { + item.set('selected',checked); + }); + }, + selection:function () { + var selected = this.get('content').filterBy('selected',true); + if (selected.length == this.get('content.length') && selected.length > 0) { + this.set('selectedAll',true); + } else { + this.set('selectedAll',false); + } + }.observes('[email protected]'), +}); http://git-wip-us.apache.org/repos/asf/ambari/blob/d5b9af74/contrib/views/files/src/main/resources/ui/app/components/chmodInput.js ---------------------------------------------------------------------- diff --git a/contrib/views/files/src/main/resources/ui/app/components/chmodInput.js b/contrib/views/files/src/main/resources/ui/app/components/chmodInput.js new file mode 100644 index 0000000..151b681 --- /dev/null +++ b/contrib/views/files/src/main/resources/ui/app/components/chmodInput.js @@ -0,0 +1,79 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var App = require('app'); + +var _permissionsProp = function(n, l) { + return function (arg,val) { + if (arguments.length > 1) { + this.set('permissions', this.replaceAt(n,(val)?l:'-')); + return val; + }; + return this.get('permissions')[n]===l; + } +} + +App.ChmodInputComponent = Em.Component.extend({ + layoutName:'components/chmodInput', + tagName:'tr', + classNames:"chmod-row", + file:null, + permissions:Em.computed.alias('file.permission'), + usrR:_permissionsProp(1, 'r').property('permissions'), + usrW:_permissionsProp(2, 'w').property('permissions'), + usrE:_permissionsProp(3, 'x').property('permissions'), + grpR:_permissionsProp(4, 'r').property('permissions'), + grpW:_permissionsProp(5, 'w').property('permissions'), + grpE:_permissionsProp(6, 'x').property('permissions'), + otrR:_permissionsProp(7, 'r').property('permissions'), + otrW:_permissionsProp(8, 'w').property('permissions'), + otrE:_permissionsProp(9, 'x').property('permissions'), + replaceAt:function (index,p) { + var perm = this.get('permissions'); + var newPerm = perm.substr(0, index) + p + perm.substr(index + p.length); + return newPerm; + }, + markActive:function () { + if (this.get('isVisible')) { + this.$('.btn-chmod').each(function () { + if ($(this).children('input').is(':checked')) { + $(this).addClass('active'); + } else { + $(this).removeClass('active'); + } + }); + } + }.observes('chVisible'), + showModal:function () { + this.$('.chmodal').modal('toggle'); + }.observes('chVisible'), + actions:{ + confirm:function (r) { + this.sendAction('confirm',r); + this.set('chVisible',false); + }, + close:function () { + var file = this.get('file'); + var diff = file.changedAttributes(); + if (diff.permission) { + file.set('permission',diff.permission[0]); + }; + this.set('chVisible',false); + } + } +}); http://git-wip-us.apache.org/repos/asf/ambari/blob/d5b9af74/contrib/views/files/src/main/resources/ui/app/components/confirmDelete.js ---------------------------------------------------------------------- diff --git a/contrib/views/files/src/main/resources/ui/app/components/confirmDelete.js b/contrib/views/files/src/main/resources/ui/app/components/confirmDelete.js new file mode 100644 index 0000000..e0122bf --- /dev/null +++ b/contrib/views/files/src/main/resources/ui/app/components/confirmDelete.js @@ -0,0 +1,59 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var App = require('app'); + +App.DropdownWrapComponent = Em.Component.extend({ + onResetConfirm:function () { + var childs = this.get('childViews').filter(function (view) { + return view instanceof App.ConfirmDeleteComponent; + }); + childs.setEach('isRemoving',false); + }.on('resetConfirm'), + didInsertElement:function(){ + this.$().on('hidden.bs.dropdown',Em.run.bind(this,this.onResetConfirm)); + }, +}); + +App.ConfirmDeleteComponent = Em.Component.extend({ + layoutName:'components/deleteBulk', + tagName:'li', + deleteForever:false, + isRemoving:false, + cancelRemoving:function () { + this.set('isRemoving',false); + }, + click:function (e) { + if (!$(e.target).hasClass('delete')) { + e.stopPropagation(); + }; + }, + actions:{ + ask:function () { + this.get('parentView').trigger('resetConfirm'); + this.set('isRemoving',true); + return false; + }, + cancel:function () { + this.cancelRemoving(); + }, + confirm:function () { + this.sendAction('confirm',this.get('deleteForever')); + } + } +}); http://git-wip-us.apache.org/repos/asf/ambari/blob/d5b9af74/contrib/views/files/src/main/resources/ui/app/components/contextMenu.js ---------------------------------------------------------------------- diff --git a/contrib/views/files/src/main/resources/ui/app/components/contextMenu.js b/contrib/views/files/src/main/resources/ui/app/components/contextMenu.js index 9807e0c..c35b485 100644 --- a/contrib/views/files/src/main/resources/ui/app/components/contextMenu.js +++ b/contrib/views/files/src/main/resources/ui/app/components/contextMenu.js @@ -18,21 +18,25 @@ var App = require('app'); -App.ContextMenu = Em.Component.extend({ - layoutName:'util/contextMenu', - waitConfirm: null, - startWaitConfirm:function (v,observer) { - var self = this, - action = this.get(observer); - if (!action) { - $(this.get('element')).off('hidden.bs.context'); - return false; - }; - $(this.get('element')).on('hidden.bs.context',function(){ - self.get('target').send(self.get(observer),'cancel'); - self.set('waitConfirm', null) - }) - this.get('target').send(action,'ask'); - }.observes('waitConfirm') +App.ContextMenuComponent = Em.Component.extend({ + layoutName:'components/contextMenu', + + onTargetChange:function () { + this.$().off('hidden.bs.context'); + this.$().on('hidden.bs.context', Em.run.bind(this, this.resetConfirmations)); + }.observes('target'), + + resetConfirmations:function () { + this.triggerRecursively('resetConfirm'); + }, + + actions:{ + removeFile:function () { + this.get('target').send('deleteFile',true); + }, + moveToTrash:function () { + this.get('target').send('deleteFile'); + }, + } }); http://git-wip-us.apache.org/repos/asf/ambari/blob/d5b9af74/contrib/views/files/src/main/resources/ui/app/components/mkdirInput.js ---------------------------------------------------------------------- diff --git a/contrib/views/files/src/main/resources/ui/app/components/mkdirInput.js b/contrib/views/files/src/main/resources/ui/app/components/mkdirInput.js new file mode 100644 index 0000000..c97f8d4 --- /dev/null +++ b/contrib/views/files/src/main/resources/ui/app/components/mkdirInput.js @@ -0,0 +1,45 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var App = require('app'); + +App.MkdirInputComponent = Em.Component.extend({ + layoutName:'components/mkdirInput', + newDirName:'', + isMkdir:false, + path:'', + actions:{ + create:function () { + var name = this.get('newDirName'); + + if (Em.isEmpty(name)) { + return false; + } + newDir = [this.get('path'),name].join('/').replace('//','/'); + + this.sendAction('create',newDir); + this.setProperties({'newDirName':'','isMkdir':false}); + }, + edit:function () { + this.set('isMkdir',true); + }, + cancel:function () { + this.setProperties({'newDirName':'','isMkdir':false}); + } + } +}); http://git-wip-us.apache.org/repos/asf/ambari/blob/d5b9af74/contrib/views/files/src/main/resources/ui/app/components/popoverDelete.js ---------------------------------------------------------------------- diff --git a/contrib/views/files/src/main/resources/ui/app/components/popoverDelete.js b/contrib/views/files/src/main/resources/ui/app/components/popoverDelete.js new file mode 100644 index 0000000..bd398df --- /dev/null +++ b/contrib/views/files/src/main/resources/ui/app/components/popoverDelete.js @@ -0,0 +1,50 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var App = require('app'); + +App.PopoverDeleteComponent = Em.Component.extend({ + popover:Em.computed.alias('childViews.firstObject'), + layoutName:'components/deletePopover', + deleteForever:false, + actions:{ + confirm:function (deleteForever) { + this.sendAction('confirm',this.get('deleteForever')); + }, + close:function () { + this.set('popover.isVisible',false); + } + }, + didInsertElement:function () { + $('body').on('click.popover', Em.run.bind(this,this.hideMultiply)); + }, + hideMultiply:function (e) { + if (!this.$()) { + return; + } + if (!this.$().is(e.target) + && this.$().has(e.target).length === 0 + && $('.popover').has(e.target).length === 0) { + this.set('popover.isVisible',false); + } + }, + willClearRender:function () { + this.get('popover').$element.off('click'); + $('body').off('click.popover'); + }, +}); http://git-wip-us.apache.org/repos/asf/ambari/blob/d5b9af74/contrib/views/files/src/main/resources/ui/app/components/renameInput.js ---------------------------------------------------------------------- diff --git a/contrib/views/files/src/main/resources/ui/app/components/renameInput.js b/contrib/views/files/src/main/resources/ui/app/components/renameInput.js index 9a826a4..cd311e9 100644 --- a/contrib/views/files/src/main/resources/ui/app/components/renameInput.js +++ b/contrib/views/files/src/main/resources/ui/app/components/renameInput.js @@ -21,10 +21,10 @@ var App = require('app'); App.RenameInputComponent = Ember.Component.extend({ tagName:'span', - layoutName:'util/renameInput', + layoutName:'components/renameInput', actions:{ rename:function (opt) { - var target, tmpName; + var tmpName; switch (opt) { case 'edit': this.set('isRenaming',true); break; @@ -34,8 +34,7 @@ App.RenameInputComponent = Ember.Component.extend({ if (tmpName.length==0) { break; }; - target = this.get('targetObject'); - target.send(this.get('actionName'),this.get('filePath'),tmpName); + this.sendAction('confirm',this.get('filePath'),tmpName); this.set('isRenaming',false); break; http://git-wip-us.apache.org/repos/asf/ambari/blob/d5b9af74/contrib/views/files/src/main/resources/ui/app/components/sortArrow.js ---------------------------------------------------------------------- diff --git a/contrib/views/files/src/main/resources/ui/app/components/sortArrow.js b/contrib/views/files/src/main/resources/ui/app/components/sortArrow.js new file mode 100644 index 0000000..7c4a4e4 --- /dev/null +++ b/contrib/views/files/src/main/resources/ui/app/components/sortArrow.js @@ -0,0 +1,39 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var App = require('app'); + +App.SortArrowComponent = Em.Component.extend({ + layout:Ember.Handlebars.compile('<i {{bind-attr class=":fa view.asc:fa-chevron-down:fa-chevron-up view.cur::fa-gr view.cur::fa-rotate-270" }} ></i>'), + classNames:['pull-right'], + tagName:'span', + sPs:[], + sA:false, + sP:null, + asc:true, + cur:false, + sorting:function () { + if (this.get('sPs.firstObject') == this.get('sP')) { + this.set('asc',this.get('sA')); + this.set('cur',true); + } else{ + this.set('asc',true); + this.set('cur',false); + }; + }.observes('sPs','sA').on('init'), +}); http://git-wip-us.apache.org/repos/asf/ambari/blob/d5b9af74/contrib/views/files/src/main/resources/ui/app/components/toggleContext.js ---------------------------------------------------------------------- diff --git a/contrib/views/files/src/main/resources/ui/app/components/toggleContext.js b/contrib/views/files/src/main/resources/ui/app/components/toggleContext.js new file mode 100644 index 0000000..326fa64 --- /dev/null +++ b/contrib/views/files/src/main/resources/ui/app/components/toggleContext.js @@ -0,0 +1,56 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var App = require('app'); + +App.ToggleContextComponent = Em.Component.extend({ + didInsertElement:function () { + var fileRow = this.$().parents('.file-row'), + beforeHandler = Ember.run.bind(this, this.setContext), + itemHandler = Ember.run.bind(this, this.itemHandler); + + fileRow.on('click',Ember.run.bind(this, this.openOnClick)); + + fileRow.contextmenu({ + target:'#context-menu', + before:beforeHandler, + onItem:itemHandler + }); + }, + setContext:function(e) { + if (this.get('targetObject.isMoving')) { + return false; + }; + this.set('targetObject.parentController.targetContextMenu',this.get('targetObject')); + return true; + }, + itemHandler:function (t,e) { + if (e.target.dataset.disabled) { + return false; + }; + }, + openOnClick:function (e) { + if($(e.target).is('td') || $(e.target).hasClass('allow-open')){ + this.get('targetObject').send('open'); + } + }, + willClearRender:function () { + this.$().parents('.file-row').off('click'); + this.$().parents('.file-row').off('.context.data-api').removeData('context'); + } +}); http://git-wip-us.apache.org/repos/asf/ambari/blob/d5b9af74/contrib/views/files/src/main/resources/ui/app/components/uploader.js ---------------------------------------------------------------------- diff --git a/contrib/views/files/src/main/resources/ui/app/components/uploader.js b/contrib/views/files/src/main/resources/ui/app/components/uploader.js index 05efd58..5837c36 100644 --- a/contrib/views/files/src/main/resources/ui/app/components/uploader.js +++ b/contrib/views/files/src/main/resources/ui/app/components/uploader.js @@ -21,6 +21,12 @@ var App = require('app'); App.FileUploaderComponent = Ember.Component.extend({ didInsertElement:function () { + var _this = this; + this.uploader.reopen({ + sendAlert:function (e) { + _this.sendAction('alert',e); + } + }); this.fileInput.reopen({ filesDidChange: function() { var files = this.get('files'); @@ -42,12 +48,11 @@ App.FileUploaderComponent = Ember.Component.extend({ }, actions:{ upload:function (files) { - var self = this; var uploader = this.get('uploader'); var uploadBtn = Ladda.create(this.uploadButton.get('element')); var reset = function () { uploadBtn.stop(); - self.send('clear'); + this.send('clear'); }; if (!uploader.get('isUploading')) { var path = this.get('path'); @@ -57,7 +62,7 @@ App.FileUploaderComponent = Ember.Component.extend({ uploader.on('progress',function (e) { uploadBtn.setProgress(e.percent/100); }) - uploader.upload(file,{path:path}).then(reset,reset); + uploader.upload(file,{path:path}).finally(Em.run.bind(this,reset)); } }; }, @@ -66,7 +71,7 @@ App.FileUploaderComponent = Ember.Component.extend({ } }, uploader: null, - layoutName:'util/uploader', + layoutName:'components/uploader', path:'', info:'', files:null, http://git-wip-us.apache.org/repos/asf/ambari/blob/d5b9af74/contrib/views/files/src/main/resources/ui/app/controllers/error.js ---------------------------------------------------------------------- diff --git a/contrib/views/files/src/main/resources/ui/app/controllers/error.js b/contrib/views/files/src/main/resources/ui/app/controllers/error.js index 69b4777..ecf7749 100644 --- a/contrib/views/files/src/main/resources/ui/app/controllers/error.js +++ b/contrib/views/files/src/main/resources/ui/app/controllers/error.js @@ -17,9 +17,6 @@ */ App.ErrorController = Ember.ObjectController.extend({ - init:function () { - this._super(); - }, actions: { toggleStackTrace:function () { var value = this.get('isExpanded'); @@ -35,6 +32,8 @@ App.ErrorController = Ember.ObjectController.extend({ if (content && content.responseText) { var json = JSON.parse(content.responseText); text = json.message; + } else if (content && content.message) { + text = content.message; } return text; }.property('content'), http://git-wip-us.apache.org/repos/asf/ambari/blob/d5b9af74/contrib/views/files/src/main/resources/ui/app/controllers/file.js ---------------------------------------------------------------------- diff --git a/contrib/views/files/src/main/resources/ui/app/controllers/file.js b/contrib/views/files/src/main/resources/ui/app/controllers/file.js index 4c2cccd..f8b7046 100644 --- a/contrib/views/files/src/main/resources/ui/app/controllers/file.js +++ b/contrib/views/files/src/main/resources/ui/app/controllers/file.js @@ -16,6 +16,8 @@ * limitations under the License. */ +var App = require('app'); + App.FileController = Ember.ObjectController.extend({ init:function () { this.set('content.selected', false); @@ -25,41 +27,33 @@ App.FileController = Ember.ObjectController.extend({ download:function (option) { this.store.linkFor([this.get('content')],option).then(function (link) { window.location.href = link; - }); + },Em.run.bind(this,this.sendAlert)); + }, + showChmod:function () { + this.toggleProperty('chmodVisible',true); }, - rename:function (opt,file) { + rename:function (opt,name) { var file = this.get('content'), - self,path,name,newPath; - if (opt === 'edit') { - this.set('tmpName',file.get('name')); - this.set('isRenaming',true); - }; - - if (opt === 'cancel') { - this.set('tmpName',''); - this.set('isRenaming',false); - }; - - if (opt === 'confirm') { - self = this; - path = file.get('path'); - name = this.get('tmpName'); + path = file.get('path'), + newPath; - if (Em.isEmpty(name)) { - return false; - } - - if (name === file.get('name')) { - return self.set('isRenaming',false); - } + if (name === file.get('name') || Em.isEmpty(name)) { + return this.set('isRenaming',!Em.isEmpty(name)); + } - newPath = path.substring(0,path.lastIndexOf('/')+1)+name; + newPath = path.substring(0,path.lastIndexOf('/')+1)+name; - this.store.move(file,newPath).then(function () { - self.set('tmpName',''); - self.set('isRenaming',false); - }); - }; + this.store.move(file,newPath) + .then(Em.run.bind(this,this.set,'isRenaming',false),Em.run.bind(this,this.sendAlert)); + }, + editName:function () { + this.set('isRenaming',true); + }, + chmod:function (r) { + var record = this.get('content'); + this.store + .chmod(record) + .then(null,Em.run.bind(this,this.chmodErrorCallback,record)); }, open:function (file) { if (this.get('content.isDirectory')) { @@ -68,32 +62,22 @@ App.FileController = Ember.ObjectController.extend({ return this.send('download'); }; }, - removeFile:function (opt) { - if (opt=='ask') { - this.toggleProperty('isRemoving'); - console.log('ask removeFile') - return false; - }; - - if (opt == 'cancel' && !this.isDestroyed) { - this.set('isRemoving',false); - console.log('cancel removeFile') - } - - if (opt == 'confirm') { - this.set('isRemoving',false); - this.store.remove(this.get('content')); - } - }, - deleteFile:function () { - var file = this.get('content'); - this.store.remove(file); + deleteFile:function (deleteForever) { + this.store + .remove(this.get('content'),!deleteForever) + .then(null,Em.run.bind(this,this.sendAlert)); }, }, - tmpName:'', selected:false, isRenaming:false, - isRemoving:false, + isMovingToTrash:false, + chmodVisible:false, + targetContextMenu:null, + isPermissionsDirty:function () { + var file = this.get('content'); + var diff = file.changedAttributes(); + return !!diff.permission; + }.property('content.permission'), isMoving:function () { var movingFile = this.get('parentController.movingFile.path'); var thisFile = this.get('content.id'); @@ -102,5 +86,19 @@ App.FileController = Ember.ObjectController.extend({ setSelected:function (controller,observer) { this.set('selected',this.get(observer)) - }.observes('content.selected') + }.observes('content.selected'), + + renameSuccessCallback:function (record,error) { + record.rollback(); + this.sendAlert(error); + }, + + chmodErrorCallback:function (record,error) { + record.rollback(); + this.sendAlert(error); + }, + + sendAlert:function (error) { + this.send('showAlert',error); + }, }); http://git-wip-us.apache.org/repos/asf/ambari/blob/d5b9af74/contrib/views/files/src/main/resources/ui/app/controllers/files.js ---------------------------------------------------------------------- diff --git a/contrib/views/files/src/main/resources/ui/app/controllers/files.js b/contrib/views/files/src/main/resources/ui/app/controllers/files.js index 11b7c0c..a9d98e8 100644 --- a/contrib/views/files/src/main/resources/ui/app/controllers/files.js +++ b/contrib/views/files/src/main/resources/ui/app/controllers/files.js @@ -17,11 +17,12 @@ */ var App = require('app'); +var bind = Ember.run.bind; App.FilesController = Ember.ArrayController.extend({ actions:{ moveFile:function (opt,file) { - var src, title, self, + var src, title, file = file || this.get('selectedFiles.firstObject'), moving = this.get('movingFile'); @@ -32,11 +33,8 @@ App.FilesController = Ember.ArrayController.extend({ }; if (opt == 'move') { - self = this; this.store.move(moving.path,[this.get('path'),moving.name].join('/').replace('//','/')) - .then(function () { - self.set('movingFile',null); - }); + .then(bind(this,this.set,'movingFile',null),bind(this,this.throwAlert)); }; if (opt == 'cancel') { @@ -47,7 +45,7 @@ App.FilesController = Ember.ArrayController.extend({ this.toggleProperty('isRenaming'); }, renameDir:function (path,newName) { - var self = this, + var _this = this, basedir = path.substring(0,path.lastIndexOf('/')+1); newPath = basedir + newName; @@ -59,25 +57,28 @@ App.FilesController = Ember.ArrayController.extend({ var recordExists = listdir.isAny('id',newPath); listdir.forEach(function (file) { - self.store.unloadRecord(file); + _this.store.unloadRecord(file); }); if (recordExists) { - return self.send('showAlert',{message:newPath + ' already exists.'}); + return _this.throwAlert({message:newPath + ' already exists.'}); }; - self.store.move(path,newPath).then(function (newDir) { - self.store.unloadRecord(newDir); - self.set('path',newPath); - }); - }); + return _this.store.move(path,newPath); + }).then(function (newDir) { + if (newDir) { + _this.store.unloadRecord(newDir); + _this.set('path',newPath); + }; + }).catch(bind(this,this.throwAlert)); }, - deleteFile:function () { + deleteFile:function (deleteForever) { var self = this; var selected = this.get('selectedFiles'); + var moveToTrash = !deleteForever; selected.forEach(function (file) { - self.store.remove(file); + self.store.remove(file,moveToTrash).then(null,bind(self,self.throwAlert)); }); }, download:function (option) { @@ -86,31 +87,9 @@ App.FilesController = Ember.ArrayController.extend({ window.location.href = link; }); }, - mkdir:function (opt) { - var name,self,newDir; - if (opt === 'edit') { - this.set('isMkdir',true); - }; - - if (opt === 'cancel') { - this.set('newDirName',''); - this.set('isMkdir',false); - }; - - if (opt === 'confirm') { - self = this; - name = this.get('newDirName'); - - if (Em.isEmpty(name)) { - return false; - } - newDir = [this.get('path'),name].join('/').replace('//','/'); - - this.store.mkdir(newDir).then(function () { - self.set('newDirName',''); - self.set('isMkdir',false); - }); - }; + mkdir:function (newDirName) { + this.store.mkdir(newDirName) + .then(bind(this,this.mkdirSuccessCalback),bind(this,this.throwAlert)); }, upload:function (opt) { if (opt === 'open') { @@ -129,6 +108,9 @@ App.FilesController = Ember.ArrayController.extend({ this.set('sortProperties',[pr]); this.set('sortAscending',true); }; + }, + clearSearchField:function () { + this.set('searchString',''); } }, init:function () { @@ -140,6 +122,7 @@ App.FilesController = Ember.ArrayController.extend({ controller.store.pushPayload('file',{file:e}); }); + this._super(); }, sortProperties: ['name'], @@ -149,10 +132,7 @@ App.FilesController = Ember.ArrayController.extend({ movingFile:null, uploader:App.Uploader, isRenaming:false, - isRemoving:false, - isMkdir:false, isUploading:false, - newDirName:'', queryParams: ['path'], path: '/', isRootDir:Ember.computed.equal('path', '/'), @@ -165,23 +145,32 @@ App.FilesController = Ember.ArrayController.extend({ }.property('path'), selectedOne:Ember.computed.equal('selectedFiles.length', 1), isSelected:Ember.computed.gt('selectedFiles.length', 0), - selectedFiles:Ember.computed.filterBy('content', 'selected', true), + selectedFiles:function () { + return this.get('content').filterBy('selected', true); + }.property('[email protected]'), canConcat:function () { return this.get('selectedFiles').filterProperty('isDirectory').get('length')==0; }.property('selectedFiles.length'), - fileList: Ember.computed.alias('arrangedContent') -}); + searchString:'', + fileList: function () { + var fileList = this.get('arrangedContent'); + var search = this.get('searchString'); + return (search)?fileList.filter(function (file) { + return !!file.get('name').match(search); + }):fileList; + }.property('arrangedContent','searchString'), + + mkdirSuccessCalback:function (newDir) { + if (newDir.get('path') != [this.get('path'),newDir.get('name')].join('/')){ + newDir.unloadRecord(); + newDir.store.listdir(this.get('path')); + } + }, -App.FilesAlertController = Em.ObjectController.extend({ - content:null, - output:function () { - var error = this.get('content'),output; - if (error instanceof Em.Error) { - output = error; - } else { - output = {status:error.status, message:error.statusText||error.message}; - }; - return output; - }.property('content') + throwAlert:function (error) { + this.send('showAlert',error); + } }); + + http://git-wip-us.apache.org/repos/asf/ambari/blob/d5b9af74/contrib/views/files/src/main/resources/ui/app/controllers/filesAlert.js ---------------------------------------------------------------------- diff --git a/contrib/views/files/src/main/resources/ui/app/controllers/filesAlert.js b/contrib/views/files/src/main/resources/ui/app/controllers/filesAlert.js index c2be8c2..f2ef6b6 100644 --- a/contrib/views/files/src/main/resources/ui/app/controllers/filesAlert.js +++ b/contrib/views/files/src/main/resources/ui/app/controllers/filesAlert.js @@ -17,4 +17,14 @@ */ App.FilesAlertController = App.ErrorController.extend({ + content:null, + output:function () { + var error = this.get('content'),output; + if (error instanceof Em.Error) { + output = error; + } else { + output = {status:error.status, message:error.statusText||error.message}; + }; + return output; + }.property('content') }); http://git-wip-us.apache.org/repos/asf/ambari/blob/d5b9af74/contrib/views/files/src/main/resources/ui/app/initialize.js ---------------------------------------------------------------------- diff --git a/contrib/views/files/src/main/resources/ui/app/initialize.js b/contrib/views/files/src/main/resources/ui/app/initialize.js index 564bdfe..8b79492 100644 --- a/contrib/views/files/src/main/resources/ui/app/initialize.js +++ b/contrib/views/files/src/main/resources/ui/app/initialize.js @@ -30,12 +30,16 @@ require('templates/application'); require('templates/index'); require('templates/files'); require('templates/error'); -require('templates/util/deletePopover'); -require('templates/util/uploader'); -require('templates/util/contextMenu'); -require('templates/util/deleteBulk'); require('templates/util/errorRow'); -require('templates/util/renameInput'); +require('templates/util/fileRow'); + +require('templates/components/uploader'); +require('templates/components/renameInput'); +require('templates/components/deletePopover'); +require('templates/components/mkdirInput'); +require('templates/components/contextMenu'); +require('templates/components/deleteBulk'); +require('templates/components/chmodInput'); ////////////////////////////////// // Models @@ -59,12 +63,22 @@ require('controllers/filesAlert'); require('components/uploader'); require('components/contextMenu'); require('components/renameInput'); +require('components/bsPopover'); +require('components/confirmDelete'); +require('components/sortArrow'); +require('components/breadCrumbs'); +require('components/popoverDelete'); +require('components/bulkCheckbox'); +require('components/mkdirInput'); +require('components/toggleContext'); +require('components/chmodInput'); ///////////////////////////////// // Views ///////////////////////////////// require('views/file'); +require('views/filesAlert'); ///////////////////////////////// // Routes http://git-wip-us.apache.org/repos/asf/ambari/blob/d5b9af74/contrib/views/files/src/main/resources/ui/app/routes/file.js ---------------------------------------------------------------------- diff --git a/contrib/views/files/src/main/resources/ui/app/routes/file.js b/contrib/views/files/src/main/resources/ui/app/routes/file.js index afe98a8..5df1c0b 100644 --- a/contrib/views/files/src/main/resources/ui/app/routes/file.js +++ b/contrib/views/files/src/main/resources/ui/app/routes/file.js @@ -25,6 +25,9 @@ App.FilesRoute = Em.Route.extend({ } }, actions:{ + refreshDir:function () { + this.refresh(); + }, error:function (error,transition,e) { if (this.router._lookupActiveView('files')) { this.send('showAlert',error); http://git-wip-us.apache.org/repos/asf/ambari/blob/d5b9af74/contrib/views/files/src/main/resources/ui/app/styles/application.less ---------------------------------------------------------------------- diff --git a/contrib/views/files/src/main/resources/ui/app/styles/application.less b/contrib/views/files/src/main/resources/ui/app/styles/application.less index 6242bc5..c44923e 100644 --- a/contrib/views/files/src/main/resources/ui/app/styles/application.less +++ b/contrib/views/files/src/main/resources/ui/app/styles/application.less @@ -68,6 +68,22 @@ .i-am-in { margin: 0; width: 80%; + .dir-name { + color: black; + } + } + .input-group-search { + .input-search { + padding-right: 25px; + } + .form-control-feedback { + position: absolute; + z-index: 2; + top: 8px; + right: 39px; + cursor: pointer; + opacity: 0.5; + } } } @@ -139,6 +155,24 @@ } } } + .chmod-row { + &:hover > td { + background-color: #fff; + cursor: default; + } + & > td { + border-top: 0; + padding: 0; + } +/* .chmod-wrap { + transition: all 0.3s ease; + opacity: 1; + margin-right: 0px; + height: 32px; + overflow: hidden; + padding-top: 5px; +} */ + } .btn-delete { .popover-content{ width: 80px; @@ -161,8 +195,8 @@ width: 130px; margin-bottom: 0; .delete-forever { - display: inline-block; - margin: 5px; + float: right; + margin: 0px 0px 0 10px; } } .mod-time{ @@ -249,6 +283,27 @@ } } +#bulkDropdown { + .sub-label{ + display: inline-block; + width: 55%; + } +} + +#context-menu { + .sub-label{ + display: inline-block; + width: 55%; + } + .dropdown-confirm { + margin: -4px 8px; + } +} + +.dropdown-confirm { + margin: -4px 0; +} + .fa-right { top: 3px; @@ -293,10 +348,6 @@ } } -.dropdown-confirm { - margin: -4px 0; -} - .renameable { display: inline-block; &.half { @@ -321,3 +372,7 @@ margin-left: -1px; } } +.modal-backdrop.in { + filter: alpha(opacity=0); + opacity: 0; +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ambari/blob/d5b9af74/contrib/views/files/src/main/resources/ui/app/templates/components/chmodInput.hbs ---------------------------------------------------------------------- diff --git a/contrib/views/files/src/main/resources/ui/app/templates/components/chmodInput.hbs b/contrib/views/files/src/main/resources/ui/app/templates/components/chmodInput.hbs new file mode 100644 index 0000000..4805e65 --- /dev/null +++ b/contrib/views/files/src/main/resources/ui/app/templates/components/chmodInput.hbs @@ -0,0 +1,100 @@ +{{! + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +}} + +<td colspan="8" class=""> + +<div class="modal chmodal" tabindex="-1" role="dialog" aria-hidden="true" data-backdrop="static"> + <div class="modal-dialog modal-sm"> + <div class="modal-content"> + <div class="modal-header"> + <button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">×</span><span class="sr-only">Close</span></button> + <h4 class="modal-title">Edit permission</h4> + </div> + <div class="modal-body"> + + <form class="form-horizontal" role="form"> + <div class="form-group"> + <label class="col-sm-2 control-label">User</label> + <div class="col-sm-10"> + <div class="btn-group" data-toggle="buttons"> + <label {{bind-attr class=":btn :btn-sm usrR:btn-primary:btn-default :btn-chmod" }} > + {{input type="checkbox" checked=usrR}} <span>Read</span> + </label> + <label {{bind-attr class=":btn :btn-sm usrW:btn-primary:btn-default :btn-chmod" }} > + {{input type="checkbox" checked=usrW}} <span>Write</span> + </label> + <label {{bind-attr class=":btn :btn-sm usrE:btn-primary:btn-default :btn-chmod" }} > + {{input type="checkbox" checked=usrE}} <span>Execute</span> + </label> + </div> + </div> + </div> + <div class="form-group"> + <label class="col-sm-2 control-label">Group</label> + <div class="col-sm-10"> + <div class="btn-group" data-toggle="buttons"> + <label {{bind-attr class=":btn :btn-sm grpR:btn-primary:btn-default :btn-chmod" }} > + {{input type="checkbox" checked=grpR}} <span>Read</span> + </label> + <label {{bind-attr class=":btn :btn-sm grpW:btn-primary:btn-default :btn-chmod" }} > + {{input type="checkbox" checked=grpW}} <span>Write</span> + </label> + <label {{bind-attr class=":btn :btn-sm grpE:btn-primary:btn-default :btn-chmod" }} > + {{input type="checkbox" checked=grpE}} <span>Execute</span> + </label> + </div> + </div> + </div> + <div class="form-group"> + <label class="col-sm-2 control-label">Other</label> + <div class="col-sm-10"> + <div class="btn-group" data-toggle="buttons"> + <label {{bind-attr class=":btn :btn-sm otrR:btn-primary:btn-default :btn-chmod" }} > + {{input type="checkbox" checked=otrR}} <span>Read</span> + </label> + <label {{bind-attr class=":btn :btn-sm otrW:btn-primary:btn-default :btn-chmod" }} > + {{input type="checkbox" checked=otrW}} <span>Write</span> + </label> + <label {{bind-attr class=":btn :btn-sm otrE:btn-primary:btn-default :btn-chmod" }} > + {{input type="checkbox" checked=otrE}} <span>Execute</span> + </label> + </div> + </div> + </div> + + <div class="form-group"> + <div class="col-sm-offset-2 col-sm-10"> + <div class="checkbox"> + <label> + {{input type="checkbox"}} <span> Modify recursively</span> + </label> + </div> + </div> + </div> + </form> + + </div> + <div class="modal-footer"> + <button type="button" class="btn btn-default" {{action 'close'}}>Close</button> + <button type="button" class="btn btn-primary" {{action 'confirm'}}>Save changes</button> + </div> + </div> + </div> +</div> + +</td> http://git-wip-us.apache.org/repos/asf/ambari/blob/d5b9af74/contrib/views/files/src/main/resources/ui/app/templates/components/contextMenu.hbs ---------------------------------------------------------------------- diff --git a/contrib/views/files/src/main/resources/ui/app/templates/components/contextMenu.hbs b/contrib/views/files/src/main/resources/ui/app/templates/components/contextMenu.hbs new file mode 100644 index 0000000..844363c --- /dev/null +++ b/contrib/views/files/src/main/resources/ui/app/templates/components/contextMenu.hbs @@ -0,0 +1,43 @@ +{{! + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +}} + +{{#dropdown-wrap}} +<div id="context-menu"> + <ul class="dropdown-menu dropdown-context compressed-context" role="menu"> + {{#if view.target.content.isDirectory}} + <li><a tabindex="-1" href="#" {{action 'open'}}>Open folder</a></li> + {{else}} + <li><a tabindex="-1" href="#" {{action 'download'}}>Download</a></li> + {{/if}} + <li><a tabindex="-1" href="#" {{action 'moveFile' 'cut' view.target.content}}>Move</a></li> + <li><a tabindex="-1" href="#" {{action 'showChmod'}} >Permissions</a></li> + <li><a tabindex="-1" href="#" {{action 'editName'}} >Rename</a></li> + <li class="divider"></li> + <li class="dropdown-submenu"> + <a href="#" data-disabled="disabled"> + <span> Delete </span> + <i class="fa fa-chevron-right pull-right fa-right"></i> + </a> + <ul class="dropdown-menu"> + {{confirm-delete confirm="removeFile" deleteForever=true }} + {{confirm-delete confirm="moveToTrash" deleteForever=false }} + </ul> + </li> + </ul> +</div> +{{/dropdown-wrap}} http://git-wip-us.apache.org/repos/asf/ambari/blob/d5b9af74/contrib/views/files/src/main/resources/ui/app/templates/components/deleteBulk.hbs ---------------------------------------------------------------------- diff --git a/contrib/views/files/src/main/resources/ui/app/templates/components/deleteBulk.hbs b/contrib/views/files/src/main/resources/ui/app/templates/components/deleteBulk.hbs new file mode 100644 index 0000000..e7e6c69 --- /dev/null +++ b/contrib/views/files/src/main/resources/ui/app/templates/components/deleteBulk.hbs @@ -0,0 +1,46 @@ +{{! + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +}} + +{{#if isRemoving}} +<a tabindex="-1"> + {{#if deleteForever}} + <i class="fa fa-fw fa-exclamation-triangle"></i> + <span class="sub-label" > Delete forever </span> + {{else}} + <i class="fa fa-fw fa-trash-o"></i> + <span class="sub-label" >Move To Trash</span> + {{/if}} + <div class="btn-group text-center dropdown-confirm"> + <button {{action 'cancel'}} type="button" class="btn btn-xs btn-danger"> + <span class="glyphicon glyphicon-remove"></span> + </button> + <button {{action 'confirm'}} type="button" class="btn btn-xs btn-success delete"> + <span class="glyphicon glyphicon-ok delete"></span> + </button> + </div> +</a> +{{else}} +<a {{action 'ask'}} tabindex="-1" href="#"> + {{#if deleteForever}} + <i class="fa fa-fw fa-exclamation-triangle"></i> <span class="sub-label" > Delete forever </span> + {{else}} + <i class="fa fa-fw fa-trash-o"></i> <span class="sub-label" >Move To Trash</span> + {{/if}} +</a> +{{/if}} + http://git-wip-us.apache.org/repos/asf/ambari/blob/d5b9af74/contrib/views/files/src/main/resources/ui/app/templates/components/deletePopover.hbs ---------------------------------------------------------------------- diff --git a/contrib/views/files/src/main/resources/ui/app/templates/components/deletePopover.hbs b/contrib/views/files/src/main/resources/ui/app/templates/components/deletePopover.hbs new file mode 100644 index 0000000..e5f3b9a --- /dev/null +++ b/contrib/views/files/src/main/resources/ui/app/templates/components/deletePopover.hbs @@ -0,0 +1,38 @@ +{{! + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +}} + +<a data-toggle="tooltip" data-placement="bottom" title="Delete"> <i class="fa fa-trash-o fa-lg"></i> </a> + +{{#bs-popover triggers='click' placement='left'}} + <div class="input-group" > + <div class="btn-group "> + <button {{action 'close'}} type="button" class="btn btn-xs btn-danger"> + <i class="fa fa-times fa-fw"></i> + </button> + <button {{action 'confirm'}} type="button" class="btn btn-xs btn-success"> + <i class="fa fa-check fa-fw"></i> + </button> + </div> + <div class="checkbox delete-forever"> + <label> + {{input type="checkbox" checkedBinding='deleteForever' }} Delete forever + </label> + </div> + </div> +{{/bs-popover}} + http://git-wip-us.apache.org/repos/asf/ambari/blob/d5b9af74/contrib/views/files/src/main/resources/ui/app/templates/components/mkdirInput.hbs ---------------------------------------------------------------------- diff --git a/contrib/views/files/src/main/resources/ui/app/templates/components/mkdirInput.hbs b/contrib/views/files/src/main/resources/ui/app/templates/components/mkdirInput.hbs new file mode 100644 index 0000000..0db9240 --- /dev/null +++ b/contrib/views/files/src/main/resources/ui/app/templates/components/mkdirInput.hbs @@ -0,0 +1,37 @@ +{{! + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +}} + +{{#unless isMkdir}} + <button type="button" {{action 'edit'}} {{bind-attr class=":btn :btn-default :btn-sm :pull-right :mkdirwrap"}}> + <i class="fa fa-plus"></i> New directory + </button> +{{else}} + <div class="input-group input-group-sm pull-right mkdir-area"> + {{input class="form-control mkdir-input" valueBinding='newDirName' placeholder="Enter Directory Name"}} + <div class="input-group-btn"> + <button type="button" {{action 'cancel'}} {{bind-attr class=":btn :btn-danger :btn-sm :btn-mkdir-cancel"}} > + <i class="fa fa-times"></i> Cancel + </button> + </div> + <div class="input-group-btn"> + <button type="button" {{action 'create'}} {{bind-attr class="newDirName::disabled :btn :btn-success :btn-sm :btn-mkdir"}} > + <i class="fa fa-check"></i> Create + </button> + </div> + </div> +{{/unless}} http://git-wip-us.apache.org/repos/asf/ambari/blob/d5b9af74/contrib/views/files/src/main/resources/ui/app/templates/components/renameInput.hbs ---------------------------------------------------------------------- diff --git a/contrib/views/files/src/main/resources/ui/app/templates/components/renameInput.hbs b/contrib/views/files/src/main/resources/ui/app/templates/components/renameInput.hbs new file mode 100644 index 0000000..57a7b7c --- /dev/null +++ b/contrib/views/files/src/main/resources/ui/app/templates/components/renameInput.hbs @@ -0,0 +1,38 @@ +{{! + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +}} +{{#if isRenaming}} + +<div class="input-group input-group-sm rename-area"> + {{view view.renameInputView class="form-control rename-input" valueBinding='tmpName'}} + <div class="input-group-btn"> + <button type="button" {{action 'rename' 'cancel'}} {{bind-attr class=":btn :btn-danger :btn-xs :btn-rename-cancel isRenaming:show"}} > + <i class="fa fa-times"></i> Cancel + </button> + </div> + <div class="input-group-btn"> + <button type="button" {{action 'rename' 'confirm'}} {{bind-attr class=":btn :btn-success :btn-xs :btn-rename isRenaming:show"}} > + <i class="fa fa-check"></i> Rename + </button> + </div> +</div> + +{{else}} + + {{yield}} + +{{/if}} http://git-wip-us.apache.org/repos/asf/ambari/blob/d5b9af74/contrib/views/files/src/main/resources/ui/app/templates/components/uploader.hbs ---------------------------------------------------------------------- diff --git a/contrib/views/files/src/main/resources/ui/app/templates/components/uploader.hbs b/contrib/views/files/src/main/resources/ui/app/templates/components/uploader.hbs new file mode 100644 index 0000000..cefc9f8 --- /dev/null +++ b/contrib/views/files/src/main/resources/ui/app/templates/components/uploader.hbs @@ -0,0 +1,35 @@ +{{! + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +}} + +<div class="input-group input-group-sm"> + <span class="input-group-btn"> + <span class="btn btn-primary btn-file"> + Browse⦠{{view fileInput}} + </span> + <span {{bind-attr class=":btn :btn-danger isFiles:hide"}} {{action 'clear'}} > + Clear + </span> + </span> + <span {{bind-attr class=":input-group-btn :btn-upload isFiles:hide"}}> + {{#view uploadButton data-style="expand-right" data-size="xs"}} + <span class="ladda-label">Upload</span> + {{/view}} + </span> + {{view controlInput placeholder='Select files to upload.'}} +</div> + http://git-wip-us.apache.org/repos/asf/ambari/blob/d5b9af74/contrib/views/files/src/main/resources/ui/app/templates/files.hbs ---------------------------------------------------------------------- diff --git a/contrib/views/files/src/main/resources/ui/app/templates/files.hbs b/contrib/views/files/src/main/resources/ui/app/templates/files.hbs index 8b4cff0..6f65e91 100644 --- a/contrib/views/files/src/main/resources/ui/app/templates/files.hbs +++ b/contrib/views/files/src/main/resources/ui/app/templates/files.hbs @@ -19,13 +19,13 @@ <div class="panel-default panel-files"> <div class="panel-heading"> {{!-- BREADCRUMBS --}} - {{view view.breadcrumbsView}} + {{bread-crumbs path=path}} <div class="um-section"> {{!-- UPLOADER --}} <div {{bind-attr class="isUploading::hide :pull-right" }}> <button {{action 'upload' 'close'}} type="button" class="close" aria-hidden="true">×</button> - {{file-uploader path=path uploader=uploader class=" upload-area pull-right"}} + {{file-uploader path=path uploader=uploader class=" upload-area pull-right" alert='showAlert'}} </div> <div {{bind-attr class="isUploading:hide: :pull-right :uploadwrap" }}> @@ -34,57 +34,26 @@ </button> </div> - {{!-- MKDIR --}} - {{#unless isMkdir}} - <button type="button" {{action 'mkdir' 'edit'}} {{bind-attr class=":btn :btn-default :btn-sm :pull-right :mkdirwrap"}}> - <i class="fa fa-plus"></i> New directory - </button> - {{else}} - <div class="input-group input-group-sm pull-right mkdir-area"> - {{input class="form-control mkdir-input" valueBinding='newDirName'}} - <div class="input-group-btn"> - <button type="button" {{action 'mkdir' 'cancel'}} {{bind-attr class=":btn :btn-danger :btn-sm :btn-mkdir-cancel"}} > - <i class="fa fa-times"></i> Cancel - </button> - </div> - <div class="input-group-btn"> - <button type="button" {{action 'mkdir' 'confirm'}} {{bind-attr class=":btn :btn-success :btn-sm :btn-mkdir"}} > - <i class="fa fa-check"></i> Create - </button> - </div> - </div> - {{/unless}} - + {{!-- MKDIR --}} + {{mkdir-input create="mkdir" path=path}} </div> </div> <div class="panel-body"> <h4 class="i-am-in pull-left"> <i class="fa fa-folder fa-lg"></i> - {{#rename-input file=path actionName='renameDir' isRenaming=isRenaming class='renameable stocked half'}} - {{currentDir}} <a href="#" {{bind-attr class="isRootDir:hide"}} {{action showRenameInput}}><i class="fa fa-edit"></i></a> + {{#rename-input file=path confirm='renameDir' isRenaming=isRenaming class='renameable stocked half'}} + <a href="#" class="dir-name" {{action refreshDir}}>{{currentDir}}</a> + <a href="#" {{bind-attr class="isRootDir:hide"}} {{action showRenameInput}}><i class="fa fa-edit"></i></a> {{/rename-input}} </h4> - <div class="btn-group btn-sort pull-right" data-toggle="tooltip" data-placement="left" title="Sort by:"> - <button type="button" class="btn btn-xs btn-default" {{action sort 'toggle'}}> - {{#if sortAscending}} Asc {{else}} Desc {{/if}} - </button> - - <button type="button" class="btn btn-xs btn-default dropdown-toggle" data-toggle="dropdown"> - <span> - {{capitalize sortProperties.firstObject}} - </span> - <span class="caret"></span> - </button> - <ul class="dropdown-menu" role="menu"> - <li><a href="#" {{action 'sort' 'name'}} >Name</a></li> - <li><a href="#" {{action 'sort' 'size'}} >Size</a></li> - <li><a href="#" {{action 'sort' 'owner'}} >Owner</a></li> - <li><a href="#" {{action 'sort' 'group'}} >Group</a></li> - <li><a href="#" {{action 'sort' 'permission'}} >Permission</a></li> - <li><a href="#" {{action 'sort' 'date'}} >Date</a></li> - </ul> + <div class="input-group input-group-sm input-group-search"> + {{input valueBinding='searchString' class="form-control input-search" placeholder="Search File Names" }} + {{#if searchString}} + <i {{action 'clearSearchField'}} class="fa fa-times form-control-feedback"></i> + {{/if}} + <div class="input-group-addon"><i class="fa fa-search"></i></div> </div> </div> @@ -92,44 +61,66 @@ <thead> <tr> <th class="icon"></th> - <th class="path" {{action 'sort' 'name'}}> Name {{view view.sortArrow sortProperty='name'}} </th> - <th class="size" {{action 'sort' 'size'}}>Size {{view view.sortArrow sortProperty='size'}}</th> - <th class="owner" {{action 'sort' 'owner'}}>Owner {{view view.sortArrow sortProperty='owner'}}</th> - <th class="grp" {{action 'sort' 'group'}} >Group {{view view.sortArrow sortProperty='group'}}</th> - <th class="perm" {{action 'sort' 'permission'}} >Permission {{view view.sortArrow sortProperty='permission'}}</th> + <th class="path" {{action 'sort' 'name'}}> Name {{sort-arrow sPs=sortProperties sA=sortAscending sP='name'}} </th> + <th class="size" {{action 'sort' 'size'}}>Size {{sort-arrow sPs=sortProperties sA=sortAscending sP='size'}}</th> + <th class="owner" {{action 'sort' 'owner'}}>Owner {{sort-arrow sPs=sortProperties sA=sortAscending sP='owner'}}</th> + <th class="grp" {{action 'sort' 'group'}} >Group {{sort-arrow sPs=sortProperties sA=sortAscending sP='group'}}</th> + <th class="perm" {{action 'sort' 'permission'}} >Permission {{sort-arrow sPs=sortProperties sA=sortAscending sP='permission'}}</th> <th class="download"> + <div class="btn-group btn-sort pull-right" data-toggle="tooltip" data-placement="left" title="Sort by:"> + <button type="button" class="btn btn-xs btn-default" {{action sort 'toggle'}}> + {{#if sortAscending}} Asc {{else}} Desc {{/if}} + </button> + + <button type="button" class="btn btn-xs btn-default dropdown-toggle" data-toggle="dropdown"> + <span> + {{capitalize sortProperties.firstObject}} + </span> + <span class="caret"></span> + </button> + <ul class="dropdown-menu" role="menu"> + <li><a href="#" {{action 'sort' 'name'}} >Name</a></li> + <li><a href="#" {{action 'sort' 'size'}} >Size</a></li> + <li><a href="#" {{action 'sort' 'owner'}} >Owner</a></li> + <li><a href="#" {{action 'sort' 'group'}} >Group</a></li> + <li><a href="#" {{action 'sort' 'permission'}} >Permission</a></li> + <li><a href="#" {{action 'sort' 'date'}} >Date</a></li> + </ul> + </div> </th> <th class="check"> + {{#dropdown-wrap}} <div id="bulkDropdown" class="btn-group"> - <span class="input-group-addon"> - <div class="checkbox"> - {{view view.checkboxAll contentBinding='fileList'}} - </div> - </span> - <button type="button" data-toggle="dropdown" {{bind-attr class=":btn :btn-xs :btn-default :dropdown-toggle isSelected::disabled"}} > - <span class="caret"></span> - </button> - <ul class="dropdown-menu pull-right" role="menu"> - <li><a href="#" {{action 'download' 'zip'}} ><i class="fa fa-archive fa-fw"></i> Download zip</a></li> - {{#if canConcat}} - <li><a href="#" {{action 'download' 'concat'}} ><i class="fa fa-th fa-fw"></i> Concat</a></li> - {{/if}} - <li class="divider"></li> - <li class="dropdown-submenu"> - <a href="#" disabled="disabled"> - <i class="fa fa-chevron-left fa-gr fa-fw"></i> Delete - </a> - <ul class="dropdown-menu left-submenu"> - {{view view.deleteBulkView}} - </ul> - </li> - </ul> - </div> + <span class="input-group-addon"> + <div class="checkbox"> + {{bulk-checkbox content=fileList}} + </div> + </span> + <button type="button" data-toggle="dropdown" {{bind-attr class=":btn :btn-xs :btn-default :dropdown-toggle isSelected::disabled"}} > + <span class="caret"></span> + </button> + <ul class="dropdown-menu pull-right" role="menu"> + <li><a href="#" {{action 'download' 'zip'}} ><i class="fa fa-archive fa-fw"></i> Download zip</a></li> + {{#if canConcat}} + <li><a href="#" {{action 'download' 'concat'}} ><i class="fa fa-th fa-fw"></i> Concat</a></li> + {{/if}} + <li class="divider"></li> + <li class="dropdown-submenu"> + <a href="#" disabled="disabled"> + <i class="fa fa-chevron-left fa-gr fa-fw"></i> Delete + </a> + <ul class="dropdown-menu left-submenu"> + {{confirm-delete confirm="deleteFile" deleteForever=true selector='bulkDropdown'}} + {{confirm-delete confirm="deleteFile" deleteForever=false selector='bulkDropdown'}} + </ul> + </li> + </ul> + </div> + {{/dropdown-wrap}} </th> </tr> </thead> <tbody> - <div> <tr> <td><i class="fa fa-folder"></i></td> <td {{action 'dirUp'}} colspan="7"> @@ -167,7 +158,7 @@ </td> <td> {{#unless content.isDirectory}} - {{humanSize movingFile.size}} + {{humanSize movingFile.len}} {{/unless}} </td> <td >{{movingFile.owner}}</td> @@ -186,87 +177,17 @@ </tr> {{/if}} {{/unless}} - </div> {{#each fileList itemController="file"}} - <tr {{bind-attr class=":file-row isMoving:isMoving"}}> - <td> - {{#if content.isDirectory}} - <i class="fa fa-folder"></i> - {{else}} - <i class="fa fa-file"></i> - {{/if}} - </td> - <td> - {{#unless isRenaming}} - <div class="file-name allow-open"> - <span> - <a {{action 'open'}}> - <strong> - {{content.name}} - </strong> - </a> - </span> - <span class="help-block mod-time allow-open"> - <small class='allow-open'> - Updated {{showDate modificationTime 'YYYY-MM-DD HH:mm'}} - </small> - </span> - </div> - {{else}} - <div class="input-group input-group-sm rename-area"> - {{view view.renameInputView class="form-control rename-input" valueBinding='tmpName'}} - <div class="input-group-btn"> - <button type="button" {{action 'rename' 'cancel'}} {{bind-attr class=":btn :btn-danger :btn-xs :btn-rename-cancel isRenaming:show"}} > - <i class="fa fa-times"></i> Cancel - </button> - </div> - <div class="input-group-btn"> - <button type="button" {{action 'rename' 'confirm'}} {{bind-attr class=":btn :btn-success :btn-xs :btn-rename isRenaming:show"}} > - <i class="fa fa-check"></i> Rename - </button> - </div> - </div> - {{/unless}} - </td> - <td> - {{#unless content.isDirectory}} - {{humanSize content.size}} - {{/unless}} - </td> - <td >{{content.owner}}</td> - <td>{{content.group}}</td> - <td>{{content.permission}}</td> - <td> - {{#unless isMoving}} - <ul class="list-inline file-actions text-right"> - <li> - {{#if content.isDirectory}} - <a href="#" {{action 'download' 'zip'}} data-toggle="tooltip" data-placement="bottom" title="Download zip"><i class="fa fa-archive fa-fw fa-lg"></i></a> - {{else}} - <a href="#" {{action 'download' 'browse'}} data-toggle="tooltip" data-placement="bottom" title="Download"><i class="fa fa-download fa-fw fa-lg"></i></a> - {{/if}} - </li> - <li> - <a href="#" {{action 'moveFile' 'cut' this.content}} data-toggle="tooltip" data-placement="bottom" title="Move"><i class="fa fa-level-down fa-rotate-270 fa-fw fa-lg"></i></span></a> - </li> - <li> - <a {{action 'rename' 'edit'}} data-toggle="tooltip" data-placement="bottom" title="Rename"><i class="fa fa-edit fa-lg"></i></a> - </li> - <li>{{view view.deleteSingleView}}</li> - </ul> - {{/unless}} - </td> - <td> - {{#if isMoving}} - <a href="#" {{action 'moveFile' 'cancel' target="parentController" }} data-toggle="tooltip" data-placement="bottom" title="Cancel moving"> <i class="fa fa-times fa-lg"></i></a> - {{else}} - {{input type="checkbox" checkedBinding='content.selected'}} - {{/if}} - {{view view.togglecontext}} - </td> - </tr> + {{partial 'util/fileRow'}} {{/each}} + {{#unless fileList}} + <tr> + <td colspan="8"> + No files + </td> + </tr> + {{/unless}} </tbody> </table> - {{view view.contextMenu }} + {{context-menu target=targetContextMenu}} </div>
