Add role alert to toasts

For accessibility reasons, 'role="alert" is required for toast
messages. This notifies screen readers that an error or success
message has appeared. Adds a service layer for toast messages
that adds the role attribute to the message.

Change-Id: Ic4dbf5556337eea589de5692c1b4c3323e771813
Signed-off-by: beccabroek <beccabroek@gmail.com>
diff --git a/app/common/services/toastService.js b/app/common/services/toastService.js
new file mode 100644
index 0000000..c199e96
--- /dev/null
+++ b/app/common/services/toastService.js
@@ -0,0 +1,28 @@
+/**
+ * data service
+ *
+ * @module app/common/services/toastService
+ * @exports toastService
+ * @name toastService
+
+ */
+
+window.angular && (function(angular) {
+  'use strict';
+
+  angular.module('app.common.services').service('toastService', [
+    'ngToast', '$sce',
+    function(ngToast, $sce) {
+      this.error = function(message) {
+        var errorMessage = $sce.trustAsHtml(
+            '<div role="alert"><b>Error</b><br>' + message + '</div>');
+        ngToast.create({className: 'danger', content: errorMessage});
+      };
+      this.success = function(message) {
+        var successMessage = $sce.trustAsHtml(
+            '<div role="alert"><b>Success!</b><br>' + message + '</div>');
+        ngToast.create({className: 'success', content: successMessage});
+      };
+    }
+  ]);
+})(window.angular);
diff --git a/app/common/styles/elements/toast.scss b/app/common/styles/elements/toast.scss
index ae6d1bc..a530472 100644
--- a/app/common/styles/elements/toast.scss
+++ b/app/common/styles/elements/toast.scss
@@ -15,14 +15,4 @@
     border-radius: 0;
     text-align: left;
   }
-  .alert-success::before {
-    content: "Success!";
-    display: block;
-    font-weight: bold;
-  }
-  .alert-danger::before {
-    content: "Error";
-    display: block;
-    font-weight: bold;
-  }
 }
