http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/d37643b0/eagle-webservice/src/main/webapp/app/public/feature/common/controller.js
----------------------------------------------------------------------
diff --git 
a/eagle-webservice/src/main/webapp/app/public/feature/common/controller.js 
b/eagle-webservice/src/main/webapp/app/public/feature/common/controller.js
new file mode 100644
index 0000000..50f377c
--- /dev/null
+++ b/eagle-webservice/src/main/webapp/app/public/feature/common/controller.js
@@ -0,0 +1,1023 @@
+/*
+ * 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("common");
+
+       // ==============================================================
+       // =                          Function                          =
+       // ==============================================================
+       feature.service("Policy", function(Entities) {
+               var Policy = function () {};
+
+               Policy.updatePolicyStatus = function(policy, status) {
+                       $.dialog({
+                               title: "Confirm",
+                               content: "Do you want to " + (status ? "enable" 
: "disable") + " policy[" + policy.tags.policyId + "]?",
+                               confirm: true
+                       }, function(ret) {
+                               if(ret) {
+                                       policy.enabled = status;
+                                       
Entities.updateEntity("AlertDefinitionService", policy);
+                               }
+                       });
+               };
+               Policy.deletePolicy = function(policy, callback) {
+                       $.dialog({
+                               title: "Confirm",
+                               content: "Do you want to delete policy[" + 
policy.tags.policyId + "]?",
+                               confirm: true
+                       }, function(ret) {
+                               if(ret) {
+                                       policy.enabled = status;
+                                       
Entities.deleteEntity("AlertDefinitionService", 
policy)._promise.finally(function() {
+                                               if(callback) {
+                                                       callback(policy);
+                                               }
+                                       });
+                               }
+                       });
+               };
+               return Policy;
+       });
+
+       // ==============================================================
+       // =                          Policies                          =
+       // ==============================================================
+
+       // ========================= Policy List ========================
+       feature.navItem("policyList", "Policies", "list");
+       feature.controller('policyList', function(PageConfig, Site, $scope, 
Application, Entities, Policy) {
+               PageConfig.pageTitle = "Policy List";
+               PageConfig.pageSubTitle = Site.current().tags.site;
+
+               // Initial load
+               $scope.policyList = [];
+               $scope.application = Application.current();
+
+               // List policies
+               var _policyList = 
Entities.queryEntities("AlertDefinitionService", {site: 
Site.current().tags.site, dataSource: $scope.application.tags.application});
+               _policyList._promise.then(function() {
+                       $.each(_policyList, function(i, policy) {
+                               policy.__mailStr = 
common.getValueByPath(common.parseJSON(policy.notificationDef, {}), 
"0.recipients", "");
+                               policy.__mailList = policy.__mailStr.trim() === 
"" ? [] : policy.__mailStr.split(/[,;]/);
+                               policy.__expression = 
common.parseJSON(policy.policyDef, {}).expression;
+
+                               $scope.policyList.push(policy);
+                       });
+               });
+               $scope.policyList._promise = _policyList._promise;
+
+               // Function
+               $scope.searchFunc = function(item) {
+                       var key = $scope.search;
+                       if(!key) return true;
+
+                       var _key = key.toLowerCase();
+                       function _hasKey(item, path) {
+                               var _value = common.getValueByPath(item, path, 
"").toLowerCase();
+                               return _value.indexOf(_key) !== -1;
+                       }
+                       return _hasKey(item, "tags.policyId") || _hasKey(item, 
"__expression") || _hasKey(item, "desc") || _hasKey(item, "owner") || 
_hasKey(item, "__mailStr");
+               };
+
+               $scope.updatePolicyStatus = Policy.updatePolicyStatus;
+               $scope.deletePolicy = function(policy) {
+                       Policy.deletePolicy(policy, function(policy) {
+                               var _index = $scope.policyList.indexOf(policy);
+                               $scope.policyList.splice(_index, 1);
+                       });
+               };
+       });
+
+       // ======================= Policy Detail ========================
+       feature.controller('policyDetail', function(PageConfig, Site, $scope, 
$wrapState, Entities, Policy, nvd3) {
+               var MAX_PAGESIZE = 10000;
+
+               PageConfig.pageTitle = "Policy Detail";
+               PageConfig.lockSite = true;
+               PageConfig
+                       .addNavPath("Policy List", "/common/policyList")
+                       .addNavPath("Policy Detail");
+
+               $scope.chartConfig = {
+                       chart: "line",
+                       xType: "time"
+               };
+
+               // Query policy
+               if($wrapState.param.filter) {
+                       $scope.policyList = 
Entities.queryEntity("AlertDefinitionService", $wrapState.param.filter);
+               } else {
+                       $scope.policyList = 
Entities.queryEntities("AlertDefinitionService", {
+                               policyId: $wrapState.param.policy,
+                               site: $wrapState.param.site,
+                               alertExecutorId: $wrapState.param.executor
+                       });
+               }
+
+               $scope.policyList._promise.then(function() {
+                       var policy = null;
+
+                       if($scope.policyList.length === 0) {
+                               $.dialog({
+                                       title: "OPS!",
+                                       content: "Policy not found!"
+                               }, function() {
+                                       location.href = "#/common/policyList";
+                               });
+                               return;
+                       } else {
+                               policy = $scope.policyList[0];
+
+                               policy.__mailStr = 
common.getValueByPath(common.parseJSON(policy.notificationDef, {}), 
"0.recipients", "");
+                               policy.__mailList = policy.__mailStr.trim() === 
"" ? [] : policy.__mailStr.split(/[,;]/);
+                               policy.__expression = 
common.parseJSON(policy.policyDef, {}).expression;
+
+                               $scope.policy = policy;
+                               
Site.current(Site.find($scope.policy.tags.site));
+                               console.log($scope.policy);
+                       }
+
+                       // Visualization
+                       var _endTime = 
app.time.now().hour(23).minute(59).second(59).millisecond(0);
+                       var _startTime = _endTime.clone().subtract(1, 
"month").hour(0).minute(0).second(0).millisecond(0);
+                       var _cond = {
+                               dataSource: policy.tags.dataSource,
+                               policyId: policy.tags.policyId,
+                               _startTime: _startTime,
+                               _endTime: _endTime
+                       };
+
+                       // > eagle.policy.eval.count
+                       $scope.policyEvalSeries = 
nvd3.convert.eagle([Entities.querySeries("GenericMetricService", 
$.extend({_metricName: "eagle.policy.eval.count"}, _cond), "@cluster", 
"sum(value)", 60 * 24)]);
+
+                       // > eagle.policy.eval.fail.count
+                       $scope.policyEvalFailSeries = 
nvd3.convert.eagle([Entities.querySeries("GenericMetricService", 
$.extend({_metricName: "eagle.policy.eval.fail.count"}, _cond), "@cluster", 
"sum(value)", 60 * 24)]);
+
+                       // > eagle.alert.count
+                       $scope.alertSeries = 
nvd3.convert.eagle([Entities.querySeries("GenericMetricService", 
$.extend({_metricName: "eagle.alert.count"}, _cond), "@cluster", "sum(value)", 
60 * 24)]);
+
+                       // > eagle.alert.fail.count
+                       $scope.alertFailSeries = 
nvd3.convert.eagle([Entities.querySeries("GenericMetricService", 
$.extend({_metricName: "eagle.alert.fail.count"}, _cond), "@cluster", 
"sum(value)", 60 * 24)]);
+
+                       // Alert list
+                       $scope.alertList = 
Entities.queryEntities("AlertService", {
+                               site: Site.current().tags.site,
+                               dataSource: policy.tags.dataSource,
+                               policyId: policy.tags.policyId,
+                               _pageSize: MAX_PAGESIZE,
+                               _duration: 1000 * 60 * 60 * 24 * 30,
+                               __ETD: 1000 * 60 * 60 * 24
+                       });
+               });
+
+               // Function
+               $scope.updatePolicyStatus = Policy.updatePolicyStatus;
+               $scope.deletePolicy = function(policy) {
+                       Policy.deletePolicy(policy, function() {
+                               location.href = "#/common/policyList";
+                       });
+               };
+       });
+
+       // ======================== Policy Edit =========================
+       function policyCtrl(create, PageConfig, Site, Policy, $scope, 
$wrapState, $q, Entities, Application, Authorization) {
+               PageConfig.pageTitle = create ? "Policy Create" : "Policy Edit";
+               PageConfig.pageSubTitle = Site.current().tags.site;
+               PageConfig
+                       .addNavPath("Policy List", "/common/policyList")
+                       .addNavPath("Policy Edit");
+
+               var _winTimeDesc = "Number unit[millisecond, sec, min, hour, 
day, month, year]. e.g. 23 sec";
+               var _winTimeRegex = 
/^\d+\s+(millisecond|milliseconds|second|seconds|sec|minute|minutes|min|hour|hours|day|days|week|weeks|month|months|year|years)$/;
+               var _winTimeDefaultValue = '10 min';
+               $scope.config = {
+                       window: [
+                               // Display name, window type, required 
columns[Title, Description || "LONG_FIELD", Regex check, default value]
+                               {
+                                       title: "Message Time Slide",
+                                       description: "Using timestamp filed 
from input is used as event's timestamp",
+                                       type: "externalTime",
+                                       fields:[
+                                               {title: "Field", defaultValue: 
"timestamp", hide: true},
+                                               {title: "Time", description: 
_winTimeDesc, regex: _winTimeRegex, defaultValue: _winTimeDefaultValue}
+                                       ]
+                               },
+                               {
+                                       title: "System Time Slide",
+                                       description: "Using System time is used 
as timestamp for event's timestamp",
+                                       type: "time",
+                                       fields:[
+                                               {title: "Time", description: 
_winTimeDesc, regex: _winTimeRegex, defaultValue: _winTimeDefaultValue}
+                                       ]
+                               },
+                               {
+                                       title: "System Time Batch",
+                                       description: "Same as System Time 
Window except the policy is evaluated at fixed duration",
+                                       type: "timeBatch",
+                                       fields:[
+                                               {title: "Time", description: 
_winTimeDesc, regex: _winTimeRegex, defaultValue: _winTimeDefaultValue}
+                                       ]
+                               },
+                               {
+                                       title: "Length Slide",
+                                       description: "The slide window has a 
fixed length",
+                                       type: "length",
+                                       fields:[
+                                               {title: "Number", description: 
"Number only. e.g. 1023", regex: /^\d+$/}
+                                       ]
+                               },
+                               {
+                                       title: "Length Batch",
+                                       description: "Same as Length window 
except the policy is evaluated in batch mode when fixed event count reached",
+                                       type: "lengthBatch",
+                                       fields:[
+                                               {title: "Number", description: 
"Number only. e.g. 1023", regex: /^\d+$/}
+                                       ]
+                               }
+                       ]
+               };
+
+               $scope.create = create;
+               $scope.encodedRowkey = $wrapState.param.filter;
+
+               $scope.step = 0;
+               $scope.dataSources = {};
+               $scope.policy = {};
+
+               // ==========================================
+               // =            Data Preparation            =
+               // ==========================================
+               // Steam list
+               var _streamList = Entities.queryEntities("AlertStreamService", 
{dataSource: Application.current().tags.application});
+               var _executorList = 
Entities.queryEntities("AlertExecutorService", {dataSource: 
Application.current().tags.application});
+               $scope.streamList = _streamList;
+               $scope.executorList = _executorList;
+               $scope.streamReady = false;
+
+               $q.all([_streamList._promise, 
_executorList._promise]).then(function() {
+                       // Map executor with stream
+                       $.each(_executorList, function(i, executor) {
+                               $.each(_streamList, function(j, stream) {
+                                       if(stream.tags.dataSource === 
executor.tags.dataSource && stream.tags.streamName === 
executor.tags.streamName) {
+                                               stream.alertExecutor = executor;
+                                               return false;
+                                       }
+                               });
+                       });
+
+                       // Fill stream list
+                       $.each(_streamList, function(i, unit) {
+                               var _srcStreamList = 
$scope.dataSources[unit.tags.dataSource] = 
$scope.dataSources[unit.tags.dataSource] || [];
+                               _srcStreamList.push(unit);
+                       });
+
+                       $scope.streamReady = true;
+
+                       // ==========================================
+                       // =                Function                =
+                       // ==========================================
+                       function _findStream(dataSource, streamName) {
+                               var _streamList = 
$scope.dataSources[dataSource];
+                               if(!_streamList) return null;
+
+                               for(var i = 0 ; i < _streamList.length ; i += 
1) {
+                                       if(_streamList[i].tags.streamName === 
streamName) {
+                                               return _streamList[i];
+                                       }
+                               }
+                               return null;
+                       }
+
+                       // ==========================================
+                       // =              Step control              =
+                       // ==========================================
+                       $scope.steps = [
+                               // >> Select stream
+                               {
+                                       title: "Select Stream",
+                                       ready: function() {
+                                               return $scope.streamReady;
+                                       },
+                                       init: function() {
+                                               $scope.policy.__.streamName = 
$scope.policy.__.streamName ||
+                                                       
common.array.find($scope.policy.tags.dataSource, _streamList, 
"tags.dataSource").tags.streamName;
+                                       },
+                                       nextable: function() {
+                                               var _streamName = 
common.getValueByPath($scope.policy, "__.streamName");
+                                               if(!_streamName) return false;
+
+                                               // Detect stream in current 
data source list
+                                               return 
!!common.array.find(_streamName, 
$scope.dataSources[$scope.policy.tags.dataSource], "tags.streamName");
+                                       }
+                               },
+
+                               // >> Define Alert Policy
+                               {
+                                       title: "Define Alert Policy",
+                                       init: function() {
+                                               // Normal mode will fetch meta 
list
+                                               if(!$scope.policy.__.advanced) {
+                                                       var _stream = 
_findStream($scope.policy.tags.dataSource, $scope.policy.__.streamName);
+                                                       $scope._stream = 
_stream;
+
+                                                       if(!_stream.metas) {
+                                                               _stream.metas = 
Entities.queryEntities("AlertStreamSchemaService", {dataSource: 
$scope.policy.tags.dataSource, streamName: $scope.policy.__.streamName});
+                                                               
_stream.metas._promise.then(function() {
+                                                                       
_stream.metas.sort(function(a, b) {
+                                                                               
if(a.tags.attrName < b.tags.attrName) {
+                                                                               
        return -1;
+                                                                               
} else if(a.tags.attrName > b.tags.attrName) {
+                                                                               
        return 1;
+                                                                               
}
+                                                                               
return 0;
+                                                                       });
+                                                               });
+                                                       }
+                                               }
+                                       },
+                                       ready: function() {
+                                               if(!$scope.policy.__.advanced) {
+                                                       return 
$scope._stream.metas._promise.$$state.status === 1;
+                                               }
+                                               return true;
+                                       },
+                                       nextable: function() {
+                                               if($scope.policy.__.advanced) {
+                                                       // Check stream source
+                                                       $scope._stream = null;
+                                                       
$.each($scope.dataSources[$scope.policy.tags.dataSource], function(i, stream) {
+                                                               
if(($scope.policy.__._expression || "").indexOf(stream.tags.streamName) !== -1) 
{
+                                                                       
$scope._stream = stream;
+                                                                       return 
false;
+                                                               }
+                                                       });
+                                                       return $scope._stream;
+                                               } else {
+                                                       // Window
+                                                       
if($scope.policy.__.windowConfig) {
+                                                               var _winMatch = 
true;
+                                                               var _winConds = 
$scope.getWindow().fields;
+                                                               
$.each(_winConds, function(i, cond) {
+                                                                       
if(!(cond.val || "").match(cond.regex)) {
+                                                                               
_winMatch = false;
+                                                                               
return false;
+                                                                       }
+                                                               });
+                                                               if(!_winMatch) 
return false;
+
+                                                               // Aggregation
+                                                               
if($scope.policy.__.groupAgg) {
+                                                                       
if(!$scope.policy.__.groupAggPath ||
+                                                                               
!$scope.policy.__.groupCondOp ||
+                                                                               
!$scope.policy.__.groupCondVal) {
+                                                                               
return false;
+                                                                       }
+                                                               }
+                                                       }
+                                               }
+                                               return true;
+                                       }
+                               },
+
+                               // >> Configuration & Notification
+                               {
+                                       title: "Configuration & Notification",
+                                       nextable: function() {
+                                               return 
!!$scope.policy.tags.policyId;
+                                       }
+                               }
+                       ];
+
+                       // ==========================================
+                       // =              Policy Logic              =
+                       // ==========================================
+                       _streamList._promise.then(function() {
+                               // Initial policy entity
+                               if(create) {
+                                       $scope.policy = {
+                                               __: {
+                                                       toJSON: jQuery.noop,
+                                                       conditions: {},
+                                                       notification: [],
+                                                       dedupe: {
+                                                               
alertDedupIntervalMin: 10,
+                                                               
emailDedupIntervalMin: 10
+                                                       },
+                                                       policy: {},
+                                                       window: "externalTime",
+                                                       group: "",
+                                                       groupAgg: "count",
+                                                       groupAggPath: 
"timestamp",
+                                                       groupCondOp: ">=",
+                                                       groupCondVal: "2"
+                                               },
+                                               desc: "",
+                                               enabled: true,
+                                               prefix: "alertdef",
+                                               remediationDef: "",
+                                               tags: {
+                                                       dataSource: 
Application.current().tags.application,
+                                                       policyType: 
"siddhiCEPEngine"
+                                               }
+                                       };
+
+                                       // If configured data source
+                                       if($wrapState.param.dataSrc) {
+                                               $scope.policy.tags.dataSource = 
$wrapState.param.dataSrc;
+                                               
if(common.array.find($wrapState.param.dataSrc, Site.current().dataSrcList, 
"tags.dataSource")) {
+                                                       setTimeout(function() {
+                                                               
$scope.changeStep(0, 2, false);
+                                                               $scope.$apply();
+                                                       }, 1);
+                                               }
+                                       }
+
+                                       // Start step
+                                       $scope.changeStep(0, 1, false);
+                                       console.log($scope.policy);
+                               } else {
+                                       var _policy = 
Entities.queryEntity("AlertDefinitionService", $scope.encodedRowkey);
+                                       _policy._promise.then(function() {
+                                               if(_policy.length) {
+                                                       $scope.policy = 
_policy[0];
+                                                       $scope.policy.__ = {
+                                                               toJSON: 
jQuery.noop
+                                                       };
+
+                                                       
Site.current(Site.find($scope.policy.tags.site));
+                                               } else {
+                                                       $.dialog({
+                                                               title: "OPS",
+                                                               content: 
"Policy not found!"
+                                                       }, function() {
+                                                               
$location.path("/common/policyList");
+                                                               $scope.$apply();
+                                                       });
+                                                       return;
+                                               }
+
+                                               // === Revert inner data ===
+                                               // >> De-dupe
+                                               $scope.policy.__.dedupe = 
common.parseJSON($scope.policy.dedupeDef, {});
+
+                                               // >> Notification
+                                               $scope.policy.__.notification = 
common.parseJSON($scope.policy.notificationDef, []);
+
+                                               // >> Policy
+                                               var _policyUnit = 
$scope.policy.__.policy = common.parseJSON($scope.policy.policyDef);
+
+                                               // >> Parse expression
+                                               $scope.policy.__.conditions = 
{};
+                                               var _condition = 
_policyUnit.expression.match(/from\s+(\w+)(\[(.*)])?(#window[^\)]*\))?\s+(select
 (\w+, )?(\w+)\((\w+)\) as [\w\d_]+ (group by (\w+) )?having ([\w\d_]+) 
([<>=]+) ([^\s]+))?/);
+                                               var _cond_stream = 
_condition[1];
+                                               var _cond_query = _condition[3] 
|| "";
+                                               var _cond_window = 
_condition[4];
+                                               var _cond_group = _condition[5];
+                                               var _cond_groupUnit = 
_condition.slice(7,14);
+
+                                               if(!_condition) {
+                                                       
$scope.policy.__.advanced = true;
+                                               } else {
+                                                       // > StreamName
+                                                       var _streamName = 
_cond_stream;
+                                                       var _cond = _cond_query;
+
+                                                       
$scope.policy.__.streamName = _streamName;
+
+                                                       // > Conditions
+                                                       // Loop condition groups
+                                                       if(_cond.trim() !== "" 
&& /^\(.*\)$/.test(_cond)) {
+                                                               var _condGrps = 
_cond.substring(1, _cond.length - 1).split(/\)\s+and\s+\(/);
+                                                               
$.each(_condGrps, function(i, line) {
+                                                                       // Loop 
condition cells
+                                                                       var 
_condCells = line.split(/\s+or\s+/);
+                                                                       
$.each(_condCells, function(i, cell) {
+                                                                               
var _opMatch = 
cell.match(/(\S*)\s*(==|!=|>|<|>=|<=|contains)\s*('(?:[^'\\]|\\.)*'|[\w\d]+)/);
+                                                                               
if(!common.isEmpty(_opMatch)) {
+                                                                               
        var _key = _opMatch[1];
+                                                                               
        var _op = _opMatch[2];
+                                                                               
        var _val = _opMatch[3];
+                                                                               
        var _conds = $scope.policy.__.conditions[_key] = 
$scope.policy.__.conditions[_key] || [];
+                                                                               
        var _type = "";
+                                                                               
        if(_val.match(/'.*'/)) {
+                                                                               
                _val = _val.slice(1, -1);
+                                                                               
                _type = "string";
+                                                                               
        } else if(_val === "true" || _val === "false") {
+                                                                               
                var _regexMatch = _key.match(/^str:regexp\((\w+),'(.*)'\)/);
+                                                                               
                var _containsMatch = 
_key.match(/^str:contains\((\w+),'(.*)'\)/);
+                                                                               
                var _mathes = _regexMatch || _containsMatch;
+                                                                               
                if(_mathes) {
+                                                                               
                        _key = _mathes[1];
+                                                                               
                        _val = _mathes[2];
+                                                                               
                        _type = "string";
+                                                                               
                        _op = _regexMatch ? "regex" : "contains";
+                                                                               
                        _conds = $scope.policy.__.conditions[_key] = 
$scope.policy.__.conditions[_key] || [];
+                                                                               
                } else {
+                                                                               
                        _type = "bool";
+                                                                               
                }
+                                                                               
        } else {
+                                                                               
                _type = "number";
+                                                                               
        }
+                                                                               
        _conds.push($scope._CondUnit(_key, _op, _val, _type));
+                                                                               
}
+                                                                       });
+                                                               });
+                                                       } else if(_cond_query 
!== "") {
+                                                               
$scope.policy.__.advanced = true;
+                                                       }
+                                               }
+
+                                               if($scope.policy.__.advanced) {
+                                                       
$scope.policy.__._expression = _policyUnit.expression;
+                                               } else {
+                                                       // > window
+                                                       if(!_cond_window) {
+                                                               
$scope.policy.__.window = "externalTime";
+                                                               
$scope.policy.__.group = "";
+                                                               
$scope.policy.__.groupAgg = "count";
+                                                               
$scope.policy.__.groupAggPath = "timestamp";
+                                                               
$scope.policy.__.groupCondOp = ">=";
+                                                               
$scope.policy.__.groupCondVal = "2";
+                                                       } else {
+                                                               try {
+                                                                       
$scope.policy.__.windowConfig = true;
+
+                                                                       var 
_winCells = _cond_window.match(/\.(\w+)\((.*)\)/);
+                                                                       
$scope.policy.__.window = _winCells[1];
+                                                                       var 
_winConds = $scope.getWindow().fields;
+                                                                       
$.each(_winCells[2].split(","), function(i, val) {
+                                                                               
_winConds[i].val = val;
+                                                                       });
+
+                                                                       // Group
+                                                                       
if(_cond_group) {
+                                                                               
$scope.policy.__.group = _cond_groupUnit[3];
+                                                                               
$scope.policy.__.groupAgg = _cond_groupUnit[0];
+                                                                               
$scope.policy.__.groupAggPath = _cond_groupUnit[1];
+                                                                               
$scope.policy.__.groupAggAlias = _cond_groupUnit[4] === "aggValue" ? "" : 
_cond_groupUnit[4];
+                                                                               
$scope.policy.__.groupCondOp = _cond_groupUnit[5];
+                                                                               
$scope.policy.__.groupCondVal = _cond_groupUnit[6];
+                                                                       } else {
+                                                                               
$scope.policy.__.group = "";
+                                                                               
$scope.policy.__.groupAgg = "count";
+                                                                               
$scope.policy.__.groupAggPath = "timestamp";
+                                                                               
$scope.policy.__.groupCondOp = ">=";
+                                                                               
$scope.policy.__.groupCondVal = "2";
+                                                                       }
+                                                               } catch(err) {
+                                                                       
$scope.policy.__.window = "externalTime";
+                                                               }
+                                                       }
+                                               }
+
+                                               $scope.changeStep(0, 2, false);
+                                               console.log($scope.policy);
+                                       });
+                               }
+                       });
+
+                       // ==========================================
+                       // =                Function                =
+                       // ==========================================
+                       // UI: Highlight select step
+                       $scope.stepSelect = function(step) {
+                               return step === $scope.step ? "active" : "";
+                       };
+
+                       // UI: Collapse all
+                       $scope.collapse = function(cntr) {
+                               var _list = 
$(cntr).find(".collapse").css("height", "auto");
+                               if(_list.hasClass("in")) {
+                                       _list.removeClass("in");
+                               } else {
+                                       _list.addClass("in");
+                               }
+                       };
+
+                       // Step process. Will fetch target step attribute and 
return boolean
+                       function _check(key, step) {
+                               var _step = $scope.steps[step - 1];
+                               if(!_step) return;
+
+                               var _value = _step[key];
+                               if(typeof _value === "function") {
+                                       return _value();
+                               } else if(typeof _value === "boolean") {
+                                       return _value;
+                               }
+                               return true;
+                       }
+                       // Check step is ready. Otherwise will display load 
animation
+                       $scope.stepReady = function(step) {
+                               return _check("ready", step);
+                       };
+                       // Check whether process next step. Otherwise will 
disable next button
+                       $scope.checkNextable = function(step) {
+                               return !_check("nextable", step);
+                       };
+                       // Switch step
+                       $scope.changeStep = function(step, targetStep, check) {
+                               if(check === false || _check("checkStep", 
step)) {
+                                       $scope.step =  targetStep;
+
+                                       _check("init", targetStep);
+                               }
+                       };
+
+                       // Window
+                       $scope.getWindow = function() {
+                               if(!$scope.policy || !$scope.policy.__) return 
null;
+                               return 
common.array.find($scope.policy.__.window, $scope.config.window, "type");
+                       };
+
+                       // Aggregation
+                       $scope.groupAggPathList = function() {
+                               return $.grep(common.getValueByPath($scope, 
"_stream.metas", []), function(meta) {
+                                       return $.inArray(meta.attrType, 
['long','integer','number', 'double', 'float']) !== -1;
+                               });
+                       };
+
+                       $scope.updateGroupAgg = function() {
+                               $scope.policy.__.groupAggPath = 
$scope.policy.__.groupAggPath || 
common.getValueByPath($scope.groupAggPathList()[0], "tags.attrName");
+
+                               if($scope.policy.__.groupAgg === 'count') {
+                                       $scope.policy.__.groupAggPath = 
'timestamp';
+                               }
+                       };
+
+                       // Resolver
+                       $scope.resolverTypeahead = function(value, resolver) {
+                               var _resolverList = 
Entities.query("stream/attributeresolve", {
+                                       site: Site.current().tags.site,
+                                       resolver: resolver,
+                                       query: value
+                               });
+                               return _resolverList._promise.then(function() {
+                                       return _resolverList;
+                               });
+                       };
+
+                       // Used for input box when pressing enter
+                       $scope.conditionPress = function(event) {
+                               if(event.which == 13) {
+                                       setTimeout(function() {
+                                               
$(event.currentTarget).closest(".input-group").find("button").click();
+                                       }, 1);
+                               }
+                       };
+                       // Check whether has condition
+                       $scope.hasCondition = function(key, type) {
+                               var _list = 
common.getValueByPath($scope.policy.__.conditions, key, []);
+                               if(_list.length === 0) return false;
+
+                               if(type === "bool") {
+                                       return !_list[0].ignored();
+                               }
+                               return true;
+                       };
+                       // Condition unit definition
+                       $scope._CondUnit = function(key, op, value, type) {
+                               return {
+                                       key: key,
+                                       op: op,
+                                       val: value,
+                                       type: type,
+                                       ignored: function() {
+                                               return this.type === "bool" && 
this.val === "none";
+                                       },
+                                       getVal: function() {
+                                               return this.type === "string" ? 
"'" + this.val + "'" : this.val;
+                                       },
+                                       toString: function() {
+                                               return this.op + " " + 
this.getVal();
+                                       },
+                                       toCondString: function() {
+                                               var _op = this.op === "=" ? 
"==" : this.op;
+                                               if(_op === "regex") {
+                                                       return "str:regexp(" + 
this.key + "," + this.getVal() + ")==true";
+                                               } else if(_op === "contains") {
+                                                       return "str:contains(" 
+ this.key + "," + this.getVal() + ")==true";
+                                               } else {
+                                                       return this.key + " " + 
_op + " " + this.getVal();
+                                               }
+                                       }
+                               };
+                       };
+                       // Add condition for policy
+                       $scope.addCondition = function(key, op, value, type) {
+                               if(value === "" || value === undefined) return 
false;
+
+                               var _condList = 
$scope.policy.__.conditions[key] = $scope.policy.__.conditions[key] || [];
+                               _condList.push($scope._CondUnit(key, op, value, 
type));
+                               return true;
+                       };
+                       // Convert condition list to description string
+                       $scope.parseConditionDesc = function(key) {
+                               return $.map($scope.policy.__.conditions[key] 
|| [], function(cond) {
+                                       if(!cond.ignored()) return "[" + 
cond.toString() + "]";
+                               }).join(" or ");
+                       };
+
+                       // To query
+                       $scope.toQuery = function() {
+                               if(!$scope.policy.__) return "";
+
+                               if($scope.policy.__.advanced) return 
$scope.policy.__._expression;
+
+                               // > Query
+                               var _query = 
$.map(common.getValueByPath($scope.policy, "__.conditions", {}), function(list) 
{
+                                       var _conds = $.map(list, function(cond) 
{
+                                               if(!cond.ignored()) return 
cond.toCondString();
+                                       });
+                                       if(_conds.length) {
+                                               return "(" + _conds.join(" or 
") + ")";
+                                       }
+                               }).join(" and ");
+                               if(_query) {
+                                       _query = "[" + _query + "]";
+                               }
+
+                               // > Window
+                               var _window = $scope.getWindow();
+                               var _windowStr = "";
+                               if($scope.policy.__.windowConfig) {
+                                       _windowStr = $.map(_window.fields, 
function(field) {
+                                               return field.val;
+                                       }).join(",");
+                                       _windowStr = "#window." + _window.type 
+ "(" + _windowStr + ")";
+
+                                       // > Group
+                                       if($scope.policy.__.group) {
+                                               _windowStr += common.template(" 
select ${group}, ${groupAgg}(${groupAggPath}) as ${groupAggAlias} group by 
${group} having ${groupAggAlias} ${groupCondOp} ${groupCondVal}", {
+                                                       group: 
$scope.policy.__.group,
+                                                       groupAgg: 
$scope.policy.__.groupAgg,
+                                                       groupAggPath: 
$scope.policy.__.groupAggPath,
+                                                       groupCondOp: 
$scope.policy.__.groupCondOp,
+                                                       groupCondVal: 
$scope.policy.__.groupCondVal,
+                                                       groupAggAlias: 
$scope.policy.__.groupAggAlias || "aggValue"
+                                               });
+                                       } else {
+                                               _windowStr += common.template(" 
select ${groupAgg}(${groupAggPath}) as ${groupAggAlias} having ${groupAggAlias} 
${groupCondOp} ${groupCondVal}", {
+                                                       groupAgg: 
$scope.policy.__.groupAgg,
+                                                       groupAggPath: 
$scope.policy.__.groupAggPath,
+                                                       groupCondOp: 
$scope.policy.__.groupCondOp,
+                                                       groupCondVal: 
$scope.policy.__.groupCondVal,
+                                                       groupAggAlias: 
$scope.policy.__.groupAggAlias || "aggValue"
+                                               });
+                                       }
+                               } else {
+                                       _windowStr = " select *";
+                               }
+
+                               return common.template("from 
${stream}${query}${window} insert into outputStream;", {
+                                       stream: $scope.policy.__.streamName,
+                                       query: _query,
+                                       window: _windowStr
+                               });
+                       };
+
+                       // ==========================================
+                       // =             Update Policy              =
+                       // ==========================================
+                       // dedupeDef notificationDef policyDef
+                       $scope.finishPolicy = function() {
+                               $scope.lock = true;
+
+                               // dedupeDef
+                               $scope.policy.dedupeDef = 
JSON.stringify($scope.policy.__.dedupe);
+
+                               // notificationDef
+                               $scope.policy.__.notification = 
$scope.policy.__.notification || [];
+                               var _notificationUnit = 
$scope.policy.__.notification[0];
+                               if(_notificationUnit) {
+                                       _notificationUnit.flavor = "email";
+                                       _notificationUnit.id = "email_1";
+                                       _notificationUnit.tplFileName = "";
+                               }
+                               $scope.policy.notificationDef = 
JSON.stringify($scope.policy.__.notification);
+
+                               // policyDef
+                               $scope.policy.__._dedupTags = 
$scope.policy.__._dedupTags || {};
+                               $scope.policy.__.policy = {
+                                       expression: $scope.toQuery(),
+                                       type: "siddhiCEPEngine"
+                               };
+                               $scope.policy.policyDef = 
JSON.stringify($scope.policy.__.policy);
+
+                               // alertExecutorId
+                               if($scope._stream.alertExecutor) {
+                                       $scope.policy.tags.alertExecutorId = 
$scope._stream.alertExecutor.tags.alertExecutorId;
+                               } else {
+                                       $scope.lock = false;
+                                       $.dialog({
+                                               title: "OPS!",
+                                               content: "Alert Executor not 
defined! Please check 'AlertExecutorService'!"
+                                       });
+                                       return;
+                               }
+
+                               // site
+                               $scope.policy.tags.site = 
$scope.policy.tags.site || Site.current().tags.site;
+
+                               // owner
+                               $scope.policy.owner = 
Authorization.userProfile.username;
+
+                               // Update function
+                               function _updatePolicy() {
+                                       
Entities.updateEntity("AlertDefinitionService", 
$scope.policy)._promise.success(function(data) {
+                                               $.dialog({
+                                                       title: "Success",
+                                                       content: (create ? 
"Create" : "Update") + " success!"
+                                               }, function() {
+                                                       if(data.success) {
+                                                               location.href = 
"#/common/policyList";
+                                                       } else {
+                                                               $.dialog({
+                                                                       title: 
"OPS",
+                                                                       
content: (create ? "Create" : "Update") + "failed!" + JSON.stringify(data)
+                                                               });
+                                                       }
+                                               });
+
+                                               $scope.create = create = false;
+                                               $scope.encodedRowkey = 
data.obj[0];
+                                       }).error(function(data) {
+                                               $.dialog({
+                                                       title: "OPS",
+                                                       content: (create ? 
"Create" : "Update") + "failed!" + JSON.stringify(data)
+                                               });
+                                       }).then(function() {
+                                               $scope.lock = false;
+                                       });
+                               }
+
+                               // Check if already exist
+                               if($scope.create) {
+                                       var _checkList = 
Entities.queryEntities("AlertDefinitionService", {
+                                               alertExecutorId: 
$scope.policy.tags.alertExecutorId,
+                                               policyId: 
$scope.policy.tags.policyId,
+                                               policyType: "siddhiCEPEngine",
+                                               dataSource: 
$scope.policy.tags.dataSource
+                                       });
+                                       _checkList._promise.then(function() {
+                                               if(_checkList.length) {
+                                                       $.dialog({
+                                                               title: 
"Override Confirm",
+                                                               content: 
"Already exists PolicyID '" + $scope.policy.tags.policyId + "'. Do you want to 
override?",
+                                                               confirm: true
+                                                       }, function(ret) {
+                                                               if(ret) {
+                                                                       
_updatePolicy();
+                                                               } else {
+                                                                       
$scope.lock = false;
+                                                                       
$scope.$apply();
+                                                               }
+                                                       });
+                                               } else {
+                                                       _updatePolicy();
+                                               }
+                                       });
+                               } else {
+                                       _updatePolicy();
+                               }
+                       };
+               });
+       }
+
+       feature.controller('policyCreate', function(PageConfig, Site, Policy, 
$scope, $wrapState, $q, Entities, Application, Authorization) {
+               var _args = [true];
+               _args.push.apply(_args, arguments);
+               policyCtrl.apply(this, _args);
+       }, "policyEdit");
+       feature.controller('policyEdit', function(PageConfig, Site, Policy, 
$scope, $wrapState, $q, Entities, Application, Authorization) {
+               PageConfig.lockSite = true;
+               var _args = [false];
+               _args.push.apply(_args, arguments);
+               policyCtrl.apply(this, _args);
+       });
+
+       // ==============================================================
+       // =                           Alerts                           =
+       // ==============================================================
+
+       // ========================= Alert List =========================
+       feature.navItem("alertList", "Alerts", "exclamation-triangle");
+       feature.controller('alertList', function(PageConfig, Site, $scope, 
$wrapState, $interval, $timeout, Entities, Application) {
+               PageConfig.pageSubTitle = Site.current().tags.site;
+
+               var MAX_PAGESIZE = 10000;
+
+               // Initial load
+               $scope.application = Application.current();
+
+               $scope.alertList = [];
+               $scope.alertList.ready = false;
+
+               // Load data
+               function _loadAlerts() {
+                       if($scope.alertList._promise) {
+                               $scope.alertList._promise.abort();
+                       }
+
+                       var _list = Entities.queryEntities("AlertService", {
+                               site: Site.current().tags.site,
+                               dataSource: $scope.application.tags.application,
+                               hostname: null,
+                               _pageSize: MAX_PAGESIZE,
+                               _duration: 1000 * 60 * 60 * 24 * 30,
+                               __ETD: 1000 * 60 * 60 * 24
+                       });
+                       $scope.alertList._promise = _list._promise;
+                       _list._promise.then(function() {
+                               var index;
+
+                               if($scope.alertList[0]) {
+                                       // List new alerts
+                                       for(index = 0 ; index < _list.length ; 
index += 1) {
+                                               var _alert = _list[index];
+                                               _alert.__new = true;
+                                               if(_alert.encodedRowkey === 
$scope.alertList[0].encodedRowkey) {
+                                                       break;
+                                               }
+                                       }
+
+                                       if(index > 0) {
+                                               
$scope.alertList.unshift.apply($scope.alertList, _list.slice(0, index));
+
+                                               // Clean up UI highlight
+                                               $timeout(function() {
+                                                       $.each(_list, 
function(i, alert) {
+                                                               delete 
alert.__new;
+                                                       });
+                                               }, 100);
+                                       }
+                               } else {
+                                       // List all alerts
+                                       
$scope.alertList.push.apply($scope.alertList, _list);
+                               }
+
+                               $scope.alertList.ready = true;
+                       });
+               }
+
+               _loadAlerts();
+               var _loadInterval = $interval(_loadAlerts, 
app.time.refreshInterval);
+               $scope.$on('$destroy',function(){
+                       $interval.cancel(_loadInterval);
+               });
+       });
+
+       // ========================= Alert List =========================
+       feature.controller('alertDetail', function(PageConfig, Site, $scope, 
$wrapState, Entities) {
+               PageConfig.pageTitle = "Alert Detail";
+               PageConfig.lockSite = true;
+               PageConfig
+                       .addNavPath("Alert List", "/common/alertList")
+                       .addNavPath("Alert Detail");
+
+               $scope.common = common;
+
+               // Query policy
+               $scope.alertList = Entities.queryEntity("AlertService", 
$wrapState.param.filter);
+               $scope.alertList._promise.then(function() {
+                       if($scope.alertList.length === 0) {
+                               $.dialog({
+                                       title: "OPS!",
+                                       content: "Alert not found!"
+                               }, function() {
+                                       location.href = "#/common/alertList";
+                               });
+                       } else {
+                               $scope.alert = $scope.alertList[0];
+                               Site.current(Site.find($scope.alert.tags.site));
+                               console.log($scope.alert);
+                       }
+               });
+
+               // UI
+               $scope.getMessageTime = function(alert) {
+                       var _time = common.getValueByPath(alert, 
"alertContext.properties.timestamp");
+                       return Number(_time);
+               };
+       });
+})();
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/d37643b0/eagle-webservice/src/main/webapp/app/public/feature/common/page/alertDetail.html
----------------------------------------------------------------------
diff --git 
a/eagle-webservice/src/main/webapp/app/public/feature/common/page/alertDetail.html
 
b/eagle-webservice/src/main/webapp/app/public/feature/common/page/alertDetail.html
new file mode 100644
index 0000000..fee5ab1
--- /dev/null
+++ 
b/eagle-webservice/src/main/webapp/app/public/feature/common/page/alertDetail.html
@@ -0,0 +1,61 @@
+<!--
+  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" id="policyId">
+                       {{alert.tags.policyId}}
+                       <small>{{common.format.date(alert.timestamp)}}</small>
+               </h3>
+       </div><!-- /.box-header -->
+
+       <div class="box-body">
+               <a class="btn btn-primary pull-right" 
href="#/common/policyDetail/?policy={{alert.tags.policyId}}&site={{alert.tags.site}}&executor={{alert.tags.alertExecutorId}}">View
 Policy</a>
+
+               <div class="inline-group">
+                       <dl><dt>Site</dt><dd>{{alert.tags.site}}</dd></dl>
+                       <dl><dt>Data 
Source</dt><dd>{{alert.tags.dataSource}}</dd></dl>
+               </div>
+               <div class="inline-group">
+                       <dl><dt>Alert 
Time</dt><dd>{{common.format.date(alert.timestamp)}}</dd></dl>
+                       <dl><dt>Message 
Time</dt><dd>{{common.format.date(alert.alertContext.properties.timestamp)}}</dd></dl>
+               </div>
+               <!--div class="inline-group">
+                       
<dl><dt>Severity</dt><dd>{{alert.alertContext.properties.severity}}</dd></dl>
+               </div-->
+               <div class="inline-group">
+                       <dl><dt>Stream 
Name</dt><dd>{{alert.tags.sourceStreams}}</dd></dl>
+               </div>
+               <div class="inline-group">
+                       <dl><dt>Alert 
Source</dt><dd>{{alert.tags.alertSource}}</dd></dl>
+               </div>
+               <div class="inline-group">
+                       
<dl><dt>User</dt><dd>{{alert.alertContext.properties.user}}</dd></dl>
+                       
<dl><dt>Host</dt><dd>{{alert.alertContext.properties.host}}</dd></dl>
+               </div>
+               <div class="inline-group">
+                       
<dl><dt>Event</dt><dd>{{alert.alertContext.properties.alertEvent}}</dd></dl>
+               </div>
+               <div class="inline-group">
+                       
<dl><dt>Message</dt><dd>{{alert.alertContext.properties.alertMessage}}</dd></dl>
+               </div>
+       </div><!-- /.box-body -->
+
+       <div class="overlay" ng-hide="alertList._promise.$$state.status === 1;">
+               <i class="fa fa-refresh fa-spin"></i>
+       </div>
+</div>

http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/d37643b0/eagle-webservice/src/main/webapp/app/public/feature/common/page/alertList.html
----------------------------------------------------------------------
diff --git 
a/eagle-webservice/src/main/webapp/app/public/feature/common/page/alertList.html
 
b/eagle-webservice/src/main/webapp/app/public/feature/common/page/alertList.html
new file mode 100644
index 0000000..44f5a32
--- /dev/null
+++ 
b/eagle-webservice/src/main/webapp/app/public/feature/common/page/alertList.html
@@ -0,0 +1,69 @@
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+<div class="box box-primary">
+       <div class="box-header with-border">
+               <i class="fa fa-list-alt"> </i>
+               <h3 class="box-title">
+                       {{application.displayName}}
+               </h3>
+       </div>
+       <div class="box-body">
+               <p ng-show="!alertList.ready">
+                       <span class="fa fa-refresh fa-spin"> </span>
+                       Loading...
+               </p>
+
+               <div sorttable source="alertList" sort="-timestamp">
+                       <table class="table table-bordered" ng-non-bindable>
+                               <thead>
+                                       <tr>
+                                               <th width="170" 
sortpath="timestamp">Alert Time</th>
+                                               <th width="170" 
sortpath="alertContext.properties.timestamp">Message Time</th>
+                                               <th width="105" 
sortpath="tags.dataSource">Data Source</th>
+                                               <!--th width="70" 
sortpath="severity">Type</th-->
+                                               <th width="150" 
sortpath="tags.policyId">Policy Name</th>
+                                               <th width="60" 
sortpath="alertContext.properties.user">User</th>
+                                               <th width="150" 
sortpath="alertContext.properties.host">Host</th>
+                                               <th 
sortpath="alertContext.properties.emailMessage">Description</th>
+                                               <th width="50"> </th>
+                                       </tr>
+                               </thead>
+                               <tbody>
+                                       <tr ng-class="{info : item.__new}">
+                                               
<td>{{common.format.date(item.timestamp)}}</td>
+                                               
<td>{{common.format.date(item.alertContext.properties.timestamp)}}</td>
+                                               
<td>{{item.tags.dataSource}}</td>
+                                               <!--td>{{item.severity}}</td-->
+                                               <td class="text-nowrap">
+                                                       <a class="fa 
fa-share-square-o" ng-show="item.tags.policyId"
+                                                       
href="#/common/policyDetail/?policy={{item.tags.policyId}}&site={{item.tags.site}}&executor={{item.tags.alertExecutorId}}">
 </a>
+                                                       {{item.tags.policyId}}
+                                               </td>
+                                               
<td>{{item.alertContext.properties.user}}</td>
+                                               
<td>{{item.alertContext.properties.host}}</td>
+                                               
<td>{{item.alertContext.properties.alertMessage}}</td>
+                                               <td><a 
href="#/common/alertDetail/{{item.encodedRowkey}}">Detail</a></td>
+                                       </tr>
+                               </tbody>
+                       </table>
+               </div>
+
+       </div>
+       <!--div class="box-footer clearfix">
+       </div-->
+</div>

http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/d37643b0/eagle-webservice/src/main/webapp/app/public/feature/common/page/dataSrcConfig.html
----------------------------------------------------------------------
diff --git 
a/eagle-webservice/src/main/webapp/app/public/feature/common/page/dataSrcConfig.html
 
b/eagle-webservice/src/main/webapp/app/public/feature/common/page/dataSrcConfig.html
new file mode 100644
index 0000000..ba4f728
--- /dev/null
+++ 
b/eagle-webservice/src/main/webapp/app/public/feature/common/page/dataSrcConfig.html
@@ -0,0 +1,125 @@
+<div class="box box-primary" ng-repeat="_site in Site.list">
+       <div class="box-header">
+               <h3 class="box-title">{{_site.name}}</h3>
+               <div class="box-tools pull-right">
+                       <button class="btn btn-box-tool" data-widget="collapse">
+                               <i class="fa fa-minus"></i>
+                       </button>
+                       <button class="btn btn-box-tool" 
ng-click="showSiteEditor(_site)">
+                               <i class="fa fa-wrench"></i>
+                       </button>
+               </div>
+       </div><!-- /.box-header -->
+       <div class="box-body">
+               <div class="row">
+                       <div class="col-md-4" ng-repeat="dataSrc in 
_site.dataSrcList">
+                               <div class="info-box" ng-class="dataSrc.enabled 
!== false ? 'bg-aqua' : 'bg-gray'">
+                                       <span class="info-box-icon"><i 
class="fa fa-suitcase"></i></span>
+                                       <div class="info-box-content">
+                                               <a class="fa fa-cog config 
pull-right" ng-click="showDataSourceEditor(dataSrc)"></a>
+                                               <span 
class="info-box-text">{{dataSrc.desc || dataSrc.tags.dataSource}}</span>
+
+                                               <span class="info-box-number" 
ng-show="dataSrc.hide"><small>-</small></span>
+                                               <span class="info-box-number" 
ng-show="!dataSrc.hide">
+                                                       <span class="fa 
fa-refresh fa-spin" ng-hide="policyStatistic._promise.$$state.status === 
1"></span>
+                                                       <span 
ng-show="policyStatistic._promise.$$state.status === 
1">{{getPolicyCount(_site.name, dataSrc.tags.dataSource)}}</span>
+                                                       <small>Policies</small>
+                                               </span>
+                                               <div class="progress">
+                                                       <div 
class="progress-bar"></div>
+                                               </div>
+                                               <span 
class="progress-description" ng-show="dataSrc.hide"><small>-</small></span>
+                                               <span 
class="progress-description" ng-show="!dataSrc.hide">
+                                                       <span class="fa 
fa-refresh fa-spin" ng-hide="alertStatistic._promise.$$state.status === 
1"></span>
+                                                       <span 
ng-show="alertStatistic._promise.$$state.status === 
1">{{getAlertCount(_site.name, dataSrc.tags.dataSource)}}</span>
+                                                       alerts in 30 Days
+                                               </span>
+                                       </div><!-- /.info-box-content -->
+                               </div>
+                       </div>
+               </div>
+       </div>
+</div>
+
+<!-- Modal: Create / Edit site -->
+<div class="modal fade" id="siteMDL" 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">{{!_siteEntity.srcSite 
? 'Create' : 'Config'}} Site</h4>
+                       </div>
+                       <div class="modal-body">
+                               <div class="form-group">
+                                       <label>* Site Name</label>
+                                       <input type="text" class="form-control" 
placeholder="Site name..." ng-model="_siteEntity.name" 
ng-disabled="!!_siteEntity.srcSite" id="siteName">
+                               </div>
+
+                               <label>
+                                       * Data Source
+                                       <small class="text-muted">at least 
select 1 source</small>
+                               </label>
+                               <div class="checkbox" ng-repeat="item in 
_siteEntity.dataSrcList">
+                                       <label>
+                                               <input type="checkbox" 
value="{{item.name}}"
+                                                          
ng-checked="item.enabled"
+                                                          
ng-click="item.enabled = !item.enabled">
+                                               {{item.name}}
+                                       </label>
+                               </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="confirmUpateSite()"
+                                               
ng-disabled="!checkUpdateSite()">
+                                       {{!_siteEntity.srcSite ? 'Create' : 
'Update'}}
+                               </button>
+                       </div>
+               </div>
+       </div>
+</div>
+
+<!-- Modal: Edit data source -->
+<div class="modal fade" id="dataSrcMDL" 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">{{_dataSrcEntity.tags.dataSource}}</h4>
+                       </div>
+                       <div class="modal-body">
+                               <div class="checkbox">
+                                       <label>
+                                               <input type="checkbox"
+                                                          
ng-checked="_dataSrcEntity.enabled"
+                                                          
ng-click="_dataSrcEntity.enabled = !_dataSrcEntity.enabled">
+                                               Enabled
+                                       </label>
+                               </div>
+
+                               <div class="form-group">
+                                       <label>Configuration</label>
+                                       <textarea type="text" 
class="form-control" placeholder="Data source configuration..." 
ng-model="_dataSrcEntity.config" id="dataSrcConfig" rows="10"></textarea>
+                               </div>
+                       </div>
+                       <div class="modal-footer">
+                               <button type="button" class="btn btn-danger 
pull-left" ng-click="confirmDeleteDataSource()">
+                                       Delete Source
+                               </button>
+
+                               <button type="button" class="btn btn-default" 
data-dismiss="modal">
+                                       Close
+                               </button>
+                               <button type="button" class="btn btn-primary" 
ng-click="confirmUpateDataSource()" ng-disabled="_dataSrcEntityLock">
+                                       Update
+                               </button>
+                       </div>
+               </div>
+       </div>
+</div>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/d37643b0/eagle-webservice/src/main/webapp/app/public/feature/common/page/policyDetail.html
----------------------------------------------------------------------
diff --git 
a/eagle-webservice/src/main/webapp/app/public/feature/common/page/policyDetail.html
 
b/eagle-webservice/src/main/webapp/app/public/feature/common/page/policyDetail.html
new file mode 100644
index 0000000..8670839
--- /dev/null
+++ 
b/eagle-webservice/src/main/webapp/app/public/feature/common/page/policyDetail.html
@@ -0,0 +1,172 @@
+<!--
+  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">
+                       {{policy.tags.policyId}}
+                       <small>{{policy.tags.site}}</small>
+               </h3>
+       </div>
+
+       <div class="box-body">
+               <div class="row">
+                       <div class="col-xs-8">
+                               <div class="inline-group">
+                                       <dl><dt>Data 
Source</dt><dd>{{policy.tags.dataSource}}</dd></dl>
+                                       <dl><dt>Status</dt><dd>
+                                               <span ng-show="policy.enabled" 
class="text-muted"><i class="fa fa-square text-green"></i> Enabled</span>
+                                               <span ng-show="!policy.enabled" 
class="text-muted"><i class="fa fa-square text-muted"></i> Disabled</span>
+                                       </dd></dl>
+                               </div>
+                               <div class="inline-group">
+                                       
<dl><dt>Description</dt><dd>{{policy.desc}}</dd></dl>
+                               </div>
+                               <div class="inline-group">
+                                       <dl><dt>Alert</dt><dd>
+                                               <a href="mailto:{{mail}}"; 
ng-repeat="mail in policy.__mailList track by $index" style="margin-right: 
10px;">{{mail}}</a>
+                                       </dd></dl>
+                               </div>
+                       </div>
+                       <div class="col-xs-4 text-right" 
ng-show="Auth.isRole('ROLE_ADMIN')">
+                               <a class="btn btn-primary" 
href="#/common/policyEdit/{{policy.encodedRowkey}}">Edit</a>
+                               <button class="btn btn-warning" 
ng-show="!policy.enabled" ng-click="updatePolicyStatus(policy, 
true)">Enable</button>
+                               <button class="btn btn-warning" 
ng-show="policy.enabled" ng-click="updatePolicyStatus(policy, 
false)">Disable</button>
+                               <button class="btn btn-danger" 
ng-click="deletePolicy(policy)">Delete</button>
+                       </div>
+               </div>
+       </div>
+
+       <div class="overlay" ng-hide="policyList._promise.$$state.status === 
1;">
+               <i class="fa fa-refresh fa-spin"></i>
+       </div>
+
+       <div class="box-footer clearfix">
+               <a data-toggle="collapse" href="[data-id='query']">
+                       View Query
+               </a>
+               <div data-id="query" class="collapse in">
+                       <pre>{{policy.__expression}}</pre>
+               </div>
+       </div>
+</div>
+
+<div class="nav-tabs-custom">
+       <ul class="nav nav-tabs">
+               <li class="active">
+                       <a href="[data-id='visualization']" 
data-toggle="tab">Visualization</a>
+               </li>
+               <li>
+                       <a href="[data-id='statistics']" 
data-toggle="tab">Statistics</a>
+               </li>
+               <li>
+                       <a href="[data-id='alerts']" 
data-toggle="tab">Alerts</a>
+               </li>
+       </ul>
+       <div class="tab-content">
+               <div class="tab-pane active" data-id="visualization">
+                       <div class="row">
+                               <div class="col-xs-6">
+                                       <div nvd3="policyEvalSeries" 
data-title="Policy Eval Count" data-config="chartConfig" 
class="nvd3-chart-cntr"></div>
+                               </div>
+                               <div class="col-xs-6">
+                                       <div nvd3="policyEvalFailSeries" 
data-title="Policy Eval Fail Count" data-config="chartConfig" 
class="nvd3-chart-cntr"></div>
+                               </div>
+                               <div class="col-xs-6">
+                                       <div nvd3="alertSeries" 
data-title="Alert Count" data-config="chartConfig" 
class="nvd3-chart-cntr"></div>
+                               </div>
+                               <div class="col-xs-6">
+                                       <div nvd3="alertFailSeries" 
data-title="Alert Fail Count" data-config="chartConfig" 
class="nvd3-chart-cntr"></div>
+                               </div>
+                       </div>
+               </div>
+
+               <div class="tab-pane" data-id="statistics">
+                       <div class="row">
+                               <div class="col-xs-3">
+                                       <div class="info-box bg-aqua">
+                                               <span class="info-box-icon"><i 
class="fa fa-bookmark-o"></i></span>
+                                               <div class="info-box-content">
+                                                       <span 
class="info-box-text">Policy Eval Count</span>
+                                                       <span 
class="info-box-number">{{common.array.sum(policyEvalSeries, "1")}} 
<small>(Monthly)</small></span>
+                                                       <span 
class="info-box-number">{{policyEvalSeries[policyEvalSeries.length - 1][1]}} 
<small>(Daily)</small></span>
+                                               </div>
+                                       </div>
+                               </div>
+                               <div class="col-xs-3">
+                                       <div class="info-box bg-red">
+                                               <span class="info-box-icon"><i 
class="fa fa-bookmark-o"></i></span>
+                                               <div class="info-box-content">
+                                                       <span 
class="info-box-text">Policy Eval Fail Count</span>
+                                                       <span 
class="info-box-number">{{common.array.sum(policyEvalFailSeries, "1")}} 
<small>(Monthly)</small></span>
+                                                       <span 
class="info-box-number">{{policyEvalFailSeries[policyEvalFailSeries.length - 
1][1]}} <small>(Daily)</small></span>
+                                               </div>
+                                       </div>
+                               </div>
+                               <div class="col-xs-3">
+                                       <div class="info-box bg-aqua">
+                                               <span class="info-box-icon"><i 
class="fa fa-bookmark-o"></i></span>
+                                               <div class="info-box-content">
+                                                       <span 
class="info-box-text">Alert Count</span>
+                                                       <span 
class="info-box-number">{{common.array.sum(alertSeries, "1")}} 
<small>(Monthly)</small></span>
+                                                       <span 
class="info-box-number">{{alertSeries[alertSeries.length - 1][1]}} 
<small>(Daily)</small></span>
+                                               </div>
+                                       </div>
+                               </div>
+                               <div class="col-xs-3">
+                                       <div class="info-box bg-red">
+                                               <span class="info-box-icon"><i 
class="fa fa-bookmark-o"></i></span>
+                                               <div class="info-box-content">
+                                                       <span 
class="info-box-text">Alert Fail Count</span>
+                                                       <span 
class="info-box-number">{{common.array.sum(alertFailSeries, "1")}} 
<small>(Monthly)</small></span>
+                                                       <span 
class="info-box-number">{{alertFailSeries[alertFailSeries.length - 1][1]}} 
<small>(Daily)</small></span>
+                                               </div>
+                                       </div>
+                               </div>
+                       </div>
+               </div>
+
+               <div class="tab-pane" data-id="alerts">
+                       <div sorttable source="alertList" sort="-timestamp">
+                               <table class="table table-bordered" 
ng-non-bindable>
+                                       <thead>
+                                               <tr>
+                                                       <th width="170" 
sortpath="timestamp">Alert Time</th>
+                                                       <th width="170" 
sortpath="alertContext.properties.timestamp">Message Time</th>
+                                                       <th width="60" 
sortpath="alertContext.properties.user">User</th>
+                                                       <th width="150" 
sortpath="alertContext.properties.host">Host</th>
+                                                       <th 
sortpath="alertContext.properties.emailMessage">Description</th>
+                                                       <th width="50"> </th>
+                                               </tr>
+                                       </thead>
+                                       <tbody>
+                                               <tr ng-class="{info : 
item.__new}">
+                                                       
<td>{{common.format.date(item.timestamp)}}</td>
+                                                       
<td>{{common.format.date(item.alertContext.properties.timestamp)}}</td>
+                                                       
<td>{{item.alertContext.properties.user}}</td>
+                                                       
<td>{{item.alertContext.properties.host}}</td>
+                                                       
<td>{{item.alertContext.properties.alertMessage}}</td>
+                                                       <td><a 
href="#/common/alertDetail/{{item.encodedRowkey}}">Detail</a></td>
+                                               </tr>
+                                       </tbody>
+                               </table>
+                       </div>
+               </div>
+
+       </div>
+</div>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/d37643b0/eagle-webservice/src/main/webapp/app/public/feature/common/page/policyEdit.html
----------------------------------------------------------------------
diff --git 
a/eagle-webservice/src/main/webapp/app/public/feature/common/page/policyEdit.html
 
b/eagle-webservice/src/main/webapp/app/public/feature/common/page/policyEdit.html
new file mode 100644
index 0000000..e07cb0c
--- /dev/null
+++ 
b/eagle-webservice/src/main/webapp/app/public/feature/common/page/policyEdit.html
@@ -0,0 +1,359 @@
+<!--
+  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="progress active" ng-show="!streamReady">
+       <div class="progress-bar progress-bar-primary progress-bar-striped" 
style="width: 100%">
+       </div>
+</div>
+
+<!-- Step navigation -->
+<div ng-show="streamReady">
+       <div class="row step-cntr">
+               <div class="col-sm-4" ng-repeat="step in steps">
+                       <div class="step" ng-class="stepSelect($index + 1)">
+                               <h1>{{$index + 1}}</h1>
+                               <h2>Step {{$index + 1}}</h2>
+                               <p title="{{step.title}}">{{step.title}}</p>
+                       </div>
+               </div>
+       </div>
+
+       <!-- Step container -->
+       <div class="box box-info">
+               <div class="box-header with-border">
+                       <h3 class="box-title">Step {{step}} - {{steps[step - 
1].title}}</h3>
+               </div><!-- /.box-header -->
+
+               <div class="box-body">
+                       <!-- ---------------------- Step Body Start 
---------------------- -->
+
+                       <!-- Step 1: Data Source -->
+                       <!--div ng-show="step === 1">
+                               <div class="form-group">
+                                       <label>Select Data Source</label>
+                                       <select class="form-control" 
ng-model="policy.tags.dataSource">
+                                               <option ng-repeat="dataSource 
in Site.current().dataSrcList" ng-hide="dataSource.hide" 
value="{{dataSource.tags.dataSource}}">{{dataSource.desc || 
dataSource.tags.dataSource}}</option>
+                                       </select>
+                               </div>
+                       </div-->
+
+                       <!-- Step 1: Stream -->
+                       <div ng-show="step === 1">
+                               <div class="pull-right" 
ng-show="policy.__.advanced === undefined">
+                                       <span class="text-muted">or</span>
+                                       <a ng-click="policy.__.advanced = 
true;">Advanced</a>
+                               </div>
+
+                               <div class="form-group">
+                                       <label>Select Stream</label>
+                                       <select class="form-control" 
ng-model="policy.__.streamName" ng-show="!policy.__.advanced">
+                                               <option ng-repeat="stream in 
dataSources[policy.tags.dataSource]">{{stream.tags.streamName}}</option>
+                                       </select>
+                                       <select class="form-control" 
ng-show="policy.__.advanced" disabled="disabled">
+                                               <option>[Advanced Mode]</option>
+                                       </select>
+                               </div>
+
+                               <div class="checkbox" 
ng-show="policy.__.advanced !== undefined">
+                                       <label>
+                                               <input type="checkbox" 
ng-model="policy.__.advanced">
+                                               Advanced Mode
+                                       </label>
+                               </div>
+                       </div>
+
+                       <!-- Step 2: Define Alert Policy -->
+                       <div ng-show="step === 2 && !policy.__.advanced">
+                               <!-- Criteria -->
+                               <div>
+                                       <label>Match Criteria</label>
+                                       <a 
ng-click="collapse('.panel-group')">expand / collapse all</a>
+
+                                       <div class="panel-group panel-group-sm" 
role="tablist">
+                                               <div class="panel panel-default"
+                                                       ng-repeat="meta in 
_stream.metas"
+                                                       ng-init="op = '=='; val 
= null; type = (meta.attrType || '').toLowerCase();">
+                                                       <div 
class="panel-heading" role="tab">
+                                                               <h4 
class="panel-title">
+                                                                       <span 
class="bg-navy disabled color-palette pull-right">
+                                                                               
{{parseConditionDesc(meta.tags.attrName)}}
+                                                                       </span>
+
+                                                                       <a 
role="button" data-toggle="collapse" 
href="[data-name='{{meta.tags.attrName}}']" class="collapsed">
+                                                                               
<span class="fa fa-square" ng-class="hasCondition(meta.tags.attrName, type) ? 
'text-green' : 'text-muted'"> </span>
+                                                                               
{{meta.attrDisplayName || meta.tags.attrName}}
+                                                                               
<span class="fa fa-question-circle" ng-show="meta.attrDescription"
+                                                                               
uib-tooltip="{{meta.attrDescription}}" tooltip-placement="right" 
tooltip-animation="false"> </span>
+                                                                       </a>
+                                                               </h4>
+                                                       </div>
+                                                       <div 
data-name="{{meta.tags.attrName}}" data-type="{{meta.attrType}}" 
role="tabpanel" class="collapse">
+                                                               <div 
class="panel-body">
+                                                                       <ul 
ng-show="type !== 'bool'">
+                                                                               
<li ng-repeat="cond in policy.__.conditions[meta.tags.attrName]">
+                                                                               
        [<a ng-click="policy.__.conditions[meta.tags.attrName].splice($index, 
1)">X</a>]
+                                                                               
        {{cond.toString()}}
+                                                                               
</li>
+                                                                       </ul>
+
+                                                                       <!-- 
String -->
+                                                                       <div 
ng-if="type == 'string'">
+                                                                               
<div class="input-group" style="max-width: 450px;">
+                                                                               
        <div class="input-group-btn">
+                                                                               
                <select class="form-control" ng-model="op">
+                                                                               
                        <option ng-repeat="mark in 
['==','!=','contains','regex']">{{mark}}</option>
+                                                                               
                </select>
+                                                                               
        </div>
+
+                                                                               
        <!-- With resolver -->
+                                                                               
        <input type="text" class="form-control" autocomplete="off" 
ng-model="val" ng-show="meta.attrValueResolver"
+                                                                               
                ng-keypress="conditionPress($event, meta.tags.attrName, op, 
val, type)"
+                                                                               
                uib-typeahead="item for item in resolverTypeahead($viewValue, 
meta.attrValueResolver)">
+                                                                               
        <!-- Without resolver -->
+                                                                               
        <input type="text" class="form-control" autocomplete="off" 
ng-model="val" ng-show="!meta.attrValueResolver"
+                                                                               
                ng-keypress="conditionPress($event, meta.tags.attrName, op, 
val, type)">
+
+                                                                               
        <span class="input-group-btn">
+                                                                               
                <button class="btn btn-info btn-flat" type="button" 
ng-click="addCondition(meta.tags.attrName, op, val, 
type);val=null;">Add</button>
+                                                                               
        </span>
+                                                                               
</div>
+                                                                       </div>
+
+                                                                       <!-- 
Number -->
+                                                                       <div 
ng-if="type == 'long' || type == 'integer' || type == 'number' || type == 
'double' || type == 'float'">
+                                                                               
<div class="input-group" style="max-width: 450px;">
+                                                                               
        <div class="input-group-btn">
+                                                                               
                <select class="form-control" ng-model="op">
+                                                                               
                        <option ng-repeat="mark in 
['==','!=','>','>=','<','<=']">{{mark}}</option>
+                                                                               
                </select>
+                                                                               
        </div>
+
+                                                                               
        <input type="number" class="form-control" autocomplete="off" 
placeholder="Number Only..." ng-model="val" ng-keypress="conditionPress($event, 
meta.tags.attrName, op, val, type)">
+                                                                               
        <span class="input-group-btn">
+                                                                               
                <button class="btn btn-info btn-flat" type="button" 
ng-click="addCondition(meta.tags.attrName, op, val, type) ? val=null : 
void(0);">Add</button>
+                                                                               
        </span>
+                                                                               
</div>
+                                                                       </div>
+
+                                                                       <!-- 
Boolean -->
+                                                                       <div 
ng-if="type == 'bool'" ng-init="policy.__.conditions[meta.tags.attrName] = 
policy.__.conditions[meta.tags.attrName] || [_CondUnit(meta.tags.attrName, 
'==', 'none', 'bool')]">
+                                                                               
<select class="form-control" 
ng-model="policy.__.conditions[meta.tags.attrName][0].val" style="max-width: 
100px;">
+                                                                               
        <option ng-repeat="bool in ['none','true','false']">{{bool}}</option>
+                                                                               
</select>
+                                                                       </div>
+                                                               </div>
+                                                       </div>
+                                               </div>
+                                       </div>
+                               </div>
+
+                               <!-- Window -->
+                               <div class="checkbox">
+                                       <label>
+                                               <input type="checkbox" 
ng-checked="policy.__.windowConfig" ng-click="policy.__.windowConfig = 
!policy.__.windowConfig"> Slide Window
+                                       </label>
+                               </div>
+                               <div ng-show="policy.__.windowConfig">
+                                       <div class="row">
+                                               <div class="col-md-4">
+                                                       <div class="form-group">
+                                                               
<label>Window</label>
+                                                               <select 
class="form-control" ng-model="policy.__.window"
+                                                               
uib-tooltip="{{getWindow().description}}" tooltip-animation="false">
+                                                                       <option 
ng-repeat="item in config.window" value="{{item.type}}">{{item.title}}</option>
+                                                               </select>
+                                                       </div>
+                                               </div>
+
+                                               <!-- fields -->
+                                               <div class="col-md-4" 
ng-repeat="field in getWindow().fields" ng-init="field.val = field.val || 
(field.defaultValue || '');" ng-hide="field.hide">
+                                                       <div class="form-group" 
ng-class="{'has-warning' : !field.val || !field.val.match(field.regex)}">
+                                                               <label>Window - 
{{field.title}}</label>
+                                                               <input 
type="text" class="form-control" autocomplete="off" 
placeholder="{{field.description}}" ng-model="field.val" 
title="{{field.description}}">
+                                                       </div>
+                                               </div>
+                                       </div>
+
+                                       <!-- Aggregation -->
+                                       <div class="row">
+                                               <div class="col-md-4">
+                                                       <div class="form-group" 
ng-class="{'text-yellow' : (policy.__.groupAgg && !policy.__.groupAggPath)}">
+                                                               
<label>Aggregation</label>
+                                                               <div 
class="input-group">
+                                                                       <div 
class="input-group-btn">
+                                                                               
<select class="form-control" ng-model="policy.__.groupAgg" 
ng-change="updateGroupAgg()">
+                                                                               
        <option ng-repeat="op in ['max','min','avg','count', 
'sum']">{{op}}</option>
+                                                                               
</select>
+                                                                       </div>
+                                                                       <select 
class="form-control" ng-model="policy.__.groupAggPath" ng-class="{'has-warning' 
: !policy.__.groupAggPath}" id="groupAggPath"
+                                                                               
        ng-show="policy.__.groupAgg" ng-disabled="policy.__.groupAgg === 
'count'">
+                                                                               
<option ng-repeat="meta in groupAggPathList()">{{meta.tags.attrName}}</option>
+                                                                       
</select>
+                                                               </div>
+                                                       </div>
+                                               </div>
+
+                                               <div class="col-md-4">
+                                                       <div class="form-group" 
ng-class="{'text-yellow' : (!policy.__.groupCondOp || 
!policy.__.groupCondVal)}">
+                                                               
<label>Condition</label>
+                                                               <div 
class="input-group">
+                                                                       <div 
class="input-group-btn">
+                                                                               
<select class="form-control" ng-model="policy.__.groupCondOp" 
ng-class="{'has-warning' : !policy.__.groupCondOp}">
+                                                                               
        <option ng-repeat="op in ['>','<','>=','<=','==']">{{op}}</option>
+                                                                               
</select>
+                                                                       </div>
+                                                                       <input 
type="text" class="form-control" ng-model="policy.__.groupCondVal" 
ng-class="{'has-warning' : !policy.__.groupCondVal}" />
+                                                               </div>
+                                                       </div>
+                                               </div>
+
+                                               <div class="col-md-4">
+                                                       <div class="form-group">
+                                                               <label>Alias 
(Optional)</label>
+                                                               <input 
type="text" class="form-control" ng-model="policy.__.groupAggAlias" 
placeholder="Default: aggValue" />
+                                                       </div>
+                                               </div>
+                                       </div>
+
+                                       <!-- Group -->
+                                       <div class="row">
+                                               <div class="col-md-4">
+                                                       <div class="form-group">
+                                                               <label>Group 
By</label>
+                                                               <select 
class="form-control" ng-model="policy.__.group">
+                                                                       <option 
value="">None</option>
+                                                                       <option 
ng-repeat="meta in _stream.metas">{{meta.tags.attrName}}</option>
+                                                               </select>
+                                                       </div>
+                                               </div>
+                                       </div>
+                               </div>
+                       </div>
+
+                       <!-- Step 2: Define Alert Policy -->
+                       <div ng-show="step === 2 && policy.__.advanced">
+                               <div class="form-group">
+                                       <label>Query Expression</label>
+                                       <textarea class="form-control" 
ng-model="policy.__._expression"
+                                       placeholder="Query expression. e.g. 
from hdfsAuditLogEventStream[(cmd=='open') and (host=='localhost' or 
host=='127.0.0.1')]#window.time(2 sec) select * insert into outputStream;" 
rows="5"></textarea>
+                               </div>
+                       </div>
+
+                       <!-- Step 3: Email Notification -->
+                       <div ng-show="step === 3">
+                               <div class="row">
+                                       <div class="col-xs-4">
+                                               <div class="form-group" 
ng-class="{'has-warning' : !policy.tags.policyId}">
+                                                       <label>Policy 
Name</label>
+                                                       <input type="text" 
class="form-control" placeholder="" ng-model="policy.tags.policyId" 
ng-disabled="!create">
+                                               </div>
+                                       </div>
+                                       <div class="col-xs-3">
+                                               <div class="form-group">
+                                                       <label>
+                                                               Alert De-Dup 
Interval(min)
+                                                               <span class="fa 
fa-question-circle" uib-tooltip="Same type alert will be De-dup in configured 
interval"> </span>
+                                                       </label>
+                                                       <input type="number" 
class="form-control" ng-model="policy.__.dedupe.alertDedupIntervalMin" 
placeholder="[Minute] Number only. Suggestion: 720">
+                                               </div>
+                                       </div>
+                                       <div class="col-xs-3">
+                                               <div class="form-group">
+                                                       <label>
+                                                               Email De-Dup 
Interval(min)
+                                                               <span class="fa 
fa-question-circle" uib-tooltip="The minimun time interval of email"> </span>
+                                                       </label>
+                                                       <input type="number" 
class="form-control" ng-model="policy.__.dedupe.emailDedupIntervalMin" 
placeholder="[Minute] Number only. Suggestion: 1440">
+                                               </div>
+                                       </div>
+                                       <div class="col-xs-2">
+                                               <div class="form-group">
+                                                       <label>
+                                                               Enabled
+                                                       </label>
+                                                       <p>
+                                                               <input 
type="checkbox" checked="checked" ng-model="policy.enabled">
+                                                       </p>
+                                               </div>
+                                       </div>
+                               </div>
+
+                               <div>
+                                       <a data-toggle="collapse" 
href="[data-id='advancedDeDup']">Advanced De-Dup</a>
+                                       <div data-id='advancedDeDup' 
class="collapse">
+                                               <label>
+                                                       De-Dup Key
+                                                       <span class="fa 
fa-question-circle" uib-tooltip="Type of grouping alerts. If you don't know how 
to config, leave it default."> </span>
+                                               </label>
+                                               <div class="form-group">
+                                                       <div class="checkbox" 
ng-repeat="meta in _stream.metas" ng-init="create ? 
policy.__._dedupTags[meta.tags.attrName] = !!meta.usedAsTag : void(0);">
+                                                               <label>
+                                                                       <input 
type="checkbox" ng-model="policy.__._dedupTags[meta.tags.attrName]">
+                                                                       
{{meta.tags.attrName}}
+                                                               </label>
+                                                       </div>
+                                               </div>
+                                       </div>
+                               </div>
+
+                               <hr/>
+
+                               <div class="form-group">
+                                       <label>Sender</label>
+                                       <input type="text" class="form-control" 
value="noreply-hadoop-ea...@company1.com" placeholder="Enter Sender. e.g. 
sen...@eaxmple.com" ng-model="policy.__.notification[0].sender">
+                               </div>
+                               <div class="form-group">
+                                       <label>Recipients</label>
+                                       <input type="text" class="form-control" 
placeholder="Enter Recipients. Split with ','. e.g. us...@example.com, 
us...@example.com" ng-model="policy.__.notification[0].recipients">
+                               </div>
+                               <div class="form-group">
+                                       <label>Subject</label>
+                                       <input type="text" class="form-control" 
placeholder="Enter Subject" ng-model="policy.__.notification[0].subject">
+                               </div>
+                               <div class="form-group">
+                                       <label>Description</label>
+                                       <textarea class="form-control" 
placeholder="Policy description" ng-model="policy.desc"></textarea>
+                               </div>
+
+                               <a data-toggle="collapse" 
href="[data-id='policyQuery']">
+                                       View Query
+                               </a>
+                               <div class="collapse in" data-id="policyQuery">
+                                       <pre>{{toQuery()}}</pre>
+                               </div>
+                       </div>
+
+                       <!-- ----------------------- Step Body End 
----------------------- -->
+               </div><!-- /.box-body -->
+
+               <div class="overlay" ng-hide="stepReady(step)">
+                       <span class="fa fa-refresh fa-spin"> </span>
+               </div>
+
+               <div class="box-footer text-right">
+                       <button class="btn btn-info" ng-show="step > 1" 
ng-click="changeStep(step, step - 1, false)" ng-disabled="lock">
+                               Prev <span class="fa fa-arrow-circle-o-left"> 
</span>
+                       </button>
+                       <button class="btn btn-info" ng-show="step < 
steps.length" ng-click="changeStep(step, step + 1)" 
ng-disabled="checkNextable(step) || lock">
+                               Next <span class="fa fa-arrow-circle-o-right"> 
</span>
+                       </button>
+                       <button class="btn btn-info" ng-show="step === 
steps.length" ng-click="finishPolicy()" ng-disabled="checkNextable(step) || 
lock">
+                               Done <span class="fa fa-check-circle-o"> </span>
+                       </button>
+               </div>
+       </div>
+</div>

http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/d37643b0/eagle-webservice/src/main/webapp/app/public/feature/common/page/policyList.html
----------------------------------------------------------------------
diff --git 
a/eagle-webservice/src/main/webapp/app/public/feature/common/page/policyList.html
 
b/eagle-webservice/src/main/webapp/app/public/feature/common/page/policyList.html
new file mode 100644
index 0000000..d7141a6
--- /dev/null
+++ 
b/eagle-webservice/src/main/webapp/app/public/feature/common/page/policyList.html
@@ -0,0 +1,84 @@
+<!--
+  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-list-alt"> </i>
+               <h3 class="box-title">
+                       {{application.displayName}}
+               </h3>
+       </div>
+       <div class="box-body">
+               <div class="row">
+                       <div class="col-xs-3">
+                               <div class="search-box">
+                                       <input type="search" 
class="form-control input-sm" placeholder="Search" ng-model="search" />
+                                       <span class="fa fa-search"> </span>
+                               </div>
+                       </div>
+                       <div class="col-xs-6">
+                               <div class="inline-group form-inline 
text-muted">
+                                       <dl><dt><i class="fa fa-square 
text-green"> </i></dt><dd>Enabled</dd></dl>
+                                       <dl><dt><i class="fa fa-square 
text-muted"> </i></dt><dd>Disabled</dd></dl>
+                               </div>
+                       </div>
+                       <div class="col-xs-3 text-right">
+                               <a class="btn btn-primary" 
href="#/common/policyCreate/{{!dataSource ? '' : '?dataSrc=' + 
dataSource.tags.dataSource}}" ng-show="Auth.isRole('ROLE_ADMIN')">
+                                       New Policy
+                                       <i class="fa fa-plus-circle"> </i>
+                               </a>
+                       </div>
+               </div>
+
+               <p ng-show="policyList._promise.$$state.status !== 1">
+                       <span class="fa fa-refresh fa-spin"> </span>
+                       Loading...
+               </p>
+
+               <div sorttable source="policyList" 
ng-show="policyList._promise.$$state.status === 1" search="false" 
searchfunc="searchFunc">
+                       <table class="table table-bordered" ng-non-bindable>
+                               <thead>
+                                       <tr>
+                                               <th width="30" 
sortpath="enabled"> </th>
+                                               <th width="200" 
sortpath="tags.policyId">Policy Name</th>
+                                               <th 
sortpath="desc">Description</th>
+                                               <th width="150" 
sortpath="owner">Owner</th>
+                                               <th width="170" 
sortpath="lastModifiedDate">Last Modified</th>
+                                               <th width="95" 
ng-show="_parent.Auth.isRole('ROLE_ADMIN')">Action</th>
+                                       </tr>
+                               </thead>
+                               <tbody>
+                                       <tr>
+                                               <td><span class='fa fa-square' 
ng-class="item.enabled ? 'text-green' : 'text-muted'"> </span></td>
+                                               <td><a 
href="#/common/policyDetail/{{item.encodedRowkey}}">{{item.tags.policyId}}</a></td>
+                                               <td>{{item.desc}}</td>
+                                               <td>{{item.owner}}</td>
+                                               
<td>{{common.format.date(item.lastModifiedDate) || "-"}}</td>
+                                               <td 
ng-show="_parent.Auth.isRole('ROLE_ADMIN')">
+                                                       <a class="fa fa-pencil 
btn btn-default btn-xs" uib-tooltip="Edit" tooltip-animation="false" 
href="#/common/policyEdit/{{item.encodedRowkey}}"> </a>
+                                                       <button class="fa 
fa-play sm btn btn-default btn-xs" uib-tooltip="Enable" 
tooltip-animation="false" ng-show="!item.enabled" 
ng-click="_parent.updatePolicyStatus(item, true)"> </button>
+                                                       <button class="fa 
fa-pause sm btn btn-default btn-xs" uib-tooltip="Disable" 
tooltip-animation="false" ng-show="item.enabled" 
ng-click="_parent.updatePolicyStatus(item, false)"> </button>
+                                                       <button class="rm fa 
fa-trash-o btn btn-danger btn-xs" uib-tooltip="Delete" 
tooltip-animation="false" ng-click="_parent.deletePolicy(item)"> </button>
+                                               </td>
+                                       </tr>
+                               </tbody>
+                       </table>
+               </div>
+       </div>
+       <!--div class="box-footer clearfix">
+       </div-->
+</div>

http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/d37643b0/eagle-webservice/src/main/webapp/app/public/feature/metadata/controller.js
----------------------------------------------------------------------
diff --git 
a/eagle-webservice/src/main/webapp/app/public/feature/metadata/controller.js 
b/eagle-webservice/src/main/webapp/app/public/feature/metadata/controller.js
new file mode 100644
index 0000000..2797223
--- /dev/null
+++ b/eagle-webservice/src/main/webapp/app/public/feature/metadata/controller.js
@@ -0,0 +1,66 @@
+/*
+ * 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("metadata");
+
+       // ==============================================================
+       // =                          Function                          =
+       // ==============================================================
+
+       // ==============================================================
+       // =                          Metadata                          =
+       // ==============================================================
+
+       // ======================= Policy Summary =======================
+       feature.navItem("streamList", "Metadata", "bullseye");
+       feature.controller('streamList', function(PageConfig, Site, $scope, $q, 
Application, Entities) {
+               PageConfig.hideSite = true;
+
+               $scope.streams = {};
+               $scope._streamEntity = null;
+               $scope._streamEntityLock = false;
+
+               // =========================================== List 
===========================================
+               var _streamList = Entities.queryEntities("AlertStreamService", 
{dataSource: Application.current().tags.application});
+               var _streamSchemaList = 
Entities.queryEntities("AlertStreamSchemaService", {dataSource: 
Application.current().tags.application});
+               $scope.streamList = _streamList;
+               $scope.streamSchemaList = _streamSchemaList;
+
+               _streamList._promise.then(function() {
+                       $.each(_streamList, function(i, stream) {
+                               stream.metaList = [];
+                               $scope.streams[stream.tags.dataSource + "_" + 
stream.tags.streamName] = stream;
+                       });
+               });
+
+               $q.all([_streamList._promise, 
_streamSchemaList._promise]).then(function() {
+                       $.each(_streamSchemaList, function(i, meta) {
+                               var _stream = 
$scope.streams[meta.tags.dataSource + "_" + meta.tags.streamName];
+                               if(_stream) {
+                                       _stream.metaList.push(meta);
+                               } else {
+                                       console.warn("[Meta] Stream not 
match:", meta.tags.dataSource + "_" + meta.tags.streamName);
+                               }
+                       });
+               });
+       });
+})();
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-eagle/blob/d37643b0/eagle-webservice/src/main/webapp/app/public/feature/metadata/page/streamList.html
----------------------------------------------------------------------
diff --git 
a/eagle-webservice/src/main/webapp/app/public/feature/metadata/page/streamList.html
 
b/eagle-webservice/src/main/webapp/app/public/feature/metadata/page/streamList.html
new file mode 100644
index 0000000..9218d8d
--- /dev/null
+++ 
b/eagle-webservice/src/main/webapp/app/public/feature/metadata/page/streamList.html
@@ -0,0 +1,84 @@
+<!--
+  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 ng-show="streamList._promise.$$state.status !== 1">
+       <span class="fa fa-refresh fa-spin"> </span>
+       Loading...
+</p>
+
+<div class="box box-primary" ng-repeat="stream in streams">
+       <div class="box-header with-border">
+               <h3 class="box-title">{{stream.tags.streamName}}</h3>
+       </div>
+       <div class="box-body">
+               <div class="inline-group">
+                       <dl>
+                               <dt>
+                                       Data Source
+                               </dt>
+                               <dd>
+                                       {{stream.tags.dataSource}}
+                               </dd>
+                       </dl>
+                       <dl>
+                               <dt>
+                                       Stream Name
+                               </dt>
+                               <dd>
+                                       {{stream.tags.streamName}}
+                               </dd>
+                       </dl>
+               </div>
+               <div>
+                       <dl>
+                               <dt>
+                                       Description
+                               </dt>
+                               <dd>
+                                       {{stream.desc}}
+                               </dd>
+                       </dl>
+               </div>
+
+               <p ng-show="streamSchemaList._promise.$$state.status !== 1">
+                       <span class="fa fa-refresh fa-spin"> </span>
+                       Loading...
+               </p>
+
+               <div class="list" 
ng-show="streamSchemaList._promise.$$state.status === 1">
+                       <table class="table table-bordered">
+                               <thead>
+                                       <tr>
+                                               <th width="10%">Attribute 
Name</th>
+                                               <th width="12%">Display 
Name</th>
+                                               <th width="8%">Type</th>
+                                               <th>Description</th>
+                                       </tr>
+                               </thead>
+                               <tbody>
+                                       <tr ng-repeat="meta in stream.metaList">
+                                               <td>{{meta.tags.attrName}}</td>
+                                               
<td>{{meta.attrDisplayName}}</td>
+                                               <td><span class="label 
label-warning">{{meta.attrType}}</span></td>
+                                               
<td>{{meta.attrDescription}}</td>
+                                       </tr>
+                               </tbody>
+                       </table>
+               </div>
+       </div><!-- /.box-body -->
+       <!-- Loading (remove the following to stop the loading)-->
+</div>
\ No newline at end of file

Reply via email to