eagle-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ji...@apache.org
Subject [10/14] incubator-eagle git commit: [EAGLE-574] UI refactor for support 0.5 api
Date Wed, 28 Sep 2016 05:38:51 GMT
http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-server/ui-build.sh
----------------------------------------------------------------------
diff --git a/eagle-server/ui-build.sh b/eagle-server/ui-build.sh
new file mode 100644
index 0000000..9f17ba6
--- /dev/null
+++ b/eagle-server/ui-build.sh
@@ -0,0 +1,40 @@
+#!/bin/bash
+
+# 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.
+
+echo "=============== Web APP Building Start ==============="
+echo "Environment Check..."
+# Pre-build check
+if [ -z "$(command -v git)" ]
+then
+	echo "git not installed!"
+	exit 1
+fi
+if [ -z "$(command -v npm)" ]
+then
+	echo "npm not installed!"
+	exit 1
+fi
+echo "Environment Check...Pass"
+
+# npm install
+cd src/main/webapp/app
+echo "npm install..."
+npm install
+
+# grunt build
+echo "building..."
+npm run build

http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/Gruntfile.js
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/Gruntfile.js b/eagle-webservice/src/main/webapp/Gruntfile.js
deleted file mode 100644
index d2a3a42..0000000
--- a/eagle-webservice/src/main/webapp/Gruntfile.js
+++ /dev/null
@@ -1,175 +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.
-*/
-'use strict';
-
-module.exports = function (grunt) {
-	// Project configuration.
-	grunt.initConfig({
-		pkg: grunt.file.readJSON('package.json'),
-		config: grunt.file.readJSON('grunt.json'),
-
-		jshint: {
-			options: {
-				browser: true,
-				globals: {
-					$: true,
-					jQuery: true,
-					moment: true
-				}
-			},
-			all: [
-				'app/**/*.js'
-			]
-		},
-
-		clean: {
-			build: ['ui/', 'tmp/'],
-			tmp: ['tmp/'],
-			ui: ['ui/']
-		},
-		concat: {
-			app: {
-				src: [
-					'app/public/js/app.js',
-
-					'app/public/js/srv/main.js',
-					'app/public/js/srv/**.js',
-
-					'app/public/js/app.*.js',
-
-					'app/public/js/common.js',
-
-					'app/public/js/components/main.js',
-					'app/public/js/components/**.js',
-					'app/public/js/components/**/**.js',
-
-					'app/public/js/ctrl/main.js',
-					'app/public/js/ctrl/*.js'
-				],
-				dest: 'tmp/public/js/scripts.js'
-			},
-			js: '<%= config.concat.js %>',
-			css: {
-				options: {
-					process: function(src, filepath) {
-						return "@import url(https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,600,700,300italic,400italic,600italic);" +
-						src.replace('@import url(https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,600,700,300italic,400italic,600italic);', '');
-					}
-				},
-				src: '<%= config.concat.css.src %>',
-				dest: '<%= config.concat.css.dest %>'
-			}
-		},
-		'regex-replace': {
-			strict: {
-				src: ['tmp/public/js/scripts.js'],
-				actions: [
-					{
-						name: 'use strict',
-						search: '\\\'use strict\\\';?',
-						replace: '',
-						flags: 'gmi'
-					},
-					{
-						name: 'build timestamp',
-						search: '\\/\\/ GRUNT REPLACEMENT\\: eagleApp\\.buildTimestamp \\= TIMESTAMP',
-						replace: 'eagleApp.buildTimestamp = ' + (+new Date()) + ';',
-						flags: 'gmi'
-					}
-				]
-			}
-		},
-		uglify: {
-			ui: {
-				options: {
-					mangle: false
-				},
-				files: [
-					{
-						src: 'tmp/public/js/scripts.js',
-						dest: 'tmp/public/js/scripts.min.js'
-					},
-					{
-						expand: true,
-						src: '**/*.js',
-						dest: 'tmp/feature',
-						cwd: 'app/public/feature'
-					}
-				]
-			}
-		},
-		cssmin: {
-			ui: {
-				files: {
-					'tmp/public/css/styles.css': ['app/public/css/main.css', 'app/public/css/animation.css']
-				}
-			}
-		},
-		htmlrefs: {
-			ui: {
-				src: 'app/index.html',
-				dest: "tmp/index.html"
-			}
-		},
-		copy: {
-			feature: {
-				files: [
-					{expand: true, cwd: 'app/', src: ['public/feature/**'], dest: 'tmp'}
-				]
-			},
-			ui: {
-				files: [
-					{expand: true, cwd: 'tmp/', src: ['**'], dest: 'ui'},
-					{expand: true, cwd: 'app/', src: ['public/images/**', 'partials/**'], dest: 'ui'},
-					{expand: true, cwd: 'node_modules/font-awesome/', src: ['fonts/**'], dest: 'ui/public'},
-					{expand: true, cwd: 'node_modules/bootstrap/', src: ['fonts/**'], dest: 'ui/public'}
-				]
-			}
-		}
-	});
-
-	grunt.loadNpmTasks('grunt-contrib-jshint');
-	grunt.loadNpmTasks('grunt-contrib-clean');
-	grunt.loadNpmTasks('grunt-contrib-concat');
-	grunt.loadNpmTasks('grunt-contrib-uglify');
-	grunt.loadNpmTasks('grunt-contrib-cssmin');
-	grunt.loadNpmTasks('grunt-htmlrefs');
-	grunt.loadNpmTasks('grunt-regex-replace');
-	grunt.loadNpmTasks('grunt-contrib-copy');
-
-	grunt.registerTask('default', [
-		// jshint
-		'jshint:all',
-		// Clean Env
-		'clean:build',
-		// Compress JS
-		'copy:feature',
-		'concat:app',
-		'regex-replace:strict',
-		'uglify',
-		'concat:js',
-		// Compress CSS
-		'cssmin',
-		'concat:css',
-		// Pass HTML Resources
-		'htmlrefs',
-		'copy:ui',
-		// Clean Env
-		'clean:tmp'
-	]);
-};
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/README.md
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/README.md b/eagle-webservice/src/main/webapp/README.md
deleted file mode 100644
index b4168d5..0000000
--- a/eagle-webservice/src/main/webapp/README.md
+++ /dev/null
@@ -1,4 +0,0 @@
-Apache Eagle Web APP
-==
-
-Web client for Apache Eagle
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/_app/index.html
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/_app/index.html b/eagle-webservice/src/main/webapp/_app/index.html
new file mode 100644
index 0000000..7cd3e25
--- /dev/null
+++ b/eagle-webservice/src/main/webapp/_app/index.html
@@ -0,0 +1,281 @@
+<!DOCTYPE html>
+<!--
+  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.
+  -->
+
+<html ng-app="eagleApp" ng-controller="MainCtrl">
+	<head>
+		<meta http-equiv="X-UA-Compatible" content="IE=Edge" />
+		<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+		<meta charset="UTF-8">
+		<meta content='width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no' name='viewport'>
+		<link rel="shortcut icon" href="public/images/favicon.png">
+
+		<title>Eagle</title>
+		<link rel="shortcut icon" type="image/png" href="public/images/favicon.png">
+
+		<!-- ref:css public/css/styles.min.css -->
+		<link href="public/css/main.css" rel="stylesheet" type="text/css" media="screen">
+		<link href="public/css/animation.css" rel="stylesheet" type="text/css" media="screen">
+		<link href="../node_modules/bootstrap/dist/css/bootstrap.css" rel="stylesheet" type="text/css" media="screen">
+		<link href="../node_modules/zombiej-bootstrap-components/bootstrap-components/css/bootstrap-components.css" rel="stylesheet" type="text/css" media="screen">
+		<link href="../node_modules/zombiej-nvd3/build/nv.d3.css" rel="stylesheet" type="text/css" />
+		<link href="../node_modules/font-awesome/css/font-awesome.css" rel="stylesheet" type="text/css" />
+		<link href="../node_modules/admin-lte/dist/css/AdminLTE.css" rel="stylesheet" type="text/css" />
+		<link href="../node_modules/admin-lte/dist/css/skins/skin-blue.css" rel="stylesheet" type="text/css" />
+		<!-- endref -->
+	</head>
+	<body class="skin-blue sidebar-mini" ng-class="{'no-sidebar' : PageConfig.hideSidebar}">
+		<!-- Site wrapper -->
+		<div class="wrapper">
+			<header class="main-header">
+				<a href="#/" class="logo">
+					<span class="logo-mini"><img src="public/images/favicon_white.png" /></span>
+					<span class="logo-lg">Apache Eagle</span>
+				</a>
+				<!-- Header Navbar: style can be found in header.less -->
+				<nav class="navbar navbar-static-top" role="navigation">
+					<!-- Sidebar toggle button-->
+					<a ng-hide="PageConfig.hideSidebar" class="sidebar-toggle" data-toggle="offcanvas" role="button">
+						<span class="sr-only">Toggle navigation</span>
+						<span class="icon-bar"></span>
+						<span class="icon-bar"></span>
+						<span class="icon-bar"></span>
+					</a>
+
+					<div class="navbar-custom-menu">
+						<ul class="nav navbar-nav">
+							<!-- Admin error list -->
+							<li class="dropdown" ng-show="ServiceError.list.length">
+								<a class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
+									<i class="fa fa-exclamation-triangle" ng-class="{blink: ServiceError.hasUnread}"></i>
+								</a>
+								<ul class="dropdown-menu">
+									<li ng-repeat="error in ServiceError.list">
+										<a ng-click="ServiceError.showError(error);">
+											<span class="fa" ng-class="{'fa-envelope': !error._read, 'fa-check': error._read}"></span>
+											{{error.title}}
+										</a>
+									</li>
+									<li role="separator" class="divider"></li>
+									<li>
+										<a ng-click="ServiceError.clearAll();">
+											<span class="fa fa-trash"></span>
+											Clear All
+										</a>
+									</li>
+								</ul>
+							</li>
+
+							<!-- Site -->
+							<li class="dropdown" ng-show="!PageConfig.hideSite && !PageConfig.lockSite">
+								<a class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
+									<i class="fa fa-server"></i>
+									{{Site.current().tags.site}}
+									<i class="fa fa-caret-down"></i>
+								</a>
+								<ul class="dropdown-menu">
+									<li ng-repeat="_site in Site.list" ng-if="_site.enabled">
+										<a ng-click="Site.current(_site);">
+											<span class="fa fa-database"></span> {{_site.tags.site}}
+										</a>
+									</li>
+								</ul>
+							</li>
+							<li class="dropdown" ng-show="PageConfig.lockSite">
+								<a>
+									<i class="fa fa-server"></i>
+									{{Site.current().tags.site}}
+								</a>
+							</li>
+
+							<!-- User -->
+							<li class="dropdown user user-menu" ng-hide="PageConfig.hideUser">
+								<a class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
+									<i class="fa fa-user"></i>
+									{{Auth.userProfile.username}}
+								</a>
+								<ul class="dropdown-menu">
+									<!-- User image -->
+									<li class="user-header">
+										<span class="img-circle">
+											<span class="fa fa-user" alt="User Image"></span>
+										</span>
+										<p>
+											{{Auth.userProfile.username}}
+											<small>
+												<span ng-repeat="role in Auth.userProfile.authorities">{{role.authority}} </span>
+											</small>
+										</p>
+									</li>
+									<!-- Menu Footer-->
+									<li class="user-footer">
+										<div class="pull-left" ng-if="Auth.isRole('ROLE_ADMIN')">
+											<a href="#/config/site" class="btn btn-default btn-flat">Management</a>
+										</div>
+										<div class="pull-right">
+											<a ng-click="logout();" class="btn btn-default btn-flat">Sign out</a>
+										</div>
+									</li>
+								</ul>
+							</li>
+						</ul>
+					</div>
+
+					<!-- Applications -->
+					<div ng-hide="PageConfig.hideApplication">
+						<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#moduleMenu">
+							<span class="sr-only">Toggle navigation</span>
+							<span class="fa fa-map"></span>
+						</button>
+						<div class="collapse navbar-collapse" id="moduleMenu">
+							<ul class="nav navbar-nav">
+								<li ng-repeat="_grp in Site.current().applicationGroupList" ng-if="_grp.enabledList.length"
+									class="dropdown" ng-class="{active: Application.current().group === _grp.name}">
+									<a class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
+										{{_grp.name}}
+									</a>
+									<ul class="dropdown-menu">
+										<li ng-repeat="_app in _grp.enabledList">
+											<a ng-click="Application.current(_app);">
+												<span class="fa fa-cubes"></span> {{_app.displayName}}
+											</a>
+										</li>
+									</ul>
+								</li>
+							</ul>
+						</div>
+					</div>
+				</nav>
+			</header>
+
+			<!-- =============================================== -->
+			<!-- Left side column. contains the side bar -->
+			<aside class="main-sidebar" ng-hide="PageConfig.hideSidebar">
+				<!-- side bar: style can be found in sidebar.less -->
+				<section class="sidebar">
+					<ul class="sidebar-menu">
+						<li class="header">
+							{{Application.current().group || 'Application'}} >
+							{{Application.current().displayName || 'Features'}}
+						</li>
+						<li ng-repeat="page in PageConfig.navConfig.pageList track by $index" ng-class="getNavClass(page)" ng-show="getNavVisible(page)">
+							<a href="{{page.url}}">
+								<i class="fa fa-{{page.icon}}"></i> <span>{{page.title}}</span> 
+							</a>
+						</li>
+					</ul>
+				</section>
+				<!-- /.sidebar -->
+			</aside>
+
+			<!-- =============================================== -->
+			<!-- Right side column. Contains the navbar and content of the page -->
+			<div class="content-wrapper">
+				<!-- Content Header (Page header) -->
+				<section class="content-header" ng-hide="PageConfig.hideSidebar">
+					<h1>
+						<span class="pageTitle">{{PageConfig.pageTitle}}</span>
+						<small class="pageSubTitle">{{PageConfig.pageSubTitle}}</small>
+					</h1>
+
+
+					<ol class="breadcrumb">
+						<li ng-repeat="navPath in PageConfig.navPath">
+							<a ng-href="#{{navPath.path}}">
+								<span class="fa fa-home" ng-if="$first"></span>
+								{{navPath.title || navPath.path}}
+							</a>
+						</li>
+					</ol>
+				</section>
+
+				<!-- Main content -->
+				<section class="content">
+					<div id="content">
+						<div ui-view></div>
+					</div>
+				</section><!-- /.content -->
+			</div><!-- /.content-wrapper -->
+
+			<footer class="main-footer">
+				<div class="pull-right hidden-xs">
+					<b>License</b>
+					<a href="http://www.apache.org/licenses/LICENSE-2.0" class="text-muted">Apache-2.0</a>
+				</div>
+				<strong>
+					Apache Eagle
+					<a target="_blank" href="https://eagle.incubator.apache.org/">Home</a> /
+					<a target="_blank" href="https://eagle.incubator.apache.org/docs/community.html">Community</a> /
+					<a target="_blank" href="https://cwiki.apache.org/confluence/display/EAG/FAQ">FAQ</a>
+				</strong>
+			</footer>
+		</div><!-- ./wrapper -->
+
+		<!-- ref:js public/js/doc.js -->
+		<script src="../node_modules/jquery/dist/jquery.js"></script>
+		<script src="../node_modules/jquery-slimscroll/jquery.slimscroll.min.js"></script>
+		<script src="../node_modules/bootstrap/dist/js/bootstrap.min.js"></script>
+		<script src="../node_modules/zombiej-bootstrap-components/bootstrap-components/js/bootstrap-components.min.js"></script>
+		<script src="../node_modules/moment/min/moment-with-locales.min.js"></script>
+		<script src="../node_modules/moment-timezone/builds/moment-timezone-with-data.min.js"></script>
+		<script src="../node_modules/admin-lte/dist/js/app.min.js"></script>
+		<script src="../node_modules/angular/angular.js"></script>
+		<script src="../node_modules/angular-resource/angular-resource.js"></script>
+		<script src="../node_modules/angular-route/angular-route.js"></script>
+		<script src="../node_modules/angular-animate/angular-animate.js"></script>
+		<script src="../node_modules/angular-ui-bootstrap/dist/ui-bootstrap-tpls.js"></script>
+		<script src="../node_modules/angular-ui-router/release/angular-ui-router.js"></script>
+		<script src="../node_modules/d3/d3.js"></script>
+		<script src="../node_modules/zombiej-nvd3/build/nv.d3.js"></script>
+
+		<!-- Application -->
+		<script src="public/js/app.js" type="text/javascript" charset="utf-8"></script>
+
+		<!-- Service -->
+		<script src="public/js/srv/main.js" type="text/javascript" charset="utf-8"></script>
+		<script src="public/js/srv/applicationSrv.js" type="text/javascript" charset="utf-8"></script>
+		<script src="public/js/srv/authorizationSrv.js" type="text/javascript" charset="utf-8"></script>
+		<script src="public/js/srv/entitiesSrv.js" type="text/javascript" charset="utf-8"></script>
+		<script src="public/js/srv/siteSrv.js" type="text/javascript" charset="utf-8"></script>
+		<script src="public/js/srv/pageSrv.js" type="text/javascript" charset="utf-8"></script>
+		<script src="public/js/srv/wrapStateSrv.js" type="text/javascript" charset="utf-8"></script>
+		<script src="public/js/srv/uiSrv.js" type="text/javascript" charset="utf-8"></script>
+
+		<!-- Misc -->
+		<script src="public/js/app.ui.js" type="text/javascript" charset="utf-8"></script>
+		<script src="public/js/app.time.js" type="text/javascript" charset="utf-8"></script>
+		<script src="public/js/app.config.js" type="text/javascript" charset="utf-8"></script>
+
+		<script src="public/js/common.js" type="text/javascript" charset="utf-8"></script>
+
+		<!-- Components -->
+		<script src="public/js/components/main.js" type="text/javascript" charset="utf-8"></script>
+		<script src="public/js/components/sortTable.js" type="text/javascript" charset="utf-8"></script>
+		<script src="public/js/components/tabs.js" type="text/javascript" charset="utf-8"></script>
+		<script src="public/js/components/file.js" type="text/javascript" charset="utf-8"></script>
+		<script src="public/js/components/charts/line3d.js" type="text/javascript" charset="utf-8"></script>
+		<script src="public/js/components/nvd3.js" type="text/javascript" charset="utf-8"></script>
+		<script src="public/js/components/sortable.js" type="text/javascript" charset="utf-8"></script>
+
+		<!-- Controllers -->
+		<script src="public/js/ctrl/main.js" type="text/javascript" charset="utf-8"></script>
+		<script src="public/js/ctrl/authController.js" type="text/javascript" charset="utf-8"></script>
+		<script src="public/js/ctrl/configurationController.js" type="text/javascript" charset="utf-8"></script>
+		<!-- endref -->
+	</body>
+</html>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/_app/partials/config/application.html
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/_app/partials/config/application.html b/eagle-webservice/src/main/webapp/_app/partials/config/application.html
new file mode 100644
index 0000000..0bf194c
--- /dev/null
+++ b/eagle-webservice/src/main/webapp/_app/partials/config/application.html
@@ -0,0 +1,124 @@
+<!--
+  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="box box-info">
+	<div class="box-header with-border">
+		<h3 class="box-title">
+			<span class="fa fa-cogs"></span>
+			Configuration
+			<small class="text-danger" ng-show="changed">
+				<span class="label label-warning label-sm">Unsaved</span>
+			</small>
+		</h3>
+	</div><!-- /.box-header -->
+
+	<div class="box-body">
+		<div class="row">
+			<div class="col-md-3">
+				<ul class="nav nav-pills nav-stacked">
+					<li class="disabled"><a>Application</a></li>
+					<li role="presentation" ng-repeat="_application in Application.list track by $index" ng-class="{active: application === _application}">
+						<a ng-click="setApplication(_application)">
+							<span class="fa fa-server"></span>
+							{{_application.tags.application}}
+							<span ng-if="_application.alias">({{_application.alias}})</span>
+						</a>
+					</li>
+
+					<li>
+						<a class="text-light-blue" ng-click="newApplication()" ng-disabled="_pageLock">
+							<span class="fa fa-plus-square"></span>
+							New Application
+						</a>
+					</li>
+				</ul>
+			</div>
+
+			<div class="col-md-9">
+				<a class="pull-right btn btn-danger btn-xs" ng-click="deleteApplication(application)" ng-disabled="_pageLock">
+					<span class="fa fa-trash-o"></span>
+					Delete Application
+				</a>
+
+				<!-- Title -->
+				<h3 class="guideline">
+					Application
+					<small>{{application.tags.application}}</small>
+				</h3>
+				<hr/>
+
+				<!-- Config -->
+				<div class="form-group">
+					<label for="displayName">Display Name</label>
+					<input type="text" class="form-control" id="displayName" placeholder="(Optional) Display name." ng-model="applications[application.tags.application].alias">
+				</div>
+				<div class="form-group">
+					<label for="applicationGroup">Group</label>
+					<input type="text" class="form-control" id="applicationGroup" placeholder="(Optional) Group name" ng-model="applications[application.tags.application].groupName">
+				</div>
+				<div class="form-group">
+					<label for="applicationDescription">Description</label>
+					<textarea id="applicationDescription" class="form-control" placeholder="(Optional) Application description" rows="2" ng-model="applications[application.tags.application].description"></textarea>
+				</div>
+				<div class="form-group">
+					<label for="applicationConfiguration">Configuration</label>
+					<span class="text-danger">{{configCheck(applications[application.tags.application].config)}}</span>
+					<textarea id="applicationConfiguration" class="form-control" placeholder="Application configuration. Feature can read this " rows="5" ng-model="applications[application.tags.application].config"></textarea>
+				</div>
+
+				<!-- Feature -->
+				<label>* Feature</label>
+				<div class="row">
+					<div class="col-sm-6">
+						<h1 class="text-muted text-center" ng-show="applications[application.tags.application].features.length === 0">No feature in using</h1>
+						<ul class="products-list product-list-in-box fixed-height" ng-show="applications[application.tags.application].features.length !== 0">
+							<li class="item" ng-repeat="feature in applications[application.tags.application].features track by $index" ng-class="{active: _feature === feature}">
+								<div class="product-operation">
+									<a class="fa fa-chevron-up" ng-click="moveFeature(feature, applications[application.tags.application].features, -1)"></a>
+									<a class="fa fa-chevron-down" ng-click="moveFeature(feature, applications[application.tags.application].features, 1)"></a>
+								</div>
+								<div class="product-info">
+									<a class="fa fa-times pull-right" ng-click="removeFeature(feature, applications[application.tags.application])"></a>
+									<span class="product-title">{{feature}}</span>
+									<span class="product-description">{{Application.featureList.set[feature].description}}</span>
+								</div>
+							</li>
+						</ul>
+					</div>
+					<div class="col-sm-6">
+						<ul class="products-list product-list-in-box fixed-height">
+							<li class="item" ng-repeat="feature in applications[application.tags.application].optionalFeatures track by $index">
+								<button class="btn btn-lg btn-primary pull-left" ng-click="addFeature(feature, applications[application.tags.application])" ng-disabled="_pageLock">
+									<span class="fa fa-star-o"></span>
+								</button>
+								<div class="product-info">
+									<span class="product-title">{{feature}}</span>
+									<span class="product-description">{{Application.featureList.set[feature].description}}</span>
+								</div>
+							</li>
+						</ul>
+					</div>
+				</div>
+			</div>
+		</div>
+	</div><!-- /.box-body -->
+
+	<div class="box-footer clearfix">
+		<button class="btn btn-primary" ng-click="saveAll()" ng-disabled="_pageLock">Save All</button>
+	</div>
+</div>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/_app/partials/config/feature.html
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/_app/partials/config/feature.html b/eagle-webservice/src/main/webapp/_app/partials/config/feature.html
new file mode 100644
index 0000000..945d90b
--- /dev/null
+++ b/eagle-webservice/src/main/webapp/_app/partials/config/feature.html
@@ -0,0 +1,85 @@
+<!--
+  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="box box-info">
+	<div class="box-header with-border">
+		<h3 class="box-title">
+			<span class="fa fa-cogs"></span>
+			Configuration
+			<small class="text-danger" ng-show="changed">
+				<span class="label label-warning label-sm">Unsaved</span>
+			</small>
+		</h3>
+	</div><!-- /.box-header -->
+
+	<div class="box-body">
+		<div class="row">
+			<div class="col-md-3">
+				<ul class="nav nav-pills nav-stacked">
+					<li class="disabled">
+						<a>Feature</a>
+					</li>
+					<li role="presentation" ng-repeat="_feature in Application.featureList" ng-class="{active: feature === _feature}">
+						<a ng-click="setFeature(_feature)">
+							<span class="fa fa-leaf" ng-class="{'text-danger': _feature._loaded === false}" uib-tooltip="Module load failed!" tooltip-enable="_feature._loaded === false"></span>
+							{{_feature.tags.feature}}
+						</a>
+					</li>
+					<li>
+						<a class="text-light-blue" ng-click="newFeature()" ng-disabled="_pageLock">
+							<span class="fa fa-plus-square"></span>
+							New Feature
+						</a>
+					</li>
+				</ul>
+			</div>
+
+			<div class="col-md-9">
+				<a class="pull-right btn btn-danger btn-xs" ng-click="deleteFeature(feature)" ng-disabled="_pageLock">
+					<span class="fa fa-trash-o"></span>
+					Delete Feature
+				</a>
+
+				<h3 class="guideline">
+					<span class="fa fa-exclamation-triangle text-danger" uib-tooltip="Module load failed!" ng-show="feature._loaded === false"></span>
+					{{feature.tags.feature}}
+				</h3>
+				<hr/>
+
+				<p class="text text-muted">
+					Will load the start up file <code>controller.js</code> from <code>public/feature/{{feature.tags.feature}}</code>.
+					If you are developing customized feature, please reference provided feature.
+				</p>
+
+				<!-- Config -->
+				<div class="form-group">
+					<label for="featureVersion">Version</label>
+					<input id="featureVersion" type="text" class="form-control" placeholder="(Optional) Feature version." ng-model="features[feature.tags.feature].version">
+				</div>
+				<div class="form-group">
+					<label for="featureDescription">Description</label>
+					<textarea id="featureDescription" class="form-control" placeholder="(Optional) Feature description." rows="10" ng-model="features[feature.tags.feature].description"></textarea>
+				</div>
+			</div>
+		</div>
+	</div><!-- /.box-body -->
+
+	<div class="box-footer clearfix">
+		<button class="btn btn-primary" ng-click="saveAll()" ng-disabled="_pageLock">Save All</button>
+	</div>
+</div>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/_app/partials/config/site.html
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/_app/partials/config/site.html b/eagle-webservice/src/main/webapp/_app/partials/config/site.html
new file mode 100644
index 0000000..f7d43eb
--- /dev/null
+++ b/eagle-webservice/src/main/webapp/_app/partials/config/site.html
@@ -0,0 +1,115 @@
+<!--
+  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="box box-info">
+	<div class="box-header with-border">
+		<h3 class="box-title">
+			<span class="fa fa-cogs"></span>
+			Configuration
+			<small class="text-danger" ng-show="changed">
+				<span class="label label-warning label-sm">Unsaved</span>
+			</small>
+		</h3>
+	</div><!-- /.box-header -->
+
+	<div class="box-body">
+		<div class="row">
+			<div class="col-md-3">
+				<ul class="nav nav-pills nav-stacked">
+					<li class="disabled"><a>Site</a></li>
+					<li role="presentation" ng-repeat="_site in Site.list track by $index" ng-class="{active: site === _site}">
+						<a ng-click="setSite(_site)">
+							<span class="fa fa-server"></span>
+							{{_site.tags.site}}
+						</a>
+					</li>
+
+					<li>
+						<a class="text-light-blue" ng-click="newSite()" ng-disabled="_pageLock">
+							<span class="fa fa-plus-square"></span>
+							New Site
+						</a>
+					</li>
+				</ul>
+			</div>
+
+			<div class="col-md-9">
+				<a class="pull-right btn btn-danger btn-xs" ng-click="deleteSite(site)" ng-disabled="_pageLock">
+					<span class="fa fa-trash-o"></span>
+					Delete Site
+				</a>
+
+				<!-- Title -->
+				<h3 class="guideline">
+					Site
+					<small>{{site.tags.site}}</small>
+				</h3>
+				<hr/>
+
+				<!-- Config -->
+				<div class="checkbox">
+					<label>
+						<input type="checkbox" ng-checked="sites[site.tags.site].enabled" ng-click="sites[site.tags.site].enabled = !sites[site.tags.site].enabled">
+						<strong>Enabled</strong>
+					</label>
+				</div>
+				<hr/>
+
+				<!-- Application -->
+				<label>* Application</label>
+				<div class="row">
+					<div class="col-sm-6">
+						<h1 class="text-muted text-center" ng-show="sites[site.tags.site].applications.length === 0">No application in using</h1>
+						<ul class="products-list product-list-in-box fixed-height" ng-show="sites[site.tags.site].applications.length !== 0">
+							<li class="item" ng-repeat="application in sites[site.tags.site].applications track by $index" ng-class="{active: _application === application}">
+								<div class="product-operation single">
+									<span class="fa fa-cubes"></span>
+								</div>
+								<div class="product-info">
+									<a class="fa fa-times pull-right" ng-click="removeApplication(application, sites[site.tags.site])"></a>
+									<span class="product-title">
+										<a class="fa fa-cog" ng-click="setApplication(application)"></a>
+										{{application.tags.application}}
+									</span>
+									<span class="product-description">{{Application.list.set[application.tags.application].description}}</span>
+								</div>
+							</li>
+						</ul>
+					</div>
+					<div class="col-sm-6">
+						<ul class="products-list product-list-in-box fixed-height">
+							<li class="item" ng-repeat="application in sites[site.tags.site].optionalApplications track by $index">
+								<button class="btn btn-lg btn-primary pull-left" ng-click="addApplication(application, sites[site.tags.site])" ng-disabled="_pageLock">
+									<span class="fa fa-star-o"></span>
+								</button>
+								<div class="product-info">
+									<span class="product-title">{{application.tags.application}}</span>
+									<span class="product-description">{{Application.list.set[application.tags.application].description}}</span>
+								</div>
+							</li>
+						</ul>
+					</div>
+				</div>
+			</div>
+		</div>
+	</div><!-- /.box-body -->
+
+	<div class="box-footer clearfix">
+		<button class="btn btn-primary" ng-click="saveAll()" ng-disabled="_pageLock">Save All</button>
+	</div>
+</div>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/_app/partials/landing.html
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/_app/partials/landing.html b/eagle-webservice/src/main/webapp/_app/partials/landing.html
new file mode 100644
index 0000000..a2e0f47
--- /dev/null
+++ b/eagle-webservice/src/main/webapp/_app/partials/landing.html
@@ -0,0 +1,30 @@
+<!--
+  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.
+  -->
+
+<p class="lead">
+	<span ng-if="!Application.current()">Current site do not use any application.</span>
+	<span ng-if="Application.current()">Current application do not install any feature.</span>
+
+	<span ng-if="Auth.isRole('ROLE_ADMIN')">
+		Click
+		<a href="#/config/site" ng-if="!Application.current()">here</a>
+		<a href="#/config/application" ng-if="Application.current()">here</a>
+		to configure.
+	</span>
+	<span ng-if="!Auth.isRole('ROLE_ADMIN')">Please contact your admin.</span>
+</p>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/_app/partials/login.html
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/_app/partials/login.html b/eagle-webservice/src/main/webapp/_app/partials/login.html
new file mode 100644
index 0000000..7faef42
--- /dev/null
+++ b/eagle-webservice/src/main/webapp/_app/partials/login.html
@@ -0,0 +1,54 @@
+<!--
+  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="login-box">
+	<div class="login-logo">
+		<a href="#/">Apache Eagle</a>
+	</div>
+
+	<div class="login-box-body" ng-show="!loginSuccess">
+		<p class="login-box-msg">Sign in to start your session</p>
+		<div class="form-group has-feedback">
+			<input type="text" class="form-control" placeholder="User Name" ng-model="username" ng-keypress="login($event)" autocomplete="off" id="username">
+			<span class="glyphicon glyphicon-user form-control-feedback"></span>
+		</div>
+		<div class="form-group has-feedback">
+			<input type="password" class="form-control" placeholder="Password" ng-model="password" ng-keypress="login($event)">
+			<span class="glyphicon glyphicon-lock form-control-feedback"></span>
+		</div>
+		<div class="row">
+			<div class="col-xs-8">
+				<div class="checkbox">
+					<label> <input type="checkbox" ng-checked="rememberUser" ng-click="rememberUser = !rememberUser;" /> Remember Me
+					</label>
+				</div>
+			</div>
+			<div class="col-xs-4">
+				<button class="btn btn-primary btn-block btn-flat" ng-click="login($event, true)" ng-disabled="lock">Sign In</button>
+			</div>
+		</div>
+	</div>
+
+	<div class="login-box-body text-center" ng-show="loginSuccess">
+		<p class="login-box-msg">Login success</p>
+		<p>
+			<span class="fa fa-refresh fa-spin"></span>
+			Loading environment...
+		</p>
+	</div>
+</div>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/_app/public/css/animation.css
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/_app/public/css/animation.css b/eagle-webservice/src/main/webapp/_app/public/css/animation.css
new file mode 100644
index 0000000..954bd29
--- /dev/null
+++ b/eagle-webservice/src/main/webapp/_app/public/css/animation.css
@@ -0,0 +1,46 @@
+@CHARSET "UTF-8";
+/*
+ * 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.
+ */
+
+[ui-view].ng-enter, [ui-view].ng-leave {
+	position: absolute;
+	left: 0;
+	right: 0;
+	-webkit-transition: all .5s ease-in-out;
+	-moz-transition: all .5s ease-in-out;
+	-o-transition: all .5s ease-in-out;
+	transition: all .3s ease-in-out;
+}
+
+[ui-view].ng-enter {
+	opacity: 0;
+}
+
+[ui-view].ng-enter-active {
+	opacity: 1;
+}
+
+[ui-view].ng-leave {
+	opacity: 1;
+	transform:translate3d(0, 0, 0);
+}
+
+[ui-view].ng-leave-active {
+	opacity: 0;
+	transform:translate3d(20%, 0, 0);
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/_app/public/css/main.css
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/_app/public/css/main.css b/eagle-webservice/src/main/webapp/_app/public/css/main.css
new file mode 100644
index 0000000..a7eba4b
--- /dev/null
+++ b/eagle-webservice/src/main/webapp/_app/public/css/main.css
@@ -0,0 +1,805 @@
+@CHARSET "UTF-8";
+/*
+ * 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.
+ */
+
+/* Frame */
+body.no-sidebar .content-wrapper {
+	margin-left: 0;
+
+	-webkit-transition: none;
+	-moz-transition: none;
+	-o-transition: none;
+	transition: none;
+}
+
+body.no-sidebar .main-footer {
+	margin-left: 0;
+}
+
+/* Navigation */
+.navbar-nav > .user-menu > .dropdown-menu > li.user-header .img-circle {
+	display: inline-block;
+	border: 3px solid;
+	border-color: rgba(255,255,255,0.2);
+	width: 90px;
+	height: 90px;
+	margin-top: 10px;
+}
+
+.navbar-nav > .user-menu > .dropdown-menu > li.user-header .fa {
+	font-size: 60px;
+	color: rgba(255,255,255,0.8);
+	margin-top: 10px;
+}
+
+	/* Common */
+a {
+	cursor: pointer;
+}
+
+/* Table */
+.table.table-sm>tbody>tr>td,
+.table.table-sm>tbody>tr>th,
+.table.table-sm>tfoot>tr>td,
+.table.table-sm>tfoot>tr>th,
+.table.table-sm>thead>tr>td,
+.table.table-sm>thead>tr>th{
+	padding: 3px 8px;
+}
+
+.table thead th .fa.fa-sort,
+.table thead th .fa.fa-sort-asc,
+.table thead th .fa.fa-sort-desc {
+	margin-top: 5px;
+	opacity: 0.3;
+	float: right;
+}
+.table thead th:hover .fa.fa-sort,
+.table thead th:hover .fa.fa-sort-asc,
+.table thead th:hover .fa.fa-sort-desc {
+	opacity: 0.8;
+}
+
+.table tr th,
+.table tr td {
+	-webkit-transition: background .5s linear;
+	-o-transition: background .5s linear;
+	transition: background .5s linear;
+}
+
+.sortTable-cntr .pagination {
+	margin-top: 0;
+}
+
+.table th.input-field,
+.table td.input-field {
+	padding: 0;
+	vertical-align: middle;
+}
+
+.table th.input-field > input,
+.table td.input-field > input,
+.table th.input-field > select,
+.table td.input-field > select {
+	border: none;
+	transition: border-color 0s;
+}
+
+.table th.input-field > input:focus,
+.table td.input-field > input:focus,
+.table th.input-field > select:focus,
+.table td.input-field > select:focus {
+	box-shadow: inset 1px 1px 0px #3c8dbc, inset -1px -1px 0px #3c8dbc;
+}
+
+.table th.input-field > input.has-warning,
+.table td.input-field > input.has-warning {
+	box-shadow: inset 1px 1px 0px #f39c12, inset -1px -1px 0px #f39c12;
+}
+
+/* Box */
+.small-box > a.inner {
+	color: #FFF;
+	display: block;
+}
+
+.small-box > a.inner h3 {
+	overflow: hidden;
+	white-space: nowrap;
+	text-overflow: ellipsis;
+	font-size: 32px;
+}
+
+.info-box.bg-gray,
+.info-box a {
+	color: #FFFFFF;
+}
+.info-box a:hover {
+	color: #FFFFFF;
+	text-decoration: underline;
+}
+
+.info-box-content a.config {
+	color: rgba(255,255,255,0.8);
+}
+.info-box-content a.config:hover {
+	color: #FFFFFF;
+}
+
+.info-box-content.box-clickable {
+	box-shadow: 0 0 3px;
+}
+.box-clickable {
+	cursor: pointer;
+}
+
+.info-box-content .info-box-text.text-large {
+	font-size: 26px;
+	margin: 5px 0 10px 0;
+}
+
+/* inline group */
+.inline-group dl,
+.inline-group dl dt,
+.inline-group dl dd {
+	display: inline-block;
+}
+
+.inline-group dl {
+	margin-right: 35px;
+}
+.inline-group dl dt {
+	margin-right: 20px;
+}
+
+.inline-group.form-inline {
+	margin-top: 5px;
+}
+.inline-group dl {
+	margin-right: 25px;
+}
+.inline-group dl dt {
+	margin-right: 5px;
+}
+
+/* Search box */
+.search-box {
+	position: relative;
+	margin-bottom: 15px;
+}
+.search-box input[type="search"] {
+	padding-left: 26px;
+}
+.search-box .fa.fa-search {
+	position: absolute;
+	top: 8px;
+	left: 8px;
+	z-index: 2;
+	pointer-events: none;
+	color: #999;
+}
+
+/* Navigation Tab */
+ul.nav.nav-tabs li .btn {
+	margin-top: 1px;
+}
+
+.modal-body ul.nav.nav-tabs {
+	border-bottom-color: #F4F4F4;
+	margin-bottom: 15px;
+}
+
+.modal-body ul.nav.nav-tabs li {
+	border-top: 3px solid #FFFFFF;
+	margin-right: 3px;
+}
+.modal-body ul.nav.nav-tabs li.active {
+	border-top-color: #3c8dbc;
+}
+
+.modal-body ul.nav.nav-tabs li > a,
+.modal-body ul.nav.nav-tabs li > a:active,
+.modal-body ul.nav.nav-tabs li > a:hover {
+	border: none;
+	border-radius: 0;
+	margin: 0;
+	padding: 6px 15px 8px 15px;
+	color: #444;
+}
+.modal-body ul.nav.nav-tabs li:not(.active) > a:hover {
+	background: rgba(0,0,0,0);
+	color: #999;
+}
+.modal-body ul.nav.nav-tabs li.active > a {
+	border-left: 1px solid #F4F4F4;
+	border-right: 1px solid #F4F4F4;
+}
+
+/* Step Navigation */
+.step-cntr .step {
+	background: #3c8dbc;
+	margin: 0 0 20px 0;
+	color: #FFF;
+	height: 60px;
+	border-radius: 3px;
+	box-shadow: 0 1px 1px rgba(0,0,0,0.1);
+	display: block;
+
+	-webkit-transition: background .15s linear;
+	-o-transition: background .15s linear;
+	transition: background .15s linear;
+}
+.step-cntr .step.active {
+	background: #f39c12;
+}
+
+.step-cntr .step h1,
+.step-cntr .step h2,
+.step-cntr .step p {
+	margin: 0;
+	padding: 0;
+	overflow: hidden;
+	white-space: nowrap;
+	text-overflow: ellipsis;
+}
+
+.step-cntr .step h1 {
+	display: inline-block;
+	font-size: 30px;
+	float: left;
+	border-right: 2px solid rgba(255,255,255,0.2);
+	width: 60px;
+	height: 60px;
+	text-align: center;
+	padding-top: 12px;
+	margin-right: 10px;
+}
+.step-cntr .step h2 {
+	font-size: 18px;
+	padding: 8px 0 5px 0;
+}
+
+/* Panel */
+.panel-group.panel-group-sm .panel .panel-heading {
+	padding: 5px 6px 5px 10px;
+}
+.panel-group.panel-group-sm .panel .panel-heading h4 {
+	font-size: 14px;
+}
+.panel-group.panel-group-sm .panel .panel-heading h4 a {
+	display: block;
+}
+.panel-group.panel-group-sm .panel .panel-heading .pull-right {
+	padding-left: 5px;
+	padding-right: 5px;
+	border-radius: 3px;
+}
+
+/* Drop Down */
+.dropdown-menu > li.danger > a {
+	color: #dd4b39;
+}
+.dropdown-menu > li.danger > a:hover {
+	color: #FFFFFF;
+	background: #dd4b39;
+}
+
+/* Drop Down */
+.dropdown-menu.left {
+	right: 0;
+	left: auto;
+}
+
+.dropdown-submenu{position:relative;}
+.dropdown-submenu>.dropdown-menu{top:0;left:100%;margin-top:-6px;margin-left:-1px;-webkit-border-radius:0 6px 6px 6px;-moz-border-radius:0 6px 6px 6px;border-radius:0 6px 6px 6px;}
+.dropdown-submenu:hover>.dropdown-menu{display:block;}
+.dropdown-submenu>a:after{display:block;content:" ";float:right;width:0;height:0;border-color:transparent;border-style:solid;border-width:5px 0 5px 5px;border-left-color:#cccccc;margin-top:5px;margin-right:-10px;}
+.dropdown-submenu:hover>a:after{border-left-color:#ffffff;}
+.dropdown-submenu.pull-left{float:none;}.dropdown-submenu.pull-left>.dropdown-menu{left:-100%;margin-left:10px;-webkit-border-radius:6px 0 6px 6px;-moz-border-radius:6px 0 6px 6px;border-radius:6px 0 6px 6px;}
+
+/* Input Group */
+.input-group .input-group-btn select {
+	width: auto;
+}
+
+/* Form group */
+.form-group .checkbox {
+	display: inline;
+	margin-right: 10px;
+}
+
+.form-group select.has-warning,
+.form-group input.has-warning {
+	border-color: #f39c12;
+	box-shadow: none;
+}
+
+.checkbox.noMargin {
+	margin-top: 0;
+	margin-bottom: 5px;
+}
+
+/* UL */
+ul.path {
+	margin-left: 0;
+}
+
+ul.path li {
+	padding: 0;
+	margin-right: 5px;
+}
+ul.path li a {
+	color: #FFFFFF;
+}
+
+ul.tree {
+	padding: 0 0 0 5px;
+}
+
+ul.tree > li,
+ul.tree > li > ul > li {
+	list-style-type: none;
+}
+
+ul.tree .tree-item .hover {
+	display: none;
+}
+ul.tree .tree-item:hover .hover {
+	display: inline-block;
+}
+
+ul.tree > li > ul {
+	padding: 0 0 0 25px;
+}
+
+ul.tree > li > ul > li.active {
+	background: #F4F4F4;
+}
+
+ul.tree.tree-bordered {
+	border: 1px solid #f4f4f4;
+}
+ul.tree.tree-bordered,
+ul.tree.tree-bordered > li > ul {
+	padding: 0;
+}
+ul.tree.tree-bordered > li:not(:last-child) {
+	border-bottom: 1px solid #f4f4f4;
+}
+ul.tree.tree-bordered > li > ul > li {
+	border-top: 1px solid #f4f4f4;
+}
+ul.tree.tree-bordered > li > span,
+ul.tree.tree-bordered > li > a {
+	display: block;
+	padding: 8px;
+}
+ul.tree.tree-bordered > li > ul > li > span,
+ul.tree.tree-bordered > li > ul > li > a {
+	display: block;
+	padding: 8px 8px 8px 30px;
+}
+
+.product-list-in-box > .item {
+	-webkit-transition: background .5s linear;
+	-o-transition: background .5s linear;
+	transition: background .5s linear;
+}
+.product-list-in-box > .item.ng-animate {
+	transition: 0s;
+}
+.product-list-in-box > .item.active {
+	background: #F5FAFC;
+	-webkit-transition: none;
+	-o-transition: none;
+	transition: none;
+}
+
+.nav.fixed-height,
+.products-list.fixed-height {
+	height: 402px;
+	overflow-y: auto;
+}
+
+.products-list .product-operation {
+	float: left;
+	border: 1px solid #9EC8E0;
+	border-radius: 5px;
+	overflow: hidden;
+}
+
+.products-list .product-operation .fa {
+	display: block;
+	padding: 4px 16px;
+	color: #3c8dbc;
+}
+.products-list .product-operation a.fa:hover {
+	color: #FFFFFF;
+	background: #337ab7;
+}
+
+.products-list .product-operation.single .fa {
+	padding: 12px 12px;
+	font-size: 20px;
+}
+
+.products-list .item .product-info a.fa.fa-times {
+	display: none;
+}
+.products-list .item:hover .product-info a.fa.fa-times {
+	display: block;
+}
+
+/* Label */
+.label.label-default {
+	color: #FFFFFF;
+}
+
+.label.label-sm {
+	padding: .0em .4em .1em;
+}
+
+/* Row */
+.row.narrow {
+	margin-left: -5px;
+	margin-right: -5px;
+	margin-bottom: -10px;
+}
+
+.row.narrow>.col-xs-1, .row.narrow>.col-sm-1, .row.narrow>.col-md-1, .row.narrow>.col-lg-1, .row.narrow>.col-xs-2, .row.narrow>.col-sm-2, .row.narrow>.col-md-2, .row.narrow>.col-lg-2, .row.narrow>.col-xs-3, .row.narrow>.col-sm-3, .row.narrow>.col-md-3, .row.narrow>.col-lg-3, .row.narrow>.col-xs-4, .row.narrow>.col-sm-4, .row.narrow>.col-md-4, .row.narrow>.col-lg-4, .row.narrow>.col-xs-5, .row.narrow>.col-sm-5, .row.narrow>.col-md-5, .row.narrow>.col-lg-5, .row.narrow>.col-xs-6, .row.narrow>.col-sm-6, .row.narrow>.col-md-6, .row.narrow>.col-lg-6, .row.narrow>.col-xs-7, .row.narrow>.col-sm-7, .row.narrow>.col-md-7, .row.narrow>.col-lg-7, .row.narrow>.col-xs-8, .row.narrow>.col-sm-8, .row.narrow>.col-md-8, .row.narrow>.col-lg-8, .row.narrow>.col-xs-9, .row.narrow>.col-sm-9, .row.narrow>.col-md-9, .row.narrow>.col-lg-9, .row.narrow>.col-xs-10, .row.narrow>.col-sm-10, .row.narrow>.col-md-10, .row.narrow>.col-lg-10, .row.narrow>.col-xs-11, .row.narrow>.col-sm-11, .row.narrow>.col-md-11, .
 row.narrow>.col-lg-11, .row.narrow>.col-xs-12, .row.narrow>.col-sm-12, .row.narrow>.col-md-12, .row.narrow>.col-lg-12 {
+	padding-left: 5px;
+	padding-right: 5px;
+}
+
+.row.narrow > [class^="col-"],
+.row.narrow > [class*=" col-"] {
+	margin-bottom: 10px;
+}
+
+/* Chart */
+.sortable-mock-element .nvd3-chart-wrapper {
+	background: #FFFFFF;
+	opacity: 0.8;
+}
+
+.sortable-enter .nvd3-chart-wrapper {
+	border-color: #3c8dbc;
+	pointer-events: none;
+}
+.sortable-enter .nvd3-chart-wrapper .nvtooltip {
+	display: none;
+}
+
+.nvd3-chart-wrapper {
+	position: relative;
+	border: 1px solid rgba(0,0,0,0.1);
+}
+.nvd3-chart-wrapper:hover {
+	//border-color: #F4F4F4;
+}
+
+.nvd3-chart-wrapper .nvd3-chart-config {
+	position: absolute;
+	top: 1px;
+	right: 1px;
+	display: none;
+	border-radius: 0;
+	padding: 0 5px;
+	background: rgba(0,0,0,0.7);
+}
+.nvd3-chart-wrapper:hover .nvd3-chart-config {
+	display: block;
+}
+
+.nvd3-chart-wrapper .nvd3-chart-config a {
+	color: rgba(255,255,255, 0.9);
+	padding: 5px 2px 4px 2px;
+	font-size: 16px;
+}
+.nvd3-chart-wrapper .nvd3-chart-config a:hover {
+	color: #FFFFFF;
+}
+
+.nvd3-chart-cntr {
+	padding: 5px;
+}
+
+.nvd3-chart-cntr > h3 {
+	text-align: center;
+	font-size: 16px;
+	font-weight: bolder;
+	margin: 0;
+	padding: 5px 0;
+
+	overflow:hidden;
+	text-overflow:ellipsis;
+
+}
+
+.nvd3-chart-cntr > svg.nvd3-svg {
+	height: 200px;
+}
+
+.nvd3-chart-cntr.lg > svg.nvd3-svg {
+	height: 400px;
+}
+
+/* Tab */
+body .tab-content>.tab-pane {
+	display: block;
+	height: 0px;
+	overflow: hidden;
+	position: relative;
+}
+body .tab-content>.tab-pane.active {
+	height: auto;
+	overflow-x: visible;
+	overflow-y: visible;
+}
+
+body .modal-body .nav-pills > li > a,
+body .box-body .nav-pills > li > a {
+	padding: 5px 15px;
+	border: none;
+}
+
+body .modal-body .nav-stacked > li {
+	border-bottom: 1px solid #f4f4f4;
+	margin: 0;
+}
+body .modal-body .nav-stacked > li:last-child {
+	border-bottom: none;
+}
+
+body .box-body .nav-tabs-custom {
+	box-shadow: none;
+	margin-bottom: 0;
+}
+body .box-body .nav-tabs-custom > .nav-tabs > li:first-of-type.active > a {
+	border-left-color: #f4f4f4;
+}
+body .box-body .nav-tabs-custom > .nav-tabs > li > a {
+	padding: 8px 15px;
+}
+body .box-body .nav-tabs-custom > .tab-content {
+	padding: 10px 0;
+}
+
+/* Box */
+.box .guideline {
+	margin-top: 0;
+}
+
+.box.inner-box {
+	border: none;
+	box-shadow: none;
+	padding: 5px 10px;
+	margin: 0;
+	border-bottom: 1px solid #f4f4f4;
+	position: relative;
+	border-radius: 0;
+}
+
+.box.inner-box .box-title {
+	margin: 0 5px 5px 0;
+	padding: 0;
+	font-size: 16px;
+	font-weight: bolder;
+	display: inline-block;
+	word-break: break-all;
+}
+
+.box.inner-box .box-tools {
+	position: absolute;
+	top: 0;
+	right: 0;
+}
+
+.box.inner-box:last-child {
+	border-bottom: none;
+}
+
+/* Navigation Tab */
+.nav-tabs-custom {
+	position: relative;
+}
+
+.nav-tabs-custom .box-tools {
+	position: absolute;
+	right: 15px;
+	top: 8px;
+}
+
+.nav-tabs-custom .box-tools .strong {
+	font-weight: bolder;
+}
+
+/* Customize */
+#content {
+	position: relative;
+}
+
+.page-fixed {
+	position: absolute;
+	top: -45px;
+	right: 0;
+}
+
+@media (max-width:991px) {
+	.page-fixed {
+		top: -70px;
+	}
+}
+
+.fixed-right {
+	position: absolute;
+	right: 0;
+	z-index: 3;
+}
+
+.main-header .logo img {
+	height: 34px;
+}
+
+.main-header .navbar-toggle {
+	float: none;
+	border-radius: 0;
+}
+.main-header .navbar-toggle:hover {
+	background: rgba(0, 0, 0, 0.1);
+}
+
+#moduleMenu > ul > li.active > a {
+	border-top: 3px solid rgba(255,255,255,0.8);
+	padding-top: 12px;
+}
+
+@media (max-width: 767px) {
+	#moduleMenu > ul > li.active > a {
+		padding: 10px 15px;
+		border-top: none;
+		border-left: 3px solid rgba(255,255,255,0.8);
+	}
+
+	.main-header .navbar .navbar-custom-menu .nav .dropdown-menu li a {
+		color: #333;
+	}
+	.main-header .navbar .navbar-custom-menu .nav .dropdown-menu li a:hover {
+		color: #FFF;
+	}
+}
+
+#timeRangePickerCntr .navbar-form {
+	display: inline-block;
+	padding-right: 0;
+}
+
+#timeRangePickerCntr #timeRangePicker {
+	min-width: 300px;
+}
+
+body .login-box, body .register-box {
+	margin: 3% auto;
+}
+
+.content-header > .breadcrumb > li {
+	font-size: 14px;
+}
+
+.daterangepicker .ranges {
+  width: 110px!important;
+}
+.daterangepicker .daterangepicker_start_input,
+.daterangepicker .daterangepicker_end_input {
+	display: block!important;
+	padding: 0!important;
+	float: none!important;
+}
+.daterangepicker .daterangepicker_start_input .input-mini,
+.daterangepicker .daterangepicker_end_input .input-mini {
+	width: 110px!important;
+}
+
+.form-group.inner-icon {
+	position: relative;
+}
+.form-group.inner-icon .fa {
+	position: absolute;
+	left: 10px;
+	top: 10px;
+}
+.form-group.inner-icon input {
+	padding-left: 35px;
+}
+
+#autoRefreshCntr > a {
+	border: none;
+	opacity: 0.3;
+}
+#autoRefreshCntr.autoRefresh > a {
+	opacity: 1;
+}
+
+.table-responsive .row {
+	margin: 0;
+}
+
+
+/* Misc */
+body .tooltip-inner {
+	max-width: 500px;
+}
+
+.text-nowrap {
+	white-space: nowrap;
+}
+
+.text-ellipsis,
+.label.text-ellipsis {
+	overflow:hidden;
+	text-overflow:ellipsis;
+	display: inline-block;
+	white-space: nowrap;
+	max-width: 100%;
+}
+td.text-ellipsis {
+	display: table-cell;
+}
+
+.text-breakall {
+	max-width: 100%;
+	display: inline-block;
+	word-wrap: break-word;
+}
+
+.btn.btn-xs.sm {
+	font-size: 12px;
+	padding: 2px 6px;
+}
+
+.form-control.input-xs {
+	height: 24px;
+	padding: 2px 8px;
+	font-size: 12px;
+	line-height: 100%;
+}
+
+pre.noWrap {
+	border: none;
+	border-radius: 0;
+	background: transparent;
+	margin: 0;
+	padding: 0;
+}
+
+.noSelect {
+	-khtml-user-select: none;
+	-moz-user-select: none;
+	-ms-user-select: none;
+	user-select: none;
+	-webkit-touch-callout: none;
+	-webkit-user-select: none;
+}
+
+.blink {
+	animation: blinker 1s linear infinite;
+}
+
+@keyframes blinker {
+	50% {opacity: 0.0;}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/_app/public/feature/classification/controller.js
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/_app/public/feature/classification/controller.js b/eagle-webservice/src/main/webapp/_app/public/feature/classification/controller.js
new file mode 100644
index 0000000..462b41b
--- /dev/null
+++ b/eagle-webservice/src/main/webapp/_app/public/feature/classification/controller.js
@@ -0,0 +1,358 @@
+/*
+ * 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.
+ */
+
+(function() {
+	'use strict';
+
+	var featureControllers = angular.module('featureControllers');
+	var feature = featureControllers.register("classification");
+	var eagleApp = angular.module('eagleApp');
+
+	// ==============================================================
+	// =                          Function                          =
+	// ==============================================================
+
+	// =============================================================
+	// =                        Sensitivity                        =
+	// =============================================================
+	feature.navItem("sensitivity", "Classification", "user-secret");
+	feature.controller('sensitivity', function(PageConfig, Site, $scope, Application, Entities, UI) {
+		PageConfig.pageTitle = "Data Classification";
+		PageConfig.pageSubTitle = Site.current().tags.site;
+		$scope.ajaxId = eagleApp._TRS();
+		$scope.viewConfig = Application.current().configObj.view;
+
+		if(!$scope.viewConfig) {
+			$.dialog({
+				title: "OPS",
+				content: "View configuration not defined in Application."
+			});
+			return;
+		}
+
+		// ===================== Function =====================
+		$scope.export = function() {
+			var _data = {};
+			UI.fieldConfirm({title: "Export Classification", confirm: false, size: "large"}, _data, [
+				{name: "Data", field: "data", type: "blob", rows: 20, optional: true, readonly: true}]
+			);
+
+			Entities.queryEntities($scope.viewConfig.service, {site: Site.current().tags.site})._promise.then(function(data) {
+				_data.data = JSON.stringify(data, null, "\t");
+			});
+		};
+
+		$scope.import = function() {
+			UI.fieldConfirm({title: "Import Classification", size: "large"}, {}, [
+				{name: "Data", field: "data", type: "blob", rows: 20, optional: true}
+			], function(entity) {
+				var _list = common.parseJSON(entity.data, false);
+				if(!_list) {
+					return "Invalid JSON format";
+				}
+				if(!$.isArray(_list)) {
+					return "Not an array";
+				}
+			}).then(null, null, function(holder) {
+				Entities.updateEntity($scope.viewConfig.service, common.parseJSON(holder.entity.data, []), {timestamp: false})._promise.then(function() {
+					holder.closeFunc();
+					location.reload();
+				});
+			});
+		};
+
+		$scope.deleteAll = function() {
+			UI.deleteConfirm("All the Classification Data").then(null, null, function(holder) {
+				Entities.deleteEntities($scope.viewConfig.service, {site: Site.current().tags.site})._promise.then(function() {
+					holder.closeFunc();
+					location.reload();
+				});
+			});
+		};
+	});
+     // =============================================================
+    	// =                    Sensitivity - Job                   =
+    	// =============================================================
+    	feature.controller('sensitivityViewJob', function(Site, $scope, $wrapState, Entities) {
+    		$scope.items = [];
+
+    		// Mark sensitivity
+    		$scope._oriItem = {};
+    		$scope._markItem = {};
+
+    		// ======================= View =======================
+    		// Item
+    		$scope.updateItems = function() {
+    			$scope.items = Entities.query($scope.viewConfig.api, {site: Site.current().tags.site});
+    		};
+
+
+    		$scope.updateItems();
+
+    		// =================== Sensitivity ===================
+    		$scope.markSensitivity = function(item) {
+    			$scope._oriItem = item;
+    			$scope._markItem = {
+    				prefix: $scope.viewConfig.prefix,
+    				tags: {
+    					site: Site.current().tags.site
+    				},
+    				sensitivityType: ""
+    			};
+
+    			$scope._markItem.tags[$scope.viewConfig.keys[0]] = item.jobId;
+    			$("#sensitivityMDL").modal();
+    		};
+    		$scope.confirmUpateSensitivity = function() {
+    			$scope._oriItem.sensitiveType = $scope._markItem.sensitivityType;
+    			Entities.updateEntity($scope.viewConfig.service, $scope._markItem, {timestamp: false})._promise.success(function(data) {
+    				Entities.dialog(data);
+    			});
+    			$("#sensitivityMDL").modal('hide');
+    		};
+    		$scope.unmarkSensitivity = function(item) {
+    			$.dialog({
+    				title: "Unmark Confirm",
+    				content: "Do you want to remove the sensitivity mark on '" + item.jobId + "'?",
+    				confirm: true
+    			}, function(ret) {
+    				if(!ret) return;
+
+    				var _cond = {site: Site.current().tags.site};
+    				_cond[$scope.viewConfig.keys[0]] = item.jobId;
+    				Entities.deleteEntities($scope.viewConfig.service, _cond);
+
+    				item.sensitiveType = null;
+    				$scope.$apply();
+    			});
+    		};
+    	});
+	// =============================================================
+	// =                    Sensitivity - Folder                   =
+	// =============================================================
+	feature.controller('sensitivityViewFolder', function(Site, $scope, $wrapState, Entities) {
+		$scope.path = $wrapState.param.path || "/";
+		$scope.pathUnitList = [];
+		$scope.items = [];
+
+		// Mark sensitivity
+		$scope._oriItem = {};
+		$scope._markItem = {};
+
+		// ======================= View =======================
+		// Path
+		function _refreshPathUnitList(_path) {
+			var _start,_current, _unitList = [];
+			_path = _path + (_path.match(/\/$/) ? "" : "/");
+			for(_current = _start = 0 ; _current < _path.length ; _current += 1) {
+				if(_path[_current] === "/") {
+					_unitList.push({
+						name: _path.substring(_start, _current + (_current === 0 ? 1 : 0)),
+						path: _path.substring(0, _current === 0 ? 1 : _current)
+					});
+					_start = _current + 1;
+				}
+			}
+			$scope.pathUnitList = _unitList;
+		}
+
+		// Item
+		$scope.updateItems = function(path) {
+			if(path) $scope.path = path;
+
+			$scope.items = Entities.query($scope.viewConfig.api, {site: Site.current().tags.site, path: $scope.path});
+			$scope.items._promise.success(function(data) {
+				Entities.dialog(data, function() {
+					if($scope.path !== "/") $scope.updateItems("/");
+				});
+			});
+			_refreshPathUnitList($scope.path);
+		};
+
+		$scope.getFileName = function(item) {
+			return (item.resource + "").replace(/^.*\//, "");
+		};
+
+		$scope.updateItems($scope.path);
+
+		// =================== Sensitivity ===================
+		$scope.markSensitivity = function(item) {
+			$scope._oriItem = item;
+			$scope._markItem = {
+				prefix: $scope.viewConfig.prefix,
+				tags: {
+					site: Site.current().tags.site
+				},
+				sensitivityType: ""
+			};
+			$scope._markItem.tags[$scope.viewConfig.keys[0]] = item.resource;
+			$("#sensitivityMDL").modal();
+		};
+		$scope.confirmUpateSensitivity = function() {
+			$scope._oriItem.sensitiveType = $scope._markItem.sensitivityType;
+			Entities.updateEntity($scope.viewConfig.service, $scope._markItem, {timestamp: false})._promise.success(function(data) {
+				Entities.dialog(data);
+			});
+			$("#sensitivityMDL").modal('hide');
+		};
+		$scope.unmarkSensitivity = function(item) {
+			$.dialog({
+				title: "Unmark Confirm",
+				content: "Do you want to remove the sensitivity mark on '" + item.resource + "'?",
+				confirm: true
+			}, function(ret) {
+				if(!ret) return;
+
+				var _cond = {site: Site.current().tags.site};
+				_cond[$scope.viewConfig.keys[0]] = item.resource;
+				Entities.deleteEntities($scope.viewConfig.service, _cond);
+
+				item.sensitiveType = null;
+				$scope.$apply();
+			});
+		};
+	});
+
+	// =============================================================
+	// =                    Sensitivity - Table                    =
+	// =============================================================
+	feature.controller('sensitivityViewTable', function(Site, $scope, Entities) {
+		$scope.databases = null;
+		$scope.table = null;
+
+		// Mark sensitivity
+		$scope._oriItem = {};
+		$scope._markItem = {};
+
+		// ======================= View =======================
+		var _fillAttr = function(list, key, target) {
+			list._promise.then(function() {
+				$.each(list, function(i, unit) {
+					unit[key] = unit[target];
+				});
+			});
+			return list._promise;
+		};
+
+		$scope.loadDatabases = function(database) {
+			var _dbs = Entities.query($scope.viewConfig.api.database, {site: Site.current().tags.site});
+			return _fillAttr(_dbs, "database", $scope.viewConfig.mapping.database).then(function() {
+				if($scope.databases) {
+					$.each($scope.databases, function(i, oriDB) {
+						var db = common.array.find(oriDB.resource, _dbs, "resource");
+						if(db) {
+							db.show = oriDB.show;
+							db.tables = oriDB.tables;
+						}
+					});
+				}
+				$scope.databases = _dbs;
+			});
+		};
+		$scope.loadDatabases();
+
+		$scope.loadTables = function(database, force) {
+			var _tables, _qry;
+			if(database.tables && !force) return;
+			_qry = {
+				site: Site.current().tags.site
+			};
+			_qry[$scope.viewConfig.mapping.database] = database[$scope.viewConfig.mapping.database];
+			_tables = Entities.query($scope.viewConfig.api.table, _qry);
+			if(!database.tables) database.tables = _tables;
+			_fillAttr(_tables, "table", $scope.viewConfig.mapping.table);
+			return _fillAttr(_tables, "database", $scope.viewConfig.mapping.database).then(function() {
+				database.tables = _tables;
+			});
+		};
+
+		$scope.loadColumns = function(database, table) {
+			$scope.table = table;
+
+			if(table.columns) return;
+			var _qry = {
+				site: Site.current().tags.site
+			};
+			_qry[$scope.viewConfig.mapping.database] = database[$scope.viewConfig.mapping.database];
+			_qry[$scope.viewConfig.mapping.table] = table[$scope.viewConfig.mapping.table];
+			table.columns = Entities.query($scope.viewConfig.api.column, _qry);
+			_fillAttr(table.columns, "column", $scope.viewConfig.mapping.column);
+		};
+
+		$scope.refreshData = function() {
+			$scope.loadDatabases().then(function() {
+				if(!$scope.table) return;
+
+				var _table = $scope.table;
+				var _db = common.array.find($scope.table.database, $scope.databases, "database");
+				if(_db) {
+					$scope.loadTables(_db, true).then(function() {
+						$scope.table = common.array.find(_table.table, _db.tables, "table");
+						$scope.table.columns = _table.columns;
+					});
+				}
+			});
+		};
+
+		// =================== Sensitivity ===================
+		$scope.markSensitivity = function(item, event) {
+			if(event) event.stopPropagation();
+
+			$scope._oriItem = item;
+			$scope._markItem = {
+				prefix: $scope.viewConfig.prefix,
+				tags: {
+					site: Site.current().tags.site
+				},
+				sensitivityType: ""
+			};
+			$scope._markItem.tags[$scope.viewConfig.keys[0]] = item.resource;
+			$("#sensitivityMDL").modal();
+		};
+		$scope.confirmUpateSensitivity = function() {
+			$scope._oriItem.sensitiveType = $scope._markItem.sensitivityType;
+			Entities.updateEntity($scope.viewConfig.service, $scope._markItem, {timestamp: false})._promise.success(function(data) {
+				Entities.dialog(data);
+				$scope.refreshData();
+			});
+			$("#sensitivityMDL").modal('hide');
+		};
+		$scope.unmarkSensitivity = function(item, event) {
+			if(event) event.stopPropagation();
+
+			$.dialog({
+				title: "Unmark Confirm",
+				content: "Do you want to remove the sensitivity mark on '" + item.resource + "'?",
+				confirm: true
+			}, function(ret) {
+				if(!ret) return;
+
+				var _qry = {
+					site: Site.current().tags.site
+				};
+				_qry[$scope.viewConfig.keys[0]] = item.resource;
+				Entities.deleteEntities($scope.viewConfig.service, _qry)._promise.then(function() {
+					$scope.refreshData();
+				});
+
+				item.sensitiveType = null;
+				$scope.$apply();
+			});
+		};
+	});
+})();
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/_app/public/feature/classification/page/sensitivity.html
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/_app/public/feature/classification/page/sensitivity.html b/eagle-webservice/src/main/webapp/_app/public/feature/classification/page/sensitivity.html
new file mode 100644
index 0000000..41fb291
--- /dev/null
+++ b/eagle-webservice/src/main/webapp/_app/public/feature/classification/page/sensitivity.html
@@ -0,0 +1,40 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<div class="box box-primary">
+	<div class="box-header with-border">
+		<i class="fa fa-folder-open"></i>
+		<h3 class="box-title ng-binding">{{Application.current().displayName}}</h3>
+		<div class="box-tools pull-right" ng-if="viewConfig">
+			<div class="btn-group">
+				<button type="button" class="btn btn-box-tool dropdown-toggle" data-toggle="dropdown">
+					<span class="fa fa-wrench"></span>
+				</button>
+				<ul class="dropdown-menu" role="menu">
+					<li><a ng-click="import()"><span class="fa fa-cloud-upload"></span> Import</a></li>
+					<li><a ng-click="export()"><span class="fa fa-cloud-download"></span> Export</a></li>
+					<li class="divider"></li>
+					<li class="danger"><a ng-click="deleteAll()"><span class="fa fa-trash"></span> Delete All</a></li>
+				</ul>
+			</div>
+		</div>
+	</div>
+	<div class="box-body">
+		<ng-include ng-if="viewConfig" src="'public/feature/classification/page/sensitivity/' + viewConfig.type + '.html?_=' + ajaxId"></ng-include>
+	</div>
+</div>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/_app/public/feature/classification/page/sensitivity/folder.html
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/_app/public/feature/classification/page/sensitivity/folder.html b/eagle-webservice/src/main/webapp/_app/public/feature/classification/page/sensitivity/folder.html
new file mode 100644
index 0000000..cfefffa
--- /dev/null
+++ b/eagle-webservice/src/main/webapp/_app/public/feature/classification/page/sensitivity/folder.html
@@ -0,0 +1,110 @@
+<!--
+  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 ng-controller="classification_sensitivityViewFolder">
+	<ul class="list-inline path">
+		<li>Path:</li>
+		<li ng-repeat="unit in pathUnitList">
+			<a ng-click="updateItems(unit.path)" class="label bg-black">{{unit.name}}</a>
+		</li>
+	</ul>
+
+	<table class="table table-bordered">
+		<thead>
+			<tr>
+				<th width="15%">File Name</th>
+				<th width="10%">Owner</th>
+				<th width="10%">Group</th>
+				<th>Sensitivity Type</th>
+				<th width="10" ng-show="Auth.isRole('ROLE_ADMIN')"> </th>
+			</tr>
+		</thead>
+		<tbody>
+			<tr ng-show="items._promise.$$state.status !== 1">
+				<td colspan="5">
+					<span class="fa fa-refresh fa-spin"> </span>
+					Loading...
+				</td>
+			</tr>
+			<tr ng-show="items._promise.$$state.status === 1 && !items.length">
+				<td colspan="5">
+					<span class="fa fa-exclamation-triangle"> </span>
+					Empty Folder
+				</td>
+			</tr>
+			<tr ng-repeat="item in items" ng-class="{warning : item.sensitiveType}">
+				<td>
+					<span ng-show="!item.isdir">
+						<span class="fa fa-file"> </span>
+						{{getFileName(item)}}
+					</span>
+					<a ng-show="item.isdir" ng-click="updateItems(item.resource)">
+						<span class="fa fa-folder"> </span>
+						{{getFileName(item)}}
+					</a>
+
+					<span class="pull-right" ng-show="item.childSensitiveTypes.length">
+						<span class="fa fa-dot-circle-o text-muted" uib-tooltip="Contain child sensitivity defination"> </span>
+					</span>
+				</td>
+				<td>{{item.owner}}</td>
+				<td>{{item.groupName}}</td>
+				<td>{{item.sensitiveType}}</td>
+				<td ng-show="Auth.isRole('ROLE_ADMIN')">
+					<button class="fa fa-eye btn btn-primary btn-xs" ng-click="markSensitivity(item)" ng-show="!item.sensitiveType"
+					uib-tooltip="Mark as sensitivity data" tooltip-animation="false" tooltip-placement="left"> </button>
+					<button class="fa fa-eye-slash btn btn-warning btn-xs" ng-click="unmarkSensitivity(item)" ng-show="item.sensitiveType"
+					uib-tooltip="Remove the sensitivity mark" tooltip-animation="false" tooltip-placement="left"> </button>
+				</td>
+			</tr>
+		</tbody>
+	</table>
+
+
+	<!-- Modal: Create / Edit site -->
+	<div class="modal fade" id="sensitivityMDL" tabindex="-1" role="dialog">
+		<div class="modal-dialog" role="document">
+			<div class="modal-content">
+				<div class="modal-header">
+					<button type="button" class="close" data-dismiss="modal" aria-label="Close">
+						<span aria-hidden="true">&times;</span>
+					</button>
+					<h4 class="modal-title">Mark Sensitivity Data</h4>
+				</div>
+				<div class="modal-body">
+					<div class="form-group">
+						<label>Resource</label>
+						<input type="text" readonly="readonly" class="form-control" ng-model="_markItem.tags.filedir" />
+					</div>
+					<div class="form-group">
+						<label>* Sensitivity Type</label>
+						<input type="text" class="form-control" ng-model="_markItem.sensitivityType" id="sensitiveType" />
+					</div>
+				</div>
+				<div class="modal-footer">
+					<button type="button" class="btn btn-default" data-dismiss="modal">
+						Close
+					</button>
+					<button type="button" class="btn btn-primary" ng-click="confirmUpateSensitivity()" ng-disabled="!_markItem.sensitivityType">
+						Update
+					</button>
+				</div>
+			</div>
+		</div>
+	</div>
+
+</div>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/afb89794/eagle-webservice/src/main/webapp/_app/public/feature/classification/page/sensitivity/job.html
----------------------------------------------------------------------
diff --git a/eagle-webservice/src/main/webapp/_app/public/feature/classification/page/sensitivity/job.html b/eagle-webservice/src/main/webapp/_app/public/feature/classification/page/sensitivity/job.html
new file mode 100644
index 0000000..05d70da
--- /dev/null
+++ b/eagle-webservice/src/main/webapp/_app/public/feature/classification/page/sensitivity/job.html
@@ -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.
+  -->
+<div ng-controller="classification_sensitivityViewJob">
+    <ul class="list-inline path">
+        <li>Oozie CoordinatorJob:</li>
+    </ul>
+
+    <table class="table table-bordered">
+        <thead>
+        <tr>
+            <th width="15%">JobId</th>
+            <th width="10%">AppName</th>
+            <th>Sensitivity Type</th>
+            <th width="10" ng-show="Auth.isRole('ROLE_ADMIN')"> </th>
+        </tr>
+        </thead>
+        <tbody>
+        <tr ng-show="items._promise.$$state.status !== 1">
+            <td colspan="5">
+                <span class="fa fa-refresh fa-spin"> </span>
+                Loading...
+            </td>
+        </tr>
+        <tr ng-show="items._promise.$$state.status === 1 && !items.length">
+            <td colspan="5">
+                <span class="fa fa-exclamation-triangle"> </span>
+                Empty
+            </td>
+        </tr>
+        <tr ng-repeat="item in items" ng-class="{warning : item.sensitiveType}">
+            <td>{{item.jobId}}</td>
+            <td>{{item.name}}</td>
+            <td>{{item.sensitiveType}}</td>
+            <td ng-show="Auth.isRole('ROLE_ADMIN')">
+                <button class="fa fa-eye btn btn-primary btn-xs" ng-click="markSensitivity(item)" ng-show="!item.sensitiveType"
+                        uib-tooltip="Mark as sensitivity data" tooltip-animation="false" tooltip-placement="left"> </button>
+                <button class="fa fa-eye-slash btn btn-warning btn-xs" ng-click="unmarkSensitivity(item)" ng-show="item.sensitiveType"
+                        uib-tooltip="Remove the sensitivity mark" tooltip-animation="false" tooltip-placement="left"> </button>
+            </td>
+        </tr>
+        </tbody>
+    </table>
+
+
+    <!-- Modal: Create / Edit site -->
+    <div class="modal fade" id="sensitivityMDL" tabindex="-1" role="dialog">
+        <div class="modal-dialog" role="document">
+            <div class="modal-content">
+                <div class="modal-header">
+                    <button type="button" class="close" data-dismiss="modal" aria-label="Close">
+                        <span aria-hidden="true">&times;</span>
+                    </button>
+                    <h4 class="modal-title">Mark Sensitivity Data</h4>
+                </div>
+                <div class="modal-body">
+                    <div class="form-group">
+                        <label>Resource</label>
+                        <input type="text" readonly="readonly" class="form-control" ng-model="_markItem.tags.oozieResource" />
+                    </div>
+                    <div class="form-group">
+                        <label>* Sensitivity Type</label>
+                        <input type="text" class="form-control" ng-model="_markItem.sensitivityType" id="sensitiveType" />
+                    </div>
+                </div>
+                <div class="modal-footer">
+                    <button type="button" class="btn btn-default" data-dismiss="modal">
+                        Close
+                    </button>
+                    <button type="button" class="btn btn-primary" ng-click="confirmUpateSensitivity()" ng-disabled="!_markItem.sensitivityType">
+                        Update
+                    </button>
+                </div>
+            </div>
+        </div>
+    </div>
+
+</div>
\ No newline at end of file


Mime
View raw message