Use-defined CSS (#2458)

This commit is contained in:
Naoki Takezoe
2020-06-08 15:50:37 +09:00
committed by GitHub
parent eebabf9b08
commit 2a1edeaca3
6 changed files with 55 additions and 27 deletions

View File

@@ -46,7 +46,7 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
"ssh" -> mapping( "ssh" -> mapping(
"enabled" -> trim(label("SSH access", boolean())), "enabled" -> trim(label("SSH access", boolean())),
"host" -> trim(label("SSH host", optional(text()))), "host" -> trim(label("SSH host", optional(text()))),
"port" -> trim(label("SSH port", optional(number()))), "port" -> trim(label("SSH port", optional(number())))
)(Ssh.apply), )(Ssh.apply),
"useSMTP" -> trim(label("SMTP", boolean())), "useSMTP" -> trim(label("SMTP", boolean())),
"smtp" -> optionalIfNotChecked( "smtp" -> optionalIfNotChecked(
@@ -91,6 +91,7 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
)(OIDC.apply) )(OIDC.apply)
), ),
"skinName" -> trim(label("AdminLTE skin name", text(required))), "skinName" -> trim(label("AdminLTE skin name", text(required))),
"userDefinedCss" -> trim(label("User-defined CSS", optional(text()))),
"showMailAddress" -> trim(label("Show mail address", boolean())), "showMailAddress" -> trim(label("Show mail address", boolean())),
"webhook" -> mapping( "webhook" -> mapping(
"blockPrivateAddress" -> trim(label("Block private address", boolean())), "blockPrivateAddress" -> trim(label("Block private address", boolean())),
@@ -536,7 +537,8 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
} }
} }
private def members: Constraint = new Constraint() { private def members: Constraint =
new Constraint() {
override def validate(name: String, value: String, messages: Messages): Option[String] = { override def validate(name: String, value: String, messages: Messages): Option[String] = {
if (value.split(",").exists { if (value.split(",").exists {
_.split(":") match { case Array(userName, isManager) => isManager.toBoolean } _.split(":") match { case Array(userName, isManager) => isManager.toBoolean }
@@ -545,7 +547,8 @@ trait SystemSettingsControllerBase extends AccountManagementControllerBase {
} }
} }
protected def disableByNotYourself(paramName: String): Constraint = new Constraint() { protected def disableByNotYourself(paramName: String): Constraint =
new Constraint() {
override def validate(name: String, value: String, messages: Messages): Option[String] = { override def validate(name: String, value: String, messages: Messages): Option[String] = {
params.get(paramName).flatMap { userName => params.get(paramName).flatMap { userName =>
if (userName == context.loginAccount.get.userName && params.get("removed") == Some("true")) if (userName == context.loginAccount.get.userName && params.get("removed") == Some("true"))

View File

@@ -70,6 +70,7 @@ trait SystemSettingsService {
} }
} }
props.setProperty(SkinName, settings.skinName.toString) props.setProperty(SkinName, settings.skinName.toString)
settings.userDefinedCss.foreach(x => props.setProperty(UserDefinedCss, x))
props.setProperty(ShowMailAddress, settings.showMailAddress.toString) props.setProperty(ShowMailAddress, settings.showMailAddress.toString)
props.setProperty(WebHookBlockPrivateAddress, settings.webHook.blockPrivateAddress.toString) props.setProperty(WebHookBlockPrivateAddress, settings.webHook.blockPrivateAddress.toString)
props.setProperty(WebHookWhitelist, settings.webHook.whitelist.mkString("\n")) props.setProperty(WebHookWhitelist, settings.webHook.whitelist.mkString("\n"))
@@ -106,7 +107,11 @@ trait SystemSettingsService {
getOptionValue[String](props, SshHost, None).map(_.trim), getOptionValue[String](props, SshHost, None).map(_.trim),
getOptionValue(props, SshPort, Some(DefaultSshPort)) getOptionValue(props, SshPort, Some(DefaultSshPort))
), ),
getValue(props, UseSMTP, getValue(props, Notification, false)), // handle migration scenario from only notification to useSMTP getValue(
props,
UseSMTP,
getValue(props, Notification, false)
), // handle migration scenario from only notification to useSMTP
if (getValue(props, UseSMTP, getValue(props, Notification, false))) { if (getValue(props, UseSMTP, getValue(props, Notification, false))) {
Some( Some(
Smtp( Smtp(
@@ -154,6 +159,7 @@ trait SystemSettingsService {
None None
}, },
getValue(props, SkinName, "skin-blue"), getValue(props, SkinName, "skin-blue"),
getOptionValue(props, UserDefinedCss, None),
getValue(props, ShowMailAddress, false), getValue(props, ShowMailAddress, false),
WebHook(getValue(props, WebHookBlockPrivateAddress, false), getSeqValue(props, WebHookWhitelist, "")), WebHook(getValue(props, WebHookBlockPrivateAddress, false), getSeqValue(props, WebHookWhitelist, "")),
Upload( Upload(
@@ -191,6 +197,7 @@ object SystemSettingsService {
oidcAuthentication: Boolean, oidcAuthentication: Boolean,
oidc: Option[OIDC], oidc: Option[OIDC],
skinName: String, skinName: String,
userDefinedCss: Option[String],
showMailAddress: Boolean, showMailAddress: Boolean,
webHook: WebHook, webHook: WebHook,
upload: Upload upload: Upload
@@ -212,7 +219,8 @@ object SystemSettingsService {
.fold(base)(_ + base.dropWhile(_ != ':')) .fold(base)(_ + base.dropWhile(_ != ':'))
} }
def sshAddress: Option[SshAddress] = ssh.sshHost.collect { def sshAddress: Option[SshAddress] =
ssh.sshHost.collect {
case host if ssh.enabled => case host if ssh.enabled =>
SshAddress(host, ssh.sshPort.getOrElse(DefaultSshPort), "git") SshAddress(host, ssh.sshPort.getOrElse(DefaultSshPort), "git")
} }
@@ -265,7 +273,7 @@ object SystemSettingsService {
host: String, host: String,
port: Int, port: Int,
user: Option[String], user: Option[String],
password: Option[String], password: Option[String]
) )
case class SshAddress(host: String, port: Int, genericUser: String) case class SshAddress(host: String, port: Int, genericUser: String)
@@ -318,6 +326,7 @@ object SystemSettingsService {
private val OidcClientSecret = "oidc.client_secret" private val OidcClientSecret = "oidc.client_secret"
private val OidcJwsAlgorithm = "oidc.jws_algorithm" private val OidcJwsAlgorithm = "oidc.jws_algorithm"
private val SkinName = "skinName" private val SkinName = "skinName"
private val UserDefinedCss = "userDefinedCss"
private val ShowMailAddress = "showMailAddress" private val ShowMailAddress = "showMailAddress"
private val WebHookBlockPrivateAddress = "webhook.block_private_address" private val WebHookBlockPrivateAddress = "webhook.block_private_address"
private val WebHookWhitelist = "webhook.whitelist" private val WebHookWhitelist = "webhook.whitelist"

View File

@@ -94,6 +94,14 @@
</div> </div>
</div> </div>
<!--====================================================================--> <!--====================================================================-->
<!-- User-defined CSS -->
<!--====================================================================-->
<hr>
<label><span class="strong">User-defined CSS</span></label>
<fieldset>
<textarea name="userDefinedCss" class="form-control" style="height: 100px;">@context.settings.userDefinedCss</textarea>
</fieldset>
<!--====================================================================-->
<!-- Account registration --> <!-- Account registration -->
<!--====================================================================--> <!--====================================================================-->
<hr> <hr>

View File

@@ -55,6 +55,9 @@
<meta name="go-import" content="@context.baseUrl.replaceFirst("^https?://", "")/@repository.owner/@repository.name git @repository.httpUrl" /> <meta name="go-import" content="@context.baseUrl.replaceFirst("^https?://", "")/@repository.owner/@repository.name git @repository.httpUrl" />
} }
<script src="@helpers.assets("/vendors/AdminLTE-2.4.2/js/adminlte.min.js")" type="text/javascript"></script> <script src="@helpers.assets("/vendors/AdminLTE-2.4.2/js/adminlte.min.js")" type="text/javascript"></script>
@context.settings.userDefinedCss.map { css =>
<style type="text/css">@css</style>
}
</head> </head>
<body class="@context.settings.skinName page-load @if(body.toString.contains("menu-item-hover")){sidebar-mini} @if(context.sidebarCollapse){sidebar-collapse}"> <body class="@context.settings.skinName page-load @if(body.toString.contains("menu-item-hover")){sidebar-mini} @if(context.sidebarCollapse){sidebar-collapse}">
<div class="wrapper"> <div class="wrapper">

View File

@@ -52,6 +52,7 @@ trait ServiceSpecBase extends MockitoSugar {
oidcAuthentication = false, oidcAuthentication = false,
oidc = None, oidc = None,
skinName = "skin-blue", skinName = "skin-blue",
userDefinedCss = None,
showMailAddress = false, showMailAddress = false,
webHook = SystemSettingsService.WebHook( webHook = SystemSettingsService.WebHook(
blockPrivateAddress = false, blockPrivateAddress = false,
@@ -114,7 +115,8 @@ trait ServiceSpecBase extends MockitoSugar {
} }
def generateNewIssue(userName: String, repositoryName: String, loginUser: String = "root")( def generateNewIssue(userName: String, repositoryName: String, loginUser: String = "root")(
implicit s: Session implicit
s: Session
): Int = { ): Int = {
dummyService.insertIssue( dummyService.insertIssue(
owner = userName, owner = userName,
@@ -130,7 +132,8 @@ trait ServiceSpecBase extends MockitoSugar {
} }
def generateNewPullRequest(base: String, request: String, loginUser: String)( def generateNewPullRequest(base: String, request: String, loginUser: String)(
implicit s: Session implicit
s: Session
): (Issue, PullRequest) = { ): (Issue, PullRequest) = {
implicit val context = Context(createSystemSettings(), None, this.request) implicit val context = Context(createSystemSettings(), None, this.request)
val Array(baseUserName, baseRepositoryName, baesBranch) = base.split("/") val Array(baseUserName, baseRepositoryName, baesBranch) = base.split("/")

View File

@@ -138,6 +138,7 @@ class AvatarImageProviderSpec extends FunSpec with MockitoSugar {
oidcAuthentication = false, oidcAuthentication = false,
oidc = None, oidc = None,
skinName = "skin-blue", skinName = "skin-blue",
userDefinedCss = None,
showMailAddress = false, showMailAddress = false,
webHook = WebHook( webHook = WebHook(
blockPrivateAddress = false, blockPrivateAddress = false,
@@ -157,7 +158,8 @@ class AvatarImageProviderSpec extends FunSpec with MockitoSugar {
class AvatarImageProviderImpl(account: Option[Account]) extends AvatarImageProvider with RequestCache { class AvatarImageProviderImpl(account: Option[Account]) extends AvatarImageProvider with RequestCache {
def toHtml(userName: String, size: Int, mailAddress: String = "", tooltip: Boolean = false)( def toHtml(userName: String, size: Int, mailAddress: String = "", tooltip: Boolean = false)(
implicit context: Context implicit
context: Context
): Html = getAvatarImageHtml(userName, size, mailAddress, tooltip) ): Html = getAvatarImageHtml(userName, size, mailAddress, tooltip)
override def getAccountByMailAddress(mailAddress: String)(implicit context: Context): Option[Account] = account override def getAccountByMailAddress(mailAddress: String)(implicit context: Context): Option[Account] = account