You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by db...@apache.org on 2016/02/24 19:18:03 UTC

[05/10] ambari git commit: AMBARI-15145. Revamped Filebrowser Design - UI. (dipayanb)

http://git-wip-us.apache.org/repos/asf/ambari/blob/b988562a/contrib/views/files/src/main/resources/ui/app/components/context-row-menu.js
----------------------------------------------------------------------
diff --git a/contrib/views/files/src/main/resources/ui/app/components/context-row-menu.js b/contrib/views/files/src/main/resources/ui/app/components/context-row-menu.js
new file mode 100644
index 0000000..1e9551b
--- /dev/null
+++ b/contrib/views/files/src/main/resources/ui/app/components/context-row-menu.js
@@ -0,0 +1,137 @@
+/**
+ * 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.
+ */
+
+import Ember from 'ember';
+
+export default Ember.Component.extend({
+  fileSelectionService: Ember.inject.service('files-selection'),
+  modalEventBus: Ember.inject.service('modal-event-bus'),
+  alertMessages: Ember.inject.service('alert-messages'),
+  filesDownloadService: Ember.inject.service('files-download'),
+
+  classNames: ['row', 'context-menu-row'],
+  selectedFilesCount: Ember.computed.oneWay('fileSelectionService.filesCount'),
+  selectedFolderCount: Ember.computed.oneWay('fileSelectionService.folderCount'),
+  isMultiSelected: Ember.computed('selectedFilesCount', 'selectedFolderCount', function() {
+    return this.get('selectedFilesCount') + this.get('selectedFolderCount') > 1;
+  }),
+  isSingleSelected: Ember.computed('selectedFilesCount', 'selectedFolderCount', function() {
+    return this.get('selectedFilesCount') + this.get('selectedFolderCount') === 1;
+  }),
+  isSelected: Ember.computed('selectedFilesCount', 'selectedFolderCount', function() {
+    return (this.get('selectedFilesCount') + this.get('selectedFolderCount')) !== 0;
+  }),
+  isOnlyMultiFilesSelected: Ember.computed('selectedFilesCount', 'selectedFolderCount', function() {
+    return this.get('selectedFolderCount') === 0 && this.get('selectedFilesCount') > 1;
+  }),
+
+  didInitAttrs: function() {
+    // Register different modal so that they can be controlled from outside
+    this.get('modalEventBus').registerModal('ctx-open');
+    this.get('modalEventBus').registerModal('ctx-rename');
+    this.get('modalEventBus').registerModal('ctx-permission');
+    this.get('modalEventBus').registerModal('ctx-delete');
+    this.get('modalEventBus').registerModal('ctx-copy');
+    this.get('modalEventBus').registerModal('ctx-move');
+    this.get('modalEventBus').registerModal('ctx-download');
+    this.get('modalEventBus').registerModal('ctx-concatenate');
+  },
+
+  willDestroyElement() {
+    this.get('modalEventBus').resetModal('ctx-open');
+    this.get('modalEventBus').resetModal('ctx-rename');
+    this.get('modalEventBus').resetModal('ctx-permission');
+    this.get('modalEventBus').resetModal('ctx-delete');
+    this.get('modalEventBus').resetModal('ctx-copy');
+    this.get('modalEventBus').resetModal('ctx-move');
+    this.get('modalEventBus').resetModal('ctx-download');
+    this.get('modalEventBus').resetModal('ctx-concatenate');
+  },
+
+  actions: {
+    open: function(event) {
+      if (this.get('isSingleSelected')) {
+        var file = this.get('fileSelectionService.files').objectAt(0);
+        if (file.get('isDirectory')) {
+          this.sendAction('openFolderAction', file.get('path'));
+        } else {
+          this.get('modalEventBus').showModal('ctx-open');
+        }
+      }
+
+    },
+
+    delete: function(event) {
+      if (!this.get('isSelected')) {
+        return false;
+      }
+      this.get('modalEventBus').showModal('ctx-delete');
+    },
+
+    copy: function(event) {
+      if (!this.get('isSelected')) {
+        return false;
+      }
+      this.get('modalEventBus').showModal('ctx-copy');
+    },
+
+    move: function(event) {
+      if (!this.get('isSelected')) {
+        return false;
+      }
+      this.get('modalEventBus').showModal('ctx-move');
+    },
+
+    download: function(event) {
+      if (!this.get('isSelected')) {
+        return false;
+      }
+      this.get('filesDownloadService').download();
+    },
+
+    concatenate: function(event) {
+      if (!this.get('isOnlyMultiFilesSelected')) {
+        return false;
+      }
+      this.get('filesDownloadService').concatenate();
+    },
+
+    rename: function(event) {
+      if (!this.get('isSingleSelected')) {
+        return false;
+      }
+      this.get('modalEventBus').showModal('ctx-rename');
+    },
+    permission: function(event) {
+      if (!this.get('isSingleSelected')) {
+        return false;
+      }
+      this.get('modalEventBus').showModal('ctx-permission');
+    },
+
+    modalClosed: function(modalName) {
+      this.get('modalEventBus').resetModal(modalName);
+    },
+
+    refreshCurrentRoute: function() {
+      this.get('fileSelectionService').reset();
+      this.sendAction('refreshCurrentRouteAction');
+    }
+  }
+
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/b988562a/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
deleted file mode 100644
index 0c715dc..0000000
--- a/contrib/views/files/src/main/resources/ui/app/components/contextMenu.js
+++ /dev/null
@@ -1,42 +0,0 @@
-/**
- * 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.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/b988562a/contrib/views/files/src/main/resources/ui/app/components/copy-modal.js
----------------------------------------------------------------------
diff --git a/contrib/views/files/src/main/resources/ui/app/components/copy-modal.js b/contrib/views/files/src/main/resources/ui/app/components/copy-modal.js
new file mode 100644
index 0000000..79a78ec
--- /dev/null
+++ b/contrib/views/files/src/main/resources/ui/app/components/copy-modal.js
@@ -0,0 +1,126 @@
+/**
+ * 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.
+ */
+
+import Ember from 'ember';
+import OperationModal from '../mixins/operation-modal';
+
+export default Ember.Component.extend(OperationModal, {
+  closeOnEscape: true,
+  fileSelectionService: Ember.inject.service('files-selection'),
+  fileOperationService: Ember.inject.service('file-operation'),
+  selectedFiles: Ember.computed.alias('fileSelectionService.files'),
+  selected: Ember.computed('selectedFiles', function () {
+    return this.get('selectedFiles').objectAt(0);
+  }),
+  selectionName: '/',
+  isUpdating: false,
+  browseError: false,
+  browseErrorMessege: '',
+  hasError: false,
+  shouldRetry: false,
+  currentFailedPath: '',
+  currentUnprocessedPaths: [],
+  currentFailureMessage: '',
+
+  copyPaths: function (paths, destination) {
+    this.set('isUpdating', true);
+
+    this.get('fileOperationService').copyPaths(paths, destination).then(
+      (response) => {
+        this.set('isUpdating', false);
+        this.send('close');
+        this.sendAction('refreshAction');
+      }, (error) => {
+        this.set('isUpdating', false);
+        if (error.unprocessable === true) {
+          this.set('hasError', true);
+          this.set('currentFailedPath', error.failed);
+          this.set('currentFailureMessage', error.message);
+          this.set('shouldRetry', error.retry);
+          this.set('currentUnprocessedPaths', error.unprocessed);
+        } else {
+          this.set('isUpdating', false);
+          this.get('logger').danger("Failed to delete files and folders.", error);
+          this.send('close');
+        }
+      });
+  },
+  reset: function () {
+    this.set('browseError', false);
+    this.set('browseErrorMessege', '');
+    this.set('selectionName', '/');
+    this.set('hasError', false);
+    this.set('shouldRetry', false);
+    this.set('isUpdating', false);
+    this.set('currentFailedPath', '');
+    this.set('currentFailureMessage', '');
+    this.set('currentUnprocessedPaths', '');
+  },
+  actions: {
+
+    didOpenModal: function () {
+      this.reset();
+      console.log("Move modal opened");
+    },
+
+    didCloseModal: function () {
+      console.log("Move Modal did close.");
+    },
+
+    copy: function () {
+      var currentPathsToMove = this.get('selectedFiles').map((entry) => {
+        return entry.get('path')
+      });
+      var destinationPath = (this.get('selectionName') !== '') ? this.get('selectionName') : '/';
+      this.copyPaths(currentPathsToMove, destinationPath);
+    },
+
+    retryError: function () {
+      var newPaths = [this.get('currentFailedPath')];
+      if (Ember.isArray(this.get('currentUnprocessedPaths'))) {
+        newPaths.pushObjects(this.get('currentUnprocessedPaths'));
+      }
+      var destinationPath = (this.get('selectionName') !== '') ? this.get('selectionName') : '/';
+      this.copyPaths(newPaths, destinationPath);
+    },
+
+    skipAndRetry: function () {
+      var destinationPath = (this.get('selectionName') !== '') ? this.get('selectionName') : '/';
+      this.copyPaths(this.get('currentUnprocessedPaths'), destinationPath);
+    },
+
+    skipAll: function () {
+      this.send('close');
+      this.sendAction('refreshAction');
+    },
+
+    pathSelected: function (path) {
+      console.log(path);
+      this.set('selectionName', path);
+      this.set('browseError', false);
+
+    },
+
+    browseError: function (error) {
+      this.set('browseError', true);
+      this.set('browseErrorMessage', error.message);
+    }
+  }
+
+
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/b988562a/contrib/views/files/src/main/resources/ui/app/components/delete-modal.js
----------------------------------------------------------------------
diff --git a/contrib/views/files/src/main/resources/ui/app/components/delete-modal.js b/contrib/views/files/src/main/resources/ui/app/components/delete-modal.js
new file mode 100644
index 0000000..cb71ba7
--- /dev/null
+++ b/contrib/views/files/src/main/resources/ui/app/components/delete-modal.js
@@ -0,0 +1,130 @@
+/**
+ * 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.
+ */
+
+import Ember from 'ember';
+import OperationModal from '../mixins/operation-modal';
+
+export default Ember.Component.extend(OperationModal, {
+  fileSelectionService: Ember.inject.service('files-selection'),
+  fileOperationService: Ember.inject.service('file-operation'),
+  logger: Ember.inject.service('alert-messages'),
+  closeOnEscape: true,
+  deletePermanently: false,
+  deletePermanentlyAlways: false,
+  showDeletePermanentCheckbox: true,
+  selectedFiles: Ember.computed.alias('fileSelectionService.files'),
+  filesCount: Ember.computed.oneWay('fileSelectionService.filesCount'),
+  folderCount: Ember.computed.oneWay('fileSelectionService.folderCount'),
+  hasFiles: Ember.computed('filesCount', function() {
+    return this.get('filesCount') > 0;
+  }),
+  hasFolders: Ember.computed('folderCount', function() {
+    return this.get('folderCount') > 0;
+  }),
+  hasError: false,
+  shouldRetry: false,
+  currentFailedPath: '',
+  currentUnprocessedPaths: [],
+  currentFailureMessage: '',
+  currentServerFailureMessage: '',
+  isDeleting: false,
+
+  setTrashSettings: Ember.on('init', Ember.observer('currentPathIsTrash', function() {
+    if(this.get('currentPathIsTrash')) {
+      this.set('deletePermanentlyAlways', true);
+      this.set('showDeletePermanentCheckbox', false);
+    } else {
+      this.set('deletePermanentlyAlways', false);
+      this.set('showDeletePermanentCheckbox', true);
+    }
+
+  })),
+
+  disableCloseOnEscape: Ember.observer('isDeleting', function() {
+    if (this.get('isDeleting') === true) {
+      this.set('closeOnEscape', false);
+    } else {
+      this.set('closeOnEscape', true);
+    }
+  }),
+
+  deletePaths: function(paths) {
+    this.set('isDeleting', true);
+    let deletePermanently = this.get('deletePermanently');
+    if(this.get('deletePermanentlyAlways')) {
+      deletePermanently = true;
+    }
+    this.get('fileOperationService').deletePaths(paths, deletePermanently).then(
+      (response) => {
+        this.set('isDeleting', false);
+        this.send('close');
+        this.sendAction('refreshAction');
+      }, (error) => {
+        this.set('isDeleting', false);
+        if (error.unprocessable === true) {
+          this.set('hasError', true);
+          this.set('currentFailedPath', error.failed);
+          this.set('currentServerFailureMessage', error.message);
+          this.set('currentFailureMessage', `Failed to delete <strong>${error.failed}</strong>.`);
+          this.set('shouldRetry', error.retry);
+          this.set('currentUnprocessedPaths', error.unprocessed);
+        } else {
+          this.set('isDeleting', false);
+          this.get('logger').danger("Failed to delete files and folders.", error);
+          this.send('close');
+        }
+      });
+  },
+  reset: function() {
+    this.set('deletePermanently', false);
+    this.set('hasError', false);
+    this.set('shouldRetry', false);
+    this.set('isDeleting', false);
+    this.set('currentFailedPath', '');
+    this.set('currentFailureMessage', '');
+    this.set('currentUnprocessedPaths', '');
+  },
+  actions: {
+    didOpenModal: function() {
+      this.reset();
+      console.log("Delete modal opened");
+    },
+
+    didCloseModal: function() {
+      console.log("Delete Modal closed");
+    },
+    delete: function() {
+      var currentPathsToDelete = this.get('selectedFiles').map((entry) => { return entry.get('path');});
+      this.deletePaths(currentPathsToDelete);
+    },
+    retryError: function() {
+      var newPaths = [this.get('currentFailedPath')];
+      if (Ember.isArray(this.get('currentUnprocessedPaths'))) {
+        newPaths.pushObjects(this.get('currentUnprocessedPaths'));
+      }
+      this.deletePaths(newPaths);
+    },
+    skipAndRetry: function() {
+      this.deletePaths(this.get('currentUnprocessedPaths'));
+    },
+    skipAll: function() {
+      this.send('close');
+      this.sendAction('refreshAction');
+    }
+  }
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/b988562a/contrib/views/files/src/main/resources/ui/app/components/directory-viewer.js
----------------------------------------------------------------------
diff --git a/contrib/views/files/src/main/resources/ui/app/components/directory-viewer.js b/contrib/views/files/src/main/resources/ui/app/components/directory-viewer.js
new file mode 100644
index 0000000..69e725b
--- /dev/null
+++ b/contrib/views/files/src/main/resources/ui/app/components/directory-viewer.js
@@ -0,0 +1,165 @@
+/**
+ * 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.
+ */
+
+import Ember from 'ember';
+import FileOperationMixin from '../mixins/file-operation';
+
+export default Ember.Component.extend(FileOperationMixin, {
+  fileOperationService: Ember.inject.service('file-operation'),
+  classNames: ['directory-viewer'],
+  startPath: '/',
+  treeData: Ember.A(),
+  currentPath: Ember.computed.oneWay('startPath'),
+  currentQueryParam: Ember.computed('currentPath', function() {
+    return Ember.$.param({path: this.get('currentPath')});
+  }),
+
+  startFetch: Ember.on('didInitAttrs', function() {
+    this.fetchData();
+  }),
+
+  fetchData: function() {
+    this.get('fileOperationService').listPath(this.get('currentQueryParam')).then(
+      (response) => {
+        this.modifyTreeViewData(response);
+      }, (error) => {
+        this.sendAction('errorAction', error);
+      }
+    )
+  },
+
+  modifyTreeViewData: function(response) {
+
+    let paths = response.map((entry) => {
+      return {
+        path: entry.path,
+        pathSegment: this.getNameForPath(entry.path),
+        text: this.getNameForPath(entry.path),
+        nodes: Ember.A()
+      };
+    });
+
+    var currentPath = this.get('currentPath');
+    var newTreeData = Ember.copy(this.get('treeData'), true);
+    if(currentPath === '/') {
+      newTreeData = paths;
+    } else {
+      this.insertPathToTreeData(newTreeData, paths, currentPath.substring(1));
+    }
+
+    this.set('treeData', newTreeData);
+    this.send('refreshTreeView');
+  },
+
+  insertPathToTreeData(treeData, paths, pathSegment) {
+    let firstPathSegment;
+    if (pathSegment.indexOf('/') !== -1) {
+      firstPathSegment = pathSegment.substring(0, pathSegment.indexOf('/'));
+    } else {
+      firstPathSegment = pathSegment;
+    }
+
+    if(treeData.length === 0) {
+      treeData.pushObjects(paths);
+    } else {
+      treeData.forEach((entry) => {
+        entry.state = {};
+        if (entry.pathSegment === firstPathSegment) {
+          entry.state.expanded = true;
+          if(entry.nodes.length === 0) {
+            entry.nodes.pushObjects(paths);
+          } else {
+            this.insertPathToTreeData(entry.nodes, paths, pathSegment.substring(pathSegment.indexOf('/') + 1));
+          }
+        } else {
+          this.collapseAll(entry);
+        }
+      });
+    }
+  },
+
+  collapseAll: function(node) {
+    if (Ember.isNone(node.state)) {
+      node.state = {};
+    }
+    node.state.expanded = false;
+    node.nodes.forEach((entry) => {
+      this.collapseAll(entry);
+    });
+
+  },
+
+  getNameForPath: function(path) {
+    return path.substring(path.lastIndexOf("/") + 1);
+  },
+
+  collapseAllExceptPath: function(pathSegment) {
+    let collapseAll = function(nodes, pathSegment) {
+      var firstPathSegment;
+      if (pathSegment.indexOf('/') !== -1) {
+        firstPathSegment = pathSegment.substring(0, pathSegment.indexOf('/'));
+      } else {
+        firstPathSegment = pathSegment;
+      }
+
+      nodes.forEach((entry) => {
+        if (Ember.isNone(entry.state)) {
+          entry.state = {};
+        }
+        if(firstPathSegment !== entry.pathSegment) {
+          entry.state.expanded = false;
+        } else {
+          entry.state.expanded = true;
+          collapseAll(entry.nodes, pathSegment.substring(pathSegment.indexOf('/') + 1));
+        }
+      });
+    };
+    var newTreeData = this.get('treeData');
+    collapseAll(newTreeData, pathSegment);
+    this.set('treeData', newTreeData);
+    this.send('refreshTreeView');
+  },
+
+  actions: {
+    refreshTreeView() {
+      Ember.run.later(() => {
+        this.$().treeview({
+          data: this.get('treeData'),
+          expandIcon: "fa fa-folder",
+          collapseIcon: "fa fa-folder-open",
+          emptyIcon: "fa fa-folder-open-o",
+          showBorder: false,
+          onNodeSelected: (event, data) => {
+            this.set('currentPath', data.path);
+            this.sendAction('pathSelectAction', data.path);
+          },
+          onNodeExpanded: (event, data) => {
+            this.set('currentPath', data.path);
+            if (!Ember.isNone(data.nodes) && data.nodes.length === 0) {
+              var node = this.$().treeview('getNode', data.nodeId);
+              node.icon = "fa fa-refresh fa-spin";
+              this.fetchData();
+            } else {
+              this.collapseAllExceptPath(data.path.substring(1));
+            }
+          }
+        });
+      });
+    }
+  }
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/b988562a/contrib/views/files/src/main/resources/ui/app/components/file-row.js
----------------------------------------------------------------------
diff --git a/contrib/views/files/src/main/resources/ui/app/components/file-row.js b/contrib/views/files/src/main/resources/ui/app/components/file-row.js
new file mode 100644
index 0000000..a7fdbee
--- /dev/null
+++ b/contrib/views/files/src/main/resources/ui/app/components/file-row.js
@@ -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.
+ */
+
+import Ember from 'ember';
+
+export default Ember.Component.extend({
+  classNames: ['col-md-12', 'file-row'],
+  classNameBindings: ['isSelected:row-selected'],
+  isSelected: Ember.computed.alias('file.isSelected'),
+
+  click: function(event) {
+    if(event.shiftKey) {
+      this.sendAction("multiSelectAction", this.get('file'), true);
+    } else if (event.ctrlKey) {
+      this.sendAction("multiSelectAction", this.get('file'), false);
+    } else if (event.metaKey) {
+      this.sendAction("multiSelectAction", this.get('file'), false);
+    } else {
+      this.sendAction("singleSelectAction", this.get('file'));
+    }
+  }
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/b988562a/contrib/views/files/src/main/resources/ui/app/components/file-search.js
----------------------------------------------------------------------
diff --git a/contrib/views/files/src/main/resources/ui/app/components/file-search.js b/contrib/views/files/src/main/resources/ui/app/components/file-search.js
new file mode 100644
index 0000000..b65749c
--- /dev/null
+++ b/contrib/views/files/src/main/resources/ui/app/components/file-search.js
@@ -0,0 +1,42 @@
+/**
+ * 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.
+ */
+
+import Ember from 'ember';
+
+export default Ember.Component.extend({
+  classNames: ['input-group'],
+  classNameBindings: ['expanded::col-md-9', 'expanded::col-md-offset-3'],
+  expanded: false,
+
+  searchText: '',
+
+  throttleTyping: Ember.observer('searchText', function() {
+    Ember.run.debounce(this, this.searchFiles, 500);
+  }),
+
+  searchFiles: function() {
+    this.sendAction('searchAction', this.get('searchText'));
+  },
+
+  focusIn: function() {
+    this.set('expanded', true);
+  },
+  focusOut: function() {
+    this.set('expanded', false);
+  }
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/b988562a/contrib/views/files/src/main/resources/ui/app/components/files-breadcrumb.js
----------------------------------------------------------------------
diff --git a/contrib/views/files/src/main/resources/ui/app/components/files-breadcrumb.js b/contrib/views/files/src/main/resources/ui/app/components/files-breadcrumb.js
new file mode 100644
index 0000000..be3054c
--- /dev/null
+++ b/contrib/views/files/src/main/resources/ui/app/components/files-breadcrumb.js
@@ -0,0 +1,69 @@
+/**
+ * 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.
+ */
+
+import Ember from 'ember';
+
+export default Ember.Component.extend({
+  path: '',
+  collapseAt: 4,
+  tagName: 'ul',
+  classNames: ['breadcrumb'],
+  collapsingRequired: false,
+  collapsedCrumbs: [],
+  expandedCrumbs: [],
+
+  crumbs: Ember.on('init', Ember.observer('path', function() {
+    var path = this.get('path');
+    var currentPath = path.split('/').filter((entry) => { return !Ember.isBlank(entry) });
+    currentPath.unshift("/");
+    var that = this;
+    var shouldCollapse = function(scope, array, index) {
+      return (((array.length - 1) >= scope.get('collapseAt')) && (index < array.length - 2));
+    };
+    var getCrumb = function(index, allCrumbs) {
+      return {name: allCrumbs[index], path: "/" + allCrumbs.slice(1, index + 1).join('/'), last: false};
+    };
+
+    var collapsedCrumbs = currentPath.map(function(curr, i, array) {
+      if(shouldCollapse(that, array, i)) {
+        return getCrumb(i, array);
+      } else {
+        return {};
+      }
+    }).filterBy('name');
+
+    var crumbs = currentPath.map(function(curr, i, array) {
+      if(!shouldCollapse(that, array, i)) {
+        return getCrumb(i, array);
+      } else {
+        return {};
+      }
+    }).filterBy('name');
+
+    crumbs.set('lastObject.last', true);
+
+    if (collapsedCrumbs.length > 0) {
+      this.set('collapsingRequired', true);
+    } else {
+      this.set('collapsingRequired', false);
+    }
+    this.set('collapsedCrumbs', collapsedCrumbs.reverse());
+    this.set('expandedCrumbs', crumbs);
+  }))
+
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/b988562a/contrib/views/files/src/main/resources/ui/app/components/files-collection.js
----------------------------------------------------------------------
diff --git a/contrib/views/files/src/main/resources/ui/app/components/files-collection.js b/contrib/views/files/src/main/resources/ui/app/components/files-collection.js
new file mode 100644
index 0000000..43ef243
--- /dev/null
+++ b/contrib/views/files/src/main/resources/ui/app/components/files-collection.js
@@ -0,0 +1,98 @@
+/**
+ * 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.
+ */
+
+import Ember from 'ember';
+import { EKMixin, keyUp } from 'ember-keyboard';
+
+export default Ember.Component.extend(EKMixin, {
+  minHeight: 600,
+  currentWidth: 1000,
+  currentHeight: 600,
+  columnsConfig: [],
+  sortOptions: [-1, 0, 1],
+  parentPath: '',
+  isEmptyParentPath: Ember.computed('parentPath', function() {
+    return Ember.isBlank(this.get('parentPath'));
+  }),
+
+  resizeView: Ember.on('init', function() {
+    $(window).resize(this.windowResized(this));
+  }),
+
+  destroyResizeView: Ember.on('willDestroyElement', function() {
+    $(window).off("resize");
+  }),
+
+  activateKeyboard: Ember.on('init', function() {
+    this.set('keyboardActivated', true);
+  }),
+
+  resetAllSelection: Ember.on(keyUp('Escape'), function() {
+    this.sendAction('resetSelection');
+  }),
+
+  selectAll: Ember.on(keyUp('shift+s'), function() {
+    this.sendAction('selectAllAction', false);
+  }),
+
+  containerStyle: Ember.computed('currentHeight', function() {
+    var height = this.get('currentHeight');
+    var style = 'position: relative; height: ' + height + 'px';
+    return style.htmlSafe();
+  }),
+
+  windowResized: function(scope) {
+    return function() {
+      Ember.run.later(function() {
+        var currentWidth = $("#" + scope.get('containerId')).width();
+        var windowHeight = $(window).height();
+        var relativeHeight = windowHeight - 220;
+        if(relativeHeight < scope.get('minHeight')) {
+          relativeHeight = scope.get('minHeight');
+        }
+        scope.set('currentWidth', currentWidth);
+        scope.set('currentHeight', relativeHeight);
+      });
+    };
+  },
+
+  didInsertElement: function() {
+    var func = this.windowResized(this);
+    func();
+  },
+
+  actions: {
+    rotateSort: function(column) {
+      if(!column['sortable'] || this.get('sortEnabled') !== true) {
+        return false;
+      }
+      var sortOptions = this.get('sortOptions');
+      // Resetting the current sort order
+      this.get('columnsConfig').forEach(function(entry) {
+        if(entry['key'] !== column['key']) {
+          Ember.set(entry, 'sortOrder', sortOptions[1]);
+        }
+      });
+      var currentSortOrder = column['sortOrder'];
+      var currentSortOrderIndex = sortOptions.indexOf(currentSortOrder);
+      var nextSortOrderIndex = (currentSortOrderIndex + 1) % sortOptions.length;
+      Ember.set(column, 'sortOrder', sortOptions[nextSortOrderIndex]);
+      this.sendAction('sortAction', column);
+    }
+  }
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/b988562a/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
deleted file mode 100644
index d98a429..0000000
--- a/contrib/views/files/src/main/resources/ui/app/components/mkdirInput.js
+++ /dev/null
@@ -1,50 +0,0 @@
-/**
- * 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});
-    }
-  },
-  focusOnInput: function () {
-    Em.run.next(this,function() {
-      this.$('.mkdir-input').focus();
-    });
-  }.observes('isMkdir'),
-});

http://git-wip-us.apache.org/repos/asf/ambari/blob/b988562a/contrib/views/files/src/main/resources/ui/app/components/move-modal.js
----------------------------------------------------------------------
diff --git a/contrib/views/files/src/main/resources/ui/app/components/move-modal.js b/contrib/views/files/src/main/resources/ui/app/components/move-modal.js
new file mode 100644
index 0000000..41de5f3
--- /dev/null
+++ b/contrib/views/files/src/main/resources/ui/app/components/move-modal.js
@@ -0,0 +1,126 @@
+/**
+ * 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.
+ */
+
+import Ember from 'ember';
+import OperationModal from '../mixins/operation-modal';
+
+export default Ember.Component.extend(OperationModal, {
+  closeOnEscape: true,
+  fileSelectionService: Ember.inject.service('files-selection'),
+  fileOperationService: Ember.inject.service('file-operation'),
+  selectedFiles: Ember.computed.alias('fileSelectionService.files'),
+  selected: Ember.computed('selectedFiles', function () {
+    return this.get('selectedFiles').objectAt(0);
+  }),
+  selectionName: '/',
+  isUpdating: false,
+  browseError: false,
+  browseErrorMessege: '',
+  hasError: false,
+  shouldRetry: false,
+  currentFailedPath: '',
+  currentUnprocessedPaths: [],
+  currentFailureMessage: '',
+
+  movePaths: function (paths, destination) {
+    this.set('isUpdating', true);
+
+    this.get('fileOperationService').movePaths(paths, destination).then(
+      (response) => {
+        this.set('isUpdating', false);
+        this.send('close');
+        this.sendAction('refreshAction');
+      }, (error) => {
+        this.set('isUpdating', false);
+        if (error.unprocessable === true) {
+          this.set('hasError', true);
+          this.set('currentFailedPath', error.failed);
+          this.set('currentFailureMessage', error.message);
+          this.set('shouldRetry', error.retry);
+          this.set('currentUnprocessedPaths', error.unprocessed);
+        } else {
+          this.set('isUpdating', false);
+          this.get('logger').danger("Failed to delete files and folders.", error);
+          this.send('close');
+        }
+      });
+  },
+  reset: function () {
+    this.set('browseError', false);
+    this.set('browseErrorMessege', '');
+    this.set('selectionName', '/');
+    this.set('hasError', false);
+    this.set('shouldRetry', false);
+    this.set('isUpdating', false);
+    this.set('currentFailedPath', '');
+    this.set('currentFailureMessage', '');
+    this.set('currentUnprocessedPaths', '');
+  },
+  actions: {
+
+    didOpenModal: function () {
+      this.reset();
+      console.log("Move modal opened");
+    },
+
+    didCloseModal: function () {
+      console.log("Move Modal did close.");
+    },
+
+    move: function () {
+      var currentPathsToMove = this.get('selectedFiles').map((entry) => {
+        return entry.get('path')
+      });
+      var destinationPath = (this.get('selectionName') !== '') ? this.get('selectionName') : '/';
+      this.movePaths(currentPathsToMove, destinationPath);
+    },
+
+    retryError: function () {
+      var newPaths = [this.get('currentFailedPath')];
+      if (Ember.isArray(this.get('currentUnprocessedPaths'))) {
+        newPaths.pushObjects(this.get('currentUnprocessedPaths'));
+      }
+      var destinationPath = (this.get('selectionName') !== '') ? this.get('selectionName') : '/';
+      this.movePaths(newPaths, destinationPath);
+    },
+
+    skipAndRetry: function () {
+      var destinationPath = (this.get('selectionName') !== '') ? this.get('selectionName') : '/';
+      this.movePaths(this.get('currentUnprocessedPaths'), destinationPath);
+    },
+
+    skipAll: function () {
+      this.send('close');
+      this.sendAction('refreshAction');
+    },
+
+    pathSelected: function (path) {
+      console.log(path);
+      this.set('selectionName', path);
+      this.set('browseError', false);
+
+    },
+
+    browseError: function (error) {
+      this.set('browseError', true);
+      this.set('browseErrorMessage', error.message);
+    }
+  }
+
+
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/b988562a/contrib/views/files/src/main/resources/ui/app/components/new-directory.js
----------------------------------------------------------------------
diff --git a/contrib/views/files/src/main/resources/ui/app/components/new-directory.js b/contrib/views/files/src/main/resources/ui/app/components/new-directory.js
new file mode 100644
index 0000000..c30cc8c
--- /dev/null
+++ b/contrib/views/files/src/main/resources/ui/app/components/new-directory.js
@@ -0,0 +1,75 @@
+/**
+ * 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.
+ */
+
+import Ember from 'ember';
+import OperationModal from '../mixins/operation-modal';
+
+export default Ember.Component.extend(OperationModal, {
+  modalEventBus: Ember.inject.service('modal-event-bus'),
+  fileOperationService: Ember.inject.service('file-operation'),
+  closeOnEscape: true,
+  tagName: 'span',
+  name: 'ctx-new-directory',
+  hasError: false,
+  errorMessage: '',
+  folderName: '',
+  didInitAttrs: function() {
+    this.get('modalEventBus').registerModal("ctx-new-directory");
+  },
+  willDestroyElement() {
+    this.get('modalEventBus').resetModal("ctx-new-directory");
+  },
+  resetError: Ember.observer('folderName', function() {
+    this.set('hasError', false);
+    this.set('errorMessage', '');
+  }),
+  setError: function(message) {
+    this.set('hasError', true);
+    this.set('errorMessage', message);
+  },
+  actions: {
+    didOpenModal: function() {
+      this.set('folderName');
+      Ember.run.later(() => {
+        this.$('input').focus();
+      }, 500);
+    },
+    create: function() {
+      if(Ember.isBlank(this.get('folderName'))) {
+        this.setError('Cannot be empty');
+        return false;
+      }
+
+      if(this.get('fileOperationService').isExistsInCurrentPath(this.get('folderName'))) {
+        this.setError('Name already exists');
+        return false;
+      }
+
+      this.get('fileOperationService').createNewFolder(this.get('path'), this.get('folderName')).then(
+        (response) => {
+          this.send('close');
+          this.sendAction('refreshAction');
+        }, (error) => {
+          this.send('close');
+        });
+    },
+    openModal : function() {
+      this.get('modalEventBus').showModal('ctx-new-directory');
+    }
+  }
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/b988562a/contrib/views/files/src/main/resources/ui/app/components/open-preview-modal.js
----------------------------------------------------------------------
diff --git a/contrib/views/files/src/main/resources/ui/app/components/open-preview-modal.js b/contrib/views/files/src/main/resources/ui/app/components/open-preview-modal.js
new file mode 100644
index 0000000..bc60227
--- /dev/null
+++ b/contrib/views/files/src/main/resources/ui/app/components/open-preview-modal.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.
+ */
+
+import Ember from 'ember';
+import OperationModal from '../mixins/operation-modal';
+
+export default Ember.Component.extend(OperationModal, {
+  closeOnEscape: true,
+  filePreviewService: Ember.inject.service('file-preview'),
+  selectedFilePath: '',
+  modalGuardChanged: Ember.observer('modalGuard', function () {
+    if (this.get('modalGuard')) {
+      console.log("Modal Guard set");
+    } else {
+      console.log("Modal Guard not set");
+    }
+  }),
+
+  actions: {
+    // Actions to preview modal HTML.
+    didOpenModal: function () {
+      this.set('selectedFilePath', this.get('filePreviewService.selectedFilePath'));
+      this.get('filePreviewService').getNextContent();
+      var _self = this;
+      this.$('.preview-content').on('scroll', function () {
+        if (Ember.$(this).scrollTop() + Ember.$(this).innerHeight() >= this.scrollHeight) {
+          _self.get('filePreviewService').getNextContent();
+        }
+      });
+    },
+    didCloseModal: function () {
+      this.$('.preview-content').off('scroll');
+      this.get('filePreviewService').reset();
+    },
+    download: function () {
+      this.get('filePreviewService').download();
+    }
+  }
+
+});
+

http://git-wip-us.apache.org/repos/asf/ambari/blob/b988562a/contrib/views/files/src/main/resources/ui/app/components/permission-modal.js
----------------------------------------------------------------------
diff --git a/contrib/views/files/src/main/resources/ui/app/components/permission-modal.js b/contrib/views/files/src/main/resources/ui/app/components/permission-modal.js
new file mode 100644
index 0000000..706e839
--- /dev/null
+++ b/contrib/views/files/src/main/resources/ui/app/components/permission-modal.js
@@ -0,0 +1,116 @@
+/**
+ * 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.
+ */
+
+import Ember from 'ember';
+import OperationModal from '../mixins/operation-modal';
+
+export default Ember.Component.extend(OperationModal, {
+  fileSelectionService: Ember.inject.service('files-selection'),
+  fileOperationService: Ember.inject.service('file-operation'),
+  closeOnEscape: true,
+  isUpdating: false,
+  selected: Ember.computed('fileSelectionService.files', function() {
+    return this.get('fileSelectionService.files').objectAt(0);
+  }),
+  permission: Ember.computed('selected.permission', function() {
+    return this.get('selected.permission');
+  }),
+  setPermissionGuards: function() {
+    var permission = this.get('permission');
+    this.set('usrR', this.isSet(permission, 'user', "read"));
+    this.set('usrW', this.isSet(permission, 'user', "write"));
+    this.set('usrE', this.isSet(permission, 'user', "execute"));
+
+    this.set('grpR', this.isSet(permission, 'group', "read"));
+    this.set('grpW', this.isSet(permission, 'group', "write"));
+    this.set('grpE', this.isSet(permission, 'group', "execute"));
+
+    this.set('othR', this.isSet(permission, 'other', "read"));
+    this.set('othW', this.isSet(permission, 'other', "write"));
+    this.set('othE', this.isSet(permission, 'other', "execute"));
+  },
+
+  isSet: function(permission, userType, permissionType) {
+    var checkValueAtLocation = function(index, value) {
+      return permission[index] === value;
+    };
+
+    var checkValueForPermissionType = function(startIndex, permissionType) {
+      switch(permissionType) {
+        case 'read':
+          return checkValueAtLocation(startIndex, 'r');
+        case 'write':
+          return checkValueAtLocation(startIndex + 1, 'w');
+        case 'execute':
+          return checkValueAtLocation(startIndex + 2, 'x');
+      }
+    };
+    switch(userType) {
+      case "user":
+        return checkValueForPermissionType(1, permissionType);
+      case "group":
+        return checkValueForPermissionType(4, permissionType);
+      case "other":
+        return checkValueForPermissionType(7, permissionType);
+    }
+  },
+
+  getPermissionFromGuards: function() {
+    var oldPermission = this.get('permission');
+    var replaceAt = function(index, value) {
+      return oldPermission.substring(0, index) + value + oldPermission.substring(index + value.length);
+    };
+    oldPermission = this.get('usrR') ? replaceAt(1, 'r') : replaceAt(1, '-');
+    oldPermission = this.get('usrW') ? replaceAt(2, 'w') : replaceAt(2, '-');
+    oldPermission = this.get('usrE') ? replaceAt(3, 'x') : replaceAt(3, '-');
+    oldPermission = this.get('grpR') ? replaceAt(4, 'r') : replaceAt(4, '-');
+    oldPermission = this.get('grpW') ? replaceAt(5, 'w') : replaceAt(5, '-');
+    oldPermission = this.get('grpE') ? replaceAt(6, 'x') : replaceAt(6, '-');
+    oldPermission = this.get('othR') ? replaceAt(7, 'r') : replaceAt(7, '-');
+    oldPermission = this.get('othW') ? replaceAt(8, 'w') : replaceAt(8, '-');
+    oldPermission = this.get('othE') ? replaceAt(9, 'x') : replaceAt(9, '-');
+    return oldPermission;
+  },
+  actions: {
+    didOpenModal: function() {
+      this.setPermissionGuards();
+    },
+
+    chmod: function() {
+      var newPermission = this.getPermissionFromGuards();
+      if(newPermission === this.get('permission')) {
+        return false;
+      }
+      this.set('isUpdating', true);
+      this.get('fileOperationService').chmod(this.get('selected').get('path'), newPermission).then((response) => {
+        this.get('selected').set('permission', response.permission);
+        this.set('isUpdating', false);
+        this.send('close');
+      }, (error) => {
+        this.set('isUpdating', false);
+        this.send('close');
+      });
+    },
+
+    togglePermission: function(propertyName) {
+      Ember.run.later(() => {
+        this.set(propertyName, !this.get(propertyName));
+      });
+    }
+  }
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/b988562a/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
deleted file mode 100644
index 2bd6fba..0000000
--- a/contrib/views/files/src/main/resources/ui/app/components/popoverDelete.js
+++ /dev/null
@@ -1,68 +0,0 @@
-/**
- * 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');
-
-Em.BsPopoverComponent.reopen({
-  willClearRender:function () {
-    var triggers = this.triggers.split(' ');
-
-      for (var i = triggers.length; i--;) {
-          var trigger = triggers[i];
-
-          if (trigger == 'click') {
-              this.$element.off('click');
-          } else if (trigger != 'manual') {
-              var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focus';
-              var eventOut = trigger == 'hover' ? 'mouseleave' : 'blur';
-
-              this.$element.off(eventIn);
-              this.$element.off(eventOut);
-          }
-      }
-  }
-});
-
-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/b988562a/contrib/views/files/src/main/resources/ui/app/components/rename-modal.js
----------------------------------------------------------------------
diff --git a/contrib/views/files/src/main/resources/ui/app/components/rename-modal.js b/contrib/views/files/src/main/resources/ui/app/components/rename-modal.js
new file mode 100644
index 0000000..09ae061
--- /dev/null
+++ b/contrib/views/files/src/main/resources/ui/app/components/rename-modal.js
@@ -0,0 +1,77 @@
+/**
+ * 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.
+ */
+
+import Ember from 'ember';
+import OperationModal from '../mixins/operation-modal';
+
+export default Ember.Component.extend(OperationModal, {
+  closeOnEscape: true,
+  hasError: false,
+  errorMessage: '',
+  isUpdating: false,
+  renameService: Ember.inject.service('file-rename'),
+  fileSelectionService: Ember.inject.service('files-selection'),
+  selectedFiles: Ember.computed.alias('fileSelectionService.files'),
+  selected: Ember.computed('selectedFiles', function() {
+    return this.get('selectedFiles').objectAt(0);
+  }),
+  selectionName: Ember.computed.oneWay('selected.name'),
+  hasErrorReset: Ember.observer('selectionName', 'selected.name', function() {
+    if (this.get('hasError') && (this.get('selectionName') !== this.get('selected.name'))) {
+      this.set('hasError', false);
+    }
+  }),
+
+  actions: {
+    didOpenModal: function() {
+      this.set('selectionName', this.get('selected.name'));
+      // This was required as the DOM may not be visible due to animation in bootstrap modal
+      Ember.run.later(() => {
+        this.$('input').focus();
+      }, 500);
+
+    },
+
+    rename: function() {
+      if(Ember.isBlank(this.get('selectionName'))) {
+        return false;
+      }
+
+      if(this.get('selected.name') === this.get('selectionName')) {
+        this.set('hasError', true);
+        this.set('errorMessage', 'Name should be different');
+        return false;
+      }
+      this.set('isUpdating', true);
+      this.get('renameService').rename(this.get('selected.path'), this.get('selectionName'))
+      .then((response) => {
+        this.set('isUpdating', false);
+        this.send('close');
+        this.sendAction('refreshAction');
+      }, (error) => {
+        this.set('isUpdating', false);
+        if(error.retry) {
+          this.set('hasError', true);
+          this.set('errorMessage', error.message);
+        } else {
+          this.send('close');
+        }
+      });
+    }
+  }
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/b988562a/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
deleted file mode 100644
index 965cebd..0000000
--- a/contrib/views/files/src/main/resources/ui/app/components/renameInput.js
+++ /dev/null
@@ -1,92 +0,0 @@
-/**
- * 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.RenameInputComponent = Ember.Component.extend({
-  tagName:'span',
-  layoutName:'components/renameInput',
-  actions:{
-    rename:function (opt) {
-      var tmpName;
-
-      switch (opt) {
-        case 'edit': this.set('isRenaming',true); break;
-        case 'cancel': this.set('isRenaming',false); break;
-        case 'confirm':
-          tmpName = this.get('tmpName');
-          if (tmpName.length ===0) {
-            break;
-          }
-          this.sendAction('confirm',this.get('filePath'),tmpName);
-          this.set('isRenaming',false);
-          break;
-
-        default: this.toggleProperty('isRenaming');
-      }
-    }
-  },
-
-  /**
-   * passed params
-   */
-  file:null,
-  actionName:null,
-  isRenaming:false,
-
-  fileName:function () {
-    var file = this.get('file');
-    return (file instanceof DS.Model)?file.get('name'):file.substr(file.lastIndexOf('/')+1);
-  }.property('file'),
-
-  filePath:function () {
-    var file = this.get('file');
-    return (file instanceof DS.Model)?file.get('path'):file;
-  }.property('file'),
-
-  setTmpName:function () {
-    if (this.get('isRenaming')) {
-      this.set('tmpName',this.get('fileName'));
-    } else {
-      this.set('tmpName','');
-    }
-  }.observes('isRenaming'),
-
-  onFileChange:function () {
-    this.set('isRenaming',false);
-  }.observes('file'),
-
-  renameInputView: Em.TextField.extend({
-    controller:null,
-    didInsertElement:function () {
-      var element = $(this.get('element'));
-      element.focus().val(this.value);
-    },
-    keyUp: function(e) {
-      var target = this.get('targetObject');
-      if (e.keyCode==13) {
-        return target.send('rename', 'confirm');
-      }
-
-      if (e.keyCode==27) {
-        return target.send('rename', 'cancel');
-      }
-    }
-  })
-});

http://git-wip-us.apache.org/repos/asf/ambari/blob/b988562a/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
deleted file mode 100644
index c680c41..0000000
--- a/contrib/views/files/src/main/resources/ui/app/components/sortArrow.js
+++ /dev/null
@@ -1,34 +0,0 @@
-/**
- * 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 asc:fa-chevron-down:fa-chevron-up 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 () {
-    var isSp = this.get('sPs.firstObject') == this.get('sP');
-    this.setProperties({'asc':(isSp)?this.get('sA'):true,'cur':isSp});
-  }.observes('sPs','sA').on('didInsertElement')
-});

http://git-wip-us.apache.org/repos/asf/ambari/blob/b988562a/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
deleted file mode 100644
index 10f1b52..0000000
--- a/contrib/views/files/src/main/resources/ui/app/components/toggleContext.js
+++ /dev/null
@@ -1,79 +0,0 @@
-/**
- * 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');
-
-function _shake (element) {
-  var l = 5;
-  for ( var i = 0; i < 4; i++ ) {
-    element.animate( (l>0) ? {'margin-left':(l=-l)+'px','padding-left':0}:{'padding-left':+(l=-l)+'px','margin-left':0}, 50, function (el) {
-      element.css({'padding-left':0,'margin-left':0});
-    });
-  }
-}
-
-App.ToggleContextComponent = Em.Component.extend({
-  didInsertElement:function () {
-    var fileRow = this.$().parents('tr'),
-        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 () {
-    var fileRow = this.$().parents('tr');
-    fileRow.off('click');
-    fileRow.data('context').closemenu();
-    fileRow.data('context').destroy();
-  }
-});
-
-App.FileShakerComponent = Em.Component.extend({
-  action:'',
-  isValid:false,
-  click:function () {
-    if (this.get('isValid')) {
-      this.sendAction('action');
-    } else {
-      _shake(this.$());
-    }
-  }
-});

http://git-wip-us.apache.org/repos/asf/ambari/blob/b988562a/contrib/views/files/src/main/resources/ui/app/components/upload-file.js
----------------------------------------------------------------------
diff --git a/contrib/views/files/src/main/resources/ui/app/components/upload-file.js b/contrib/views/files/src/main/resources/ui/app/components/upload-file.js
new file mode 100644
index 0000000..9da6854
--- /dev/null
+++ b/contrib/views/files/src/main/resources/ui/app/components/upload-file.js
@@ -0,0 +1,92 @@
+/**
+ * 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.
+ */
+
+import Ember from 'ember';
+import OperationModal from '../mixins/operation-modal';
+import FileUploader from '../utils/file-uploader';
+
+export default Ember.Component.extend(OperationModal, {
+  modalEventBus: Ember.inject.service('modal-event-bus'),
+  fileOperationService: Ember.inject.service('file-operation'),
+  logger: Ember.inject.service('alert-messages'),
+  tagName: "span",
+  closeOnEscape: true,
+  name: 'ctx-uploader',
+  path: '',
+  isUploading: false,
+  uploadFileName: '',
+  uploadPercent: '0%',
+  uploadPercentStyle: Ember.computed('uploadPercent', function() {
+    var style = 'width: ' + this.get('uploadPercent') + ';';
+    return style.htmlSafe();
+  }),
+  didInitAttrs: function() {
+    this.get('modalEventBus').registerModal("ctx-uploader");
+  },
+  willDestroyElement() {
+    this.get('modalEventBus').resetModal("ctx-uploader");
+  },
+  setUploadPercent: function(percent) {
+    var intValue = Math.round(percent);
+    this.set('uploadPercent', `${intValue}%`);
+  },
+
+  setUploading: function(fileName) {
+    this.set('uploadFileName', fileName);
+    this.set('isUploading', true);
+    this.set('closeOnEscape', false);
+  },
+
+  actions: {
+    openModal : function() {
+      this.get('modalEventBus').showModal('ctx-uploader');
+    },
+    didOpenModal: function() {
+      this.set('isUploading', false);
+      this.set('uploadFileName', '');
+      this.set('closeOnEscape', true);
+    },
+
+    fileLoaded: function(file) {
+      var url = this.get('fileOperationService').getUploadUrl();
+      var uploader = FileUploader.create({
+        url: url
+      });
+      if(!Ember.isEmpty(file)) {
+        uploader.upload(file, {path: this.get('path')});
+        this.setUploading(file.name);
+        uploader.on('progress', (e) => {
+          this.setUploadPercent(e.percent);
+        });
+        uploader.on('didUpload', (e) => {
+          this.send('close');
+          this.sendAction('refreshAction');
+        });
+        uploader.on('didError', (jqXHR, textStatus, errorThrown) => {
+          var error = Ember.$.parseJSON(jqXHR.responseText);
+          this.get('logger').danger(`Failed to upload ${file.name} to ${this.get('path')}`, error);
+          this.send('close');
+          return false;
+        });
+      }
+
+    }
+
+  }
+});
+

http://git-wip-us.apache.org/repos/asf/ambari/blob/b988562a/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
deleted file mode 100644
index f6e61c4..0000000
--- a/contrib/views/files/src/main/resources/ui/app/components/uploader.js
+++ /dev/null
@@ -1,111 +0,0 @@
-/**
- * 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.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');
-        if (!files) {
-          this.set('parentView.files',null);
-          this.set('parentView.controlInput.value','');
-          this.set('value','');
-          return;
-        }
-        var numFiles = files ? files.length : 1;
-        var label = this.get('value').replace(/\\/g, '/').replace(/.*\//, '');
-        var log = numFiles > 1 ? numFiles + ' files selected' : label;
-
-        this.set('parentView.controlInput.value',log);
-        this.set('parentView.files',files);
-
-      }.observes('files')
-    });
-  },
-  actions:{
-    upload:function () {
-      this.uploadFile();
-    },
-    clear:function () {
-      this.set('fileInput.files',null);
-    }
-  },
-  uploader: null,
-  layoutName:'components/uploader',
-  path:'',
-  info:'',
-  files:null,
-  isFiles:function () {
-    return !this.get('files.length');
-  }.property('files'),
-  uploadFile:function () {
-    var path = this.get('path');
-    var uploader = this.get('uploader');
-    var uploadBtn = Ladda.create(this.uploadButton.get('element'));
-    var reset = function () {
-      uploadBtn.stop();
-      this.send('clear');
-    };
-    if (!uploader.get('isUploading')) {
-      if (!Ember.isEmpty(this.get('files'))) {
-        var file = this.get('files')[0];
-        uploadBtn.start();
-        uploader.on('progress',function (e) {
-          uploadBtn.setProgress(e.percent/100);
-        });
-        uploader.upload(file,{path:path}).finally(Em.run.bind(this,reset));
-      }
-    }
-  },
-  uploadButton: Em.View.createWithMixins(Ember.TargetActionSupport, {
-    tagName:'button',
-    target: Ember.computed.alias('controller'),
-    classNames:['btn','ladda-button'],
-    classNameBindings:['isFiles:hide','target.isError:btn-danger:btn-success'],
-    attributeBindings: ["data-style","data-size"],
-    action:'upload',
-    click: function() {
-      this.triggerAction();
-    }
-  }),
-  fileInput : Ember.TextField.create({
-    type: 'file',
-    attributeBindings: ['multiple'],
-    multiple: false,
-    files:null,
-    change: function(e) {
-      var input = e.target;
-      if (!Ember.isEmpty(input.files)) {
-        this.set('files', input.files);
-      }
-    }
-  }),
-  controlInput:Ember.TextField.create({
-    readonly:true,
-    classNames:['form-control']
-  })
-});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/b988562a/contrib/views/files/src/main/resources/ui/app/config/files-columns.js
----------------------------------------------------------------------
diff --git a/contrib/views/files/src/main/resources/ui/app/config/files-columns.js b/contrib/views/files/src/main/resources/ui/app/config/files-columns.js
new file mode 100644
index 0000000..4dce394
--- /dev/null
+++ b/contrib/views/files/src/main/resources/ui/app/config/files-columns.js
@@ -0,0 +1,73 @@
+/**
+ * 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.
+ */
+
+/*
+  Static configuration for the columns to be shown.
+*/
+var columnsConfig = [
+  {
+    title: 'Name',
+    key: 'name',
+    isVisible: true,
+    sortable: true,
+    sortOrder: 0,
+    columnClass: 'col-md-4 col-xs-4'
+  },
+  {
+    title: 'Size',
+    key: 'size',
+    isVisible: true,
+    sortable: true,
+    sortOrder: 0,
+    columnClass: 'col-md-1 col-xs-1'
+  },
+  {
+    title: 'Last Modified',
+    key: 'date',
+    isVisible: true,
+    sortable: true,
+    sortOrder: 0,
+    columnClass: 'col-md-2 col-xs-2'
+  },
+  {
+    title: 'Owner',
+    key: 'owner',
+    isVisible: true,
+    sortable: true,
+    sortOrder: 0,
+    columnClass: 'col-md-2 col-xs-2'
+  },
+  {
+    title: 'Group',
+    key: 'group',
+    isVisible: true,
+    sortable: true,
+    sortOrder: 0,
+    columnClass: 'col-md-1 col-xs-1'
+  },
+  {
+    title: 'Permission',
+    key: 'permission',
+    isVisible: true,
+    sortable: false,
+    sortOrder: 0,
+    columnClass: 'col-md-2 col-xs-2'
+  }
+];
+
+export default columnsConfig;

http://git-wip-us.apache.org/repos/asf/ambari/blob/b988562a/contrib/views/files/src/main/resources/ui/app/controllers/.gitkeep
----------------------------------------------------------------------
diff --git a/contrib/views/files/src/main/resources/ui/app/controllers/.gitkeep b/contrib/views/files/src/main/resources/ui/app/controllers/.gitkeep
new file mode 100644
index 0000000..e69de29

http://git-wip-us.apache.org/repos/asf/ambari/blob/b988562a/contrib/views/files/src/main/resources/ui/app/controllers/application.js
----------------------------------------------------------------------
diff --git a/contrib/views/files/src/main/resources/ui/app/controllers/application.js b/contrib/views/files/src/main/resources/ui/app/controllers/application.js
new file mode 100644
index 0000000..29ab49c
--- /dev/null
+++ b/contrib/views/files/src/main/resources/ui/app/controllers/application.js
@@ -0,0 +1,24 @@
+/**
+ * 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.
+ */
+
+import Ember from 'ember';
+
+export default Ember.Controller.extend({
+  firstLoad: true,
+  isLoading: false
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/b988562a/contrib/views/files/src/main/resources/ui/app/controllers/chmodModal.js
----------------------------------------------------------------------
diff --git a/contrib/views/files/src/main/resources/ui/app/controllers/chmodModal.js b/contrib/views/files/src/main/resources/ui/app/controllers/chmodModal.js
deleted file mode 100644
index a7170f7..0000000
--- a/contrib/views/files/src/main/resources/ui/app/controllers/chmodModal.js
+++ /dev/null
@@ -1,49 +0,0 @@
-/**
- * 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.ChmodModalController = Em.ObjectController.extend({
-  needs:['files'],
-  classNames:'chmod-row',
-  file:Em.computed.alias('content'),
-  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');
-    return perm.substr(0, index) + p + perm.substr(index + p.length);
-  }
-});

http://git-wip-us.apache.org/repos/asf/ambari/blob/b988562a/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
deleted file mode 100644
index efcc4de..0000000
--- a/contrib/views/files/src/main/resources/ui/app/controllers/error.js
+++ /dev/null
@@ -1,49 +0,0 @@
-/**
- * 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.
- */
-
-App.ErrorController = Ember.ObjectController.extend({
-  actions: {
-    toggleStackTrace:function () {
-      var value = this.get('isExpanded');
-      this.set('isExpanded', !value);
-    }
-  },
-
-  isExpanded: false,
-
-  publicMessage:function () {
-    var content = this.get('content');
-    var text = content.statusText;
-    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'),
-  stackTrace:function () {
-    var content = this.get('content');
-    var trace = null;
-    if (content && content.responseText) {
-      var json = JSON.parse(content.responseText);
-      trace = json.trace;
-    }
-    return trace;
-  }.property('content')
-});

http://git-wip-us.apache.org/repos/asf/ambari/blob/b988562a/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
deleted file mode 100644
index 88fa5fb..0000000
--- a/contrib/views/files/src/main/resources/ui/app/controllers/file.js
+++ /dev/null
@@ -1,117 +0,0 @@
-/**
- * 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.FileController = Ember.ObjectController.extend({
-  needs:['files'],
-  actions:{
-    confirmPreview:function (file) {
-      this.downloadFile(file, "browse");
-    },
-    download:function (option) {
-      this.downloadFile(this.get('content'), option);
-    },
-    preview:function (option) {
-      this.send('showPreviewModal',this.get('content'));
-    },
-    showChmod:function () {
-      this.send('showChmodModal',this.get('content'));
-    },
-    rename:function (opt,name) {
-      var file = this.get('content'),
-          path = file.get('path'),
-          newPath;
-
-      if (name === file.get('name') || Em.isEmpty(name)) {
-        return this.set('isRenaming',!Em.isEmpty(name));
-      }
-
-      newPath = path.substring(0,path.lastIndexOf('/')+1)+name;
-
-      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);
-    },
-    open:function (file) {
-      if (this.get('content.isDirectory')) {
-        return this.transitionToRoute('files',{queryParams: {path: this.get('content.id')}});
-      } else{
-        //return this.send('download');
-        return this.send('preview');
-      }
-    },
-    deleteFile:function (deleteForever) {
-      this.store
-        .remove(this.get('content'),!deleteForever)
-        .then(null,Em.run.bind(this,this.deleteErrorCallback,this.get('content')));
-    }
-  },
-  selected:false,
-  isRenaming: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');
-    return movingFile === thisFile;
-  }.property('parentController.movingFile'),
-
-  setSelected:function (controller,observer) {
-    this.set('selected',this.get(observer));
-  }.observes('content.selected'),
-
-  renameSuccessCallback:function (record,error) {
-    record.rollback();
-    this.sendAlert(error);
-  },
-
-  dirInfo: Em.computed.alias('controllers.files.content.meta'),
-
-  deleteErrorCallback:function (record,error) {
-    this.get('parentController.model').pushRecord(record);
-    this.send('showAlert',error);
-  },
-
-  sendAlert:function (error) {
-    this.send('showAlert',error);
-  },
-  downloadFile: function(files, option) {
-    var _this = this;
-    this.store.linkFor([files], option, false, true).then(function(link) {
-      var that = _this;
-      Ember.$.get(link).done(function(data) {
-        if(data.allowed) {
-          that.store.linkFor([files],option).then(function (link) {
-            window.location.href = link;
-          },Em.run.bind(that,that.sendAlert));
-        }
-      }).fail(function(jqXHR, textStatus, errorThrown) {
-        that.send('showAlert', jqXHR);
-      });
-    }, Em.run.bind(this,this.sendAlert));
-  }
-});