WebUI system logs implementation

This commit implements the webui logs page
which will show all system logs depends
on user selection( SEL or Event or Oem). This
is based out of redfish systems log services.
 - View logs with pagination.
 - Search filter for logs view.
 - Sort option with Type, Id, Severity, Date.
 - Clear logs.
 - Export logs.

UnitTest:
 - Existing bmcweb send the system logs of EntryType
   "Event". So Loaded UI, selected Type "Event" and
   validated all the above mentioned operations.

Change-Id: I0384e475f7913ca66b6db5d64831583fb382f8d5
Signed-off-by: AppaRao Puli <apparao.puli@linux.intel.com>
diff --git a/app/server-health/controllers/syslog-controller.html b/app/server-health/controllers/syslog-controller.html
new file mode 100644
index 0000000..b503bd0
--- /dev/null
+++ b/app/server-health/controllers/syslog-controller.html
@@ -0,0 +1,100 @@
+<loader loading="loading"></loader>
+<div id="sys-log">
+    <section id="sys-log__events" class="table row column">
+      <div class="row column">
+        <h1>System Logs</h1>
+        <div class="page-header">
+          <p class="inline h4">Select system log type: </p>
+          <div class="inline dropdown__wrapper" >
+            <button type="button" class="dropdown__button" ng-click="showLogDropdown = !showLogDropdown"><strong>{{selectedRecordType}}</strong></button>
+            <ul class="dropdown__list inline" ng-show="showLogDropdown">
+              <li ng-repeat="recordType in recordTypeList">
+                <button type="button" ng-click="selectRecordType(recordType);">{{recordType}}</button>
+              </li>
+            </ul>
+          </div>
+          <a ng-href="data:text/json;charset=utf-8,{{sysLogs}}" class="inline btn-export float-right"  download="systemLogs.json" ng-show="sysLogs.length">Export </a>
+          <button class="inline clear-input float-right btn-secondary" ng-click="confirm = !confirm" ng-show="sysLogs.length">&#10005; Clear {{selectedRecordType}} Logs</button>
+          <div class="inline__confirm event__confirm" ng-show="confirm">
+            <div class="inline__confirm-message">
+              <p class="h3">Are you sure you want to <strong class="ng-binding">clear {{selectedRecordType}} logs</strong>?
+              </p>
+            </div>
+            <div class="inline__confirm-buttons">
+              <button class="btn-primary" ng-click="clearSystemLogEntries()">Yes</button>
+              <button class="btn-primary" ng-click="confirm = false">No</button>
+            </div>
+          </div>
+        </div>
+      </div>
+    </section>
+    <section class="row column">
+      <p class="content-label">Filter {{selectedRecordType}} Logs</p>
+      <div class="content__search">
+        <label for="content__search-input">Search</label>
+        <input id="content__search-input" type="text" ng-model="customSearch" ng-keydown="doSearchOnEnter($event)"/>
+        <div class="search-submit__wrapper">
+          <button class="clear-input" ng-click="clear()">&#10005;</button>
+          <input id="content__search-submit" type="submit" class="btn btn-primary content__search-submit" value="Filter" ng-click="doSearchOnClick()"/>
+        </div>
+      </div>
+    </section>
+    <section id="sys-log__events" class="table row column" ng-show="sysLogs.length">
+      <div class="table row column">
+        <div class="table__head">
+          <div class="table__row">
+            <div class="table__cell sys-log__col-wrapper sys-log__col-id">
+              <div class="column small-2 sort_button_wrapper">
+                <button class="sort-ascending" ng-click="sortBy('Id', false)"></button>
+                <button class="sort-descending" ng-click="sortBy('Id', true)"></button>
+              </div>
+              ID
+            </div>
+            <div class="table__cell sys-log__col-wrapper">
+              <div class="column small-2 sort_button_wrapper">
+                <button class="sort-ascending" ng-click="sortBy('Created', false)"></button>
+                <button class="sort-descending" ng-click="sortBy('Created', true)"></button>
+              </div>
+              Timestamp
+            </div>
+            <div class="table__cell sys-log__col-wrapper">
+              <div class="column small-2 sort_button_wrapper">
+                <button class="sort-ascending" ng-click="sortBy('Name', false)"></button>
+                <button class="sort-descending" ng-click="sortBy('Name', true)"></button>
+              </div>
+              Name
+            </div>
+            <div class="table__cell sys-log__col-wrapper">
+              <div class="column small-2 sort_button_wrapper">
+                <button class="sort-ascending" ng-click="sortBy('SensorType', false)"></button>
+                <button class="sort-descending" ng-click="sortBy('SensorType', true)"></button>
+              </div>
+              Type
+            </div>
+            <div class="table__cell sys-log__col-wrapper sys-log__col-sev">
+              <div class="column small-2 sort_button_wrapper">
+                <button class="sort-ascending" ng-click="sortBy('Severity', false)"></button>
+                <button class="sort-descending" ng-click="sortBy('Severity', true)"></button>
+              </div>
+              Severity
+            </div>
+            <div class="table__cell sys-log__col-wrapper sys-log__col-desc">Description</div>
+          </div>
+        </div>
+        <div class="table__body">
+          <div class="table__row" dir-paginate="log in (sysLogs | filter:filterBySearchTerms | orderBy:sortKey:reverse) | itemsPerPage:itemsPerPage">
+            <div class="table__cell sys-log__col-wrapper sys-log__col-id">{{log.Id}}</div>
+            <div class="table__cell sys-log__col-wrapper">{{log.Created | localeDate}}</div>
+            <div class="table__cell sys-log__col-wrapper">{{log.Name}}</div>
+            <div class="table__cell sys-log__col-wrapper">{{log.SensorType}}</div>
+            <div class="table__cell sys-log__col-wrapper sys-log__col-sev">{{log.Severity}}</div>
+            <div class="table__cell sys-log__col-wrapper sys-log__col-desc">{{log.Message}}</div>
+          </div>
+        </div>
+      </div>
+      <dir-pagination-controls></dir-pagination-controls>
+    </section>
+    <section id="sys-log__events" class="table row column" ng-show="!sysLogs.length">
+      <p>There are no {{selectedRecordType}} logs to display at this time.</p>
+    </section>
+</div> <!-- end event log -->
diff --git a/app/server-health/controllers/syslog-controller.js b/app/server-health/controllers/syslog-controller.js
new file mode 100644
index 0000000..cec1d99
--- /dev/null
+++ b/app/server-health/controllers/syslog-controller.js
@@ -0,0 +1,122 @@
+/**
+ * Controller for log
+ *
+ * @module app/serverHealth
+ * @exports sysLogController
+ * @name sysLogController
+ */
+
+window.angular && (function(angular) {
+  'use strict';
+  angular.module('app.serverHealth')
+      .config([
+        'paginationTemplateProvider',
+        function(paginationTemplateProvider) {
+          paginationTemplateProvider.setString(
+              require('../../common/directives/dirPagination.tpl.html'));
+        }
+      ])
+      .controller('sysLogController', [
+        '$scope', 'APIUtils', 'Constants',
+        function($scope, APIUtils, Constants) {
+          $scope.itemsPerPage = Constants.PAGINATION.LOG_ITEMS_PER_PAGE;
+          $scope.loading = true;
+          $scope.sysLogs = [];
+          $scope.customSearch = '';
+          $scope.searchTerms = [];
+          $scope.sortKey = 'Id';
+          $scope.showLogDropdown = false;
+          $scope.recordTypeList =
+              ['SEL', 'Event', 'Oem'];        // From Redfish specification.
+          $scope.selectedRecordType = 'SEL';  // Default Select to SEL.
+
+          $scope.selectRecordType = function(recordType) {
+            $scope.selectedRecordType = recordType;
+            $scope.showLogDropdown = false;
+            APIUtils.getSystemLogs(recordType)
+                .then(
+                    function(res) {
+                      $scope.sysLogs = res;
+                      $scope.filterTypes.push('All');
+                      $scope.sysLogs.forEach(function(log) {
+                        if ($scope.filterTypes.indexOf(log.SensorType) < 0) {
+                          $scope.filterTypes.push(log.SensorType);
+                        }
+                      });
+                    },
+                    function(error) {
+                      console.log(JSON.stringify(error));
+                    })
+                .finally(function() {
+                  $scope.loading = false;
+                });
+          };
+
+          $scope.clearSystemLogEntries = function() {
+            $scope.confirm = false;
+            APIUtils.clearSystemLogs()
+                .then(
+                    function(res) {
+                      console.log(JSON.stringify(res));
+                    },
+                    function(error) {
+                      console.log(JSON.stringify(error));
+                    })
+                .finally(function() {
+                  $scope.selectRecordType($scope.selectedRecordType);
+                });
+          };
+
+          $scope.sortBy = function(keyname, isReverse) {
+            $scope.sortKey = keyname;
+            $scope.reverse = isReverse;
+          };
+
+          $scope.clear = function() {
+            $scope.customSearch = '';
+            $scope.searchTerms = [];
+          };
+
+          $scope.doSearchOnEnter = function(event) {
+            var search =
+                $scope.customSearch.replace(/^\s+/g, '').replace(/\s+$/g, '');
+            if (event.keyCode === 13 && search.length >= 2) {
+              $scope.searchTerms = $scope.customSearch.split(' ');
+            } else {
+              if (search.length == 0) {
+                $scope.searchTerms = [];
+              }
+            }
+          };
+
+          $scope.doSearchOnClick = function() {
+            var search =
+                $scope.customSearch.replace(/^\s+/g, '').replace(/\s+$/g, '');
+            if (search.length >= 2) {
+              $scope.searchTerms = $scope.customSearch.split(' ');
+            } else {
+              if (search.length == 0) {
+                $scope.searchTerms = [];
+              }
+            }
+          };
+
+          $scope.filterBySearchTerms = function(log) {
+            if (!$scope.searchTerms.length) return true;
+
+            for (var i = 0, length = $scope.searchTerms.length; i < length;
+                 i++) {
+              // TODO: Form it while getting data
+              var search_text = log.Id + ' ' + log.Name.toLowerCase() + ' ' +
+                  log.Message.toLowerCase();
+              if (search_text.indexOf($scope.searchTerms[i].toLowerCase()) ==
+                  -1)
+                return false;
+            }
+            return true;
+          };
+
+          setTimeout($scope.selectRecordType($scope.selectedRecordType), 2000);
+        }
+      ]);
+})(angular);
diff --git a/app/server-health/index.js b/app/server-health/index.js
index 088105b..96172d8 100644
--- a/app/server-health/index.js
+++ b/app/server-health/index.js
@@ -42,6 +42,11 @@
                 'controller': 'sensorsOverviewController',
                 authenticated: true
               })
