Merge remote-tracking branch 'Simon/develop' into develop

This commit is contained in:
hongwei1 2021-12-07 11:10:53 +01:00
commit de3db4e52c
18 changed files with 437 additions and 325 deletions

View File

@ -149,11 +149,17 @@ dev.port=8082
# need to set it manually here.
#api_manager_url=
# API explorer menu structure (temporary layout - for first workshops)
webui_index_dynamic_1_link_url=/?tags=Dynamic-Endpoint
webui_index_dynamic_1_link_text=Third Party
webui_index_dynamic_2_link_url=/?tags=Dynamic-Endpoint
webui_index_dynamic_2_link_text=Third Party 2
# API explorer menu structure, the value should be validate json format.
#webui_index_dynamic_url_text_pairs=[\
# {\
# "url": "/?api-collection-id=feb47896-6a18-4a96-b8e9-ed6db05dbd401",\
# "text": "Third Party 1"\
# },\
# {\
# "url": "/?api-collection-id=feb47896-6a18-4a96-b8e9-ed6db05dbd401",\
# "text": "Third Party 2"\
# }\
# ]
# Sngle Sign On

View File

@ -1,11 +1,12 @@
package code.lib
import code.lib.ObpAPI.UnknownErrorMessage
import java.io._
import java.text.SimpleDateFormat
import java.util.{Date, UUID}
import code.lib.ObpJson._
import code.util.Helper
import code.util.Helper.MdcLoggable
import code.util.Helper.{MdcLoggable, covertWebpageIdToObpOperationId}
import code.util.cache.Caching
import net.liftweb.common.{Box, Failure, Full, _}
import net.liftweb.http.{RequestVar, S, SessionVar}
@ -21,6 +22,7 @@ import scala.concurrent.duration._
import scala.language.postfixOps
import java.util.UUID.randomUUID
import net.liftweb.common._
import net.liftweb.json
case class Header(key: String, value: String)
@ -39,6 +41,41 @@ object ObpAPI extends Loggable {
val defaultProvider = Helper.getPropsValue("defaultAuthProvider").getOrElse("")
val userNotFoundError = "user (\\S+) at provider (\\S+) not found".r
final val AccountUrlPath = "/accounts/"
final val ApiCollectionId = "api-collection-id"
final val CacheModifier = "cache-modifier"
final val ContentEqualStatic = "content=static"
final val ContentEqualDynamic = "content=dynamic"
final val UnknownErrorMessage = "Unknown Error!"
final val OBPVersionV400 = "OBPv4.0.0"
final val UKVersionV31 = "UKv3.1"
final val UKVersionV20 = "UKv2.0"
final val BGVersionV13 = "BGv1.3"
final val PAPIVersionV2111 = "PAPIv2.1.1.1"
final val BGVersionV133 = "BGv1.3.3"
final val VersionV133 = "v1.3.3"
final val DisplayEqualNone = "display: none"
final val ResourceStyleCss = ".resource [style]"
final val ResourceErrorStyleCss = ".resource-error [style]"
final val DisplayEqualBlock = "display: block"
final val ContentBoxHeadline = ".content-box__headline *"
final val RolesBoxId = "@roles_box [id]"
final val RolesBoxStyle = "@roles_box [style]"
final val RoleItemClassCss = ".role_item"
final val RolesRoleNameCss = "@roles__role_name"
final val RoleStatusNameCss = "@roles__status"
final val RolesBankIdInput = "@roles__bank_id_input"
final val RolesRoleInput = "@roles__role_input"
final val RolesEntitlementRequestId = "@roles__entitlement_request_response [id]"
final val RolesRequestEntitlementButton = "@roles__request_entitlement_button"
final val PleaseLoginToRequestThisRole = " - Please login to request this Role"
final val YouHaveThisRole = s" - You have this Role."
final val ContactOBPTeam = s" - You have requested this Role. Please contact Open Bank Project team to grant your this role."
final val YouCanRequestThisRole = s" - You can request this Role."
final val RolesEntitlementRequestButtonBox = "@roles__entitlement_request_button_box [style]"
final val EndPointAnchorHref = ".end-point-anchor [href]"
final val ContentBoxHeadlineId = ".content-box__headline [id]"
/**
* The request vars ensure that for one page load, the same API call isn't
@ -83,7 +120,7 @@ object ObpAPI extends Loggable {
fromDate.map(f => Header("obp_from_date", dateFormat.format(f))).toList ::: toDate.map(t => Header("obp_to_date", dateFormat.format(t))).toList :::
sortDirection.map(s => Header("obp_sort_direction", s.value)).toList ::: Nil
ObpGet(s"$obpPrefix/v3.0.0/banks/" + urlEncode(bankId) + "/accounts/" + urlEncode(accountId) + "/" + urlEncode(viewId) +
ObpGet(s"$obpPrefix/v3.0.0/banks/" + urlEncode(bankId) + AccountUrlPath + urlEncode(accountId) + "/" + urlEncode(viewId) +
"/transactions", headers).flatMap(x => x.extractOpt[TransactionsJsonV300])
}
@ -94,19 +131,19 @@ object ObpAPI extends Loggable {
def publicAccounts(bankId : String) : Box[BarebonesAccountsJson] = {
ObpGet(s"$obpPrefix/v3.1.0/banks/" + urlEncode(bankId) + "/accounts/public").flatMap(_.extractOpt[BarebonesAccountsJson])
ObpGet(s"$obpPrefix/v3.1.0/banks/" + urlEncode(bankId) + AccountUrlPath + "public").flatMap(_.extractOpt[BarebonesAccountsJson])
}
def publicAccounts : Box[BarebonesAccountsJson] = {
ObpGet(s"$obpPrefix/v3.1.0/accounts/public").flatMap(_.extractOpt[BarebonesAccountsJson])
ObpGet(s"$obpPrefix/v3.1.0${AccountUrlPath}public").flatMap(_.extractOpt[BarebonesAccountsJson])
}
def privateAccounts(bankId : String) : Box[BarebonesAccountsJson] = {
ObpGet(s"$obpPrefix/v3.1.0/banks/" + urlEncode(bankId) + "/accounts/private").flatMap(_.extractOpt[BarebonesAccountsJson])
ObpGet(s"$obpPrefix/v3.1.0/banks/" + urlEncode(bankId) + AccountUrlPath + "private").flatMap(_.extractOpt[BarebonesAccountsJson])
}
def privateAccounts : Box[BarebonesAccountsJson] = {
ObpGet(s"$obpPrefix/v1.2.1/accounts/private").flatMap(_.extractOpt[BarebonesAccountsJson])
ObpGet(s"$obpPrefix/v1.2.1${AccountUrlPath}private").flatMap(_.extractOpt[BarebonesAccountsJson])
}
@deprecated("This method will mix public and private, not clear for Apps.","2018-02-18")
@ -116,20 +153,20 @@ object ObpAPI extends Loggable {
// Similar to getViews below
def getViewsForBankAccount(bankId: String, accountId: String) = {
ObpGet(s"$obpPrefix/v3.1.0/banks/" + bankId + "/accounts/" + accountId + "/views").flatMap(_.extractOpt[ViewsJson])
ObpGet(s"$obpPrefix/v3.1.0/banks/" + bankId + AccountUrlPath + accountId + "/views").flatMap(_.extractOpt[ViewsJson])
}
def getAccount(bankId: String, accountId: String, viewId: String) : Box[AccountJson] = {
ObpGet(s"$obpPrefix/v3.1.0/banks/" + urlEncode(bankId) + "/accounts/" + urlEncode(accountId) + "/" + urlEncode(viewId) + "/account").flatMap(x => x.extractOpt[AccountJson])
ObpGet(s"$obpPrefix/v3.1.0/banks/" + urlEncode(bankId) + AccountUrlPath + urlEncode(accountId) + "/" + urlEncode(viewId) + "/account").flatMap(x => x.extractOpt[AccountJson])
}
def getCounterparties(bankId: String, accountId: String, viewId: String): Box[DirectOtherAccountsJson] = {
val counterparties = ObpGet(s"$obpPrefix/v3.1.0/banks/" + urlEncode(bankId) + "/accounts/" + urlEncode(accountId) + "/" + urlEncode(viewId) + "/other_accounts").flatMap(x => x.extractOpt[DirectOtherAccountsJson])
val counterparties = ObpGet(s"$obpPrefix/v3.1.0/banks/" + urlEncode(bankId) + AccountUrlPath + urlEncode(accountId) + "/" + urlEncode(viewId) + "/other_accounts").flatMap(x => x.extractOpt[DirectOtherAccountsJson])
counterparties
}
def getExplictCounterparties(bankId: String, accountId: String, viewId: String): Box[ExplictCounterpartiesJson] = {
ObpGet(s"$obpPrefix/v2.2.0/banks/" + urlEncode(bankId) + "/accounts/" + urlEncode(accountId) + "/" + urlEncode(viewId) + "/counterparties").flatMap(x => x.extractOpt[ExplictCounterpartiesJson])
ObpGet(s"$obpPrefix/v2.2.0/banks/" + urlEncode(bankId) + AccountUrlPath + urlEncode(accountId) + "/" + urlEncode(viewId) + "/counterparties").flatMap(x => x.extractOpt[ExplictCounterpartiesJson])
}
def getEntitlementsV300 : Box[EntitlementsJson] = {
@ -226,44 +263,55 @@ object ObpAPI extends Loggable {
ObpPost(s"$obpPrefix/v4.0.0/my/api-collections", Extraction.decompose(postSelectionEndpointJson))
}
def createMyApiCollectionEndpoint (apiCollectionName: String, operationId: String) = {
val postSelectionEndpointJson = PostSelectionEndpointJson400(operationId)
def createMyApiCollectionEndpoint (apiCollectionName: String, webPageOperationId: String) = {
val obpOperationId = covertWebpageIdToObpOperationId(webPageOperationId)
val postSelectionEndpointJson = PostSelectionEndpointJson400(obpOperationId)
ObpPost(s"$obpPrefix/v4.0.0/my/api-collections/$apiCollectionName/api-collection-endpoints", Extraction.decompose(postSelectionEndpointJson))
}
def deleteMyApiCollectionEndpoint (apiCollectionName: String, operationId: String) = {
ObpDelete(s"$obpPrefix/v4.0.0/my/api-collections/$apiCollectionName/api-collection-endpoints/$operationId")
def deleteMyApiCollectionEndpoint (apiCollectionName: String, webPageOperationId: String) = {
val obpOperationId = covertWebpageIdToObpOperationId(webPageOperationId)
ObpDelete(s"$obpPrefix/v4.0.0/my/api-collections/$apiCollectionName/api-collection-endpoints/$obpOperationId")
}
//NOTE: there is no parameters for the method, the cache is not working well. need to fix later
// private val sharableApiCollectionsTTL: FiniteDuration = Helper.getPropsAsIntValue("sharable_api_collections.cache.ttl.seconds", 0) seconds
def sharableApiCollections: Box[List[(String, String)]] = {
// var cacheKey = (randomUUID().toString, randomUUID().toString, randomUUID().toString)
// CacheKeyFromArguments.buildCacheKey {
// Caching.memoizeSyncWithProvider(Some(cacheKey.toString()))(sharableApiCollectionsTTL) {
val apiCollectionsResponse = ObpGet(s"$obpPrefix/v4.0.0/api-collections/featured").flatMap(_.extractOpt[ApiCollectionsJson400])
apiCollectionsResponse.map(_.api_collections.map(apiCollection => (apiCollection.api_collection_name, apiCollection.api_collection_id)))
// }
// }
}
@deprecated("16-11-2021","this is the Legacy props, now we introduce `webui_index_dynamic_url_text_pairs` ")
def getApiCollectionsFromPropsLegacy: List[(String, String)] = {
val webuiIndexDynamic1LinkUrl = Helper.getPropsValue("webui_index_dynamic_1_link_url", "")
val webuiIndexDynamic1LinkText = Helper.getPropsValue("webui_index_dynamic_1_link_text", "")
val webuiIndexDynamic1LinkUrl = Helper.getPropsValue("webui_index_dynamic_1_link_url", "")
val webuiIndexDynamic1LinkText = Helper.getPropsValue("webui_index_dynamic_1_link_text", "")
val webuiIndexDynamic2LinkUrl = Helper.getPropsValue("webui_index_dynamic_2_link_url", "")
val webuiIndexDynamic2LinkText = Helper.getPropsValue("webui_index_dynamic_2_link_text", "")
val webuiIndexDynamic2LinkUrl = Helper.getPropsValue("webui_index_dynamic_2_link_url", "")
val webuiIndexDynamic2LinkText = Helper.getPropsValue("webui_index_dynamic_2_link_text", "")
if (webuiIndexDynamic1LinkUrl.nonEmpty && webuiIndexDynamic1LinkText.nonEmpty && webuiIndexDynamic2LinkText.nonEmpty && webuiIndexDynamic2LinkUrl.nonEmpty){
List(
(webuiIndexDynamic1LinkText,webuiIndexDynamic1LinkUrl), (webuiIndexDynamic2LinkText,webuiIndexDynamic2LinkUrl)
)
} else if (webuiIndexDynamic1LinkUrl.nonEmpty && webuiIndexDynamic1LinkText.nonEmpty )
List(
(webuiIndexDynamic1LinkText,webuiIndexDynamic1LinkUrl)
)
else
Nil
}
val dynamicUrlTextPairsJson: List[DynamicUrlTextPairsJson] = {
def extractor(str: String) = try {
val dynamicUrlTextPairs = json.parse(str).extract[List[DynamicUrlTextPairsJson]]
//The props value can be parse to JNothing.
if(str.nonEmpty && dynamicUrlTextPairs == Nil)
throw new RuntimeException(s"props [webui_index_dynamic_url_text_pairs] parse -> extract to Nil! it should be the valid class($DynamicUrlTextPairsJson) json format, current value is $str .")
else
dynamicUrlTextPairs
} catch {
case e: Throwable => // error handling, found wrong props value as early as possible.
this.logger.error(s"props [webui_index_dynamic_url_text_pairs] value is invalid, it should be the class($DynamicUrlTextPairsJson) json format, current value is $str ." );
throw e;
}
Helper.getPropsValue("webui_index_dynamic_url_text_pairs").map(extractor).getOrElse(Nil)
}
def getApiCollectionsFromProps: Box[List[(String, String)]] = {
if (webuiIndexDynamic1LinkUrl.nonEmpty && webuiIndexDynamic1LinkText.nonEmpty && webuiIndexDynamic2LinkText.nonEmpty && webuiIndexDynamic2LinkUrl.nonEmpty){
Full(List(
(webuiIndexDynamic1LinkText,webuiIndexDynamic1LinkUrl), (webuiIndexDynamic2LinkText,webuiIndexDynamic2LinkUrl)
))
} else if (webuiIndexDynamic1LinkText.nonEmpty && webuiIndexDynamic1LinkText.nonEmpty )
Full(List(
(webuiIndexDynamic1LinkText,webuiIndexDynamic1LinkUrl)
))
else
Full(Nil)
Full(getApiCollectionsFromPropsLegacy ++ dynamicUrlTextPairsJson.map(dynamicUrlTextPair => List((dynamicUrlTextPair.text, dynamicUrlTextPair.url))).flatten)
}
/**
@ -275,17 +323,18 @@ object ObpAPI extends Loggable {
// Returns both system and dynamic resource docs:
def getAllResourceDocsJson(apiVersion : String): Box[List[ResourceDocJson]] = {
val apiCollectionIdParam = List("api-collection-id")
val apiCollectionIdParam = List(ApiCollectionId)
.map(paramName => (paramName, S.param(paramName)))
.collect{
case (paramName, Full(paramValue)) if(paramValue.trim.size > 0) => s"$paramName=$paramValue"
case (paramName, Full(paramValue)) if(paramValue.trim.size > 0
) => s"$paramName=$paramValue"
}
.mkString("?", "&", "")
//Note: ?content=static&content=dynamic
// if there are two content parameters there, only the first one is valid for the api call.
// so requestParams have the high priority
val requestParams = List("tags", "language", "functions", "content", "cache-modifier")
val requestParams = List("tags", "language", "functions", "content", CacheModifier)
.map(paramName => (paramName, S.param(paramName)))
.collect{
case (paramName, Full(paramValue)) if(paramValue.trim.size > 0) => s"$paramName=$paramValue"
@ -302,11 +351,11 @@ object ObpAPI extends Loggable {
lazy val dynamicResourcesDocs = getDynamicResourceDocs(apiVersion,requestParams, canReadResourceDocRole, OAuthClient.loggedIn)
//If the api-collection-id in the URL, it will ignore all other parameters, so here we first check it:
if(apiCollectionIdParam.contains("api-collection-id=")) {
if(apiCollectionIdParam.contains(ApiCollectionId + "=")) {
getResourceDocsByApiCollectionId(apiVersion, apiCollectionIdParam)
}else if(requestParams.contains("content=static")) {
}else if(requestParams.contains(ContentEqualStatic)) {
staticResourcesDocs
} else if (requestParams.contains("content=dynamic")){
} else if (requestParams.contains(ContentEqualDynamic)){
dynamicResourcesDocs
} else{
for{
@ -321,7 +370,7 @@ object ObpAPI extends Loggable {
// Returns all bank level dynamic resources
def getStaticAndAllBankLevelDynamicResourceDocs(apiVersion : String) = {
val apiCollectionIdParam = List("api-collection-id")
val apiCollectionIdParam = List(ApiCollectionId)
.map(paramName => (paramName, S.param(paramName)))
.collect{
case (paramName, Full(paramValue)) if(paramValue.trim.size > 0) => s"$paramName=$paramValue"
@ -331,7 +380,7 @@ object ObpAPI extends Loggable {
//Note: ?content=static&content=dynamic
// if there are two content parameters there, only the first one is valid for the api call.
// so requestParams have the high priority
val requestParams = List("tags", "language", "functions", "cache-modifier")
val requestParams = List("tags", "language", "functions", CacheModifier)
.map(paramName => (paramName, S.param(paramName)))
.collect{
case (paramName, Full(paramValue)) if(paramValue.trim.size > 0) => s"$paramName=$paramValue"
@ -351,11 +400,11 @@ object ObpAPI extends Loggable {
bankId => getBankLevelDynamicResourceDocs(apiVersion,bankId,requestParams)).flatten.flatten)
//If the api-collection-id in the URL, it will ignore all other parameters, so here we first check it:
if(apiCollectionIdParam.contains("api-collection-id=")) {
if(apiCollectionIdParam.contains(ApiCollectionId + "=")) {
getResourceDocsByApiCollectionId(apiVersion, apiCollectionIdParam)
}else if(requestParams.contains("content=static")) {
}else if(requestParams.contains(ContentEqualStatic)) {
staticResourcesDocs
} else if (requestParams.contains("content=dynamic")){
} else if (requestParams.contains(ContentEqualDynamic)){
dynamicResourcesDocs
} else{
for{
@ -369,7 +418,7 @@ object ObpAPI extends Loggable {
// Returns only the bank level resource docs
def getOneBankLevelResourceDocsJson(apiVersion : String, bankId:String) = {
val apiCollectionIdParam = List("api-collection-id")
val apiCollectionIdParam = List(ApiCollectionId)
.map(paramName => (paramName, S.param(paramName)))
.collect{
case (paramName, Full(paramValue)) if(paramValue.trim.size > 0) => s"$paramName=$paramValue"
@ -379,7 +428,7 @@ object ObpAPI extends Loggable {
//Note: ?content=static&content=dynamic
// if there are two content parameters there, only the first one is valid for the api call.
// so requestParams have the high priority
val requestParams = List("tags", "language", "functions", "content", "cache-modifier")
val requestParams = List("tags", "language", "functions", "content", CacheModifier)
.map(paramName => (paramName, S.param(paramName)))
.collect{
case (paramName, Full(paramValue)) if(paramValue.trim.size > 0) => s"$paramName=$paramValue"
@ -396,7 +445,7 @@ object ObpAPI extends Loggable {
var cacheKey = (randomUUID().toString, randomUUID().toString, randomUUID().toString)
CacheKeyFromArguments.buildCacheKey {
Caching.memoizeSyncWithProvider(Some(cacheKey.toString()))(getStaticResourceDocsJsonTTL) {
val requestParamsRemovedContent = requestParams.replace("content=static","")
val requestParamsRemovedContent = requestParams.replace(ContentEqualStatic,"")
getResourceDocs(apiVersion, requestParamsRemovedContent, "static")
}
}
@ -409,7 +458,7 @@ object ObpAPI extends Loggable {
var cacheKey = (randomUUID().toString, randomUUID().toString, randomUUID().toString)
CacheKeyFromArguments.buildCacheKey {
Caching.memoizeSyncWithProvider(Some(cacheKey.toString()))(getDynamicResourceDocsJsonTTL) {
val requestParamsRemovedContent = requestParams.replace("content=dynamic","")
val requestParamsRemovedContent = requestParams.replace(ContentEqualDynamic,"")
getResourceDocs(apiVersion, requestParamsRemovedContent, "dynamic")
}
}
@ -439,7 +488,7 @@ object ObpAPI extends Loggable {
def getBankLevelDynamicResourceDocsJValueResponse(apiVersion : String, bankId:String, requestParams: String) = {
logger.debug("getBankLevelResourceDocsJValueResponse says Hello")
val result = ObpGet(s"$obpPrefix/v4.0.0/banks/$bankId/resource-docs/$apiVersion/obp$requestParams&cache-modifier=${UUID.randomUUID().toString}")
val result = ObpGet(s"$obpPrefix/v4.0.0/banks/$bankId/resource-docs/$apiVersion/obp$requestParams&$CacheModifier=${UUID.randomUUID().toString}")
logger.debug("getBankLevelResourceDocsJValueResponse says result is: " + result)
result
}
@ -448,7 +497,7 @@ object ObpAPI extends Loggable {
ObpGet(s"$obpPrefix/v4.0.0/resource-docs/$apiVersion/obp$requestParams").map(extractResourceDocsJson).map(_.resource_docs)
def getApiCollectionByIdJValueResponse(apiVersion : String) = {
val apiCollectionIdParam = List("api-collection-id")
val apiCollectionIdParam = List(ApiCollectionId)
.map(paramName => (paramName, S.param(paramName)))
.collect{
case (paramName, Full(paramValue)) if(paramValue.trim.size > 0) => s"$paramName=$paramValue"
@ -605,7 +654,7 @@ object ObpPut {
OBPRequest(apiPath, Some(json), "PUT", Nil) match {
case Full((status, result, _)) => APIUtils.getAPIResponseBody(status, result)
case Failure(msg, exception, chain) => Failure(msg)
case _ => Failure("Unknown Error!")
case _ =>Failure(UnknownErrorMessage)
}
}
}
@ -622,7 +671,7 @@ object ObpPost {
OBPRequest(apiPath, Some(json), "POST", Nil) match {
case Full((status, result, _)) => APIUtils.getAPIResponseBody(status, result)
case Failure(msg, exception, chain) => Failure(msg)
case _ => Failure("Unknown Error!")
case _ => Failure(UnknownErrorMessage)
}
}
}
@ -657,16 +706,16 @@ object ObpDeleteBoolean {
object ObpDelete {
def apply(apiPath: String): Box[JValue] = {
OBPRequest(apiPath, None, "DELETE", Nil) match {
case Full((status, result, _)) => Full(APIUtils.apiResponseWorked(status, result))
case Full((status, result, _)) => APIUtils.getAPIResponseBody(status, result)
case Failure(msg, exception, chain) => Failure(msg)
case _ => Failure("Unknown Error!")
case _ => Failure(UnknownErrorMessage)
}
}
}
object ObpDeleteWithHeader {
def apply(apiPath: String, headers : List[Header] = Nil): (Box[JValue], List[String]) = {
OBPRequest(apiPath, None, "DELETE", headers) match {
case Full(value) => (APIUtils.deleteApiResponse(value._1, value._2), value._3)
case Full(value) => (APIUtils.getAPIResponseBody(value._1, value._2), value._3)
}
}
}
@ -682,7 +731,7 @@ object ObpGet {
OBPRequest(apiPath, None, "GET", headers) match {
case Full((status, result, _)) => APIUtils.getAPIResponseBody(status, result)
case Failure(msg, exception, chain) => Failure(msg)
case _ => Failure("Unknown Error!")
case _ => Failure(UnknownErrorMessage)
}
}
}
@ -696,7 +745,7 @@ object ObpHead {
OBPRequest(apiPath, None, "HEAD", headers) match {
case Full((status, result, _)) => APIUtils.getAPIResponseBody(status, result)
case Failure(msg, exception, chain) => Failure(msg)
case _ => Failure("Unknown Error!")
case _ => Failure(UnknownErrorMessage)
}
}
}
@ -1232,6 +1281,7 @@ object ObpJson {
)
case class ResourceDocsJson (resource_docs : List[ResourceDocJson])
case class DynamicUrlTextPairsJson (url:String, text:String)
///////////////////////////////////////////

View File

@ -1,10 +1,10 @@
package code.snippet
import code.lib.ObpAPI.{getAuthenticationTypeValidations, getJsonSchemaValidations, getMySpaces, obpPrefix}
import code.lib.ObpAPI._
import code.lib.ObpJson._
import code.lib.{ObpAPI, ObpGet, _}
import code.util.Helper
import code.util.Helper.MdcLoggable
import code.util.Helper.{MdcLoggable, covertObpOperationIdToWebpageId}
import net.liftweb.json
import net.liftweb.util.{CssSel, Html5}
import java.net.URL
@ -180,7 +180,7 @@ WIP to add comments on resource docs. This code copied from Sofit.
logger.info(s"nativeParam is $nativeParam")
def apiCollectionIdParam = S.param("api-collection-id")
def apiCollectionIdParam = S.param(ApiCollectionId)
logger.info(s"apiCollectionIdParam is $apiCollectionIdParam")
@ -343,7 +343,7 @@ WIP to add comments on resource docs. This code copied from Sofit.
val defaultVersion: String = Helper.getPropsValue("default.version") match {
case Full(v) => v
case _ => "OBPv4.0.0"
case _ => OBPVersionV400
}
// Get the requested version from the url parameter and default if none
@ -352,9 +352,10 @@ WIP to add comments on resource docs. This code copied from Sofit.
// Possible OBP Versions
val obpVersionsSupported = List("OBPv3.1.0", "OBPv4.0.0")
val obpVersionsSupported = List("OBPv3.1.0", OBPVersionV400)
val otherVersionsSupported = List("BGv1.3", "UKv3.1")
val otherVersionsSupported = List(BGVersionV13, UKVersionV31)
// This is basically all versions supported
val allSupportedVersionsInDropdownMenu = List(
@ -366,11 +367,11 @@ WIP to add comments on resource docs. This code copied from Sofit.
"OBPv2.2.0",
"OBPv3.0.0",
"OBPv3.1.0",
"OBPv4.0.0",
"UKv3.1",
OBPVersionV400,
UKVersionV31,
"BGv1.3",
"STETv1.4",
"PAPIv2.1.1.1",
PAPIVersionV2111,
"AUv1.0.0",
"0.6v1",
"MXOFv1.0.0",
@ -590,7 +591,10 @@ WIP to add comments on resource docs. This code copied from Sofit.
var errorResponseBodies = List("")
var isFavourites = "false"
var favouritesOperationId = ""
//Note: OperationIdFromWebpage = OBPv4_0_0-getBanks
//But operationIdForOBP => OBPv4.0.0-getBanks (Javascript do not support '.' there.)
//We must do the converting properly for this two ids.
var favouritesOperationIdFromWebpage = ""
var favouritesApiCollectionId = ""
@ -665,20 +669,18 @@ WIP to add comments on resource docs. This code copied from Sofit.
val allResources = for {
r <- allResourcesList
} yield ResourceDocPlus(
//in OBP-API, before it returned v3_1_0, but now, only return v3.1.0
//But this field will be used in JavaScript, so need clean the field.
id = r.operation_id.replace(".","_").replaceAll(" ","_"),
id = covertObpOperationIdToWebpageId(r.operation_id),
operationId = r.operation_id,
verb = r.request_verb,
url = modifiedRequestUrl(
r.specified_url, // We used to use the request_url - but we want to use the specified url i.e. the later version.
apiVersion
.replaceAll("UKv2.0", "v2.0")
.replaceAll("UKv3.1", "v3.1")
.replaceAll("BGv1.3.3", "v1.3.3")
.replaceAll(UKVersionV20, "v2.0")
.replaceAll(UKVersionV31, "v3.1")
.replaceAll(BGVersionV133, VersionV133)
.replaceAll("BGv1", "v1")
.replaceAll("BGv1.3", "v1.3")
.replaceAll("PAPIv2.1.1.1", "v2.1.1.1")
.replaceAll(BGVersionV13, "v1.3")
.replaceAll(PAPIVersionV2111, "v2.1.1.1")
.replaceAll("OBPv", ""),
presetBankId,
presetAccountId
@ -773,8 +775,8 @@ WIP to add comments on resource docs. This code copied from Sofit.
//this can be empty list, if there is no operationIds there.
val getOperationIdsByApiCollectionId = ObpAPI.getApiCollectionEndpointsById(apiCollectionId).map(_.api_collection_endpoints.map(_.operation_id)).openOr(List())
val getMyOperationIds = ObpAPI.getApiCollectionEndpoints("Favourites").map(_.api_collection_endpoints.map(_.operation_id)).openOr(List())
def webpageOperationIds = ObpAPI.getApiCollectionEndpointsById(apiCollectionId).map(_.api_collection_endpoints.map(_.operation_id)).openOr(List()).map(covertObpOperationIdToWebpageId)
def myWebpageOperationIds = ObpAPI.getApiCollectionEndpoints("Favourites").map(_.api_collection_endpoints.map(_.operation_id)).openOr(List()).map(covertObpOperationIdToWebpageId)
// Group resources by the first tag
val unsortedGroupedResources: Map[String, List[ResourceDocPlus]] = resources.groupBy(_.tags.headOr("ToTag"))
@ -963,20 +965,20 @@ WIP to add comments on resource docs. This code copied from Sofit.
s"$requestUrl"
}
logger.info(s"urlWithVersion is: " + urlWithVersion
.replaceAll("UKv2.0", "v2.0")
.replaceAll("UKv3.1", "v3.1")
.replaceAll("BGv1.3.3", "v1.3.3")
.replaceAll(UKVersionV20, "v2.0")
.replaceAll(UKVersionV31, "v3.1")
.replaceAll(BGVersionV133, VersionV133)
.replaceAll("BGv1", "v1")
.replaceAll("BGv1.3", "v1.3")
.replaceAll(BGVersionV13, "v1.3")
.replaceAll("(?<![Vv]validations/)OBPv", "") //delete OBPv, but if the OBPv is part of operationId, not to do delete, e.g: /validations/OBPv4.0.0-dynamicEndpoint_POST__account_access_consents
)
//val urlWithVersion = s"/$apiVersion$requestUrl"
val fullPath = new URL(apiUrl + urlWithVersion
.replaceAll("UKv2.0", "v2.0")
.replaceAll("UKv3.1", "v3.1")
.replaceAll("BGv1.3.3", "v1.3.3")
.replaceAll("BGv1.3", "v1.3")
.replaceAll(UKVersionV20, "v2.0")
.replaceAll(UKVersionV31, "v3.1")
.replaceAll(BGVersionV133, VersionV133)
.replaceAll(BGVersionV13, "v1.3")
.replaceAll("BGv1", "v1")
.replaceAll("(?<![Vv]validations/)OBPv", "")) //delete OBPv, but if the OBPv is part of operationId, not to do delete, e.g: /validations/OBPv4.0.0-dynamicEndpoint_POST__account_access_consents
//////////////
@ -1001,32 +1003,41 @@ WIP to add comments on resource docs. This code copied from Sofit.
def processFavourites(name: String): JsCmd = {
// enable button
val jsEnabledBtn = s"jQuery('input[name=$name]').removeAttr('disabled')"
//We call the getApiCollectionsForCurrentUser endpoint again, to make sure we already created or delelet the record there.
//We call the getApiCollectionsForCurrentUser endpoint again, to make sure we already created or delete the record there.
val apiFavouriteCollection = ObpAPI.getApiCollection("Favourites")
val errorMessage = if(apiFavouriteCollection.isInstanceOf[Failure]) apiFavouriteCollection.asInstanceOf[Failure].messageChain else ""
if(apiFavouriteCollection.isInstanceOf[Failure]){ // If the user is not logged in, we do not need call any apis calls. (performance enhancement)
SetHtml(s"favourites_error_message_${favouritesOperationId}", Text(errorMessage))&
SetHtml(s"favourites_error_message_${favouritesOperationIdFromWebpage}", Text(errorMessage))&
Run (jsEnabledBtn)
} else {
if(errorMessage.equals("")){ //If there is no error, we changed the button
if(favouritesApiCollectionId.nonEmpty && !apiFavouriteCollection.map(_.api_collection_id).contains(favouritesApiCollectionId)){
SetHtml(s"favourites_error_message_${favouritesOperationId}", Text("You only have read access for the Favourites. You can only edit your own Favourites."))&
SetHtml(s"favourites_error_message_${favouritesOperationIdFromWebpage}", Text("You only have read access for the Favourites. You can only edit your own Favourites."))&
Run (jsEnabledBtn)
}else{
//prepare the js for the button color changing.
val favouritesBtnColour = if (getMyOperationIds.contains(favouritesOperationId)) {
ObpAPI.deleteMyApiCollectionEndpoint("Favourites",favouritesOperationId)
s"jQuery('#favourites_button_${favouritesOperationId}').css('color','#767676')"
} else {
ObpAPI.createMyApiCollectionEndpoint("Favourites",favouritesOperationId)
s"jQuery('#favourites_button_${favouritesOperationId}').css('color','#53C4EF')"
if (myWebpageOperationIds.contains(favouritesOperationIdFromWebpage)) { //If we already have this operationId, we need to delete it
val deletedBox = ObpAPI.deleteMyApiCollectionEndpoint("Favourites",favouritesOperationIdFromWebpage)
val deleteErrorMessage = if(deletedBox.isInstanceOf[Failure]) deletedBox.asInstanceOf[Failure].messageChain else ""
if (deletedBox.isInstanceOf[Failure]){
SetHtml(s"favourites_error_message_${favouritesOperationIdFromWebpage}", Text(deleteErrorMessage)) &
Run(jsEnabledBtn)
}else{
Run (jsEnabledBtn) & Run(s"jQuery('#favourites_button_${favouritesOperationIdFromWebpage}').css('color','#767676')")
}
} else {//If we do not have this operationId, we need to create it.
val createdBox = ObpAPI.createMyApiCollectionEndpoint("Favourites",favouritesOperationIdFromWebpage)
val createdErrorMessage = if(createdBox.isInstanceOf[Failure]) createdBox.asInstanceOf[Failure].messageChain else ""
if (createdBox.isInstanceOf[Failure]){
SetHtml(s"favourites_error_message_${favouritesOperationIdFromWebpage}", Text(createdErrorMessage)) &
Run(jsEnabledBtn)
}else{
Run (jsEnabledBtn) & Run(s"jQuery('#favourites_button_${favouritesOperationIdFromWebpage}').css('color','#53C4EF')")
}
}
Run (jsEnabledBtn) &
Run (favouritesBtnColour)}
} else { //if there is error, we show the OBP-API error there.
SetHtml(s"favourites_error_message_${favouritesOperationId}", Text(errorMessage)) &
}} else { //if there is error, we show the OBP-API error there.
SetHtml(s"favourites_error_message_${favouritesOperationIdFromWebpage}", Text(errorMessage)) &
Run(jsEnabledBtn)
}
}
@ -1044,13 +1055,13 @@ WIP to add comments on resource docs. This code copied from Sofit.
// Includes hack for Berlin Group
val otherVersionUrls: List[(String, String)] = otherVersionsSupported.map(i => (i
.replace("b1", "API Builder")
.replace("BGv1.3.3", "Berlin Group 1.3.3")
.replace("BGv1.3", "Berlin Group 1.3")
.replace(BGVersionV133, "Berlin Group 1.3.3")
.replace(BGVersionV13, "Berlin Group 1.3")
.replace("BGv1", "Berlin Group")
.replace("UKv2.0", "UK 2.0")
.replace("UKv3.1", "UK 3.1")
.replace(UKVersionV20, "UK 2.0")
.replace(UKVersionV31, "UK 3.1")
.replace("STETv1.4", "STET 1.4")
.replace("PAPIv2.1.1.1", "Polish API 2.1.1.1")
.replace(PAPIVersionV2111, "Polish API 2.1.1.1")
.replace("AUv1.0.0", "AU CDR v1.0.0"),
s"${CurrentReq.value.uri}?version=${i}&list-all-banks=${listAllBanks}"))
@ -1083,12 +1094,15 @@ WIP to add comments on resource docs. This code copied from Sofit.
featuredBankIds.contains(b.id.get) // Add a flag to say if this bank is featured.
)
// Banks where a user has accounts + My Spaces + Featured Banks.
val userCanShowBankIds= (myBankIds++featuredBankIds.map(BankId(_))++ getMySpaces.map(_.bank_ids.map(BankId(_))).getOrElse(List.empty[BankId])).toSet
val banksForUser =
if (listAllBanks) // Url param says show all.
banks
else
if(!myBankIds.isEmpty) // User has accounts so show those banks
banks.filter(b => myBankIds.contains(BankId(b.id)))
if(!userCanShowBankIds.isEmpty) // User has accounts so show those banks
banks.filter(b => userCanShowBankIds.contains(BankId(b.id)))
else
// If we have a featured list of banks show those, else all.
banks.filter(b => b.isFeatured || featuredBankIds.length == 0)
@ -1309,7 +1323,7 @@ WIP to add comments on resource docs. This code copied from Sofit.
* we need to skip the case: &api-collection-id=&
*/
def isCollectionOfResourceDocs_? = {
S.param("api-collection-id").isDefined && S.param("api-collection-id").getOrElse("").nonEmpty
S.param(ApiCollectionId).isDefined && S.param(ApiCollectionId).getOrElse("").nonEmpty
}
val glossaryItems = getGlossaryItemsJson.map(_.glossary_items).getOrElse(List())
@ -1415,7 +1429,14 @@ WIP to add comments on resource docs. This code copied from Sofit.
"@api_glossary_item_link * " #>{
val description = glossaryItems.find(_.title == i._1.replaceAll("-"," ")).map(_.description.markdown).getOrElse("")
if (description.length > 100)
description.substring(0,100).trim() +" ..."
"More..."
else
"" //If there is no description, we will show empty here.
} &
"@api_glossary_item_text * " #>{
val description = glossaryItems.find(_.title == i._1.replaceAll("-"," ")).map(_.description.markdown).getOrElse("")
if (description.length > 100)
description.substring(0,100).trim()
else
"" //If there is no description, we will show empty here.
} &
@ -1483,51 +1504,51 @@ WIP to add comments on resource docs. This code copied from Sofit.
// This creates the list of resources in the DOM
{
if(allResourcesBox.isInstanceOf[Failure]) {
".resource [style]" #> s"display: none" &
".resource-error [style]" #> s"display: block" &
".content-box__headline *" #> {
ResourceStyleCss #> s"${DisplayEqualNone}" &
ResourceErrorStyleCss #> s"${DisplayEqualBlock}" &
ContentBoxHeadline #> {
allResourcesBox.asInstanceOf[Failure].msg
}&
{
if(allResourcesBox.asInstanceOf[Failure].msg.contains("CanReadResourceDoc")){
//required roles and related user information
"@roles_box [id]" #> s"roles_box_canReadResourceDocRoleInfo" &
"@roles_box [style]" #> {s"display: block"} &
RolesBoxId #> s"roles_box_canReadResourceDocRoleInfo" &
RolesBoxStyle #> {s"${DisplayEqualBlock}"} &
// We generate mulutiple .role_items from roleInfos (including the form defined in index.html)
".role_item" #> canReadResourceDocRoleInfo.map { r =>
"@roles__status" #> {if (! isLoggedIn)
s" - Please login to request this Role"
RoleItemClassCss #> canReadResourceDocRoleInfo.map { r =>
RoleStatusNameCss #> {if (! isLoggedIn)
PleaseLoginToRequestThisRole
else if (r.userHasEntitlement)
s" - You have this Role."
YouHaveThisRole
else if (r.userHasEntitlementRequest)
s" - You have requested this Role. Please contact Open Bank Project team to grant your this role."
ContactOBPTeam
else
s" - You can request this Role."} &
"@roles__role_name" #> s"${r.role}" &
YouCanRequestThisRole} &
RolesRoleNameCss #> s"${r.role}" &
// ajaxSubmit will submit the form.
// The value of rolesBankId is given to bank_id_input field and the value of bank_id_input entered by user is given back to rolesBankId
"@roles__bank_id_input" #> SHtml.text({if (r.requiresBankId) rolesBankId else ""}, rolesBankId = _, if (r.requiresBankId) "type" -> "text" else "type" -> "hidden") &
"@roles__role_input" #> SHtml.text(s"${r.role}", entitlementRequestRoleName = _, "type" -> "hidden" ) &
RolesBankIdInput #> SHtml.text({if (r.requiresBankId) rolesBankId else ""}, rolesBankId = _, if (r.requiresBankId) "type" -> "text" else "type" -> "hidden") &
RolesRoleInput #> SHtml.text(s"${r.role}", entitlementRequestRoleName = _, "type" -> "hidden" ) &
"@roles__resource_id_input" #> text("canReadResourceDocRoleInfo", s => RolesResourceId = s, "type" -> "hidden", "id" -> s"roles__resource_id_input_${canReadResourceDocRoleInfo}") &
"@roles__request_entitlement_button" #> Helper.ajaxSubmit("Request", disabledBtn, processEntitlementRequest) &
"@roles__entitlement_request_response [id]" #> s"roles__entitlement_request_response_${canReadResourceDocRoleInfo}_${r.role}" &
"@roles__entitlement_request_button_box [style]" #> { if (! isLoggedIn || r.userHasEntitlement || r.userHasEntitlementRequest)
s"display: none"
RolesRequestEntitlementButton #> Helper.ajaxSubmit("Request", disabledBtn, processEntitlementRequest) &
RolesEntitlementRequestId #> s"roles__entitlement_request_response_${canReadResourceDocRoleInfo}_${r.role}" &
RolesEntitlementRequestButtonBox #> { if (! isLoggedIn || r.userHasEntitlement || r.userHasEntitlementRequest)
s"${DisplayEqualNone}"
else
s"display: block"
s"${DisplayEqualBlock}"
}
}
} else{
"@roles_box [style]" #> s"display: none"
RolesBoxStyle #> s"${DisplayEqualNone}"
}
}
}else if(allResourcesBox.isEmpty || allResourcesBox.openOr(Nil).length ==0){
".resource [style]" #> s"display: none" &
".resource-error [style]" #> s"display: block" &
".content-box__headline *" #> {
ResourceStyleCss #> s"${DisplayEqualNone}" &
ResourceErrorStyleCss #> s"${DisplayEqualBlock}" &
ContentBoxHeadline #> {
"Sorry, we could not return any Resource Docs."
}&
".content-box__info-box [style]" #> s"display: none"
".content-box__info-box [style]" #> s"${DisplayEqualNone}"
}
else {
//The default tag is the first tag of the resource, if it is empty, we use the API Tag.
@ -1542,9 +1563,9 @@ WIP to add comments on resource docs. This code copied from Sofit.
resourcesShowedInPage
}).map { i =>
// append the anchor to the current url. Maybe need to set the catalogue to all etc else another user might not find if the link is sent to them.
".end-point-anchor [href]" #> s"#${i.id}" &
".content-box__headline *" #> i.summary &
".content-box__headline [id]" #> i.id & // id for the anchor to find
EndPointAnchorHref #> s"#${i.id}" &
ContentBoxHeadline #> i.summary &
ContentBoxHeadlineId #> i.id & // id for the anchor to find
// Replace attribute named overview_text with the value (whole div/span element is replaced leaving just the text)
"@description *" #> i.description &
"@special_instructions *" #> i.specialInstructions &
@ -1607,34 +1628,34 @@ WIP to add comments on resource docs. This code copied from Sofit.
".connector_method_item_link *" #> i
} &
//required roles and related user information
"@roles_box [id]" #> s"roles_box_${i.id}" &
"@roles_box [style]" #> { if (i.roleInfos.isEmpty)
s"display: none"
RolesBoxId #> s"roles_box_${i.id}" &
RolesBoxStyle #> { if (i.roleInfos.isEmpty)
s"${DisplayEqualNone}"
else
s"display: block"
s"${DisplayEqualBlock}"
} &
// We generate multiple .role_items from roleInfos (including the form defined in index.html)
".role_item" #> i.roleInfos.map { r =>
"@roles__status" #> {if (! isLoggedIn)
s" - Please login to request this Role"
RoleItemClassCss #> i.roleInfos.map { r =>
RoleStatusNameCss #> {if (! isLoggedIn)
PleaseLoginToRequestThisRole
else if (r.userHasEntitlement)
s" - You have this Role."
YouHaveThisRole
else if (r.userHasEntitlementRequest)
s" - You have requested this Role. Please contact Open Bank Project team to grant your this role."
ContactOBPTeam
else
s" - You can request this Role."} &
"@roles__role_name" #> s"${r.role}" &
YouCanRequestThisRole} &
RolesRoleNameCss #> s"${r.role}" &
// ajaxSubmit will submit the form.
// The value of rolesBankId is given to bank_id_input field and the value of bank_id_input entered by user is given back to rolesBankId
"@roles__bank_id_input" #> SHtml.text({if (r.requiresBankId) rolesBankId else ""}, rolesBankId = _, if (r.requiresBankId) "type" -> "text" else "type" -> "hidden") &
"@roles__role_input" #> SHtml.text(s"${r.role}", entitlementRequestRoleName = _, "type" -> "hidden" ) &
RolesBankIdInput #> SHtml.text({if (r.requiresBankId) rolesBankId else ""}, rolesBankId = _, if (r.requiresBankId) "type" -> "text" else "type" -> "hidden") &
RolesRoleInput #> SHtml.text(s"${r.role}", entitlementRequestRoleName = _, "type" -> "hidden" ) &
"@roles__resource_id_input" #> text(i.id.toString, s => RolesResourceId = s, "type" -> "hidden", "id" -> s"roles__resource_id_input_${i.id}_${r.role}") &
"@roles__request_entitlement_button" #> Helper.ajaxSubmit("Request", disabledBtn, processEntitlementRequest) &
"@roles__entitlement_request_response [id]" #> s"roles__entitlement_request_response_${i.id}_${r.role}" &
"@roles__entitlement_request_button_box [style]" #> { if (! isLoggedIn || r.userHasEntitlement || r.userHasEntitlementRequest)
s"display: none"
RolesRequestEntitlementButton #> Helper.ajaxSubmit("Request", disabledBtn, processEntitlementRequest) &
RolesEntitlementRequestId #> s"roles__entitlement_request_response_${i.id}_${r.role}" &
RolesEntitlementRequestButtonBox #> { if (! isLoggedIn || r.userHasEntitlement || r.userHasEntitlementRequest)
s"${DisplayEqualNone}"
else
s"display: block"
s"${DisplayEqualBlock}"
}
} &
//
@ -1644,11 +1665,11 @@ WIP to add comments on resource docs. This code copied from Sofit.
"@success_response_body [id]" #> s"success_response_body_${i.id}" &
// The button. First argument is the text of the button (GET, POST etc). Second argument is function to call. Arguments to the func could be sent in third argument
"@call_button" #> Helper.ajaxSubmit(i.verb, disabledBtn, process) &
".favourites_operatino_id" #> text(i.id.toString, s => favouritesOperationId = s, "type" -> "hidden","class" -> "favourites_operatino_id") &
".favourites_operation_id" #> text(i.id.toString, s => favouritesOperationIdFromWebpage = s, "type" -> "hidden","class" -> "favourites_operation_id") &
".favourites_api_collection_id" #> text(apiCollectionId, s => favouritesApiCollectionId = s, "type" -> "hidden","class" -> "favourites_api_collection_id") &
".favourites_button" #> Helper.ajaxSubmit("★", disabledBtn, processFavourites, "id" -> s"favourites_button_${i.id.toString}",
if(apiCollectionIdParam.isDefined && getOperationIdsByApiCollectionId.nonEmpty) {"style" -> "color:#53C4EF"}
else if(getMyOperationIds.contains(i.id.toString)) {"style" -> "color:#53C4EF"}
if(apiCollectionIdParam.isDefined && webpageOperationIds.nonEmpty) {"style" -> "color:#53C4EF"}
else if(myWebpageOperationIds.contains(i.id.toString)) {"style" -> "color:#53C4EF"}
else {"style" -> "color:#767676"}
) &
".favourites_error_message [id]" #> s"favourites_error_message_${i.id}" &
@ -1675,7 +1696,6 @@ WIP to add comments on resource docs. This code copied from Sofit.
logger.debug("before showResources:")
def resourceDocsRequiresRole = ObpAPI.getRoot.flatMap(_.extractOpt[APIInfoJson400].map(_.resource_docs_requires_role)).openOr(false)
// Get a list of resource docs from the API server
// This will throw an exception if resource_docs key is not populated
// Convert the json representation to ResourceDoc (pretty much a one to one mapping)
@ -1691,9 +1711,9 @@ WIP to add comments on resource docs. This code copied from Sofit.
val glossaryItems = getGlossaryItemsJson.map(_.glossary_items).getOrElse(List())
if(glossaryItems.length==0) {
".resource [style]" #> s"display: none" &
".resource-error [style]" #> s"display: block" &
".content-box__headline *" #> {
ResourceStyleCss #> s"${DisplayEqualNone}" &
ResourceErrorStyleCss #> s"${DisplayEqualBlock}" &
ContentBoxHeadline #> {
if(!isLoggedIn)//If no resources, first check the login,
"Sorry, we could not return any Glossary Items. Note: OBP-20001: User not logged in."
else if(isLoggedIn && canReadGlossaryRole.isEmpty) //Then check the missing role
@ -1703,33 +1723,33 @@ WIP to add comments on resource docs. This code copied from Sofit.
}&{
if(isLoggedIn && canReadGlossaryRole.isEmpty){
//required roles and related user information
"@roles_box [id]" #> s"roles_box_CanReadGlossaryRoleInfo" &
"@roles_box [style]" #> {s"display: block"} &
RolesBoxId #> s"roles_box_CanReadGlossaryRoleInfo" &
RolesBoxStyle #> {s"${DisplayEqualBlock}"} &
// We generate multiple .role_items from roleInfos (including the form defined in index.html)
".role_item" #> canReadGlossaryRoleInfo.map { r =>
"@roles__status" #> {if (! isLoggedIn)
s" - Please login to request this Role"
RoleItemClassCss #> canReadGlossaryRoleInfo.map { r =>
RoleStatusNameCss #> {if (! isLoggedIn)
PleaseLoginToRequestThisRole
else if (r.userHasEntitlement)
s" - You have this Role."
YouHaveThisRole
else if (r.userHasEntitlementRequest)
s" - You have requested this Role. Please contact the administrators to grant you this role."
else
s" - You can request this Role."} &
"@roles__role_name" #> s"${r.role}" &
YouCanRequestThisRole} &
RolesRoleNameCss #> s"${r.role}" &
// ajaxSubmit will submit the form.
// The value of rolesBankId is given to bank_id_input field and the value of bank_id_input entered by user is given back to rolesBankId
"@roles__bank_id_input" #> SHtml.text({if (r.requiresBankId) rolesBankId else ""}, rolesBankId = _, if (r.requiresBankId) "type" -> "text" else "type" -> "hidden") &
"@roles__role_input" #> SHtml.text(s"${r.role}", entitlementRequestRoleName = _, "type" -> "hidden" ) &
"@roles__request_entitlement_button" #> Helper.ajaxSubmit("Request", disabledBtn, processEntitlementRequest) &
"@roles__entitlement_request_response [id]" #> s"roles__entitlement_request_response_${canReadGlossaryRoleInfo}_${r.role}" &
"@roles__entitlement_request_button_box [style]" #> { if (! isLoggedIn || r.userHasEntitlement || r.userHasEntitlementRequest)
s"display: none"
RolesBankIdInput #> SHtml.text({if (r.requiresBankId) rolesBankId else ""}, rolesBankId = _, if (r.requiresBankId) "type" -> "text" else "type" -> "hidden") &
RolesRoleInput #> SHtml.text(s"${r.role}", entitlementRequestRoleName = _, "type" -> "hidden" ) &
RolesRequestEntitlementButton #> Helper.ajaxSubmit("Request", disabledBtn, processEntitlementRequest) &
RolesEntitlementRequestId #> s"roles__entitlement_request_response_${canReadGlossaryRoleInfo}_${r.role}" &
RolesEntitlementRequestButtonBox #> { if (! isLoggedIn || r.userHasEntitlement || r.userHasEntitlementRequest)
s"${DisplayEqualNone}"
else
s"display: block"
s"${DisplayEqualBlock}"
}
}
}else{
"@roles_box [style]" #> s"display: none"
RolesBoxStyle #> s"${DisplayEqualNone}"
}
}
}else{
@ -1738,9 +1758,9 @@ WIP to add comments on resource docs. This code copied from Sofit.
val tag = i.title.replaceAll(" ", "-")
val operationId = allResourcesList.find(_.tags.head == tag).map(_.operation_id).getOrElse("")
// append the anchor to the current url. Maybe need to set the catalogue to all etc else another user might not find if the link is sent to them.
".end-point-anchor [href]" #> s"#${urlEncode(i.title.replaceAll(" ", "-"))}" &
".content-box__headline *" #> i.title &
".content-box__headline [id]" #> i.title.replaceAll(" ", "-") & // id for the anchor to find
EndPointAnchorHref #> s"#${urlEncode(i.title.replaceAll(" ", "-"))}" &
ContentBoxHeadline #> i.title &
ContentBoxHeadlineId #> i.title.replaceAll(" ", "-") & // id for the anchor to find
//i.title must be a proper tag, and will prepare the URL for it ...
".glossary_item_apis [href]" #> {
s"./?operation_id=${operationId.replace(".","_").replaceAll(" ","_")}#group-${tag}"
@ -1783,9 +1803,9 @@ WIP to add comments on resource docs. This code copied from Sofit.
}
} &
".message-doc" #> messageDocs.map { i =>
".end-point-anchor [href]" #> s"#${urlEncode(i.process.replaceAll(" ", "-"))}" &
".content-box__headline *" #> i.process &
".content-box__headline [id]" #> i.process.replaceAll(" ", "-") & // id for the anchor to find
EndPointAnchorHref #> s"#${urlEncode(i.process.replaceAll(" ", "-"))}" &
ContentBoxHeadline #> i.process &
ContentBoxHeadlineId #> i.process.replaceAll(" ", "-") & // id for the anchor to find
".outbound-topic *" #> stringToNodeSeq(i.outbound_topic.getOrElse("")) &
".inbound-topic *" #> stringToNodeSeq(i.inbound_topic.getOrElse("")) &
".outbound-message *" #> stringToNodeSeq(Helper.renderJson(i.example_outbound_message)) &

