Topic Id: {{topic.id}}
Date Created: {{formatDate(topic.createDate)}}
Last Updated: {{formatDate(topic.changeDate)}}
Status: {{data.topicState}}
*Required Field
Topic Name*
Please Describe Your Query's Intent*
+ ng-click="setState('edit')">
EDIT
diff --git a/apps/steward-app/src/main/js/src/app/dashboard/topics/topic-table/topic-detail/role1-edit.tpl.html b/apps/steward-app/src/main/js/src/app/dashboard/topics/topic-table/topic-detail/role1-edit.tpl.html
index 71330196e..54131eadc 100644
--- a/apps/steward-app/src/main/js/src/app/dashboard/topics/topic-table/topic-detail/role1-edit.tpl.html
+++ b/apps/steward-app/src/main/js/src/app/dashboard/topics/topic-table/topic-detail/role1-edit.tpl.html
@@ -1,51 +1,53 @@
Topic Id: {{topic.id}}
Date Created: {{formatDate(topic.createDate)}}
Last Updated: {{formatDate(topic.changeDate)}}
Status: {{data.topicState}}
*Required Field
Topic Name*
Please Describe Your Query's Intent*
UPDATE
diff --git a/apps/steward-app/src/main/js/src/app/dashboard/topics/topic-table/topic-detail/role2-description.tpl.html b/apps/steward-app/src/main/js/src/app/dashboard/topics/topic-table/topic-detail/role2-description.tpl.html
index 9609ed421..63f3a27fd 100644
--- a/apps/steward-app/src/main/js/src/app/dashboard/topics/topic-table/topic-detail/role2-description.tpl.html
+++ b/apps/steward-app/src/main/js/src/app/dashboard/topics/topic-table/topic-detail/role2-description.tpl.html
@@ -1,48 +1,48 @@
Topic Id: {{topic.id}}
Date Created: {{formatDate(topic.createDate)}}
Last Updated: {{formatDate(topic.changeDate)}}
*Required Field
Topic Name*
Please Describe Your Query's Intent*
Status:
{{data.topicState}}
+ ng-click="setState('edit')">
EDIT
\ No newline at end of file
diff --git a/apps/steward-app/src/main/js/src/app/dashboard/topics/topic-table/topic-detail/topic-detail.js b/apps/steward-app/src/main/js/src/app/dashboard/topics/topic-table/topic-detail/topic-detail.js
deleted file mode 100644
index 5f2a308af..000000000
--- a/apps/steward-app/src/main/js/src/app/dashboard/topics/topic-table/topic-detail/topic-detail.js
+++ /dev/null
@@ -1,69 +0,0 @@
-angular
- .module("topic-detail", ['topic-detail-model'])
- .controller('TopicDetailCtrl', ['$scope', '$modalInstance', 'modalData', 'modalCallback', 'Role2TopicDetailMdl', 'Role1TopicDetailMdl', '$app', function ($scope, $modalInstance, modalData, modalCallback, Role2TopicDetailMdl, Role1TopicDetailMdl, $app) {
-
- $scope.roles = $app.globals.UserRoles;
- $scope.formatDate = $app.utils.utcToMMDDYYYY;
- $scope.currentTopic = modalData;
- $scope.tabState = 'description';
- $scope.modalCallback= modalCallback;
- $scope.selectedTab = $scope.tabState;
-
- $scope.ok = function (id) {
- if($scope.currentTopic.state === "Pending") {
- $modalInstance.close();
- return;
- }
-
- (($scope.currentTopic.state == "Approved") ?
- Role2TopicDetailMdl.approveTopic(id) :
- Role2TopicDetailMdl.rejectTopic(id))
- .then(function (result) {
- $scope.modalCallback();
- $modalInstance.close(result);
- });
- };
-
- $scope.update = function (id, name, description) {
- Role1TopicDetailMdl.updateTopic(id, name, description)
- .then( function (result) {
- $scope.modalCallback();
- $modalInstance.close(result);
- });
- };
-
- $scope.setState = function (state) {
- if ($scope.isEditable() === true) {
- $scope.tabState = state;
- }
- };
-
- $scope.cancel = function () {
- $modalInstance.dismiss('cancel');
- };
-
- $scope.isEditable = function () {
- return ($app.globals.currentUser.roles[0] === $scope.roles.ROLE2) || ($scope.currentTopic.state === "Pending");
- };
-
- $scope.canViewHistory = function () {
- var canView = ($app.globals.currentUser.roles[0] === $scope.roles.ROLE2 && $scope.currentTopic.state !== "Pending") || ($scope.currentTopic.state === "Approved");
- return canView;
- };
-
- }])
-
- .directive("topicEdit", function () {
- return {
- restrict: "A",
- templateUrl: "src/app/common/topic-detail/edit/edit.tpl.html",
- replace: true
- };
- })
- .directive("topicDescription", function () {
- return {
- restrict: "A",
- templateUrl: "src/app/common/topic-detail/description/description-per-role.tpl.html",
- replace: true
- };
- });
\ No newline at end of file
diff --git a/apps/steward-app/src/main/js/src/app/dashboard/topics/topics-model.js b/apps/steward-app/src/main/js/src/app/dashboard/topics/topics-model.js
index 4d972f170..f691abd3a 100644
--- a/apps/steward-app/src/main/js/src/app/dashboard/topics/topics-model.js
+++ b/apps/steward-app/src/main/js/src/app/dashboard/topics/topics-model.js
@@ -1,133 +1,134 @@
angular
.module("topics-model", ["topics-model.private"])
.service("TopicsModelFactory", ['$app', 'Role1TopicsMdl', 'Role2TopicsMdl', function ($app, Role1TopicsMdl, Role2TopicsMdl) {
this.getInstance = function (role) {
var instance = (role === $app.globals.UserRoles.ROLE1) ? new Role1TopicsMdl() : new Role2TopicsMdl();
return instance;
};
}]);
angular
.module("topics-model.private", ['model-service'])
.service("TopicsModelSvc", ['$http', 'ModelService', '$app', function ($http, mdlSvc, $app) {
var topics = [],
URLS = {
FETCH: "/topics",
REQUEST_ACCESS: "/requestTopicAccess"
};
function processNewRequestSuccess(result) {
var test = result;
}
function onFail(result) {
alert("HTTP Request Fail: " + result);
}
function cacheTopics(result) {
var topics = result.data.topics,
skipped = result.data.skipped,
totalCount = result.data.totalCount;
return {
topics: topics,
numberSkipped: skipped,
totalCount: totalCount
};
}
this.getTopics = function (role, skip, limit, sortBy, sortDirection) {
var roleSegment = mdlSvc.getRoleSegment(role, $app.globals.UserRoles),
url = mdlSvc.getURL(mdlSvc.url.base + roleSegment + URLS.FETCH, skip, limit, sortBy, sortDirection);
return $http.get(url)
.then(cacheTopics, onFail);
};
this.getTopicsByState = function (role, skip, limit, state, sortBy, sortDirection) {
var roleSegment = mdlSvc.getRoleSegment(role, $app.globals.UserRoles),
url = mdlSvc.getURL(mdlSvc.url.base + roleSegment + URLS.FETCH, skip, limit, state, sortBy, sortDirection);
return $http.get(url)
.then(cacheTopics, onFail);
};
this.requestNewTopic = function (role, topic) {
var roleSegment = mdlSvc.getRoleSegment(role, $app.globals.UserRoles),
url = mdlSvc.getURL(mdlSvc.url.base +
roleSegment + URLS.REQUEST_ACCESS);
return $http.post(url, topic)
.then(processNewRequestSuccess, onFail);
};
}])
.service('Role2TopicsMdl', ['TopicsModelSvc', '$app', function (svc, $app) {
function TopicsMdl() {
this.role = $app.globals.UserRoles.ROLE2;
}
TopicsMdl.prototype.getTopics = function (skip, limit, state, sortBy, sortDirection) {
return svc.getTopicsByState(this.role, skip, limit, state, sortBy, sortDirection);
};
return TopicsMdl;
}])
.service("Role1TopicsMdl", ['TopicsModelSvc', '$app', 'Role2TopicsMdl', function (svc, $app, superMdl) {
function TopicsMdl() {
this.role = $app.globals.UserRoles.ROLE1;
}
//@todo: clean this up same as above...use just one model.
TopicsMdl.prototype.getTopics = function (skip, limit, state, sortBy, sortDirection) {
return svc.getTopicsByState(this.role, skip, limit, state, sortBy, sortDirection);
};
TopicsMdl.prototype.requestNewTopic = function (topic) {
+ topic.name = topic.name;
return svc.requestNewTopic(this.role, topic);
};
return TopicsMdl;
}])
.service("Role2TopicDetailMdl", ["$http", "ModelService", function ($http, mdlSvc) {
var model = this,
URLS = {
APPROVE_TOPIC: "steward/approveTopic/topic/",
REJECT_TOPIC: "steward/rejectTopic/topic/"
};
model.approveTopic = function (topicId) {
var url = mdlSvc.url.base + URLS.APPROVE_TOPIC + topicId;
return $http.post(url, {});
};
model.rejectTopic = function(topicId) {
var url = mdlSvc.url.base + URLS.REJECT_TOPIC + topicId;
return $http.post(url, {});
};
return model;
}])
.service("Role1TopicDetailMdl", ["$http", "ModelService", function ($http, mdlSvc) {
/*
curl -w " %{http_code}\n" -u ben:kapow -X POST "http://localhost:8080/steward/researcher/editTopicRequest/1" -H "Content-Type: application/json" -d '{"name":"KidneyStudy","description":"Kidney Study you should approve"}'
*/
var model = this,
URLS = {
UPDATE_TOPIC: "researcher/editTopicRequest/"
};
model.updateTopic = function (id, name, description) {
var url = mdlSvc.url.base + URLS.UPDATE_TOPIC + id;
return $http.post(url, {
- name: name,
+ name: name.substring(0,254),
description: description
});
};
return model;
}]);
diff --git a/apps/steward-app/src/main/js/src/app/dashboard/topics/topics.js b/apps/steward-app/src/main/js/src/app/dashboard/topics/topics.js
index 36324ee78..a283f05c8 100644
--- a/apps/steward-app/src/main/js/src/app/dashboard/topics/topics.js
+++ b/apps/steward-app/src/main/js/src/app/dashboard/topics/topics.js
@@ -1,266 +1,266 @@
'use strict';
/**
* @ngdoc function
* @name sbAdminApp.controller:MainCtrl
* @description
* # MainCtrl
* Controller of the sbAdminApp
* https://scotch.io/tutorials/sort-and-filter-a-table-using-angular
*/
angular.module('stewardApp')
.controller("NewTopicCtrl", function ($scope, $modalInstance, model, refreshTopics) {
$scope.newTopic = {name: "", description: ""};
$scope.ok = function () {
var name = $scope.newTopic.name,
description = $scope.newTopic.description;
model.requestNewTopic({"name": name, "description": description})
.then(function () {
refreshTopics();
$modalInstance.close();
});
};
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
})
.controller('TopicsCtrl', function ($scope, $position, $app, TopicsModelFactory, $modal) {
//private vars
var roles = $app.globals.UserRoles,
user = $app.globals.currentUser,
utils = $app.utils,
scope = $scope,
ModelFactory = TopicsModelFactory,
role, model, initState;
//determine role of user.
role = (utils.hasAccess(user, [roles.ROLE2])) ? roles.ROLE2 : roles.ROLE1;
initState = (role === roles.ROLE2) ? $app.globals.States.STATE1 : "ALL";
model = ModelFactory.getInstance(role);
$scope.model = model;
$scope.roles = roles;
$scope.userRole = role;
$scope.formatDate = $app.utils.utcToMMDDYYYY;
$scope.states = $app.globals.States;
$scope.topics = [];
$scope.filterBy = 'topicName';
$scope.sort = {
currentColumn: 'changeDate',
descending: false
};
$scope.selectedTab = initState;
/**
* Parent scope data referenced by children.
* @type {{topics: Array, name: string, description: string, sort: {column: string, descending: boolean}}}
*/
$scope.data = {
name: 'name',
description: 'description'
};
//set pagination values.
$scope.range = $app.globals.ViewConfig.RANGE;//range of paging numbers at bottom
$scope.length = 0; //total number of results
$scope.pageIndex = $app.globals.ViewConfig.INDEX;//current page
$scope.limit = $app.globals.ViewConfig.LIMIT;//number of results to show in table at per page.
$scope.skip = 0; //number of results to skip.
/*
* Handler for when pagination page is changed.
*/
$scope.onPageSelected = function () {
var mult;
mult = ($scope.pageIndex > 0) ? $scope.pageIndex - 1 : 0;
$scope.skip = $scope.limit * mult;
$scope.refreshTopics($scope.skip, $scope.limit);
};
$scope.setStateAndRefresh = function (state) {
$scope.state = state;
$scope.refreshTopics();
};
$scope.refreshTopics = function () {
var state, sortBy, sortDirection;
if ($scope.state !== "ALL") {
state = $scope.state;
}
if ($scope.sort && $scope.sort.currentColumn !== "") {
sortBy = $scope.sort.currentColumn;
sortDirection = ($scope.sort.descending) ? "descending" : "ascending";
}
$scope.model.getTopics($scope.skip, $scope.limit, state, sortBy, sortDirection)
.then(function (result) {
scope.topics = result.topics;
$scope.length = result.totalCount;
});
};
$scope.getTitle = function (state) {
var title = " Query Topics";
return (role === roles.ROLE2) ?
($scope.state + title) : title;
};
//method wrapper for scope resolution in modal context.
function requestNewTopic(topic) {
if (model.requestNewTopic !== undefined) {
return model.requestNewTopic(topic);
}
}
//method wrapper for scope resolution in modal context.
function refreshTopics() {
return $scope.refreshTopics();
}
$scope.createTopic = function () {
var model = $scope.model;
var modalInstance = $modal.open({
animation: true,
templateUrl: 'src/app/dashboard/topics/new-topic/new-topic.tpl.html',
controller: function ($scope, $modalInstance) {
$scope.newTopic = {name: "", description: ""};
$scope.ok = function () {
var name = $scope.newTopic.name,
description = $scope.newTopic.description;
requestNewTopic({"name": name, "description": description})
.then(function () {
refreshTopics();
$modalInstance.close();
});
};
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
},
//size: undefined,
resolve: {
model: model,
refreshTopics: $scope.refreshTopics
}
});
};
$scope.open = function (topic) {
var modalInstance = $modal.open({
animation: true,
templateUrl: 'src/app/dashboard/topics/topic-table/topic-detail.tpl.html',
controller: function ($scope, $modalInstance, topic, $app, Role2TopicDetailMdl, Role1TopicDetailMdl){
$scope.roles = $app.globals.UserRoles;
$scope.userRole = $app.globals.currentUser.roles[0];
$scope.topic = topic;
$scope.tabState = 'description';
$scope.formatDate = $app.utils.utcToMMDDYYYY;
$scope.loadedState = $scope.topic.state;
$scope.topicName = topic.name;
$scope.topicDescription = topic.description;
$scope.data = {topicState: topic.state};
$scope.ok = function (id) {
$scope.topic.state = $scope.data.topicState;
if ($scope.topic.state === "Pending") {
$modalInstance.close($scope.topic);
return;
}
(($scope.topic.state == "Approved") ?
Role2TopicDetailMdl.approveTopic(id) :
Role2TopicDetailMdl.rejectTopic(id))
.then(function (result) {
refreshTopics();
$modalInstance.close(result);
});
};
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
$scope.isEditable = function () {
return ($app.globals.currentUser.roles[0] === $scope.roles.ROLE2) || ($scope.topic.state === "Pending");
};
$scope.setState = function (state) {
if ($scope.isEditable() === true) {
$scope.tabState = state;
}
};
$scope.update = function (id, name, description) {
Role1TopicDetailMdl.updateTopic(id, name, description)
.then( function (result) {
refreshTopics();
$modalInstance.close(result);
});
};
$scope.canViewHistory = function () {
- var canView = ($app.globals.currentUser.roles[0] === $scope.roles.ROLE2 && $scope.loadedState !== "Pending") || ($scope.loadedState === "Approved");
+ var canView = (($app.globals.currentUser.roles[0] === $scope.roles.ROLE2 && $scope.loadedState !== "Pending") || ($scope.loadedState === "Approved")) && $scope.tabState != 'edit';
return canView;
};
},
//size: undefined,
resolve: {
topic: function () {
return topic;
}
}
});
};
$scope.setStateAndRefresh(initState);
})
.directive("topicTable", function () {
return {
restrict: "E",
templateUrl: "src/app/dashboard/topics/topic-table/topic-table.tpl.html",
replace: true
};
})
.directive("role1Description", function () {
return {
restrict: "E",
templateUrl: "src/app/dashboard/topics/topic-table/topic-detail/role1-description.tpl.html",
replace: true
};
})
.directive("role2Description", function () {
return {
restrict: "E",
templateUrl: "src/app/dashboard/topics/topic-table/topic-detail/role2-description.tpl.html",
replace: true
};
})
.directive("role1Edit", function () {
return {
restrict: "E",
templateUrl: "src/app/dashboard/topics/topic-table/topic-detail/role1-edit.tpl.html",
replace: true
};
})
.directive("role2Edit", function () {
return {
restrict: "E",
templateUrl: "src/app/dashboard/topics/topic-table/topic-detail/role2-edit.tpl.html",
replace: true
};
});
diff --git a/apps/steward-app/src/main/js/src/assets/css/shrine.css b/apps/steward-app/src/main/js/src/assets/css/shrine.css
index 464c8e591..0bccff261 100644
--- a/apps/steward-app/src/main/js/src/assets/css/shrine.css
+++ b/apps/steward-app/src/main/js/src/assets/css/shrine.css
@@ -1,712 +1,768 @@
-
@font-face {
- font-family: "Nexa";
- src: url('../fnt/nexa/Nexa-Light.otf');
+ font-family: 'Nexa';
+ src: url('../fnt/nexa/nexa-light-webfont.eot');
+ src: url('../fnt/nexa/nexa-light-webfont.eot?#iefix') format('embedded-opentype'),
+ url('../fnt/nexa/nexa-light-webfont.woff2') format('woff2'),
+ url('../fnt/nexa/nexa-light-webfont.woff') format('woff'),
+ url('../fnt/nexa/nexa-light-webfont.ttf') format('truetype'),
+ url('../fnt/nexa/nexa-light-webfont.svg#nexa_lightregular') format('svg');
+ font-weight: normal;
+ font-style: normal;
}
+
+
+
@font-face {
- font-family: "Nexa Bold";
- src: url('../fnt/nexa/Nexa-Bold.otf');
+ font-family: 'Nexa Bold';
+ src: url('../fnt/nexa/nexa-bold-webfont.eot');
+ src: url('../fnt/nexa/nexa-bold-webfont.eot?#iefix') format('embedded-opentype'),
+ url('../fnt/nexa/nexa-bold-webfont.woff2') format('woff2'),
+ url('../fnt/nexa/nexa-bold-webfont.woff') format('woff'),
+ url('../fnt/nexa/nexa-bold-webfont.ttf') format('truetype'),
+ url('../fnt/nexa/nexa-bold-webfont.svg#nexa_boldregular') format('svg');
+ font-weight: normal;
+ font-style: normal;
+
}
+
@font-face {
- font-family: "Open Sans Semibold";
- src: url('../fnt/open-sans/OpenSans-Semibold.ttf');
+ font-family: 'Open Sans Regular';
+ src: url('../fnt/open-sans/opensans-regular-webfont.eot');
+ src: url('../fnt/open-sans/opensans-regular-webfont.eot?#iefix') format('embedded-opentype'),
+ url('../fnt/open-sans/opensans-regular-webfont.woff2') format('woff2'),
+ url('../fnt/open-sans/opensans-regular-webfont.woff') format('woff'),
+ url('../fnt/open-sans/opensans-regular-webfont.ttf') format('truetype'),
+ url('../fnt/open-sans/opensans-regular-webfont.svg#open_sansregular') format('svg');
+ font-weight: normal;
+ font-style: normal;
+
}
+
+
+
@font-face {
- font-family: "Open Sans Regular";
- src: url('../fnt/open-sans/OpenSans-Regular.ttf');
+ font-family: 'Open Sans Semibold';
+ src: url('../fnt/open-sans/opensans-semibold-webfont.eot');
+ src: url('../fnt/open-sans/opensans-semibold-webfont.eot?#iefix') format('embedded-opentype'),
+ url('../fnt/open-sans/opensans-semibold-webfont.woff2') format('woff2'),
+ url('../fnt/open-sans/opensans-semibold-webfont.woff') format('woff'),
+ url('../fnt/open-sans/opensans-semibold-webfont.ttf') format('truetype'),
+ url('../fnt/open-sans/opensans-semibold-webfont.svg#open_sanssemibold') format('svg');
+ font-weight: normal;
+ font-style: normal;
+
}
-@font-face{
- font-family: "Roboto Bold";
- src: url('../fnt/roboto/Roboto-Bold.ttf');
+@font-face {
+ font-family: 'Roboto Bold';
+ src: url('../fnt/roboto/roboto-bold-webfont.eot');
+ src: url('../fnt/roboto/roboto-bold-webfont.eot?#iefix') format('embedded-opentype'),
+ url('../fnt/roboto/roboto-bold-webfont.woff2') format('woff2'),
+ url('../fnt/roboto/roboto-bold-webfont.woff') format('woff'),
+ url('../fnt/roboto/roboto-bold-webfont.ttf') format('truetype'),
+ url('../fnt/roboto/roboto-bold-webfont.svg#robotobold') format('svg');
+ font-weight: normal;
+ font-style: normal;
+
}
-@font-face{
- font-family: "Roboto Regular";
- src: url('../fnt/roboto/Roboto-Regular.ttf');
+
+
+
+@font-face {
+ font-family: 'Roboto Regular';
+ src: url('../fnt/roboto/roboto-regular-webfont.eot');
+ src: url('../fnt/roboto/roboto-regular-webfont.eot?#iefix') format('embedded-opentype'),
+ url('../fnt/roboto/roboto-regular-webfont.woff2') format('woff2'),
+ url('../fnt/roboto/roboto-regular-webfont.woff') format('woff'),
+ url('../fnt/roboto/roboto-regular-webfont.ttf') format('truetype'),
+ url('../fnt/roboto/roboto-regular-webfont.svg#robotoregular') format('svg');
+ font-weight: normal;
+ font-style: normal;
+
}
.main-app{
background-image: url('../img/Background_Shrine.jpg');
background-size: cover;
background-color: transparent;
background-attachment: fixed;
width: 100%;
height:100%;
width: calc(100vw);
height: calc(100vh);
min-width: 100%;
min-height: 100%;
}
.shrine-navbar {
background-color: rgba(255, 255, 255, 0.62);
border-color: transparent;
font-family: "Nexa";
color: #5d5d5d;
min-height: 60px;
width: 100%;
height: 4.8em;
}
.shrine-brand {
float: left;
padding: 22px 15px;
font-size: 30px;
line-height: 30px;
height: 30px;
}
.shrine-brand strong {
font-family: "Nexa Bold";
color: #2c5566;
}
.shrine-navbar .shrine-institution-logo {
background-image: url('/static/logo.png');
background-size: contain;
background-color: transparent;
background-color: rgba(255,255,255,0.1);
background-repeat: no-repeat;
background-position: right top;
margin: 5px;
width: 4em;
height: 4em;
max-height: 4em;
max-width: 4em;
}
.shrine-button {
cursor: pointer !important;
background-color: transparent;
border: none;
}
.shrine-button span {
position: relative;
bottom: 5px;
}
.shrine-btn-default {
margin-right: 6px;
border:none;
}
.shrine-btn-on {
padding: 12px 12px;
border-radius: 0;
font-family: "Roboto Bold" !important;
color: #FFFFFF !important;
background:
linear-gradient(rgba(4, 141, 190, .80), rgba(2, 89, 120, .80)),
url('../img/bckg_diagonal_lines_no_border.png') !important;
}
.shrine-btn-off {
padding: 6px 12px !important;
border-radius: 4px !important;
font-family: "Open Sans Semibold" !important;
background-color: #8896A4 !important;
color: #FFFFFF !important;
}
.shrine-on, .shrine-on a {
font-family: "Roboto Bold" !important;
color: #FFFFFF !important;
background: linear-gradient(#048DBE, #025978) !important;
}
.shrine-off {
font-family: "Roboto Regular" !important;
color: #2C5566 !important;
background-color: #ECEEEE !important;
}
.shrine-button.disabled, .shrine-button[disabled] {
cursor: default !important;
opacity: 0.2 !important;
}
.shrine-copy-bold {
font-family: "Nexa Bold";
color: #64818e;
}
.shrine-copy {
font-family: "Nexa";
color: #000000;
}
.row {
margin-right: 0;
margin-left: 0;
}
td.error, span.error {
color: red;
}
td.error a, td.error a:hover, span.error a, span.error a:hover{
color: inherit;
text-decoration: underline !important;
cursor: pointer;
}
td.ok, span.ok {
color:green;
}
.form-group span {
font-family: "Open Sans Semibold";
color: #2c5566;
}
fieldset button {
color: #2E5366;
}
fieldset button:hover, form a:hover {
color: #008CBA;
text-decoration: none;
cursor: pointer;
}
form a {
font-family: "Open Sans Regular";
color: #647d8d;
text-decoration: none;
}
footer img {
margin-left: 10px;
margin-top: 2px;
}
footer {
background-color: rgba(50, 62, 74, 0.48);
position: fixed;
bottom: 0;
left: 0;
width: 100%;
height: 83px;
min-height: 83px;
max-width: 100%;
}
table {
background-image: url('../img/bckg_diagonal_lines_no_border.png');
border: 1px solid #CCD8DF;
}
.table tr>td:first-child {
width: 20%;
min-width: 140px;
}
.table tr>td.thin-col {
width: 6%;
min-width: 35px;
}
.table-striped>tbody>tr:nth-of-type(odd) {
background-color: #EFF6F9;
}
.table-striped>tbody>tr:nth-of-type(even) {
background-color: #FFFFFF;
}
thead tr{
border: 1px solid #CCD8DF;
}
td{
border-right: 1px solid #CCD8DF;
overflow: hidden;
max-width: 450px;
word-wrap: break-word;
}
thead tr td, thead tr td label, tfoot tr td span{
font-family: "Open Sans Semibold";
color: #003153;
}
td a, td a:hover{
text-decoration: none !important;
cursor: pointer;
font-family: "Open Sans Semibold";
color: #003153;
}
.shrine-panel{
background-image: url('../img/bckg_diagonal_lines.png');
background-size: 100% 100%;
padding-right: 20px;
padding-left: 20px;
padding-top: 30px;
padding-bottom: 30px;
}
/*!
* Start Bootstrap - SB Admin 2 Bootstrap Admin Theme (http://startbootstrap.com)
* Code licensed under the Apache License v2.0.
* For details, see http://www.apache.org/licenses/LICENSE-2.0.
*/
body {
background-image: url('../img/Background_Shrine.jpg');
background-repeat:no-repeat;
background-size:100% 100%;
}
#wrapper {
width: 100%;
margin-bottom: 83px; /* clearance space for footer at bottom of long page */
}
.login-wrapper{
margin-top: 51px;
margin-right: 20px;
}
#page-wrapper {
background-color: transparent;
margin-top: 54px;
margin-right: 20px;
}
.navbar-top-links li {
display: inline-block;
}
.navbar-top-links li:last-child {
margin-right: 15px;
}
.navbar-top-links li a {
padding: 15px;
min-height: 50px;
font-family: "Open Sans Semibold";
color: #2c5566;
}
.navbar-top-links .dropdown-menu li {
font-family: "Open Sans Semibold";
color: #2c5566;
display: block;
}
.navbar-top-links .dropdown-menu li:last-child {
margin-right: 0;
}
.navbar-top-links .dropdown-menu li a {
padding: 3px 20px;
min-height: 0;
}
.navbar-top-links .dropdown-menu li a div {
white-space: normal;
}
.navbar-top-links .dropdown-messages,
.navbar-top-links .dropdown-tasks,
.navbar-top-links .dropdown-alerts {
width: 310px;
min-width: 0;
}
.navbar-top-links .dropdown-messages {
margin-left: 5px;
}
.navbar-top-links .dropdown-tasks {
margin-left: -59px;
}
.navbar-top-links .dropdown-alerts {
margin-left: -123px;
}
.navbar-top-links{
right: 0;
left: auto;
}
.sidebar .sidebar-nav.navbar-collapse {
padding-right: 0;
padding-left: 0;
}
.sidebar .sidebar-search {
padding: 15px;
}
.sidebar ul li {
border-bottom: 1px solid #e7e7e7;
}
.sidebar ul li a.active {
background-color: #eee;
}
.sidebar .arrow {
float: right;
}
.sidebar .fa.arrow:before {
content: "\f104";
}
.sidebar .active>a>.fa.arrow:before {
content: "\f107";
}
.sidebar .nav-second-level li,
.sidebar .nav-third-level li {
border-bottom: 0!important;
}
.sidebar .nav-second-level li a {
padding-left: 37px;
}
.sidebar .nav-third-level li a {
padding-left: 52px;
}
@media(min-width:768px) {
.sidebar {
z-index: 1;
margin-top: 51px;
}
.navbar-top-links .dropdown-messages,
.navbar-top-links .dropdown-tasks,
.navbar-top-links .dropdown-alerts {
margin-left: auto;
}
}
.btn-outline {
color: inherit;
background-color: transparent;
transition: all .5s;
}
.btn-primary.btn-outline {
color: #428bca;
}
.btn-success.btn-outline {
color: #5cb85c;
}
.btn-info.btn-outline {
color: #5bc0de;
}
.btn-warning.btn-outline {
color: #f0ad4e;
}
.btn-danger.btn-outline {
color: #d9534f;
}
.btn-primary.btn-outline:hover,
.btn-success.btn-outline:hover,
.btn-info.btn-outline:hover,
.btn-warning.btn-outline:hover,
.btn-danger.btn-outline:hover {
color: #fff;
}
.chat {
margin: 0;
padding: 0;
list-style: none;
}
.chat li {
margin-bottom: 10px;
padding-bottom: 5px;
border-bottom: 1px dotted #999;
}
.chat li.left .chat-body {
margin-left: 60px;
}
.chat li.right .chat-body {
margin-right: 60px;
}
.chat li .chat-body p {
margin: 0;
}
.panel .slidedown .glyphicon,
.chat .glyphicon {
margin-right: 5px;
}
.chat-panel .panel-body {
height: 350px;
overflow-y: scroll;
}
.login-panel {
margin-top: 25%;
}
.flot-chart {
display: block;
height: 400px;
}
.flot-chart-content {
width: 100%;
height: 100%;
}
.dataTables_wrapper {
position: relative;
clear: both;
}
table.dataTable thead .sorting,
table.dataTable thead .sorting_asc,
table.dataTable thead .sorting_desc,
table.dataTable thead .sorting_asc_disabled,
table.dataTable thead .sorting_desc_disabled {
background: 0 0;
}
table.dataTable thead .sorting_asc:after {
content: "\f0de";
float: right;
font-family: fontawesome;
}
table.dataTable thead .sorting_desc:after {
content: "\f0dd";
float: right;
font-family: fontawesome;
}
table.dataTable thead .sorting:after {
content: "\f0dc";
float: right;
font-family: fontawesome;
color: rgba(50,50,50,.5);
}
.btn-circle {
width: 30px;
height: 30px;
padding: 6px 0;
border-radius: 15px;
text-align: center;
font-size: 12px;
line-height: 1.428571429;
}
.btn-circle.btn-lg {
width: 50px;
height: 50px;
padding: 10px 16px;
border-radius: 25px;
font-size: 18px;
line-height: 1.33;
}
.btn-circle.btn-xl {
width: 70px;
height: 70px;
padding: 10px 16px;
border-radius: 35px;
font-size: 24px;
line-height: 1.33;
}
.show-grid [class^=col-] {
padding-top: 10px;
padding-bottom: 10px;
border: 1px solid #ddd;
background-color: #eee!important;
}
.show-grid {
margin: 15px 0;
}
.huge {
font-size: 40px;
}
.panel-green {
border-color: #5cb85c;
}
.panel-green .panel-heading {
border-color: #5cb85c;
color: #fff;
background-color: #5cb85c;
}
.panel-green a {
color: #5cb85c;
}
.panel-green a:hover {
color: #3d8b3d;
}
.panel-red {
border-color: #d9534f;
}
.panel-red .panel-heading {
border-color: #d9534f;
color: #fff;
background-color: #d9534f;
}
.panel-red a {
color: #d9534f;
}
.panel-red a:hover {
color: #b52b27;
}
.panel-yellow {
border-color: #f0ad4e;
}
.panel-yellow .panel-heading {
border-color: #f0ad4e;
color: #fff;
background-color: #f0ad4e;
}
.panel-yellow a {
color: #f0ad4e;
}
.panel-yellow a:hover {
color: #df8a13;
}
.modal-content{
border: none;
}
.shrine-modal{
background-color: white;
border: 1px solid #2c5566;
font-family: "Open Sans Semibold";
color: #2e5366;
padding: 15px;
}
.shrine-modal form div.col-sm-12{
border: 1px solid rgba(2, 89, 120, .80);
}
.shrine-modal input, .shrine-modal textarea{
border-radius: 0px;
border: 1px solid #2c5566;
}
.shrine-modal span{
font-family: "Nexa Bold";
color: #2e5366;
}
.shrine-modal span:hover{
font-family: "Nexa Bold";
color: #008CBA;
}
.shrine-modal button{
background-color: white;
border: none;
font-family: "Nexa Bold";
color: #2e5366;
}
.shrine-modal button span{
position: relative;
bottom: 6px;
}
.shrine-modal button:hover, .btn-success{
font-family: "Nexa Bold";
color: #008CBA;
background-color: transparent;
border: none;
}
.shrine-login{
margin-top: 8%;
margin-left: 1%;
}
.shrine-content{
overflow: auto;
}
/*Fix for resizeable text area.*/
textarea{
resize: none;
}
@media (min-width: 768px) {
.shrine-content {
padding: 0;
}
}
.shrine-calendar-input{
margin-right: 1px;
max-width:50%;
}
i.shrine-close{
float:right;
margin-top:-40px;
margin-right:-40px;
cursor:pointer;
color: #fff;
border: 2px solid #C8CED1;
border-radius: 30px;
background: #8896a4;
font-size: 31px;
font-weight: normal;
display: inline-block;
line-height: 0px;
padding: 11px 3px;
font-style:normal;
}
i.shrine-close:hover{
background: #008cba;
}
.shrine-close:before {
content: "×";
}
diff --git a/apps/steward-app/src/main/js/src/assets/fnt/nexa/nexa-bold-webfont.eot b/apps/steward-app/src/main/js/src/assets/fnt/nexa/nexa-bold-webfont.eot
new file mode 100755
index 000000000..d6058b9ec
Binary files /dev/null and b/apps/steward-app/src/main/js/src/assets/fnt/nexa/nexa-bold-webfont.eot differ
diff --git a/apps/steward-app/src/main/js/src/assets/fnt/nexa/nexa-bold-webfont.svg b/apps/steward-app/src/main/js/src/assets/fnt/nexa/nexa-bold-webfont.svg
new file mode 100755
index 000000000..56565994f
--- /dev/null
+++ b/apps/steward-app/src/main/js/src/assets/fnt/nexa/nexa-bold-webfont.svg
@@ -0,0 +1,1816 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/apps/steward-app/src/main/js/src/assets/fnt/nexa/nexa-bold-webfont.ttf b/apps/steward-app/src/main/js/src/assets/fnt/nexa/nexa-bold-webfont.ttf
new file mode 100755
index 000000000..31219f311
Binary files /dev/null and b/apps/steward-app/src/main/js/src/assets/fnt/nexa/nexa-bold-webfont.ttf differ
diff --git a/apps/steward-app/src/main/js/src/assets/fnt/nexa/nexa-bold-webfont.woff b/apps/steward-app/src/main/js/src/assets/fnt/nexa/nexa-bold-webfont.woff
new file mode 100755
index 000000000..f549acc4a
Binary files /dev/null and b/apps/steward-app/src/main/js/src/assets/fnt/nexa/nexa-bold-webfont.woff differ
diff --git a/apps/steward-app/src/main/js/src/assets/fnt/nexa/nexa-bold-webfont.woff2 b/apps/steward-app/src/main/js/src/assets/fnt/nexa/nexa-bold-webfont.woff2
new file mode 100755
index 000000000..91090b249
Binary files /dev/null and b/apps/steward-app/src/main/js/src/assets/fnt/nexa/nexa-bold-webfont.woff2 differ
diff --git a/apps/steward-app/src/main/js/src/assets/fnt/nexa/nexa-light-webfont.eot b/apps/steward-app/src/main/js/src/assets/fnt/nexa/nexa-light-webfont.eot
new file mode 100755
index 000000000..7c4a0b82f
Binary files /dev/null and b/apps/steward-app/src/main/js/src/assets/fnt/nexa/nexa-light-webfont.eot differ
diff --git a/apps/steward-app/src/main/js/src/assets/fnt/nexa/nexa-light-webfont.svg b/apps/steward-app/src/main/js/src/assets/fnt/nexa/nexa-light-webfont.svg
new file mode 100755
index 000000000..68dcda1ca
--- /dev/null
+++ b/apps/steward-app/src/main/js/src/assets/fnt/nexa/nexa-light-webfont.svg
@@ -0,0 +1,1821 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/apps/steward-app/src/main/js/src/assets/fnt/nexa/nexa-light-webfont.ttf b/apps/steward-app/src/main/js/src/assets/fnt/nexa/nexa-light-webfont.ttf
new file mode 100755
index 000000000..1b6860d22
Binary files /dev/null and b/apps/steward-app/src/main/js/src/assets/fnt/nexa/nexa-light-webfont.ttf differ
diff --git a/apps/steward-app/src/main/js/src/assets/fnt/nexa/nexa-light-webfont.woff b/apps/steward-app/src/main/js/src/assets/fnt/nexa/nexa-light-webfont.woff
new file mode 100755
index 000000000..2530ad431
Binary files /dev/null and b/apps/steward-app/src/main/js/src/assets/fnt/nexa/nexa-light-webfont.woff differ
diff --git a/apps/steward-app/src/main/js/src/assets/fnt/nexa/nexa-light-webfont.woff2 b/apps/steward-app/src/main/js/src/assets/fnt/nexa/nexa-light-webfont.woff2
new file mode 100755
index 000000000..130a1b12f
Binary files /dev/null and b/apps/steward-app/src/main/js/src/assets/fnt/nexa/nexa-light-webfont.woff2 differ
diff --git a/apps/steward-app/src/main/js/src/assets/fnt/open-sans/opensans-regular-webfont.eot b/apps/steward-app/src/main/js/src/assets/fnt/open-sans/opensans-regular-webfont.eot
new file mode 100755
index 000000000..a7979b7aa
Binary files /dev/null and b/apps/steward-app/src/main/js/src/assets/fnt/open-sans/opensans-regular-webfont.eot differ
diff --git a/apps/steward-app/src/main/js/src/assets/fnt/open-sans/opensans-regular-webfont.svg b/apps/steward-app/src/main/js/src/assets/fnt/open-sans/opensans-regular-webfont.svg
new file mode 100755
index 000000000..a169e01ae
--- /dev/null
+++ b/apps/steward-app/src/main/js/src/assets/fnt/open-sans/opensans-regular-webfont.svg
@@ -0,0 +1,1824 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/apps/steward-app/src/main/js/src/assets/fnt/open-sans/opensans-regular-webfont.ttf b/apps/steward-app/src/main/js/src/assets/fnt/open-sans/opensans-regular-webfont.ttf
new file mode 100755
index 000000000..2026a19b1
Binary files /dev/null and b/apps/steward-app/src/main/js/src/assets/fnt/open-sans/opensans-regular-webfont.ttf differ
diff --git a/apps/steward-app/src/main/js/src/assets/fnt/open-sans/opensans-regular-webfont.woff b/apps/steward-app/src/main/js/src/assets/fnt/open-sans/opensans-regular-webfont.woff
new file mode 100755
index 000000000..b8d862024
Binary files /dev/null and b/apps/steward-app/src/main/js/src/assets/fnt/open-sans/opensans-regular-webfont.woff differ
diff --git a/apps/steward-app/src/main/js/src/assets/fnt/open-sans/opensans-regular-webfont.woff2 b/apps/steward-app/src/main/js/src/assets/fnt/open-sans/opensans-regular-webfont.woff2
new file mode 100755
index 000000000..981deb62c
Binary files /dev/null and b/apps/steward-app/src/main/js/src/assets/fnt/open-sans/opensans-regular-webfont.woff2 differ
diff --git a/apps/steward-app/src/main/js/src/assets/fnt/open-sans/opensans-semibold-webfont.eot b/apps/steward-app/src/main/js/src/assets/fnt/open-sans/opensans-semibold-webfont.eot
new file mode 100755
index 000000000..a17aea1b3
Binary files /dev/null and b/apps/steward-app/src/main/js/src/assets/fnt/open-sans/opensans-semibold-webfont.eot differ
diff --git a/apps/steward-app/src/main/js/src/assets/fnt/open-sans/opensans-semibold-webfont.svg b/apps/steward-app/src/main/js/src/assets/fnt/open-sans/opensans-semibold-webfont.svg
new file mode 100755
index 000000000..2704ab443
--- /dev/null
+++ b/apps/steward-app/src/main/js/src/assets/fnt/open-sans/opensans-semibold-webfont.svg
@@ -0,0 +1,1824 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/apps/steward-app/src/main/js/src/assets/fnt/open-sans/opensans-semibold-webfont.ttf b/apps/steward-app/src/main/js/src/assets/fnt/open-sans/opensans-semibold-webfont.ttf
new file mode 100755
index 000000000..c64ef3027
Binary files /dev/null and b/apps/steward-app/src/main/js/src/assets/fnt/open-sans/opensans-semibold-webfont.ttf differ
diff --git a/apps/steward-app/src/main/js/src/assets/fnt/open-sans/opensans-semibold-webfont.woff b/apps/steward-app/src/main/js/src/assets/fnt/open-sans/opensans-semibold-webfont.woff
new file mode 100755
index 000000000..691ae5834
Binary files /dev/null and b/apps/steward-app/src/main/js/src/assets/fnt/open-sans/opensans-semibold-webfont.woff differ
diff --git a/apps/steward-app/src/main/js/src/assets/fnt/open-sans/opensans-semibold-webfont.woff2 b/apps/steward-app/src/main/js/src/assets/fnt/open-sans/opensans-semibold-webfont.woff2
new file mode 100755
index 000000000..74ad0e1b2
Binary files /dev/null and b/apps/steward-app/src/main/js/src/assets/fnt/open-sans/opensans-semibold-webfont.woff2 differ
diff --git a/apps/steward-app/src/main/js/src/assets/fnt/roboto/roboto-bold-webfont.eot b/apps/steward-app/src/main/js/src/assets/fnt/roboto/roboto-bold-webfont.eot
new file mode 100755
index 000000000..42e48bc4a
Binary files /dev/null and b/apps/steward-app/src/main/js/src/assets/fnt/roboto/roboto-bold-webfont.eot differ
diff --git a/apps/steward-app/src/main/js/src/assets/fnt/roboto/roboto-bold-webfont.svg b/apps/steward-app/src/main/js/src/assets/fnt/roboto/roboto-bold-webfont.svg
new file mode 100755
index 000000000..c9576fe24
--- /dev/null
+++ b/apps/steward-app/src/main/js/src/assets/fnt/roboto/roboto-bold-webfont.svg
@@ -0,0 +1,675 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/apps/steward-app/src/main/js/src/assets/fnt/roboto/roboto-bold-webfont.ttf b/apps/steward-app/src/main/js/src/assets/fnt/roboto/roboto-bold-webfont.ttf
new file mode 100755
index 000000000..6bd6515c0
Binary files /dev/null and b/apps/steward-app/src/main/js/src/assets/fnt/roboto/roboto-bold-webfont.ttf differ
diff --git a/apps/steward-app/src/main/js/src/assets/fnt/roboto/roboto-bold-webfont.woff b/apps/steward-app/src/main/js/src/assets/fnt/roboto/roboto-bold-webfont.woff
new file mode 100755
index 000000000..8bc018fc0
Binary files /dev/null and b/apps/steward-app/src/main/js/src/assets/fnt/roboto/roboto-bold-webfont.woff differ
diff --git a/apps/steward-app/src/main/js/src/assets/fnt/roboto/roboto-bold-webfont.woff2 b/apps/steward-app/src/main/js/src/assets/fnt/roboto/roboto-bold-webfont.woff2
new file mode 100755
index 000000000..7089ee8d7
Binary files /dev/null and b/apps/steward-app/src/main/js/src/assets/fnt/roboto/roboto-bold-webfont.woff2 differ
diff --git a/apps/steward-app/src/main/js/src/assets/fnt/roboto/roboto-regular-webfont.eot b/apps/steward-app/src/main/js/src/assets/fnt/roboto/roboto-regular-webfont.eot
new file mode 100755
index 000000000..c4ef976cf
Binary files /dev/null and b/apps/steward-app/src/main/js/src/assets/fnt/roboto/roboto-regular-webfont.eot differ
diff --git a/apps/steward-app/src/main/js/src/assets/fnt/roboto/roboto-regular-webfont.svg b/apps/steward-app/src/main/js/src/assets/fnt/roboto/roboto-regular-webfont.svg
new file mode 100755
index 000000000..fb6643454
--- /dev/null
+++ b/apps/steward-app/src/main/js/src/assets/fnt/roboto/roboto-regular-webfont.svg
@@ -0,0 +1,666 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/apps/steward-app/src/main/js/src/assets/fnt/roboto/roboto-regular-webfont.ttf b/apps/steward-app/src/main/js/src/assets/fnt/roboto/roboto-regular-webfont.ttf
new file mode 100755
index 000000000..e8e19531f
Binary files /dev/null and b/apps/steward-app/src/main/js/src/assets/fnt/roboto/roboto-regular-webfont.ttf differ
diff --git a/apps/steward-app/src/main/js/src/assets/fnt/roboto/roboto-regular-webfont.woff b/apps/steward-app/src/main/js/src/assets/fnt/roboto/roboto-regular-webfont.woff
new file mode 100755
index 000000000..c3aed8f8e
Binary files /dev/null and b/apps/steward-app/src/main/js/src/assets/fnt/roboto/roboto-regular-webfont.woff differ
diff --git a/apps/steward-app/src/main/js/src/assets/fnt/roboto/roboto-regular-webfont.woff2 b/apps/steward-app/src/main/js/src/assets/fnt/roboto/roboto-regular-webfont.woff2
new file mode 100755
index 000000000..9be30541d
Binary files /dev/null and b/apps/steward-app/src/main/js/src/assets/fnt/roboto/roboto-regular-webfont.woff2 differ
diff --git a/commons/auth/src/main/scala/net/shrine/authorization/PmAuthorizerComponent.scala b/commons/auth/src/main/scala/net/shrine/authorization/PmAuthorizerComponent.scala
index 299a349a4..cfcbbfe62 100644
--- a/commons/auth/src/main/scala/net/shrine/authorization/PmAuthorizerComponent.scala
+++ b/commons/auth/src/main/scala/net/shrine/authorization/PmAuthorizerComponent.scala
@@ -1,111 +1,111 @@
package net.shrine.authorization
import net.shrine.log.Loggable
import net.shrine.problem.{LoggingProblemHandler, Problem, ProblemSources, AbstractProblem, ProblemDigest}
import scala.util.{Failure, Success, Try}
import net.shrine.client.HttpResponse
import net.shrine.i2b2.protocol.pm.GetUserConfigurationRequest
import net.shrine.i2b2.protocol.pm.User
import net.shrine.protocol.AuthenticationInfo
import net.shrine.protocol.ErrorResponse
import scala.util.control.NonFatal
/**
* @author clint
* @since Apr 5, 2013
*/
trait PmAuthorizerComponent { self: PmHttpClientComponent with Loggable =>
import PmAuthorizerComponent._
//noinspection RedundantBlock
object Pm {
def parsePmResult(authn: AuthenticationInfo)(httpResponse: HttpResponse): Try[Either[ErrorResponse, User]] = {
User.fromI2b2(httpResponse.body).map(Right(_)).recoverWith {
case NonFatal(e) => {
debug(s"Couldn't extract a User from '$httpResponse'")
Try(Left(ErrorResponse.fromI2b2(httpResponse.body)))
}
}.recover {
case NonFatal(e) => {
val problem = CouldNotInterpretResponseFromPmCell(pmPoster.url,authn,httpResponse,e)
LoggingProblemHandler.handleProblem(problem)
Left(ErrorResponse(problem.summary,Some(problem)))
}
}
}
def authorize(projectId: String, neededRoles: Set[String], authn: AuthenticationInfo): AuthorizationStatus = {
val request = GetUserConfigurationRequest(authn)
val responseAttempt: Try[HttpResponse] = Try {
debug(s"Authorizing with PM cell at ${pmPoster.url}")
pmPoster.post(request.toI2b2String)
}
val authStatusAttempt: Try[AuthorizationStatus with Product with Serializable] = responseAttempt.flatMap(parsePmResult(authn)).map {
case Right(user) => {
val managerUserOption = for {
roles <- user.rolesByProject.get(projectId)
if neededRoles.forall(roles.contains)
} yield user
managerUserOption.map(Authorized).getOrElse {
NotAuthorized(MissingRequiredRoles(projectId,neededRoles,authn))
}
}
case Left(errorResponse) => {
//todo remove when ErrorResponse gets its message
info(s"ErrorResponse message '${errorResponse.errorMessage}' may not have carried through to the NotAuthorized object")
NotAuthorized(errorResponse.problemDigest)
}
}
authStatusAttempt match {
case Success(s) => s
case Failure(x) => NotAuthorized(CouldNotReachPmCell(pmPoster.url,authn,x))
}
}
}
}
object PmAuthorizerComponent {
sealed trait AuthorizationStatus
case class Authorized(user: User) extends AuthorizationStatus
case class NotAuthorized(problemDigest: ProblemDigest) extends AuthorizationStatus {
def toErrorResponse = ErrorResponse(problemDigest.summary,problemDigest)
}
object NotAuthorized {
def apply(problem:Problem):NotAuthorized = NotAuthorized(problem.toDigest)
}
}
case class MissingRequiredRoles(projectId: String, neededRoles: Set[String], authn: AuthenticationInfo) extends AbstractProblem(ProblemSources.Qep) {
override val summary: String = s"User ${authn.domain}:${authn.username} is missing roles in project '$projectId'"
override val description:String = s"User ${authn.domain}:${authn.username} does not have all the needed roles: ${neededRoles.map("'" + _ + "'").mkString(", ")} in the project '$projectId'"
}
case class CouldNotReachPmCell(pmUrl:String,authn: AuthenticationInfo,x:Throwable) extends AbstractProblem(ProblemSources.Qep) {
- override val throwable = Option(x)
- override val summary: String = s"Could not reach PM cell at $pmUrl for ${authn.domain}:${authn.username}"
+ override val throwable = Some(x)
+ override val summary: String = s"Could not reach PM cell."
override val description:String = s"Shrine encountered ${throwable.get} while attempting to reach the PM cell at $pmUrl for ${authn.domain}:${authn.username}."
}
case class CouldNotInterpretResponseFromPmCell(pmUrl:String,authn: AuthenticationInfo,httpResponse: HttpResponse,x:Throwable) extends AbstractProblem(ProblemSources.Qep) {
override val throwable = Some(x)
- override def summary: String = s"Could not interpret response from PM cell at ${pmUrl} for ${authn.domain}:${authn.username}"
+ override def summary: String = s"Could not interpret response from PM cell."
override def description: String = s"Shrine could not interpret the response from the PM cell at ${pmUrl} for ${authn.domain}:${authn.username}: due to ${throwable.get}"
override val detailsXml =
Response is {httpResponse}
{throwableDetail.getOrElse("")}
}
\ No newline at end of file
diff --git a/commons/util/src/main/scala/net/shrine/problem/Problem.scala b/commons/util/src/main/scala/net/shrine/problem/Problem.scala
index 3a9a6d9ce..9d3f91413 100644
--- a/commons/util/src/main/scala/net/shrine/problem/Problem.scala
+++ b/commons/util/src/main/scala/net/shrine/problem/Problem.scala
@@ -1,157 +1,166 @@
package net.shrine.problem
import java.net.InetAddress
import java.util.Date
import net.shrine.log.Loggable
import net.shrine.serialization.{XmlUnmarshaller, XmlMarshaller}
import scala.xml.{Elem, Node, NodeSeq}
/**
* Describes what information we have about a problem at the site in code where we discover it.
*
* @author david
* @since 8/6/15
*/
trait Problem {
def summary:String
def problemName = getClass.getName
def throwable:Option[Throwable] = None
def stamp:Stamp
def description:String
//todo stack trace as xml elements? would be easy
def exceptionXml(exception:Option[Throwable]): Option[Elem] = exception.map{x =>
{x.getClass.getName}
{x.getMessage}
{x.getStackTrace.map(line => {line} )}{exceptionXml(Option(x.getCause)).getOrElse("")}
}
def throwableDetail = exceptionXml(throwable)
def detailsXml: NodeSeq = NodeSeq.fromSeq(
{throwableDetail.getOrElse("")} )
def toDigest:ProblemDigest = ProblemDigest(problemName,stamp.pretty,summary,description,detailsXml)
}
case class ProblemDigest(codec: String, stampText: String, summary: String, description: String, detailsXml: NodeSeq) extends XmlMarshaller {
override def toXml: Node = {
{codec}
{stampText}
{summary}
{description}
{detailsXml}
}
/**
* Ignores detailXml. equals with scala.xml is impossible. See http://www.scala-lang.org/api/2.10.3/index.html#scala.xml.Equality$
*/
override def equals(other: Any): Boolean =
other match {
case that: ProblemDigest =>
(that canEqual this) &&
codec == that.codec &&
stampText == that.stampText &&
summary == that.summary &&
description == that.description
case _ => false
}
/**
* Ignores detailXml
*/
override def hashCode: Int = {
val prime = 67
codec.hashCode + prime * (stampText.hashCode + prime *(summary.hashCode + prime * description.hashCode))
}
}
object ProblemDigest extends XmlUnmarshaller[ProblemDigest] with Loggable {
override def fromXml(xml: NodeSeq): ProblemDigest = {
val problemNode = xml \ "problem"
require(problemNode.nonEmpty,s"No problem tag in $xml")
def extractText(tagName:String) = (problemNode \ tagName).text
val codec = extractText("codec")
val stampText = extractText("stamp")
val summary = extractText("summary")
val description = extractText("description")
val detailsXml: NodeSeq = problemNode \ "details"
ProblemDigest(codec,stampText,summary,description,detailsXml)
}
}
case class Stamp(host:InetAddress,time:Long,source:ProblemSources.ProblemSource) {
def pretty = s"${new Date(time)} on $host ${source.pretty}"
}
object Stamp {
def apply(source:ProblemSources.ProblemSource): Stamp = Stamp(InetAddress.getLocalHost,System.currentTimeMillis(),source)
}
abstract class AbstractProblem(source:ProblemSources.ProblemSource) extends Problem {
val stamp = Stamp(source)
}
trait ProblemHandler {
def handleProblem(problem:Problem)
}
/**
* An example problem handler
*/
object LoggingProblemHandler extends ProblemHandler with Loggable {
override def handleProblem(problem: Problem): Unit = {
problem.throwable.fold(error(problem.toString))(throwable =>
error(problem.toString,throwable)
)
}
}
object ProblemSources{
sealed trait ProblemSource {
def pretty = getClass.getSimpleName.dropRight(1)
}
case object Adapter extends ProblemSource
case object Hub extends ProblemSource
case object Qep extends ProblemSource
case object Dsa extends ProblemSource
case object Unknown extends ProblemSource
def problemSources = Set(Adapter,Hub,Qep,Dsa,Unknown)
}
+case class ProblemNotYetEncoded(internalSummary:String,t:Throwable) extends AbstractProblem(ProblemSources.Unknown){
+ override val summary = "An unanticipated problem encountered."
-case class ProblemNotYetEncoded(summary:String,t:Throwable) extends AbstractProblem(ProblemSources.Unknown){
override val throwable = Some(t)
- override val description = s"An unexpected problem has occurred. This problem has not yet been codified in Shrine."
+ override val description = "This problem is not yet classified in Shrine source code. Please report the details to the Shrine dev team."
+
+ override val detailsXml: NodeSeq = NodeSeq.fromSeq(
+
+ {internalSummary}
+ {throwableDetail.getOrElse("")}
+
+ )
+
}
object ProblemNotYetEncoded {
def apply(summary:String):ProblemNotYetEncoded = {
val x = new IllegalStateException(s"$summary , not yet codified in Shrine.")
x.fillInStackTrace()
new ProblemNotYetEncoded(summary,x)
}
}
diff --git a/hub/broadcaster-aggregator/src/main/scala/net/shrine/aggregation/BasicAggregator.scala b/hub/broadcaster-aggregator/src/main/scala/net/shrine/aggregation/BasicAggregator.scala
index 2953e3a30..b9563283c 100644
--- a/hub/broadcaster-aggregator/src/main/scala/net/shrine/aggregation/BasicAggregator.scala
+++ b/hub/broadcaster-aggregator/src/main/scala/net/shrine/aggregation/BasicAggregator.scala
@@ -1,125 +1,125 @@
package net.shrine.aggregation
import java.net.ConnectException
import net.shrine.broadcaster.CouldNotParseResultsException
import net.shrine.log.Loggable
import net.shrine.problem.{ProblemNotYetEncoded, ProblemSources, AbstractProblem}
import scala.concurrent.duration.Duration
import net.shrine.protocol.ErrorResponse
import net.shrine.protocol.Failure
import net.shrine.protocol.NodeId
import net.shrine.protocol.Result
import net.shrine.protocol.SingleNodeResult
import net.shrine.protocol.Timeout
import net.shrine.protocol.BaseShrineResponse
/**
*
* @author Clint Gilbert
* @since Sep 16, 2011
*
* @see http://cbmi.med.harvard.edu
*
* This software is licensed under the LGPL
* @see http://www.gnu.org/licenses/lgpl.html
*
* Represents the basic aggregation strategy shared by several aggregators:
* - Parses a sequence of SpinResultEntries into a sequence of some
* combination of valid responses, ErrorResponses, and invalid
* responses (cases where ShrineResponse.fromXml returns None)
* - Filters the valid responses, weeding out responses that aren't of
* the expected type
* Invokes an abstract method with the valid responses, errors, and
* invalid responses.
*
* Needs to be an abstract class instead of a trait due to the view bound on T (: Manifest)
*/
abstract class BasicAggregator[T <: BaseShrineResponse: Manifest] extends Aggregator with Loggable {
private[aggregation] def isAggregatable(response: BaseShrineResponse): Boolean = {
manifest[T].runtimeClass.isAssignableFrom(response.getClass)
}
import BasicAggregator._
override def aggregate(results: Iterable[SingleNodeResult], errors: Iterable[ErrorResponse]): BaseShrineResponse = {
val resultsOrErrors: Iterable[ParsedResult[T]] = {
for {
result <- results
} yield {
val parsedResponse: ParsedResult[T] = result match {
case Result(origin, _, errorResponse: ErrorResponse) => Error(Option(origin), errorResponse)
case Result(origin, elapsed, response: T) if isAggregatable(response) => Valid(origin, elapsed, response)
case Timeout(origin) => Error(Option(origin), ErrorResponse(s"Timed out querying node '${origin.name}'"))
//todo failure becomes an ErrorResponse and Error status type here. And the stack trace gets eaten.
case Failure(origin, cause) => {
cause match {
case cx: ConnectException => Error(Option(origin), ErrorResponse(CouldNotConnectToAdapter(origin, cx)))
case cnprx:CouldNotParseResultsException => {
if(cnprx.statusCode >= 400) Error(Option(origin), ErrorResponse(HttpErrorResponseProblem(cnprx)))
else Error(Option(origin), ErrorResponse(CouldNotParseResultsProblem(cnprx)))
}
case x => Error(Option(origin), ErrorResponse(ProblemNotYetEncoded(s"Failure querying node ${origin.name}",x)))
}
}
case _ => Invalid(None, s"Unexpected response in $getClass:\r\n $result")
}
parsedResponse
}
}
val invalidResponses = resultsOrErrors.collect { case invalid: Invalid => invalid }
val validResponses = resultsOrErrors.collect { case valid: Valid[T] => valid }
val errorResponses: Iterable[Error] = resultsOrErrors.collect { case error: Error => error }
//Log all parsing errors
invalidResponses.map(_.errorMessage).foreach(this.error(_))
val previouslyDetectedErrors = errors.map(Error(None, _))
makeResponseFrom(validResponses, errorResponses ++ previouslyDetectedErrors, invalidResponses)
}
private[aggregation] def makeResponseFrom(validResponses: Iterable[Valid[T]], errorResponses: Iterable[Error], invalidResponses: Iterable[Invalid]): BaseShrineResponse
}
object BasicAggregator {
private[aggregation] sealed abstract class ParsedResult[+T]
private[aggregation] final case class Valid[T](origin: NodeId, elapsed: Duration, response: T) extends ParsedResult[T]
private[aggregation] final case class Error(origin: Option[NodeId], response: ErrorResponse) extends ParsedResult[Nothing]
private[aggregation] final case class Invalid(origin: Option[NodeId], errorMessage: String) extends ParsedResult[Nothing]
}
case class CouldNotConnectToAdapter(origin:NodeId,cx:ConnectException) extends AbstractProblem(ProblemSources.Hub) {
override val throwable = Some(cx)
- override val summary: String = s"Shrine could not connect to the adapter at ${origin.name}."
- override val description: String = s"Shrine could not connect to the adapter at ${origin.name} due to ${throwable.get}"
+ override val summary: String = "Shrine could not connect to the adapter."
+ override val description: String = s"Shrine could not connect to the adapter at ${origin.name} due to ${throwable.get}."
}
case class CouldNotParseResultsProblem(cnrpx:CouldNotParseResultsException) extends AbstractProblem(ProblemSources.Hub) {
override val throwable = Some(cnrpx)
- override val summary: String = s"Caught a ${cnrpx.cause.getClass.getSimpleName} while parsing a response from ${cnrpx.url}"
+ override val summary: String = "Could not parse response."
override val description = s"While parsing a response from ${cnrpx.url} with http code ${cnrpx.statusCode} caught '${cnrpx.cause}'"
override val detailsXml =
Message body is {cnrpx.body}
{throwableDetail.getOrElse("")}
}
case class HttpErrorResponseProblem(cnrpx:CouldNotParseResultsException) extends AbstractProblem(ProblemSources.Hub) {
override val throwable = Some(cnrpx)
- override val summary: String = s"Observed ${cnrpx.statusCode} and caught a ${cnrpx.cause.getClass.getSimpleName} while parsing a response from ${cnrpx.url}"
- override val description = s"Observed http status code ${cnrpx.statusCode} from ${cnrpx.url} and caught '${cnrpx.cause}'"
+ override val summary: String = "Adapter error."
+ override val description = s"Observed http status code ${cnrpx.statusCode} from ${cnrpx.url} and caught ${cnrpx.cause}."
override val detailsXml =
Message body is {cnrpx.body}
{throwableDetail.getOrElse("")}
}
\ No newline at end of file
diff --git a/hub/broadcaster-aggregator/src/main/scala/net/shrine/aggregation/IgnoresErrorsAggregator.scala b/hub/broadcaster-aggregator/src/main/scala/net/shrine/aggregation/IgnoresErrorsAggregator.scala
index 53d723a0c..b6fa453d3 100644
--- a/hub/broadcaster-aggregator/src/main/scala/net/shrine/aggregation/IgnoresErrorsAggregator.scala
+++ b/hub/broadcaster-aggregator/src/main/scala/net/shrine/aggregation/IgnoresErrorsAggregator.scala
@@ -1,43 +1,43 @@
package net.shrine.aggregation
import net.shrine.aggregation.BasicAggregator.{Invalid, Error, Valid}
import net.shrine.problem.{ProblemSources, AbstractProblem}
import net.shrine.protocol.ErrorResponse
import net.shrine.protocol.BaseShrineResponse
/**
*
* @author Clint Gilbert
* @since Sep 16, 2011
*
* @see http://cbmi.med.harvard.edu
*
* This software is licensed under the LGPL
* @see http://www.gnu.org/licenses/lgpl.html
*
* Extends BasicAggregator to ignore Errors and Invalid responses
*
* Needs to be an abstract class instead of a trait due to the view bound on T (: Manifest)
*/
abstract class IgnoresErrorsAggregator[T <: BaseShrineResponse : Manifest] extends BasicAggregator[T] {
private[aggregation] override def makeResponseFrom(validResponses: Iterable[Valid[T]], errorResponses: Iterable[Error], invalidResponses: Iterable[Invalid]): BaseShrineResponse = {
//Filter out errors and invalid responses
makeResponseFrom(validResponses)
}
//Default implementation, just returns first valid response, or if there are none, an ErrorResponse
private[aggregation] def makeResponseFrom(validResponses: Iterable[Valid[T]]): BaseShrineResponse = {
validResponses.map(_.response).toSet.headOption.getOrElse{
val problem = NoValidResponsesToAggregate()
ErrorResponse(problem.summary,Some(problem))
}
}
}
case class NoValidResponsesToAggregate() extends AbstractProblem(ProblemSources.Hub) {
- override val summary: String = "No valid responses to aggregate"
+ override val summary: String = "No valid responses to aggregate."
- override val description:String = "The hub received no valid responses to aggregate"
+ override val description:String = "The hub received no valid responses to aggregate."
}
\ No newline at end of file
diff --git a/hub/broadcaster-aggregator/src/main/scala/net/shrine/aggregation/PackagesErrorsAggregator.scala b/hub/broadcaster-aggregator/src/main/scala/net/shrine/aggregation/PackagesErrorsAggregator.scala
index 60cc945c3..3af2986b7 100644
--- a/hub/broadcaster-aggregator/src/main/scala/net/shrine/aggregation/PackagesErrorsAggregator.scala
+++ b/hub/broadcaster-aggregator/src/main/scala/net/shrine/aggregation/PackagesErrorsAggregator.scala
@@ -1,55 +1,54 @@
package net.shrine.aggregation
import net.shrine.problem.{ProblemSources, AbstractProblem}
import net.shrine.protocol.ShrineResponse
import net.shrine.aggregation.BasicAggregator.{Invalid, Error, Valid}
import net.shrine.protocol.QueryResult
/**
*
* @author Clint Gilbert
* @since Sep 16, 2011
*
* @see http://cbmi.med.harvard.edu
*
* This software is licensed under the LGPL
* @see http://www.gnu.org/licenses/lgpl.html
*
* Extends BasicAggregator to package Errors and Invalid responses into QueryResults
*
* Needs to be an abstract class instead of a trait due to the view bound on T (: Manifest)
*/
abstract class PackagesErrorsAggregator[T <: ShrineResponse : Manifest](
errorMessage: Option[String] = None,
invalidMessage: Option[String] = None) extends BasicAggregator[T] {
private[aggregation] def makeErrorResult(error: Error): QueryResult = {
val Error(originOption, errorResponse) = error
//Use node name as the description, to avoid giving the web UI more data than it can display
val desc = originOption.map(_.name)
QueryResult.errorResult(desc, errorMessage.getOrElse(errorResponse.errorMessage),errorResponse.problemDigest)
}
private[aggregation] def makeInvalidResult(invalid: Invalid): QueryResult = {
val Invalid(originOption, errorMessage) = invalid
//Use node name as the description, to avoid giving the web UI more data than it can display
val desc = originOption.map(_.name)
QueryResult.errorResult(desc, invalidMessage.getOrElse(errorMessage),Option(InvalidResultProblem(invalid)))
}
private[aggregation] final override def makeResponseFrom(validResponses: Iterable[Valid[T]], errorResponses: Iterable[Error], invalidResponses: Iterable[Invalid]): ShrineResponse = {
makeResponse(validResponses, errorResponses.map(makeErrorResult), invalidResponses.map(makeInvalidResult))
}
private[aggregation] def makeResponse(validResponses: Iterable[Valid[T]], errorResponses: Iterable[QueryResult], invalidResponses: Iterable[QueryResult]): ShrineResponse
}
-//todo Problem these two should really propagate problems from the Error or the Invalid result
case class InvalidResultProblem(invalid:Invalid) extends AbstractProblem(ProblemSources.Hub) {
- override def summary: String = s"The hub received an invalid response from ${invalid.origin.getOrElse("an unknown node")}"
+ override def summary: String = s"Invalid response."
override def description: String = s"${invalid.errorMessage} from ${invalid.origin.getOrElse("an unknown node")}"
}
\ No newline at end of file