diff --git a/app/configuration/controllers/date-time-controller.js b/app/configuration/controllers/date-time-controller.js
index 826ae72..2f4fd3d 100644
--- a/app/configuration/controllers/date-time-controller.js
+++ b/app/configuration/controllers/date-time-controller.js
@@ -10,8 +10,8 @@
   'use strict';
 
   angular.module('app.configuration').controller('dateTimeController', [
-    '$scope', '$window', 'APIUtils', '$route', '$q', 'ngToast',
-    function($scope, $window, APIUtils, $route, $q, ngToast) {
+    '$scope', '$window', 'APIUtils', '$route', '$q', 'toastService',
+    function($scope, $window, APIUtils, $route, $q, toastService) {
       $scope.bmc = {};
       // Only used when the owner is "Split"
       $scope.host = {};
@@ -119,11 +119,11 @@
               $q.all(manual_promises)
                   .then(
                       function() {
-                        ngToast.success('Date and time settings saved');
+                        toastService.success('Date and time settings saved');
                       },
                       function(errors) {
                         console.log(JSON.stringify(errors));
-                        ngToast.danger(
+                        toastService.error(
                             'Date and time settings could not be saved');
                       })
                   .finally(function() {
@@ -132,7 +132,7 @@
             },
             function(errors) {
               console.log(JSON.stringify(errors));
-              ngToast.danger('Date and time settings could not be saved');
+              toastService.error('Date and time settings could not be saved');
               $scope.loading = false;
             });
       };
diff --git a/app/configuration/controllers/firmware-controller.js b/app/configuration/controllers/firmware-controller.js
index 8d4926d..9a811c3 100644
--- a/app/configuration/controllers/firmware-controller.js
+++ b/app/configuration/controllers/firmware-controller.js
@@ -12,10 +12,10 @@
   angular.module('app.configuration').controller('firmwareController', [
     '$scope', '$window', 'APIUtils', 'dataService', '$location',
     '$anchorScroll', 'Constants', '$interval', '$q', '$timeout', '$interpolate',
-    'ngToast',
+    'toastService',
     function(
         $scope, $window, APIUtils, dataService, $location, $anchorScroll,
-        Constants, $interval, $q, $timeout, $interpolate, ngToast) {
+        Constants, $interval, $q, $timeout, $interpolate, toastService) {
       $scope.dataService = dataService;
 
       // Scroll to target anchor
@@ -96,7 +96,7 @@
                 },
                 function(error) {
                   console.log(JSON.stringify(error));
-                  ngToast.danger('Unable to activate image');
+                  toastService.error('Unable to activate image');
                 })
             .then(function(activationState) {
               waitForActive($scope.activate_image_id)
@@ -106,7 +106,7 @@
                       },
                       function(error) {
                         console.log(JSON.stringify(error));
-                        ngToast.danger('Unable to activate image');
+                        toastService.error('Unable to activate image');
                       })
                   .then(function(state) {
                     if ($scope.activate.reboot &&
@@ -126,7 +126,7 @@
                             function(response) {},
                             function(error) {
                               console.log(JSON.stringify(error));
-                              ngToast.danger('Unable to reboot BMC');
+                              toastService.error('Unable to reboot BMC');
                             });
                       }, 10000);
                     }
@@ -156,7 +156,7 @@
             })
             .catch(function(error) {
               console.log(JSON.stringify(error));
-              ngToast.danger(Constants.MESSAGES.POWER_OP.POWER_ON_FAILED);
+              toastService.error(Constants.MESSAGES.POWER_OP.POWER_ON_FAILED);
             });
       };
       function warmReboot() {
@@ -171,7 +171,8 @@
             })
             .catch(function(error) {
               console.log(JSON.stringify(error));
-              ngToast.danger(Constants.MESSAGES.POWER_OP.WARM_REBOOT_FAILED);
+              toastService.error(
+                  Constants.MESSAGES.POWER_OP.WARM_REBOOT_FAILED);
             });
       };
       $scope.isServerOff = function() {
@@ -185,7 +186,7 @@
               .then(
                   function(response) {
                     $scope.uploading = false;
-                    ngToast.success(
+                    toastService.success(
                         'Image file "' + $scope.file.name +
                         '" has been uploaded');
                     $scope.file = '';
@@ -194,7 +195,7 @@
                   function(error) {
                     $scope.uploading = false;
                     console.log(error);
-                    ngToast.danger('Unable to upload image file');
+                    toastService.error('Unable to upload image file');
                   });
         }
       };
@@ -236,7 +237,8 @@
 
       $scope.download = function() {
         if (!$scope.download_host || !$scope.download_filename) {
-          ngToast.danger('TFTP server IP address and file name are required!');
+          toastService.error(
+              'TFTP server IP address and file name are required!');
           return false;
         }
 
@@ -260,12 +262,12 @@
                   $scope.download_host = '';
                   $scope.download_filename = '';
                   $scope.downloading = false;
-                  ngToast.success('Download complete');
+                  toastService.success('Download complete');
                   $scope.loadFirmwares();
                 },
                 function(error) {
                   console.log(error);
-                  ngToast.danger(
+                  toastService.error(
                       'Image file from TFTP server "' + $scope.download_host +
                       '" could not be downloaded');
                   $scope.downloading = false;
@@ -286,7 +288,7 @@
             .then(function(response) {
               $scope.loading = false;
               if (response.status == 'error') {
-                ngToast.danger('Unable to update boot priority');
+                toastService.error('Unable to update boot priority');
               } else {
                 $scope.loadFirmwares();
               }
@@ -303,7 +305,7 @@
         APIUtils.deleteImage($scope.delete_image_id).then(function(response) {
           $scope.loading = false;
           if (response.status == 'error') {
-            ngToast.danger('Unable to delete image');
+            toastService.error('Unable to delete image');
           } else {
             $scope.loadFirmwares();
           }
diff --git a/app/configuration/controllers/network-controller.js b/app/configuration/controllers/network-controller.js
index 8a92ef4..d2d99ba 100644
--- a/app/configuration/controllers/network-controller.js
+++ b/app/configuration/controllers/network-controller.js
@@ -11,9 +11,10 @@
 
   angular.module('app.configuration').controller('networkController', [
     '$scope', '$window', 'APIUtils', 'dataService', '$timeout', '$route', '$q',
-    'ngToast',
+    'toastService',
     function(
-        $scope, $window, APIUtils, dataService, $timeout, $route, $q, ngToast) {
+        $scope, $window, APIUtils, dataService, $timeout, $route, $q,
+        toastService) {
       $scope.dataService = dataService;
       $scope.network = {};
       $scope.oldInterface = {};
@@ -98,7 +99,7 @@
           for (var i in $scope.interface.ipv4.values) {
             if (!APIUtils.validIPV4IP(
                     $scope.interface.ipv4.values[i].Address)) {
-              ngToast.danger(
+              toastService.error(
                   $scope.interface.ipv4.values[i].Address +
                   ' invalid IP parameter');
               $scope.loading = false;
@@ -106,7 +107,7 @@
             }
             if (!APIUtils.validIPV4IP(
                     $scope.interface.ipv4.values[i].Gateway)) {
-              ngToast.danger(
+              toastService.error(
                   $scope.interface.ipv4.values[i].Address +
                   ' invalid gateway parameter');
               $scope.loading = false;
@@ -114,7 +115,7 @@
             }
             // The netmask prefix length will be undefined if outside range
             if (!$scope.interface.ipv4.values[i].PrefixLength) {
-              ngToast.danger(
+              toastService.error(
                   $scope.interface.ipv4.values[i].Address +
                   ' invalid Prefix Length parameter');
               $scope.loading = false;
@@ -152,12 +153,12 @@
                 $timeout(function() {
                   loadNetworkInfo();
                   $scope.loading = false;
-                  ngToast.success('Network settings saved');
+                  toastService.success('Network settings saved');
                 }, 4000);
               },
               function(error) {
                 $scope.loading = false;
-                ngToast.danger('Network settings could not be saved');
+                toastService.error('Network settings could not be saved');
               })
         } else {
           $scope.loading = false;
diff --git a/app/configuration/controllers/snmp-controller.js b/app/configuration/controllers/snmp-controller.js
index 4ad0685..9d71765 100644
--- a/app/configuration/controllers/snmp-controller.js
+++ b/app/configuration/controllers/snmp-controller.js
@@ -10,8 +10,8 @@
   'use strict';
 
   angular.module('app.configuration').controller('snmpController', [
-    '$scope', '$window', 'APIUtils', '$route', '$q', 'ngToast',
-    function($scope, $window, APIUtils, $route, $q, ngToast) {
+    '$scope', '$window', 'APIUtils', '$route', '$q', 'toastService',
+    function($scope, $window, APIUtils, $route, $q, toastService) {
       $scope.managers = [];
       $scope.loading = true;
       $scope.managersToDelete = [];
@@ -31,7 +31,7 @@
             }
           },
           function(error) {
-            ngToast.danger('Unable to load SNMP settings.');
+            toastService.error('Unable to load SNMP settings.');
             console.log(JSON.stringify(error));
           });
 
@@ -74,7 +74,7 @@
           if (!$scope.managers[i].address || !$scope.managers[i].port) {
             // TODO: Highlight the field that is empty
             $scope.loading = false;
-            ngToast.danger('All fields are required.');
+            toastService.error('All fields are required.');
             return;
           }
 
@@ -104,10 +104,10 @@
         $q.all(promises)
             .then(
                 function() {
-                  ngToast.success('SNMP Managers set.');
+                  toastService.success('SNMP Managers set.');
                 },
                 function(errors) {
-                  ngToast.danger('Unable to set SNMP Managers.');
+                  toastService.error('Unable to set SNMP Managers.');
                   console.log(JSON.stringify(errors));
                 })
             .finally(function() {
diff --git a/app/index.js b/app/index.js
index 2313016..c9fed83 100644
--- a/app/index.js
+++ b/app/index.js
@@ -31,6 +31,7 @@
 import services_index from './common/services/index.js';
 import constants from './common/services/constants.js';
 import dataService from './common/services/dataService.js';
+import toastService from './common/services/toastService.js';
 import api_utils from './common/services/api-utils.js';
 import userModel from './common/services/userModel.js';
 import apiInterceptor from './common/services/apiInterceptor.js';
diff --git a/app/server-control/controllers/power-operations-controller.js b/app/server-control/controllers/power-operations-controller.js
index 1bb0a81..1a1f355 100644
--- a/app/server-control/controllers/power-operations-controller.js
+++ b/app/server-control/controllers/power-operations-controller.js
@@ -11,10 +11,10 @@
 
   angular.module('app.serverControl').controller('powerOperationsController', [
     '$scope', 'APIUtils', 'dataService', 'Constants', '$timeout', '$interval',
-    '$interpolate', '$q', 'ngToast',
+    '$interpolate', '$q', 'toastService',
     function(
         $scope, APIUtils, dataService, Constants, $timeout, $interval,
-        $interpolate, $q, ngToast) {
+        $interpolate, $q, toastService) {
       $scope.dataService = dataService;
       $scope.confirm = false;
       $scope.power_confirm = false;
@@ -64,7 +64,7 @@
               $scope.loading = false;
             })
             .catch(function(error) {
-              ngToast.danger(Constants.MESSAGES.POWER_OP.POWER_ON_FAILED);
+              toastService.error(Constants.MESSAGES.POWER_OP.POWER_ON_FAILED);
               $scope.loading = false;
             });
       };
@@ -118,7 +118,8 @@
               $scope.loading = false;
             })
             .catch(function(error) {
-              ngToast.danger(Constants.MESSAGES.POWER_OP.WARM_REBOOT_FAILED);
+              toastService.error(
+                  Constants.MESSAGES.POWER_OP.WARM_REBOOT_FAILED);
               $scope.loading = false;
             });
       };
@@ -161,7 +162,8 @@
               $scope.loading = false;
             })
             .catch(function(error) {
-              ngToast.danger(Constants.MESSAGES.POWER_OP.COLD_REBOOT_FAILED);
+              toastService.error(
+                  Constants.MESSAGES.POWER_OP.COLD_REBOOT_FAILED);
               $scope.loading = false;
             });
       };
@@ -191,7 +193,7 @@
               $scope.loading = false;
             })
             .catch(function(error) {
-              ngToast.danger(
+              toastService.error(
                   Constants.MESSAGES.POWER_OP.ORDERLY_SHUTDOWN_FAILED);
               $scope.loading = false;
             });
@@ -220,7 +222,7 @@
               $scope.loading = false;
             })
             .catch(function(error) {
-              ngToast.danger(
+              toastService.error(
                   Constants.MESSAGES.POWER_OP.IMMEDIATE_SHUTDOWN_FAILED);
               $scope.loading = false;
             });
diff --git a/app/server-control/controllers/power-usage-controller.js b/app/server-control/controllers/power-usage-controller.js
index 5dc848e..9398259 100644
--- a/app/server-control/controllers/power-usage-controller.js
+++ b/app/server-control/controllers/power-usage-controller.js
@@ -10,8 +10,8 @@
   'use strict';
 
   angular.module('app.serverControl').controller('powerUsageController', [
-    '$scope', '$window', 'APIUtils', '$route', '$q', 'ngToast',
-    function($scope, $window, APIUtils, $route, $q, ngToast) {
+    '$scope', '$window', 'APIUtils', '$route', '$q', 'toastService',
+    function($scope, $window, APIUtils, $route, $q, toastService) {
       $scope.power_consumption = '';
       $scope.power_cap = {};
       $scope.loading = false;
@@ -49,7 +49,8 @@
       $scope.setPowerCap = function() {
         // The power cap value will be undefined if outside range
         if (!$scope.power_cap.PowerCap) {
-          ngToast.danger('Power cap value between 100 and 10,000 is required');
+          toastService.error(
+              'Power cap value between 100 and 10,000 is required');
           return;
         }
         $scope.loading = true;
@@ -61,10 +62,10 @@
         $q.all(promises)
             .then(
                 function() {
-                  ngToast.success('Power cap settings saved');
+                  toastService.success('Power cap settings saved');
                 },
                 function(errors) {
-                  ngToast.danger('Power cap settings could not be saved');
+                  toastService.error('Power cap settings could not be saved');
                 })
             .finally(function() {
               $scope.loading = false;
diff --git a/app/server-control/controllers/server-led-controller.js b/app/server-control/controllers/server-led-controller.js
index 96ea064..f44bcc0 100644
--- a/app/server-control/controllers/server-led-controller.js
+++ b/app/server-control/controllers/server-led-controller.js
@@ -10,8 +10,8 @@
   'use strict';
 
   angular.module('app.serverControl').controller('serverLEDController', [
-    '$scope', '$window', '$route', 'APIUtils', 'dataService', 'ngToast',
-    function($scope, $window, $route, APIUtils, dataService, ngToast) {
+    '$scope', '$window', '$route', 'APIUtils', 'dataService', 'toastService',
+    function($scope, $window, $route, APIUtils, dataService, toastService) {
       $scope.dataService = dataService;
 
       APIUtils.getLEDState().then(function(state) {
@@ -39,7 +39,7 @@
             .then(
                 function(response) {},
                 function(errors) {
-                  ngToast.danger(
+                  toastService.error(
                       'Failed to turn LED light ' +
                       (toggleState ? 'on' : 'off'));
                   console.log(JSON.stringify(errors));