View File

@ -46,8 +46,9 @@ class Login {
ObpAPI.currentUser.map {
u =>
u.provider.toLowerCase() match {
case provider if provider.contains("google") => u.email
case provider if provider.contains("yahoo") => u.email
case provider if provider.contains("google") && !u.email.isEmpty => u.email
case provider if provider.contains("yahoo") && !u.email.isEmpty => u.email
case provider if provider.contains("microsoft") && !u.email.isEmpty => u.email
case _ => u.username
}
}

View File

@ -156,4 +156,22 @@ Returns a string which can be used for the title of the account
case JNothing => ""
case v => pretty(render(v))
}
//in OBP-API, before it returned v3_1_0, but now, only return v3.1.0
//But this field will be used in JavaScript/Webpage html id attribute, so need clean the field.
//To use any of the meta-characters (such as !"#$%&'()*+,./:;<=>?@[\]^`{|}~) as a literal part of a name,
//it must be escaped with with two backslashes: \\. For example, an element with id="foo.bar",
//can use the selector $("#foo\\.bar").
//So in Leftweb:
// eg: SetHtml(s"OBPv4.0.0_getBank", Text("Wrong OperationId")) --> not working
// eg: SetHtml(s"OBPv4_0_0_getBank", Text("Wrong OperationId")) --> working,
def covertObpOperationIdToWebpageId(operation_id: String) = {
operation_id.replace(".", "_").replaceAll(" ", "_")
}
def covertWebpageIdToObpOperationId(web_page_operation_id: String) = {
web_page_operation_id.replace("_", ".")
}
}

