diff --git a/adapter/adapter-api/src/main/scala/net/shrine/adapter/client/RemoteAdapterClient.scala b/adapter/adapter-api/src/main/scala/net/shrine/adapter/client/RemoteAdapterClient.scala index 5fef4e5d6..bfe391c37 100644 --- a/adapter/adapter-api/src/main/scala/net/shrine/adapter/client/RemoteAdapterClient.scala +++ b/adapter/adapter-api/src/main/scala/net/shrine/adapter/client/RemoteAdapterClient.scala @@ -1,127 +1,127 @@ package net.shrine.adapter.client import java.net.SocketTimeoutException import net.shrine.problem.{ProblemNotYetEncoded, ProblemSources, AbstractProblem} import org.xml.sax.SAXParseException import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.Future import scala.concurrent.blocking import scala.concurrent.duration.Duration import scala.concurrent.duration.DurationInt import scala.util.control.NonFatal import scala.xml.{NodeSeq, XML} import com.sun.jersey.api.client.ClientHandlerException import net.shrine.client.{HttpResponse, TimeoutException, Poster} import net.shrine.protocol.BroadcastMessage import net.shrine.protocol.ErrorResponse import net.shrine.protocol.NodeId import net.shrine.protocol.Result import scala.util.{Failure, Success, Try} import net.shrine.protocol.ResultOutputType /** * @author clint * @since Nov 15, 2013 * * */ final class RemoteAdapterClient private (val poster: Poster, val breakdownTypes: Set[ResultOutputType]) extends AdapterClient { import RemoteAdapterClient._ //NB: Overriding apply in the companion object screws up case-class code generation for some reason, so //we add the would-have-been-generated methods here override def toString = s"RemoteAdapterClient($poster)" override def hashCode: Int = 31 * (if(poster == null) 1 else poster.hashCode) override def equals(other: Any): Boolean = other match { case that: RemoteAdapterClient if that != null => poster == that.poster case _ => false } //TODO: Revisit this import scala.concurrent.ExecutionContext.Implicits.global override def query(request: BroadcastMessage): Future[Result] = { val requestXml = request.toXml Future { blocking { val response: HttpResponse = poster.post(requestXml.toString()) interpretResponse(response) } }.recover { case e if isTimeout(e) => throw new TimeoutException(s"Invoking adapter at ${poster.url} timed out", e) } } def interpretResponse(response:HttpResponse):Result = { if(response.statusCode <= 400){ val responseXml = response.body import scala.concurrent.duration._ //Should we know the NodeID here? It would let us make a better error response. Try(XML.loadString(responseXml)).flatMap(Result.fromXml(breakdownTypes)) match { case Success(result) => result case Failure(x) => { val errorResponse = x match { case sx: SAXParseException => ErrorResponse(CouldNotParseXmlFromAdapter(poster.url,response.statusCode,responseXml,sx)) case _ => ErrorResponse(ProblemNotYetEncoded(s"Couldn't understand response from adapter at '${poster.url}': $responseXml", x)) } Result(NodeId.Unknown, 0.milliseconds, errorResponse) } } } else { Result(NodeId.Unknown,0.milliseconds,ErrorResponse(HttpErrorCodeFromAdapter(poster.url,response.statusCode,response.body))) } } } object RemoteAdapterClient { def apply(poster: Poster, breakdownTypes: Set[ResultOutputType]): RemoteAdapterClient = { //NB: Replicate URL-munging that used to be performed by JerseyAdapterClient val posterToUse = { if(poster.url.endsWith("requests")) { poster } else { poster.mapUrl(_ + "/requests") } } new RemoteAdapterClient(posterToUse, breakdownTypes) } def isTimeout(e: Throwable): Boolean = e match { case e: SocketTimeoutException => true case e: ClientHandlerException => { val cause = e.getCause cause != null && cause.isInstanceOf[SocketTimeoutException] } case _ => false } } case class HttpErrorCodeFromAdapter(url:String,statusCode:Int,responseBody:String) extends AbstractProblem(ProblemSources.Adapter) { - override def summary: String = s"Hub received error code $statusCode" + override def summary: String = "Hub received a fatal error response" - override def description: String = s"Hub received error code $statusCode from $url" + override def description: String = s"Hub received error code $statusCode from the adapter at $url" override def detailsXml:NodeSeq =
{s"Http response body was $responseBody"}
} case class CouldNotParseXmlFromAdapter(url:String,statusCode:Int,responseBody:String,saxx: SAXParseException) extends AbstractProblem(ProblemSources.Adapter) { override def throwable = Some(saxx) - override def summary: String = s"Hub could not parse xml from adapter" + override def summary: String = s"Hub could not parse response from adapter" override def description: String = s"Hub could not parse xml from $url due to ${saxx.toString}" override def detailsXml:NodeSeq =
{s"Http response code was $statusCode and the body was $responseBody"} {throwableDetail}
} \ No newline at end of file diff --git a/adapter/adapter-service/src/main/scala/net/shrine/adapter/AbstractReadQueryResultAdapter.scala b/adapter/adapter-service/src/main/scala/net/shrine/adapter/AbstractReadQueryResultAdapter.scala index 8b51f1bf4..c1bba1184 100644 --- a/adapter/adapter-service/src/main/scala/net/shrine/adapter/AbstractReadQueryResultAdapter.scala +++ b/adapter/adapter-service/src/main/scala/net/shrine/adapter/AbstractReadQueryResultAdapter.scala @@ -1,298 +1,298 @@ package net.shrine.adapter import java.util.concurrent.Executors import java.util.concurrent.TimeUnit import net.shrine.adapter.audit.AdapterAuditDb import net.shrine.problem.{AbstractProblem, ProblemSources} import scala.Option.option2Iterable import scala.concurrent.Await import scala.concurrent.ExecutionContext import scala.concurrent.Future import scala.util.Failure import scala.util.Success import scala.util.Try import scala.xml.NodeSeq import net.shrine.adapter.Obfuscator.obfuscateResults import net.shrine.adapter.dao.AdapterDao import net.shrine.adapter.dao.model.Breakdown import net.shrine.adapter.dao.model.ShrineQueryResult import net.shrine.protocol.{HiveCredentials, AuthenticationInfo, BroadcastMessage, ErrorResponse, HasQueryResults, QueryResult, ReadResultRequest, ReadResultResponse, ResultOutputType, ShrineRequest, ShrineResponse, BaseShrineRequest} import net.shrine.protocol.query.QueryDefinition import net.shrine.util.StackTrace import net.shrine.util.Tries.sequence import scala.concurrent.duration.Duration import net.shrine.client.Poster /** * @author clint * @since Nov 2, 2012 * */ object AbstractReadQueryResultAdapter { private final case class RawResponseAttempts(countResponseAttempt: Try[ReadResultResponse], breakdownResponseAttempts: Seq[Try[ReadResultResponse]]) private final case class SpecificResponseAttempts[R](responseAttempt: Try[R], breakdownResponseAttempts: Seq[Try[ReadResultResponse]]) } abstract class AbstractReadQueryResultAdapter[Req <: BaseShrineRequest, Rsp <: ShrineResponse with HasQueryResults]( poster: Poster, override val hiveCredentials: HiveCredentials, dao: AdapterDao, doObfuscation: Boolean, getQueryId: Req => Long, getProjectId: Req => String, toResponse: (Long, QueryResult) => Rsp, breakdownTypes: Set[ResultOutputType], collectAdapterAudit:Boolean ) extends WithHiveCredentialsAdapter(hiveCredentials) { //TODO: Make this configurable private val numThreads = math.max(5, Runtime.getRuntime.availableProcessors) //TODO: Use scala.concurrent.ExecutionContext.Implicits.global instead? private lazy val executorService = Executors.newFixedThreadPool(numThreads) private lazy val executionContext = ExecutionContext.fromExecutorService(executorService) override def shutdown() { try { executorService.shutdown() executorService.awaitTermination(5, TimeUnit.SECONDS) } finally { executorService.shutdownNow() super.shutdown() } } import AbstractReadQueryResultAdapter._ override protected[adapter] def processRequest(message: BroadcastMessage): ShrineResponse = { val req = message.request.asInstanceOf[Req] val queryId = getQueryId(req) def findShrineQueryRow = dao.findQueryByNetworkId(queryId) def findShrineQueryResults = dao.findResultsFor(queryId) findShrineQueryRow match { case None => { debug(s"Query $queryId not found in the Shrine DB") errorResponse(queryId) } case Some(shrineQueryRow) => { if (shrineQueryRow.hasNotBeenRun) { debug(s"Query $queryId found, but it wasn't run before") findShrineQueryResults.map(makeResponseFrom(queryId, _)).getOrElse { debug(s"Couldn't retrive all results for query $queryId; it's likely the query's status is incomplete (QUEUED, PROCESSING, etc)") errorResponse(queryId) } } else { findShrineQueryResults match { case None => { debug(s"Query $queryId found, and it has been run, but its results are not available yet") //TODO: When precisely can this happen? Should we go back to the CRC here? errorResponse(queryId) } case Some(shrineQueryResult) => { if (shrineQueryResult.isDone) { debug(s"Query $queryId is done and already stored, returning stored results") makeResponseFrom(queryId, shrineQueryResult) } else { debug(s"Query $queryId is incomplete, asking CRC for results") val result: ShrineResponse = retrieveQueryResults(queryId, req, shrineQueryResult, message) if (collectAdapterAudit) AdapterAuditDb.db.insertResultSent(queryId,result) result } } } } } } } private def errorResponse(queryId: Long) = ErrorResponse(QueryNotFound(queryId)) private def makeResponseFrom(queryId: Long, shrineQueryResult: ShrineQueryResult): ShrineResponse = { shrineQueryResult.toQueryResults(doObfuscation).map(toResponse(queryId, _)).getOrElse(errorResponse(queryId)) } private def retrieveQueryResults(queryId: Long, req: Req, shrineQueryResult: ShrineQueryResult, message: BroadcastMessage): ShrineResponse = { //NB: If the requested query was not finished executing on the i2b2 side when Shrine recorded it, attempt to //retrieve it and all its sub-components (breakdown results, if any) in parallel. Asking for the results in //parallel is quite possibly too clever, but may be faster than asking for them serially. //TODO: Review this. //Make requests for results in parallel val futureResponses = scatter(message.networkAuthn, req, shrineQueryResult) //Gather all the results (block until they're all returned) val SpecificResponseAttempts(countResponseAttempt, breakdownResponseAttempts) = gather(queryId, futureResponses, req.waitTime) countResponseAttempt match { //If we successfully received the parent response (the one with query type PATIENT_COUNT_XML), re-store it along //with any retrieved breakdowns before returning it. case Success(countResponse) => { //NB: Only store the result if needed, that is, if all results are done //TODO: REVIEW THIS storeResultIfNecessary(shrineQueryResult, countResponse, req.authn, queryId, getFailedBreakdownTypes(breakdownResponseAttempts)) countResponse } case Failure(e) => ErrorResponse(s"Couldn't retrieve query with id '$queryId' from the CRC: exception message follows: ${e.getMessage} stack trace: ${StackTrace.stackTraceAsString(e)}") } } private def scatter(authn: AuthenticationInfo, req: Req, shrineQueryResult: ShrineQueryResult): Future[RawResponseAttempts] = { def makeRequest(localResultId: Long) = ReadResultRequest(hiveCredentials.projectId, req.waitTime, hiveCredentials.toAuthenticationInfo, localResultId.toString) def process(localResultId: Long): ShrineResponse = { delegateResultRetrievingAdapter.process(authn, makeRequest(localResultId)) } implicit val executionContext = this.executionContext import scala.concurrent.blocking def futureBlockingAttempt[T](f: => T): Future[Try[T]] = Future(blocking(Try(f))) val futureCountAttempt: Future[Try[ShrineResponse]] = futureBlockingAttempt { process(shrineQueryResult.count.localId) } val futureBreakdownAttempts = Future.sequence(for { Breakdown(_, localResultId, resultType, data) <- shrineQueryResult.breakdowns } yield futureBlockingAttempt { process(localResultId) }) //Log errors retrieving count futureCountAttempt.collect { case Success(e: ErrorResponse) => error(s"Error requesting count result from the CRC: '$e'") case Failure(e) => error(s"Error requesting count result from the CRC: ", e) } //Log errors retrieving breakdown for { breakdownResponseAttempts <- futureBreakdownAttempts } { breakdownResponseAttempts.collect { case Success(e: ErrorResponse) => error(s"Error requesting breakdown result from the CRC: '$e'") case Failure(e) => error(s"Error requesting breakdown result from the CRC: ", e) } } //"Filter" for non-ErrorResponses val futureNonErrorCountAttempt: Future[Try[ReadResultResponse]] = futureCountAttempt.collect { case Success(resp: ReadResultResponse) => Success(resp) //NB: Need to repackage response here to avoid ugly, obscure, superfluous cast case unexpected => Failure(new Exception(s"Getting count result failed. Response is: '$unexpected'")) } //"Filter" for non-ErrorResponses val futureNonErrorBreakdownResponseAttempts: Future[Seq[Try[ReadResultResponse]]] = for { breakdownResponseAttempts <- futureBreakdownAttempts } yield { breakdownResponseAttempts.collect { case Success(resp: ReadResultResponse) => Try(resp) } } for { countResponseAttempt <- futureNonErrorCountAttempt breakdownResponseAttempts <- futureNonErrorBreakdownResponseAttempts } yield { RawResponseAttempts(countResponseAttempt, breakdownResponseAttempts) } } private def gather(queryId: Long, futureResponses: Future[RawResponseAttempts], waitTime: Duration): SpecificResponseAttempts[Rsp] = { val RawResponseAttempts(countResponseAttempt, breakdownResponseAttempts) = Await.result(futureResponses, waitTime) //Log any failures (countResponseAttempt +: breakdownResponseAttempts).collect { case Failure(e) => e }.foreach(error("Error retrieving result from the CRC: ", _)) //NB: Count response and ALL breakdown responses must be available (not Failures) or else a Failure will be returned val responseAttempt = for { countResponse: ReadResultResponse <- countResponseAttempt countQueryResult = countResponse.metadata breakdownResponses: Seq[ReadResultResponse] <- sequence(breakdownResponseAttempts) } yield { val localCountResultId = countResponse.metadata.resultId val breakdownsByType = (for { breakdownResponse <- breakdownResponses resultType <- breakdownResponse.metadata.resultType } yield resultType -> breakdownResponse.data).toMap val queryResultWithBreakdowns = countQueryResult.withBreakdowns(breakdownsByType) val queryResultToReturn = if(doObfuscation) Obfuscator.obfuscate(queryResultWithBreakdowns) else queryResultWithBreakdowns toResponse(queryId, queryResultToReturn) } SpecificResponseAttempts(responseAttempt, breakdownResponseAttempts) } private def getFailedBreakdownTypes(attempts: Seq[Try[ReadResultResponse]]): Set[ResultOutputType] = { val successfulBreakdownTypes = attempts.collect { case Success(ReadResultResponse(_, metadata, _)) => metadata.resultType }.flatten breakdownTypes -- successfulBreakdownTypes } private def storeResultIfNecessary(shrineQueryResult: ShrineQueryResult, response: Rsp, authn: AuthenticationInfo, queryId: Long, failedBreakdownTypes: Set[ResultOutputType]) { val responseIsDone = response.results.forall(_.statusType.isDone) if (responseIsDone) { storeResult(shrineQueryResult, response, authn, queryId, failedBreakdownTypes) } } private def storeResult(shrineQueryResult: ShrineQueryResult, response: Rsp, authn: AuthenticationInfo, queryId: Long, failedBreakdownTypes: Set[ResultOutputType]) { val rawResults = response.results val obfuscatedResults = obfuscateResults(doObfuscation)(response.results) for { shrineQuery <- dao.findQueryByNetworkId(queryId) queryResult <- rawResults.headOption obfuscatedQueryResult <- obfuscatedResults.headOption } { val queryDefinition = QueryDefinition(shrineQuery.name, shrineQuery.queryDefinition.expr) dao.inTransaction { dao.deleteQuery(queryId) dao.storeResults(authn, shrineQueryResult.localId, queryId, queryDefinition, rawResults, obfuscatedResults, failedBreakdownTypes.toSeq, queryResult.breakdowns, obfuscatedQueryResult.breakdowns) } } } private type Unmarshaller[R] = Set[ResultOutputType] => NodeSeq => Try[R] private final class DelegateAdapter[Rqst <: ShrineRequest, Rspns <: ShrineResponse](unmarshaller: Unmarshaller[Rspns]) extends CrcAdapter[Rqst, Rspns](poster, hiveCredentials) { def process(authn: AuthenticationInfo, req: Rqst): Rspns = processRequest(BroadcastMessage(authn, req)).asInstanceOf[Rspns] override protected def parseShrineResponse(xml: NodeSeq): ShrineResponse = unmarshaller(breakdownTypes)(xml).get //TODO: Avoid .get call } private lazy val delegateResultRetrievingAdapter = new DelegateAdapter[ReadResultRequest, ReadResultResponse](ReadResultResponse.fromI2b2 _) } case class QueryNotFound(queryId:Long) extends AbstractProblem(ProblemSources.Adapter) { - override def summary: String = s"Query with id '$queryId' not found" + override def summary: String = s"Query not found" override def description:String = s"No query with id $queryId found on ${stamp.host}" } diff --git a/adapter/adapter-service/src/main/scala/net/shrine/adapter/Adapter.scala b/adapter/adapter-service/src/main/scala/net/shrine/adapter/Adapter.scala index 3257008f9..c93d74968 100644 --- a/adapter/adapter-service/src/main/scala/net/shrine/adapter/Adapter.scala +++ b/adapter/adapter-service/src/main/scala/net/shrine/adapter/Adapter.scala @@ -1,79 +1,79 @@ package net.shrine.adapter import java.net.InetAddress import net.shrine.log.Loggable import net.shrine.problem.{Problem, ProblemNotYetEncoded, LoggingProblemHandler, ProblemSources, AbstractProblem} import net.shrine.protocol.{ShrineRequest, BroadcastMessage, ErrorResponse, BaseShrineResponse, AuthenticationInfo} /** * @author Bill Simons * @since 4/8/11 * @see http://cbmi.med.harvard.edu * @see http://chip.org *

* NOTICE: This software comes with NO guarantees whatsoever and is * licensed as Lgpl Open Source * @see http://www.gnu.org/licenses/lgpl.html */ abstract class Adapter extends Loggable { final def perform(message: BroadcastMessage): BaseShrineResponse = { def problemToErrorResponse(problem:Problem):ErrorResponse = { LoggingProblemHandler.handleProblem(problem) ErrorResponse(problem) } val shrineResponse = try { processRequest(message) } catch { case e: AdapterLockoutException => problemToErrorResponse(AdapterLockout(message.request.authn,e)) case e @ CrcInvocationException(invokedCrcUrl, request, cause) => problemToErrorResponse(CrcCouldNotBeInvoked(invokedCrcUrl,request,e)) case e: AdapterMappingException => problemToErrorResponse(AdapterMappingProblem(e)) //noinspection RedundantBlock case e: Exception => { val summary = if(message == null) "Unknown problem in Adapter.perform with null BroadcastMessage" else s"Unexpected exception in Adapter" problemToErrorResponse(ProblemNotYetEncoded(summary,e)) } } shrineResponse } protected[adapter] def processRequest(message: BroadcastMessage): BaseShrineResponse //NOOP, may be overridden by subclasses def shutdown(): Unit = () } case class AdapterLockout(authn:AuthenticationInfo,x:AdapterLockoutException) extends AbstractProblem(ProblemSources.Adapter) { override val throwable = Some(x) - override val summary: String = s"User '${authn.domain}:${authn.username}' is temporarily prevented from running queries at ${x.url}" - override val description:String = s"User '${authn.domain}:${authn.username}' has run too many queries that produce the same result the same result at ${x.url}" + override val summary: String = s"User '${authn.domain}:${authn.username}' locked out." + override val description:String = s"User '${authn.domain}:${authn.username}' has run too many queries that produce the same result at ${x.url} ." } case class CrcCouldNotBeInvoked(crcUrl:String,request:ShrineRequest,x:CrcInvocationException) extends AbstractProblem(ProblemSources.Adapter) { override val throwable = Some(x) - override val summary: String = s"Error invoking the CRC at '$crcUrl' due to ${throwable.get}} ." - override val description: String = s"Error invoking the CRC at '$crcUrl' with a ${request.getClass.getSimpleName} ." + override val summary: String = s"Error communicating with I2B2 CRC." + override val description: String = s"Error invoking the CRC at '$crcUrl' with a ${request.getClass.getSimpleName} due to ${throwable.get}." override val detailsXml =

Request is {request} {throwableDetail.getOrElse("")}
} case class AdapterMappingProblem(x:AdapterMappingException) extends AbstractProblem(ProblemSources.Adapter) { override val throwable = Some(x) - override val summary: String = s"Could not map query terms on ${stamp.host}" - override val description = s"The Shrine Adapter on ${stamp.host} cannot map this query to its local terms. Running query ${x.runQueryRequest.queryDefinition} caused ${x.cause}. This error must be corrected at the queried site." + override val summary: String = "Could not map query term(s)." + override val description = s"The Shrine Adapter on ${stamp.host} cannot map this query to its local terms." override val detailsXml =
Query Defitiontion is {x.runQueryRequest.queryDefinition} RunQueryRequest is ${x.runQueryRequest.elideAuthenticationInfo} {throwableDetail.getOrElse("")}
} diff --git a/adapter/adapter-service/src/main/scala/net/shrine/adapter/components/QueryDefinitions.scala b/adapter/adapter-service/src/main/scala/net/shrine/adapter/components/QueryDefinitions.scala index ce0ff8e90..7b79e4c7a 100644 --- a/adapter/adapter-service/src/main/scala/net/shrine/adapter/components/QueryDefinitions.scala +++ b/adapter/adapter-service/src/main/scala/net/shrine/adapter/components/QueryDefinitions.scala @@ -1,40 +1,40 @@ package net.shrine.adapter.components import net.shrine.adapter.dao.AdapterDao import net.shrine.problem.{ProblemSources, AbstractProblem} import net.shrine.protocol.ShrineResponse import net.shrine.protocol.ReadQueryDefinitionRequest import net.shrine.protocol.ReadQueryDefinitionResponse import net.shrine.protocol.ErrorResponse import net.shrine.protocol.query.QueryDefinition import net.shrine.protocol.AbstractReadQueryDefinitionRequest /** * @author clint * @since Apr 4, 2013 * * NB: Tested by ReadQueryDefinitionAdapterTest */ final case class QueryDefinitions[Req <: AbstractReadQueryDefinitionRequest](dao: AdapterDao) { def get(request: Req): ShrineResponse = { val resultOption = for { shrineQuery <- dao.findQueryByNetworkId(request.queryId) } yield { ReadQueryDefinitionResponse( shrineQuery.networkId, shrineQuery.name, shrineQuery.username, shrineQuery.dateCreated, //TODO: I2b2 or Shrine format? shrineQuery.queryDefinition.toI2b2String) } resultOption.getOrElse(ErrorResponse(QueryNotInDatabase(request))) } } case class QueryNotInDatabase(request:AbstractReadQueryDefinitionRequest) extends AbstractProblem(ProblemSources.Hub) { //todo on which adapter? - override val summary: String = s"Couldn't find query with network id: ${request.queryId}" - override val description:String = "" + override val summary: String = s"Couldn't find query definition." + override val description:String = s"The query definition with network id: ${request.queryId} does not exist at this site." } \ No newline at end of file diff --git a/adapter/adapter-service/src/main/scala/net/shrine/adapter/dao/squeryl/SquerylAdapterDao.scala b/adapter/adapter-service/src/main/scala/net/shrine/adapter/dao/squeryl/SquerylAdapterDao.scala index 5ae66aa1a..db025fb97 100644 --- a/adapter/adapter-service/src/main/scala/net/shrine/adapter/dao/squeryl/SquerylAdapterDao.scala +++ b/adapter/adapter-service/src/main/scala/net/shrine/adapter/dao/squeryl/SquerylAdapterDao.scala @@ -1,463 +1,463 @@ package net.shrine.adapter.dao.squeryl import net.shrine.log.Loggable import net.shrine.problem.{AbstractProblem, ProblemSources} import org.squeryl.Query import javax.xml.datatype.XMLGregorianCalendar import net.shrine.adapter.dao.AdapterDao import net.shrine.adapter.dao.model.BreakdownResultRow import net.shrine.adapter.dao.model.CountRow import net.shrine.adapter.dao.model.ObfuscatedPair import net.shrine.adapter.dao.model.PrivilegedUser import net.shrine.adapter.dao.model.QueryResultRow import net.shrine.adapter.dao.model.ShrineError import net.shrine.adapter.dao.model.ShrineQuery import net.shrine.adapter.dao.model.ShrineQueryResult import net.shrine.adapter.dao.model.squeryl.SquerylBreakdownResultRow import net.shrine.adapter.dao.model.squeryl.SquerylCountRow import net.shrine.adapter.dao.model.squeryl.SquerylQueryResultRow import net.shrine.adapter.dao.model.squeryl.SquerylShrineError import net.shrine.adapter.dao.model.squeryl.SquerylShrineQuery import net.shrine.adapter.dao.squeryl.tables.Tables import net.shrine.dao.DateHelpers import net.shrine.dao.squeryl.SquerylInitializer import net.shrine.dao.squeryl.SquerylEntryPoint import net.shrine.protocol.AuthenticationInfo import net.shrine.protocol.I2b2ResultEnvelope import net.shrine.protocol.QueryResult import net.shrine.protocol.ResultOutputType import net.shrine.protocol.query.QueryDefinition import net.shrine.util.XmlDateHelper import scala.util.Try import net.shrine.adapter.dao.model.squeryl.SquerylQueryResultRow import net.shrine.adapter.dao.model.squeryl.SquerylPrivilegedUser import scala.xml.NodeSeq /** * @author clint * @since May 22, 2013 */ final class SquerylAdapterDao(initializer: SquerylInitializer, tables: Tables)(implicit breakdownTypes: Set[ResultOutputType]) extends AdapterDao with Loggable { initializer.init() override def inTransaction[T](f: => T): T = SquerylEntryPoint.inTransaction { f } import SquerylEntryPoint._ override def flagQuery(networkQueryId: Long, flagMessage: Option[String]): Unit = mutateFlagField(networkQueryId, newIsFlagged = true, flagMessage) override def unFlagQuery(networkQueryId: Long): Unit = mutateFlagField(networkQueryId, newIsFlagged = false, None) private def mutateFlagField(networkQueryId: Long, newIsFlagged: Boolean, newFlagMessage: Option[String]): Unit = { inTransaction { update(tables.shrineQueries) { queryRow => where(queryRow.networkId === networkQueryId). set(queryRow.isFlagged := newIsFlagged, queryRow.flagMessage := newFlagMessage) } } } override def storeResults( authn: AuthenticationInfo, masterId: String, networkQueryId: Long, queryDefinition: QueryDefinition, rawQueryResults: Seq[QueryResult], obfuscatedQueryResults: Seq[QueryResult], failedBreakdownTypes: Seq[ResultOutputType], mergedBreakdowns: Map[ResultOutputType, I2b2ResultEnvelope], obfuscatedBreakdowns: Map[ResultOutputType, I2b2ResultEnvelope]): Unit = { inTransaction { val insertedQueryId = insertQuery(masterId, networkQueryId, authn, queryDefinition, isFlagged = false, hasBeenRun = true, flagMessage = None) val insertedQueryResultIds = insertQueryResults(insertedQueryId, rawQueryResults) storeCountResults(rawQueryResults, obfuscatedQueryResults, insertedQueryResultIds) storeErrorResults(rawQueryResults, insertedQueryResultIds) storeBreakdownFailures(failedBreakdownTypes.toSet, insertedQueryResultIds) insertBreakdownResults(insertedQueryResultIds, mergedBreakdowns, obfuscatedBreakdowns) } } private[adapter] def storeCountResults(raw: Seq[QueryResult], obfuscated: Seq[QueryResult], insertedIds: Map[ResultOutputType, Seq[Int]]): Unit = { val notErrors = raw.filter(!_.isError) val obfuscatedNotErrors = obfuscated.filter(!_.isError) if(notErrors.size > 1) { warn(s"Got ${notErrors.size} raw (hopefully-)count results; more than 1 is unusual.") } if(obfuscatedNotErrors.size > 1) { warn(s"Got ${obfuscatedNotErrors.size} obfuscated (hopefully-)count results; more than 1 is unusual.") } if(notErrors.size != obfuscatedNotErrors.size) { warn(s"Got ${notErrors.size} raw and ${obfuscatedNotErrors.size} obfuscated (hopefully-)count results; that these numbers are different is unusual.") } import ResultOutputType.PATIENT_COUNT_XML def isCount(qr: QueryResult): Boolean = qr.resultType == Some(PATIENT_COUNT_XML) inTransaction { //NB: Take the count/setSize from the FIRST PATIENT_COUNT_XML QueryResult, //though the same count should be there for all of them, if there are more than one for { Seq(insertedCountQueryResultId) <- insertedIds.get(PATIENT_COUNT_XML) notError <- notErrors.find(isCount) //NB: Find a count result, just to be sure obfuscatedNotError <- obfuscatedNotErrors.find(isCount) //NB: Find a count result, just to be sure } { insertCountResult(insertedCountQueryResultId, notError.setSize, obfuscatedNotError.setSize) } } } private[adapter] def storeErrorResults(results: Seq[QueryResult], insertedIds: Map[ResultOutputType, Seq[Int]]): Unit = { val errors = results.filter(_.isError) val insertedErrorResultIds = insertedIds.getOrElse(ResultOutputType.ERROR,Nil) val insertedIdsToErrors = insertedErrorResultIds zip errors inTransaction { for { (insertedErrorResultId, errorQueryResult) <- insertedIdsToErrors } { val pd = errorQueryResult.problemDigest.get //it's an error so it will have a problem digest insertErrorResult( insertedErrorResultId, errorQueryResult.statusMessage.getOrElse("Unknown failure"), pd.codec, pd.stampText, pd.summary, pd.description, pd.detailsXml ) } } } private[adapter] def storeBreakdownFailures(failedBreakdownTypes: Set[ResultOutputType], insertedIds: Map[ResultOutputType, Seq[Int]]): Unit = { val insertedIdsForFailedBreakdownTypes = insertedIds.filterKeys(failedBreakdownTypes.contains) inTransaction { for { (failedBreakdownType, Seq(resultId)) <- insertedIdsForFailedBreakdownTypes } { - //todo propagate backwards to teh breakdown failure to create the corect problem + //todo propagate backwards to the breakdown failure to create the corect problem object BreakdownFailure extends AbstractProblem(ProblemSources.Adapter) { - override val summary: String = s"Couldn't retrieve breakdown of type '$failedBreakdownType'" - override val description:String = "" + override val summary: String = "Couldn't retrieve result breakdown" + override val description:String = s"Couldn't retrieve result breakdown of type '$failedBreakdownType'" } val pd = BreakdownFailure.toDigest insertErrorResult( resultId, s"Couldn't retrieve breakdown of type '$failedBreakdownType'", pd.codec, pd.stampText, pd.summary, pd.description, pd.detailsXml ) } } } override def findRecentQueries(howMany: Int): Seq[ShrineQuery] = { inTransaction { Queries.queriesForAllUsers.take(howMany).map(_.toShrineQuery).toSeq } } def findAllCounts():Seq[SquerylCountRow] = { inTransaction{ Queries.allCountResults.toSeq } } override def renameQuery(networkQueryId: Long, newName: String) { inTransaction { update(tables.shrineQueries) { queryRow => where(queryRow.networkId === networkQueryId). set(queryRow.name := newName) } } } override def deleteQuery(networkQueryId: Long): Unit = { inTransaction { tables.shrineQueries.deleteWhere(_.networkId === networkQueryId) } } override def deleteQueryResultsFor(networkQueryId: Long): Unit = { inTransaction { val resultIdsForNetworkQueryId = join(tables.shrineQueries, tables.queryResults) { (queryRow, resultRow) => where(queryRow.networkId === networkQueryId). select(resultRow.id). on(queryRow.id === resultRow.queryId) }.toSet tables.queryResults.deleteWhere(_.id in resultIdsForNetworkQueryId) } } override def isUserLockedOut(authn: AuthenticationInfo, defaultThreshold: Int): Boolean = Try { inTransaction { val privilegedUserOption = Queries.privilegedUsers(authn.domain, authn.username).singleOption val threshold = privilegedUserOption.map(_.threshold).getOrElse(defaultThreshold.intValue) val thirtyDaysInThePast: XMLGregorianCalendar = DateHelpers.daysFromNow(-30) val overrideDate: XMLGregorianCalendar = privilegedUserOption.map(_.toPrivilegedUser).flatMap(_.overrideDate).getOrElse(thirtyDaysInThePast) val counts: Seq[Long] = Queries.repeatedResults(authn.domain, authn.username, overrideDate).toSeq.sorted val repeatedResultCount: Long = counts.lastOption.getOrElse(0L) val result = repeatedResultCount > threshold debug(s"User ${authn.domain}:${authn.username} locked out? $result") result } }.getOrElse(false) override def insertQuery(localMasterId: String, networkId: Long, authn: AuthenticationInfo, queryDefinition: QueryDefinition, isFlagged: Boolean, hasBeenRun: Boolean, flagMessage: Option[String]): Int = { inTransaction { val inserted = tables.shrineQueries.insert(new SquerylShrineQuery( 0, localMasterId, networkId, authn.username, authn.domain, XmlDateHelper.now, isFlagged, flagMessage, hasBeenRun, queryDefinition)) inserted.id } } /** * Insert rows into QueryResults, one for each QueryResult in the passed RunQueryResponse * Inserted rows are 'children' of the passed ShrineQuery (ie, they are the results of the query) */ override def insertQueryResults(parentQueryId: Int, results: Seq[QueryResult]): Map[ResultOutputType, Seq[Int]] = { def execTime(result: QueryResult): Option[Long] = { //TODO: How are locales handled here? Do we care? def toMillis(xmlGc: XMLGregorianCalendar) = xmlGc.toGregorianCalendar.getTimeInMillis for { start <- result.startDate end <- result.endDate } yield toMillis(end) - toMillis(start) } val typeToIdTuples = inTransaction { for { result <- results resultType = result.resultType.getOrElse(ResultOutputType.ERROR) //TODO: under what circumstances can QueryResults NOT have start and end dates set? elapsed = execTime(result) } yield { val lastInsertedQueryResultRow = tables.queryResults.insert(new SquerylQueryResultRow(0, result.resultId, parentQueryId, resultType, result.statusType, elapsed, XmlDateHelper.now)) (resultType, lastInsertedQueryResultRow.id) } } typeToIdTuples.groupBy { case (resultType, _) => resultType }.mapValues(_.map { case (_, count) => count }) } override def insertCountResult(resultId: Int, originalCount: Long, obfuscatedCount: Long) { //NB: Squeryl steers us toward inserting with dummy ids :( inTransaction { tables.countResults.insert(new SquerylCountRow(0, resultId, originalCount, obfuscatedCount, XmlDateHelper.now)) } } override def insertBreakdownResults(parentResultIds: Map[ResultOutputType, Seq[Int]], originalBreakdowns: Map[ResultOutputType, I2b2ResultEnvelope], obfuscatedBreakdowns: Map[ResultOutputType, I2b2ResultEnvelope]) { def merge(original: I2b2ResultEnvelope, obfuscated: I2b2ResultEnvelope): Map[String, ObfuscatedPair] = { Map.empty ++ (for { (key, originalValue) <- original.data obfuscatedValue <- obfuscated.data.get(key) } yield (key, ObfuscatedPair(originalValue, obfuscatedValue))) } inTransaction { for { (resultType, Seq(resultId)) <- parentResultIds if resultType.isBreakdown originalBreakdown <- originalBreakdowns.get(resultType) obfuscatedBreakdown <- obfuscatedBreakdowns.get(resultType) (key, ObfuscatedPair(original, obfuscated)) <- merge(originalBreakdown, obfuscatedBreakdown) } { tables.breakdownResults.insert(SquerylBreakdownResultRow(0, resultId, key, original, obfuscated)) } } } override def insertErrorResult(parentResultId: Int, errorMessage: String, codec:String, stampText:String, summary:String, digestDescription:String,detailsXml:NodeSeq) { //NB: Squeryl steers us toward inserting with dummy ids :( inTransaction { tables.errorResults.insert(SquerylShrineError(0, parentResultId, errorMessage, codec, stampText, summary, digestDescription, detailsXml.toString())) } } override def findQueryByNetworkId(networkQueryId: Long): Option[ShrineQuery] = { inTransaction { Queries.queriesByNetworkId(networkQueryId).headOption.map(_.toShrineQuery) } } override def findQueriesByUserAndDomain(domain: String, username: String, howMany: Int): Seq[ShrineQuery] = { inTransaction { Queries.queriesForUser(username, domain).take(howMany).toSeq.map(_.toShrineQuery) } } override def findResultsFor(networkQueryId: Long): Option[ShrineQueryResult] = { inTransaction { val breakdownRowsByType = Queries.breakdownResults(networkQueryId).toSeq.groupBy { case (outputType, _) => outputType.toQueryResultRow.resultType }.mapValues(_.map { case (_, row) => row.toBreakdownResultRow }) val queryRowOption = Queries.queriesByNetworkId(networkQueryId).headOption.map(_.toShrineQuery) val countRowOption = Queries.countResults(networkQueryId).headOption.map(_.toCountRow) val queryResultRows = Queries.resultsForQuery(networkQueryId).toSeq.map(_.toQueryResultRow) val errorResultRows = Queries.errorResults(networkQueryId).toSeq.map(_.toShrineError) for { queryRow <- queryRowOption countRow <- countRowOption shrineQueryResult <- ShrineQueryResult.fromRows(queryRow, queryResultRows, countRow, breakdownRowsByType, errorResultRows) } yield { shrineQueryResult } } } /** * @author clint * @since Nov 19, 2012 */ object Queries { def privilegedUsers(domain: String, username: String): Query[SquerylPrivilegedUser] = { from(tables.privilegedUsers) { user => where(user.username === username and user.domain === domain).select(user) } } def repeatedResults(domain: String, username: String, overrideDate: XMLGregorianCalendar): Query[Long] = { val counts = join(tables.shrineQueries, tables.queryResults, tables.countResults) { (queryRow, resultRow, countRow) => where(queryRow.username === username and queryRow.domain === domain and (countRow.originalValue <> 0L) and queryRow.dateCreated > DateHelpers.toTimestamp(overrideDate)). groupBy(countRow.originalValue). compute(count(countRow.originalValue)). on(queryRow.id === resultRow.queryId, resultRow.id === countRow.resultId) } //Filter for result counts > 1 from(counts) { cnt => where(cnt.measures gt 1).select(cnt.measures) } } val queriesForAllUsers: Query[SquerylShrineQuery] = { from(tables.shrineQueries) { queryRow => select(queryRow).orderBy(queryRow.dateCreated.desc) } } //TODO: Find a way to parameterize on limit, to avoid building the query every time //TODO: limit def queriesForUser(username: String, domain: String): Query[SquerylShrineQuery] = { from(tables.shrineQueries) { queryRow => where(queryRow.domain === domain and queryRow.username === username). select(queryRow). orderBy(queryRow.dateCreated.desc) } } val allCountResults: Query[SquerylCountRow] = { from(tables.countResults) { queryRow => select(queryRow) } } def queriesByNetworkId(networkQueryId: Long): Query[SquerylShrineQuery] = { from(tables.shrineQueries) { queryRow => where(queryRow.networkId === networkQueryId).select(queryRow) } } //TODO: Find out how to compose queries, to re-use queriesByNetworkId def queryNamesByNetworkId(networkQueryId: Long): Query[String] = { from(tables.shrineQueries) { queryRow => where(queryRow.networkId === networkQueryId).select(queryRow.name) } } def resultsForQuery(networkQueryId: Long): Query[SquerylQueryResultRow] = { val resultsForNetworkQueryId = join(tables.shrineQueries, tables.queryResults) { (queryRow, resultRow) => where(queryRow.networkId === networkQueryId). select(resultRow). on(queryRow.id === resultRow.queryId) } from(resultsForNetworkQueryId)(select(_)) } def countResults(networkQueryId: Long): Query[SquerylCountRow] = { join(tables.shrineQueries, tables.queryResults, tables.countResults) { (queryRow, resultRow, countRow) => where(queryRow.networkId === networkQueryId). select(countRow). on(queryRow.id === resultRow.queryId, resultRow.id === countRow.resultId) } } def errorResults(networkQueryId: Long): Query[SquerylShrineError] = { join(tables.shrineQueries, tables.queryResults, tables.errorResults) { (queryRow, resultRow, errorRow) => where(queryRow.networkId === networkQueryId). select(errorRow). on(queryRow.id === resultRow.queryId, resultRow.id === errorRow.resultId) } } //NB: using groupBy here is too much of a pain; do it 'manually' later def breakdownResults(networkQueryId: Long): Query[(SquerylQueryResultRow, SquerylBreakdownResultRow)] = { join(tables.shrineQueries, tables.queryResults, tables.breakdownResults) { (queryRow, resultRow, breakdownRow) => where(queryRow.networkId === networkQueryId). select((resultRow, breakdownRow)). on(queryRow.id === resultRow.queryId, resultRow.id === breakdownRow.resultId) } } } } \ No newline at end of file diff --git a/apps/dashboard-app/src/main/js/src/app/dashboard/sidebar/sidebar.tpl.html b/apps/dashboard-app/src/main/js/src/app/dashboard/sidebar/sidebar.tpl.html index 9653b19ed..10bda826a 100755 --- a/apps/dashboard-app/src/main/js/src/app/dashboard/sidebar/sidebar.tpl.html +++ b/apps/dashboard-app/src/main/js/src/app/dashboard/sidebar/sidebar.tpl.html @@ -1,16 +1,16 @@ diff --git a/apps/dashboard-app/src/main/js/src/assets/css/shrine.css b/apps/dashboard-app/src/main/js/src/assets/css/shrine.css index 2ac2f17d3..9afcc24b2 100644 --- a/apps/dashboard-app/src/main/js/src/assets/css/shrine.css +++ b/apps/dashboard-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/dashboard-app/src/main/js/src/assets/fnt/nexa/nexa-bold-webfont.eot b/apps/dashboard-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/dashboard-app/src/main/js/src/assets/fnt/nexa/nexa-bold-webfont.eot differ diff --git a/apps/dashboard-app/src/main/js/src/assets/fnt/nexa/nexa-bold-webfont.svg b/apps/dashboard-app/src/main/js/src/assets/fnt/nexa/nexa-bold-webfont.svg new file mode 100755 index 000000000..56565994f --- /dev/null +++ b/apps/dashboard-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/dashboard-app/src/main/js/src/assets/fnt/nexa/nexa-bold-webfont.ttf b/apps/dashboard-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/dashboard-app/src/main/js/src/assets/fnt/nexa/nexa-bold-webfont.ttf differ diff --git a/apps/dashboard-app/src/main/js/src/assets/fnt/nexa/nexa-bold-webfont.woff b/apps/dashboard-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/dashboard-app/src/main/js/src/assets/fnt/nexa/nexa-bold-webfont.woff differ diff --git a/apps/dashboard-app/src/main/js/src/assets/fnt/nexa/nexa-bold-webfont.woff2 b/apps/dashboard-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/dashboard-app/src/main/js/src/assets/fnt/nexa/nexa-bold-webfont.woff2 differ diff --git a/apps/dashboard-app/src/main/js/src/assets/fnt/nexa/nexa-light-webfont.eot b/apps/dashboard-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/dashboard-app/src/main/js/src/assets/fnt/nexa/nexa-light-webfont.eot differ diff --git a/apps/dashboard-app/src/main/js/src/assets/fnt/nexa/nexa-light-webfont.svg b/apps/dashboard-app/src/main/js/src/assets/fnt/nexa/nexa-light-webfont.svg new file mode 100755 index 000000000..68dcda1ca --- /dev/null +++ b/apps/dashboard-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/dashboard-app/src/main/js/src/assets/fnt/nexa/nexa-light-webfont.ttf b/apps/dashboard-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/dashboard-app/src/main/js/src/assets/fnt/nexa/nexa-light-webfont.ttf differ diff --git a/apps/dashboard-app/src/main/js/src/assets/fnt/nexa/nexa-light-webfont.woff b/apps/dashboard-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/dashboard-app/src/main/js/src/assets/fnt/nexa/nexa-light-webfont.woff differ diff --git a/apps/dashboard-app/src/main/js/src/assets/fnt/nexa/nexa-light-webfont.woff2 b/apps/dashboard-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/dashboard-app/src/main/js/src/assets/fnt/nexa/nexa-light-webfont.woff2 differ diff --git a/apps/dashboard-app/src/main/js/src/assets/fnt/open-sans/opensans-regular-webfont.eot b/apps/dashboard-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/dashboard-app/src/main/js/src/assets/fnt/open-sans/opensans-regular-webfont.eot differ diff --git a/apps/dashboard-app/src/main/js/src/assets/fnt/open-sans/opensans-regular-webfont.svg b/apps/dashboard-app/src/main/js/src/assets/fnt/open-sans/opensans-regular-webfont.svg new file mode 100755 index 000000000..a169e01ae --- /dev/null +++ b/apps/dashboard-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/dashboard-app/src/main/js/src/assets/fnt/open-sans/opensans-regular-webfont.ttf b/apps/dashboard-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/dashboard-app/src/main/js/src/assets/fnt/open-sans/opensans-regular-webfont.ttf differ diff --git a/apps/dashboard-app/src/main/js/src/assets/fnt/open-sans/opensans-regular-webfont.woff b/apps/dashboard-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/dashboard-app/src/main/js/src/assets/fnt/open-sans/opensans-regular-webfont.woff differ diff --git a/apps/dashboard-app/src/main/js/src/assets/fnt/open-sans/opensans-regular-webfont.woff2 b/apps/dashboard-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/dashboard-app/src/main/js/src/assets/fnt/open-sans/opensans-regular-webfont.woff2 differ diff --git a/apps/dashboard-app/src/main/js/src/assets/fnt/open-sans/opensans-semibold-webfont.eot b/apps/dashboard-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/dashboard-app/src/main/js/src/assets/fnt/open-sans/opensans-semibold-webfont.eot differ diff --git a/apps/dashboard-app/src/main/js/src/assets/fnt/open-sans/opensans-semibold-webfont.svg b/apps/dashboard-app/src/main/js/src/assets/fnt/open-sans/opensans-semibold-webfont.svg new file mode 100755 index 000000000..2704ab443 --- /dev/null +++ b/apps/dashboard-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/dashboard-app/src/main/js/src/assets/fnt/open-sans/opensans-semibold-webfont.ttf b/apps/dashboard-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/dashboard-app/src/main/js/src/assets/fnt/open-sans/opensans-semibold-webfont.ttf differ diff --git a/apps/dashboard-app/src/main/js/src/assets/fnt/open-sans/opensans-semibold-webfont.woff b/apps/dashboard-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/dashboard-app/src/main/js/src/assets/fnt/open-sans/opensans-semibold-webfont.woff differ diff --git a/apps/dashboard-app/src/main/js/src/assets/fnt/open-sans/opensans-semibold-webfont.woff2 b/apps/dashboard-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/dashboard-app/src/main/js/src/assets/fnt/open-sans/opensans-semibold-webfont.woff2 differ diff --git a/apps/dashboard-app/src/main/js/src/assets/fnt/roboto/roboto-bold-webfont.eot b/apps/dashboard-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/dashboard-app/src/main/js/src/assets/fnt/roboto/roboto-bold-webfont.eot differ diff --git a/apps/dashboard-app/src/main/js/src/assets/fnt/roboto/roboto-bold-webfont.svg b/apps/dashboard-app/src/main/js/src/assets/fnt/roboto/roboto-bold-webfont.svg new file mode 100755 index 000000000..c9576fe24 --- /dev/null +++ b/apps/dashboard-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/dashboard-app/src/main/js/src/assets/fnt/roboto/roboto-bold-webfont.ttf b/apps/dashboard-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/dashboard-app/src/main/js/src/assets/fnt/roboto/roboto-bold-webfont.ttf differ diff --git a/apps/dashboard-app/src/main/js/src/assets/fnt/roboto/roboto-bold-webfont.woff b/apps/dashboard-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/dashboard-app/src/main/js/src/assets/fnt/roboto/roboto-bold-webfont.woff differ diff --git a/apps/dashboard-app/src/main/js/src/assets/fnt/roboto/roboto-bold-webfont.woff2 b/apps/dashboard-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/dashboard-app/src/main/js/src/assets/fnt/roboto/roboto-bold-webfont.woff2 differ diff --git a/apps/dashboard-app/src/main/js/src/assets/fnt/roboto/roboto-regular-webfont.eot b/apps/dashboard-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/dashboard-app/src/main/js/src/assets/fnt/roboto/roboto-regular-webfont.eot differ diff --git a/apps/dashboard-app/src/main/js/src/assets/fnt/roboto/roboto-regular-webfont.svg b/apps/dashboard-app/src/main/js/src/assets/fnt/roboto/roboto-regular-webfont.svg new file mode 100755 index 000000000..fb6643454 --- /dev/null +++ b/apps/dashboard-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/dashboard-app/src/main/js/src/assets/fnt/roboto/roboto-regular-webfont.ttf b/apps/dashboard-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/dashboard-app/src/main/js/src/assets/fnt/roboto/roboto-regular-webfont.ttf differ diff --git a/apps/dashboard-app/src/main/js/src/assets/fnt/roboto/roboto-regular-webfont.woff b/apps/dashboard-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/dashboard-app/src/main/js/src/assets/fnt/roboto/roboto-regular-webfont.woff differ diff --git a/apps/dashboard-app/src/main/js/src/assets/fnt/roboto/roboto-regular-webfont.woff2 b/apps/dashboard-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/dashboard-app/src/main/js/src/assets/fnt/roboto/roboto-regular-webfont.woff2 differ diff --git a/apps/steward-app/src/main/js/src/app/dashboard/history/history-table/history-table.tpl.html b/apps/steward-app/src/main/js/src/app/dashboard/history/history-table/history-table.tpl.html index 038fb5d81..d6fabc9f6 100644 --- a/apps/steward-app/src/main/js/src/app/dashboard/history/history-table/history-table.tpl.html +++ b/apps/steward-app/src/main/js/src/app/dashboard/history/history-table/history-table.tpl.html @@ -1,94 +1,87 @@
+ + + + - - - - - - - - - + + + +
- + ID - - + + - + Query Topic - - + + Username Query Text Status Date
{{query.stewardId}}{{query.topic.name}}{{query.user.userName}}
{{query.stewardId}}{{query.topic.name}}{{query.user.userName}} - {{query.name}} - {{query.stewardResponse}}{{formatDate(query.date)}}
+ {{query.name}} + {{query.stewardResponse}}{{formatDate(query.date)}}
\ No newline at end of file diff --git a/apps/steward-app/src/main/js/src/app/dashboard/history/history.tpl.html b/apps/steward-app/src/main/js/src/app/dashboard/history/history.tpl.html index ec5c82d0c..2af17c1d0 100644 --- a/apps/steward-app/src/main/js/src/app/dashboard/history/history.tpl.html +++ b/apps/steward-app/src/main/js/src/app/dashboard/history/history.tpl.html @@ -1,14 +1,6 @@ - - -
-
- - - - - - - -
- -
\ No newline at end of file +
+
+ + +
+
\ No newline at end of file diff --git a/apps/steward-app/src/main/js/src/app/dashboard/sidebar/sidebar.tpl.html b/apps/steward-app/src/main/js/src/app/dashboard/sidebar/sidebar.tpl.html index 5c0da2583..c8767de67 100755 --- a/apps/steward-app/src/main/js/src/app/dashboard/sidebar/sidebar.tpl.html +++ b/apps/steward-app/src/main/js/src/app/dashboard/sidebar/sidebar.tpl.html @@ -1,28 +1,28 @@ diff --git a/apps/steward-app/src/main/js/src/app/dashboard/topics/new-topic/new-topic.tpl.html b/apps/steward-app/src/main/js/src/app/dashboard/topics/new-topic/new-topic.tpl.html index 6a39e37fc..e25aad88b 100644 --- a/apps/steward-app/src/main/js/src/app/dashboard/topics/new-topic/new-topic.tpl.html +++ b/apps/steward-app/src/main/js/src/app/dashboard/topics/new-topic/new-topic.tpl.html @@ -1,51 +1,55 @@ -
+
-
+

- -
\ No newline at end of file diff --git a/apps/steward-app/src/main/js/src/app/dashboard/topics/topic-table/topic-detail.tpl.html b/apps/steward-app/src/main/js/src/app/dashboard/topics/topic-table/topic-detail.tpl.html index 43b18fbdd..565d2643e 100644 --- a/apps/steward-app/src/main/js/src/app/dashboard/topics/topic-table/topic-detail.tpl.html +++ b/apps/steward-app/src/main/js/src/app/dashboard/topics/topic-table/topic-detail.tpl.html @@ -1,37 +1,37 @@
\ No newline at end of file diff --git a/apps/steward-app/src/main/js/src/app/dashboard/topics/topic-table/topic-detail/role1-description.tpl.html b/apps/steward-app/src/main/js/src/app/dashboard/topics/topic-table/topic-detail/role1-description.tpl.html index d70245a07..0214a99ff 100644 --- a/apps/steward-app/src/main/js/src/app/dashboard/topics/topic-table/topic-detail/role1-description.tpl.html +++ b/apps/steward-app/src/main/js/src/app/dashboard/topics/topic-table/topic-detail/role1-description.tpl.html @@ -1,45 +1,45 @@

{{topic.id}}
{{formatDate(topic.createDate)}}
{{formatDate(topic.changeDate)}}
{{data.topicState}}
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}}
{{formatDate(topic.createDate)}}
{{formatDate(topic.changeDate)}}
{{data.topicState}}
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}}
{{formatDate(topic.createDate)}}
{{formatDate(topic.changeDate)}}
\ 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