Updated Branches:
  refs/heads/trunk 06fd82a65 -> 82d26cbe0

AMBARI-3319. Simplify Local Repo setup via UI. (xiwang via yusaku)


Project: http://git-wip-us.apache.org/repos/asf/incubator-ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-ambari/commit/82d26cbe
Tree: http://git-wip-us.apache.org/repos/asf/incubator-ambari/tree/82d26cbe
Diff: http://git-wip-us.apache.org/repos/asf/incubator-ambari/diff/82d26cbe

Branch: refs/heads/trunk
Commit: 82d26cbe05cf02bec5235c794130798d904df26d
Parents: 06fd82a
Author: Yusaku Sako <[email protected]>
Authored: Thu Oct 10 19:41:27 2013 -0700
Committer: Yusaku Sako <[email protected]>
Committed: Thu Oct 10 19:41:27 2013 -0700

----------------------------------------------------------------------
 ambari-web/app/config.js                  |   2 +-
 ambari-web/app/controllers/installer.js   |  70 +++++++++++++
 ambari-web/app/messages.js                |   4 +-
 ambari-web/app/routes/installer.js        |  21 +++-
 ambari-web/app/styles/application.less    |  63 ++++++++---
 ambari-web/app/templates/wizard/step1.hbs |  79 ++++++++------
 ambari-web/app/templates/wizard/step2.hbs |   4 +-
 ambari-web/app/utils/ajax.js              |  12 +++
 ambari-web/app/views/wizard/step1_view.js | 140 +++++++++++--------------
 9 files changed, 264 insertions(+), 131 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/82d26cbe/ambari-web/app/config.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/config.js b/ambari-web/app/config.js
