diff --git a/horizon/static/angular/modal/modal.js b/horizon/static/angular/modal/modal.js
new file mode 100644
index 0000000000..a3af9f9019
--- /dev/null
+++ b/horizon/static/angular/modal/modal.js
@@ -0,0 +1,95 @@
+(function() {
+ 'use strict';
+
+ /**
+ * @ngdoc overview
+ * @name hz.widget.modal
+ *
+ * # hz.widget.modal
+ *
+ * The `hz.widget.modal` provides modal services.
+ *
+ * Requires {@link http://angular-ui.github.io/bootstrap/ `Angular-bootstrap`}
+ *
+ * | Components |
+ * |--------------------------------------------------------------------------|
+ * | {@link hz.widget.modal.controller:simpleModalCtrl `simpleModalCtrl`} |
+ * | {@link hz.widget.modal.factory:simpleModalService `simpleModalService`} |
+ *
+ */
+ angular.module('hz.widget.modal', ['ui.bootstrap'])
+
+ /**
+ * @ngdoc controller
+ * @name simpleModalCtrl
+ *
+ * @param(object) scope of the controller
+ * @param(object) modal instance from angular-bootstrap
+ * @param(object) context object provided by the user
+ *
+ * @description
+ * Horizon's controller for confirmation dialog.
+ * Passes context along to the template.
+ * If user presses cancel button or closes dialog, modal gets dismissed.
+ * If user presses submit button, modal gets closed.
+ * This controller is automatically included in modalService.
+ */
+ .controller('simpleModalCtrl', [ '$scope', '$modalInstance', 'context',
+ function($scope, $modalInstance, context) {
+ $scope.context = context;
+ $scope.submit = function(){ $modalInstance.close(); };
+ $scope.cancel = function(){ $modalInstance.dismiss('cancel'); };
+ } // end of function
+ ]) // end of controller
+
+ /**
+ * @ngdoc service
+ * @name simpleModalService
+ *
+ * @description
+ * Horizon's wrapper for angular-bootstrap modal service.
+ * It should only be use for small confirmation dialogs.
+ * @param {object} the object containing title, body, submit, and cancel labels
+ * @param {object} the object returned from angular-bootstrap $modal
+ *
+ * @example:
+ * angular.controller('modalExampleCtrl', [ '$scope', 'simpleModalService',
+ * function($scope, simpleModalService){
+ * var options = {
+ * title: 'Confirm Delete',
+ * body: 'Are you sure you want to delete this item?',
+ * submit: 'Yes',
+ * cancel: 'No',
+ * };
+ * simpleModalService(options).result.then(function(){
+ * // user clicked on submit button
+ * // do something useful here
+ * });
+ * }
+ * ]);
+ */
+ .factory('simpleModalService', ['$modal', 'basePath',
+ function($modal, path) {
+ return function(params) {
+ if (params && params.title && params.body){
+ var options = {
+ controller: 'simpleModalCtrl',
+ templateUrl: path + 'modal/simple-modal.html',
+ resolve: {
+ context: function() {
+ return {
+ title: params.title? params.title: '',
+ body: params.body? params.body: '',
+ submit: params.submit? params.submit: gettext('Submit'),
+ cancel: gettext('Cancel')
+ };
+ }
+ }
+ };
+ return $modal.open(options);
+ }
+ }; // end of return
+ } // end of function
+ ]); // end of factory
+
+})();
\ No newline at end of file
diff --git a/horizon/static/angular/modal/simple-modal.html b/horizon/static/angular/modal/simple-modal.html
new file mode 100644
index 0000000000..39904f39c6
--- /dev/null
+++ b/horizon/static/angular/modal/simple-modal.html
@@ -0,0 +1,18 @@
+
+
+
\ No newline at end of file
diff --git a/horizon/static/angular/modal/simple-modal.spec.js b/horizon/static/angular/modal/simple-modal.spec.js
new file mode 100644
index 0000000000..9fae12b486
--- /dev/null
+++ b/horizon/static/angular/modal/simple-modal.spec.js
@@ -0,0 +1,55 @@
+'use strict';
+
+describe('hz.widget.modal module', function(){
+ it('should have been defined', function(){
+ expect(angular.module('hz.widget.modal')).toBeDefined();
+ });
+});
+
+describe('modal factory', function(){
+
+ var modal;
+ var modalData = {
+ title: 'Confirm deletion',
+ body: 'Are you sure?',
+ submit: 'Yes',
+ cancel: 'No'
+ };
+
+ beforeEach(module('ui.bootstrap'));
+ beforeEach(module('hz'));
+ beforeEach(module('hz.widgets'));
+ beforeEach(module('hz.widget.modal'));
+ beforeEach(inject(function($injector, simpleModalService){
+ modal = simpleModalService;
+ }));
+
+ it('should fail without any params', function(){
+ expect(modal()).toBeUndefined();
+ });
+
+ it('should fail without a title', function(){
+ var data = angular.copy(modalData);
+ delete data.title;
+ expect(modal(data)).toBeUndefined();
+ });
+
+ it('should fail without a body', function(){
+ var data = angular.copy(modalData);
+ delete data.body;
+ expect(modal(data)).toBeUndefined();
+ });
+
+ it('should have default submit and cancel labels', function(){
+ var data = angular.copy(modalData);
+ delete data.submit;
+ delete data.cancel;
+ expect(modal(data)).toBeDefined();
+ });
+
+ it('should work when all params are supplied', function(){
+ var data = angular.copy(modalData);
+ expect(modal(data)).toBeDefined();
+ });
+
+});
\ No newline at end of file
diff --git a/horizon/static/angular/widget.module.js b/horizon/static/angular/widget.module.js
index 68f50e6e77..1be4d484cc 100644
--- a/horizon/static/angular/widget.module.js
+++ b/horizon/static/angular/widget.module.js
@@ -4,7 +4,8 @@
angular.module('hz.widgets', [
'hz.widget.help-panel',
'hz.widget.wizard',
- 'hz.widget.table'
+ 'hz.widget.table',
+ 'hz.widget.modal'
])
.constant('basePath', '/static/angular/');
diff --git a/horizon/templates/horizon/_scripts.html b/horizon/templates/horizon/_scripts.html
index af375eb9bf..d89b255a63 100644
--- a/horizon/templates/horizon/_scripts.html
+++ b/horizon/templates/horizon/_scripts.html
@@ -26,6 +26,7 @@
+
diff --git a/horizon/test/jasmine/jasmine_tests.py b/horizon/test/jasmine/jasmine_tests.py
index 5a1de149e8..402a98080c 100644
--- a/horizon/test/jasmine/jasmine_tests.py
+++ b/horizon/test/jasmine/jasmine_tests.py
@@ -29,6 +29,7 @@ class ServicesTests(test.JasmineTests):
'angular/help-panel/help-panel.js',
'angular/wizard/wizard.js',
'angular/table/table.js',
+ 'angular/modal/modal.js',
]
specs = [
'horizon/tests/jasmine/utilsSpec.js',
@@ -37,8 +38,9 @@ class ServicesTests(test.JasmineTests):
'angular/help-panel/help-panel.spec.js',
'angular/wizard/wizard.spec.js',
'angular/table/table.spec.js',
+ 'angular/modal/simple-modal.spec.js',
]
externalTemplates = [
'angular/help-panel/help-panel.html',
- 'angular/wizard/wizard.html',
+ 'angular/wizard/wizard.html'
]
diff --git a/openstack_dashboard/templates/_header.html b/openstack_dashboard/templates/_header.html
index 386158969f..66ee0a991f 100644
--- a/openstack_dashboard/templates/_header.html
+++ b/openstack_dashboard/templates/_header.html
@@ -6,7 +6,7 @@