You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by ab...@apache.org on 2018/07/27 11:59:49 UTC
[ambari] branch trunk updated: AMBARI-24362 Fixes for modal with
config validations and dependent properties. (ababiichuk)
This is an automated email from the ASF dual-hosted git repository.
ababiichuk pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/ambari.git
The following commit(s) were added to refs/heads/trunk by this push:
new b7d81b2 AMBARI-24362 Fixes for modal with config validations and dependent properties. (ababiichuk)
b7d81b2 is described below
commit b7d81b2c523a052b776df28a8d387230c3371f68
Author: ababiichuk <ab...@hortonworks.com>
AuthorDate: Thu Jul 26 19:42:48 2018 +0300
AMBARI-24362 Fixes for modal with config validations and dependent properties. (ababiichuk)
---
ambari-web/app/controllers/main/host/details.js | 184 ++++++++++++---------
ambari-web/app/controllers/main/service/item.js | 4 +-
.../common/configs/config_recommendations.js | 12 +-
ambari-web/app/styles/application.less | 33 +++-
ambari-web/app/styles/bootstrap_overrides.less | 2 +-
ambari-web/app/styles/common.less | 13 ++
ambari-web/app/styles/modal_popups.less | 13 +-
ambari-web/app/styles/top-nav.less | 4 +-
.../templates/common/configs/services_config.hbs | 4 +-
.../modal_popups/config_recommendation_popup.hbs | 40 ++---
.../common/modal_popups/dependent_configs_list.hbs | 6 +-
.../modal_popups/dependent_configs_table.hbs | 24 +--
ambari-web/app/views/common/modal_popup.js | 13 +-
.../config_validation/config_validation_popup.js | 4 +-
.../modal_popups/dependent_configs_list_popup.js | 45 +++--
.../common/modal_popups/log_file_search_popup.js | 2 +-
.../common/configs/config_recommendations_test.js | 22 ++-
17 files changed, 269 insertions(+), 156 deletions(-)
diff --git a/ambari-web/app/controllers/main/host/details.js b/ambari-web/app/controllers/main/host/details.js
index d2c9aca..8a445dd 100644
--- a/ambari-web/app/controllers/main/host/details.js
+++ b/ambari-web/app/controllers/main/host/details.js
@@ -540,9 +540,9 @@ App.MainHostDetailsController = Em.Controller.extend(App.SupportClientConfigsDow
header: Em.I18n.t('popup.confirmation.commonHeader'),
controller: this,
hasPropertiesToChange: false,
- classNameBindings: ['controller.hasPropertiesToChange:common-modal-wrapper', 'controller.hasPropertiesToChange:modal-full-width'],
+ classNameBindings: ['controller.hasPropertiesToChange:common-modal-wrapper'],
modalDialogClasses: function () {
- return this.get('controller.hasPropertiesToChange') ? ['modal-lg'] : [];
+ return this.get('controller.hasPropertiesToChange') ? ['modal-xlg'] : [];
}.property('controller.hasPropertiesToChange'),
primary: Em.I18n.t('hosts.host.deleteComponent.popup.confirm'),
bodyClass: this.get('addDeleteComponentPopupBody').extend({
@@ -808,9 +808,9 @@ App.MainHostDetailsController = Em.Controller.extend(App.SupportClientConfigsDow
header: Em.I18n.t('popup.confirmation.commonHeader'),
controller: self,
hasPropertiesToChange: false,
- classNameBindings: ['hasPropertiesToChange:common-modal-wrapper', 'hasPropertiesToChange:modal-full-width'],
+ classNameBindings: ['hasPropertiesToChange:common-modal-wrapper'],
modalDialogClasses: function () {
- return this.get('controller.hasPropertiesToChange') ? ['modal-lg'] : [];
+ return this.get('controller.hasPropertiesToChange') ? ['modal-xlg'] : [];
}.property('controller.hasPropertiesToChange'),
primary: Em.I18n.t('hosts.host.addComponent.popup.confirm'),
bodyClass: self.get('addDeleteComponentPopupBody').extend({
@@ -990,19 +990,19 @@ App.MainHostDetailsController = Em.Controller.extend(App.SupportClientConfigsDow
* @method updateZkConfigs
*/
updateZkConfigs: function (configs) {
- var portValue = configs['zoo.cfg'] && Em.get(configs['zoo.cfg'], 'clientPort');
- var zkPort = typeof portValue === 'undefined' ? '2181' : portValue;
- var infraSolrZnode = configs['infra-solr-env'] ? Em.get(configs['infra-solr-env'], 'infra_solr_znode') : '/ambari-solr';
- var initializer = App.AddZooKeeperComponentsInitializer;
- var hostComponentsTopology = {
- masterComponentHosts: []
- };
- var propertiesToChange = this.get('allPropertiesToChange');
- var masterComponents = this.bootstrapHostsMapping('ZOOKEEPER_SERVER');
+ const portValue = configs['zoo.cfg'] && Em.get(configs['zoo.cfg'], 'clientPort'),
+ zkPort = typeof portValue === 'undefined' ? '2181' : portValue,
+ infraSolrZnode = configs['infra-solr-env'] ? Em.get(configs['infra-solr-env'], 'infra_solr_znode') : '/ambari-solr',
+ initializer = App.AddZooKeeperComponentsInitializer,
+ hostComponentsTopology = {
+ masterComponentHosts: []
+ },
+ propertiesToChange = this.get('allPropertiesToChange'),
+ masterComponents = this.bootstrapHostsMapping('ZOOKEEPER_SERVER');
if (this.get('fromDeleteHost') || this.get('fromDeleteZkServer')) {
this.set('fromDeleteHost', false);
this.set('fromDeleteZkServer', false);
- var removedHost = masterComponents.findProperty('hostName', this.get('content.hostName'));
+ let removedHost = masterComponents.findProperty('hostName', this.get('content.hostName'));
if (!Em.isNone(removedHost)) {
Em.set(removedHost, 'isInstalled', false);
}
@@ -1014,34 +1014,38 @@ App.MainHostDetailsController = Em.Controller.extend(App.SupportClientConfigsDow
isInstalled: true
});
}
- var dependencies = {
+ const dependencies = {
zkClientPort: zkPort,
- infraSolrZnode: infraSolrZnode
+ infraSolrZnode
};
hostComponentsTopology.masterComponentHosts = masterComponents;
- Em.keys(configs).forEach(function(fileName) {
- var properties = configs[fileName];
- Em.keys(properties).forEach(function(propertyName) {
- var currentValue = properties[propertyName],
+ Em.keys(configs).forEach(fileName => {
+ const properties = configs[fileName];
+ Em.keys(properties).forEach(propertyName => {
+ const currentValue = properties[propertyName],
propertyDef = {
- fileName: fileName,
+ fileName,
name: propertyName,
value: currentValue
},
configProperty = initializer.initialValue(propertyDef, hostComponentsTopology, dependencies);
initializer.updateSiteObj(configs[fileName], configProperty);
if (currentValue !== configs[fileName][propertyName]) {
- var service = App.config.get('serviceByConfigTypeMap')[fileName];
+ const service = App.config.get('serviceByConfigTypeMap')[fileName],
+ configObject = App.configsCollection.getConfigByName(propertyName, fileName),
+ displayName = configObject && configObject.displayName;
propertiesToChange.pushObject({
propertyFileName: fileName,
- propertyName: propertyName,
+ propertyName,
+ propertyTitle: configObject && Em.I18n.t('installer.controls.serviceConfigPopover.title').format(displayName, displayName === propertyName ? '' : propertyName),
+ propertyDescription: configObject && configObject.description,
serviceDisplayName: service && service.get('displayName'),
initialValue: currentValue,
recommendedValue: propertyDef.value
});
}
- }, this);
- }, this);
+ });
+ });
},
/**
@@ -1069,40 +1073,46 @@ App.MainHostDetailsController = Em.Controller.extend(App.SupportClientConfigsDow
* @method onLoadStormConfigs
*/
onLoadStormConfigs: function (data) {
- var nimbusHost = this.get('nimbusHost'),
+ const nimbusHost = this.get('nimbusHost'),
stormNimbusHosts = this.getStormNimbusHosts(),
configs = {},
attributes = {},
propertiesToChange = this.get('allPropertiesToChange');
this.saveLoadedConfigs(data);
- data.items.forEach(function (item) {
+ data.items.forEach(item => {
configs[item.type] = item.properties;
attributes[item.type] = item.properties_attributes || {};
- }, this);
+ });
this.updateZkConfigs(configs);
- var nimbusSeedsInit = configs['storm-site']['nimbus.seeds'],
+ const propertyName = 'nimbus.seeds',
+ propertyFileName = 'storm-site',
+ nimbusSeedsInit = configs[propertyFileName][propertyName],
nimbusSeedsRecommended = JSON.stringify(stormNimbusHosts).replace(/"/g, "'");
- configs['storm-site']['nimbus.seeds'] = nimbusSeedsRecommended;
+ configs[propertyFileName][propertyName] = nimbusSeedsRecommended;
if (this.get('isReconfigureRequired') && nimbusSeedsInit !== nimbusSeedsRecommended) {
- var service = App.config.get('serviceByConfigTypeMap')['storm-site'];
+ const service = App.config.get('serviceByConfigTypeMap')[propertyFileName],
+ configObject = App.configsCollection.getConfigByName(propertyName, propertyFileName),
+ displayName = configObject && configObject.displayName;
propertiesToChange.pushObject({
- propertyFileName: 'storm-site',
- propertyName: 'nimbus.seeds',
+ propertyFileName,
+ propertyName,
+ propertyTitle: configObject && Em.I18n.t('installer.controls.serviceConfigPopover.title').format(displayName, displayName === propertyName ? '' : propertyName),
+ propertyDescription: configObject && configObject.description,
serviceDisplayName: service && service.get('displayName'),
initialValue: nimbusSeedsInit,
recommendedValue: nimbusSeedsRecommended
});
}
- var groups = [
+ const groups = [
{
properties: {
- 'storm-site': configs['storm-site']
+ [propertyFileName]: configs[propertyFileName]
},
properties_attributes: {
- 'storm-site': attributes['storm-site']
+ [propertyFileName]: attributes[propertyFileName]
}
}
];
@@ -1110,41 +1120,45 @@ App.MainHostDetailsController = Em.Controller.extend(App.SupportClientConfigsDow
},
onLoadAtlasConfigs: function(data) {
- var atlasServer = this.get('atlasServer'),
+ const atlasServer = this.get('atlasServer'),
atlasServerHosts = this.getAtlasServerHosts(),
configs = {},
attributes = {},
propertiesToChange = this.get('allPropertiesToChange');
this.saveLoadedConfigs(data);
- data.items.forEach(function (item) {
+ data.items.forEach(item => {
configs[item.type] = item.properties;
attributes[item.type] = item.properties_attributes || {};
- }, this);
+ });
- var atlasAddresses = configs['application-properties']['atlas.rest.address'];
- var hostMask = atlasAddresses.split(',')[0].replace(/([https|http]*\:\/\/)(.*?)(:[0-9]+)/, '$1{hostname}$3');
- var atlasAddressesRecommended = atlasServerHosts.map(function(hostName) {
- return hostMask.replace('{hostname}', hostName);
- }).join(',');
- configs['application-properties']['atlas.rest.address'] = atlasAddressesRecommended;
+ const propertyFileName = 'application-properties',
+ propertyName = 'atlas.rest.address',
+ atlasAddresses = configs[propertyFileName][propertyName],
+ hostMask = atlasAddresses.split(',')[0].replace(/([https|http]*\:\/\/)(.*?)(:[0-9]+)/, '$1{hostname}$3'),
+ atlasAddressesRecommended = atlasServerHosts.map(hostName => hostMask.replace('{hostname}', hostName)).join(',');
+ configs[propertyFileName][propertyName] = atlasAddressesRecommended;
if (this.get('isReconfigureRequired') && atlasAddresses !== atlasAddressesRecommended) {
- var service = App.config.get('serviceByConfigTypeMap')['application-properties'];
+ var service = App.config.get('serviceByConfigTypeMap')[propertyFileName],
+ configObject = App.configsCollection.getConfigByName(propertyName, propertyFileName),
+ displayName = configObject && configObject.displayName;
propertiesToChange.pushObject({
- propertyFileName: 'application-properties',
- propertyName: 'atlas.rest.address',
+ propertyFileName,
+ propertyName,
+ propertyTitle: configObject && Em.I18n.t('installer.controls.serviceConfigPopover.title').format(displayName, displayName === propertyName ? '' : propertyName),
+ propertyDescription: configObject && configObject.description,
serviceDisplayName: service && service.get('displayName'),
initialValue: atlasAddresses,
recommendedValue: atlasAddressesRecommended
});
}
- var groups = [
+ const groups = [
{
properties: {
- 'application-properties': configs['application-properties']
+ [propertyFileName]: configs[propertyFileName]
},
properties_attributes: {
- 'application-properties': attributes['application-properties']
+ [propertyFileName]: attributes[propertyFileName]
}
}
];
@@ -1202,26 +1216,26 @@ App.MainHostDetailsController = Em.Controller.extend(App.SupportClientConfigsDow
* @method onLoadHiveConfigs
*/
onLoadHiveConfigs: function (data, opt, params) {
- var port = "";
- var configs = {};
- var attributes = {};
- var userSetup = {};
- var localDB = {
- masterComponentHosts: this.getHiveHosts()
- };
- var dependencies = {
- hiveMetastorePort: ""
- };
- var initializer = params.webHCat ? App.AddWebHCatComponentsInitializer : App.AddHiveComponentsInitializer;
+ let port = "";
+ const configs = {},
+ attributes = {},
+ userSetup = {},
+ localDB = {
+ masterComponentHosts: this.getHiveHosts()
+ },
+ dependencies = {
+ hiveMetastorePort: ""
+ },
+ initializer = params.webHCat ? App.AddWebHCatComponentsInitializer : App.AddHiveComponentsInitializer;
this.saveLoadedConfigs(data);
this.set('configs.params', {
webHCat: params.webHCat
});
- data.items.forEach(function (item) {
+ data.items.forEach(item => {
configs[item.type] = item.properties;
attributes[item.type] = item.properties_attributes || {};
- }, this);
- var propertiesToChange = this.get('allPropertiesToChange');
+ });
+ const propertiesToChange = this.get('allPropertiesToChange');
port = configs['hive-site']['hive.metastore.uris'].match(/:[0-9]{2,4}/);
port = port ? port[0].slice(1) : "9083";
@@ -1236,30 +1250,34 @@ App.MainHostDetailsController = Em.Controller.extend(App.SupportClientConfigsDow
initializer.setup(userSetup);
- ['hive-site', 'webhcat-site', 'hive-env', 'core-site'].forEach(function(fileName) {
+ ['hive-site', 'webhcat-site', 'hive-env', 'core-site'].forEach(fileName => {
if (configs[fileName]) {
- Em.keys(configs[fileName]).forEach(function(propertyName) {
- var currentValue = configs[fileName][propertyName],
+ Em.keys(configs[fileName]).forEach(propertyName => {
+ const currentValue = configs[fileName][propertyName],
propertyDef = {
- fileName: fileName,
+ fileName,
name: propertyName,
value: currentValue
},
configProperty = initializer.initialValue(propertyDef, localDB, dependencies);
initializer.updateSiteObj(configs[fileName], configProperty);
if (this.get('isReconfigureRequired') && currentValue !== configs[fileName][propertyName]) {
- var service = App.config.get('serviceByConfigTypeMap')[fileName];
+ const service = App.config.get('serviceByConfigTypeMap')[fileName],
+ configObject = App.configsCollection.getConfigByName(propertyName, fileName),
+ displayName = configObject && configObject.displayName;
propertiesToChange.pushObject({
propertyFileName: fileName,
- propertyName: propertyName,
+ propertyName,
+ propertyTitle: configObject && Em.I18n.t('installer.controls.serviceConfigPopover.title').format(displayName, displayName === propertyName ? '' : propertyName),
+ propertyDescription: configObject && configObject.description,
serviceDisplayName: service && service.get('displayName'),
initialValue: currentValue,
recommendedValue: propertyDef.value
});
}
- }, this);
+ });
}
- }, this);
+ });
initializer.cleanup();
@@ -1416,7 +1434,7 @@ App.MainHostDetailsController = Em.Controller.extend(App.SupportClientConfigsDow
* @method onLoadRangerConfigs
*/
onLoadRangerConfigs: function (data) {
- var hdfsProperties = [
+ const hdfsProperties = [
{
type: 'core-site',
name: 'hadoop.security.key.provider.path'
@@ -1490,8 +1508,8 @@ App.MainHostDetailsController = Em.Controller.extend(App.SupportClientConfigsDow
this.saveLoadedConfigs(data);
- hdfsProperties.forEach(function (property) {
- var typeConfigs = data.items.findProperty('type', property.type).properties,
+ hdfsProperties.forEach(property => {
+ const typeConfigs = data.items.findProperty('type', property.type).properties,
currentValue = typeConfigs[property.name],
pattern = new RegExp('^kms:\\/\\/http@(.+):' + rkmsPort + '\\/kms$'),
patternMatch = currentValue && currentValue.match(pattern),
@@ -1499,10 +1517,16 @@ App.MainHostDetailsController = Em.Controller.extend(App.SupportClientConfigsDow
if (currentHostsList !== rkmsHostsStr) {
typeConfigs[property.name] = newValue;
if (this.get('isReconfigureRequired')) {
- var service = App.config.get('serviceByConfigTypeMap')[property.type];
+ const propertyFileName = property.type,
+ propertyName = property.name,
+ service = App.config.get('serviceByConfigTypeMap')[propertyFileName],
+ configObject = App.configsCollection.getConfigByName(propertyName, propertyFileName),
+ displayName = configObject && configObject.displayName;
propertiesToChange.pushObject({
- propertyFileName: property.type,
- propertyName: property.name,
+ propertyFileName,
+ propertyName,
+ propertyTitle: configObject && Em.I18n.t('installer.controls.serviceConfigPopover.title').format(displayName, displayName === propertyName ? '' : propertyName),
+ propertyDescription: configObject && configObject.description,
serviceDisplayName: service && service.get('displayName'),
initialValue: currentValue,
recommendedValue: newValue,
@@ -1510,7 +1534,7 @@ App.MainHostDetailsController = Em.Controller.extend(App.SupportClientConfigsDow
});
}
}
- }, this);
+ });
kmsSiteProperties.forEach(function (property) {
var currentValue = kmsSiteConfigs.properties[property.name];
diff --git a/ambari-web/app/controllers/main/service/item.js b/ambari-web/app/controllers/main/service/item.js
index 1bc8410..6560666 100644
--- a/ambari-web/app/controllers/main/service/item.js
+++ b/ambari-web/app/controllers/main/service/item.js
@@ -1521,8 +1521,8 @@ App.MainServiceItemController = Em.Controller.extend(App.SupportClientConfigsDow
primary: popupPrimary,
primaryClass: 'btn-danger',
disablePrimary: Em.computed.alias('controller.isRecommendationInProgress'),
- classNameBindings: ['controller.changedProperties.length:common-modal-wrapper', 'controller.changedProperties.length:modal-full-width'],
- modalDialogClasses: Em.computed.ifThenElse('controller.changedProperties.length', ['modal-lg'], []),
+ classNameBindings: ['controller.changedProperties.length:common-modal-wrapper'],
+ modalDialogClasses: Em.computed.ifThenElse('controller.changedProperties.length', ['modal-xlg'], []),
bodyClass: Em.View.extend({
templateName: require('templates/main/service/info/delete_service_warning_popup'),
warningMessage: new Em.Handlebars.SafeString(warningMessage)
diff --git a/ambari-web/app/mixins/common/configs/config_recommendations.js b/ambari-web/app/mixins/common/configs/config_recommendations.js
index 4842692..839b7e8 100644
--- a/ambari-web/app/mixins/common/configs/config_recommendations.js
+++ b/ambari-web/app/mixins/common/configs/config_recommendations.js
@@ -96,20 +96,24 @@ App.ConfigRecommendations = Em.Mixin.create({
*/
addRecommendation: function (name, fileName, configGroupName, recommendedValue, initialValue, parentPropertyIds, isEditable) {
Em.assert('name and fileName should be defined', name && fileName);
- var site = App.config.getConfigTagFromFileName(fileName);
- var service = App.config.get('serviceByConfigTypeMap')[site];
+ const site = App.config.getConfigTagFromFileName(fileName);
+ const service = App.config.get('serviceByConfigTypeMap')[site];
+ const configObject = App.configsCollection.getConfigByName(name, fileName);
+ const displayName = configObject && configObject.displayName;
- var recommendation = {
+ const recommendation = {
saveRecommended: true,
saveRecommendedDefault: true,
propertyFileName: site,
propertyName: name,
+ propertyTitle: configObject && Em.I18n.t('installer.controls.serviceConfigPopover.title').format(displayName, displayName === name ? '' : name),
+ propertyDescription: configObject && configObject.description,
isDeleted: Em.isNone(recommendedValue),
notDefined: Em.isNone(initialValue),
configGroup: configGroupName || "Default",
- initialValue: initialValue,
+ initialValue,
parentConfigs: parentPropertyIds || [],
serviceName: service.get('serviceName'),
allowChangeGroup: false,//TODO groupName!= "Default" && (service.get('serviceName') != this.get('selectedService.serviceName'))
diff --git a/ambari-web/app/styles/application.less b/ambari-web/app/styles/application.less
index d72bd9a..3f8bd9a 100644
--- a/ambari-web/app/styles/application.less
+++ b/ambari-web/app/styles/application.less
@@ -20,7 +20,7 @@
body {
overflow-y: scroll;
- line-height: 1.3em;
+ line-height: @default-line-height;
}
html, body {
@@ -2177,6 +2177,26 @@ input[type="radio"].align-checkbox, input[type="checkbox"].align-checkbox {
.config-validation-warnings {
table {
+ table-layout: fixed;
+ td, th {
+ &.issue-type-cell {
+ width: 5%;
+ }
+ &.service-name-cell {
+ width: 15%;
+ }
+ &.property-name-cell {
+ width: 15%;
+ }
+ &.property-value-cell {
+ width: 25%;
+ overflow: hidden;
+ overflow-wrap: break-word;
+ }
+ &.property-description-cell {
+ width: 40%;
+ }
+ }
tbody{
tr {
&.warning {
@@ -2227,13 +2247,18 @@ input[type="radio"].align-checkbox, input[type="checkbox"].align-checkbox {
#config-dependencies {
max-height: 500px;
overflow-y: visible;
- td {
- min-width: 120px;
- word-break: break-all;
+ td, th {
&.check-box-col {
min-width: 5px;
width: 5px;
+ .checkbox {
+ display: inline-block;
+ }
}
+ }
+ td {
+ min-width: 120px;
+ word-break: break-all;
&.config-dependency-name {
min-width: @config-dependency-t-name-width;
}
diff --git a/ambari-web/app/styles/bootstrap_overrides.less b/ambari-web/app/styles/bootstrap_overrides.less
index 9243ef6..70d9945 100644
--- a/ambari-web/app/styles/bootstrap_overrides.less
+++ b/ambari-web/app/styles/bootstrap_overrides.less
@@ -432,7 +432,7 @@ select.form-control {
.btn-group.open .dropdown-menu input[type="checkbox"] + label,
.dropdown.open .dropdown-menu input[type="checkbox"] + label {
- line-height: 1.3em;
+ line-height: @default-line-height;
}
.navigation-bar-container.collapsed ul.nav.side-nav-menu li ul.sub-menu,
diff --git a/ambari-web/app/styles/common.less b/ambari-web/app/styles/common.less
index 15babfa..22ef496 100644
--- a/ambari-web/app/styles/common.less
+++ b/ambari-web/app/styles/common.less
@@ -189,6 +189,7 @@
@default-font-size: 14px;
@smaller-font-size: 12px;
@default-button-height: 34px;
+@default-line-height: 1.3em;
/************************************************************************
* Modal popup properties
@@ -199,6 +200,10 @@
@modal-header-height: 57px;
// modal footer height
@modal-footer-height: 75px;
+// default modal top margin
+@modal-dialog-margin-top-default: 10px;
+// modal top margin for wide screen
+@modal-dialog-margin-top-wide-screen: 30px;
.ellipsis-overflow {
overflow: hidden;
@@ -211,3 +216,11 @@
}
@dashboard-widget-height: 157px;
+
+/************************************************************************
+* Top navbar styles
+***********************************************************************/
+@navbar-header-vertical-padding: 19px;
+@navbar-header-padding-right: 15px;
+@navbar-header-padding-left: 0;
+@navbar-header-font-size: 20px;
diff --git a/ambari-web/app/styles/modal_popups.less b/ambari-web/app/styles/modal_popups.less
index 0674541..52c036c 100644
--- a/ambari-web/app/styles/modal_popups.less
+++ b/ambari-web/app/styles/modal_popups.less
@@ -17,6 +17,8 @@
*/
@import 'common.less';
+@modal-top-calculated-without-margin: @navbar-header-vertical-padding + (@navbar-header-font-size * unit(@default-line-height));
+
#modal {
outline: none;
}
@@ -25,7 +27,7 @@
margin-left: 20px;
margin-right: 20px;
text-indent: -20px;
- line-height: 1.3em;
+ line-height: @default-line-height;
a:not(.disabled) {
cursor: pointer;
@@ -97,7 +99,7 @@
}
.modal {
- top: 5%;
+ top: @modal-top-calculated-without-margin - @modal-dialog-margin-top-default;
.modal-body {
.top-wrap {
&.top-wrap-header {
@@ -107,6 +109,13 @@
}
}
}
+
+@media (min-width: 768px) {
+ .modal {
+ top: @modal-top-calculated-without-margin - @modal-dialog-margin-top-wide-screen;
+ }
+}
+
.modal-body {
max-height: 600px;
overflow-y: auto;
diff --git a/ambari-web/app/styles/top-nav.less b/ambari-web/app/styles/top-nav.less
index 9fcc5f3..f14a014 100644
--- a/ambari-web/app/styles/top-nav.less
+++ b/ambari-web/app/styles/top-nav.less
@@ -26,9 +26,9 @@
margin-bottom: 10px;
.navbar-header {
- padding: 19px 15px 19px 0;
+ padding: @navbar-header-vertical-padding @navbar-header-padding-right @navbar-header-vertical-padding @navbar-header-padding-left;
margin-top: -5px;
- font-size: 20px;
+ font-size: @navbar-header-font-size;
width: ~"calc(100% - 400px)";
a {
color: #313D54;
diff --git a/ambari-web/app/templates/common/configs/services_config.hbs b/ambari-web/app/templates/common/configs/services_config.hbs
index adea9df..044f32e7 100644
--- a/ambari-web/app/templates/common/configs/services_config.hbs
+++ b/ambari-web/app/templates/common/configs/services_config.hbs
@@ -72,7 +72,9 @@
<div id="notifications-dropdown" class="dropdown-menu row">
<div class="popover-content">
<div class="popup-arrow-up"></div>
- {{view App.DependentConfigsListView recommendationsBinding="controller.recommendations" requiredChangesBinding="controller.requiredChanges"}}
+ {{view App.DependentConfigsListView recommendationsBinding="controller.recommendations"
+ requiredChangesBinding="controller.requiredChanges" isRecommendationsClickable=true
+ showRecommendationsPopovers=false}}
</div>
</div>
</div>
diff --git a/ambari-web/app/templates/common/modal_popups/config_recommendation_popup.hbs b/ambari-web/app/templates/common/modal_popups/config_recommendation_popup.hbs
index 9dbd401..0f51473 100644
--- a/ambari-web/app/templates/common/modal_popups/config_recommendation_popup.hbs
+++ b/ambari-web/app/templates/common/modal_popups/config_recommendation_popup.hbs
@@ -25,29 +25,29 @@
<table class="table table-hover">
<thead>
<tr>
- <th>{{t common.type}}</th>
- <th>{{t common.service}}</th>
- <th>{{t common.property}}</th>
- <th>{{t common.value}}</th>
- <th>{{t common.description}}</th>
+ <th class="issue-type-cell">{{t common.type}}</th>
+ <th class="service-name-cell">{{t common.service}}</th>
+ <th class="property-name-cell">{{t common.property}}</th>
+ <th class="property-value-cell">{{t common.value}}</th>
+ <th class="property-description-cell">{{t common.description}}</th>
</tr>
</thead>
<tbody>
{{#each error in view.configErrors.criticalIssues}}
<tr {{bindAttr class="error.cssClass"}}>
- <td>
+ <td class="issue-type-cell">
{{t common.critical}}
</td>
- <td>{{error.serviceName}}</td>
- <td>
+ <td class="service-name-cell">{{error.serviceName}}</td>
+ <td class="property-name-cell">
{{#if controller.isInstallWizard}}
<a href="#" {{action "showConfigProperty" error target="controller"}}>{{error.propertyName}}</a>
{{else}}
{{error.propertyName}}
{{/if}}
</td>
- <td>{{error.value}}</td>
- <td>
+ <td class="property-value-cell">{{error.value}}</td>
+ <td class="property-description-cell">
{{#each message in error.messages}}
<div class="property-message">{{message}}</div>
{{/each}}
@@ -71,17 +71,17 @@
<table class="table table-hover">
<thead>
<tr>
- <th>{{t common.type}}</th>
- <th>{{t common.service}}</th>
- <th>{{t common.property}}</th>
- <th>{{t installer.step7.popup.currentValue}}</th>
- <th>{{t common.description}}</th>
+ <th class="issue-type-cell">{{t common.type}}</th>
+ <th class="service-name-cell">{{t common.service}}</th>
+ <th class="property-name-cell">{{t common.property}}</th>
+ <th class="property-value-cell">{{t installer.step7.popup.currentValue}}</th>
+ <th class="property-description-cell">{{t common.description}}</th>
</tr>
</thead>
<tbody>
{{#each error in view.configErrors.issues}}
<tr {{bindAttr class="error.cssClass"}}>
- <td>
+ <td class="issue-type-cell">
{{#if error.isError}}
{{t common.error}}
{{else}}
@@ -94,16 +94,16 @@
<td colspan="4">{{error.message}}</td>
{{/each}}
{{else}}
- <td>{{error.serviceName}}</td>
- <td>
+ <td class="service-name-cell">{{error.serviceName}}</td>
+ <td class="property-name-cell">
{{#if controller.isInstallWizard}}
<a href="#" {{action "showConfigProperty" error target="controller"}}>{{error.propertyName}}</a>
{{else}}
{{error.propertyName}}
{{/if}}
</td>
- <td>{{error.value}}</td>
- <td>
+ <td class="property-value-cell">{{error.value}}</td>
+ <td class="property-description-cell">
{{#each message in error.messages}}
<div class="property-message">{{message}}</div>
{{/each}}
diff --git a/ambari-web/app/templates/common/modal_popups/dependent_configs_list.hbs b/ambari-web/app/templates/common/modal_popups/dependent_configs_list.hbs
index d5d54ff..d12af61 100644
--- a/ambari-web/app/templates/common/modal_popups/dependent_configs_list.hbs
+++ b/ambari-web/app/templates/common/modal_popups/dependent_configs_list.hbs
@@ -63,9 +63,11 @@
{{/if}}
<span id="config-dependencies" class="limited-height-2">
{{#if view.recommendations.length}}
- {{view App.DependentConfigsTableView recommendationsBinding="view.recommendations" checkboxesFirstBinding="controller.isInstallWizard" isClickable=true}}
+ {{view App.DependentConfigsTableView recommendationsBinding="view.recommendations"
+ isClickableBinding="view.isRecommendationsClickable" showPopoversBinding="view.showRecommendationsPopovers"}}
{{/if}}
{{#if view.requiredChanges.length}}
- {{view App.DependentConfigsTableView recommendationsBinding="view.requiredChanges" isEditable=false checkboxesFirstBinding="controller.isInstallWizard" isClickable=true}}
+ {{view App.DependentConfigsTableView recommendationsBinding="view.requiredChanges" isEditable=false
+ isClickableBinding="view.isRecommendationsClickable" showPopoversBinding="view.showRecommendationsPopovers"}}
{{/if}}
</span>
diff --git a/ambari-web/app/templates/common/modal_popups/dependent_configs_table.hbs b/ambari-web/app/templates/common/modal_popups/dependent_configs_table.hbs
index 637ec26..1f506d0 100644
--- a/ambari-web/app/templates/common/modal_popups/dependent_configs_table.hbs
+++ b/ambari-web/app/templates/common/modal_popups/dependent_configs_table.hbs
@@ -22,10 +22,8 @@
<table class="table table-hover">
<thead>
<tr>
- {{#if view.checkboxesFirst}}
- {{#if view.isEditable}}
- <th class="check-box-col">{{view view.parentView.toggleAll}}<label {{bindAttr for="view.parentView.toggleAllId"}}></label></th>
- {{/if}}
+ {{#if view.isEditable}}
+ <th class="check-box-col">{{view view.parentView.toggleAll}}<label {{bindAttr for="view.parentView.toggleAllId"}}></label></th>
{{/if}}
<th>{{t common.property}}</th>
<th>{{t common.service}}</th>
@@ -45,22 +43,15 @@
</div>
</div>
</th>
- {{#unless view.checkboxesFirst}}
- {{#if view.isEditable}}
- <th class="check-box-col">{{view view.parentView.toggleAll}}<label {{bindAttr for="view.parentView.toggleAllId"}}></label></th>
- {{/if}}
- {{/unless}}
</tr>
</thead>
<tbody>
{{#each recommendation in view.recommendations}}
<tr {{bindAttr class="recommendation.saveRecommended:active"}}>
- {{#if view.checkboxesFirst}}
- {{#if view.isEditable}}
- <td class="check-box-col">{{view App.CheckboxView checkedBinding="recommendation.saveRecommended"}}</td>
- {{/if}}
+ {{#if view.isEditable}}
+ <td class="check-box-col">{{view App.CheckboxView checkedBinding="recommendation.saveRecommended"}}</td>
{{/if}}
- <td class="config-dependency-name">
+ <td class="config-dependency-name" {{bindAttr data-original-title="recommendation.propertyTitle" data-content="recommendation.propertyDescription"}}>
{{#if view.isClickable}}
<a href="#" {{action "showConfigProperty" recommendation target="controller"}}>{{recommendation.propertyName}}</a>
{{else}}
@@ -82,11 +73,6 @@
{{view App.ConfigDiffView configBinding="recommendation"}}
</div>
</td>
- {{#unless view.checkboxesFirst}}
- {{#if view.isEditable}}
- <td class="check-box-col">{{view App.CheckboxView checkedBinding="recommendation.saveRecommended"}}</td>
- {{/if}}
- {{/unless}}
</tr>
{{/each}}
</tbody>
diff --git a/ambari-web/app/views/common/modal_popup.js b/ambari-web/app/views/common/modal_popup.js
index ad27a40..4b9830f 100644
--- a/ambari-web/app/views/common/modal_popup.js
+++ b/ambari-web/app/views/common/modal_popup.js
@@ -162,12 +162,19 @@ App.ModalPopup = Ember.View.extend({
fitHeight: function () {
if (this.get('state') === 'destroyed') return;
- var popup = this.$().find('#modal'),
+ const popup = this.$().find('#modal'),
wrapper = $(popup).find('.modal-dialog'),
block = $(popup).find('.modal-body'),
wh = $(window).height(),
- top = wh * 0.05,
- newMaxHeight = wh - top * 2 - (wrapper.height() - block.height());
+ ww = $(window).width(),
+ topNavPaddingTop = 19, // from ambari-web/app/styles/common.less
+ topNavFontSize = 20, // from ambari-web/app/styles/common.less
+ topNavLineHeight = 1.3, // from ambari-web/app/styles/common.less
+ modalMarginTopDefault = 10, // from ambari-web/app/styles/common.less
+ modalMarginTopWide = 30, // from ambari-web/app/styles/common.less
+ modalMarginTop = ww < 768 ? modalMarginTopDefault : modalMarginTopWide, // from ambari-web/vendor/styles/bootstrap.css
+ top = topNavPaddingTop + topNavFontSize * topNavLineHeight - modalMarginTop;
+ let newMaxHeight = wh - top * 2 - (wrapper.height() - block.height());
popup.css({
'top': top + 'px',
diff --git a/ambari-web/app/views/common/modal_popups/config_validation/config_validation_popup.js b/ambari-web/app/views/common/modal_popups/config_validation/config_validation_popup.js
index 665226d..2271be1 100644
--- a/ambari-web/app/views/common/modal_popups/config_validation/config_validation_popup.js
+++ b/ambari-web/app/views/common/modal_popups/config_validation/config_validation_popup.js
@@ -26,8 +26,8 @@ var App = require('app');
App.showConfigValidationPopup = function (configErrors, primary, secondary, controller) {
return App.ModalPopup.show({
header: Em.I18n.t('installer.step7.popup.validation.warning.header'),
- classNames: ['common-modal-wrapper','modal-full-width'],
- modalDialogClasses: ['modal-lg'],
+ classNames: ['common-modal-wrapper'],
+ modalDialogClasses: ['modal-xlg'],
primary: Em.I18n.t('common.proceedAnyway'),
primaryClass: 'btn-danger',
marginBottom: 200,
diff --git a/ambari-web/app/views/common/modal_popups/dependent_configs_list_popup.js b/ambari-web/app/views/common/modal_popups/dependent_configs_list_popup.js
index 7f0c581..451d038 100644
--- a/ambari-web/app/views/common/modal_popups/dependent_configs_list_popup.js
+++ b/ambari-web/app/views/common/modal_popups/dependent_configs_list_popup.js
@@ -21,8 +21,11 @@ var App = require('app');
App.DependentConfigsTableView = Em.View.extend({
templateName: require('templates/common/modal_popups/dependent_configs_table'),
recommendations: [],
- checkboxesFirst: false,
isClickable: false,
+ showPopovers: true,
+ elementsWithPopover: function () {
+ return this.$('td.config-dependency-name');
+ }.property(),
hideMessage: function () {
return this.get('controller.isInstallWizard');
}.property('controller.isInstallWizard'),
@@ -47,12 +50,28 @@ App.DependentConfigsTableView = Em.View.extend({
message += Em.I18n.t('popup.dependent.configs.title.required');
}
return message;
- }.property('isEditable')
+ }.property('isEditable'),
+ didInsertElement: function () {
+ if (this.get('showPopovers')) {
+ App.popover(this.get('elementsWithPopover'), {
+ placement: 'auto right',
+ trigger: 'hover',
+ html: true
+ });
+ }
+ },
+ willDestroyElement: function () {
+ if (this.get('showPopovers')) {
+ this.get('elementsWithPopover').popover('destroy');
+ }
+ }
});
App.DependentConfigsListView = Em.View.extend({
templateName: require('templates/common/modal_popups/dependent_configs_list'),
isAfterRecommendation: true,
+ isRecommendationsClickable: false,
+ showRecommendationsPopovers: true,
recommendations: [],
requiredChanges: [],
allConfigsWithErrors: [],
@@ -96,32 +115,36 @@ App.DependentConfigsListView = Em.View.extend({
/**
* Show confirmation popup
- * @param {[Object]} recommendedChanges
+ * @param {[Object]} recommendations
* @param {[Object]} requiredChanges
* @param {function} [primary=null]
* @param {function} [secondary=null]
+ * @param {boolean} [isRecommendationsClickable=false]
+ * @param {boolean} [isRecommendationsClickable=true]
* we use this parameter to defer saving configs before we make some decisions.
* @return {App.ModalPopup}
*/
-App.showDependentConfigsPopup = function (recommendedChanges, requiredChanges, primary, secondary, controller) {
+App.showDependentConfigsPopup = function (recommendations, requiredChanges, primary, secondary, controller, isRecommendationsClickable = false, showRecommendationsPopovers = true) {
return App.ModalPopup.show({
encodeBody: false,
header: Em.I18n.t('popup.dependent.configs.header'),
- classNames: ['common-modal-wrapper','modal-full-width'],
- modalDialogClasses: ['modal-lg'],
+ classNames: ['common-modal-wrapper'],
+ modalDialogClasses: ['modal-xlg'],
secondaryClass: 'cancel-button',
bodyClass: App.DependentConfigsListView.extend({
- recommendations: recommendedChanges,
- requiredChanges: requiredChanges,
- controller: controller
+ recommendations,
+ requiredChanges,
+ controller,
+ isRecommendationsClickable,
+ showRecommendationsPopovers
}),
saveChanges: function() {
- recommendedChanges.forEach(function (c) {
+ recommendations.forEach(function (c) {
Em.set(c, 'saveRecommendedDefault', Em.get(c, 'saveRecommended'));
});
},
discardChanges: function() {
- recommendedChanges.forEach(function(c) {
+ recommendations.forEach(function(c) {
Em.set(c, 'saveRecommended', Em.get(c, 'saveRecommendedDefault'));
});
},
diff --git a/ambari-web/app/views/common/modal_popups/log_file_search_popup.js b/ambari-web/app/views/common/modal_popups/log_file_search_popup.js
index af40367..3c5ca11 100644
--- a/ambari-web/app/views/common/modal_popups/log_file_search_popup.js
+++ b/ambari-web/app/views/common/modal_popups/log_file_search_popup.js
@@ -20,7 +20,7 @@ var App = require('app');
App.LogFileSearchPopup = function(header) {
return App.ModalPopup.show({
- classNames: ['modal-full-width', 'common-modal-wrapper', 'log-file-search-popup'],
+ classNames: ['common-modal-wrapper', 'log-file-search-popup'],
modalDialogClasses: ['modal-lg'],
header: header,
bodyView: null,
diff --git a/ambari-web/test/mixins/common/configs/config_recommendations_test.js b/ambari-web/test/mixins/common/configs/config_recommendations_test.js
index ca29eb8..95db61b 100644
--- a/ambari-web/test/mixins/common/configs/config_recommendations_test.js
+++ b/ambari-web/test/mixins/common/configs/config_recommendations_test.js
@@ -32,12 +32,17 @@ describe('App.ConfigRecommendations', function() {
sinon.stub(App.config, 'get').withArgs('serviceByConfigTypeMap').returns({
'pFile': Em.Object.create({serviceName: 'sName', displayName: 'sDisplayName'})
});
- sinon.stub(Handlebars, 'SafeString');
+ sinon.stub(App.configsCollection, 'getConfigByName').withArgs('pName', 'pFile').returns({
+ displayName: 'pDisplayName',
+ description: 'pDescription'
+ });
+ sinon.stub(Handlebars, 'SafeString');
});
afterEach(function() {
instanceObject.formatParentProperties.restore();
App.config.get.restore();
- Handlebars.SafeString.restore();
+ App.configsCollection.getConfigByName.restore();
+ Handlebars.SafeString.restore();
});
it('adds new recommendation', function() {
@@ -47,6 +52,8 @@ describe('App.ConfigRecommendations', function() {
saveRecommendedDefault: true,
propertyFileName: 'pFile',
propertyName: 'pName',
+ propertyTitle: 'pDisplayName<br><small>pName</small>',
+ propertyDescription: 'pDescription',
isDeleted: false,
notDefined: false,
configGroup: 'pGroup',
@@ -123,6 +130,8 @@ describe('App.ConfigRecommendations', function() {
saveRecommendedDefault: true,
propertyFileName: 'pFile',
propertyName: 'pName',
+ propertyTitle: 'pDisplayName<br><small>pName</small>',
+ propertyDescription: 'pDescription',
isDeleted: false,
notDefined: false,
configGroup: 'pGroup',
@@ -144,6 +153,8 @@ describe('App.ConfigRecommendations', function() {
saveRecommendedDefault: true,
propertyFileName: 'pFile',
propertyName: 'pName',
+ propertyTitle: 'pDisplayName<br><small>pName</small>',
+ propertyDescription: 'pDescription',
isDeleted: false,
notDefined: false,
configGroup: 'pGroup',
@@ -165,6 +176,8 @@ describe('App.ConfigRecommendations', function() {
saveRecommendedDefault: true,
propertyFileName: 'pFile',
propertyName: 'pName',
+ propertyTitle: 'pDisplayName<br><small>pName</small>',
+ propertyDescription: 'pDescription',
isDeleted: true,
notDefined: true,
configGroup: 'Default',
@@ -187,12 +200,17 @@ describe('App.ConfigRecommendations', function() {
'pFile': c.service
});
sinon.stub(Handlebars, 'SafeString');
+ sinon.stub(App.configsCollection, 'getConfigByName').withArgs('pName', 'pFile.xml').returns({
+ displayName: 'pDisplayName',
+ description: 'pDescription'
+ });
recommendation = instanceObject.addRecommendation(c.name, c.file, c.group, c.recommended, c.initial, c.parent, c.isEditable);
});
afterEach(function() {
App.config.get.restore();
Handlebars.SafeString.restore();
+ App.configsCollection.getConfigByName.restore();
});
it(c.title, function() {