index 60ac91c..0a4ee05 100644
--- a/ambari-web/app/config.js
+++ b/ambari-web/app/config.js
@@ -57,7 +57,7 @@ App.supports = {
   customizeSmokeTestUser: true,
   hue: false,
   ldapGroupMapping: false,
-  localRepositories: false,
+  localRepositories: true,
   highAvailability: true,
   deleteHost: false,
   autoRollbackHA: true

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/82d26cbe/ambari-web/app/controllers/installer.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/installer.js 
b/ambari-web/app/controllers/installer.js
index 2fe4f5e..02c0880 100644
--- a/ambari-web/app/controllers/installer.js
+++ b/ambari-web/app/controllers/installer.js
@@ -357,6 +357,76 @@ App.InstallerController = App.WizardController.extend({
   },
 
   /**
+   * Check validation of the customized local urls
+   * @param stepController step1WizardController
+   */
+  checkRepoURL: function (stepController) {
+    var selectedStack = this.get('content.stacks').findProperty('isSelected', 
true);
+    selectedStack.set('reload', true);
+    var nameVersionCombo = selectedStack.name;
+    var stackName = nameVersionCombo.split('-')[0];
+    var stackVersion = nameVersionCombo.split('-')[1];
+    if (selectedStack && selectedStack.operatingSystems) {
+      this.set('validationCnt', selectedStack.operatingSystems.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,
+            data: {
+              'Repositories': {
+                'base_url': os.baseUrl
+              }
+            }
+          },
+          success: 'checkRepoURLSuccessCallback',
+          error: 'checkRepoURLErrorCallback'
+        });
+      }, this);
+    }
+  },
+  setInvalidUrlCnt: function () {
+    var selectedStack = this.get('content.stacks').findProperty('isSelected', 
true);
+    selectedStack.set('invalidCnt', this.get('invalidCnt'));
+  }.observes('invalidCnt'),
+  /**
+   * onSuccess callback for check Repo URL.
+   */
+  checkRepoURLSuccessCallback: function (response, request, data) {
+    console.log('Success in check Repo URL. data osType: ' + data.osType );
+    var selectedStack = this.get('content.stacks').findProperty('isSelected', 
true);
+    if (selectedStack && selectedStack.operatingSystems) {
+      var os = selectedStack.operatingSystems.findProperty('osType', 
data.osType);
+      os.validation = 'icon-ok';
+      selectedStack.set('reload', !selectedStack.get('reload'));
+      this.set('validationCnt', this.get('validationCnt') - 1);
+    }
+  },
+
+  /**
+   * onError callback for check Repo URL.
+   */
+  checkRepoURLErrorCallback: function (request, ajaxOptions, error, data) {
+    console.log('Error in check Repo URL. The baseURL sent is:  ' + data.data);
+    var osType = data.url.split('/')[8];
+    var selectedStack = this.get('content.stacks').findProperty('isSelected', 
true);
+    if (selectedStack && selectedStack.operatingSystems) {
+      var os = selectedStack.operatingSystems.findProperty('osType', osType);
+      os.validation = 'icon-remove';
+      selectedStack.set('reload', !selectedStack.get('reload'));
+      this.set('validationCnt', this.get('validationCnt') - 1);
+      this.set('invalidCnt', this.get('invalidCnt') + 1);
+    }
+  },
+
+  /**
    * Load data for all steps until <code>current step</code>
    */
   loadAllPriorSteps: function () {

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/82d26cbe/ambari-web/app/messages.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/messages.js b/ambari-web/app/messages.js
index e7d0d03..944c64e 100644
--- a/ambari-web/app/messages.js
+++ b/ambari-web/app/messages.js
@@ -263,7 +263,7 @@ 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':'Advaned Repository Options',
+  '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.localRepo.error.modifyUrl':'Local repository 
URL must be modified',
   'installer.step1.advancedRepo.localRepo.error.noUrl':'Base URL required for 
a local repository',
@@ -271,6 +271,8 @@ Em.I18n.translations = {
   '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.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/82d26cbe/ambari-web/app/routes/installer.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/routes/installer.js 
b/ambari-web/app/routes/installer.js
index 55434e4..c90c82e 100644
--- a/ambari-web/app/routes/installer.js
+++ b/ambari-web/app/routes/installer.js
@@ -136,10 +136,23 @@ module.exports = Em.Route.extend({
     next: function (router) {
       var wizardStep1Controller = router.get('wizardStep1Controller');
       var installerController = router.get('installerController');
-      installerController.saveStacks(wizardStep1Controller);
-      App.db.setService(undefined);
-      installerController.clearInstallOptions();
-      router.transitionTo('step2');
+      installerController.checkRepoURL(wizardStep1Controller);
+     // make sure got all validations feedback and no invalid url, then proceed
+      var myVar = setInterval(
+        function(){
+          var cnt = installerController.get('validationCnt');
+          var invalidCnt = installerController.get('invalidCnt')
+          if (cnt == 0 && invalidCnt == 0) { // all feedback exist and no 
invalid url
+            installerController.saveStacks(wizardStep1Controller);
+            App.db.setService(undefined);
+            installerController.clearInstallOptions();
+            router.transitionTo('step2');
+            clearInterval(myVar);
+          } else if ( cnt == 0 && invalidCnt != 0) {
+            clearInterval(myVar);
+          }
+        },
+      1000);
     }
   }),
 

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/82d26cbe/ambari-web/app/styles/application.less
----------------------------------------------------------------------
diff --git a/ambari-web/app/styles/application.less 
b/ambari-web/app/styles/application.less
index d248819..cd95024 100644
--- a/ambari-web/app/styles/application.less
+++ b/ambari-web/app/styles/application.less
@@ -4654,23 +4654,56 @@ i.icon-asterisks {
 }
 
 #advancedRepoAccordion{
-  #collapseOne{
-    .pull-right{
-      margin-bottom: 5px;
-    }
-    th.os{
-      width: 15%;
-    }
-    th.actions{
-      width: 15%;
+  .accordion-heading {
+    background-color: #f0f0f0;
+  }
+  .accordion-body {
+    .table thead {
     }
-    .action{
-      cursor: pointer;
-      .icon-minus-sign{
-        color: #FF4B4B;
-        margin-right: 2px;
+    .table tbody{
+      .label-error{
+        color: #b94a48;
+      }
+      .textfield-error input{
+        border-color: #b94a48;
+        -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+        -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);
+      }
+      .os {
+        width: 13%;
+      }
+      .url {
+        width: 70%;
+      }
+      .url-results {
+        width: 70%;
+        .ember-text-field {
+          width: 100%;
+        }
+      }
+      .validation {
+        width: 7%;
+      }
+      .validation-results {
+        width: 7%;
+        padding-top: 11px;
+      }
+      .action {
+        width: 10%;
+      }
+      .action-results {
+        width: 10%;
+        padding-top: 11px;
+        a {
+          cursor: pointer;
+        }
+        .icon-undo {
+          color: rgb(243, 178, 11);
+          margin-right: 2px;
+        }
       }
     }
-    
   }
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/82d26cbe/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 9995e51..64b9576 100644
--- a/ambari-web/app/templates/wizard/step1.hbs
+++ b/ambari-web/app/templates/wizard/step1.hbs
@@ -29,44 +29,63 @@
 {{#if App.supports.localRepositories}}
 <div class="accordion" id="advancedRepoAccordion">
   <div class="accordion-group">
-    <div class="accordion-heading">
-      <a class="accordion-toggle" data-toggle="collapse" 
data-parent="#advancedRepoAccordion" href="#collapseOne">
-        {{t installer.step1.advancedRepo.title}}
-      </a>
+    <div class="accordion-heading" {{action "onToggleBlock" target="view"}}>
+      <i {{bindAttr class=":pull-left :accordion-toggle 
view.isRLCollapsed:icon-caret-right:icon-caret-down"}}></i>
+      <a class="accordion-toggle">{{t installer.step1.advancedRepo.title}}</a>
     </div>
-    <div id="collapseOne" class="accordion-body collapse">
-      <div class="accordion-inner">
-        <div class="alert alert-info">
-          {{t installer.step1.advancedRepo.message}}
-        </div>
-        <table class="table table-striped">
-          <thead>
+      <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">
+            <thead>
             <tr>
               <th class="os">{{t common.os}}</th>
-              <th class="baseUrl">{{t 
installer.step1.advancedRepo.localRepo.column.baseUrl}}</th>
+              <th class="url">{{t 
installer.step1.advancedRepo.localRepo.column.baseUrl}}</th>
+              <th class="validations"></th>
               <th class="actions"></th>
             </tr>
-          </thead>
-          <tbody>
-          {{#each localRepo in view.localRepositories}}
-            <tr>
-              <td>{{localRepo.osType}}</td>
-              <td>{{localRepo.baseUrl}}</td>
-              <td>
-                <a class="action" {{action "removeLocalRepository" localRepo 
target="view" }} >
-                  <i class="icon-minus-sign"></i>{{t common.remove}}
-                </a>
-              </td>
-            </tr>
-          {{/each}}
-          </tbody>
-        </table>
-        <a {{bindAttr class=":btn :pull-right view.isAddOSDisabled:disabled"}} 
{{action addLocalRepository target="view"}}><i class="icon-plus"></i> Add OS</a>
+            </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}}
+            </tbody>
+          </table>
+          {{#if view.isSubmitDisabled}}
+            <div class="alert">{{t installer.step1.attentionNeeded}}</div>
+          {{/if}}
+          {{#if view.invalidUrlExist}}
+            <div class="alert">{{t installer.step1.invalidURLAttention}}</div>
+          {{/if}}
+
+        </div>
       </div>
-    </div>
   </div>
 </div>
 {{/if}}
 
 <a class="btn pull-left" {{action back}}>&larr; {{t common.back}}</a>
-<a class="btn btn-success pull-right" {{action next}}>{{t common.next}} 
&rarr;</a>
\ No newline at end of file
+{{#if view.isSubmitDisabled}}
+   <a class="btn btn-success pull-right" disabled="disabled">{{t common.next}} 
&rarr;</a>
+{{else}}
+   <a class="btn btn-success pull-right" {{action next}}>{{t common.next}} 
&rarr;</a>
+{{/if}}

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/82d26cbe/ambari-web/app/templates/wizard/step2.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/wizard/step2.hbs 
b/ambari-web/app/templates/wizard/step2.hbs
index 685dc74..3270c5d 100644
--- a/ambari-web/app/templates/wizard/step2.hbs
+++ b/ambari-web/app/templates/wizard/step2.hbs
@@ -98,9 +98,8 @@
   </div>
 
   <div class="advancedOptions">
-    <h5>{{t installer.step2.advancedOptions.header}}</h5>
-
     {{#unless App.supports.localRepositories}}
+    <h5>{{t installer.step2.advancedOptions.header}}</h5>
     <label {{bindAttr class=":checkbox"}}>
       {{view Ember.Checkbox checkedBinding="content.installOptions.localRepo"}}
 
@@ -113,7 +112,6 @@
       {{t installer.step2.localRepo.label_instead}}
     </label>
     {{/unless}}
-    
   </div>
   <div class="btn-area">
     {{#unless view.parentView.controller.hideBackButton}}

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/82d26cbe/ambari-web/app/utils/ajax.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/utils/ajax.js b/ambari-web/app/utils/ajax.js
index d8b8952..d55f372 100644
--- a/ambari-web/app/utils/ajax.js
+++ b/ambari-web/app/utils/ajax.js
@@ -835,6 +835,18 @@ var urls = {
     'real': '/clusters/{cluster}/requests/{requestId}?fields=tasks/*',
     'mock': '/data/wizard/{mock}'
   },
+  'wizard.advanced_repositories.valid_url': {
+    'real': 
'/stacks2/{stackName}/versions/{stackVersion}/operatingSystems/{osType}/repositories/{nameVersionCombo}',
+    'mock': '',
+    'type': 'PUT',
+    'format': function (data) {
+      return {
+        type: 'PUT',
+        async: true,
+        data: JSON.stringify(data.data)
+      }
+    }
+  },
   'wizard.install_services.add_host_controller.is_retry': {
     'real': '/clusters/{cluster}/host_components',
     'mock': '',

http://git-wip-us.apache.org/repos/asf/incubator-ambari/blob/82d26cbe/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 f3d9af9..b8d051e 100644
--- a/ambari-web/app/views/wizard/step1_view.js
+++ b/ambari-web/app/views/wizard/step1_view.js
@@ -31,27 +31,6 @@ App.WizardStep1View = Em.View.extend({
     return stacks;
   }.property('[email protected]'),
 
-  isAddOSDisabled: true,
-  localRepositories: [],
-  defaultRepositories: [],
-  refreshRepositoryInfo: function () {
-    var selectedStack = 
this.get('controller.content.stacks').findProperty('isSelected', true);
-    var localRepos = [];
-    var defaultRepos = [];
-    if (selectedStack && selectedStack.operatingSystems) {
-      selectedStack.operatingSystems.forEach(function (os) {
-        if (os.baseUrl !== os.defaultBaseUrl) {
-          localRepos.push($.extend({}, os));
-        } else {
-          defaultRepos.push($.extend({}, os));
-        }
-      });
-    }
-    this.set('localRepositories', localRepos);
-    this.set('defaultRepositories', defaultRepos);
-    this.set('isAddOSDisabled', defaultRepos.get('length') < 1);
-  }.observes('[email protected]', 
'[email protected][email protected]'),
-
   stackRadioButton: Ember.Checkbox.extend({
     tagName: 'input',
     attributeBindings: [ 'type', 'checked' ],
@@ -66,68 +45,75 @@ App.WizardStep1View = Em.View.extend({
     }
   }),
 
-  removeLocalRepository: function (event) {
-    var localRepo = event.context;
+  allRepositories: [],
+  repoErrorCnt: function () {
+    return this.get('allRepositories').filterProperty('empty-error', 
true).length;
+  }.property('[email protected]'),
+  loadRepositories: function () {
+    var selectedStack = 
this.get('controller.content.stacks').findProperty('isSelected', true);
+    var repos = [];
+    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);
+      });
+    }
+    this.set('allRepositories', repos);
+  }.observes('[email protected]', 
'[email protected]'),
 
+  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;
-
-    this.refreshRepositoryInfo();
+    cos.validation = null;
+    this.loadRepositories();
   },
-
-  addLocalRepository: function () {
-    var self = this;
-    var defaultRepos = self.get('defaultRepositories');
+  editLocalRepository: function (event) {
+    //upload to content
+    var repos = this.get('allRepositories');
     var selectedStack = 
this.get('controller.content.stacks').findProperty('isSelected', true);
-
-    App.ModalPopup.show({
-      // classNames: ['big-modal'],
-      classNames: [ 'sixty-percent-width-modal' ],
-      header: "Add Local Repository",
-      primary: 'Add',
-      secondary: 'Cancel',
-      onPrimary: function () {
-        var error = null;
-        var childViews = this.get('childViews');
-        if (childViews && childViews.get('length') > 0) {
-          var childView = childViews.objectAt(0);
-          if (childView) {
-            if (childView.get('enteredUrl')) {
-              if (childView.get('selectedOS').baseUrl !== 
childView.get('enteredUrl') && childView.get('selectedOS').defaultBaseUrl !== 
childView.get('enteredUrl')) {
-                var selectedStack = 
self.get('controller.content.stacks').findProperty('isSelected', true);
-                var cos = 
selectedStack.operatingSystems.findProperty('osType', 
childView.get('selectedOS').osType);
-                cos.baseUrl = childView.get('enteredUrl');
-                self.refreshRepositoryInfo();
-                this.hide();
-              } else {
-                error = 
Em.I18n.t('installer.step1.advancedRepo.localRepo.error.modifyUrl');
-              }
-            } else {
-              error = 
Em.I18n.t('installer.step1.advancedRepo.localRepo.error.noUrl')
-            }
-            if (childView.get('isVisible'))
-              childView.set('errorMessage', error);
-          }
-        }
-      },
-      onSecondary: function () {
-        this.hide();
-      },
-      bodyClass: Ember.View.extend({
-        templateName: require('templates/wizard/step1_addLocalRepository'),
-        controller: self.get('controller'),
-        stackName: selectedStack.get('name'),
-        selectedOS: defaultRepos.objectAt(0),
-        enteredUrl: defaultRepos.objectAt(0).baseUrl,
-        oses: defaultRepos,
-        errorMessage: null,
-        selectOS: function (event) {
-          var os = event.context;
-          this.set('selectedOS', os);
-          this.set('enteredUrl', os.baseUrl);
+    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');
+          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'));
         }
-      })
-    });
+      });
+    }
+  }.observes('[email protected]'),
+  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('[email protected]'),
+
+  /**
+   * 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();
+    }
   }
 });

Reply via email to