Skip to content

Commit

Permalink
Enhance custom field validation
Browse files Browse the repository at this point in the history
  • Loading branch information
takezoe committed Apr 17, 2022
1 parent 78eb162 commit a74d510
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 59 deletions.
27 changes: 10 additions & 17 deletions src/main/scala/gitbucket/core/controller/IssuesController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -156,15 +156,12 @@ trait IssuesControllerBase extends ControllerBase {
params.toMap.foreach {
case (key, value) =>
if (key.startsWith("custom-field-")) {
for {
field <- getCustomField(
repository.owner,
repository.name,
key.replaceFirst("^custom-field-", "").toInt
)
behavior <- CustomFieldBehavior(field.fieldType)
} {
behavior.validate(field.fieldName, value, messages) match {
getCustomField(
repository.owner,
repository.name,
key.replaceFirst("^custom-field-", "").toInt
).foreach { field =>
CustomFieldBehavior.validate(field, value, messages) match {
case None =>
insertOrUpdateCustomFieldValue(field, repository.owner, repository.name, issue.issueId, value)
case Some(_) => halt(400)
Expand Down Expand Up @@ -388,16 +385,13 @@ trait IssuesControllerBase extends ControllerBase {
Ok("updated")
})

ajaxPost("/:owner/:repository/issues/:id/customfield_validation/:fieldId")(writableUsersOnly { repository =>
ajaxPost("/:owner/:repository/issues/customfield_validation/:fieldId")(writableUsersOnly { repository =>
val fieldId = params("fieldId").toInt
val value = params("value")
getCustomField(repository.owner, repository.name, fieldId)
.flatMap { field =>
CustomFieldBehavior(field.fieldType).map { behavior =>
behavior.validate(field.fieldName, value, messages) match {
case None => Ok()
case Some(error) => Ok(error)
}
CustomFieldBehavior.validate(field, value, messages).map { error =>
Ok(error)
}
}
.getOrElse(Ok())
Expand All @@ -411,9 +405,8 @@ trait IssuesControllerBase extends ControllerBase {
for {
_ <- getIssue(repository.owner, repository.name, issueId.toString)
field <- getCustomField(repository.owner, repository.name, fieldId)
behavior <- CustomFieldBehavior(field.fieldType)
} {
behavior.validate(field.fieldName, value, messages) match {
CustomFieldBehavior.validate(field, value, messages) match {
case None => insertOrUpdateCustomFieldValue(field, repository.owner, repository.name, issueId, value)
case Some(_) => halt(400)
}
Expand Down
119 changes: 80 additions & 39 deletions src/main/scala/gitbucket/core/model/CustomField.scala
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,23 @@ case class CustomField(
)

trait CustomFieldBehavior {
def createHtml(fieldId: Int): String
def createHtml(repository: RepositoryInfo, fieldId: Int)(implicit conext: Context): String
def fieldHtml(repository: RepositoryInfo, issueId: Int, fieldId: Int, value: String, editable: Boolean)(
implicit context: Context
): String
def validate(name: String, value: String, messages: Messages): Option[String]
}

object CustomFieldBehavior {
def validate(field: CustomField, value: String, messages: Messages): Option[String] = {
if (value.isEmpty) None
else {
CustomFieldBehavior(field.fieldType).flatMap { behavior =>
behavior.validate(field.fieldName, value, messages)
}
}
}

def apply(fieldType: String): Option[CustomFieldBehavior] = {
fieldType match {
case "long" => Some(LongFieldBehavior)
Expand Down Expand Up @@ -77,14 +86,46 @@ object CustomFieldBehavior {
}
case object StringFieldBehavior extends TextFieldBehavior
case object DateFieldBehavior extends TextFieldBehavior {
private val pattern = "yyyy-MM-dd"
override protected val fieldType: String = "date"

override def validate(name: String, value: String, messages: Messages): Option[String] = {
try {
new java.text.SimpleDateFormat(pattern).parse(value)
None
} catch {
case _: java.text.ParseException =>
Some(messages("error.datePattern").format(name, pattern))
}
}
}

trait TextFieldBehavior extends CustomFieldBehavior {
protected val fieldType = "text"

def createHtml(fieldId: Int): String = {
s"""<input type="$fieldType" class="form-control input-sm" id="custom-field-$fieldId" name="custom-field-$fieldId" style="width: 120px;"/>"""
def createHtml(repository: RepositoryInfo, fieldId: Int)(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;"/>"""
)
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,
| { value: $$this.val() },
| function(data){
| if (data != '') {
| $$('#custom-field-$fieldId-error').text(data);
| } else {
| $$('#custom-field-$fieldId-error').text('');
| }
| }
| );
|});
|</script>
|""".stripMargin)
sb.toString()
}

def fieldHtml(repository: RepositoryInfo, issueId: Int, fieldId: Int, value: String, editable: Boolean)(
Expand All @@ -100,45 +141,45 @@ object CustomFieldBehavior {
s"""<input type="$fieldType" id="custom-field-$fieldId-editor" class="form-control input-sm custom-field-editor" data-field-id="$fieldId" style="width: 120px; display: none;"/>"""
)
sb.append(s"""<script>
| $$('#custom-field-$fieldId-label').click(function(){
| const $$this = $$(this);
| $$this.hide();
| $$this.next().val($$this.text()).show().focus();
| });
|$$('#custom-field-$fieldId-label').click(function(){
| const $$this = $$(this);
| $$this.hide();
| $$this.next().val($$this.text()).show().focus();
|});
|
| $$('#custom-field-$fieldId-editor').focusout(function(){
| const $$this = $$(this);
| const fieldId = $$this.data('field-id');
| $$.post('${helpers.url(repository)}/issues/$issueId/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,
| { value: $$this.val() },
| function(data){
| $$this.hide();
| $$this.prev().text(data).show();
| }
| );
|$$('#custom-field-$fieldId-editor').focusout(function(){
| const $$this = $$(this);
| const fieldId = $$this.data('field-id');
| $$.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,
| { value: $$this.val() },
| function(data){
| $$this.hide();
| $$this.prev().text(data).show();
| }
| }
| );
| });
|
| // ESC key handling in text field
| $$('#custom-field-$fieldId-editor').keyup(function(e){
| if (e.keyCode == 27) {
| const $$this = $$(this);
| $$this.hide();
| $$this.prev().show();
| );
| }
| if (e.keyCode == 13) {
| $$('#custom-field-$fieldId-editor').blur();
| }
| });
| }
| );
|});
|
|// ESC key handling in text field
|$$('#custom-field-$fieldId-editor').keyup(function(e){
| if (e.keyCode == 27) {
| const $$this = $$(this);
| $$this.hide();
| $$this.prev().show();
| }
| if (e.keyCode == 13) {
| $$('#custom-field-$fieldId-editor').blur();
| }
|});
|</script>
|""".stripMargin)
}
Expand Down
13 changes: 12 additions & 1 deletion src/main/twirl/gitbucket/core/issues/create.scala.html
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
elastic = true
)
<div class="align-right">
<input type="submit" class="btn btn-success" value="Submit new issue"/>
<input type="submit" class="btn btn-success" value="Submit new issue" onclick="javascript:return checkCustomFieldErrors();"/>
</div>
</div>
<div class="col-md-3">
Expand All @@ -48,5 +48,16 @@
</div>
</div>
</form>
<script>
function checkCustomFieldErrors() {
let error = false;
$('.custom-field-error').each(function(i, e) {
if ($(e).text() != '') {
error = true;
}
});
return !error;
}
</script>
}
}
4 changes: 2 additions & 2 deletions src/main/twirl/gitbucket/core/issues/issueinfo.scala.html
Original file line number Diff line number Diff line change
Expand Up @@ -160,12 +160,12 @@
@Html(behavior.fieldHtml(repository, issue.get.issueId, field.fieldId, value.map(_.value).getOrElse(""), isManageable))
}
@if(issue.isEmpty) {
@Html(behavior.createHtml(field.fieldId))
@Html(behavior.createHtml(repository, field.fieldId))
}
}
</div>
</div>
<span id="custom-field-@field.fieldId-error" class="error"></span>
<span id="custom-field-@field.fieldId-error" class="error custom-field-error"></span>
}

@issue.map { issue =>
Expand Down

0 comments on commit a74d510

Please sign in to comment.