Updated Branches: refs/heads/trunk 9530083bb -> abaca9f8f
AMBARI-3709. Advanced Repo options UI and flow needs updating (xiwang) Project: http://git-wip-us.apache.org/repos/asf/incubator-ambari/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-ambari/commit/abaca9f8 Tree: http://git-wip-us.apache.org/repos/asf/incubator-ambari/tree/abaca9f8 Diff: http://git-wip-us.apache.org/repos/asf/incubator-ambari/diff/abaca9f8 Branch: refs/heads/trunk Commit: abaca9f8f9de340e76c2794b1754fe2b68abab8f Parents: 9530083 Author: Xi Wang <xiw...@apache.org> Authored: Wed Nov 6 16:15:35 2013 -0800 Committer: Xi Wang <xiw...@apache.org> Committed: Wed Nov 6 16:57:11 2013 -0800 ---------------------------------------------------------------------- ambari-web/app/controllers/installer.js | 38 +++-- ambari-web/app/messages.js | 8 +- ambari-web/app/styles/application.less | 52 +++++- ambari-web/app/templates/wizard/step1.hbs | 71 +++++--- ambari-web/app/views/wizard/step1_view.js | 228 +++++++++++++++++++------ 5 files changed, 295 insertions(+), 102 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/abaca9f8/ambari-web/app/controllers/installer.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/controllers/installer.js b/ambari-web/app/controllers/installer.js index 1061c73..4803680 100644 --- a/ambari-web/app/controllers/installer.js +++ b/ambari-web/app/controllers/installer.js @@ -372,28 +372,30 @@ App.InstallerController = App.WizardController.extend({ var stackName = nameVersionCombo.split('-')[0]; var stackVersion = nameVersionCombo.split('-')[1]; if (selectedStack && selectedStack.operatingSystems) { - this.set('validationCnt', selectedStack.operatingSystems.length); + this.set('validationCnt', selectedStack.get('operatingSystems').filterProperty('selected', true).length); this.set('invalidCnt', 0); selectedStack.operatingSystems.forEach(function (os) { - os.validation = 'icon-repeat'; - selectedStack.set('reload', !selectedStack.get('reload')); - App.ajax.send({ - name: 'wizard.advanced_repositories.valid_url', - sender: this, - data: { - stackName: stackName, - stackVersion: stackVersion, - nameVersionCombo: nameVersionCombo, - osType: os.osType, + if (os.selected) { + os.validation = 'icon-repeat'; + selectedStack.set('reload', !selectedStack.get('reload')); + App.ajax.send({ + name: 'wizard.advanced_repositories.valid_url', + sender: this, data: { - 'Repositories': { - 'base_url': os.baseUrl + stackName: stackName, + stackVersion: stackVersion, + nameVersionCombo: nameVersionCombo, + osType: os.osType, + data: { + 'Repositories': { + 'base_url': os.baseUrl + } } - } - }, - success: 'checkRepoURLSuccessCallback', - error: 'checkRepoURLErrorCallback' - }); + }, + success: 'checkRepoURLSuccessCallback', + error: 'checkRepoURLErrorCallback' + }); + } }, this); } }, http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/abaca9f8/ambari-web/app/messages.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/messages.js b/ambari-web/app/messages.js index ce362fb..dddc3ba 100644 --- a/ambari-web/app/messages.js +++ b/ambari-web/app/messages.js @@ -269,15 +269,17 @@ Em.I18n.translations = { 'installer.step1.header':'Select Stack', 'installer.step1.body':'Please select the service stack that you want to use to install your Hadoop cluster.', 'installer.step1.advancedRepo.title':'Advanced Repository Options', - 'installer.step1.advancedRepo.message':'Specify the repository where software packages will be downloaded from. If your hosts do not have access to the internet, you will have to create a local mirror of the repository accessible by all hosts and specify the Base URL here.', + 'installer.step1.advancedRepo.message':'Customize the repository Base URLs for downloading the Stack software packages. If your hosts do not have access to the internet, you will have to create a local mirror of the Stack repository that is accessible by all hosts and use those Base URLs shere.', + 'installer.step1.advancedRepo.importantMassage':'<b>Important:</b> When using local mirror repositories, you only need to provide Base URLs for the Operating System you are installing for your Stack. Uncheck all other repositories.', 'installer.step1.advancedRepo.localRepo.error.modifyUrl':'Local repository URL must be modified', 'installer.step1.advancedRepo.localRepo.error.noUrl':'Base URL required for a local repository', 'installer.step1.advancedRepo.localRepo.column.baseUrl':'Base URL', 'installer.step1.advancedRepo.localRepo.label.os':'Operating System', 'installer.step1.advancedRepo.localRepo.label.baseUrl':'Repository Base URL', 'installer.step1.advancedRepo.localRepo.label.stack':'Stack', - 'installer.step1.attentionNeeded':'<b>Attention:</b> All repository URLs are required before you can proceed.', - 'installer.step1.invalidURLAttention': '<b>Attention:</b> Please make sure all repository URLs are valid.', + 'installer.step1.attentionNeeded':'<b>Attention:</b> Repository URLs are REQUIRED before you can proceed.', + 'installer.step1.invalidURLAttention': '<b>Attention:</b> Please make sure all repository URLs are valid before proceed.', + 'installer.step1.checkAtLeastOneAttention': '<b>Attention:</b> Please check at least one repository.', 'installer.step2.header':'Install Options', 'installer.step2.body':'Enter the list of hosts to be included in the cluster and provide your SSH key.', http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/abaca9f8/ambari-web/app/styles/application.less ---------------------------------------------------------------------- diff --git a/ambari-web/app/styles/application.less b/ambari-web/app/styles/application.less index 0c3767e..adc7878 100644 --- a/ambari-web/app/styles/application.less +++ b/ambari-web/app/styles/application.less @@ -4750,31 +4750,69 @@ i.icon-asterisks { -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); } + .disabled-textfield input{ + color: #808080; + disabled: disabled; + pointer-events: none; + cursor: default; + } + .disabled-label { + color: #808080; + } + .group-checkbox { + width: 3%; + vertical-align: middle; + padding-left: 2px; + padding-right: 2px; + } .os { - width: 13%; + padding: 6px; + border-top: transparent; + } + .os-td { + width: 17%; + padding: 2px; } .url { - width: 70%; + width: 65%; } .url-results { - width: 70%; + width: 65%; + vertical-align: middle; .ember-text-field { width: 100%; } } .validation { - width: 7%; + width: 3%; } .validation-results { - width: 7%; - padding-top: 11px; + width: 3%; + vertical-align: middle; + padding-top: 0px; + padding-left: 4px; + padding-right: 4px; + } + .clearAll-icon { + width: 2%; + vertical-align: middle; + padding-top: 0px; + padding-left: 4px; + padding-right: 4px; + a { + cursor: pointer; + color: #808080; + } } .action { width: 10%; } .action-results { width: 10%; - padding-top: 11px; + vertical-align: middle; + padding-top: 0px; + padding-left: 4px; + padding-right: 4px; a { cursor: pointer; } http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/abaca9f8/ambari-web/app/templates/wizard/step1.hbs ---------------------------------------------------------------------- diff --git a/ambari-web/app/templates/wizard/step1.hbs b/ambari-web/app/templates/wizard/step1.hbs index 64b9576..4b038e7 100644 --- a/ambari-web/app/templates/wizard/step1.hbs +++ b/ambari-web/app/templates/wizard/step1.hbs @@ -35,12 +35,13 @@ </div> <div class="accordion-body collapse in"> <div class="accordion-inner"> - <div class="alert alert-info"> - {{t installer.step1.advancedRepo.message}} - </div> - <table class="table table-striped"> + <div class="alert alert-info">{{t installer.step1.advancedRepo.message}}</div> + <div class="alert alert-danger">{{t installer.step1.advancedRepo.importantMassage}}</div> + + <table class="table"> <thead> <tr> + <th></th> <th class="os">{{t common.os}}</th> <th class="url">{{t installer.step1.advancedRepo.localRepo.column.baseUrl}}</th> <th class="validations"></th> @@ -48,35 +49,55 @@ </tr> </thead> <tbody> - {{#each repo in view.allRepositories}} - <tr> - <td {{bindAttr class=":os repo.empty-error:label-error repo.invalid-error:label-error "}}>{{repo.osType}}</td> - <td {{bindAttr class=":url-results repo.empty-error:textfield-error repo.invalid-error:textfield-error"}}> - {{view Ember.TextField valueBinding="repo.baseUrl"}} - </td> - <td class="validation-results"> - {{#if repo.validation}} - <i {{bindAttr class="repo.validation"}}></i> - {{/if}} - </td> - <td class="action-results"> - {{#if repo.undo}} - <a {{action "undoLocalRepository" repo target="view" }}> - <i class="icon-undo"></i>{{t common.undo}} - </a> - {{/if}} - </td> - </tr> + {{#each repoGroup in view.allRepositoriesGroup}} + <tr> + <td class="group-checkbox">{{view Ember.Checkbox checkedBinding="repoGroup.checked"}}</td> + <td class="os-td"> + <table {{bindAttr class="repoGroup.checked::disabled-label"}}> + <tbody> + {{#each repo in repoGroup}} + <tr> + <td {{bindAttr class=":os repo.empty-error:label-error repo.invalid-error:label-error "}}>{{repo.osType}}</td> + </tr> + {{/each}} + </tbody> + </table> + </td> + <td {{bindAttr class=":url-results repoGroup.checked::disabled-textfield repoGroup.empty-error:textfield-error repoGroup.invalid-error:textfield-error"}}> + {{view Ember.TextField valueBinding="repoGroup.baseUrl"}} + </td> + <td class="clearAll-icon"> + {{#if repoGroup.clearAll}} + <a {{action "clearGroupLocalRepository" repoGroup target="view" }}> + <i class="icon-remove-sign"></i> + </a> + {{/if}} + </td> + <td class="validation-results"> + {{#if repoGroup.validation}} + <i {{bindAttr class="repoGroup.validation"}}></i> + {{/if}} + </td> + <td class="action-results"> + {{#if repoGroup.undo}} + <a {{action "undoGroupLocalRepository" repoGroup target="view" }}> + <i class="icon-undo"></i>{{t common.undo}} + </a> + {{/if}} + </td> + </tr> {{/each}} </tbody> </table> - {{#if view.isSubmitDisabled}} + {{#if view.emptyRepoExist}} <div class="alert">{{t installer.step1.attentionNeeded}}</div> {{/if}} {{#if view.invalidUrlExist}} <div class="alert">{{t installer.step1.invalidURLAttention}}</div> {{/if}} - + {{#if view.allRepoUnchecked}} + <div class="alert">{{t installer.step1.checkAtLeastOneAttention}}</div> + {{/if}} </div> </div> </div> http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/abaca9f8/ambari-web/app/views/wizard/step1_view.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/views/wizard/step1_view.js b/ambari-web/app/views/wizard/step1_view.js index b8d051e..5ffbdb6 100644 --- a/ambari-web/app/views/wizard/step1_view.js +++ b/ambari-web/app/views/wizard/step1_view.js @@ -45,75 +45,205 @@ App.WizardStep1View = Em.View.extend({ } }), - allRepositories: [], - repoErrorCnt: function () { - return this.get('allRepositories').filterProperty('empty-error', true).length; - }.property('allRepositories.@each.empty-error'), + allRepositoriesGroup: [[],[],[]], + emptyRepoExist: function () { + return (this.get('allRepositoriesGroup').filterProperty('empty-error', true).length != 0); + }.property('allRepositoriesGroup.@each.empty-error'), + isSubmitDisabled: function() { + return this.get('emptyRepoExist') || this.get('allRepoUnchecked') ; + }.property('emptyRepoExist', 'allRepoUnchecked'), + invalidUrlExist: function () { + var selectedStack = this.get('controller.content.stacks').findProperty('isSelected', true); + return (selectedStack.get('invalidCnt') > 0); + }.property('controller.content.stacks.@each.invalidCnt'), + allRepoUnchecked: function () { + return (!this.get('allRepositoriesGroup').filterProperty('checked', true).length); + }.property('allRepositoriesGroup.@each.checked'), + + /** + * Onclick handler for Config Group Header. Used to show/hide block + */ + onToggleBlock: function () { + this.$('.accordion-body').toggle('blind', 500); + this.set('isRLCollapsed', !this.get('isRLCollapsed')); + }, + isRLCollapsed: true, + didInsertElement: function () { + if (this.get('isRLCollapsed')) { + this.$('.accordion-body').hide(); + } + }, loadRepositories: function () { var selectedStack = this.get('controller.content.stacks').findProperty('isSelected', true); - var repos = []; + var reposGroup = [[],[],[]]; + var self = this; if (selectedStack && selectedStack.operatingSystems) { selectedStack.operatingSystems.forEach(function (os) { - var cur_repo = Em.Object.create({ - baseUrl: os.baseUrl, - defaultBaseUrl: os.defaultBaseUrl, - osType: os.osType, - validation: os.validation - }); - cur_repo.set('empty-error', !os.baseUrl); - cur_repo.set('invalid-error', os.validation == 'icon-remove'); - cur_repo.set('undo', os.baseUrl != os.defaultBaseUrl); - repos.pushObject(cur_repo); + var cur_repo = Em.Object.create({ + baseUrl: os.baseUrl + }); + switch(os.osType) { + case 'redhat5': + cur_repo.set('osType', 'Red Hat 5'); + reposGroup[0][0] = cur_repo; + // set group 0 properties by redhat5 (any of the three is ok) + self.setGroupByOs(reposGroup[0], os, 0); + break; + case 'centos5': + cur_repo.set('osType', 'CentOS 5'); + reposGroup[0][1] = cur_repo; + break; + case 'oraclelinux5': + cur_repo.set('osType', 'Oracle Linux 5'); + reposGroup[0][2] = cur_repo; + break; + case 'redhat6': + cur_repo.set('osType', 'Red Hat 6'); + reposGroup[1][0] = cur_repo; + // set group 1 properties by redhat6 (any of the three is ok) + self.setGroupByOs(reposGroup[1], os, 1); + break; + case 'centos6': + cur_repo.set('osType', 'CentOS 6'); + reposGroup[1][1] = cur_repo; + break; + case 'oraclelinux6': + cur_repo.set('osType', 'Oracle Linux 6'); + reposGroup[1][2] = cur_repo; + break; + case 'sles11': + cur_repo.set('osType', 'SLES 11'); + reposGroup[2][0] = cur_repo; + // set group 2 properties by sles11 (any of the twe is ok) + self.setGroupByOs(reposGroup[2], os, 2); + break; + case 'suse11': + cur_repo.set('osType', 'SUSE 11'); + reposGroup[2][1] = cur_repo; + break; + } }); } - this.set('allRepositories', repos); + this.set('allRepositoriesGroup', reposGroup); }.observes('controller.content.stacks.@each.isSelected', 'controller.content.stacks.@each.reload'), - - undoLocalRepository: function (event) { - var localRepo = event.context; - var selectedStack = this.get('controller.content.stacks').findProperty('isSelected', true); - var cos = selectedStack.operatingSystems.findProperty('osType', localRepo.osType); - cos.baseUrl = cos.defaultBaseUrl; - cos.validation = null; - this.loadRepositories(); + setGroupByOs: function (group, os, groupNumber) { + var isChecked = this.get('allGroupsCheckbox')[groupNumber]; + group.set('checked', isChecked); + group.set('baseUrl', os.baseUrl); + group.set('defaultBaseUrl', os.defaultBaseUrl); + group.set('empty-error', !os.baseUrl); + group.set('invalid-error', os.validation == 'icon-remove'); + group.set('validation', os.validation); + group.set('undo', os.baseUrl != os.defaultBaseUrl); + group.set('clearAll', os.baseUrl); + group.set('group-number', groupNumber); }, - editLocalRepository: function (event) { + /** + * Onclick handler for checkbox of each repo group + */ + updateByCheckbox: function () { //upload to content - var repos = this.get('allRepositories'); + var groups = this.get('allRepositoriesGroup'); + var self = this; var selectedStack = this.get('controller.content.stacks').findProperty('isSelected', true); if (selectedStack && selectedStack.operatingSystems) { selectedStack.operatingSystems.forEach(function (os) { - var target = repos.findProperty('osType', os.osType); - if ( os.baseUrl != target.get('baseUrl')) { - os.baseUrl = target.get('baseUrl'); + var groupNumber = self.osTypeToGroup(os.osType); + var targetGroup = groups.findProperty('group-number', groupNumber); + if (!targetGroup.get('checked')) { + os.baseUrl = os.defaultBaseUrl; os.validation = null; - target.set('undo', target.get('baseUrl') != target.get('defaultBaseUrl')); - target.set('invalid-error', false); - target.set('validation', null); - target.set('empty-error',!target.get('baseUrl')); + os.selected = false; + targetGroup.set('baseUrl', os.defaultBaseUrl); + targetGroup.set('undo', targetGroup.get('baseUrl') != targetGroup.get('defaultBaseUrl')); + targetGroup.set('invalid-error', false); + targetGroup.set('validation', null); + targetGroup.set('clearAll', false); + targetGroup.set('empty-error',!targetGroup.get('baseUrl')); + self.get('allGroupsCheckbox')[groupNumber] = false; + self.set('allGroupsCheckbox', self.get('allGroupsCheckbox')); + } else { + os.selected = true; + targetGroup.set('clearAll', targetGroup.get('baseUrl')); + targetGroup.set('empty-error',!targetGroup.get('baseUrl')); + self.get('allGroupsCheckbox')[groupNumber] = true; } }); } - }.observes('allRepositories.@each.baseUrl'), - isSubmitDisabled: function() { - return this.get('repoErrorCnt') != 0; - }.property('repoErrorCnt'), - invalidUrlExist: function () { - var selectedStack = this.get('controller.content.stacks').findProperty('isSelected', true); - return (selectedStack.get('invalidCnt') > 0); - }.property('controller.content.stacks.@each.invalidCnt'), + }.observes('allRepositoriesGroup.@each.checked'), + allGroupsCheckbox: [true, true, true], /** - * Onclick handler for Config Group Header. Used to show/hide block + * Onclick handler for undo action of each repo group */ - onToggleBlock: function () { - this.$('.accordion-body').toggle('blind', 500); - this.set('isRLCollapsed', !this.get('isRLCollapsed')); + undoGroupLocalRepository: function (event) { + var group = event.context; + var osTypes = this.groupToOsType(group.get('group-number')); + var selectedStack = this.get('controller.content.stacks').findProperty('isSelected', true); + osTypes.forEach( function (os) { + var cos = selectedStack.operatingSystems.findProperty('osType', os ); + cos.baseUrl = cos.defaultBaseUrl; + cos.validation = null; + }); + this.loadRepositories(); }, - isRLCollapsed: true, - didInsertElement: function () { - if (this.get('isRLCollapsed')) { - this.$('.accordion-body').hide(); + clearGroupLocalRepository: function (event) { + var group = event.context; + var osTypes = this.groupToOsType(group.get('group-number')); + var selectedStack = this.get('controller.content.stacks').findProperty('isSelected', true); + osTypes.forEach( function (os) { + var cos = selectedStack.operatingSystems.findProperty('osType', os ); + cos.baseUrl = ''; + cos.validation = null; + }); + this.loadRepositories(); + }, + /** + * Handler when editing any repo group BaseUrl + */ + editGroupLocalRepository: function (event) { + //upload to content + var groups = this.get('allRepositoriesGroup'); + var self = this; + var selectedStack = this.get('controller.content.stacks').findProperty('isSelected', true); + if (selectedStack && selectedStack.operatingSystems) { + selectedStack.operatingSystems.forEach(function (os) { + var targetGroup = groups.findProperty('group-number', self.osTypeToGroup(os.osType)); + if (os.baseUrl != targetGroup.get('baseUrl')) { + os.baseUrl = targetGroup.get('baseUrl'); + os.validation = null; + targetGroup.set('undo', targetGroup.get('baseUrl') != targetGroup.get('defaultBaseUrl')); + targetGroup.set('invalid-error', false); + targetGroup.set('validation', null); + targetGroup.set('clearAll', os.baseUrl); + targetGroup.set('empty-error',!targetGroup.get('baseUrl')); + } + }); + } + }.observes('allRepositoriesGroup.@each.baseUrl'), + groupToOsType: function (groupNumber) { + switch (groupNumber) { + case 0: + return ['redhat5', 'centos5', 'oraclelinux5']; + case 1: + return ['redhat6', 'centos6', 'oraclelinux6']; + case 2: + return ['sles11', 'suse11']; + } + }, + osTypeToGroup: function (osType) { + switch(osType) { + case 'redhat5': + case 'centos5': + case 'oraclelinux5': + return 0; + case 'redhat6': + case 'centos6': + case 'oraclelinux6': + return 1; + case 'sles11': + case 'suse11': + return 2; } } });