mirror of
https://github.com/gitbucket/gitbucket.git
synced 2025-12-30 12:19:58 +01:00
Enum support in custom fields (#3195)
This commit is contained in:
6
src/main/resources/update/gitbucket-core_4.39.xml
Normal file
6
src/main/resources/update/gitbucket-core_4.39.xml
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<changeSet>
|
||||
<addColumn tableName="CUSTOM_FIELD">
|
||||
<column name="CONSTRAINTS" type="varchar(200)" nullable="true"/>
|
||||
</addColumn>
|
||||
</changeSet>
|
||||
@@ -115,5 +115,6 @@ object GitBucketCoreModule
|
||||
new Version("4.38.1"),
|
||||
new Version("4.38.2"),
|
||||
new Version("4.38.3"),
|
||||
new Version("4.38.4")
|
||||
new Version("4.38.4"),
|
||||
new Version("4.39.0", new LiquibaseMigration("update/gitbucket-core_4.39.xml")),
|
||||
)
|
||||
|
||||
@@ -126,6 +126,7 @@ trait RepositorySettingsControllerBase extends ControllerBase {
|
||||
case class CustomFieldForm(
|
||||
fieldName: String,
|
||||
fieldType: String,
|
||||
constraints: Option[String],
|
||||
enableForIssues: Boolean,
|
||||
enableForPullRequests: Boolean
|
||||
)
|
||||
@@ -133,6 +134,7 @@ trait RepositorySettingsControllerBase extends ControllerBase {
|
||||
val customFieldForm = mapping(
|
||||
"fieldName" -> trim(label("Field name", text(required, maxlength(100)))),
|
||||
"fieldType" -> trim(label("Field type", text(required))),
|
||||
"constraints" -> trim(label("Constraints", optional(text()))),
|
||||
"enableForIssues" -> trim(label("Enable for issues", boolean(required))),
|
||||
"enableForPullRequests" -> trim(label("Enable for pull requests", boolean(required))),
|
||||
)(CustomFieldForm.apply)
|
||||
@@ -511,6 +513,7 @@ trait RepositorySettingsControllerBase extends ControllerBase {
|
||||
repository.name,
|
||||
form.fieldName,
|
||||
form.fieldType,
|
||||
if (form.fieldType == "enum") form.constraints else None,
|
||||
form.enableForIssues,
|
||||
form.enableForPullRequests
|
||||
)
|
||||
@@ -533,6 +536,7 @@ trait RepositorySettingsControllerBase extends ControllerBase {
|
||||
params("fieldId").toInt,
|
||||
form.fieldName,
|
||||
form.fieldType,
|
||||
if (form.fieldType == "enum") form.constraints else None,
|
||||
form.enableForIssues,
|
||||
form.enableForPullRequests
|
||||
)
|
||||
|
||||
@@ -5,6 +5,7 @@ import gitbucket.core.service.RepositoryService.RepositoryInfo
|
||||
import gitbucket.core.util.StringUtil
|
||||
import gitbucket.core.view.helpers
|
||||
import org.scalatra.i18n.Messages
|
||||
import play.twirl.api.Html
|
||||
|
||||
trait CustomFieldComponent extends TemplateComponent { self: Profile =>
|
||||
import profile.api._
|
||||
@@ -15,10 +16,11 @@ trait CustomFieldComponent extends TemplateComponent { self: Profile =>
|
||||
val fieldId = column[Int]("FIELD_ID", O AutoInc)
|
||||
val fieldName = column[String]("FIELD_NAME")
|
||||
val fieldType = column[String]("FIELD_TYPE")
|
||||
val constraints = column[Option[String]]("CONSTRAINTS")
|
||||
val enableForIssues = column[Boolean]("ENABLE_FOR_ISSUES")
|
||||
val enableForPullRequests = column[Boolean]("ENABLE_FOR_PULL_REQUESTS")
|
||||
def * =
|
||||
(userName, repositoryName, fieldId, fieldName, fieldType, enableForIssues, enableForPullRequests)
|
||||
(userName, repositoryName, fieldId, fieldName, fieldType, constraints, enableForIssues, enableForPullRequests)
|
||||
.<>(CustomField.tupled, CustomField.unapply)
|
||||
|
||||
def byPrimaryKey(userName: String, repositoryName: String, fieldId: Int) =
|
||||
@@ -31,17 +33,28 @@ case class CustomField(
|
||||
repositoryName: String,
|
||||
fieldId: Int = 0,
|
||||
fieldName: String,
|
||||
fieldType: String, // long, double, string, or date
|
||||
fieldType: String, // long, double, string, date, or enum
|
||||
constraints: Option[String],
|
||||
enableForIssues: Boolean,
|
||||
enableForPullRequests: Boolean
|
||||
)
|
||||
|
||||
trait CustomFieldBehavior {
|
||||
def createHtml(repository: RepositoryInfo, fieldId: Int)(implicit conext: Context): String
|
||||
def fieldHtml(repository: RepositoryInfo, issueId: Int, fieldId: Int, value: String, editable: Boolean)(
|
||||
def createHtml(repository: RepositoryInfo, fieldId: Int, fieldName: String, constraints: Option[String])(
|
||||
implicit context: Context
|
||||
): String
|
||||
def validate(name: String, value: String, messages: Messages): Option[String]
|
||||
def fieldHtml(
|
||||
repository: RepositoryInfo,
|
||||
issueId: Int,
|
||||
fieldId: Int,
|
||||
fieldName: String,
|
||||
constraints: Option[String],
|
||||
value: String,
|
||||
editable: Boolean
|
||||
)(
|
||||
implicit context: Context
|
||||
): String
|
||||
def validate(name: String, constraints: Option[String], value: String, messages: Messages): Option[String]
|
||||
}
|
||||
|
||||
object CustomFieldBehavior {
|
||||
@@ -49,7 +62,7 @@ object CustomFieldBehavior {
|
||||
if (value.isEmpty) None
|
||||
else {
|
||||
CustomFieldBehavior(field.fieldType).flatMap { behavior =>
|
||||
behavior.validate(field.fieldName, value, messages)
|
||||
behavior.validate(field.fieldName, field.constraints, value, messages)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -60,12 +73,18 @@ object CustomFieldBehavior {
|
||||
case "double" => Some(DoubleFieldBehavior)
|
||||
case "string" => Some(StringFieldBehavior)
|
||||
case "date" => Some(DateFieldBehavior)
|
||||
case "enum" => Some(EnumFieldBehavior)
|
||||
case _ => None
|
||||
}
|
||||
}
|
||||
|
||||
case object LongFieldBehavior extends TextFieldBehavior {
|
||||
override def validate(name: String, value: String, messages: Messages): Option[String] = {
|
||||
override def validate(
|
||||
name: String,
|
||||
constraints: Option[String],
|
||||
value: String,
|
||||
messages: Messages
|
||||
): Option[String] = {
|
||||
try {
|
||||
value.toLong
|
||||
None
|
||||
@@ -75,7 +94,12 @@ object CustomFieldBehavior {
|
||||
}
|
||||
}
|
||||
case object DoubleFieldBehavior extends TextFieldBehavior {
|
||||
override def validate(name: String, value: String, messages: Messages): Option[String] = {
|
||||
override def validate(
|
||||
name: String,
|
||||
constraints: Option[String],
|
||||
value: String,
|
||||
messages: Messages
|
||||
): Option[String] = {
|
||||
try {
|
||||
value.toDouble
|
||||
None
|
||||
@@ -89,7 +113,12 @@ object CustomFieldBehavior {
|
||||
private val pattern = "yyyy-MM-dd"
|
||||
override protected val fieldType: String = "date"
|
||||
|
||||
override def validate(name: String, value: String, messages: Messages): Option[String] = {
|
||||
override def validate(
|
||||
name: String,
|
||||
constraints: Option[String],
|
||||
value: String,
|
||||
messages: Messages
|
||||
): Option[String] = {
|
||||
try {
|
||||
new java.text.SimpleDateFormat(pattern).parse(value)
|
||||
None
|
||||
@@ -100,10 +129,142 @@ object CustomFieldBehavior {
|
||||
}
|
||||
}
|
||||
|
||||
case object EnumFieldBehavior extends CustomFieldBehavior {
|
||||
override def createHtml(repository: RepositoryInfo, fieldId: Int, fieldName: String, constraints: Option[String])(
|
||||
implicit context: Context
|
||||
): String = {
|
||||
createPulldownHtml(repository, fieldId, fieldName, constraints, None, None)
|
||||
}
|
||||
|
||||
override def fieldHtml(
|
||||
repository: RepositoryInfo,
|
||||
issueId: Int,
|
||||
fieldId: Int,
|
||||
fieldName: String,
|
||||
constraints: Option[String],
|
||||
value: String,
|
||||
editable: Boolean
|
||||
)(implicit context: Context): String = {
|
||||
if (!editable) {
|
||||
val sb = new StringBuilder
|
||||
sb.append("""</div>""")
|
||||
sb.append("""<div>""")
|
||||
if (value == "") {
|
||||
sb.append(s"""<span id="label-custom-field-$fieldId"><span class="muted small">No ${StringUtil.escapeHtml(
|
||||
fieldName
|
||||
)}</span></span>""")
|
||||
} else {
|
||||
sb.append(s"""<span id="label-custom-field-$fieldId"><span class="muted small">${StringUtil
|
||||
.escapeHtml(value)}</span></span>""")
|
||||
}
|
||||
sb.toString()
|
||||
} else {
|
||||
createPulldownHtml(repository, fieldId, fieldName, constraints, Some(issueId), Some(value))
|
||||
}
|
||||
}
|
||||
|
||||
private def createPulldownHtml(
|
||||
repository: RepositoryInfo,
|
||||
fieldId: Int,
|
||||
fieldName: String,
|
||||
constraints: Option[String],
|
||||
issueId: Option[Int],
|
||||
value: Option[String]
|
||||
)(implicit context: Context): String = {
|
||||
val sb = new StringBuilder
|
||||
sb.append("""<div class="pull-right">""")
|
||||
sb.append(
|
||||
gitbucket.core.helper.html
|
||||
.dropdown("Edit", right = true, filter = (fieldName, s"Filter $fieldName")) {
|
||||
val options = new StringBuilder()
|
||||
options.append(
|
||||
s"""<li><a href="javascript:void(0);" class="custom-field-option-$fieldId" data-value=""><i class="octicon octicon-x"></i> Clear ${StringUtil
|
||||
.escapeHtml(fieldName)}</a></li>"""
|
||||
)
|
||||
constraints.foreach {
|
||||
x =>
|
||||
x.split(",").map(_.trim).foreach {
|
||||
item =>
|
||||
options.append(s"""<li>
|
||||
| <a href="javascript:void(0);" class="custom-field-option-$fieldId" data-value="${StringUtil
|
||||
.escapeHtml(item)}">
|
||||
| ${gitbucket.core.helper.html.checkicon(value.contains(item))}
|
||||
| ${StringUtil.escapeHtml(item)}
|
||||
| </a>
|
||||
|</li>
|
||||
|""".stripMargin)
|
||||
}
|
||||
}
|
||||
Html(options.toString())
|
||||
}
|
||||
.toString()
|
||||
)
|
||||
sb.append("""</div>""")
|
||||
sb.append("""</div>""")
|
||||
sb.append("""<div>""")
|
||||
value match {
|
||||
case None =>
|
||||
sb.append(s"""<span id="label-custom-field-$fieldId"><span class="muted small">No ${StringUtil.escapeHtml(
|
||||
fieldName
|
||||
)}</span></span>""")
|
||||
case Some(value) =>
|
||||
sb.append(s"""<span id="label-custom-field-$fieldId"><span class="muted small">${StringUtil
|
||||
.escapeHtml(value)}</span></span>""")
|
||||
}
|
||||
if (value.isEmpty || issueId.isEmpty) {
|
||||
sb.append(s"""<input type="hidden" id="custom-field-$fieldId" name="custom-field-$fieldId" value=""/>""")
|
||||
sb.append(s"""<script>
|
||||
|$$('a.custom-field-option-$fieldId').click(function(){
|
||||
| const value = $$(this).data('value');
|
||||
| $$('a.custom-field-option-$fieldId i.octicon-check').removeClass('octicon-check');
|
||||
| $$('#custom-field-$fieldId').val(value);
|
||||
| if (value == '') {
|
||||
| $$('#label-custom-field-$fieldId').html($$('<span class="muted small">').text('No ${StringUtil
|
||||
.escapeHtml(fieldName)}'));
|
||||
| } else {
|
||||
| $$('#label-custom-field-$fieldId').html($$('<span class="muted small">').text(value));
|
||||
| $$('a.custom-field-option-$fieldId[data-value=' + value + '] i').addClass('octicon-check');
|
||||
| }
|
||||
|});
|
||||
|</script>""".stripMargin)
|
||||
} else {
|
||||
sb.append(s"""<script>
|
||||
|$$('a.custom-field-option-$fieldId').click(function(){
|
||||
| const value = $$(this).data('value');
|
||||
| $$.post('${helpers.url(repository)}/issues/${issueId.get}/customfield/$fieldId',
|
||||
| { value: value },
|
||||
| function(data){
|
||||
| $$('a.custom-field-option-$fieldId i.octicon-check').removeClass('octicon-check');
|
||||
| if (value == '') {
|
||||
| $$('#label-custom-field-$fieldId').html($$('<span class="muted small">').text('No ${StringUtil
|
||||
.escapeHtml(fieldName)}'));
|
||||
| } else {
|
||||
| $$('#label-custom-field-$fieldId').html($$('<span class="muted small">').text(value));
|
||||
| $$('a.custom-field-option-$fieldId[data-value=' + value + '] i').addClass('octicon-check');
|
||||
| }
|
||||
| }
|
||||
| );
|
||||
|});
|
||||
|</script>
|
||||
|""".stripMargin)
|
||||
}
|
||||
sb.toString()
|
||||
}
|
||||
|
||||
override def validate(
|
||||
name: String,
|
||||
constraints: Option[String],
|
||||
value: String,
|
||||
messages: Messages
|
||||
): Option[String] = None
|
||||
}
|
||||
|
||||
trait TextFieldBehavior extends CustomFieldBehavior {
|
||||
protected val fieldType = "text"
|
||||
|
||||
def createHtml(repository: RepositoryInfo, fieldId: Int)(implicit context: Context): String = {
|
||||
override def createHtml(repository: RepositoryInfo, fieldId: Int, fieldName: String, constraints: Option[String])(
|
||||
implicit context: Context
|
||||
): String = {
|
||||
val sb = new StringBuilder
|
||||
sb.append(
|
||||
s"""<input type="$fieldType" class="form-control input-sm" id="custom-field-$fieldId" name="custom-field-$fieldId" data-field-id="$fieldId" style="width: 120px;"/>"""
|
||||
@@ -111,8 +272,7 @@ object CustomFieldBehavior {
|
||||
sb.append(s"""<script>
|
||||
|$$('#custom-field-$fieldId').focusout(function(){
|
||||
| const $$this = $$(this);
|
||||
| const fieldId = $$this.data('field-id');
|
||||
| $$.post('${helpers.url(repository)}/issues/customfield_validation/' + fieldId,
|
||||
| $$.post('${helpers.url(repository)}/issues/customfield_validation/$fieldId',
|
||||
| { value: $$this.val() },
|
||||
| function(data){
|
||||
| if (data != '') {
|
||||
@@ -128,7 +288,15 @@ object CustomFieldBehavior {
|
||||
sb.toString()
|
||||
}
|
||||
|
||||
def fieldHtml(repository: RepositoryInfo, issueId: Int, fieldId: Int, value: String, editable: Boolean)(
|
||||
override def fieldHtml(
|
||||
repository: RepositoryInfo,
|
||||
issueId: Int,
|
||||
fieldId: Int,
|
||||
fieldName: String,
|
||||
constraints: Option[String],
|
||||
value: String,
|
||||
editable: Boolean
|
||||
)(
|
||||
implicit context: Context
|
||||
): String = {
|
||||
val sb = new StringBuilder
|
||||
@@ -149,15 +317,14 @@ object CustomFieldBehavior {
|
||||
|
|
||||
|$$('#custom-field-$fieldId-editor').focusout(function(){
|
||||
| const $$this = $$(this);
|
||||
| const fieldId = $$this.data('field-id');
|
||||
| $$.post('${helpers.url(repository)}/issues/customfield_validation/' + fieldId,
|
||||
| $$.post('${helpers.url(repository)}/issues/customfield_validation/$fieldId',
|
||||
| { value: $$this.val() },
|
||||
| function(data){
|
||||
| if (data != '') {
|
||||
| $$('#custom-field-$fieldId-error').text(data);
|
||||
| } else {
|
||||
| $$('#custom-field-$fieldId-error').text('');
|
||||
| $$.post('${helpers.url(repository)}/issues/$issueId/customfield/' + fieldId,
|
||||
| $$.post('${helpers.url(repository)}/issues/$issueId/customfield/$fieldId',
|
||||
| { value: $$this.val() },
|
||||
| function(data){
|
||||
| $$this.hide();
|
||||
@@ -186,6 +353,11 @@ object CustomFieldBehavior {
|
||||
sb.toString()
|
||||
}
|
||||
|
||||
def validate(name: String, value: String, messages: Messages): Option[String] = None
|
||||
override def validate(
|
||||
name: String,
|
||||
constraints: Option[String],
|
||||
value: String,
|
||||
messages: Messages
|
||||
): Option[String] = None
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ trait CustomFieldsService {
|
||||
repository: String,
|
||||
fieldName: String,
|
||||
fieldType: String,
|
||||
constraints: Option[String],
|
||||
enableForIssues: Boolean,
|
||||
enableForPullRequests: Boolean
|
||||
)(implicit s: Session): Int = {
|
||||
@@ -36,6 +37,7 @@ trait CustomFieldsService {
|
||||
repositoryName = repository,
|
||||
fieldName = fieldName,
|
||||
fieldType = fieldType,
|
||||
constraints = constraints,
|
||||
enableForIssues = enableForIssues,
|
||||
enableForPullRequests = enableForPullRequests
|
||||
)
|
||||
@@ -47,6 +49,7 @@ trait CustomFieldsService {
|
||||
fieldId: Int,
|
||||
fieldName: String,
|
||||
fieldType: String,
|
||||
constraints: Option[String],
|
||||
enableForIssues: Boolean,
|
||||
enableForPullRequests: Boolean
|
||||
)(
|
||||
@@ -54,8 +57,8 @@ trait CustomFieldsService {
|
||||
): Unit =
|
||||
CustomFields
|
||||
.filter(_.byPrimaryKey(owner, repository, fieldId))
|
||||
.map(t => (t.fieldName, t.fieldType, t.enableForIssues, t.enableForPullRequests))
|
||||
.update((fieldName, fieldType, enableForIssues, enableForPullRequests))
|
||||
.map(t => (t.fieldName, t.fieldType, t.constraints, t.enableForIssues, t.enableForPullRequests))
|
||||
.update((fieldName, fieldType, constraints, enableForIssues, enableForPullRequests))
|
||||
|
||||
def deleteCustomField(owner: String, repository: String, fieldId: Int)(implicit s: Session): Unit = {
|
||||
IssueCustomFields
|
||||
|
||||
@@ -964,39 +964,39 @@ object IssuesService {
|
||||
|
||||
def nonEmpty: Boolean = !isEmpty
|
||||
|
||||
def toFilterString: String =
|
||||
(
|
||||
List(
|
||||
Some(s"is:${state}"),
|
||||
author.map(author => s"author:${author}"),
|
||||
assigned.map(assignee => s"assignee:${assignee}"),
|
||||
mentioned.map(mentioned => s"mentions:${mentioned}")
|
||||
).flatten ++
|
||||
labels.map(label => s"label:${label}") ++
|
||||
List(
|
||||
milestone.map {
|
||||
case Some(x) => s"milestone:${x}"
|
||||
case None => "no:milestone"
|
||||
},
|
||||
priority.map {
|
||||
case Some(x) => s"priority:${x}"
|
||||
case None => "no:priority"
|
||||
},
|
||||
(sort, direction) match {
|
||||
case ("created", "desc") => None
|
||||
case ("created", "asc") => Some("sort:created-asc")
|
||||
case ("comments", "desc") => Some("sort:comments-desc")
|
||||
case ("comments", "asc") => Some("sort:comments-asc")
|
||||
case ("updated", "desc") => Some("sort:updated-desc")
|
||||
case ("updated", "asc") => Some("sort:updated-asc")
|
||||
case ("priority", "desc") => Some("sort:priority-desc")
|
||||
case ("priority", "asc") => Some("sort:priority-asc")
|
||||
case x => throw new MatchError(x)
|
||||
},
|
||||
visibility.map(visibility => s"visibility:${visibility}")
|
||||
).flatten ++
|
||||
groups.map(group => s"group:${group}")
|
||||
).mkString(" ")
|
||||
// def toFilterString: String =
|
||||
// (
|
||||
// List(
|
||||
// Some(s"is:${state}"),
|
||||
// author.map(author => s"author:${author}"),
|
||||
// assigned.map(assignee => s"assignee:${assignee}"),
|
||||
// mentioned.map(mentioned => s"mentions:${mentioned}")
|
||||
// ).flatten ++
|
||||
// labels.map(label => s"label:${label}") ++
|
||||
// List(
|
||||
// milestone.map {
|
||||
// case Some(x) => s"milestone:${x}"
|
||||
// case None => "no:milestone"
|
||||
// },
|
||||
// priority.map {
|
||||
// case Some(x) => s"priority:${x}"
|
||||
// case None => "no:priority"
|
||||
// },
|
||||
// (sort, direction) match {
|
||||
// case ("created", "desc") => None
|
||||
// case ("created", "asc") => Some("sort:created-asc")
|
||||
// case ("comments", "desc") => Some("sort:comments-desc")
|
||||
// case ("comments", "asc") => Some("sort:comments-asc")
|
||||
// case ("updated", "desc") => Some("sort:updated-desc")
|
||||
// case ("updated", "asc") => Some("sort:updated-asc")
|
||||
// case ("priority", "desc") => Some("sort:priority-desc")
|
||||
// case ("priority", "asc") => Some("sort:priority-asc")
|
||||
// case x => throw new MatchError(x)
|
||||
// },
|
||||
// visibility.map(visibility => s"visibility:${visibility}")
|
||||
// ).flatten ++
|
||||
// groups.map(group => s"group:${group}")
|
||||
// ).mkString(" ")
|
||||
|
||||
def toURL: String =
|
||||
"?" + List(
|
||||
|
||||
@@ -140,8 +140,8 @@
|
||||
}
|
||||
</div>
|
||||
<span id="label-assigned">
|
||||
@issueAssignees.map { asignee =>
|
||||
<div>@helpers.avatarLink(asignee.assigneeUserName, 20) @helpers.user(asignee.assigneeUserName, styleClass="username strong small")</div>
|
||||
@issueAssignees.map { assignee =>
|
||||
<div>@helpers.avatarLink(assignee.assigneeUserName, 20) @helpers.user(assignee.assigneeUserName, styleClass="username strong small")</div>
|
||||
}
|
||||
@if(issueAssignees.isEmpty) {
|
||||
<span class="muted small">No one assigned</span>
|
||||
@@ -158,10 +158,10 @@
|
||||
<div class="pull-right">
|
||||
@gitbucket.core.model.CustomFieldBehavior(field.fieldType).map { behavior =>
|
||||
@if(issue.nonEmpty) {
|
||||
@Html(behavior.fieldHtml(repository, issue.get.issueId, field.fieldId, value.map(_.value).getOrElse(""), isManageable))
|
||||
@Html(behavior.fieldHtml(repository, issue.get.issueId, field.fieldId, field.fieldName, field.constraints, value.map(_.value).getOrElse(""), isManageable))
|
||||
}
|
||||
@if(issue.isEmpty) {
|
||||
@Html(behavior.createHtml(repository, field.fieldId))
|
||||
@Html(behavior.createHtml(repository, field.fieldId, field.fieldName, field.constraints))
|
||||
}
|
||||
}
|
||||
</div>
|
||||
|
||||
@@ -7,6 +7,9 @@
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
@customField.fieldType
|
||||
@customField.constraints.map { constraints =>
|
||||
(@constraints)
|
||||
}
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
@if(customField.enableForIssues) {
|
||||
|
||||
@@ -10,7 +10,9 @@
|
||||
<option value="double" @if(field.map(_.fieldType == "double").getOrElse(false)){selected}>Double</option>
|
||||
<option value="string" @if(field.map(_.fieldType == "string").getOrElse(false)){selected}>String</option>
|
||||
<option value="date" @if(field.map(_.fieldType == "date").getOrElse(false)){selected}>Date</option>
|
||||
<option value="enum" @if(field.map(_.fieldType == "enum").getOrElse(false)){selected}>Enum</option>
|
||||
</select>
|
||||
<input type="text" id="constraints-@fieldId" style="width: 300px; @if(!field.exists(_.fieldType == "enum")){display: none;}" class="form-control input-sm" value="@field.map(_.constraints)" placeholder="Comma-separated enum values">
|
||||
<label for="enableForIssues-@fieldId" class="normal" style="margin-left: 4px;">
|
||||
<input type="checkbox" id="enableForIssues-@fieldId" @if(field.map(_.enableForIssues).getOrElse(false)){checked}> Issues
|
||||
</label>
|
||||
@@ -30,6 +32,7 @@
|
||||
$.post('@helpers.url(repository)/settings/issues/fields/@{if(fieldId == "new") "new" else s"$fieldId/edit"}', {
|
||||
'fieldName' : $('#fieldName-@fieldId').val(),
|
||||
'fieldType': $('#fieldType-@fieldId option:selected').val(),
|
||||
'constraints': $('#constraints-@fieldId').val(),
|
||||
'enableForIssues': $('#enableForIssues-@fieldId').prop('checked'),
|
||||
'enableForPullRequests': $('#enableForPullRequests-@fieldId').prop('checked')
|
||||
}, function(data, status){
|
||||
@@ -61,6 +64,14 @@
|
||||
$('#field-@fieldId').show();
|
||||
}
|
||||
});
|
||||
|
||||
$('#fieldType-@fieldId').change(function(){
|
||||
if($(this).val() == 'enum') {
|
||||
$('#constraints-@fieldId').show();
|
||||
} else {
|
||||
$('#constraints-@fieldId').hide();
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user