Quellcode durchsuchen

AMBARI-6713. Management Console: Create Manage Views Page (with mock data)

Srimanth Gunturi vor 10 Jahren
Ursprung
Commit
6961561be7
22 geänderte Dateien mit 840 neuen und 126 gelöschten Zeilen
  1. 8 4
      ambari-admin/src/main/resources/ui/admin-web/app/index.html
  2. 44 1
      ambari-admin/src/main/resources/ui/admin-web/app/scripts/app.js
  3. 51 0
      ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/LDAPModalCtrl.js
  4. 26 0
      ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/NavbarCtrl.js
  5. 70 0
      ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/ambariViews/CreateViewInstanceCtrl.js
  6. 80 1
      ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/ambariViews/ViewsEditCtrl.js
  7. 33 1
      ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/ambariViews/ViewsListCtrl.js
  8. 13 1
      ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/groups/GroupsListCtrl.js
  9. 13 1
      ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/users/UsersListCtrl.js
  10. 1 1
      ambari-admin/src/main/resources/ui/admin-web/app/scripts/routes.js
  11. 37 0
      ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/Cluster.js
  12. 156 0
      ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/View.js
  13. 56 0
      ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/ldap.js
  14. 3 0
      ambari-admin/src/main/resources/ui/admin-web/app/styles/main.css
  15. 47 43
      ambari-admin/src/main/resources/ui/admin-web/app/views/ambariViews/edit.html
  16. 12 64
      ambari-admin/src/main/resources/ui/admin-web/app/views/ambariViews/list.html
  17. 127 0
      ambari-admin/src/main/resources/ui/admin-web/app/views/ambariViews/modals/create.html
  18. 1 1
      ambari-admin/src/main/resources/ui/admin-web/app/views/groups/list.html
  19. 47 0
      ambari-admin/src/main/resources/ui/admin-web/app/views/ldapModal.html
  20. 11 6
      ambari-admin/src/main/resources/ui/admin-web/app/views/leftNavbar.html
  21. 1 1
      ambari-admin/src/main/resources/ui/admin-web/app/views/users/list.html
  22. 3 1
      ambari-web/app/templates/application.hbs

+ 8 - 4
ambari-admin/src/main/resources/ui/admin-web/app/index.html

@@ -44,7 +44,7 @@
       <div class="navbar-inner">
         <div class="container">
           <div class="navbar-header">
-            <a href="/#/main/dashboard" class="navbar-brand">Ambari Admin Console</a>
+            <a href="#/" class="navbar-brand">Ambari Admin Console</a>
           </div>  
           <ul class="nav navbar-nav navbar-right">
             <li>
@@ -54,8 +54,6 @@
                 </button>
                 <ul class="dropdown-menu" role="menu">
                   <li><a href="#">About</a></li>
-                  <li><a href="#">Settings</a></li>
-                  <li><a href="#">Manage Ambari</a></li>
                   <li class="divider"></li>
                   <li><a href="#">Sign Out</a></li>
                 </ul>
@@ -69,7 +67,7 @@
     <div class="container">
       <div class="row">
         <div class="col-sm-3">
-          <div ng-include="'views/leftNavbar.html'"></div>
+          <div ng-include="'views/leftNavbar.html'" ng-controller="NavbarCtrl"></div>
         </div>
         <div class="col-sm-9">
           <ng-view></ng-view>
@@ -109,6 +107,8 @@
     <script src="scripts/app.js"></script>
     <script src="scripts/routes.js"></script>
     <script src="scripts/controllers/mainCtrl.js"></script>
+    <script src="scripts/controllers/NavbarCtrl.js"></script>
+    <script src="scripts/controllers/LDAPModalCtrl.js"></script>
     <script src="scripts/controllers/users/UsersCreateCtrl.js"></script>
     <script src="scripts/controllers/users/UsersListCtrl.js"></script>
     <script src="scripts/controllers/users/UsersShowCtrl.js"></script>
@@ -117,11 +117,15 @@
     <script src="scripts/controllers/groups/GroupsEditCtrl.js"></script>
     <script src="scripts/controllers/ambariViews/ViewsListCtrl.js"></script>
     <script src="scripts/controllers/ambariViews/ViewsEditCtrl.js"></script>
+    <script src="scripts/controllers/ambariViews/CreateViewInstanceCtrl.js"></script>
     <script src="scripts/controllers/clusters/ClustersManageAccessCtrl.js"></script>
     <script src="scripts/directives/linkToDir.js"></script>
     <script src="scripts/directives/PasswordVerify.js"></script>
     <script src="scripts/services/User.js"></script>
     <script src="scripts/services/Group.js"></script>
+    <script src="scripts/services/View.js"></script>
+    <script src="scripts/services/ldap.js"></script>
+    <script src="scripts/services/Cluster.js"></script>
     <!-- endbuild -->
 </body>
 </html>

+ 44 - 1
ambari-admin/src/main/resources/ui/admin-web/app/scripts/app.js

