AMBARI-16940. VDF: support for selecting enabled + default Stacks. (Oleg, Joe, 
Andrii and Jaimin)


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

Branch: refs/heads/branch-2.4
Commit: 9f93ea6bc55e15f57bb0e6511d05cdd72e69d171
Parents: d4cce20
Author: Jaimin Jetly <[email protected]>
Authored: Fri May 27 17:28:43 2016 -0700
Committer: Jaimin Jetly <[email protected]>
Committed: Fri May 27 17:28:43 2016 -0700

----------------------------------------------------------------------
 .../main/resources/ui/admin-web/app/index.html  |   2 +
 .../stackVersions/StackVersionsCreateCtrl.js    | 305 +++++++++++---
 .../stackVersions/StackVersionsEditCtrl.js      |  13 +-
 .../stackVersions/StackVersionsListCtrl.js      | 205 ++++++---
 .../ui/admin-web/app/scripts/i18n.config.js     |  19 +-
 .../app/scripts/services/AddVersionModal.js     | 172 ++++++++
 .../app/scripts/services/ConfirmationModal.js   |   3 +-
 .../ui/admin-web/app/scripts/services/Stack.js  |  65 ++-
 .../admin-web/app/scripts/services/Utility.js   |  70 ++++
 .../resources/ui/admin-web/app/styles/main.css  | 191 ++++++++-
 .../app/views/modals/AddVersionModal.html       |  52 +++
 .../app/views/modals/ConfirmationModal.html     |   2 +-
 .../app/views/modals/publicRepoDisabled.html    |  23 +
 .../admin-web/app/views/stackVersions/list.html | 123 ++++--
 .../views/stackVersions/stackVersionPage.html   | 314 +++++++-------
 ambari-web/app/assets/test/tests.js             |   1 +
 ambari-web/app/controllers/installer.js         |  67 ++-
 .../app/controllers/wizard/step0_controller.js  |   4 +-
 .../app/controllers/wizard/step1_controller.js  | 417 +++++++++++++++++--
 .../app/controllers/wizard/step3_controller.js  |   3 +-
 .../app/controllers/wizard/step4_controller.js  |   2 +-
 .../app/controllers/wizard/step7_controller.js  |   2 +-
 ambari-web/app/mappers/stack_mapper.js          |  10 +-
 ambari-web/app/messages.js                      |  12 +-
 ambari-web/app/models/repository.js             |  18 +-
 ambari-web/app/models/stack.js                  |  47 ++-
 .../app/models/stack_version/service_simple.js  |   2 +-
 ambari-web/app/routes/installer.js              |   4 +-
 ambari-web/app/styles/application.less          |  65 +--
 ambari-web/app/styles/common.less               |  43 ++
 ambari-web/app/templates/wizard/step1.hbs       | 326 ++++++++-------
 .../public_option_disabled_window_body.hbs      |  25 ++
 .../app/templates/wizard/step1/vdf_upload.hbs   |  44 ++
 ambari-web/app/utils/array_utils.js             |  32 ++
 ambari-web/app/views/wizard/step1_view.js       | 293 +++++--------
 ambari-web/karma.conf.js                        |   8 +-
 ambari-web/test/controllers/installer_test.js   |  19 +
 .../test/controllers/wizard/step1_test.js       | 212 ++++++++++
 .../config_recommendation_parser_test.js        |   4 +-
 ambari-web/test/utils/array_utils_test.js       |  43 ++
 .../host_progress_popup_body_view_test.js       |  43 +-
 ambari-web/test/views/wizard/step1_view_test.js | 201 +--------
 42 files changed, 2468 insertions(+), 1038 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/9f93ea6b/ambari-admin/src/main/resources/ui/admin-web/app/index.html
----------------------------------------------------------------------
diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/index.html 
b/ambari-admin/src/main/resources/ui/admin-web/app/index.html
index 9344f15..11e0a2e 100644
--- a/ambari-admin/src/main/resources/ui/admin-web/app/index.html
+++ b/ambari-admin/src/main/resources/ui/admin-web/app/index.html
@@ -155,6 +155,7 @@
 <script src="scripts/directives/PasswordVerify.js"></script>
 <script src="scripts/directives/disabledTooltip.js"></script>
 <script src="scripts/directives/editableList.js"></script>
+<script src="scripts/services/Utility.js"></script>
 <script src="scripts/services/UserConstants.js"></script>
 <script src="scripts/services/User.js"></script>
 <script src="scripts/services/Group.js"></script>
@@ -171,6 +172,7 @@
 <script src="scripts/services/UnsavedDialog.js"></script>
 <script src="scripts/services/Stack.js"></script>
 <script src="scripts/services/AddRepositoryModal.js"></script>
+<script src="scripts/services/AddVersionModal.js"></script>
 <script src="scripts/services/RoleDetailsModal.js"></script>
 <!-- endbuild -->
 </body>

http://git-wip-us.apache.org/repos/asf/ambari/blob/9f93ea6b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/stackVersions/StackVersionsCreateCtrl.js
----------------------------------------------------------------------
diff --git 
a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/stackVersions/StackVersionsCreateCtrl.js
 
b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/stackVersions/StackVersionsCreateCtrl.js
index 47b72e6..5433bd6 100644
--- 
a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/stackVersions/StackVersionsCreateCtrl.js
+++ 
b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/stackVersions/StackVersionsCreateCtrl.js
@@ -18,13 +18,17 @@
 'use strict';
 
 angular.module('ambariAdminConsole')
