Repository: ambari Updated Branches: refs/heads/trunk 364417494 -> 9cf27bf86
AMBARI-7235. Admin View: it is difficult and error-prone to be typing users and groups in textarea for membership and permission changes (implement type-ahead for user + group input fields). (yusaku) Project: http://git-wip-us.apache.org/repos/asf/ambari/repo Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/9cf27bf8 Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/9cf27bf8 Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/9cf27bf8 Branch: refs/heads/trunk Commit: 9cf27bf86046b0061f30e5c29e16b8b7aa7ab921 Parents: 3644174 Author: Yusaku Sako <yus...@hortonworks.com> Authored: Tue Sep 9 18:32:26 2014 -0700 Committer: Yusaku Sako <yus...@hortonworks.com> Committed: Tue Sep 9 18:32:43 2014 -0700 ---------------------------------------------------------------------- .../main/resources/ui/admin-web/app/index.html | 2 + .../resources/ui/admin-web/app/scripts/app.js | 1 + .../controllers/ambariViews/ViewsEditCtrl.js | 8 + .../clusters/ClustersManageAccessCtrl.js | 8 + .../controllers/groups/GroupsEditCtrl.js | 9 + .../scripts/controllers/users/UsersShowCtrl.js | 13 ++ .../app/scripts/directives/editableList.js | 199 +++++++++++++++++++ .../ui/admin-web/app/scripts/services/Group.js | 6 + .../ui/admin-web/app/scripts/services/User.js | 7 + .../resources/ui/admin-web/app/styles/main.css | 182 +++++++++++++++++ .../admin-web/app/views/ambariViews/edit.html | 29 +-- .../app/views/clusters/manageAccess.html | 31 +-- .../app/views/directives/editableList.html | 44 ++++ .../ui/admin-web/app/views/groups/edit.html | 23 +-- .../ui/admin-web/app/views/users/show.html | 22 +- .../src/main/resources/ui/admin-web/bower.json | 3 +- 16 files changed, 490 insertions(+), 97 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ambari/blob/9cf27bf8/ambari-admin/src/main/resources/ui/admin-web/app/index.html ---------------------------------------------------------------------- diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/index.html b/ambari-admin/src/main/resources/ui/admin-web/app/index.html index 2bc520f..e7a8371 100644 --- a/ambari-admin/src/main/resources/ui/admin-web/app/index.html +++ b/ambari-admin/src/main/resources/ui/admin-web/app/index.html @@ -85,6 +85,7 @@ <script src="bower_components/jquery/dist/jquery.js"></script> <script src="bower_components/bootstrap/dist/js/bootstrap.js"></script> <script src="bower_components/angular/angular.js"></script> + <script src="bower_components/angular-animate/angular-animate.js"></script> <script src="bower_components/angular-route/angular-route.js"></script> <script src="bower_components/angular-bootstrap/ui-bootstrap-tpls.js"></script> <script src="bower_components/lodash/dist/lodash.compat.js"></script> @@ -126,6 +127,7 @@ <script src="scripts/directives/linkToDir.js"></script> <script src="scripts/directives/PasswordVerify.js"></script> <script src="scripts/directives/disabledTooltip.js"></script> + <script src="scripts/directives/editableList.js"></script> <script src="scripts/services/User.js"></script> <script src="scripts/services/Group.js"></script> <script src="scripts/services/View.js"></script> http://git-wip-us.apache.org/repos/asf/ambari/blob/9cf27bf8/ambari-admin/src/main/resources/ui/admin-web/app/scripts/app.js ---------------------------------------------------------------------- diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/app.js b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/app.js index b9fcce2..b580da7 100644 --- a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/app.js +++ b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/app.js @@ -19,6 +19,7 @@ angular.module('ambariAdminConsole', [ 'ngRoute', + 'ngAnimate', 'ui.bootstrap', 'restangular', 'angularAlert', http://git-wip-us.apache.org/repos/asf/ambari/blob/9cf27bf8/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/ambariViews/ViewsEditCtrl.js ---------------------------------------------------------------------- diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/ambariViews/ViewsEditCtrl.js b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/ambariViews/ViewsEditCtrl.js index d860904..4355f63 100644 --- a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/ambariViews/ViewsEditCtrl.js +++ b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/ambariViews/ViewsEditCtrl.js @@ -160,6 +160,14 @@ angular.module('ambariAdminConsole') $scope.editPermissionDisabled = true; }; + $scope.$watch(function() { + return $scope.permissionsEdit; + }, function(newValue) { + if(newValue){ + $scope.savePermissions(); + } + }, true); + $scope.deleteInstance = function(instance) { ConfirmationModal.show('Delete View Instance', 'Are you sure you want to delete View Instance '+ instance.ViewInstanceInfo.label +'?').then(function() { View.deleteInstance(instance.ViewInstanceInfo.view_name, instance.ViewInstanceInfo.version, instance.ViewInstanceInfo.instance_name) http://git-wip-us.apache.org/repos/asf/ambari/blob/9cf27bf8/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/clusters/ClustersManageAccessCtrl.js ---------------------------------------------------------------------- diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/clusters/ClustersManageAccessCtrl.js b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/clusters/ClustersManageAccessCtrl.js index ff61f60..8999594 100644 --- a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/clusters/ClustersManageAccessCtrl.js +++ b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/clusters/ClustersManageAccessCtrl.js @@ -61,4 +61,12 @@ angular.module('ambariAdminConsole') }); $scope.isEditMode = false; }; + + $scope.$watch(function() { + return $scope.permissionsEdit; + }, function(newValue) { + if(newValue){ + $scope.save(); + } + }, true); }]); \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ambari/blob/9cf27bf8/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/groups/GroupsEditCtrl.js ---------------------------------------------------------------------- diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/groups/GroupsEditCtrl.js b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/groups/GroupsEditCtrl.js index 0896de1..a322c1d 100644 --- a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/groups/GroupsEditCtrl.js +++ b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/groups/GroupsEditCtrl.js @@ -26,6 +26,14 @@ angular.module('ambariAdminConsole') $scope.dataLoaded = false; $scope.isMembersEditing = false; + + $scope.$watch(function() { + return $scope.group.editingUsers; + }, function(newValue) { + if(newValue && !angular.equals(newValue, $scope.groupMembers)){ + $scope.updateMembers(); + } + }, true); $scope.enableMembersEditing = function() { $scope.isMembersEditing = true; @@ -49,6 +57,7 @@ angular.module('ambariAdminConsole') function loadMembers(){ $scope.group.getMembers().then(function(members) { $scope.groupMembers = members; + $scope.group.editingUsers = angular.copy($scope.groupMembers); }); } http://git-wip-us.apache.org/repos/asf/ambari/blob/9cf27bf8/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/users/UsersShowCtrl.js ---------------------------------------------------------------------- diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/users/UsersShowCtrl.js b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/users/UsersShowCtrl.js index 97a7234..f3e4240 100644 --- a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/users/UsersShowCtrl.js +++ b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/users/UsersShowCtrl.js @@ -24,6 +24,7 @@ angular.module('ambariAdminConsole') User.get($routeParams.id).then(function(data) { $scope.user = data.Users; $scope.isCurrentUser = $scope.user.user_name === Auth.getCurrentUser(); + $scope.editingGroupsList = angular.copy($scope.user.groups); }); } @@ -38,6 +39,18 @@ angular.module('ambariAdminConsole') $scope.editingGroupsList = angular.copy($scope.user.groups); }; + $scope.$watch(function() { + return $scope.editingGroupsList; + }, function(newValue) { + if(newValue){ + if( !angular.equals(newValue, $scope.user.groups) ){ + console.log('Update!'); + $scope.updateGroups(); + } + + } + }, true); + $scope.updateGroups = function() { var groups = $scope.editingGroupsList.toString().split(',').filter(function(item) {return item.trim();}).map(function(item) {return item.trim()}); var diff = getDifference($scope.user.groups, groups); http://git-wip-us.apache.org/repos/asf/ambari/blob/9cf27bf8/ambari-admin/src/main/resources/ui/admin-web/app/scripts/directives/editableList.js ---------------------------------------------------------------------- diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/directives/editableList.js b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/directives/editableList.js new file mode 100644 index 0000000..5d72078 --- /dev/null +++ b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/directives/editableList.js @@ -0,0 +1,199 @@ +/** + * 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. + */ +'use strict'; + +angular.module('ambariAdminConsole') +.directive('editableList', ['$q', '$document', function($q, $document) { + return { + restrict: 'E', + templateUrl: 'views/directives/editableList.html', + scope: { + itemsSource: '=', + resourceType: '@', + editable: '=' + }, + link: function($scope, $elem, $attr, $ctrl) { + var $editBox = $elem.find('[contenteditable]'); + + var readInput = function() { + $scope.$apply(function() { + $scope.input = $editBox.html(); + }); + }; + + $scope.$watch(function() { + return $scope.input; + }, function(newValue) { + if(newValue === ''){ + $scope.clearInput(); + } + }); + + $scope.clearInput = function() { + $editBox.html('').blur(); + }; + + $scope.focusOnInput = function() { + setTimeout(function() { + var elem = $editBox[0]; + var selection = window.getSelection(), + range = document.createRange(); + + elem.innerHTML = '\u00a0'; + range.selectNodeContents(elem); + selection.removeAllRanges(); + selection.addRange(range); + document.execCommand('delete', false, null); + }, 0); + }; + + $editBox.on('input', readInput); + $editBox.on('keydown', function(e) { + switch(e.which){ + case 27: // ESC + $editBox.html('').blur(); + readInput(); + break; + case 13: // Enter + $scope.$apply(function() { + $scope.addItem(); + }); + return false; + break; + case 40: // Down arrow + $scope.downArrowHandler(); + break; + case 38: // Up arrow + $scope.upArrowHandler(); + break; + } + }); + }, + controller: ['$scope', '$injector', function($scope, $injector) { + var $resource = $injector.get($scope.resourceType); + + $scope.identity = angular.identity; // Sorting function + + $scope.items = angular.copy($scope.itemsSource); + $scope.editMode = false; + $scope.input = ''; + $scope.typeahead = []; + $scope.selectedTypeahed = 0; + + // Watch source of items + $scope.$watch(function() { + return $scope.itemsSource; + }, function(newValue) { + $scope.items = angular.copy($scope.itemsSource); + }, true); + + // When input has changed - load typeahead items + $scope.$watch(function() { + return $scope.input; + }, function(newValue) { + if(newValue){ + var newValue = newValue.split(',').filter(function(i){ + i = i.replace(' ', ''); // Sanitize from spaces + return !!i.trim(); + }); + if( newValue.length > 1){ + // If someone paste coma separated string, then just add all items to list + angular.forEach(newValue, function(item) { + $scope.addItem(item); + }); + $scope.clearInput(); + + } else { + // Load typeahed items based on current input + $resource.listByName(newValue).then(function(data) { + var items = []; + angular.forEach(data.data.items, function(item) { + var name; + if($scope.resourceType === 'User'){ + name = item.Users.user_name; + } else if($scope.resourceType === 'Group'){ + name = item.Groups.group_name; + } + + if($scope.items.indexOf(name) < 0){ // Only if item not in list + items.push(name); + } + $scope.typeahead = items.slice(0, 5); + $scope.selectedTypeahed = 0; + }); + }); + } + + + } else { + $scope.typeahead = []; + $scope.selectedTypeahed = 0; + } + }); + + $scope.enableEditMode = function() { + if( $scope.editable && !$scope.editMode){ + $scope.editMode = true; + if( $scope.items.length === 0){ + $scope.focusOnInput(); + } + } + }; + + $scope.cancel = function(event) { + $scope.editMode = false; + $scope.items = angular.copy($scope.itemsSource); + $scope.input = ''; + event.stopPropagation(); + }; + $scope.save = function(event) { + $scope.itemsSource = $scope.items; + $scope.editMode = false; + $scope.input = ''; + event.stopPropagation(); + }; + + + $scope.downArrowHandler = function() { + $scope.$apply(function() { + $scope.selectedTypeahed = ($scope.selectedTypeahed+1) % $scope.typeahead.length; + }); + }; + $scope.upArrowHandler = function() { + $scope.$apply(function() { + $scope.selectedTypeahed -= 1; + $scope.selectedTypeahed = $scope.selectedTypeahed < 0 ? $scope.typeahead.length-1 : $scope.selectedTypeahed; + }); + }; + + $scope.addItem = function(item) { + item = item ? item : $scope.typeahead.length ? $scope.typeahead[$scope.selectedTypeahed] : $scope.input; + + if(item && $scope.items.indexOf(item) < 0){ + $scope.items.push(item); + $scope.input = ''; + } + }; + + $scope.removeFromItems = function(item) { + $scope.items.splice( $scope.items.indexOf(item), 1); + }; + }] + }; +}]); + http://git-wip-us.apache.org/repos/asf/ambari/blob/9cf27bf8/ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/Group.js ---------------------------------------------------------------------- diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/Group.js b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/Group.js index efe39a3..87f7831 100644 --- a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/Group.js +++ b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/Group.js @@ -174,6 +174,12 @@ angular.module('ambariAdminConsole') return deferred.promise; }; + Group.listByName = function(name) { + return $http.get(Settings.baseUrl + '/groups?' + + 'Groups/group_name.matches(.*'+name+'.*)' + ); + }; + Group.getPrivilegies = function(groupId) { return $http.get(Settings.baseUrl + '/privileges', { params:{ http://git-wip-us.apache.org/repos/asf/ambari/blob/9cf27bf8/ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/User.js ---------------------------------------------------------------------- diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/User.js b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/User.js index 9c2e36a..40c5683 100644 --- a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/User.js +++ b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/User.js @@ -46,6 +46,13 @@ angular.module('ambariAdminConsole') + (params.admin ? '&Users/admin=true' : '') ); }, + listByName: function(name) { + return $http.get( + Settings.baseUrl + '/users?' + + 'Users/user_name.matches(.*'+name+'.*)' + + '&from=0&page_size=20' + ); + }, get: function(userId) { return Restangular.one('users', userId).get(); }, http://git-wip-us.apache.org/repos/asf/ambari/blob/9cf27bf8/ambari-admin/src/main/resources/ui/admin-web/app/styles/main.css ---------------------------------------------------------------------- diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/styles/main.css b/ambari-admin/src/main/resources/ui/admin-web/app/styles/main.css index e92fe1a..bfa0032 100644 --- a/ambari-admin/src/main/resources/ui/admin-web/app/styles/main.css +++ b/ambari-admin/src/main/resources/ui/admin-web/app/styles/main.css @@ -16,6 +16,182 @@ * limitations under the License. */ + + +/* + ------ START editable-list DIRECTIVE SECTION ------ - +*/ +.editable-list-container.well{ + padding: 10px; + position: relative; + margin-bottom: 30px; + cursor: pointer; +} +.editable-list-container.well.edit-mode{ + cursor: default; +} +.editable-list-container.well.disabled{ + background: white; +} + +.editable-list-container .items-box{ + +} +.editable-list-container .items-box ul.items-list{ + list-style-type: none; + margin: 0; + padding: 0; +} + +.editable-list-container .items-box ul.items-list li.item{ + display: inline-block; + padding: 4px 8px; + margin: 0 5px 5px 2px; + background: white; + border: 1px solid #ebebeb; + max-width: 100%; + white-space: nowrap; + position: relative; +} + +.editable-list-container.edit-mode .items-box ul.items-list li.item{ + padding-right: 25px; +} + +.editable-list-container .items-box ul.items-list li.item.ng-leave-active{ + display: none; +} +.editable-list-container .items-box ul.items-list li a{ + text-decoration: none; +} + +.editable-list-container .items-box ul.items-list li.item .close{ + margin: -2px 0 0 5px; + width: 13px; + outline: none; + position: absolute; + display: none; +} +.editable-list-container.edit-mode .items-box ul.items-list li.item .close{ + display: inline-block; +} + +.editable-list-container .actions-panel{ + position: absolute; + right: 5px; + bottom: -30px; + padding: 2px 5px 5px 5px; + background: #f5f5f5; + border: 1px solid #e3e3e3; + border-top: none; + border-radius: 0 0 4px 4px; + + -webkit-transition: all 0.3s; + -o-transition: all 0.3s; + transition: all 0.3s; + + -ms-transform-origin: 0% 0%; /* IE 9 */ + -webkit-transform-origin: 0% 0%; /* Chrome, Safari, Opera */ + transform-origin: 0% 0%; + + -webkit-transform: rotateX(0deg); + -ms-transform: rotateX(0deg); + -o-transform: rotateX(0deg); + transform: rotateX(0deg); +} +.editable-list-container .actions-panel.ng-hide{ + -webkit-transform: rotateX(90deg); + -ms-transform: rotateX(90deg); + -o-transform: rotateX(90deg); + transform: rotateX(90deg); +} + +.editable-list-container.edit-mode .items-box ul.items-list li.item.add-item-input.ng-hidden{ + display: none !important; +} +.editable-list-container.edit-mode .items-box ul.items-list li.item.add-item-input{ + display: inline-block!important; + outline: none; + max-width: 200px; + text-overflow: ellipsis; + white-space: nowrap; + position: relative; + padding-right: 8px; + -webkit-transition: none; + -o-transition: none; + transition: none; +} + +.add-item-input span{ + display: block; + outline: none; + min-width: 30px; + position: relative; + cursor: pointer; +} +.add-item-input span:focus{ + cursor: default; +} +.editable-list-container .items-box ul.items-list li.item.add-item{ + color: #ddd; +} +.add-item-input span:empty:before{ + content: 'New'; + position: absolute; + left: 0; + color: #ddd; +} +.add-item-input span:focus:before{ + display: none; +} +.typeahead-box{ + position: absolute; + left: 0; + margin-top: 5px; + background: white; + border: 1px solid #ebebeb; + z-index: 1000; + min-width: 65px; +} +.typeahead-box ul{ + list-style-type: none; + margin: 0; + padding: 0; +} +.typeahead-box ul li{ + padding: 3px 5px; + display: block; + cursor: pointer; +} + +.typeahead-box ul li.selected, .typeahead-box ul li:hover{ + background: #eee; +} + +.editable-list-container.disabled .pencil-box{ + display: none; +} +.editable-list-container .pencil-box{ + position: absolute; + right: 5px; + top: 5px; + opacity: 0; + -webkit-transition: all 0.3s; + -o-transition: all 0.3s; + transition: all 0.3s; +} +.editable-list-container:hover .pencil-box{ + opacity: 1; +} +.editable-list-container.edit-mode:hover .pencil-box{ + opacity: 0; +} + +/* + ------ END editable-list DIRECTIVE SECTION ------ - +*/ + + .instances-table{ table-layout: fixed; } @@ -747,6 +923,12 @@ input[type="submit"].btn.btn-mini { *padding-bottom: 1px; } +button.btn.btn-xs{ + padding: 1px 5px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} .alert-info { background-color: #E6F1F6; border-color: #D2D9DD; http://git-wip-us.apache.org/repos/asf/ambari/blob/9cf27bf8/ambari-admin/src/main/resources/ui/admin-web/app/views/ambariViews/edit.html ---------------------------------------------------------------------- diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/views/ambariViews/edit.html b/ambari-admin/src/main/resources/ui/admin-web/app/views/ambariViews/edit.html index dbb8165..02d4f77 100644 --- a/ambari-admin/src/main/resources/ui/admin-web/app/views/ambariViews/edit.html +++ b/ambari-admin/src/main/resources/ui/admin-web/app/views/ambariViews/edit.html @@ -93,9 +93,6 @@ <div class="panel panel-default views-permissions-panel" style=""> <div class="panel-heading clearfix"> <h3 class="panel-title pull-left">Permissions</h3> - <div class="pull-right" > - <a ng-hide="isPermissionsEmpty" href class="permissions-edit-toggle" ng-click="editPermissionDisabled = !editPermissionDisabled" ng-show="editPermissionDisabled"> <span class="glyphicon glyphicon-pencil"></span> Edit</a> - </div> </div> <div class="panel-body"> @@ -114,36 +111,14 @@ <label class="">{{permission.PermissionInfo.permission_name}}</label> </td> <td> - <div class="" ng-switch="editPermissionDisabled"> - <textarea name="" id="" cols="30" rows="4" class="form-control permission-textarea-user" ng-model="permissionsEdit[permission.PermissionInfo.permission_name].USER" ng-switch-when="false"></textarea> - <div class="well" ng-switch-when="true"> - <span ng-repeat="user in permission.USER | orderBy:identity"> - <link-to route="users.show" id="{{user}}">{{user}}</link-to> - {{$last ? '' :', '}} - </span> - </div> - </div> + <editable-list items-source="permissionsEdit[permission.PermissionInfo.permission_name].USER" editable="true" resource-type="User"></editable-list> </td> <td> - <div class="" ng-switch="editPermissionDisabled"> - <textarea name="" id="" cols="30" rows="4" class="form-control permission-textarea-group" ng-model="permissionsEdit[permission.PermissionInfo.permission_name].GROUP" ng-switch-when="false"></textarea> - <div class="well" ng-switch-when="true"> - <span ng-repeat="group in permission.GROUP | orderBy:identity"> - <link-to route="groups.edit" id="{{group}}" >{{group}}</link-to> - {{$last ? '' :', '}} - </span> - </div> - </div> + <editable-list items-source="permissionsEdit[permission.PermissionInfo.permission_name].GROUP" editable="true" resource-type="Group" ></editable-list> </td> </tr> </tbody> </table> - <div class="form-group" ng-hide="editPermissionDisabled"> - <div class="col-sm-offset-2 col-sm-10"> - <button class="btn btn-primary pull-right left-margin permissions-save" ng-click="savePermissions()">Save</button> - <button class="btn btn-default pull-right permissions-cancel" ng-click="cancelPermissions()">Cancel</button> - </div> - </div> <div ng-show="isPermissionsEmpty"> <div class="alert alert-info">There are no permissions defined for this view.</div> </div> http://git-wip-us.apache.org/repos/asf/ambari/blob/9cf27bf8/ambari-admin/src/main/resources/ui/admin-web/app/views/clusters/manageAccess.html ---------------------------------------------------------------------- diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/views/clusters/manageAccess.html b/ambari-admin/src/main/resources/ui/admin-web/app/views/clusters/manageAccess.html index fe27492..6de2561 100644 --- a/ambari-admin/src/main/resources/ui/admin-web/app/views/clusters/manageAccess.html +++ b/ambari-admin/src/main/resources/ui/admin-web/app/views/clusters/manageAccess.html @@ -21,17 +21,14 @@ <ol class="breadcrumb pull-left"> <li class="active">{{clusterName}} Permissions</li> </ol> - <div class="pull-right top-margin-4"> - <a href class="btn btn-primary" ng-hide="isEditMode" ng-click="toggleEditMode()"><span class="glyphicon glyphicon-pencil"></span> Edit</a> - </div> </div> <hr> <table class="table"> <thead> <tr> - <th class="col-sm-2"><label>Permission</label></th> - <th><label>Grant permission to these users</label></th> - <th><label>Grant permission to these groups</label></th> + <th class="col-sm-2" width="20%"><label>Permission</label></th> + <th class="col-sm-5" width="40%"><label>Grant permission to these users</label></th> + <th class="col-sm-5" width="40%"><label>Grant permission to these groups</label></th> </tr> </thead> <tbody> @@ -39,33 +36,15 @@ <td><label class="">{{permission.PermissionInfo.permission_name}}</label></td> <td> <div class="" ng-switch="isEditMode"> - <textarea ng-switch-when="true" name="" id="" cols="30" rows="4" class="form-control permission-user-input" ng-model="permissionsEdit[permission.PermissionInfo.permission_name].USER"></textarea> - <div class="well" ng-switch-default> - <span ng-repeat="user in permission.USER | orderBy:identity"> - <link-to route="users.show" id="{{user}}" >{{user}}</link-to> - {{$last ? '' :', '}} - </span> - </div> + <editable-list items-source="permissionsEdit[permission.PermissionInfo.permission_name].USER" resource-type="User" editable="true"></editable-list> </div> </td> <td> <div class="" ng-switch="isEditMode"> - <textarea ng-switch-when="true" name="" id="" cols="30" rows="4" class="form-control permission-group-input" ng-model="permissionsEdit[permission.PermissionInfo.permission_name].GROUP | orderBy:identity"></textarea> - <div class="well" ng-switch-default> - <span ng-repeat="group in permission.GROUP"> - <link-to route="groups.edit" id="{{group}}">{{group}}</link-to> - {{$last ? '' :', '}} - </span> - </div> + <editable-list items-source="permissionsEdit[permission.PermissionInfo.permission_name].GROUP" resource-type="Group" editable="true"></editable-list> </div> </td> </tr> </tbody> </table> - <div class="form-group" ng-show="isEditMode"> - <div class="col-sm-offset-2 col-sm-10"> - <button class="btn btn-primary pull-right permission-save left-margin" ng-click="save()">Save</button> - <button class="btn btn-default pull-right permissions-cancel" ng-click="cancel()">Cancel</button> - </div> - </div> </div> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ambari/blob/9cf27bf8/ambari-admin/src/main/resources/ui/admin-web/app/views/directives/editableList.html ---------------------------------------------------------------------- diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/views/directives/editableList.html b/ambari-admin/src/main/resources/ui/admin-web/app/views/directives/editableList.html new file mode 100644 index 0000000..5cdc148 --- /dev/null +++ b/ambari-admin/src/main/resources/ui/admin-web/app/views/directives/editableList.html @@ -0,0 +1,44 @@ +<!-- +* 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="editable-list-container well" ng-class="{'edit-mode' : editMode, 'disabled' : !editable}" ng-click="enableEditMode()"> + <div class="items-box"> + <ul class="items-list"> + <li class="item" ng-repeat="item in items | orderBy:identity"><span><a href>{{item}}</a><button ng-click="removeFromItems(item)" type="button" class="close"><span aria-hidden="true">×</span><span class="sr-only">Close</span></button></span></li><li class="item add-item-input" ng-show="editMode"> + <span contenteditable></span> + <div class="typeahead-box" ng-show="typeahead.length != 0"> + <ul> + <li ng-repeat="item in typeahead" ng-click="addItem(item)" ng-class="{'selected' : $index == selectedTypeahed}">{{item}}</li> + </ul> + </div> + </li> + <li class="item add-item" ng-show="!editMode && !items.length">Add {{resourceType}}</li> + </ul> + </div> + <div class="actions-panel" ng-show="editMode"> + <button class="btn btn-default btn-xs cancel" ng-click="cancel($event)"> + <span class="glyphicon glyphicon-remove cancel"></span> + </button> + <button class="btn btn-primary btn-xs" ng-click="save($event)"> + <span class="glyphicon glyphicon-ok"></span> + </button> + </div> + <div class="pencil-box"> + <span class="glyphicon glyphicon-pencil"></span> + </div> +</div> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ambari/blob/9cf27bf8/ambari-admin/src/main/resources/ui/admin-web/app/views/groups/edit.html ---------------------------------------------------------------------- diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/views/groups/edit.html b/ambari-admin/src/main/resources/ui/admin-web/app/views/groups/edit.html index d82caf6..5b90f79 100644 --- a/ambari-admin/src/main/resources/ui/admin-web/app/views/groups/edit.html +++ b/ambari-admin/src/main/resources/ui/admin-web/app/views/groups/edit.html @@ -40,28 +40,7 @@ <div class="form-group"> <label for="members" class="col-sm-2 control-label">{{group.ldap_group ? 'LDAP Members' : 'Local Members'}}</label> <div class="col-sm-10"> - <div class="row" ng-hide="isMembersEditing"> - <div class="col-sm-10"> - <div class="well users"> - <span ng-repeat="member in groupMembers" > - <link-to route='users.show' id="{{member}}"> - {{member}} - </link-to> - {{$last ? '' : ', '}} - </span> - </div> - </div> - <div class="col-sm-2"> - <a href ng-click="enableMembersEditing()" ng-hide="user.ldap_group"><span class="glyphicon glyphicon-pencil"></span> Edit</a> - </div> - </div> - <div class="row" ng-show="isMembersEditing"> - <div class="col-sm-12"> - <textarea name="groups" id="" cols="30" rows="5" class="form-control bottom-margin usergroups" ng-model="group.editingUsers"></textarea> - <a href class="btn btn-primary pull-right left-margin updategroups" ng-click="updateMembers()">Save</a> - <button class="btn btn-default pull-right cancel-groups-update" ng-click="cancelUpdate()">Cancel</button> - </div> - </div> + <editable-list items-source="group.editingUsers" resource-type="User" editable="!group.ldap_group"></editable-list> </div> </div> http://git-wip-us.apache.org/repos/asf/ambari/blob/9cf27bf8/ambari-admin/src/main/resources/ui/admin-web/app/views/users/show.html ---------------------------------------------------------------------- diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/views/users/show.html b/ambari-admin/src/main/resources/ui/admin-web/app/views/users/show.html index 88e48c9..0667200 100644 --- a/ambari-admin/src/main/resources/ui/admin-web/app/views/users/show.html +++ b/ambari-admin/src/main/resources/ui/admin-web/app/views/users/show.html @@ -62,27 +62,7 @@ <div class="form-group"> <label for="groups" class="col-sm-2 control-label">{{user.ldap_user ? 'LDAP Group Membership' : 'Local Group Membership'}}</label> <div class="col-sm-10"> - <div class="row" ng-hide="isGroupEditing"> - <div class="col-sm-10"> - <div class="well"> - <span ng-repeat="group in user.groups"> - <a href="#/groups/{{group}}/edit" >{{group}}</a> - {{$last ? '' : ', '}} - </span> - </div> - </div> - <div class="col-sm-2"> - <a href ng-click="enableGroupEditing()" ng-hide="user.ldap_user"><span class="glyphicon glyphicon-pencil"></span> Edit</a> - </div> - </div> - <div class="row" ng-show="isGroupEditing"> - <div class="col-sm-12"> - <textarea name="groups" id="" cols="30" rows="5" class="form-control bottom-margin usergroups" ng-model="editingGroupsList"></textarea> - <a href class="btn btn-primary pull-right left-margin updategroups" ng-click="updateGroups()">Save</a> - <button class="btn btn-default pull-right cancel-groups-update" ng-click="cancelUpdate()">Cancel</button> - </div> - - </div> + <editable-list items-source="editingGroupsList" resource-type="Group" editable="!user.ldap_user"></editable-list> </div> </div> http://git-wip-us.apache.org/repos/asf/ambari/blob/9cf27bf8/ambari-admin/src/main/resources/ui/admin-web/bower.json ---------------------------------------------------------------------- diff --git a/ambari-admin/src/main/resources/ui/admin-web/bower.json b/ambari-admin/src/main/resources/ui/admin-web/bower.json index 0c30117..27429c2 100644 --- a/ambari-admin/src/main/resources/ui/admin-web/bower.json +++ b/ambari-admin/src/main/resources/ui/admin-web/bower.json @@ -7,7 +7,8 @@ "angular-route": "~1.2.18", "angular-bootstrap": "~0.11.0", "restangular": "~1.4.0", - "angular-bootstrap-toggle-switch": "~0.5.1" + "angular-bootstrap-toggle-switch": "~0.5.1", + "angular-animate": "~1.2.23" }, "devDependencies": {} }