@@ -25,14 +25,57 @@ angular.module('ambariAdminConsole', [
 .constant('Settings',{
 	baseUrl: '/api/v1'
 })
-.config(['RestangularProvider', '$httpProvider', function(RestangularProvider, $httpProvider) {
+.config(['RestangularProvider', '$httpProvider', '$provide', function(RestangularProvider, $httpProvider, $provide) {
   // Config Ajax-module
   RestangularProvider.setBaseUrl('/api/v1');
   RestangularProvider.setDefaultHeaders({'X-Requested-By':'ambari'});
 
   $httpProvider.defaults.headers.post['Content-Type'] = 'plain/text';
   $httpProvider.defaults.headers.put['Content-Type'] = 'plain/text';
+
   $httpProvider.defaults.headers.post['X-Requested-By'] = 'ambari';
   $httpProvider.defaults.headers.put['X-Requested-By'] = 'ambari';
   $httpProvider.defaults.headers.common['X-Requested-By'] = 'ambari';
+
+  $provide.decorator('ngFormDirective', ['$delegate', function($delegate) {
+    var ngForm = $delegate[0], controller = ngForm.controller;
+    ngForm.controller = ['$scope', '$element', '$attrs', '$injector', function(scope, element, attrs, $injector) {
+    var $interpolate = $injector.get('$interpolate');
+      attrs.$set('name', $interpolate(attrs.name || '')(scope));
+      $injector.invoke(controller, this, {
+        '$scope': scope,
+        '$element': element,
+        '$attrs': attrs
+      });
+    }];
+    return $delegate;
+  }]);
+
+  $provide.decorator('ngModelDirective', ['$delegate', function($delegate) {
+    var ngModel = $delegate[0], controller = ngModel.controller;
+    ngModel.controller = ['$scope', '$element', '$attrs', '$injector', function(scope, element, attrs, $injector) {
+      var $interpolate = $injector.get('$interpolate');
+      attrs.$set('name', $interpolate(attrs.name || '')(scope));
+      $injector.invoke(controller, this, {
+        '$scope': scope,
+        '$element': element,
+        '$attrs': attrs
+      });
+    }];
+    return $delegate;
+  }]);
+
+  $provide.decorator('formDirective', ['$delegate', function($delegate) {
+    var form = $delegate[0], controller = form.controller;
+    form.controller = ['$scope', '$element', '$attrs', '$injector', function(scope, element, attrs, $injector) {
+      var $interpolate = $injector.get('$interpolate');
+      attrs.$set('name', $interpolate(attrs.name || attrs.ngForm || '')(scope));
+        $injector.invoke(controller, this, {
+        '$scope': scope,
+        '$element': element,
+        '$attrs': attrs
+      });
+    }];
+    return $delegate;
+  }]);
 }]);

+ 51 - 0
ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/LDAPModalCtrl.js

@@ -0,0 +1,51 @@
+/**
+ * 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')
+.controller('LDAPModalCtrl',['$scope', 'LDAP', 'resourceType', '$modalInstance', function($scope, LDAP, resourceType, $modalInstance) {
+  $scope.resourceType = resourceType;
+  $scope.isConfigured = false;
+  $scope.isDataLoaded = false;
+
+  $scope.items = [
+  ];
+
+
+  LDAP.get(resourceType).then(function(data) {
+    $scope.isConfigured = data['LDAP']['configured'];
+    $scope.items = data['LDAP'][resourceType];
+    $scope.isDataLoaded = true;
+  });
+
+  $scope.sync = function() {
+    if($scope.items){
+      var itemsToSync = $scope.items.filter(function(item) {return item.selected}).map(function(item) {
+        return item.name
+      });
+
+      LDAP.sync(resourceType, itemsToSync).then(function() {
+        $modalInstance.close();
+      });
+    }
+  };
+
+  $scope.cancel = function() {
+    $modalInstance.dismiss();
+  };
+}]);

+ 26 - 0
ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/NavbarCtrl.js

@@ -0,0 +1,26 @@
+/**
+ * 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')
+.controller('NavbarCtrl',['$scope', 'Cluster', function($scope, Cluster) {
+	$scope.cluster = null;
+	Cluster.getStatus().then(function(cluster) {
+		$scope.cluster = cluster;
+	});
+}]);

+ 70 - 0
ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/ambariViews/CreateViewInstanceCtrl.js

@@ -0,0 +1,70 @@
+/**
+ * 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')
+.controller('CreateViewInstanceCtrl',['$scope', 'View', '$modalInstance', 'viewVersion', function($scope, View, $modalInstance, viewVersion) {
+	$scope.form = {};
+
+	$scope.view = viewVersion;
+	$scope.isAdvancedClosed = true;
+	$scope.instanceExists = false;
+
+
+	$scope.instance = {
+		view_name: viewVersion.ViewVersionInfo.view_name,
+		version: viewVersion.ViewVersionInfo.version,
+		instance_name: '',
+		label: '',
+		visible: true,
+		icon_path: '',
+		icon64_path: '',
+		properties: viewVersion.ViewVersionInfo.parameters
+	};
+
+	$scope.nameValidationPattern = /^\s*\w*\s*$/;
+
+	$scope.save = function() {
+		window.f = $scope.form.isntanceCreateForm;
+		$scope.form.isntanceCreateForm.submitted = true;
+		if($scope.form.isntanceCreateForm.$valid){
+			View.getInstance($scope.instance.view_name, $scope.instance.version, $scope.instance.instance_name)
+			.then(function(data) {
+				if (data.ViewInstanceInfo) {
+					$scope.instanceExists = true;
+				} else {
+					View.createInstance($scope.instance)
+					.then(function(data) {
+						$modalInstance.close();
+					})
+					.catch(function(data) {
+						console.log(data);
+					});
+				}
+			})
+			.catch(function() {
+				console.log('Error');
+			});
+		}
+	};
+
+	$scope.cancel = function() {
+		$modalInstance.dismiss();
+	};
+
+}]);

+ 80 - 1
ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/ambariViews/ViewsEditCtrl.js

@@ -18,5 +18,84 @@
 'use strict';
 
 angular.module('ambariAdminConsole')
-.controller('ViewsEditCtrl', ['$scope', function($scope) {
+.controller('ViewsEditCtrl', ['$scope', '$routeParams' , 'View', '$http', function($scope, $routeParams, View, $http) {
+
+  /*
+    Perissions structure
+  */
+  $scope.permissions = [];
+  View.getPermissions($routeParams.viewId, $routeParams.version)
+  .then(function(permissionData) {
+    $scope.permissions = permissionData.data.ViewVersionInfo.permissions;
+    var permissionsObject = {};
+    // Fill permissions with empty arrays
+    angular.forEach(permissionData.data.ViewVersionInfo.permissions, function(permission) {
+      var permissionLabel = permission.PermissionInfo.permission_name.replace('VIEW.', '');
+      permissionsObject[permission.PermissionInfo.permission_name] = {
+        name: permission.PermissionInfo.permission_name,
+        label: permissionLabel,
+        user: [],
+        group: []
+      };
+    });
+
+    // Load instance data, after View permissions meta loaded
+    View.getInstance($routeParams.viewId, $routeParams.version, $routeParams.instanceId)
+    .then(function(instance) {
+      $scope.instance = instance;
+      $scope.settings = {
+        'visible': $scope.instance.ViewInstanceInfo.visible,
+        'label': $scope.instance.ViewInstanceInfo.label
+      };
+      $scope.configuration = angular.copy($scope.instance.ViewInstanceInfo.properties);
+      angular.forEach(instance.privileges, function(privilegie) {
+        permissionsObject[privilegie.Privileges.permission_name][privilegie.Privileges.principal_type] = privilegie.Privileges.principal_name;
+      });
+    });
+
+  });
+
+    
+
+  $scope.editSettingsDisabled = true;
+  
+
+  $scope.saveSettings = function() {
+    View.updateInstance($routeParams.viewId, $routeParams.version, $routeParams.instanceId, {
+      'ViewInstanceInfo':{
+        'visible': $scope.settings.visible,
+        'label': $scope.settings.label
+      }
+    })
+    .success(function() {
+      $scope.editSettingsDisabled = true;
+    });
+  };
+  $scope.cancelSettings = function() {
+    $scope.settings = {
+      'visible': $scope.instance.ViewInstanceInfo.visible,
+      'label': $scope.instance.ViewInstanceInfo.label
+    };
+    $scope.editSettingsDisabled = true;
+  };
+
+  $scope.editConfigurationDisabled = true;
+
+  $scope.saveConfiguration = function() {
+    View.updateInstance($routeParams.viewId, $routeParams.version, $routeParams.instanceId, {
+      'ViewInstanceInfo':{
+        'properties': $scope.configuration
+      }
+    })
+    .success(function() {
+      $scope.editConfigurationDisabled = true;
+    });
+  };
+  $scope.cancelConfiguration = function() {
+    $scope.configuration = angular.copy($scope.instance.ViewInstanceInfo.properties);
+    $scope.editConfigurationDisabled = true;
+  };
+
+  
+
 }]);

+ 33 - 1
ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/ambariViews/ViewsListCtrl.js

@@ -18,5 +18,37 @@
 'use strict';
 
 angular.module('ambariAdminConsole')
-.controller('ViewsListCtrl',['$scope', function($scope) {
+.controller('ViewsListCtrl',['$scope', 'View', '$modal', function($scope, View, $modal) {
+	function loadViews(){
+		View.all().then(function(views) {
+			$scope.views = views;
+		});
+	}
+
+	loadViews();
+
+	$scope.createInstance = function(view) {
+		var modalInstance = $modal.open({
+			templateUrl: 'views/ambariViews/modals/create.html',
+			size: 'lg',
+			controller: 'CreateViewInstanceCtrl',
+			resolve: {
+				viewVersion: function(){
+					return view.versionsList[ view.versionsList.length-1];
+				}
+			}
+		});
+
+		modalInstance.result.then(loadViews);
+	};
+
+	$scope.deleteInstance = function(instance) {
+		View.deleteInstance(instance.ViewInstanceInfo.view_name, instance.ViewInstanceInfo.version, instance.ViewInstanceInfo.instance_name)
+		.then(function() {
+			loadViews();
+		})
+		.catch(function(data) {
+			console.error(data);
+		});
+	};
 }]);

+ 13 - 1
ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/groups/GroupsListCtrl.js

@@ -18,7 +18,7 @@
 'use strict';
 
 angular.module('ambariAdminConsole')
-.controller('GroupsListCtrl',['$scope', 'Group', function($scope, Group) {
+.controller('GroupsListCtrl',['$scope', 'Group', '$modal', function($scope, Group, $modal) {
 	$scope.groups = [];
 
 	Group.all().then(function(groups) {
@@ -33,4 +33,16 @@ angular.module('ambariAdminConsole')
 			$scope.groups.splice( $scope.groups.indexOf(group), 1);
 		});
 	};
+
+	$scope.syncLDAP = function() {
+    var modaInstance = $modal.open({
+      templateUrl: 'views/ldapModal.html',
+      controller: 'LDAPModalCtrl',
+      resolve:{
+        resourceType: function() {
+          return 'groups';
+        }
+      }
+    });
+  };
 }]);

+ 13 - 1
ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/users/UsersListCtrl.js

@@ -18,7 +18,7 @@
 'use strict';
 
 angular.module('ambariAdminConsole')
-.controller('UsersListCtrl',['$scope', 'User', function($scope, User) {
+.controller('UsersListCtrl',['$scope', 'User', '$modal', function($scope, User, $modal) {
   $scope.users = [];
   User.list().then(function(data) {
     $scope.users = data.items;
@@ -49,4 +49,16 @@ angular.module('ambariAdminConsole')
       return user;
     }
   };
+
+  $scope.syncLDAP = function() {
+    var modaInstance = $modal.open({
+      templateUrl: 'views/ldapModal.html',
+      controller: 'LDAPModalCtrl',
+      resolve:{
+        resourceType: function() {
+          return 'users';
+        }
+      }
+    });
+  };
 }]);

+ 1 - 1
ambari-admin/src/main/resources/ui/admin-web/app/scripts/routes.js

@@ -70,7 +70,7 @@ angular.module('ambariAdminConsole')
       controller: 'ViewsListCtrl',
     },
     edit: {
-      url: '/views/:id/edit',
+      url: '/views/:viewId/versions/:version/instances/:instanceId/edit',
       templateUrl: 'views/ambariViews/edit.html',
       controller: 'ViewsEditCtrl'
     },

+ 37 - 0
ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/Cluster.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.
+ */
+'use strict';
+
+angular.module('ambariAdminConsole')
+.factory('Cluster', ['$http', '$q', 'Settings', function($http, $q, Settings) {
+  return {
+    getStatus: function() {
+      var deferred = $q.defer();
+
+      $http.get(Settings.baseUrl + '/clusters')
+      .then(function(data, status, headers) {
+        deferred.resolve(data.data.items[0]);
+      })
+      .catch(function(data) {
+        deferred.reject(data);
+      });
+
+      return deferred.promise;
+    }
+  };
+}]);

+ 156 - 0
ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/View.js

@@ -0,0 +1,156 @@
+/**
+ * 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')
+.factory('View', ['$http', '$q', 'Settings', function($http, $q, Settings) {
+
+  function ViewInstance(item){
+    angular.extend(this, item);
+  };
+
+  ViewInstance.find = function(viewName, version, instanceName) {
+    var deferred = $q.defer();
+    var fields = [
+      'privileges/PrivilegeInfo',
+      'ViewInstanceInfo',
+      'resources'
+    ];
+
+    $http({
+      method: 'GET',
+      url: Settings.baseUrl + '/views/'+viewName+'/versions/'+version+'/instances/'+instanceName,
+      params:{
+        'fields': fields.join(',')
+      }
+    })
+    .success(function(data) {
+      deferred.resolve(new ViewInstance(data));
+    })
+    .error(function(data) {
+      deferred.reject(data);
+    });
+
+    return deferred.promise;
+  };
+
+
+  function View(item){
+    var self = this;
+    self.view_name = item.ViewInfo.view_name;
+    self.versions = '';
+    self.instances = [];
+    angular.forEach(item.versions, function(version) {
+      self.versions += (self.versions ? ', ' : '') + version.ViewVersionInfo.version;
+      angular.forEach(version.instances, function(isntance) {
+        isntance.label = version.ViewVersionInfo.label;
+      })
+      self.instances = self.instances.concat(version.instances);
+    });
+
+    self.versionsList = item.versions;
+  }
+
+  View.getInstance = function(viewName, version, instanceName) {
+    return ViewInstance.find(viewName, version, instanceName);
+  };
+
+  View.deleteInstance = function(viewName, version, instanceName) {
+    return $http.delete(Settings.baseUrl +'/views/'+viewName+'/versions/'+version+'/instances/'+instanceName, {
+      headers: {
+        'X-Requested-By': 'ambari'
+      }
+    });
+  };
+
+  View.updateInstance = function(viewName, version, instanceName, data) {
+    return $http({
+      method: 'PUT',
+      url: Settings.baseUrl + '/views/' +viewName + '/versions/'+version+'/instances/' + instanceName,
+      data: data
+    });
+  };
+
+  View.getPermissions = function(viewName, version) {
+    return $http({
+      method: 'GET',
+      url: Settings.baseUrl + '/views/' + viewName + '/versions/'+ version
+    });
+  };
+
+  View.createInstance = function(instanceInfo) {
+    var deferred = $q.defer();
+    var properties = {};
+
+    angular.forEach(instanceInfo.properties, function(property) {
+      properties[property.name] = property.value
+    });
+
+    $http({
+      method: 'POST',
+      url: Settings.baseUrl + '/views/' + instanceInfo.view_name +'/versions/'+instanceInfo.version + '/instances/'+instanceInfo.instance_name,
+      data:{
+        'ViewInstanceInfo' : {
+          instance_name: instanceInfo.instance_name,
+          label: instanceInfo.label,
+          visible: instanceInfo.visible,
+          icon_path: instanceInfo.icon_path,
+          icon64_path: instanceInfo.icon64_path,
+          properties: properties
+        }
+      }
+    })
+    .success(function(data) {
+      deferred.resolve(data);
+    })
+    .error(function(data) {
+      deferred.reject(data);
+    });
+
+    return deferred.promise;
+  };
+
+  View.all = function() {
+    var deferred = $q.defer();
+    var fields = [
+      'versions/ViewVersionInfo/version',
+      'versions/instances/ViewInstanceInfo',
+      'versions/ViewVersionInfo'
+    ];
+
+    $http({
+      method: 'GET',
+      url: Settings.baseUrl + '/views',
+      params:{
+        'fields': fields.join(',')
+      }
+    }).success(function(data) {
+      var views = [];
+      angular.forEach(data.items, function(item) {
+        views.push(new View(item));
+      });
+      deferred.resolve(views);
+    })
+    .error(function(data) {
+      deferred.reject(data);
+    });
+
+    return deferred.promise;
+  };
+  return View;
+}]);

+ 56 - 0
ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/ldap.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.
+ */
+'use strict';
+
+angular.module('ambariAdminConsole')
+.factory('LDAP', ['$http', '$q', function($http, $q) {
+
+
+  return {
+    get: function(resourceType) {
+      var deferred = $q.defer();
+
+      $http({
+        method: 'GET',
+        url: '/api/v1/controllers/ldap'
+      })
+      .success(function(data) {
+        deferred.resolve(data);
+      })
+      .catch(function(data) {
+        deferred.reject(data);
+      });
+
+      return deferred.promise;
+    },
+    sync: function(resourceType, items) {
+      var items = items.map(function(item) {
+        var name = 'LDAP/synced_' + resourceType;
+        var obj = {};
+        obj['LDAP/synced_' + resourceType] = item;
+        return obj;
+      });
+      
+      return $http({
+        method: 'POST',
+        url: '/api/v1/controllers/ldap',
+        data: items
+      });
+    }
+  };
+}]);

+ 3 - 0
ambari-admin/src/main/resources/ui/admin-web/app/styles/main.css

@@ -33,6 +33,9 @@ ul.nav li > a{
 .right-margin{
   margin-right: 20px;
 }
+.left-margin{
+  margin-left: 10px;
+}
 .bottom-margin{
   margin-bottom: 10px;
 }

+ 47 - 43
ambari-admin/src/main/resources/ui/admin-web/app/views/ambariViews/edit.html

@@ -15,36 +15,47 @@
 * See the License for the specific language governing permissions and
 * limitations under the License.
 -->
-<h2>Edit PIG_CT: Pig for CT Cluster</h2>
+<h2>Edit {{instance.ViewInstanceInfo.view_name}}: {{instance.ViewInstanceInfo.label}}</h2>
 <hr>
 <div class="panel panel-default">
-  <div class="panel-heading">
-    <h3 class="panel-title">Settings</h3>
+  <div class="panel-heading clearfix">
+    <h3 class="panel-title pull-left">Settings</h3>
+    <div class="pull-right">
+      <a href ng-click="editSettingsDisabled = !editSettingsDisabled" ng-show="editSettingsDisabled"> <span class="glyphicon glyphicon-cog"></span> Edit</a>
+    </div>
   </div>
   <div class="panel-body">
     <form class="form-horizontal">
-      <div class="form-group">
-        <label for="" class="col-sm-2 control-label">Instance ID</label>
-        <label for="" class="col-sm-10 control-label text-left">PIG_CT</label>
-      </div>
-      <div class="form-group">
-        <label for="" class="col-sm-2 control-label">Display Name</label>
-        <div class="col-sm-10"><input type="text" class="form-control" placeholder="Display Name"></div>
-      </div>
-      <div class="form-group">
-        <div class="col-sm-offset-2 col-sm-10">
-          <div class="checkbox">
-            <label>
-              <input type="checkbox"> Visible
-            </label>
+      <fieldset ng-disabled="editSettingsDisabled">
+        <div class="form-group">
+          <label for="" class="col-sm-2 control-label">Instance ID</label>
+          <label for="" class="col-sm-10 control-label text-left">{{instance.ViewInstanceInfo.instance_name}}</label>
+        </div>
+        <div class="form-group">
+          <label for="" class="col-sm-2 control-label">Display Name</label>
+          <div class="col-sm-10"><input type="text" class="form-control" placeholder="Display Name" ng-model="settings.label"></div>
+        </div>
+        <div class="form-group">
+          <div class="col-sm-offset-2 col-sm-10">
+            <div class="checkbox">
+              <label>
+                <input type="checkbox" ng-model="settings.visible"> Visible
+              </label>
+            </div>
           </div>
         </div>
-      </div>
+        <div class="form-group" ng-hide="editSettingsDisabled">
+          <div class="col-sm-offset-2 col-sm-10">
+            <button class="btn btn-warning pull-right left-margin" ng-click="cancelSettings()">Cancel</button>
+            <button class="btn btn-success pull-right" ng-click="saveSettings()">Save</button>
+          </div>
+        </div>
+      </fieldset>
     </form>
   </div>
 </div>
 
-<div class="panel panel-default">
+<div class="panel panel-default" style="">
   <div class="panel-heading">
     <h3 class="panel-title">Permissions</h3>
   </div>
@@ -61,39 +72,32 @@
         <div class="col-sm-5"><textarea name="" id="" cols="30" rows="4" class="form-control"></textarea></div>
         <div class="col-sm-5"><textarea name="" id="" cols="30" rows="4" class="form-control"></textarea></div>
       </div>
-      <div class="form-group">
-        <label class="col-sm-2 control-label">EDIT_SCRIPT</label>
-        <div class="col-sm-5"><textarea name="" id="" cols="30" rows="4" class="form-control"></textarea></div>
-        <div class="col-sm-5"><textarea name="" id="" cols="30" rows="4" class="form-control"></textarea></div>
-      </div>
-      <div class="form-group">
-        <label class="control-label col-sm-2">EXECUTE_SCRIPT</label>
-        <div class="col-sm-5"><textarea name="" id="" cols="30" rows="4" class="form-control"></textarea></div>
-        <div class="col-sm-5"><textarea name="" id="" cols="30" rows="4" class="form-control"></textarea></div>
-      </div>
     </form>
       
   </div>
 </div>
 
 <div class="panel panel-default">
-  <div class="panel-heading">
-    <h3 class="panel-title">Configuration</h3>
+  <div class="panel-heading clearfix">
+    <h3 class="panel-title pull-left">Configuration</h3>
+    <div class="pull-right">
+      <a href ng-click="editConfigurationDisabled = !editConfigurationDisabled" ng-show="editConfigurationDisabled"> <span class="glyphicon glyphicon-cog"></span> Edit</a>
+    </div>
   </div>
   <div class="panel-body">
     <form action="" class="form-horizontal">
-      <div class="form-group">
-        <label for="" class="control-label col-sm-2">WebHCat URL</label>
-        <div class="col-sm-10"><input type="text" class="form-control"></div>
-      </div>
-      <div class="form-group">
-        <label for="" class="control-label col-sm-2">Other Prop</label>
-        <div class="col-sm-10"><input type="text" class="form-control"></div>
-      </div>
-      <div class="form-group">
-        <label for="" class="control-label col-sm-2">Required Prop</label>
-        <div class="col-sm-10"><input type="text" class="form-control"></div>
-      </div>
+      <fieldset ng-disabled="editConfigurationDisabled">
+        <div class="form-group" ng-repeat="(propertyName, propertyValue) in configuration">
+          <label for="" class="control-label col-sm-3">{{propertyName}}</label>
+          <div class="col-sm-9"><input type="text" class="form-control" ng-model="configuration[propertyName]"></div>
+        </div>
+        <div class="form-group" ng-hide="editConfigurationDisabled">
+          <div class="col-sm-offset-2 col-sm-10">
+            <button class="btn btn-warning pull-right left-margin" ng-click="cancelConfiguration()">Cancel</button>
+            <button class="btn btn-success pull-right" ng-click="saveConfiguration()">Save</button>
+          </div>
+        </div>
+      </fieldset>
     </form>
   </div>
 </div>

+ 12 - 64
ambari-admin/src/main/resources/ui/admin-web/app/views/ambariViews/list.html

@@ -20,7 +20,7 @@
     <h3 class="pull-left">Views management</h3>
     <div class="pull-right top-buffer col-sm-4">
       <div class="input-group">
-        <input type="text" class="form-control" placeholder="Search">
+        <input type="text" class="form-control" placeholder="Search" ng-model="viewsFilter">
         <span class="input-group-addon">
           <span class="glyphicon glyphicon-search"></span>
         </span>
@@ -29,80 +29,28 @@
   </div>
   <hr>
   <accordion>
-    <accordion-group>
+    <accordion-group ng-repeat="view in views">
       <accordion-heading>
         <div class="row">
-          <div class="col-sm-3">Pig</div>
-          <div class="col-sm-3">0.1.0, 0.2.0</div>
-          <div class="col-sm-3">v 3 instances</div>
+          <div class="col-sm-3">{{view.view_name}}</div>
+          <div class="col-sm-3">{{view.versions}}</div>
+          <div class="col-sm-3">{{view.instances.length ? view.instances.length : 'No'}} instances</div>
           <div class="col-sm-3">This is a description</div>
         </div>
       </accordion-heading>
       <table class="table">
         <tbody>
-          <tr>
-            <td class="col-sm-3"></td>
-            <td class="col-sm-3">0.1.0</td>
-            <td class="col-sm-3">PIG_CT: Pig for CT CLuster</td>
-            <td class="col-sm-3">
-              <link-to route="views.edit" id="1" class="btn btn-default"><span class="glyphicon glyphicon-cog"></span> Edit</link-to>
-              <a href="" class="btn btn-default">
-                <span class="glyphicon glyphicon-trash"></span> Delete
-              </a>
-            </td>
-          </tr>
-          <tr>
+          <tr ng-repeat="instance in view.instances">
             <td class="col-sm-3"></td>
-            <td class="col-sm-3">0.2.0</td>
-            <td class="col-sm-3">PIG_CT: Pig for CT CLuster</td>
+            <td class="col-sm-3">{{instance.ViewInstanceInfo.version}}</td>
+            <td class="col-sm-3">{{instance.ViewInstanceInfo.label}}</td>
             <td class="col-sm-3">
-              <a href="" class="btn btn-default">
-                <span class="glyphicon glyphicon-cog"></span> Edit
-              </a>
-              <a href="" class="btn btn-default">
+              <a href="#/views/{{view.view_name}}/versions/{{instance.ViewInstanceInfo.version}}/instances/{{instance.ViewInstanceInfo.instance_name}}/edit" class="btn btn-default"><span class="glyphicon glyphicon-cog"></span> Edit</a>
+              <a href="" class="btn btn-default" ng-click="deleteInstance(instance)">
                 <span class="glyphicon glyphicon-trash"></span> Delete
               </a>
             </td>
           </tr>
-          <tr>
-            <td class="col-sm-3"></td>
-            <td class="col-sm-3">0.1.0</td>
-            <td class="col-sm-3">PIG_CT: Pig for CT CLuster</td>
-            <td class="col-sm-3">
-              <a href="" class="btn btn-default">
-                <span class="glyphicon glyphicon-cog"></span> Edit
-              </a>
-              <a href="" class="btn btn-default">
-                <span class="glyphicon glyphicon-trash"></span> Delete
-              </a>
-            </td>
-          </tr>
-        </tbody>
-        <tfoot>
-          <tr>
-            <td class="col-sm-3"></td>
-            <td class="col-sm-3"></td>
-            <td class="col-sm-3"></td>
-            <td class="col-sm-3">
-              <a href="" class="btn btn-default">
-                <span class="glyphicon glyphicon-plus"></span> Create Instance
-              </a>
-            </td>
-          </tr>
-        </tfoot>
-      </table>
-    </accordion-group>
-    <accordion-group>
-      <accordion-heading>
-        <div class="row">
-          <div class="col-sm-3">File Browser</div>
-          <div class="col-sm-3">0.1.0, 0.2.0</div>
-          <div class="col-sm-3">> No instances</div>
-          <div class="col-sm-3">This is a description</div>
-        </div>
-      </accordion-heading>
-      <table class="table">
-        <tbody>
         </tbody>
         <tfoot>
           <tr>
@@ -110,8 +58,8 @@
             <td class="col-sm-3"></td>
             <td class="col-sm-3"></td>
             <td class="col-sm-3">
-              <a href="" class="btn btn-default">
-                <span class="glyphicon glyphicon-plus"></span> Create Instance
+              <a href class="btn btn-default" ng-click="createInstance(view)">
+                <span class="glyphicon glyphicon-plus" ></span> Create Instance
               </a>
             </td>
           </tr>

+ 127 - 0
ambari-admin/src/main/resources/ui/admin-web/app/views/ambariViews/modals/create.html

@@ -0,0 +1,127 @@
+<!--
+* 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.
+-->
+<form class="form-horizontal" role="form" name="form.isntanceCreateForm" novalidate>
+<div class="modal-header">
+  <h3 class="modal-title">Create View Instance</h3>
+</div>
+<div class="modal-body createViewModal">
+  <div class="view-header">
+    <img src="http://placehold.it/64x64" alt="" class="icon-big">
+    <img src="http://placehold.it/32x32" alt="" class="icon-small">
+    <div class="description">
+      <h3>{{view.ViewVersionInfo.view_name}}</h3>
+      <span>{{view.ViewVersionInfo.label}} | Version: {{view.ViewVersionInfo.version}}</span>
+    </div>
+  </div>
+  
+    <div class="panel panel-default">
+      <div class="panel-heading">
+        <h3 class="panel-title">Details</h3>
+      </div>
+      <div class="panel-body">
+        <div class="form-group" 
+        ng-class="{'has-error' : ( (form.isntanceCreateForm.instanceNameInput.$error.required || form.isntanceCreateForm.instanceNameInput.$error.pattern) && form.isntanceCreateForm.submitted) || instanceExists }"
+        >
+          <label for="" class="control-labe col-sm-2">Instance name</label>
+          <div class="col-sm-10">
+            <input type="text" class="form-control" name="instanceNameInput" ng-pattern="nameValidationPattern" required ng-model="instance.instance_name">
+
+            <div class="alert alert-danger no-margin-bottom top-margin" ng-show='form.isntanceCreateForm.instanceNameInput.$error.required && form.isntanceCreateForm.submitted'>
+              Field requried!
+            </div>
+            <div class="alert alert-danger no-margin-bottom top-margin" ng-show='form.isntanceCreateForm.instanceNameInput.$error.pattern && form.isntanceCreateForm.submitted'>
+              Must no contain special characters!
+            </div>
+            <div class="alert alert-danger no-margin-bottom top-margin" ng-show='instanceExists'>
+              Instance with this name already exists.
+            </div>
+          </div>
+        </div>
+        <div class="form-group"
+        ng-class="{'has-error' : ( (form.isntanceCreateForm.displayLabel.$error.required || form.isntanceCreateForm.displayLabel.$error.pattern) && form.isntanceCreateForm.submitted)}">
+          <label for="" class="control-labe col-sm-2">Display label</label>
+          <div class="col-sm-10">
+            <input type="text" class="form-control" name="displayLabel" ng-model="instance.label" required ng-pattern="nameValidationPattern">
+
+            <div class="alert alert-danger no-margin-bottom top-margin" ng-show='form.isntanceCreateForm.displayLabel.$error.required && form.isntanceCreateForm.submitted'>
+              Field requried!
+            </div>
+            <div class="alert alert-danger no-margin-bottom top-margin" ng-show='form.isntanceCreateForm.displayLabel.$error.pattern && form.isntanceCreateForm.submitted'>
+              Must no contain special characters!
+            </div>
+          </div>
+        </div>
+      </div>
+      <div class="form-group">
+          <div class="col-sm-10 col-sm-offset-2">
+            <button class="btn btn-default" ng-click="isAdvancedClosed = !isAdvancedClosed">Advanced</button>
+          </div>
+      </div>
+      <div collapse="isAdvancedClosed">
+        <div class="form-group">
+          <div class="col-sm-10 col-sm-offset-2">
+            <div class="checkbox">
+              <label>
+                <input type="checkbox" ng-model='instance.visible'> Visible
+              </label>
+            </div>
+          </div>
+        </div>
+        <div class="form-group">
+          <div class="col-sm-10 col-sm-offset-2">
+            <label for="" class="control-label col-sm-2">Icon</label>
+            <div class="col-sm-10">
+              <input type="text" class="form-control" name="iconUrl" ng-model="instance.icon_path">
+            </div>
+          </div>
+        </div>
+        <div class="form-group">
+          <div class="col-sm-10 col-sm-offset-2">
+            <label for="" class="control-label col-sm-2">Icon64</label>
+            <div class="col-sm-10">
+              <input type="text" class="form-control" name="icon64Url" ng-model="instance.icon64_path">
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+
+    <div class="panel panel-default">
+      <div class="panel-heading">
+        <h3 class="panel-title">Configuration</h3>
+      </div>
+      <div class="panel-body">
+      <div class="form-group" ng-repeat="parameter in instance.properties"
+        ng-class="{'has-error' : (form.isntanceCreateForm[parameter.name].$error.required && form.isntanceCreateForm.submitted)}" >
+        <label for="" class="col-sm-3 control-label">{{parameter.description}}</label>
+        <div class="col-sm-9">
+          <input type="text" class="form-control"  name="{{parameter.name}}" ng-required="parameter.required" ng-model="parameter.value">
+          <div class="alert alert-danger no-margin-bottom top-margin" ng-show='form.isntanceCreateForm[parameter.name].$error.required && form.isntanceCreateForm.submitted'>
+            Field requried!
+          </div>
+        </div>
+      </div>
+      </div>
+    </div>
+  
+</div>
+<div class="modal-footer">
+  <button class="btn btn-warning" ng-click="cancel()">Cancel</button>
+  <button class="btn btn-success" ng-click="save()" type="submit">Save</button>
+</div>
+</form>

+ 1 - 1
ambari-admin/src/main/resources/ui/admin-web/app/views/groups/list.html

@@ -20,7 +20,7 @@
     <h3 class="pull-left">Groups management</h3>
     <div class="pull-right top-buffer">
       <link-to route="groups.create" class="btn btn-primary"><span class="glyphicon glyphicon-plus"></span> Create Local Group</link-to>
-      <button class="btn btn-default ">Sync LDAP</button>
+      <button class="btn btn-default" ng-click="syncLDAP()">Sync LDAP</button>
     </div>
   </div>
   <hr>

+ 47 - 0
ambari-admin/src/main/resources/ui/admin-web/app/views/ldapModal.html

@@ -0,0 +1,47 @@
+<!--
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+-->
+<div class="modal-header">
+  <h3 class="modal-title">LDAP {{resourceType}}</h3>
+</div>
+<div class="modal-body">
+  <div class="alert alert-danger" ng-show="isDataLoaded && !isConfigured">
+    LDAP is not configured.
+  </div>
+  <table class="table table-hover table-striped ldap-table" ng-show="isDataLoaded">
+    <tbody>
+      <tr ng-repeat="item in items">
+        <td class="col-sm-10">{{item.name}}</td>
+        <td class="col-sm-2">
+          <div class="checkbox">
+            <label>
+              <input type="checkbox" ng-model="item.selected">
+            </label>
+          </div>
+        </td>
+      </tr>
+    </tbody>
+  </table>
+  <div class="alert alert-info" ng-show="isConfigured && isDataLoaded && !items">
+    No {{resourceType}} to sync.
+  </div>
+  <progressbar ng-hide="isDataLoaded" class="progress-striped active" max="200" value="200" type="default"></progressbar>
+</div>
+<div class="modal-footer">
+  <button class="btn btn-primary" ng-click="sync()">Sync</button>
+  <button class="btn btn-warning" ng-click="cancel()">Cancel</button>
+</div>

+ 11 - 6
ambari-admin/src/main/resources/ui/admin-web/app/views/leftNavbar.html

@@ -18,18 +18,23 @@
 <div class="panel panel-default">
   <div class="panel-heading"><span class="glyphicon glyphicon-cloud"></span> Clusters</div>
   <div class="panel-body">
-    <h5>My cluster</h5>
+    <div ng-show="cluster">
+      <h5>{{cluster.Clusters.cluster_name}}</h5>
       <ul class="nav nav-pills nav-stacked">
         <li>
           <link-to route="clusters.manageAccess" id="1">Manage Access</link-to>
         </li>
         <li><a href="/#/main/dashboard">Go to Dashboard</a></li>
       </ul>
-    <hr>
-    <button type="button" class="btn btn-primary btn-block">
-      <span class="glyphicon glyphicon-plus"></span>
-      Create a Cluster
-    </button>
+    </div>
+      
+    <div ng-hide="cluster">
+      <a href="/" class="btn btn-primary btn-block">
+        <span class="glyphicon glyphicon-plus"></span>
+        Create a Cluster
+      </a>
+    </div>
+      
   </div>
 </div>
 

+ 1 - 1
ambari-admin/src/main/resources/ui/admin-web/app/views/users/list.html

@@ -20,7 +20,7 @@
     <h3 class="pull-left">Users management</h3>
     <div class="pull-right top-buffer">
       <link-to route="users.create" class="btn btn-primary"><span class="glyphicon glyphicon-plus"></span> Create Local User</link-to>
-      <button class="btn btn-default ">Sync LDAP</button>
+      <button class="btn btn-default" ng-click="syncLDAP()">Sync LDAP</button>
     </div>
   </div>
   <hr>

+ 3 - 1
ambari-web/app/templates/application.hbs

@@ -49,7 +49,9 @@
               </button>
               <ul class="dropdown-menu">
                   <li><a href="" {{action showAboutPopup target="controller"}}>{{t app.aboutAmbari}}</a></li>
-                  <li><a href="/views/ADMIN_VIEW/1.0.0/INSTANCE/#/">{{t app.manageAmbari}}</a></li>
+                  {{#if App.isAdmin }}
+                    <li><a href="/views/ADMIN_VIEW/1.0.0/INSTANCE/#/">{{t app.manageAmbari}}</a></li>
+                  {{/if}}
                 {{#if isClusterDataLoaded}}
                   {{#if App.isAdmin}}
                       <li><a href="" {{action showSettingsPopup target="controller"}}>{{t app.settings}}</a></li>