-.controller('StackVersionsCreateCtrl', ['$scope', 'Stack', '$routeParams', 
'$location', 'Alert', '$translate', 'Cluster', 'AddRepositoryModal', 
'ConfirmationModal', function($scope, Stack, $routeParams, $location, Alert, 
$translate, Cluster, AddRepositoryModal, ConfirmationModal) {
+.controller('StackVersionsCreateCtrl', ['$scope', 'Stack', 'Utility', 
'$routeParams', '$location', '$timeout' ,'Alert', '$translate', 'Cluster', 
'AddRepositoryModal', 'AddVersionModal', 'ConfirmationModal',
+    function($scope, Stack, Utility, $routeParams, $location, $timeout, Alert, 
$translate, Cluster, AddRepositoryModal, AddVersionModal, ConfirmationModal) {
   var $t = $translate.instant;
   $scope.constants = {
     os: $t('versions.os')
   };
   $scope.createController = true;
   $scope.osList = [];
+  $scope.stackIds = [];
+  $scope.allVersions = [];
+  $scope.networkLost = false;
   $scope.skipValidation = false;
   $scope.useRedhatSatellite = false;
 
@@ -66,18 +70,34 @@ angular.module('ambariAdminConsole')
   /**
    * User can select ONLY one option to upload version definition file
    */
-  $scope.togglePublicLocalOptionSelect = function () {
-    $scope.option1.hasError = false;
-    $scope.option2.hasError = false;
-  };
   $scope.toggleOptionSelect = function () {
     $scope.option1.hasError = false;
     $scope.option2.hasError = false;
   };
+  $scope.isPublicRepoSelected = function () {
+    if ($scope.selectedOption.index == $scope.publicOption.index) return true;
+  };
   $scope.togglePublicLocalOptionSelect = function () {
-    if ($scope.selectedOption.index == $scope.publicOption.index && 
$scope.selectedPublicRepoVersion) {
-      $scope.setVersionSelected($scope.selectedPublicRepoVersion);
+    if ($scope.selectedOption.index == $scope.publicOption.index) {
+      $scope.setInitialPublicRepoVersions();
+    } else {
+      $scope.clearRepoVersions();
     }
+    $scope.validateRepoUrl();
+  };
+  $scope.setInitialPublicRepoVersions = function () {
+    angular.forEach($scope.osList, function (os) {
+      os.repositories.forEach(function(repo) {
+        repo.Repositories.base_url = repo.Repositories.initial_base_url;
+      });
+    });
+  };
+  $scope.clearRepoVersions = function () {
+    angular.forEach($scope.osList, function (os) {
+      os.repositories.forEach(function(repo) {
+        repo.Repositories.base_url = '';
+      });
+    });
   };
   $scope.clearOptionsError = function () {
     $scope.option1.hasError = false;
@@ -117,6 +137,13 @@ angular.module('ambariAdminConsole')
   };
 
   /**
+   * On click handler for adding a new version
+   */
+  $scope.addVersion = function() {
+    AddVersionModal.show($scope);
+  };
+
+  /**
    * Load selected file to current page content
    */
   $scope.readVersionInfo = function(){
@@ -154,21 +181,30 @@ angular.module('ambariAdminConsole')
   $scope.afterStackVersionRead = function () {
     Stack.getSupportedOSList($scope.upgradeStack.stack_name, 
$scope.upgradeStack.stack_version)
       .then(function (data) {
+        var existingOSHash = {};
+        angular.forEach($scope.osList, function (os) {
+          if (angular.isUndefined(os.selected)) {
+            os.selected = true;
+          }
+          existingOSHash[os.OperatingSystems.os_type] = os;
+
+        });
         var operatingSystems = data.operating_systems;
-        operatingSystems.map(function (os) {
-          var existingOSHash = {};
-          angular.forEach($scope.osList, function (os) {
-            existingOSHash[os.OperatingSystems.os_type] = os;
-          });
+        angular.forEach(operatingSystems, function (stackOs) {
           // if os not in the list, mark as un-selected, add this to the osList
-          if (!existingOSHash[os.OperatingSystems.os_type]) {
-            os.selected = false;
-            os.repositories.forEach(function(repo) {
+          if (!existingOSHash[stackOs.OperatingSystems.os_type]) {
+            stackOs.selected = false;
+            stackOs.repositories.forEach(function(repo) {
               repo.Repositories.base_url = '';
+              repo.Repositories.initial_base_url = '';
             });
-            $scope.osList.push(os);
+            $scope.osList.push(stackOs);
           }
         });
+        if ($scope.selectedOption.index == $scope.localOption.index) {
+          $scope.clearRepoVersions();
+          $scope.validateRepoUrl();
+        }
       })
       .catch(function (data) {
         Alert.error($t('versions.alerts.osListError'), data.message);
@@ -192,7 +228,13 @@ angular.module('ambariAdminConsole')
   /**
    * On click handler for adding new OS
    */
-  $scope.addOS = function() {
+  $scope.addOS = function($event) {
+    var dropdownEl = $event.target.parentElement.parentElement;
+    // close the dopdown when an OS is added.
+    $timeout(function () {
+      dropdownEl.click();
+    });
+
     this.os.selected = true;
     if (this.os.repositories) {
       this.os.repositories.forEach(function(repo) {
@@ -208,6 +250,23 @@ angular.module('ambariAdminConsole')
     AddRepositoryModal.show($scope.osList, $scope.upgradeStack.stack_name, 
$scope.upgradeStack.stack_version, $scope.id);
   };
 
+  $scope.validBaseUrlsExist = function () {
+    var validBaseUrlsExist = true;
+    if ($scope.osList) {
+      $scope.osList.forEach(function(os) {
+        if (os.repositories && os.selected) {
+          os.repositories.forEach(function(repo) {
+            if (repo.invalidBaseUrl) {
+              validBaseUrlsExist = false;
+            }
+          })
+        }
+      });
+    }
+    return validBaseUrlsExist;
+  };
+
+
   $scope.isSaveButtonDisabled = function() {
     var enabled = false;
     $scope.osList.forEach(function(os) {
@@ -215,7 +274,8 @@ angular.module('ambariAdminConsole')
         enabled = true
       }
     });
-    return !enabled;
+    var isRedhatSatelliteSelected = $scope.useRedhatSatellite;
+    return !(isRedhatSatelliteSelected || (enabled && 
$scope.validBaseUrlsExist()));
   };
 
   $scope.defaulfOSRepos = {};
@@ -239,42 +299,47 @@ angular.module('ambariAdminConsole')
         updateRepoUrl = true;
       }
     });
-    if ($scope.isPublicVersion) {
-      var skip = $scope.skipValidation || $scope.useRedhatSatellite;
-      return Stack.validateBaseUrls(skip, $scope.osList, 
$scope.upgradeStack).then(function (invalidUrls) {
-        if (invalidUrls.length === 0) {
+
+    var skip = $scope.skipValidation || $scope.useRedhatSatellite;
+    return Stack.validateBaseUrls(skip, $scope.osList, 
$scope.upgradeStack).then(function (invalidUrls) {
+      if (invalidUrls.length === 0) {
+        if ($scope.isPublicVersion) {
           var data = {
             "VersionDefinition": {
               "available": $scope.id
             }
           };
-          var repoUpdate = {
-            operating_systems: $scope.updateObj.operating_systems
-          };
-          Stack.postVersionDefinitionFile(false, data).then(function 
(versionInfo) {
-            if (versionInfo.id && versionInfo.stackName && 
versionInfo.stackVersion) {
-              Stack.updateRepo(versionInfo.stackName, 
versionInfo.stackVersion, versionInfo.id, repoUpdate).then(function () {
-                Alert.success($t('versions.alerts.versionEdited', {
-                  stackName: $scope.upgradeStack.stack_name,
-                  versionName: $scope.actualVersion,
-                  displayName: $scope.displayName
-                }));
-                $location.path('/stackVersions');
-              }).catch(function (data) {
-                Alert.error($t('versions.alerts.versionUpdateError'), 
data.message);
-              });
-            }
-          })
-          .catch(function (data) {
-            Alert.error($t('versions.alerts.readVersionInfoError'), 
data.message);
-          });
+          var isXMLdata = false;
         } else {
-          Stack.highlightInvalidUrls(invalidUrls);
+          var data = $scope.data;
+          var isXMLdata = $scope.isXMLdata;
         }
-      });
-    } else {
-      $scope.updateRepoVersions();
-    }
+
+        var repoUpdate = {
+          operating_systems: $scope.updateObj.operating_systems
+        };
+        Stack.postVersionDefinitionFile(isXMLdata, data, false).then(function 
(response) {
+          var versionInfo = response.resources[0].VersionDefinition;
+          if (versionInfo.id && versionInfo.stack_name && 
versionInfo.stack_version) {
+            Stack.updateRepo(versionInfo.stack_name, 
versionInfo.stack_version, versionInfo.id, repoUpdate).then(function () {
+              Alert.success($t('versions.alerts.versionEdited', {
+                stackName: $scope.upgradeStack.stack_name,
+                versionName: $scope.actualVersion,
+                displayName: $scope.displayName
+              }));
+              $location.path('/stackVersions');
+            }).catch(function (data) {
+              Alert.error($t('versions.alerts.versionUpdateError'), 
data.message);
+            });
+          }
+        })
+        .catch(function (data) {
+          Alert.error($t('versions.alerts.readVersionInfoError'), 
data.message);
+        });
+      } else {
+        Stack.highlightInvalidUrls(invalidUrls);
+      }
+    });
   };
 
   $scope.updateRepoVersions = function () {
@@ -324,8 +389,47 @@ angular.module('ambariAdminConsole')
     }
   };
 
-  $scope.clearError = function() {
-    this.repository.hasError = false;
+  $scope.showPublicRepoDisabledDialog = function() {
+    ConfirmationModal.show(
+      $t('versions.networkIssues.publicDisabledHeader'),
+      {
+        "url": 'views/modals/publicRepoDisabled.html'
+      },
+      $t('common.controls.ok'),
+      $t('common.controls.cancel'),
+      true
+    )
+  };
+
+  $scope.onRepoUrlChange = function (repository) {
+    $scope.clearError(repository);
+    $scope.setInvalidUrlError(repository);
+  };
+
+  $scope.undoChange = function(repo) {
+    if ($scope.selectedOption.index == 1) {
+      repo.Repositories.base_url = repo.Repositories.initial_base_url;
+    } else {
+      repo.Repositories.base_url = '';
+    }
+  };
+
+  $scope.clearError = function(repository) {
+    repository.hasError = false;
+  };
+
+  $scope.setInvalidUrlError = function (repository) {
+    repository.invalidBaseUrl =  
!$scope.isValidRepoBaseUrl(repository.Repositories.base_url);
+  };
+  /**
+   * Validate base URL
+   * @param {string} value
+   * @returns {boolean}
+   */
+  $scope.isValidRepoBaseUrl = function (value) {
+    var remotePattern = 
/^(?:(?:https?|ftp):\/{2})(?:\S+(?::\S*)?@)?(?:(?:(?:[\w\-.]))*)(?::[0-9]+)?(?:\/\S*)?$/,
+      localPattern = 
/^file:\/{2,3}([a-zA-Z][:|]\/){0,1}[\w~!*'();@&=\/\\\-+$,?%#.\[\]]+$/;
+    return remotePattern.test(value) || localPattern.test(value);
   };
 
   $scope.hasValidationErrors = function() {
@@ -372,10 +476,8 @@ angular.module('ambariAdminConsole')
       };
     });
     $scope.repoVersionFullName = response.repoVersionFullName;
-    angular.forEach(response.osList, function (os) {
-      os.selected = true;
-    });
     $scope.osList = response.osList;
+
     // load supported os type base on stack version
     $scope.afterStackVersionRead();
   };
@@ -385,11 +487,106 @@ angular.module('ambariAdminConsole')
     $scope.setVersionSelected(this.version);
   };
 
+  $scope.onStackIdChange = function () {
+    $scope.setStackIdActive(this.stack);
+    $scope.setVisibleStackVersions($scope.allVersions);
+    $scope.setVersionSelected($scope.activeStackVersion);
+  };
+
+  $scope.setStackIdActive =  function (stack) {
+    angular.forEach($scope.stackIds, function(_stack){
+      _stack.isSelected = false;
+    });
+    stack.isSelected = true;
+  };
+
+  $scope.setStackIds = function(stacks) {
+    var stackIds = [];
+    // sort stacks as per per {stack_name}-{stack_version}
+    stacks.sort(function(a,b){
+      if (a.stackName === b.stackName) {
+        var aStackVersion = parseFloat(a.stackVersion);
+        var bStackVersion = parseFloat(b.stackVersion);
+        if (aStackVersion === bStackVersion) {
+          // sort numerically as per per {repository_version}
+          return Utility.compareVersions(a.repositoryVersion, 
b.repositoryVersion);
+        } else {
+          //sort numerically as per per {stack_version}
+          return aStackVersion > bStackVersion;
+        }
+      } else {
+        //sort lexicographically as per per {stack_name}
+        return  (a.stackName > b.stackName);
+      }
+    }).reverse();
+    angular.forEach(stacks, function (stack) {
+      stackIds.push(stack.stackNameVersion);
+    });
+    $scope.stackIds = stackIds.filter(function(item, index, self){
+      return self.indexOf(item) === index;
+    }).map(function(item){
+      return {
+        stackNameVersion: item,
+        isSelected: false
+      };
+    });
+    $scope.stackIds[0].isSelected = true;
+  };
+
+  $scope.setActiveVersion = function () {
+    $scope.activeStackVersion = this.version;
+    $scope.setVersionSelected($scope.activeStackVersion);
+  };
+
+  $scope.setVisibleStackVersions = function (versions) {
+    var activeStackId = $scope.stackIds.find(function(item){
+      return item.isSelected === true;
+    });
+    angular.forEach(versions, function (item, index) {
+      var isPublicVersionsExist = false;
+      // If public VDF exists for a stack then default base stack version 
should be hidden
+      if (item.stackDefault) {
+        isPublicVersionsExist = versions.find(function(_version){
+          return (item.stackNameVersion === _version.stackNameVersion && 
!_version.stackDefault);
+        });
+      }
+      item.visible = (item.stackNameVersion === 
activeStackId.stackNameVersion) && !isPublicVersionsExist;
+    });
+    $scope.activeStackVersion = versions.filter(function(item){
+      return item.visible;
+    })[0];
+  };
+
+  $scope.setNetworkIssues = function (versions) {
+   $scope.networkLost = !versions.find(function(_version){
+     return !_version.stackDefault;
+   });
+    if ($scope.networkLost) {
+      $scope.selectedOption.index = 2;
+      $scope.clearRepoVersions();
+    }
+  };
+
+  $scope.validateRepoUrl = function () {
+    angular.forEach($scope.osList,function(os){
+      if (os.repositories) {
+        os.repositories.forEach(function(repo) {
+          $scope.onRepoUrlChange(repo);
+        });
+      }
+    });
+  };
+
   $scope.fetchPublicVersions = function () {
     return Stack.allPublicStackVersions().then(function (versions) {
       if (versions && versions.length) {
-        $scope.selectedPublicRepoVersion = versions[0];
-        $scope.setVersionSelected(versions[0]);
+        $scope.setStackIds(versions);
+        $scope.setVisibleStackVersions(versions);
+        $scope.allVersions = versions;
+        $scope.selectedPublicRepoVersion = $scope.activeStackVersion;
+        $scope.setVersionSelected($scope.activeStackVersion);
+        $scope.setNetworkIssues(versions);
+        $scope.validateRepoUrl();
         $scope.availableStackRepoList = versions.length == 1 ? [] : versions;
       }
     });

http://git-wip-us.apache.org/repos/asf/ambari/blob/9f93ea6b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/stackVersions/StackVersionsEditCtrl.js
----------------------------------------------------------------------
diff --git 
a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/stackVersions/StackVersionsEditCtrl.js
 
b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/stackVersions/StackVersionsEditCtrl.js
index 22ec5ae..ae9850b 100644
--- 
a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/stackVersions/StackVersionsEditCtrl.js
+++ 
b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/stackVersions/StackVersionsEditCtrl.js
@@ -36,6 +36,7 @@ angular.module('ambariAdminConsole')
 
   $scope.loadStackVersionInfo = function () {
     return Stack.getRepo($routeParams.versionId, 
$routeParams.stackName).then(function (response) {
+      $scope.activeStackVersion = response;
       $scope.id = response.id;
       $scope.isPatch = response.type == 'PATCH';
       $scope.stackNameVersion = response.stackNameVersion || $t('common.NA');
@@ -98,6 +99,9 @@ angular.module('ambariAdminConsole')
         operatingSystems.map(function (os) {
           var existingOSHash = {};
           angular.forEach($scope.osList, function (os) {
+            os.repositories.forEach(function(repo) {
+              repo.Repositories.initial_base_url = repo.Repositories.base_url;
+            });
             existingOSHash[os.OperatingSystems.os_type] = os;
           });
           // if os not in the list, mark as un-selected, add this to the osList
@@ -215,7 +219,6 @@ angular.module('ambariAdminConsole')
           });
       });
   };
-  $scope.loadStackVersionInfo();
 
   /**
    * On click handler for removing OS
@@ -281,13 +284,17 @@ angular.module('ambariAdminConsole')
       }
     });
     return !enabled;
-  }
+  };
 
   $scope.cancel = function () {
     $scope.editVersionDisabled = true;
     $location.path('/stackVersions');
   };
 
+  $scope.undoChange = function(repo) {
+    repo.Repositories.base_url = repo.Repositories.initial_base_url;
+  };
+
   $scope.clearErrors = function() {
     if ($scope.osList) {
       $scope.osList.forEach(function(os) {
@@ -388,4 +395,6 @@ angular.module('ambariAdminConsole')
       $scope.stackVersions = stackVersions;
     });
   };
+
+  $scope.loadStackVersionInfo();
 }]);

http://git-wip-us.apache.org/repos/asf/ambari/blob/9f93ea6b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/stackVersions/StackVersionsListCtrl.js
----------------------------------------------------------------------
diff --git 
a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/stackVersions/StackVersionsListCtrl.js
 
b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/stackVersions/StackVersionsListCtrl.js
index 3a8233a..b759800 100644
--- 
a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/stackVersions/StackVersionsListCtrl.js
+++ 
b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/stackVersions/StackVersionsListCtrl.js
@@ -18,66 +18,155 @@
 'use strict';
 
 angular.module('ambariAdminConsole')
-.controller('StackVersionsListCtrl', ['$scope', 'Cluster', 'Stack', 
'$routeParams', '$translate', function ($scope, Cluster, Stack, $routeParams, 
$translate) {
-  var $t = $translate.instant;
-  $scope.getConstant = function (key) {
-    return $t('common.' + key).toLowerCase();
-  }
-  $scope.clusterName = $routeParams.clusterName;
-
-  $scope.filter = {
-    version: '',
-    cluster: {
-      options: [],
-      current: null
-    }
-  };
-
-  $scope.pagination = {
-    totalRepos: 100,
-    maxVisiblePages: 1,
-    itemsPerPage: 100,
-    currentPage: 1
-  };
-  $scope.allRepos = [];
-  $scope.stackVersions = [];
-
-  /**
-   *  Formatted object to display all repos:
-   *
-   *  [{ 'name': 'HDP-2.3',
-   *     'repos': ['2.3.6.0-2343', '2.3.4.1', '2.3.4.0-56']
-   *   },
-   *   { 'name': 'HDP-2.2',
-   *     'repos': ['2.2.6.0', '2.2.4.5', '2.2.4.0']
-   *   }
-   *  ]
-   *
-   */
-  $scope.fetchRepos = function () {
-    return Stack.allRepos($scope.filter, $scope.pagination).then(function 
(repos) {
-      $scope.allRepos = repos.items.sort(function(a, b){return 
a.repository_version < b.repository_version});
-      var existingStackHash = {};
-      var stackVersions = [];
-      angular.forEach($scope.allRepos, function (repo) {
-        var stackVersionName = repo.stack_name + '-' + repo.stack_version;
-        if (!existingStackHash[stackVersionName]) {
-          existingStackHash[stackVersionName] = true;
-          stackVersions.push({
-            'name': stackVersionName,
-            'isOpened': true,
-            'repos': [repo]
+  .controller('StackVersionsListCtrl', ['$scope', 'Cluster', 'Stack', 
'$routeParams', '$translate', function ($scope, Cluster, Stack, $routeParams, 
$translate) {
+    var $t = $translate.instant;
+    $scope.getConstant = function (key) {
+      return $t('common.' + key).toLowerCase();
+    };
+    $scope.clusterName = $routeParams.clusterName;
+    $scope.filter = {
+      name: '',
+      version: '',
+      cluster: {
+        options: [],
+        current: null
+      },
+      stack: {
+        options: [],
+        current: null
+      }
+    };
+    $scope.isNotEmptyFilter = true;
+
+    $scope.pagination = {
+      totalRepos: 10,
+      maxVisiblePages: 20,
+      itemsPerPage: 10,
+      currentPage: 1
+    };
+
+    $scope.tableInfo = {
+      total: 0,
+      showed: 0,
+      filtered: 0
+    };
+
+    $scope.repos = [];
+    $scope.dropDownClusters = [];
+    $scope.selectedCluster = $scope.dropDownClusters[0];
+
+    $scope.resetPagination = function () {
+      $scope.pagination.currentPage = 1;
+      $scope.loadAllData();
+    };
+
+    $scope.pageChanged = function () {
+      $scope.loadAllData();
+    };
+
+    $scope.goToCluster = function() {
+      window.location.replace('/#/main/admin/stack/versions');
+    };
+
+    $scope.clearFilters = function () {
+      $scope.filter.name = '';
+      $scope.filter.version = '';
+      $scope.filter.cluster.current = $scope.filter.cluster.options[0];
+      $scope.filter.stack.current = $scope.filter.stack.options[0];
+      $scope.resetPagination();
+    };
+
+    $scope.fetchRepoClusterStatus = function () {
+      var clusterName = ($scope.clusters && $scope.clusters.length > 0) ? 
$scope.clusters[0].Clusters.cluster_name : null; // only support one cluster at 
the moment
+      if (clusterName) {
+        angular.forEach($scope.repos, function (repo) {
+          Cluster.getRepoVersionStatus(clusterName, repo.id).then(function 
(response) {
+            repo.status = response.status;
+            repo.totalHosts = response.totalHosts;
+            repo.currentHosts = response.currentHosts;
+            repo.installedHosts = response.installedHosts;
+            repo.stackVersionId = response.stackVersionId;
+            repo.cluster = (repo.status == 'current' || repo.status == 
'installed') ? clusterName : '';
           });
-        } else {
-          if (stackVersions[stackVersions.length -1].repos) {
-            stackVersions[stackVersions.length -1].repos.push(repo);
-          }
+        });
+      }
+    };
+
+    $scope.fetchRepos = function () {
+      return Stack.allRepos($scope.filter, $scope.pagination).then(function 
(repos) {
+        $scope.pagination.totalRepos = repos.itemTotal;
+        $scope.repos = repos.items;
+        $scope.tableInfo.total = repos.itemTotal;
+        $scope.tableInfo.showed = repos.showed;
+      });
+    };
+
+    $scope.fillClusters = function (clusters) {
+      $scope.dropDownClusters = [].concat(clusters);
+      var options = [{label: $t('common.all'), value: ''}];
+      angular.forEach(clusters, function (cluster) {
+        options.push({
+          label: cluster.Clusters.cluster_name,
+          value: cluster.Clusters.cluster_name
+        });
+      });
+      $scope.filter.cluster.options = options;
+      if (!$scope.filter.cluster.current) {
+        $scope.filter.cluster.current = options[0];
+      }
+    };
+
+    $scope.fetchClusters = function () {
+      return Cluster.getAllClusters().then(function (clusters) {
+        if (clusters && clusters.length > 0) {
+          $scope.clusters = clusters;
+          $scope.fillClusters(clusters);
         }
       });
-      $scope.stackVersions = stackVersions;
-    });
-  };
+    };
+
+    $scope.fetchStacks = function () {
+      return Stack.allStackVersions().then(function (clusters) {
+        if (clusters && clusters.length > 0) {
+          $scope.stacks = clusters;
+          $scope.fillStacks(clusters);
+        }
+      });
+    };
+
+    $scope.fillStacks = function() {
+      var options = [{label: $t('common.all'), value: ''}];
+      angular.forEach($scope.stacks, function (stack) {
+        options.push({
+          label: stack.displayName,
+          value: stack.displayName
+        });
+      });
+      $scope.filter.stack.options = options;
+      if (!$scope.filter.stack.current) {
+        $scope.filter.stack.current = options[0];
+      }
+    };
+
+    $scope.loadAllData = function () {
+      $scope.fetchStacks()
+        .then(function () {
+          return $scope.fetchClusters();
+        })
+        .then(function () {
+          return $scope.fetchRepos();
+        })
+        .then(function () {
+          $scope.fetchRepoClusterStatus();
+        });
+    };
 
-  $scope.fetchRepos();
+    $scope.loadAllData();
 
-}]);
+    $scope.$watch('filter', function (filter) {
+      $scope.isNotEmptyFilter = Boolean(filter.name
+        || filter.version
+        || (filter.cluster.current && filter.cluster.current.value)
+        || (filter.stack.current && filter.stack.current.value));
+    }, true);
+  }]);

http://git-wip-us.apache.org/repos/asf/ambari/blob/9f93ea6b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/i18n.config.js
----------------------------------------------------------------------
diff --git 
a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/i18n.config.js 
b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/i18n.config.js
index 2d51ece..7474d5f 100644
--- a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/i18n.config.js
+++ b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/i18n.config.js
@@ -38,6 +38,7 @@ angular.module('ambariAdminConsole')
       'users': 'Users',
       'groups': 'Groups',
       'versions': 'Versions',
+      'stack': 'Stack',
       'details': 'Details',
       'goToDashboard': 'Go to Dashboard',
       'noClusters': 'No Clusters',
@@ -89,6 +90,7 @@ angular.module('ambariAdminConsole')
       'listViewLabel': 'LIST',
       'rbac': 'Role Based Access Control',
       'important': 'Important',
+      'undo': 'Undo',
 
       'clusterNameChangeConfirmation': {
         'title': 'Confirm Cluster Name Change',
@@ -372,9 +374,19 @@ angular.module('ambariAdminConsole')
 
     'versions': {
       'current': 'Current',
+      'addVersion': 'Add Version',
+      'defaultVersion': '(Default Version Definition)',
       'inUse': 'In Use',
       'installed': 'Installed',
       'usePublic': "Use Public Repository",
+      'networkIssues': {
+        'networkLost': "Why is this disabled?",
+        'publicDisabledHeader': "Public Repository Option Disabled",
+        'publicRepoDisabledMsg': 'Ambari does not have access to the Internet 
and cannot use the Public Repository for installing the software. Your 
Options:',
+        'publicRepoDisabledMsg1': 'Configure your hosts for access to the 
Internet.',
+        'publicRepoDisabledMsg2': 'If you are using an Internet Proxy, refer 
to the Ambari Documentation on how to configure Ambari to use the Internet 
Proxy.',
+        'publicRepoDisabledMsg3': 'Use the Local Repositoy option.'
+      },
       'selectVersion': "Select Version",
       'selectVersionEmpty': "No other repositories",
       'useLocal': "Use Local Repository",
@@ -406,11 +418,15 @@ angular.module('ambariAdminConsole')
         'actualVersion': 'Actual Version',
         'releaseNotes': 'Release Notes'
       },
+      'repository': {
+        'placeholder': 'Enter Base URL or remove this OS'
+      },
       'useRedhatSatellite': {
         'title': 'Use RedHat Satellite/Spacewalk',
         'warning': 'By selecting to <b>"Use RedHat Satellite/Spacewalk"</b> 
for the software repositories, ' +
         'you are responsible for configuring the repository channel in 
Satellite/Spacewalk and confirming the repositories for the selected <b>stack 
version</b> are available on the hosts in the cluster. ' +
-        'Refer to the Ambari documentation for more information.'
+        'Refer to the Ambari documentation for more information.',
+        'disabledMsg': 'Use of RedHat Satellite/Spacewalk is not available 
when using Public Repositories'
       },
       'changeBaseURLConfirmation': {
         'title': 'Confirm Base URL Change',
@@ -425,6 +441,7 @@ angular.module('ambariAdminConsole')
         'filterListError': 'Fetch stack version filter list error',
         'versionCreated': 'Created version <a 
href="#/stackVersions/{{stackName}}/{{versionName}}/edit">{{stackName}}-{{versionName}}</a>',
         'versionCreationError': 'Version creation error',
+        'allOsAdded': 'All Operating Systems have been added',
         'osListError': 'getSupportedOSList error',
         'readVersionInfoError': 'Version Definition read error',
         'versionEdited': 'Edited version <a 
href="#/stackVersions/{{stackName}}/{{versionName}}/edit">{{displayName}}</a>',

http://git-wip-us.apache.org/repos/asf/ambari/blob/9f93ea6b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/AddVersionModal.js
----------------------------------------------------------------------
diff --git 
a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/AddVersionModal.js
 
b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/AddVersionModal.js
new file mode 100644
index 0000000..0af7004
--- /dev/null
+++ 
b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/AddVersionModal.js
@@ -0,0 +1,172 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+'use strict';
+
+angular.module('ambariAdminConsole')
+  .factory('AddVersionModal', ['$modal', '$q', function($modal, $q) {
+    var modalObject = {};
+
+    modalObject.repoExists = function(existingRepos, repoId) {
+      for(var i = existingRepos.length - 1; i >= 0; --i) {
+        if (existingRepos[i].Repositories.repo_id === repoId) {
+          return true;
+        }
+      }
+      return false;
+    };
+
+    modalObject.getRepositoriesForOS = function (osList, selectedOS) {
+      // Get existing list of repositories for selectedOS
+      for (var i = osList.length - 1; i >= 0; --i) {
+        if (osList[i].OperatingSystems.os_type === selectedOS) {
+          osList[i].repositories = osList[i].repositories || [];
+          return osList[i].repositories;
+        }
+      }
+      return null;
+    };
+
+    modalObject.show = function (parentScope) {
+      var deferred = $q.defer();
+      var modalInstance = $modal.open({
+        templateUrl: 'views/modals/AddVersionModal.html',
+        controller: ['$scope', '$modalInstance', '$translate', 'Stack', 
'Alert', function ($scope, $modalInstance, $translate, Stack, Alert) {
+          var $t = $translate.instant;
+          $scope.selectedLocalOption = {
+            index: 1
+          };
+          $scope.option1 = {
+            index: 1,
+            displayName: $t('versions.uploadFile'),
+            file: ''
+          };
+          $scope.option2 = {
+            index: 2,
+            displayName: $t('versions.enterURL'),
+            url: "",
+            placeholder: "Enter URL to Version Definition File",
+          };
+          $scope.readInfoButtonDisabled = function () {
+            return $scope.option1.index == $scope.selectedLocalOption.index ? 
!$scope.option1.file : !$scope.option2.url;
+          };
+          $scope.onFileSelect = function(e){
+            if (e.files && e.files.length == 1) {
+              var file = e.files[0];
+              var reader = new FileReader();
+              reader.onload = (function () {
+                return function (e) {
+                  $scope.option1.file = e.target.result;
+                };
+              })(file);
+              reader.readAsText(file);
+            } else {
+              $scope.option1.file = '';
+            }
+          };
+          /**
+           * Load selected file to current page content
+           */
+          $scope.readVersionInfo = function(){
+            var data = {};
+            var isXMLdata = false;
+            if ($scope.option2.index == $scope.selectedLocalOption.index) {
+              var url = $scope.option2.url;
+              data = {
+                "VersionDefinition": {
+                  "version_url": url
+                }
+              };
+            } else if ($scope.option1.index == 
$scope.selectedLocalOption.index) {
+              isXMLdata = true;
+              // load from file browser
+              data = $scope.option1.file;
+            }
+            parentScope.isXMLdata = isXMLdata;
+            parentScope.data = data;
+
+            return Stack.postVersionDefinitionFile(isXMLdata, data, 
true).then(function (versionInfo) {
+              var repo = versionInfo.resources[0];
+              var response = {
+                id : repo.VersionDefinition.id,
+                stackVersion : repo.VersionDefinition.stack_version,
+                stackName: repo.VersionDefinition.stack_name,
+                type: repo.VersionDefinition.release? 
repo.VersionDefinition.release.type: null,
+                stackNameVersion: repo.VersionDefinition.stack_name + '-' + 
repo.VersionDefinition.stack_version, /// HDP-2.3
+                stackNameRepositoryVersion: repo.VersionDefinition.stack_name 
+ '-' + repo.VersionDefinition.repository_version,
+                actualVersion: repo.VersionDefinition.repository_version, /// 
2.3.4.0-3846
+                version: repo.VersionDefinition.release ? 
repo.VersionDefinition.release.version: null, /// 2.3.4.0
+                releaseNotes: repo.VersionDefinition.release ? 
repo.VersionDefinition.release.release_notes: null,
+                displayName: repo.VersionDefinition.stack_name + '-' + 
repo.VersionDefinition.repository_version, //HDP-2.3.4.0
+                repoVersionFullName : repo.VersionDefinition.stack_name + '-' 
+ repo.VersionDefinition.release ? repo.VersionDefinition.release.version: 
repo.VersionDefinition.repository_version,
+                ambari_managed_repositories: 
repo.operating_systems[0].OperatingSystems.ambari_managed_repositories !== 
false,
+                osList: repo.operating_systems,
+                updateObj: repo
+              };
+              var services = [];
+              angular.forEach(repo.VersionDefinition.stack_services, function 
(service) {
+                var servicesToExclude = ['GANGLIA', 'KERBEROS', 'MAPREDUCE2'];
+                if (servicesToExclude.indexOf(service.name) === -1) {
+                  services.push({
+                    name: service.name,
+                    version: service.versions[0],
+                    displayName: service.display_name
+                  });
+                }
+              });
+              response.services = services.sort(function(a, b){return 
a.name.localeCompare(b.name)});
+              response.osList.forEach(function (os) {
+                os.repositories.forEach(function(repo) {
+                  repo.Repositories.initial_base_url = 
repo.Repositories.base_url;
+                });
+              });
+
+              angular.forEach(parentScope.stackIds, function(stack){
+                if (stack.stackNameVersion == response.stackNameVersion) {
+                  parentScope.setStackIdActive(stack);
+                }
+              });
+              angular.forEach(parentScope.allVersions, function(version) {
+                version.visible = (version.stackNameVersion === 
response.stackNameVersion);
+              });
+              parentScope.allVersions.push(response);
+              parentScope.activeStackVersion = response;
+              parentScope.selectedPublicRepoVersion = response;
+              parentScope.setVersionSelected(response);
+              $modalInstance.close();
+              deferred.resolve();
+            }).catch(function (data) {
+              Alert.error($t('versions.alerts.readVersionInfoError'), 
data.message);
+            });
+          };
+          $scope.cancel = function () {
+            $modalInstance.dismiss();
+            deferred.reject();
+          };
+        }]
+      });
+      modalInstance.result.then(function () {
+        // Gets triggered on close
+      }, function () {
+        // Gets triggered on dismiss
+        deferred.reject();
+      });
+      return deferred.promise;
+    };
+
+    return modalObject;
+  }]);
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/9f93ea6b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/ConfirmationModal.js
----------------------------------------------------------------------
diff --git 
a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/ConfirmationModal.js
 
b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/ConfirmationModal.js
index 4576a40..2e65e0f 100644
--- 
a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/ConfirmationModal.js
+++ 
b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/ConfirmationModal.js
@@ -23,7 +23,7 @@ angular.module('ambariAdminConsole')
   var $t = $translate.instant;
 
        return {
-               show: function(header, body, confirmText, cancelText) {
+               show: function(header, body, confirmText, cancelText, 
hideCancelButton) {
                        var deferred = $q.defer();
 
                        var modalInstance = $modal.open({
@@ -35,6 +35,7 @@ angular.module('ambariAdminConsole')
           $scope.innerScope = body.scope;
           $scope.confirmText = confirmText || $t('common.controls.ok');
           $scope.cancelText = cancelText || $t('common.controls.cancel');
+                                       $scope.showCancelButton = 
!hideCancelButton;
 
                                        $scope.ok = function() {
                                                $modalInstance.close();

http://git-wip-us.apache.org/repos/asf/ambari/blob/9f93ea6b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/Stack.js
----------------------------------------------------------------------
diff --git 
a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/Stack.js 
b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/Stack.js
index 96f9c9f..7e76859 100644
--- a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/Stack.js
+++ b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/Stack.js
@@ -81,8 +81,8 @@ angular.module('ambariAdminConsole')
     },
 
     allPublicStackVersions: function() {
-      var url = 
'/version_definitions?fields=operating_systems/repositories/Repositories/*,VersionDefinition/stack_services,VersionDefinition/repository_version'
 +
-        
'&VersionDefinition/show_available=true&VersionDefinition/stack_name=HDP';
+      var url = 
'/version_definitions?fields=VersionDefinition/stack_default,operating_systems/repositories/Repositories/*,VersionDefinition/stack_services,VersionDefinition/repository_version'
 +
+        '&VersionDefinition/show_available=true';
       var deferred = $q.defer();
       $http.get(Settings.baseUrl + url, {mock: 'version/versions.json'})
         .success(function (data) {
@@ -92,22 +92,34 @@ angular.module('ambariAdminConsole')
               id: version.VersionDefinition.id,
               stackName: version.VersionDefinition.stack_name,
               stackVersion: version.VersionDefinition.stack_version,
+              stackDefault: version.VersionDefinition.stack_default,
               stackNameVersion:  version.VersionDefinition.stack_name + '-' + 
version.VersionDefinition.stack_version,
               displayName: version.VersionDefinition.stack_name + '-' + 
version.VersionDefinition.repository_version.split('-')[0], //HDP-2.3.4.0
               displayNameFull: version.VersionDefinition.stack_name + '-' + 
version.VersionDefinition.repository_version, //HDP-2.3.4.0-23
               repositoryVersion: version.VersionDefinition.repository_version,
+              stackNameRepositoryVersion: version.VersionDefinition.stack_name 
+ '-' + version.VersionDefinition.repository_version,
               showAvailable: version.VersionDefinition.show_available,
               osList: version.operating_systems,
               updateObj: version
             };
             var services = [];
             angular.forEach(version.VersionDefinition.stack_services, function 
(service) {
-              services.push({
-                name: service.name,
-                version: service.versions[0]
+              // services that should not be shown on UI
+              var servicesToExclude = ['GANGLIA', 'KERBEROS', 'MAPREDUCE2'];
+              if (servicesToExclude.indexOf(service.name) === -1) {
+                services.push({
+                  name: service.name,
+                  displayName: service.display_name,
+                  version: service.versions[0]
+                });
+              }
+            });
+            versionObj.services = services.sort(function(a, b){return 
a.name.localeCompare(b.name)});
+            versionObj.osList.forEach(function (os) {
+              os.repositories.forEach(function(repo) {
+                repo.Repositories.initial_base_url = 
repo.Repositories.base_url;
               });
             });
-            versionObj.services = services;
             versions.push(versionObj);
           });
           deferred.resolve(versions)
@@ -120,9 +132,21 @@ angular.module('ambariAdminConsole')
 
     allRepos: function (filter, pagination) {
       var versionFilter = filter.version;
+      var nameFilter = filter.name;
+      var stackFilter = filter.stack && filter.stack.current && 
filter.stack.current.value;
       var url = 
'/stacks?fields=versions/repository_versions/RepositoryVersions';
       if (versionFilter) {
-        url += 
'&versions/repository_versions/RepositoryVersions/display_name.matches(.*' + 
versionFilter + '.*)';
+        url += 
'&versions/repository_versions/RepositoryVersions/repository_version.matches(.*'
 + versionFilter + '.*)';
+      }
+      if (nameFilter) {
+        url += 
'&versions/repository_versions/RepositoryVersions/display_name.matches(.*' + 
nameFilter + '.*)';
+      }
+      if (stackFilter) {
+        var stack = filter.stack.current.value.split('-'),
+          stackNameFilter = stack[0],
+          stackVersionFilter = stack[1];
+        url += '&versions/repository_versions/RepositoryVersions/stack_name=' 
+ stackNameFilter;
+        url += 
'&versions/repository_versions/RepositoryVersions/stack_version=' + 
stackVersionFilter;
       }
       var deferred = $q.defer();
       $http.get(Settings.baseUrl + url, {mock: 'version/versions.json'})
@@ -213,8 +237,7 @@ angular.module('ambariAdminConsole')
           actualVersion: 
data.repository_versions[0].RepositoryVersions.repository_version, /// 
2.3.4.0-3846
           version: data.repository_versions[0].RepositoryVersions.release ? 
data.repository_versions[0].RepositoryVersions.release.version: null, /// 
2.3.4.0
           releaseNotes: data.repository_versions[0].RepositoryVersions.release 
? data.repository_versions[0].RepositoryVersions.release.release_notes: null,
-          displayName: data.repository_versions[0].RepositoryVersions.release 
? data.Versions.stack_name + '-' + 
data.repository_versions[0].RepositoryVersions.release.version :
-            data.Versions.stack_name + '-' + 
data.repository_versions[0].RepositoryVersions.repository_version.split('-')[0],
 //HDP-2.3.4.0
+          displayName: 
data.repository_versions[0].RepositoryVersions.display_name, //HDP-2.3.4.0
           repoVersionFullName : data.Versions.stack_name + '-' + 
data.repository_versions[0].RepositoryVersions.repository_version,
           ambari_managed_repositories: 
data.repository_versions[0].operating_systems[0].OperatingSystems.ambari_managed_repositories
 !== false,
           osList: data.repository_versions[0].operating_systems,
@@ -222,11 +245,14 @@ angular.module('ambariAdminConsole')
         };
         var services = [];
         
angular.forEach(data.repository_versions[0].RepositoryVersions.stack_services, 
function (service) {
-          services.push({
-            name: service.name,
-            version: service.versions[0],
-            displayName: service.display_name
-          });
+          var servicesToExclude = ['GANGLIA', 'KERBEROS', 'MAPREDUCE2'];
+          if (servicesToExclude.indexOf(service.name) === -1) {
+            services.push({
+              name: service.name,
+              version: service.versions[0],
+              displayName: service.display_name
+            });
+          }
         });
         response.services = services.sort(function(a, b){return 
a.name.localeCompare(b.name)});
         deferred.resolve(response);
@@ -237,20 +263,15 @@ angular.module('ambariAdminConsole')
       return deferred.promise;
     },
 
-    postVersionDefinitionFile: function (isXMLdata, data) {
+    postVersionDefinitionFile: function (isXMLdata, data, isDryRun) {
       var deferred = $q.defer(),
-        url = Settings.baseUrl + '/version_definitions',
+        url = Settings.baseUrl + '/version_definitions' + (isDryRun ? 
'?dry_run=true' : ''),
         configs = isXMLdata? { headers: {'Content-Type': 'text/xml'}} : null;
 
       $http.post(url, data, configs)
         .success(function (response) {
           if (response.resources.length && 
response.resources[0].VersionDefinition) {
-            deferred.resolve(
-              {
-                stackName: response.resources[0].VersionDefinition.stack_name,
-                id: response.resources[0].VersionDefinition.id,
-                stackVersion: 
response.resources[0].VersionDefinition.stack_version
-              });
+            deferred.resolve(response);
           }
         })
         .error(function (data) {

http://git-wip-us.apache.org/repos/asf/ambari/blob/9f93ea6b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/Utility.js
----------------------------------------------------------------------
diff --git 
a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/Utility.js 
b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/Utility.js
new file mode 100644
index 0000000..9439ba9
--- /dev/null
+++ 
b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/Utility.js
@@ -0,0 +1,70 @@
+/**
+ * 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.
+ */
+'use strict';
+/**
+ * This service should be used to keep all utility functions in one place that 
can be used in any controller
+ */
+angular.module('ambariAdminConsole')
+  .factory('Utility', [function() {
+    return {
+      /**
+       *  if version1>= version2 then return true
+       *     version1 < version2 then return false
+       * @param version1 {String}
+       * @param version2 {String}
+       * @return boolean
+       */
+      compareVersions: function(version1, version2) {
+        version1 = version1 || '0';
+        version2 = version2 || '0';
+        var version1Arr = version1.split('.').map(function(item){
+          return parseInt(item);
+        }).filter(function(item){
+          return !!item || item === 0;
+        });
+        var version2Arr = version2.split('.').map(function(item){
+          return parseInt(item);
+        }).filter(function(item){
+          return !!item || item === 0;
+        });
+        var totalLength = Math.max(version1Arr.length, version2Arr.length);
+        var result = true, i;
+        for (i = 0; i <=totalLength; i++) {
+          if (version2Arr[i] === undefined) {
+            // Example: version1 = "2.3.2.2" and version2 = 2.3.2
+            result = true;
+            break;
+          } else if (version1Arr[i] === undefined) {
+            // Example: version1 = "2.3.2" and version2 = "2.3.2.2"
+            result = false;
+            break;
+          } else if (version1Arr[i] > version2Arr[i]) {
+            // Example: version1 = "2.3.2.2" and version2 = "2.3.2.1"
+            result = true;
+            break;
+          } else if (version1Arr[i] < version2Arr[i]) {
+            // Example: version1 = "2.3.1.2" and version2 = "2.3.2.1"
+            result = false;
+            break;
+          }
+        }
+        return result;
+      }
+    };
+  }
+]);
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/9f93ea6b/ambari-admin/src/main/resources/ui/admin-web/app/styles/main.css
----------------------------------------------------------------------
diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/styles/main.css 
b/ambari-admin/src/main/resources/ui/admin-web/app/styles/main.css
index 4434908..abb3c38 100644
--- a/ambari-admin/src/main/resources/ui/admin-web/app/styles/main.css
+++ b/ambari-admin/src/main/resources/ui/admin-web/app/styles/main.css
@@ -1372,9 +1372,48 @@ accordion .panel-group .panel{
   text-align: right;
 }
 
-.name-label-adjust {
+.repo-table-title {
+  padding-left: 5px;
+  padding-bottom: 10px;
+  border-bottom: 1px solid #ebebeb;
+}
+
+.repo-table-title label {
+  margin-top: 5px;
+}
+
+.repo-table-title #os-label {
+  padding-left: 7px;
+}
+
+.repo-table-title .add-os-button.disabled {
+  cursor: not-allowed;
+}
+
+.advanced-radio-buttons {
+  margin-top: 15px;
+}
+
+.advanced-radio-buttons i {
+  color: #0572ff;
+}
+
+.advanced-radio-buttons span.disabled {
+  opacity: 0.7;
+  cursor: default;
+}
+
+.repo-table-title #name-label-adjust {
   width: 20.7%;
+  padding-left:0px;
+  right:5px;
+}
+
+.repo-table-title #repo-base-url-label {
+  padding-left:0px;
+  right:3px;
 }
+
 .verison-label-row .label {
   font-size: 100%;
 }
@@ -1477,15 +1516,85 @@ thead.view-permission-header > tr > th {
 .left-menu-all-repos .repos-table .repos-td.active > a {
   color: white;
 }
-#upload-definition-file-panel {
-  padding: 5px 10px 5px 10px;
-  margin-bottom: 15px;
+#list-stack-id {
+  padding-left: 0px;
+  padding-right: 0px;
+  margin-left: 0px;
+}
+.tabs-left, .tabs-right {
+  border-bottom: none;
+  padding-top: 2px;
+}
+.tabs-left {
+  border-right: 1px solid #ddd;
+}
+.tabs-right {
+  border-left: 1px solid #ddd;
+}
+.tabs-left>li, .tabs-right>li {
+  float: none;
+  margin-bottom: 2px;
+}
+.tabs-left>li {
+  margin-right: -1px;
+}
+.tabs-right>li {
+  margin-left: -1px;
+}
+.tabs-left>li.active>a,
+.tabs-left>li.active>a:hover,
+.tabs-left>li.active>a:focus {
+  border-bottom-color: #ddd;
+  border-right-color: transparent;
+}
+
+.tabs-right>li.active>a,
+.tabs-right>li.active>a:hover,
+.tabs-right>li.active>a:focus {
+  border-bottom: 1px solid #ddd;
+  border-left-color: transparent;
+}
+.tabs-left>li>a {
+  border-radius: 4px 0 0 4px;
+  margin-right: 0;
+  display:block;
+}
+.tabs-right>li>a {
+  border-radius: 0 4px 4px 0;
+  margin-right: 0;
+}
+
+.public-disabled-option {
+  padding: 5px;
+  padding-left: 15px;
+}
+
+#upload-definition-file-panel .big-radio {
+  font-weight: bold;
+  padding: 5px 15px;
+  margin-left: -18px;
 }
 
-#upload-definition-file-panel .option-radio {
-  padding: 15px 5px;
-  font-weight: bold;;
+#upload-definition-file-panel .big-radio #public-disabled-link {
+  margin-left: 10px;
+  font-weight: normal;
+  cursor: pointer;
 }
+
+#current-stack-details {
+  bottom: 20px;
+  padding-left: 0px;
+}
+
+#current-stack-details .table-borderless {
+ border: transparent;
+}
+
+#current-stack-details .table-borderless tbody tr td, .table-borderless tbody 
tr th, .table-borderless thead tr th {
+  border: none;
+}
+
+
 #upload-definition-file-panel .stack-version-selection {
    padding-left: 25px;
  }
@@ -1510,9 +1619,9 @@ thead.view-permission-header > tr > th {
 .register-version-options .option-radio-button label {
   font-weight: normal;
 }
-.register-version-options .choose-file-input {
-  padding-top: 6px;
-  padding-bottom: 20px;
+.register-version-options .choose-file-input input {
+  padding-top: 3px;
+  padding-bottom: 3px;
 }
 .register-version-form .details-panel .patch-icon {
   color: #ff4500;
@@ -1525,9 +1634,43 @@ thead.view-permission-header > tr > th {
   margin-top: 0;
   margin-bottom: 0;
 }
+
+.details-panel {
+  border: 1px solid #e5e5e5;
+}
+
+.details-panel .version-contents-section {
+  border: 1px solid #ddd;
+  max-height: 200px;
+  overflow: auto;
+}
+
+.details-panel .version-contents-section-register-version {
+  margin: 8px 0;
+}
+
+.details-panel .version-contents-section .table {
+  margin-bottom: 0;
+}
+
+.details-panel .version-contents-section .table tr:first-child td {
+  border-top: none;
+}
+
 .register-version-form .details-panel .version-info-section {
   margin-top: 10px;
 }
+
+.register-version-options .stack-url-input input.disabled {
+  background-color: #eee;
+}
+
+.register-version-options .choose-file-input input.disabled {
+  font-weight: normal;
+  cursor: default;
+  background-color: #eee;
+}
+
 .register-version-form .details-panel .version-contents-section {
   max-height: 200px;
   overflow: auto;
@@ -1553,6 +1696,7 @@ thead.view-permission-header > tr > th {
   padding: 0;
   text-align: center;
   cursor: pointer;
+  width: 5.982905982905983%;
 }
 .register-version-form .repos-panel .remove-icon.disabled{
   color: grey;
@@ -1560,11 +1704,32 @@ thead.view-permission-header > tr > th {
 
 .register-version-form .repos-panel .repo-name-label {
   text-align: left;
+  padding-top: 10px;
+  padding-left: 0px;
+}
+
+.register-version-form .repos-panel .repo-name-url {
+  padding: 8px 0px;
+  margin-bottom: 0px;
 }
 
 .register-version-form .repos-panel .os-type-label {
-  margin-top: 27px;;
+  margin-top: 27px;
+  padding-left: 10px;
 }
+
+.register-version-form .repos-panel .repo-url {
+  padding-right: 5px;
+}
+
+.register-version-form .repos-panel .repo-url input {
+  padding: 4px;
+}
+
+.register-version-form .repos-panel .fa-undo {
+  margin-top: 10px;
+}
+
 .register-version-form .dropdown-menu li a {
   cursor: pointer;
 }
@@ -1588,6 +1753,10 @@ thead.view-permission-header > tr > th {
   color: #5ab400;
 }
 
+.orange-icon {
+  color: #f3b20b;
+}
+
 .cursor-pointer {
   cursor: pointer;
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/9f93ea6b/ambari-admin/src/main/resources/ui/admin-web/app/views/modals/AddVersionModal.html
----------------------------------------------------------------------
diff --git 
a/ambari-admin/src/main/resources/ui/admin-web/app/views/modals/AddVersionModal.html
 
b/ambari-admin/src/main/resources/ui/admin-web/app/views/modals/AddVersionModal.html
new file mode 100644
index 0000000..993cef5
--- /dev/null
+++ 
b/ambari-admin/src/main/resources/ui/admin-web/app/views/modals/AddVersionModal.html
@@ -0,0 +1,52 @@
+<!--
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+-->
+<div class="modal-header">
+    <h3 class="modal-title">Add Version</h3>
+</div>
+<br>
+<div class="clearfix register-version-options" 
ng-click="selectedLocalOption.index=1">
+    <div class="option-radio-button">
+        <label class="option-label">
+            <input type="radio" ng-model="selectedLocalOption.index" 
value="1"> {{'versions.uploadFile' | translate}}
+        </label>
+    </div>
+    <div class="col-sm-7 choose-file-input">
+        <input type="file" ng-model="option1.file" 
ng-class="selectedLocalOption.index!='1' ? 'disabled' : ''"
+               onchange="angular.element(this).scope().onFileSelect(this)"/>
+    </div>
+</div>
+<div class="clearfix register-version-options bottom-margin" 
ng-click="selectedLocalOption.index=2">
+    <div class="option-radio-button">
+        <label class="option-label">
+            <input type="radio" ng-model="selectedLocalOption.index" 
value="2"> {{'versions.enterURL' | translate}}
+        </label>
+    </div>
+    <div class="col-sm-9">
+        <div class="form-group {{option2.name}}" ng-class="{'has-error': 
option2.url.hasError }">
+            <div class="stack-url-input">
+                <input type="text" class="form-control" ng-model="option2.url"
+                       placeholder="{{option2.placeholder}}" 
ng-class="selectedLocalOption.index!='2' ? 'disabled' : ''">
+            </div>
+        </div>
+    </div>
+</div>
+<div class="modal-footer">
+    <button class="btn btn-default" ng-click="cancel()">Cancel</button>
+    <button class="btn btn-primary" ng-model="button" 
ng-click="readVersionInfo()"
+            ng-disabled="readInfoButtonDisabled()">{{'versions.readInfo' | 
translate}}</button>
+</div>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/9f93ea6b/ambari-admin/src/main/resources/ui/admin-web/app/views/modals/ConfirmationModal.html
----------------------------------------------------------------------
diff --git 
a/ambari-admin/src/main/resources/ui/admin-web/app/views/modals/ConfirmationModal.html
 
b/ambari-admin/src/main/resources/ui/admin-web/app/views/modals/ConfirmationModal.html
index b11f6bb..95a68b3 100644
--- 
a/ambari-admin/src/main/resources/ui/admin-web/app/views/modals/ConfirmationModal.html
+++ 
b/ambari-admin/src/main/resources/ui/admin-web/app/views/modals/ConfirmationModal.html
@@ -23,6 +23,6 @@
   <div ng-show="isTempalte" ng-include="body.url"></div>
 </div>
 <div class="modal-footer">
-    <button class="btn btn-default" ng-click="cancel()">{{cancelText}}</button>
+    <button class="btn btn-default" ng-if="showCancelButton" 
ng-click="cancel()">{{cancelText}}</button>
     <button class="btn btn-primary" ng-click="ok()">{{confirmText}}</button>
 </div>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/9f93ea6b/ambari-admin/src/main/resources/ui/admin-web/app/views/modals/publicRepoDisabled.html
----------------------------------------------------------------------
diff --git 
a/ambari-admin/src/main/resources/ui/admin-web/app/views/modals/publicRepoDisabled.html
 
b/ambari-admin/src/main/resources/ui/admin-web/app/views/modals/publicRepoDisabled.html
new file mode 100644
index 0000000..6ef53bf
--- /dev/null
+++ 
b/ambari-admin/src/main/resources/ui/admin-web/app/views/modals/publicRepoDisabled.html
@@ -0,0 +1,23 @@
+<!--
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+-->
+<div class="public-disabled-message" 
translate="versions.networkIssues.publicRepoDisabledMsg"></div>
+<ul>
+  <li class="public-disabled-option" 
translate="versions.networkIssues.publicRepoDisabledMsg1"></li>
+  <li class="public-disabled-option" 
translate="versions.networkIssues.publicRepoDisabledMsg2"></li>
+  <li class="public-disabled-option" 
translate="versions.networkIssues.publicRepoDisabledMsg3"></li>
+</ul>

http://git-wip-us.apache.org/repos/asf/ambari/blob/9f93ea6b/ambari-admin/src/main/resources/ui/admin-web/app/views/stackVersions/list.html
----------------------------------------------------------------------
diff --git 
a/ambari-admin/src/main/resources/ui/admin-web/app/views/stackVersions/list.html
 
b/ambari-admin/src/main/resources/ui/admin-web/app/views/stackVersions/list.html
index 3de92c1..a992898 100644
--- 
a/ambari-admin/src/main/resources/ui/admin-web/app/views/stackVersions/list.html
+++ 
b/ambari-admin/src/main/resources/ui/admin-web/app/views/stackVersions/list.html
@@ -29,34 +29,103 @@
     </div>
   </div>
   <hr/>
-
-  <accordion close-others="false" class="col-sm-2 left-menu-all-repos">
-    <accordion-group ng-repeat="stackVersion in stackVersions" 
is-open="stackVersion.isOpened">
-      <accordion-heading>
-        <div class="row stack-version-title">
-          <i class="glyphicon glyphicon-chevron-right" ng-class="{'opened': 
stackVersion.isOpened}"></i>
-          {{stackVersion.name}}
+  <table class="table table-striped table-hover">
+    <thead>
+    <tr>
+      <th class="col-small">
+        <label>{{'common.stack' | translate}}</label>
+        <select class="form-control"
+                ng-change="resetPagination()"
+                ng-model="filter.stack.current"
+                ng-options="item.label for item in filter.stack.options track 
by item.value"
+          ></select>
+      </th>
+      <th class="col-medium">
+        <label>{{'common.name' | translate}}</label>
+        <input type="text" class="form-control" ng-change="resetPagination()" 
ng-model="filter.name" placeholder="{{'common.any' | translate}}">
+      </th>
+      <th class="col-medium">
+        <label>{{'common.version' | translate}}</label>
+        <input type="text" class="form-control" ng-change="resetPagination()" 
ng-model="filter.version" placeholder="{{'common.any' | translate}}">
+      </th>
+      <th class="col-small">
+        <label>{{'common.cluster' | translate}}</label>
+        <select class="form-control"
+                ng-change="resetPagination()"
+                ng-model="filter.cluster.current"
+                ng-options="item.label for item in filter.cluster.options 
track by item.value"
+          ></select>
+      </th>
+      <th></th>
+    </tr>
+    </thead>
+    <tbody>
+    <tr ng-repeat="repo in repos">
+      <td class="col-small">
+        <span>{{repo.stack_name}}-{{repo.stack_version}}</span>
+      </td>
+      <td class="col-medium">
+        <a 
href="#/stackVersions/{{repo.stack_name}}/{{repo.repository_version}}/edit">{{repo.display_name}}</a>
+      </td>
+      <td class="col-medium">
+        <span>{{repo.repository_version}}</span>
+      </td>
+      <td class="col-small">
+        <a href="/#/main/admin/stack/versions" ng-show="repo.cluster">
+          {{repo.cluster}}
+        </a>
+        <span ng-show="!repo.cluster">
+          {{'common.none' | translate}}
+        </span>
+      </td>
+      <td class="verison-label-row">
+        <div ng-show="repo.status == 'current'">
+          <span class="label {{'status-' + repo.status}}">{{'versions.current' 
| translate}}:&nbsp;{{repo.currentHosts}}/{{repo.totalHosts}}</span>
+        </div>
+        <div ng-show="repo.status == 'installed'">
+          <span class="label {{'status-' + 
repo.status}}">{{'versions.installed' | 
translate}}:&nbsp;{{repo.installedHosts}}/{{repo.totalHosts}}</span>
         </div>
-      </accordion-heading>
-      <table class="table repos-table">
-        <tbody>
-        <tr ng-repeat="repo in stackVersion.repos">
-          <td class="repos-td">
-            <a 
href="#/stackVersions/{{repo.stack_name}}/{{repo.repository_version}}/edit">{{repo.repository_version}}</a>
-          </td>
-        </tr>
-        </tbody>
-        </table>
-    </accordion-group>
-    <div class="alert alert-info" ng-show="stackVersions && 
!stackVersions.length">
-      {{'versions.contents.empty' | translate}}
+        <div ng-show="!repo.cluster">
+          <div class="btn-group display-inline-block" dropdown 
is-open="viewsdropdown.isopen" ng-mouseover="viewsdropdown.isopen=true" 
ng-mouseout="viewsdropdown.isopen=false" ng-init="viewsdropdown.isopen=false">
+            <a class="btn dropdown-toggle">
+              <span>{{'versions.installOn' | translate}}</span>
+            </a>
+            <ul class="dropdown-menu" ng-show="viewsdropdown.isopen">
+              <li ng-repeat="cluster in dropDownClusters">
+                <a href="javascript:void(null)" ng-click="goToCluster()">
+                  <span>{{cluster.Clusters.cluster_name}}</span>
+                </a>
+              </li>
+            </ul>
+          </div>
+        </div>
+      </td>
+    </tr>
+    </tbody>
+  </table>
+  <div class="alert alert-info col-sm-12" ng-show="!repos.length">
+    {{'common.alerts.nothingToDisplay' | translate: '{term: 
getConstant("version")}'}}
+  </div>
+  <div class="col-sm-12 table-bar">
+    <div class="pull-left filtered-info">
+      <span>{{'common.filterInfo' | translate: '{showed: tableInfo.showed, 
total: tableInfo.total, term: getConstant("versions")}'}}</span>
+      <span ng-show="isNotEmptyFilter">- <a href 
ng-click="clearFilters()">{{'common.controls.clearFilters' | 
translate}}</a></span>
     </div>
-  </accordion>
-
-  <form class="col-sm-10 form-horizontal" role="form"novalidate>
-    <div class="alert alert-info no-version-alert">
-      {{'versions.noVersions' | translate}}
+    <div class="pull-right left-margin">
+      <pagination class="paginator"
+                  total-items="pagination.totalRepos"
+                  max-size="pagination.maxVisiblePages"
+                  items-per-page="pagination.itemsPerPage"
+                  ng-model="pagination.currentPage"
+                  ng-change="pageChanged()"
+        ></pagination>
     </div>
-  </form>
-
+    <div class="pull-right">
+      <select class="form-control"
+              ng-model="pagination.itemsPerPage"
+              ng-options="item for item in [10, 25, 50, 100]"
+              ng-change="resetPagination()"
+        ></select>
+    </div>
+  </div>
 </div>

Reply via email to