View File

@ -59,7 +59,9 @@
<summary class="api_group_item_details_summary"><a name="api_group_name" class="api_group_name" >API group</a></summary>
<ul>
<li name="api_glossary_item" class="api_glossary_item">
<a name="api_glossary_item_link" class="api_glossary_item_link" href="./glossary?#ATM.balance_inquiry_fee">Read More...</a>
<div name="api_glossary_item_text" class="api_glossary_item_text">
</div>
<a name="api_glossary_item_link" class="api_glossary_item_link" href="./glossary?#ATM.balance_inquiry_fee"></a>
</li>
<li name="api_list_item" class="api_list_item">
<a onclick="setApiLisItemLinkToClicked(this)" name="api_list_item_link" class="api_list_item_link" href="./#anchor">API call title</a>
@ -335,7 +337,7 @@
<h2>
<div id="anchor" class="content-box__headline"> The root of the API </div>
<form class="lift:form.ajax" name="favourites_ajax_form" class ="favourites_ajax_form">
<input type="hidden" class="favourites_operatino_id">
<input type="hidden" class="favourites_operation_id">
<input type="hidden" class="favourites_api_collection_id">
<button name="favourites_button" class="favourites_button"></button>
</form>

View File

@ -1,14 +1,14 @@
@font-face {
font-family: Roboto-Light;
font-family: Roboto-Light, sans-serif;
src: url(../font/Roboto-Light.ttf);
}
@font-face {
font-family: Roboto-Medium;
font-family: Roboto-Medium, sans-serif;
src: url(../font/Roboto-Medium.ttf);
}
@font-face {
font-family: Roboto-Regular;
font-family: Roboto-Regular,sans-serif;
src: url(../font/Roboto-Regular.ttf);
}

