All Downloads are FREE. Search and download functionalities are using the official Maven repository.

gitbucket.core.model.CustomField.scala Maven / Gradle / Ivy

The newest version!
package gitbucket.core.model

import gitbucket.core.controller.Context
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._

  lazy val CustomFields = TableQuery[CustomFields]

  class CustomFields(tag: Tag) extends Table[CustomField](tag, "CUSTOM_FIELD") with BasicTemplate {
    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, constraints, enableForIssues, enableForPullRequests)
        .mapTo[CustomField]

    def byPrimaryKey(userName: String, repositoryName: String, fieldId: Int) =
      (this.userName === userName.bind) && (this.repositoryName === repositoryName.bind) && (this.fieldId === fieldId.bind)
  }
}

case class CustomField(
  userName: String,
  repositoryName: String,
  fieldId: Int = 0,
  fieldName: String,
  fieldType: String, // long, double, string, date, or enum
  constraints: Option[String],
  enableForIssues: Boolean,
  enableForPullRequests: Boolean
)

trait CustomFieldBehavior {
  def createHtml(repository: RepositoryInfo, fieldId: Int, fieldName: String, constraints: Option[String])(implicit
    context: Context
  ): 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 {
  def validate(field: CustomField, value: String, messages: Messages): Option[String] = {
    if (value.isEmpty) None
    else {
      CustomFieldBehavior(field.fieldType).flatMap { behavior =>
        behavior.validate(field.fieldName, field.constraints, value, messages)
      }
    }
  }

  def apply(fieldType: String): Option[CustomFieldBehavior] = {
    fieldType match {
      case "long"   => Some(LongFieldBehavior)
      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,
      constraints: Option[String],
      value: String,
      messages: Messages
    ): Option[String] = {
      try {
        value.toLong
        None
      } catch {
        case _: NumberFormatException => Some(messages("error.number").format(name))
      }
    }
  }
  case object DoubleFieldBehavior extends TextFieldBehavior {
    override def validate(
      name: String,
      constraints: Option[String],
      value: String,
      messages: Messages
    ): Option[String] = {
      try {
        value.toDouble
        None
      } catch {
        case _: NumberFormatException => Some(messages("error.number").format(name))
      }
    }
  }
  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,
      constraints: Option[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))
      }
    }
  }

  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("""
""") sb.append("""
""") if (value == "") { sb.append(s"""No ${StringUtil.escapeHtml( fieldName )}""") } else { sb.append(s"""${StringUtil .escapeHtml(value)}""") } 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("""
""") sb.append( gitbucket.core.helper.html .dropdown("Edit", right = true, filter = (fieldName, s"Filter $fieldName")) { val options = new StringBuilder() options.append( s"""
  • Clear ${StringUtil .escapeHtml(fieldName)}
  • """ ) constraints.foreach { x => x.split(",").map(_.trim).foreach { item => options.append(s"""
  • | | ${gitbucket.core.helper.html.checkicon(value.contains(item))} | ${StringUtil.escapeHtml(item)} | |
  • |""".stripMargin) } } Html(options.toString()) } .toString() ) sb.append("""
    """) sb.append("""
    """) sb.append("""
    """) value match { case None => sb.append(s"""No ${StringUtil.escapeHtml( fieldName )}""") case Some(value) => sb.append(s"""${StringUtil .escapeHtml(value)}""") } if (value.isEmpty || issueId.isEmpty) { sb.append(s"""""") sb.append(s"""""".stripMargin) } else { sb.append(s""" |""".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" override def createHtml(repository: RepositoryInfo, fieldId: Int, fieldName: String, constraints: Option[String])( implicit context: Context ): String = { val sb = new StringBuilder sb.append( s"""""" ) sb.append(s""" |""".stripMargin) sb.toString() } 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 if (value.nonEmpty) { sb.append( s"""${StringUtil .escapeHtml(value)}""" ) } else { if (editable) { sb.append( s"""""" ) } else { sb.append( s"""N/A""" ) } } if (editable) { sb.append( s"""""" ) sb.append(s""" |""".stripMargin) } sb.toString() } override def validate( name: String, constraints: Option[String], value: String, messages: Messages ): Option[String] = None } }



    © 2015 - 2024 Weber Informatics LLC | Privacy Policy