+              .when('/server-health/sys-log', {
+                'template': require('./controllers/syslog-controller.html'),
+                'controller': 'sysLogController',
+                authenticated: true
+              })
               .when('/server-health', {
                 'template': require('./controllers/log-controller.html'),
                 'controller': 'logController',
diff --git a/app/server-health/styles/index.scss b/app/server-health/styles/index.scss
index 009e991..f120af0 100644
--- a/app/server-health/styles/index.scss
+++ b/app/server-health/styles/index.scss
@@ -1,3 +1,4 @@
 @import "./inventory.scss";
 @import "./log.scss";
 @import "./sensors.scss";
+@import "./syslog.scss";
diff --git a/app/server-health/styles/syslog.scss b/app/server-health/styles/syslog.scss
new file mode 100644
index 0000000..c225e55
--- /dev/null
+++ b/app/server-health/styles/syslog.scss
@@ -0,0 +1,309 @@
+// Event Log SCSS
+#sys-log {
+
+  // Dropwdowns filter
+  .dropdown__date.row {
+    padding: .5em;
+  }
+
+  //Timezone select
+  .sys-log__timezone,
+  .sys-log__timezone button {
+    position: relative;
+    text-transform: uppercase;
+    color: $lightbg__primary;
+    font-size: .9em;
+    font-weight: 700;
+    border: 0;
+  }
+  .content__search {
+    float: none;
+    @include mediaQuery(x-large) {
+      @include fastTransition-all;
+    }
+  }
+}
+
+.sort_button_wrapper {
+  padding-top:.3em;
+}
+.sys-log__filters {
+  position: relative;
+  padding-bottom: .5em;
+  padding-top: .5em;
+
+  @media (min-width: 1333px) {
+    float: right;
+    display: inline-block;
+  }
+}
+
+.accord-trigger {
+  position: absolute;
+  right: 1em;
+  top: .3em;
+}
+
+#sys-log__events,
+.sys-log__events {
+  display: block;
+  margin-top: 1.6em;
+  position: relative;
+  .header__actions-bar {
+    .btn-export,
+    .btn-delete,
+    .btn-resolve {
+      color: $white;
+    }
+  }
+  .inline__confirm {
+    height: auto;
+    margin-left: 0;
+    left: 0;
+    padding: 1em 2em 1em 2em
+  }
+  .inline__confirm-message {
+    margin-top: 5px;
+    margin-bottom: -10px;
+  }
+  .inline__confirm-buttons .btn-primary {
+    padding: .5em 2em;
+    min-height: 25px;
+    margin-top: .5em;
+    @include mediaQuery(medium) {
+      margin-top: 0;
+    }
+  }
+  .sys-log__col-wrapper {
+    word-wrap: break-word;
+    white-space: initial;
+  }
+  .col-logged-events {
+    span {
+      display: inline-block;
+      font-weight: 700;
+      margin-right: .3em;
+    }
+  }
+  .sys-log__col-id {
+    max-width: 10%;
+  }
+  .sys-log__col-sev {
+    max-width: 12%;
+  }
+  .sys-log__col-desc {
+    min-width: 30%;
+    max-width: 40%;
+  }
+
+  .dropdown__button {
+    margin-bottom: 1.2em;
+  }
+  .dropdown__list {
+    margin-top: -17px;
+  }
+  .dropdown__button {
+    width: 25em;
+    margin-top: 0;
+    text-align: left;
+    border: 0.1em solid;
+  }
+  .dropdown__wrapper {
+    width: 25em;
+    margin-top: 0.4em;
+    text-align: left;
+  }
+  //Export log
+  .btn-export, .btn-meta-copy,
+  .btn-delete,
+  .btn-resolve {
+    color: black;
+    font-size: .9em;
+    font-weight: 700;
+    position: relative;
+    padding: 0 0 1em 2em;
+    &:hover {
+      text-decoration: underline;
+    }
+  }
+  .btn-resolve.disabled:hover,
+  .btn-delete.disabled:hover{
+    cursor: default;
+    text-decoration: none;
+  }
+  .btn-export {
+    margin-top: 7px;
+    padding-bottom: 0;
+  }
+  .btn-export:before {
+    content: '\21E5';
+    position: absolute;
+    font-size: 1.7em;
+    vertical-align: middle;
+    transform: rotate(90deg);
+    display: inline-block;
+    left: 2px;
+    top: -5px;
+  }
+  .btn-meta-copy,
+  .btn-delete,
+  .btn-resolve {
+    margin-left: 5px;
+    padding: .5em .5em;
+  }
+
+  // Single event log card
+  .sys-log__single-event {
+    border: 1px solid $medgrey;
+    padding: 1em 1em 1em .7em;
+    margin: .5em 0 .5em 0;
+    position: relative;
+    overflow: hidden;
+    @include fastTransition-all;
+    &.active {
+      background-color: $lightgrey;
+      @include slowTransition-all;
+    }
+  }
+  .sys-log__event-info {
+    &:hover {
+      cursor: pointer;
+    }
+  }
+
+  //Event description
+  .event__description {
+    font-weight: 400;
+    margin-left: 1em;
+    flex:35%;
+    @include mediaQuery(small) {
+      margin-left: 4.5em;
+      margin-right: 1em;
+    }
+    word-break: break-all;
+  }
+
+  //Event ID
+  .event__id {
+    @include fontCourierBold;
+    font-size: .9em;
+    color: $darkgrey;
+    margin-right: 1em;
+  }
+
+  .event__timestamp {
+    text-align: right;
+    @include fontCourierBold;
+    font-size: .9em;
+    color: $darkgrey;
+    max-width: 18em;
+    @media (min-width: 1105px) {
+      float: right;
+    }
+  }
+
+  // Event metadata row
+  .event__metadata-row {
+    max-height: 0;
+    overflow: hidden;
+    transition: max-height .5s linear;
+    &.active {
+      max-height: 1000px;
+      @include mediaQuery(small) {
+        max-height: 1000px;
+      }
+      @include mediaQuery(medium) {
+        max-height: 1000px;
+      }
+    }
+  }
+
+  //Event metadata
+  .event__metadata {
+    height: 200px;
+    overflow-y: scroll;
+    border: 1px solid $medgrey;
+    padding: .5em .5em 0;
+    background: $white;
+    display: block;
+    margin-bottom: 1.5em;
+    &::-webkit-scrollbar {
+      -webkit-appearance: none;
+      width: 7px;
+    }
+    &::-webkit-scrollbar-thumb {
+      border-radius: 4px;
+      background-color: rgba(0, 0, 0, .5);
+      -webkit-box-shadow: 0 0 1px rgba(255, 255, 255, .5);
+    }
+  }
+
+  //Event Related Items
+  .event__related {
+    width: 100%;
+  }
+  .event__related-label {
+    font-weight: 700;
+    margin-right: 1.2em;
+    padding-top: .3em;
+    float: left;
+  }
+  .event__related-item {
+    margin-right: 1em;
+    padding-top: .3em;
+    display: inline-block;
+    float: left;
+    clear: both;
+  }
+  .event__actions {
+    margin-left: -1em;
+    margin-top: .5em;
+    @include mediaQuery(medium) {
+      float: right;
+      margin-top: -5px;
+    }
+  }
+  .event__icon {
+    width: 20px;
+    height: 20px;
+    font-weight: normal;
+    margin-right: .5em;
+    margin-top: -3px;
+    display: inline-block;
+  }
+  .sort-ascending {
+    display: block;
+    padding: 0;
+    transform: rotate(-90deg);
+    font-size: 1em;
+    color: lighten($darkgrey, 10%);
+    &:hover {
+      color: $darkbg__accent;
+    }
+    &:after {
+      content: '\276F'
+    }
+    &:focus {
+      outline: 0;
+      color: $darkbg__accent;
+    }
+  }
+  .sort-descending {
+    display: block;
+    padding: 0;
+    transform: rotate(-90deg);
+    font-size: 1em;
+    color: lighten($darkgrey, 10%);
+    &:hover {
+      color: $darkbg__accent;
+    }
+    &:after {
+      content: '\276e'
+    }
+    &:focus {
+      outline: 0;
+      color: $darkbg__accent;
+    }
+  }
+}
+//end sys-log__events