View File

@ -208,7 +208,7 @@ code,
kbd,
pre,
samp {
font-family: monospace, monospace;
font-family: monospace, monospace ,sans-serif;
font-size: 1em;
}

View File

@ -19,8 +19,8 @@ time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
font-size: 100%;
vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */

View File

@ -8,12 +8,12 @@
pre{
border: 0;
background-color: #333333;
font-family: 'Courier New', monospace;
font-family: 'Courier New', monospace ,sans-serif;
}
pre code{
border: 0;
background-color: #333333;
font-family: 'Courier New', monospace;
font-family: 'Courier New', monospace ,sans-serif;
color: #FFFFFF;
}
hr {
@ -80,7 +80,7 @@ hr {
#left_side_small_screen .logout-link{
margin-right: 28px;
margin-left: 28px;
font-family: Roboto-Regular;
font-family: Roboto-Regular,sans-serif;
font-size: 16px;
color: #333333;
text-align: center;
@ -128,13 +128,13 @@ hr {
}
.sidebar a {
text-decoration: none;
font-family: Roboto-Regular;
font-family: Roboto-Regular,sans-serif;
font-size: 16px;
color: #333333;
line-height: 24px;
}
.sidebar #sideba-api-key-div a {
font-family: Roboto-Medium;
font-family: Roboto-Medium, sans-serif;
}
#small-screen-navbar{
text-align: center;
@ -264,7 +264,7 @@ hr {
#left_side_small_screen .logout-link{
margin-right: 28px;
margin-left: 28px;
font-family: Roboto-Regular;
font-family: Roboto-Regular,sans-serif;
font-size: 16px;
color: #333333;
text-align: center;
@ -312,13 +312,13 @@ hr {
}
.sidebar a {
text-decoration: none;
font-family: Roboto-Regular;
font-family: Roboto-Regular,sans-serif;
font-size: 16px;
color: #333333;
line-height: 24px;
}
.sidebar #sideba-api-key-div a {
font-family: Roboto-Medium;
font-family: Roboto-Medium, sans-serif;
}
#small-screen-navbar{
text-align: center;
@ -409,7 +409,7 @@ hr {
padding: 5px 12px 7px;
margin: 0;
background-color: #53C4EF;
font-family: Roboto-Regular;
font-family: Roboto-Regular,sans-serif;
font-size: 12px;
color: #FFFFFF;
letter-spacing: 0;

View File

@ -1,7 +1,7 @@
@import url(/media/css/fonts.css);
@font-face {
font-family: Roboto-Light;
font-family: Roboto-Light, sans-serif;
src: url(/media/font/Roboto-Light.ttf);
}
body {
@ -69,7 +69,7 @@ a img {
.header .site-title-box__text {
display: inline-block;
margin: 0;
font-family: Roboto-Light;
font-family: Roboto-Light, sans-serif;
font-size:18px;
color: #333333;
letter-spacing: 0;
@ -106,7 +106,7 @@ a img {
}
.sign-box__text {
font-family: Roboto-Regular;
font-family: Roboto-Regular,sans-serif;
font-size: 16px;
color: #FFFFFF;
text-align: center;
@ -192,7 +192,7 @@ a img {
padding: 10px 0 10px 25px;
margin: 0;
float: left;
font-family: Roboto-Regular;
font-family: Roboto-Regular,sans-serif;
font-size: 14px;
color: #FFFFFF;
letter-spacing: 0;
@ -206,7 +206,7 @@ a img {
padding: 0;
margin: 26px 25px 22px 0;
letter-spacing: 0;
font-family: Roboto-Regular;
font-family: Roboto-Regular,sans-serif;
font-size: 14px;
color: #FFFFFF;
letter-spacing: 0;
@ -242,7 +242,7 @@ a img {
display: inline-block;
margin: 0 0 0 0;
text-decoration: none;
font-family: Roboto-Regular;
font-family: Roboto-Regular,sans-serif;
font-size: 14px;
color: #FFFFFF;
letter-spacing: 0;
@ -282,7 +282,7 @@ a img {
color: white;
background: #767676;
height: 52px;
font-family: Roboto-Medium;
font-family: Roboto-Medium, sans-serif;
font-size: 18px;
color: #FFFFFF;
line-height: 28px;
@ -305,7 +305,7 @@ a img {
.info-box__headline {
margin: 0 0 0 0;
padding: 16px 0 16px 25px;
font-family: Roboto-Light;
font-family: Roboto-Light, sans-serif;
font-size: 28px;
color: #FFFFFF;
letter-spacing: 0;
@ -313,7 +313,7 @@ a img {
}
.info-box__about_selected {
font-family: Roboto-Light;
font-family: Roboto-Light, sans-serif;
font-size: 18px;
color: #FFFFFF;
line-height: 28px;
@ -361,14 +361,14 @@ a img {
.option-section .option-box__headline {
padding-top: 0;
padding-bottom: 8px;
font-family: Roboto-Regular;
font-family: Roboto-Regular,sans-serif;
font-size: 16px;
color: #333333;
letter-spacing: 0;
line-height: 24px;
}
.option-section select {
font-family: Roboto-Regular;
font-family: Roboto-Regular,sans-serif;
/*font-size: 16px;*/
/*color: #333333;*/
/*line-height: 24px;*/
@ -482,7 +482,7 @@ a img {
margin: -240px 0 32px 0;
font-weight: 400;
padding: 240px 0 0 0;
font-family: Roboto-Light;
font-family: Roboto-Light, sans-serif;
font-size: 28px;
color: #333333;
letter-spacing: 0;
@ -530,7 +530,7 @@ a img {
margin: -222px 0 32px 0;
font-weight: 400;
padding: 222px 0 0 0;
font-family: Roboto-Light;
font-family: Roboto-Light, sans-serif;
font-size: 28px;
color: #333333;
letter-spacing: 0;
@ -553,7 +553,7 @@ a img {
width: 100%;
/*padding: 5px;*/
max-width: 1000px;
font-family: Roboto-Regular;
font-family: Roboto-Regular,sans-serif;
font-size: 16px;[
color: #333333;]
line-height: 24px;
@ -620,7 +620,7 @@ a img {
margin-right: 20px;
margin-top: 0;
margin-bottom: 13px;
font-family: Roboto-Regular;
font-family: Roboto-Regular,sans-serif;
font-size: 16px;
color: #333333;
line-height: 24px;
@ -631,7 +631,7 @@ a img {
margin-right: 20px;
margin-top: 0;
margin-bottom: 13px;
font-family: Roboto-Regular;
font-family: Roboto-Regular,sans-serif;
font-size: 16px;
color: #333333;
line-height: 24px;
@ -643,7 +643,7 @@ a img {
margin-right: 20px;
margin-top: 0;
margin-bottom: 13px;
font-family: Roboto-Regular;
font-family: Roboto-Regular,sans-serif;
font-size: 16px;
color: #333333;
line-height: 24px;
@ -659,7 +659,7 @@ a img {
font-size: 8px;
margin-left: 25px;
margin-top: 13px;
font-family: Roboto-Regular;
font-family: Roboto-Regular,sans-serif;
color: #333333;
line-height: 24px;
}
@ -703,7 +703,7 @@ a img {
}
.api_group_item a {
text-decoration: none;
font-family: Roboto-Regular;
font-family: Roboto-Regular,sans-serif;
font-size: 16px;
color: #333333;
line-height: 24px;
@ -730,7 +730,7 @@ a img {
}
.api_list_item a {
text-decoration: none;
font-family: Roboto-Regular;
font-family: Roboto-Regular,sans-serif;
font-size: 12px;
color: #333333;
line-height: 4px;
@ -739,8 +739,8 @@ a img {
color: #333333;
}
.api_glossary_item a {
text-decoration: none;
font-family: Roboto-Regular;
text-decoration: underline;
font-family: Roboto-Regular,sans-serif;
font-size: 10px;
color: #333333;
line-height: 4px;
@ -771,7 +771,7 @@ a img {
-webkit-appearance: none;
margin-top: 0px;
width: 100%;
font-family: Roboto-Regular;
font-family: Roboto-Regular,sans-serif;
font-size: 16px;
color: #333333;
line-height: 24px;
@ -787,7 +787,7 @@ a img {
margin-left: 0 ;
float: left;
border: 1px solid #333333;
font-family: Roboto-Regular;
font-family: Roboto-Regular,sans-serif;
font-size: 16px;
color: #333333;
text-align: center;
@ -840,7 +840,7 @@ a img {
margin-top: 13px;
padding: 14px 19px 44px 19px;
overflow-x: auto;
font-family: Roboto-Regular;
font-family: Roboto-Regular,sans-serif;
font-size: 16px;
color: #FFFFFF;
line-height: 24px;
@ -851,7 +851,7 @@ a img {
float: left;
margin-right: 20px;
margin-top: 5px;
font-family: Roboto-Regular;
font-family: Roboto-Regular,sans-serif;
font-size: 16px;
color: #333333;
line-height: 24px;
@ -1126,7 +1126,7 @@ code,
kbd,
pre,
samp {
font-family: Roboto-Regular;
font-family: Roboto-Regular,sans-serif;
font-size: 16px;
color: #FFFFFF;
line-height: 24px;
@ -1136,7 +1136,7 @@ samp {
.glossary kbd,
.glossary pre,
.glossary samp {
font-family: Roboto-Regular;
font-family: Roboto-Regular,sans-serif;
font-size: 16px;
color: #FFFFFF;
line-height: 24px;
@ -1387,7 +1387,7 @@ a.end-point-anchor {
letter-spacing: 0;
margin: 0 0 0 5px;
clear: both;
font-family: Roboto-Regular;
font-family: Roboto-Regular,sans-serif;
font-size: 12px;
line-height: 24px;
}
@ -1418,7 +1418,7 @@ a.end-point-anchor {
.username {
color: #000;
font-family: Roboto-Regular;
font-family: Roboto-Regular,sans-serif;
font-size: 16px;
color: #333333;
line-height: 24px;
@ -1489,7 +1489,7 @@ a.end-point-anchor {
}
.select2-results__option {
padding: 6px;
font-family: Roboto-Regular;
font-family: Roboto-Regular,sans-serif;
font-size: 16px;
color: #333333;
line-height: 24px;
@ -1500,7 +1500,7 @@ a.end-point-anchor {
}
.select2-container--default .select2-selection--single .select2-selection__rendered {
font-family: Roboto-Regular;
font-family: Roboto-Regular,sans-serif;
font-size: 16px;
color: #333333;
line-height: 24px;
@ -1528,7 +1528,7 @@ a.end-point-anchor {
#resouce-footer .container p{
margin-left: 25px;
font-family: Roboto-Regular;
font-family: Roboto-Regular,sans-serif;
font-size: 16px;
color: #333333;
line-height: 24px;
@ -1584,7 +1584,7 @@ footer .footer-links-box {
}
footer .footer-link-box__link {
font-family: Roboto-Regular;
font-family: Roboto-Regular,sans-serif;
font-size: 14px;
color: #FFFFFF;
letter-spacing: 0;
@ -1630,7 +1630,7 @@ footer .footer-box__paragraph {
}
footer .footer-box__paragraph2 {
margin-bottom: 0px;
font-family: Roboto-Regular;
font-family: Roboto-Regular,sans-serif;
font-size: 14px;
color: #FFFFFF;
letter-spacing: 0;
@ -1643,7 +1643,7 @@ footer .footer-box__paragraph2 a:hover {
}
footer a,span,
footer a:hover{
font-family: Roboto-Regular;
font-family: Roboto-Regular,sans-serif;
font-size: 14px;
color: #FFFFFF;
letter-spacing: 0;
@ -1663,7 +1663,7 @@ footer a:hover{
}
.api_group_item_small_screen a {
text-decoration: none;
font-family: Roboto-Light;
font-family: Roboto-Light, sans-serif;
font-size: 19px;
color: #333333;
line-height: 27px;
@ -1765,7 +1765,7 @@ p{
.content-box__available-since span{
color: #000000;
font-size: 8px;
font-family: Roboto-Regular;
font-family: Roboto-Regular,sans-serif;
}
#dropdownMenuButton{
@ -1800,4 +1800,16 @@ details > summary.api_group_item_details_summary{
content: "";
clear: both;
display: table;
}
.api_glossary_item_text{
text-decoration: none;
font-size: 10px;
color: #333333;
line-height: 15px;
word-wrap: break-word;
white-space: pre-wrap;
width: 240px;
margin-top: 10px;
overflow: hidden;
}

View File

@ -638,7 +638,6 @@ span#accountsMsg
.tagID{
padding: 5px 10px;
background: #F3F9F8;
display: inline-block;
margin-right: 10px;
display: inline-block;
border: 2px solid #CBCBCB;
@ -688,7 +687,6 @@ span#accountsMsg
.image-holder {
padding: 5px 10px;
background: #F3F9F8;
display: inline-block;
margin-right: 10px;
display: inline-block;
border: 2px solid #CBCBCB;

View File

@ -54,7 +54,7 @@ $(document).ready(function(){
/* make Save / Cancel / Delete unclickable for not selected columns */
var $actionButtons = $(".action");
for(i=0; i<$actionButtons.length; i++){
for(let i=0; i<$actionButtons.length; i++){
var $action = $($actionButtons[i]);
if($action.attr("data-id") !== viewId)
$action.attr("disabled", "disabled");
@ -97,7 +97,7 @@ $(document).ready(function(){
function getOptions () {
var aliasOptions = new Array("public", "private", "none (display real names only)")
var option = ""
for(a in aliasOptions){
for(let a in aliasOptions){
var alias = aliasOptions[a]
option += "<option value='"+alias+"'>"+alias+"</option>"
}
@ -152,7 +152,7 @@ $(document).ready(function(){
/* make Save / Cancel / Delete clickable for not selected columns */
var $actionButtons = $(".action");
for(i=0; i<$actionButtons.length; i++){
for(let i=0; i<$actionButtons.length; i++){
var $action = $($actionButtons[i]);
$action.removeAttr("disabled");
}
@ -174,7 +174,7 @@ $(document).ready(function(){
var $permissions = $("input.permission_value_cb");
var allowedActions = new Array();
for(i=0; i < $permissions.length; i++){
for(let i=0; i < $permissions.length; i++){
var $permission = $($permissions[i])
if($permission.attr("data-viewid") == viewId && $permission.is(':checked')){
allowedActions.push($permission.attr("name"));

View File

@ -34,7 +34,7 @@ function openNav() {
$(".api-info-section").css("display","none");
$("#right_side").css("display","none");
$("#small-nav-collapse").attr("onclick","closeNav()");
logOnButton = $("#start-login").text().indexOf("Log on");
let logOnButton = $("#start-login").text().indexOf("Log on");
if (logOnButton >= 0){
$("#left_side_small_screen .settings-box").css("display","none")
}
@ -101,7 +101,7 @@ $(document).ready(function() {
var urlParameterFilteredVersionAndTagsAndContent = urlParameters.filter(function(item) {
return !item.includes("version")
&& (!item.includes("tags"))
&& (!item.includes("api-collection-id"))
&& (!item.includes(ApiCollectionId))
&& (!item.includes("content"))
&& (!item.includes("functions"))
&& (!item.includes("space_bank_id"))
@ -110,7 +110,7 @@ $(document).ready(function() {
//and update the value for .version class
var versions =$(".breadcrumbs .breadcrumbs__row .breadcrumbs__list .version")
if(urlParameterFilteredVersionAndTagsAndContent !== ""){
for (i = 0; i < versions.length; i++) {
for (let i = 0; i < versions.length; i++) {
$(".breadcrumbs .breadcrumbs__row .breadcrumbs__list .version")[i].href=versions[i].href+"&"+urlParameterFilteredVersionAndTagsAndContent
}
}
@ -122,6 +122,11 @@ $(document).ready(function() {
$("#right_side .resource:nth-child(5) .content-box__headline").css("padding","332px 0 0 0").css("margin","0 0 32px 0");
$("#right_side .resource:nth-child(5) .content-box .end-point-anchor form").css("padding","332px 0 0 0").css("margin","0 0 32px 0");;
}
$(".option-box").click(function(){
$(".select2-dropdown--below").css('width','187px');
});
});

View File

@ -1,27 +1,27 @@
<div>
<form method="post" action="#">
<table>
<tr>
<td colspan="2" id="loginText">login</td>
</tr>
<tr>
<td id="emailAddressText">email address </td>
<td><user:email /></td>
</tr>
<tr>
<td id="passwordText"> password </td>
<td><user:password /></td>
</tr>
<tr>
<td id="recoverPasswordLink" >
<a href="#">link text</a>
</td>
</tr>
<tr>
<td colspan="2">
<user:submit />
</td>
</tr>
</table>
</form>
</div>
<!--<div>-->
<!--<form method="post" action="#">-->
<!-- <table>-->
<!-- <tr>-->
<!-- <td colspan="2" id="loginText">login</td>-->
<!-- </tr> -->
<!-- <tr>-->
<!-- <td id="emailAddressText">email address </td>-->
<!-- <td><user:email /></td>-->
<!-- </tr> -->
<!-- <tr>-->
<!-- <td id="passwordText"> password </td>-->
<!-- <td><user:password /></td>-->
<!-- </tr> -->
<!-- <tr>-->
<!-- <td id="recoverPasswordLink" >-->
<!-- <a href="#">link text</a>-->
<!-- </td> -->
<!-- </tr>-->
<!-- <tr>-->
<!-- <td colspan="2">-->
<!-- <user:submit />-->
<!-- </td>-->
<!-- </tr>-->
<!-- </table> -->
<!-- </form>-->
<!--</div>-->

View File

@ -1,30 +1,30 @@
<div>
<form method="post" action="#">
<table>
<tr>
<td colspan="2" id="loginText">login</td>
</tr>
<tr>
<td id="emailAddressText">email address </td>
<td><user:email /></td>
</tr>
<tr>
<td id="passwordText"> password </td>
<td><user:password /></td>
</tr>
<tr>
<td id="SignUpLink" >
<a href="#">link text</a>
</td>
<td id="recoverPasswordLink" >
<a href="#">link text</a>
</td>
</tr>
<tr>
<td colspan="2">
<user:submit />
</td>
</tr>
</table>
</form>
</div>
<!--<div>-->
<!--<form method="post" action="#">-->
<!-- <table>-->
<!-- <tr>-->
<!-- <td colspan="2" id="loginText">login</td>-->
<!-- </tr> -->
<!-- <tr>-->
<!-- <td id="emailAddressText">email address </td>-->
<!-- <td><user:email /></td>-->
<!-- </tr> -->
<!-- <tr>-->
<!-- <td id="passwordText"> password </td>-->
<!-- <td><user:password /></td>-->
<!-- </tr> -->
<!-- <tr>-->
<!-- <td id="SignUpLink" >-->
<!-- <a href="#">link text</a>-->
<!-- </td>-->
<!-- <td id="recoverPasswordLink" >-->
<!-- <a href="#">link text</a>-->
<!-- </td> -->
<!-- </tr>-->
<!-- <tr>-->
<!-- <td colspan="2">-->
<!-- <user:submit />-->
<!-- </td>-->
<!-- </tr>-->
<!-- </table> -->
<!-- </form>-->
<!--</div>-->

View File

@ -1,7 +1,7 @@
<li class="comment">
<span id="" class="commentLink">#0</span>
<span class="text">The comment goes here</span>
<br />
<span class="commentDate">2012/10/12 13:23</span>
<span class="userInfo">Commenter's email address goes here</span>
</li>
<!--<li class="comment">-->
<!-- <span id="" class="commentLink">#0</span>-->
<!-- <span class="text">The comment goes here</span>-->
<!-- <br />-->
<!-- <span class="commentDate">2012/10/12 13:23</span>-->
<!-- <span class="userInfo">Commenter's email address goes here</span>-->
<!--</li> -->

View File

@ -28,8 +28,8 @@ along with this program. If not, see www.gnu.org/licenses/
<!doctype html>
<html>
<!DOCTYPE html>
<html lang="en">
<head>
<title>API Explorer</title>