You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by at...@apache.org on 2015/03/31 20:19:58 UTC
ambari git commit: AMBARI-10293 Implement Actions Dropdown menu for
the widgets on service summary page. (atkach)
Repository: ambari
Updated Branches:
refs/heads/trunk 9301f9b16 -> 12598cff5
AMBARI-10293 Implement Actions Dropdown menu for the widgets on service summary page. (atkach)
Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/12598cff
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/12598cff
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/12598cff
Branch: refs/heads/trunk
Commit: 12598cff5876fe4a2323828722a5869ed5373f97
Parents: 9301f9b
Author: Andrii Tkach <at...@hortonworks.com>
Authored: Tue Mar 31 19:12:33 2015 +0300
Committer: Andrii Tkach <at...@hortonworks.com>
Committed: Tue Mar 31 21:19:00 2015 +0300
----------------------------------------------------------------------
.../data/widget_layouts/HBASE/layouts.json | 16 +++++
.../data/widget_layouts/HBASE/stack_layout.json | 3 +
.../controllers/main/service/info/summary.js | 76 +++++++++++++++++++-
ambari-web/app/mappers.js | 1 +
ambari-web/app/mappers/widget_layout_mapper.js | 29 ++++++++
ambari-web/app/mappers/widget_mapper.js | 3 +-
ambari-web/app/messages.js | 3 +
ambari-web/app/mixins/common/widget_mixin.js | 26 +++++--
ambari-web/app/models.js | 3 +-
ambari-web/app/models/widget.js | 2 +-
ambari-web/app/models/widget_layout.js | 6 +-
.../app/styles/enhanced_service_dashboard.less | 47 +++++++++++-
ambari-web/app/styles/widget_layout.less | 46 ------------
.../templates/common/widget/gauge_widget.hbs | 8 ++-
.../templates/common/widget/template_widget.hbs | 8 ++-
.../app/templates/main/service/info/summary.hbs | 42 ++++++++++-
ambari-web/app/utils/ajax/ajax.js | 5 ++
.../views/common/widget/gauge_widget_view.js | 4 +-
.../views/common/widget/graph_widget_view.js | 2 +-
.../views/common/widget/template_widget_view.js | 2 +-
.../app/views/main/service/info/summary.js | 53 ++++++++++++++
.../test/mixins/common/widget_mixin_test.js | 12 ++--
22 files changed, 323 insertions(+), 74 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/ambari/blob/12598cff/ambari-web/app/assets/data/widget_layouts/HBASE/layouts.json
----------------------------------------------------------------------
diff --git a/ambari-web/app/assets/data/widget_layouts/HBASE/layouts.json b/ambari-web/app/assets/data/widget_layouts/HBASE/layouts.json
new file mode 100644
index 0000000..ae33a84
--- /dev/null
+++ b/ambari-web/app/assets/data/widget_layouts/HBASE/layouts.json
@@ -0,0 +1,16 @@
+{
+ "href": "http://c6401.ambari.apache.org:8080/api/v1/users?widget_layouts/section_name=HBASE_SUMMARY&widget_layouts/scope=CLUSTER",
+ "items": [
+ {
+ "href": "http://c6401.ambari.apache.org:8080/api/v1/users/jaimin",
+ "Users": {
+ "user_name": "jaimin"
+ },
+ "widget_layouts": {
+ "layout_name": "default_hbase_layout",
+ "section_name": "HBASE_SUMMARY",
+ "scope": "CLUSTER"
+ }
+ }
+ ]
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/12598cff/ambari-web/app/assets/data/widget_layouts/HBASE/stack_layout.json
----------------------------------------------------------------------
diff --git a/ambari-web/app/assets/data/widget_layouts/HBASE/stack_layout.json b/ambari-web/app/assets/data/widget_layouts/HBASE/stack_layout.json
index 6285138..bfab170 100644
--- a/ambari-web/app/assets/data/widget_layouts/HBASE/stack_layout.json
+++ b/ambari-web/app/assets/data/widget_layouts/HBASE/stack_layout.json
@@ -17,6 +17,7 @@
"display_name": "RegionServer Reads and Writes",
"description": "This widget shows all the read requests and write requests on all regions for a RegionServer",
"widget_type": "GRAPH",
+ "is_visible": "true",
"metrics":[
{
"name": "regionserver.Server.Append_num_ops",
@@ -54,6 +55,7 @@
"display_name": "Files Local",
"description": "This widget shows percentage of files local.",
"widget_type": "NUMBER",
+ "is_visible": "true",
"metrics":[
{
"name": "regionserver.Server.percentFilesLocal",
@@ -77,6 +79,7 @@
"widget_name": "NAMENODE_HEAP",
"display_name": "NameNode Heap",
"widget_type": "GAUGE",
+ "is_visible": "true",
"description": "",
"metrics":[
{
http://git-wip-us.apache.org/repos/asf/ambari/blob/12598cff/ambari-web/app/controllers/main/service/info/summary.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/main/service/info/summary.js b/ambari-web/app/controllers/main/service/info/summary.js
index d1d1be8..1eec9d1 100644
--- a/ambari-web/app/controllers/main/service/info/summary.js
+++ b/ambari-web/app/controllers/main/service/info/summary.js
@@ -41,6 +41,13 @@ App.MainServiceInfoSummaryController = Em.Controller.extend({
isPreviousRangerConfigsCallFailed: false,
/**
+ * UI section name
+ */
+ sectionName: function() {
+ return this.get('content.serviceName') + "_SUMMARY";
+ }.property('content.serviceName'),
+
+ /**
* Ranger plugins data
* @type {array}
*/
@@ -298,13 +305,46 @@ App.MainServiceInfoSummaryController = Em.Controller.extend({
isWidgetsLoaded: false,
/**
- * @type Em.A
+ * @type {boolean}
+ */
+ isWidgetLayoutsLoaded: false,
+
+ /**
+ * @type {Em.A}
*/
widgets: function() {
return App.Widget.find().filterProperty('serviceName', this.get('content.serviceName'));
}.property('isWidgetsLoaded'),
/**
+ * @type {Em.A}
+ */
+ widgetLayouts: function() {
+ return App.WidgetLayout.find().filterProperty('serviceName', this.get('content.serviceName'));
+ }.property('isWidgetLayoutsLoaded'),
+
+ /**
+ * load widget layouts across all users in CLUSTER scope
+ * @returns {$.ajax}
+ */
+ loadWidgetLayouts: function() {
+ this.set('isWidgetLayoutsLoaded', false);
+ return App.ajax.send({
+ name: 'widgets.layouts.get',
+ sender: this,
+ data: {
+ sectionName: this.get('sectionName')
+ },
+ success: 'loadWidgetLayoutsSuccessCallback'
+ });
+ },
+
+ loadWidgetLayoutsSuccessCallback: function(data) {
+ App.widgetLayoutMapper.map(data);
+ this.set('isWidgetLayoutsLoaded', true);
+ },
+
+ /**
* load widgets defined by user
* @returns {$.ajax}
*/
@@ -315,7 +355,7 @@ App.MainServiceInfoSummaryController = Em.Controller.extend({
sender: this,
data: {
loginName: App.router.get('loginName'),
- sectionName: this.get('content.serviceName') + "_SUMMARY"
+ sectionName: this.get('sectionName')
},
success: 'loadWidgetsSuccessCallback'
});
@@ -355,8 +395,38 @@ App.MainServiceInfoSummaryController = Em.Controller.extend({
* @param {object|null} data
*/
loadStackWidgetsLayoutSuccessCallback: function (data) {
- App.widgetMapper.map(data.artifact_data.layouts.findProperty('section_name', (this.get('content.serviceName') + "_SUMMARY")), this.get('content.serviceName'));
+ App.widgetMapper.map(data.artifact_data.layouts.findProperty('section_name', this.get('sectionName')), this.get('content.serviceName'));
this.set('isWidgetsLoaded', true);
+ },
+
+ /**
+ * add widgets
+ */
+ addWidgets: function (event) {
+ var widgets = event.context.filterProperty('selected');
+
+ },
+
+ /**
+ * delete widgets
+ */
+ deleteWidgets: function (event) {
+ var widgets = event.context.filterProperty('selected');
+
+ },
+
+ /**
+ * save layout
+ */
+ saveLayout: function () {
+
+ },
+
+ /**
+ * create widget
+ */
+ createWidget: function () {
+
}
});
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/12598cff/ambari-web/app/mappers.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/mappers.js b/ambari-web/app/mappers.js
index 6939c0f..b96fc50 100644
--- a/ambari-web/app/mappers.js
+++ b/ambari-web/app/mappers.js
@@ -43,3 +43,4 @@ require('mappers/alert_groups_mapper');
require('mappers/alert_notification_mapper');
require('mappers/root_service_mapper');
require('mappers/widget_mapper');
+require('mappers/widget_layout_mapper');
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/12598cff/ambari-web/app/mappers/widget_layout_mapper.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/mappers/widget_layout_mapper.js b/ambari-web/app/mappers/widget_layout_mapper.js
new file mode 100644
index 0000000..d46099d
--- /dev/null
+++ b/ambari-web/app/mappers/widget_layout_mapper.js
@@ -0,0 +1,29 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+App.widgetLayoutMapper = App.QuickDataMapper.create({
+ model: App.WidgetLayout,
+ config: {
+ id: 'widget_layouts.layout_name',
+ layout_name: 'widget_layouts.layout_name',
+ section_name: 'widget_layouts.section_name',
+ scope: 'widget_layouts.scope',
+ user: 'Users.user_name'
+ }
+});
http://git-wip-us.apache.org/repos/asf/ambari/blob/12598cff/ambari-web/app/mappers/widget_mapper.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/mappers/widget_mapper.js b/ambari-web/app/mappers/widget_mapper.js
index 1df5003..c9b1aef 100644
--- a/ambari-web/app/mappers/widget_mapper.js
+++ b/ambari-web/app/mappers/widget_mapper.js
@@ -33,7 +33,8 @@ App.widgetMapper = App.QuickDataMapper.create({
properties: 'properties',
metrics: 'metrics',
values: 'values',
- description: 'description'
+ description: 'description',
+ is_visible: 'is_visible'
},
map: function (json, serviceName) {
//TODO add service name to user layout API response
http://git-wip-us.apache.org/repos/asf/ambari/blob/12598cff/ambari-web/app/messages.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/messages.js b/ambari-web/app/messages.js
index 92c0a74..86093fc 100644
--- a/ambari-web/app/messages.js
+++ b/ambari-web/app/messages.js
@@ -2236,6 +2236,9 @@ Em.I18n.translations = {
'dashboard.button.switchShort': 'Switch',
'dashboard.button.reset': 'Reset all widgets to default ',
'dashboard.button.gangliaLink': 'View metrics in Ganglia',
+ 'dashboard.widgets.create': 'Create New Widget',
+ 'dashboard.widgets.layout.import': 'Import a layout',
+ 'dashboard.widgets.layout.save': 'Save a layout',
'dashboard.widgets.NameNodeHeap': 'NameNode Heap',
'dashboard.widgets.NameNodeCpu': 'NameNode CPU WIO',
http://git-wip-us.apache.org/repos/asf/ambari/blob/12598cff/ambari-web/app/mixins/common/widget_mixin.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/mixins/common/widget_mixin.js b/ambari-web/app/mixins/common/widget_mixin.js
index 7d1abfe..5ed262c 100644
--- a/ambari-web/app/mixins/common/widget_mixin.js
+++ b/ambari-web/app/mixins/common/widget_mixin.js
@@ -55,10 +55,26 @@ App.WidgetMixin = Ember.Mixin.create({
*/
content: null,
+ beforeRender: function () {
+ this.loadMetrics();
+ },
+
+ /**
+ * callback on metrics loaded
+ */
+ onMetricsLoaded: function () {
+ var self = this;
+ this.set('isLoaded', true);
+ this.drawWidget();
+ setTimeout(function() {
+ self.loadMetrics();
+ }, App.contentUpdateInterval);
+ },
+
/**
* load metrics
*/
- beforeRender: function () {
+ loadMetrics: function () {
var requestData = this.getRequestData(this.get('content.metrics')),
request,
requestCounter = 0,
@@ -70,12 +86,12 @@ App.WidgetMixin = Ember.Mixin.create({
if (request.host_component_criteria) {
this.getHostComponentMetrics(request).complete(function () {
requestCounter--;
- if (requestCounter === 0) self.set('isLoaded', true);
+ if (requestCounter === 0) self.onMetricsLoaded();
});
} else {
this.getServiceComponentMetrics(request).complete(function () {
requestCounter--;
- if (requestCounter === 0) self.set('isLoaded', true);
+ if (requestCounter === 0) self.onMetricsLoaded();
});
}
}
@@ -111,7 +127,7 @@ App.WidgetMixin = Ember.Mixin.create({
this.get('content.values').forEach(function (value) {
var computeExpression = this.computeExpression(this.extractExpressions(value), metrics);
value.computedValue = value.value.replace(this.get('EXPRESSION_REGEX'), function (match) {
- return (computeExpression[match]) ? computeExpression[match] + (displayUnit || "") : Em.I18n.t('common.na');
+ return (!Em.isNone(computeExpression[match])) ? computeExpression[match] + (displayUnit || "") : Em.I18n.t('common.na');
});
}, this);
},
@@ -194,7 +210,7 @@ App.WidgetMixin = Ember.Mixin.create({
var metrics = [];
this.get('content.metrics').forEach(function (_metric) {
- if (Em.get(data, _metric.widget_id.replace(/\//g, '.'))) {
+ if (!Em.isNone(Em.get(data, _metric.widget_id.replace(/\//g, '.')))) {
_metric.data = Em.get(data, _metric.widget_id.replace(/\//g, '.'));
this.get('metrics').pushObject(_metric);
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/12598cff/ambari-web/app/models.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/models.js b/ambari-web/app/models.js
index 6f5da37..e9de30f 100644
--- a/ambari-web/app/models.js
+++ b/ambari-web/app/models.js
@@ -68,4 +68,5 @@ require('models/configs/config_property');
require('models/configs/tab');
require('models/configs/section');
require('models/configs/sub_section');
-require('models/widget');
\ No newline at end of file
+require('models/widget');
+require('models/widget_layout');
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/12598cff/ambari-web/app/models/widget.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/models/widget.js b/ambari-web/app/models/widget.js
index b093a53..e00cd05 100644
--- a/ambari-web/app/models/widget.js
+++ b/ambari-web/app/models/widget.js
@@ -27,7 +27,6 @@ App.Widget = DS.Model.extend({
* - HEATMAP
* - GRAPH (Line graph and stack graph)
* - NUMBER (e.g., “1 ms” for RPC latency)
- * - x / y (e.g., “2 / 3” DataNodes live)
* - LINKS
* - TEMPLATE
*/
@@ -42,6 +41,7 @@ App.Widget = DS.Model.extend({
expression: DS.attr('array'),
metrics: DS.attr('array'),
values: DS.attr('array'),
+ isVisible: DS.attr('boolean'),
/**
* @type {number}
http://git-wip-us.apache.org/repos/asf/ambari/blob/12598cff/ambari-web/app/models/widget_layout.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/models/widget_layout.js b/ambari-web/app/models/widget_layout.js
index b7cf9bb..2c29670 100644
--- a/ambari-web/app/models/widget_layout.js
+++ b/ambari-web/app/models/widget_layout.js
@@ -20,8 +20,10 @@ var App = require('app');
App.WidgetLayout = DS.Model.extend({
layoutName: DS.attr('string'),
- sectionName:DS.attr('string'),
- widgetLayoutInfo: DS.attr('string')
+ sectionName: DS.attr('string'),
+ widgetLayoutInfo: DS.attr('string'),
+ scope: DS.attr('string'),
+ user: DS.attr('string')
});
http://git-wip-us.apache.org/repos/asf/ambari/blob/12598cff/ambari-web/app/styles/enhanced_service_dashboard.less
----------------------------------------------------------------------
diff --git a/ambari-web/app/styles/enhanced_service_dashboard.less b/ambari-web/app/styles/enhanced_service_dashboard.less
index 8555f44..4d74488 100644
--- a/ambari-web/app/styles/enhanced_service_dashboard.less
+++ b/ambari-web/app/styles/enhanced_service_dashboard.less
@@ -25,8 +25,53 @@
.icon-plus {
font-size: 70px;
color: #ddd;
- position: center;
}
}
+ .actions .dropdown-menu {
+ min-width: 270px;
+ label.checkbox {
+ padding: 0 20px 0 38px;
+ margin-bottom: 0;
+ margin-top: 5px;
+ }
+ li.btn-row {
+ padding: 5px 20px 10px 20px;
+ }
+ a.action {
+ border-bottom: 1px solid #ddd
+ }
+ }
+}
+
+#widget_layout {
+ .widget {
+ .spinner {
+ margin: 55px auto;
+ }
+ .title {
+ padding: 5px 0 0 5px;
+ height: 25px;
+ font-weight: bold;
+ text-align: left;
+ }
+ .content {
+ text-align: center;
+ color: #5ab400;
+ padding-top: 35px;
+ font-weight: bold;
+ font-size: 35px;
+ }
+ .template-widget {
+ height: 150px;
+ width: 90%;
+ }
+ .gauge-widget {
+ height: 150px;
+ width: 90%;
+ .content {
+ padding-top: 5px;
+ }
+ }
+ }
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/12598cff/ambari-web/app/styles/widget_layout.less
----------------------------------------------------------------------
diff --git a/ambari-web/app/styles/widget_layout.less b/ambari-web/app/styles/widget_layout.less
deleted file mode 100644
index 8b4b94c..0000000
--- a/ambari-web/app/styles/widget_layout.less
+++ /dev/null
@@ -1,46 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#widget_layout {
- .widget {
- .title {
- padding: 5px 0 0 5px;
- height: 25px;
- font-weight: bold;
- text-align: left;
- }
- .content {
- text-align: center;
- color: #5ab400;
- padding-top: 35px;
- font-weight: bold;
- font-size: 35px;
- }
- .template-widget {
- height: 150px;
- width: 90%;
- }
- .gauge-widget {
- height: 150px;
- width: 90%;
- .content {
- padding-top: 5px;
- }
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/ambari/blob/12598cff/ambari-web/app/templates/common/widget/gauge_widget.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/common/widget/gauge_widget.hbs b/ambari-web/app/templates/common/widget/gauge_widget.hbs
index 03a975f..11e3565 100644
--- a/ambari-web/app/templates/common/widget/gauge_widget.hbs
+++ b/ambari-web/app/templates/common/widget/gauge_widget.hbs
@@ -17,6 +17,10 @@
}}
<div class="gauge-widget thumbnail">
- <div class="caption title">{{view.title}}</div>
- <div class="content"> {{view view.chartView}}</div>
+ {{#if view.isLoaded}}
+ <div class="caption title">{{view.title}}</div>
+ <div class="content"> {{view view.chartView}}</div>
+ {{else}}
+ <div class="spinner"></div>
+ {{/if}}
</div>
http://git-wip-us.apache.org/repos/asf/ambari/blob/12598cff/ambari-web/app/templates/common/widget/template_widget.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/common/widget/template_widget.hbs b/ambari-web/app/templates/common/widget/template_widget.hbs
index fda71ea..02f7425 100644
--- a/ambari-web/app/templates/common/widget/template_widget.hbs
+++ b/ambari-web/app/templates/common/widget/template_widget.hbs
@@ -17,6 +17,10 @@
}}
<div class="template-widget thumbnail">
- <div class="caption title">{{view.title}}</div>
- <div class="content"> {{view.value}}</div>
+ {{#if view.isLoaded}}
+ <div class="caption title">{{view.title}}</div>
+ <div class="content"> {{view.value}}</div>
+ {{else}}
+ <div class="spinner"></div>
+ {{/if}}
</div>
http://git-wip-us.apache.org/repos/asf/ambari/blob/12598cff/ambari-web/app/templates/main/service/info/summary.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/main/service/info/summary.hbs b/ambari-web/app/templates/main/service/info/summary.hbs
index e87ca6b..92f7cbd 100644
--- a/ambari-web/app/templates/main/service/info/summary.hbs
+++ b/ambari-web/app/templates/main/service/info/summary.hbs
@@ -89,10 +89,50 @@
</ul>
</div>
{{#if App.supports.customizedWidgets}}
- <div class="btn-group pull-right">
+ <div class="btn-group pull-right actions">
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">
{{t common.actions}} <span class="caret"></span>
</button>
+ <ul class="dropdown-menu">
+ {{#each option in view.widgetActions}}
+ <li {{bindAttr class="option.layouts:dropdown-submenu"}}>
+ {{#if option.isAction}}
+ <a href="javascript:void(0);" class="action" {{action doWidgetAction option.action target="view"}}>
+ <i {{bindAttr class="option.class"}}></i>
+ {{option.label}}
+ </a>
+ {{#if option.layouts}}
+ <ul class="dropdown-menu">
+ {{#each layout in option.layouts}}
+ <li>
+ <a href="javascript:void(0);">
+ {{layout.layoutName}}
+ </a>
+ </li>
+ {{/each}}
+ </ul>
+ {{/if}}
+ {{else}}
+ <label class="checkbox">
+ {{view Em.Checkbox checkedBinding="option.selected"}}
+ {{option.label}}
+ </label>
+ {{/if}}
+ </li>
+ {{/each}}
+ <li>
+ <a href="javascript:void(0);">
+ {{t hostPopup.serviceInfo.showMore}}
+ </a>
+ </li>
+ <li class="btn-row">
+ <div class="row-fluid">
+ <button class="btn span4">{{t common.cancel}}</button>
+ <button class="btn btn-danger span4" {{action deleteWidgets view.widgetActions target="controller"}}>{{t common.delete}}</button>
+ <button class="btn btn-primary span4" {{action addWidgets view.widgetActions target="controller"}}>{{t common.add}}</button>
+ </div>
+ </li>
+ </ul>
</div>
{{/if}}
</div>
http://git-wip-us.apache.org/repos/asf/ambari/blob/12598cff/ambari-web/app/utils/ajax/ajax.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/utils/ajax/ajax.js b/ambari-web/app/utils/ajax/ajax.js
index 1118d37..7b7c6fb 100644
--- a/ambari-web/app/utils/ajax/ajax.js
+++ b/ambari-web/app/utils/ajax/ajax.js
@@ -2413,6 +2413,11 @@ var urls = {
mock: '/data/widget_layouts/HBASE/empty_user_layout.json'
},
+ 'widgets.layouts.get': {
+ real: '/users?widget_layouts/section_name={sectionName}&widget_layouts/scope=CLUSTER',
+ mock: '/data/widget_layouts/HBASE/layouts.json'
+ },
+
'widgets.serviceComponent.metrics.get': {
real: '/clusters/{clusterName}/services/{serviceName}/components/{componentName}?fields={widgetIds}',
mock: '/data/metrics/{serviceName}/Append_num_ops_&_Delete_num_ops.json'
http://git-wip-us.apache.org/repos/asf/ambari/blob/12598cff/ambari-web/app/views/common/widget/gauge_widget_view.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/common/widget/gauge_widget_view.js b/ambari-web/app/views/common/widget/gauge_widget_view.js
index 0964e11..7a31d14 100644
--- a/ambari-web/app/views/common/widget/gauge_widget_view.js
+++ b/ambari-web/app/views/common/widget/gauge_widget_view.js
@@ -43,7 +43,7 @@ App.GaugeWidgetView = Em.View.extend(App.WidgetMixin, {
this.set('title', this.get('content.values')[0].name);
this.set('value', this.get('content.values')[0].computedValue);
}
- }.observes('isLoaded'),
+ },
chartView: App.ChartPieView.extend({
stroke: '#D6DDDF', //light grey
@@ -76,7 +76,7 @@ App.GaugeWidgetView = Em.View.extend(App.WidgetMixin, {
data: function () {
var data = parseFloat(this.get('parentView.value')) * this.get('FACTOR');
- if (isNaN(data)) return [this.get('MAX_VALUE'), 0];
+ if (isNaN(data)) return [0, this.get('MAX_VALUE')];
return [data, this.get('MAX_VALUE') - data];
}.property('parentView.value'),
http://git-wip-us.apache.org/repos/asf/ambari/blob/12598cff/ambari-web/app/views/common/widget/graph_widget_view.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/common/widget/graph_widget_view.js b/ambari-web/app/views/common/widget/graph_widget_view.js
index 2e4f0d9..9d2e4f5 100644
--- a/ambari-web/app/views/common/widget/graph_widget_view.js
+++ b/ambari-web/app/views/common/widget/graph_widget_view.js
@@ -69,7 +69,7 @@ App.GraphWidgetView = App.ChartLinearTimeView.extend(App.WidgetMixin, {
if (this.get('isLoaded')) {
this._refreshGraph(this.calculateValues())
}
- }.observes('isLoaded'),
+ },
/**
* calculate series datasets for graph widgets
http://git-wip-us.apache.org/repos/asf/ambari/blob/12598cff/ambari-web/app/views/common/widget/template_widget_view.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/common/widget/template_widget_view.js b/ambari-web/app/views/common/widget/template_widget_view.js
index 9b9d844..572e4fb 100644
--- a/ambari-web/app/views/common/widget/template_widget_view.js
+++ b/ambari-web/app/views/common/widget/template_widget_view.js
@@ -43,5 +43,5 @@ App.TemplateWidgetView = Em.View.extend(App.WidgetMixin, {
this.set('value', this.get('content.values')[0].computedValue);
this.set('title', this.get('content.values')[0].name);
}
- }.observes('isLoaded')
+ }
});
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/12598cff/ambari-web/app/views/main/service/info/summary.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/service/info/summary.js b/ambari-web/app/views/main/service/info/summary.js
index 9e108bf..f4e123d 100644
--- a/ambari-web/app/views/main/service/info/summary.js
+++ b/ambari-web/app/views/main/service/info/summary.js
@@ -365,6 +365,58 @@ App.MainServiceInfoSummaryView = Em.View.extend(App.UserPref, {
this.set('currentTimeRangeIndex', 0);
}
},
+ /**
+ * list of static actions of widget
+ * @type {Array}
+ */
+ staticWidgetActions: [
+ Em.Object.create({
+ label: Em.I18n.t('dashboard.widgets.layout.save'),
+ class: 'icon-download-alt',
+ action: 'saveLayout',
+ isAction: true
+ }),
+ Em.Object.create({
+ label: Em.I18n.t('dashboard.widgets.layout.import'),
+ class: 'icon-file',
+ isAction: true,
+ layouts: App.WidgetLayout.find()
+ }),
+ Em.Object.create({
+ label: Em.I18n.t('dashboard.widgets.create'),
+ class: 'icon-plus',
+ action: 'createWidget',
+ isAction: true
+ })
+ ],
+
+ /**
+ * @type {Array}
+ */
+ widgetActions: function() {
+ var options = [];
+
+ options.pushObjects(this.get('staticWidgetActions'));
+ this.get('controller.widgets').forEach(function (widget) {
+ options.push(Em.Object.create({
+ label: widget.get('displayName'),
+ isVisible: widget.get('isVisible'),
+ selected: true
+ }));
+ }, this);
+
+ return options;
+ }.property('controller.widgets.length'),
+
+ /**
+ * call action function defined in controller
+ * @param event
+ */
+ doWidgetAction: function(event) {
+ if($.isFunction(this.get('controller')[event.context])) {
+ this.get('controller')[event.context].apply(this.get('controller'));
+ }
+ },
/**
* time range options for service metrics, a dropdown will list all options
@@ -478,6 +530,7 @@ App.MainServiceInfoSummaryView = Em.View.extend(App.UserPref, {
var stackService = App.StackService.find().findProperty('serviceName', serviceName);
if (stackService.get('isServiceWithWidgets')) {
this.get('controller').loadWidgets();
+ this.get('controller').loadWidgetLayouts();
}
}
http://git-wip-us.apache.org/repos/asf/ambari/blob/12598cff/ambari-web/test/mixins/common/widget_mixin_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/mixins/common/widget_mixin_test.js b/ambari-web/test/mixins/common/widget_mixin_test.js
index 2ee9edd..edb5359 100644
--- a/ambari-web/test/mixins/common/widget_mixin_test.js
+++ b/ambari-web/test/mixins/common/widget_mixin_test.js
@@ -21,7 +21,7 @@ var App = require('app');
describe('App.WidgetMixin', function() {
var mixinClass = Em.Object.extend(App.WidgetMixin, {metrics: [], content: {}});
- describe('#beforeRender()', function () {
+ describe('#loadMetrics()', function () {
var mixinObject = mixinClass.create();
beforeEach(function () {
this.mock = sinon.stub(mixinObject, 'getRequestData');
@@ -31,27 +31,29 @@ describe('App.WidgetMixin', function() {
sinon.stub(mixinObject, 'getServiceComponentMetrics').returns({complete: function(callback){
callback();
}});
+ sinon.stub(mixinObject, 'onMetricsLoaded');
});
afterEach(function () {
this.mock.restore();
mixinObject.getHostComponentMetrics.restore();
mixinObject.getServiceComponentMetrics.restore();
+ mixinObject.onMetricsLoaded.restore();
});
it('has host_component_criteria', function () {
this.mock.returns({'key1': {host_component_criteria: 'criteria'}});
mixinObject.set('isLoaded', false);
- mixinObject.beforeRender();
+ mixinObject.loadMetrics();
expect(mixinObject.getHostComponentMetrics.calledWith({host_component_criteria: 'criteria'})).to.be.true;
- expect(mixinObject.get('isLoaded')).to.be.true;
+ expect(mixinObject.onMetricsLoaded.calledOnce).to.be.true;
});
it('host_component_criteria is absent', function () {
this.mock.returns({'key1': {}});
mixinObject.set('isLoaded', false);
- mixinObject.beforeRender();
+ mixinObject.loadMetrics();
expect(mixinObject.getServiceComponentMetrics.calledWith({})).to.be.true;
- expect(mixinObject.get('isLoaded')).to.be.true;
+ expect(mixinObject.onMetricsLoaded.calledOnce).to.be.true;
});
});