Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 11 additions & 12 deletions core-play28/build.sbt
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import scalariform.formatter.preferences._
//import scalariform.formatter.preferences._

name := """play-bootstrap-core"""

version := "1.6.1-P28"
version := "1.7.0-P28"

scalaVersion := "2.13.2"
scalaVersion := "2.13.17"

crossScalaVersions := Seq("2.13.2", "2.12.11")
crossScalaVersions := Seq("2.13.17", "3.3.6")

resolvers += "scalaz-bintray" at "https://dl.bintray.com/scalaz/releases"

Expand All @@ -17,13 +17,12 @@ libraryDependencies := libraryDependencies.value.filterNot(m => m.name == "twirl
playCore % "provided"
)

scalariformPreferences := scalariformPreferences.value
.setPreference(AlignSingleLineCaseStatements, true)
.setPreference(DoubleIndentConstructorArguments, true)
.setPreference(DanglingCloseParenthesis, Preserve)
//scalariformPreferences := scalariformPreferences.value
// .setPreference(AlignSingleLineCaseStatements, true)
// .setPreference(DoubleIndentConstructorArguments, true)
// .setPreference(DanglingCloseParenthesis, Preserve)


PlayKeys.playOmnidoc := false
//PlayKeys.playOmnidoc := false

//*******************************
// Maven settings
Expand Down Expand Up @@ -61,6 +60,6 @@ pomIncludeRepository := { _ => false }
credentials += Credentials(Path.userHome / ".sbt" / "sonatype.credentials")

publishConfiguration := publishConfiguration.value.withOverwrite(isSnapshot.value)
com.typesafe.sbt.pgp.PgpKeys.publishSignedConfiguration := com.typesafe.sbt.pgp.PgpKeys.publishSignedConfiguration.value.withOverwrite(isSnapshot.value)
PgpKeys.publishSignedConfiguration := PgpKeys.publishSignedConfiguration.value.withOverwrite(isSnapshot.value)
publishLocalConfiguration := publishLocalConfiguration.value.withOverwrite(isSnapshot.value)
com.typesafe.sbt.pgp.PgpKeys.publishLocalSignedConfiguration := com.typesafe.sbt.pgp.PgpKeys.publishLocalSignedConfiguration.value.withOverwrite(isSnapshot.value)
PgpKeys.publishLocalSignedConfiguration := PgpKeys.publishLocalSignedConfiguration.value.withOverwrite(isSnapshot.value)
2 changes: 1 addition & 1 deletion core-play28/project/build.properties
Original file line number Diff line number Diff line change
@@ -1 +1 @@
sbt.version=1.3.10
sbt.version=1.11.7
8 changes: 4 additions & 4 deletions core-play28/project/plugins.sbt
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
// The Play plugin
addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.8.1")
addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.9.7")

// web plugins

addSbtPlugin("org.scalariform" % "sbt-scalariform" % "1.8.2")
//addSbtPlugin("org.scalariform" % "sbt-scalariform" % "1.8.2")

addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "2.4")
addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "3.12.2")

addSbtPlugin("com.jsuereth" % "sbt-pgp" % "1.1.0")
addSbtPlugin("com.github.sbt" % "sbt-pgp" % "2.3.1")
86 changes: 86 additions & 0 deletions core-play30/app/views/bs/Args.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/** Copyright 2019 Adrian Hurtado (adrianhurt)
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package views.html.bs

object Args {

import play.api.i18n.MessagesProvider
import play.api.templates.PlayMagic.translate

/** Adds some default arguments to the parameter 'args'
*/
def withDefault(args: Seq[(Symbol, Any)], default: (Symbol, Any)*) = default ++: args

/** Adds a string value to a selected arg. For example, to add "form-control" to 'class, even if there is already
* other extra class
*/
def withAddingStringValue(args: Seq[(Symbol, Any)], arg: Symbol, value: String): Seq[(Symbol, Any)] = {
val (withArg, withoutArg) = args.partition(_._1 == arg)
(arg, withArg.headOption.map(v => s"$value ${v._2.toString}").getOrElse(value)) +: withoutArg
}
def withAddingStringValue(args: Seq[(Symbol, Any)], arg: Symbol, maybeValue: Option[String]): Seq[(Symbol, Any)] =
maybeValue.map(value => withAddingStringValue(args, arg, value)).getOrElse(args)

/** Removes those arguments which its value is None. It lets you omit those arguments that satisfy some criteria. It
* also lets you add some default arguments to the parameter 'args'.
*/
def withoutNones(args: Seq[(Symbol, Any)], default: (Symbol, Any)*) = (default ++: args).filter(_._2 != None)

/** Returns only the inner arguments (those that are exclusive for an input and not for the field constructor).
* Removes every argument with a prefixed underscore (_) and those whose value is false. It also lets you add some
* default arguments to the parameter 'args'.
*/
def inner(args: Seq[(Symbol, Any)], default: (Symbol, Any)*) =
(default ++: args).filter(arg => !arg._1.name.startsWith("_") && arg._2 != false)

/** Gets the value for the selected key
*/
def get(args: Seq[(Symbol, Any)], key: Symbol) = args.find(_._1 == key).map(_._2)

/** Removes those arguments with these keys
*/
def remove(args: Seq[(Symbol, Any)], keys: Symbol*) = args.filter(arg => !keys.contains(arg._1))

/** Returns true only if exists a pair with that key and its value is true.
*/
def isTrue(args: Seq[(Symbol, Any)], key: Symbol) = args.exists(_ == (key, true))

/** Localizes an argument
*/
def msg(arg: (Symbol, Any))(implicit msgsProv: MessagesProvider): (Symbol, Any) =
(arg._1, translate(arg._2)(msgsProv))

/** Localizes a value
*/
def msg(a: Any)(implicit msgsProv: MessagesProvider): Any = translate(a)(msgsProv)
}

object ArgsMap {

/** Adds a string value to a selected arg. For example, to add "form-control" to 'class, even if there is already
* other extra class
*/
def withAddingStringValue(argsMap: Map[Symbol, Any], arg: Symbol, value: String): Map[Symbol, Any] = {
val newValue = argsMap.get(arg).map(v => s"$value ${v.toString}").getOrElse(value)
argsMap + (arg -> newValue)
}
def withAddingStringValue(argsMap: Map[Symbol, Any], arg: Symbol, maybeValue: Option[String]): Map[Symbol, Any] =
maybeValue.map(value => withAddingStringValue(argsMap, arg, value)).getOrElse(argsMap)

/** Returns true only if the map contains an argument with that key and its value is true.
*/
def isTrue(argsMap: Map[Symbol, Any], key: Symbol) = argsMap.get(key).map(_ == true).getOrElse(false)

/** Returns true only if the map contains an argument with that key and its value is any value but false.
*/
def isNotFalse(argsMap: Map[Symbol, Any], key: Symbol) = argsMap.get(key).map(_ != false).getOrElse(false)
}
239 changes: 239 additions & 0 deletions core-play30/app/views/bs/package.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,239 @@
/** Copyright 2019 Adrian Hurtado (adrianhurt)
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package views.html

package object bs {

import play.api.data.{Field, FormError}
import play.twirl.api.Html
import play.api.i18n.MessagesProvider
import play.api.templates.PlayMagic.translate
import bs.ArgsMap.isTrue

/** Class with relevant variables for a field to pass it to the helper and field constructor
* - args: list of available arguments for the helper and field constructor
*/
class BSFieldInfo(field: Field, args: Seq[(Symbol, Any)], val msgsProv: MessagesProvider) {

/* A map with the args to work easily with them */
val argsMap: Map[Symbol, Any] = Args.withoutNones(args).toMap

/* Id of the input */
val id: String = argsMap.get(Symbol("id")).map(_.toString).getOrElse(field.id)

/* Id of the form-group */
val idFormField: String = argsMap.get(Symbol("_id")).map(_.toString).getOrElse(id + "_field")

/* The optional label */
val labelOpt: Option[Any] = argsMap.get(Symbol("_label")).orElse(argsMap.get(Symbol("_hiddenLabel")))

/* Indicates if the label must be hidden */
val hideLabel: Boolean = isTrue(argsMap, Symbol("_hideLabel")) || argsMap.contains(Symbol("_hiddenLabel"))

/* Name of the input */
def name: String = field.name

/* Value of the input */
val value: Option[String] = field.value.orElse(argsMap.get(Symbol("value")).map(_.toString))

/* List with every error and its corresponding ARIA id. Ex: ("foo_error_0" -> "foo error") */
val errors: Seq[(String, Any)] = BSFieldInfo.errors(Some(field), argsMap, msgsProv).zipWithIndex.map {
case (error, i) => (id + "_error_" + i, error)
}

/* Indicates if there is any error */
val hasErrors: Boolean = errors.nonEmpty || ArgsMap.isNotFalse(argsMap, Symbol("_error"))

/* The optional validation state ("success", "warning" or "error") */
lazy val status: Option[String] = BSFieldInfo.status(hasErrors, argsMap)

}

/** Companion object for class BSFieldInfo
*/
object BSFieldInfo {

def apply(field: Field, args: Seq[(Symbol, Any)], msgsProv: MessagesProvider): BSFieldInfo = {
new BSFieldInfo(field, args, msgsProv)
}

/* List with every error */
def errors(maybeField: Option[Field], argsMap: Map[Symbol, Any], msgsProv: MessagesProvider): Seq[Any] = {
argsMap
.get(Symbol("_error"))
.filter(!_.isInstanceOf[Boolean])
.map {
case Some(FormError(_, message, args)) =>
Seq(msgsProv.messages(message, args.map(a => translate(a)(msgsProv)): _*))
case FormError(_, message, args) =>
Seq(msgsProv.messages(message, args.map(a => translate(a)(msgsProv)): _*))
case message => Seq(translate(message)(msgsProv))
}
.getOrElse {
maybeField
.filter(_ => !argsMap.get(Symbol("_showErrors")).contains(false))
.map { field =>
field.errors.map { e => msgsProv.messages(e.message, e.args.map(a => translate(a)(msgsProv)): _*) }
}
.getOrElse(Nil)
}
}

/* List with every "feedback info" except "errors" */
def feedbackInfosButErrors(argsMap: Map[Symbol, Any], msgsProv: MessagesProvider): Seq[Any] = {
argsMap
.get(Symbol("_warning"))
.filter(!_.isInstanceOf[Boolean])
.map(m => Seq(translate(m)(msgsProv)))
.getOrElse(
argsMap
.get(Symbol("_success"))
.filter(!_.isInstanceOf[Boolean])
.map(m => Seq(translate(m)(msgsProv)))
.getOrElse(Nil)
)
}

/* List with every "help info", i.e. a help text or constraints */
def helpInfos(maybeField: Option[Field], argsMap: Map[Symbol, Any], msgsProv: MessagesProvider): Seq[Any] = {
argsMap.get(Symbol("_help")).map(m => Seq(translate(m)(msgsProv))).getOrElse {
maybeField
.filter(_ => argsMap.get(Symbol("_showConstraints")).contains(true))
.map { field =>
field.constraints.map(c =>
msgsProv.messages(c._1, c._2.map(a => translate(a)(msgsProv)): _*)
) ++ field.format.map(f => msgsProv.messages(f._1, f._2.map(a => translate(a)(msgsProv)): _*))
}
.getOrElse(Nil)
}
}

/* The optional validation state ("success", "warning" or "error") */
def status(hasErrors: Boolean, argsMap: Map[Symbol, Any]): Option[String] = {
if (hasErrors)
Some("error")
else if (ArgsMap.isNotFalse(argsMap, Symbol("_warning")))
Some("warning")
else if (ArgsMap.isNotFalse(argsMap, Symbol("_success")))
Some("success")
else
None
}

/* Generates automatically the input attributes for the constraints of a field */
def constraintsArgs(field: Field, msgsProv: MessagesProvider): Seq[(Symbol, Any)] = field.constraints.flatMap {
case ("constraint.required", params) => Some(Symbol("required") -> true)
case ("constraint.min", params: Seq[Any]) => Some(Symbol("min") -> msgsProv.messages(params.head.toString))
case ("constraint.max", params: Seq[Any]) => Some(Symbol("max") -> msgsProv.messages(params.head.toString))
case ("constraint.minLength", params: Seq[Any]) =>
Some(Symbol("minlength") -> msgsProv.messages(params.head.toString))
case ("constraint.maxLength", params: Seq[Any]) =>
Some(Symbol("maxlength") -> msgsProv.messages(params.head.toString))
case ("constraint.pattern", params: Seq[Any]) =>
params.head match {
case str: String => Some(Symbol("pattern") -> msgsProv.messages(str))
case func: Function0[_] =>
Some(
Symbol("pattern") -> msgsProv.messages(func.asInstanceOf[() => scala.util.matching.Regex]().toString)
)
case _ => None
}
case _ => None
}
}

/** Class with relevant variables for the global information of a multifield
* - fields: list of Fields
* - globalArguments: list of available arguments for the global helper
* - fieldsArguments: list of available arguments for every specific field
*/
class BSMultifieldInfo(
fields: Seq[Field],
globalArguments: Seq[(Symbol, Any)],
fieldsArguments: Seq[(Symbol, Any)],
val msgsProv: MessagesProvider
) {

/* A map with the args to work easily with them. The '_help is removed because the helper freeFormFieldormField will add it */
val argsMap: Map[Symbol, Any] = Args.withoutNones(fieldsArguments ++ globalArguments).toMap

/* List with every error */
val errors: Seq[Any] = {
val globalErrors = BSFieldInfo.errors(None, argsMap, msgsProv)
if (globalErrors.nonEmpty)
globalErrors
else
fields.flatMap { field =>
BSFieldInfo.errors(Some(field), argsMap, msgsProv)
}
}

/* Indicates if there is any error */
val hasErrors: Boolean = errors.nonEmpty || ArgsMap.isNotFalse(argsMap, Symbol("_error"))

/* The optional validation state ("success", "warning" or "error") */
lazy val status: Option[String] = BSFieldInfo.status(hasErrors, argsMap)

lazy val globalArgs: Seq[(Symbol, Any)] = globalArguments

lazy val fieldsArgs: Seq[(Symbol, Any)] = fieldsArguments
}

/** Companion object for class BSMultifieldInfo
*/
object BSMultifieldInfo {
def apply(
fields: Seq[Field],
globalArguments: Seq[(Symbol, Any)],
fieldsArguments: Seq[(Symbol, Any)],
msgsProv: MessagesProvider
): BSMultifieldInfo = {
new BSMultifieldInfo(fields, globalArguments, fieldsArguments, msgsProv)
}
}

/** Custom BSFieldConstructor for the library. Every BSFieldConstructor must extend this functionality.
*/
trait BSFieldConstructor[F <: BSFieldInfo] {
/* Renders the corresponding template of the field constructor */
def apply(fieldInfo: F, inputHtml: Html)(implicit msgsProv: MessagesProvider): Html
/* Renders the corresponding template of a fake field constructor (i.e. with the same structure but without the field) */
def apply(contentHtml: Html, argsMap: Map[Symbol, Any])(implicit msgsProv: MessagesProvider): Html
}

/** Renders an input field with its corresponding wrapper using the BSFieldConstructor.
* - fieldInfo: a BSFieldInfo with all the information about the field.
* - inputDef: function that returns a Html from the BSFieldInfo.
*/
def inputFormField[F <: BSFieldInfo](fieldInfo: F)(inputDef: F => Html)(implicit fc: BSFieldConstructor[F]): Html =
fc(fieldInfo, inputDef(fieldInfo))(fieldInfo.msgsProv)

/** Renders a fake field constructor using the BSFieldConstructor.
* - args: list of available arguments for the helper and the form-group
* - contentDef: function that returns a Html from a map of arguments
*/
def freeFormField[F <: BSFieldInfo](
args: Seq[(Symbol, Any)]
)(contentDef: Map[Symbol, Any] => Html)(implicit fc: BSFieldConstructor[F], msgsProv: MessagesProvider): Html = {
val argsWithoutNones = Args.withoutNones(args)
fc(contentDef(Args.inner(argsWithoutNones).toMap), argsWithoutNones.toMap)(msgsProv)
}

/** Renders a multi-field constructor using the BSFieldConstructor.
* - fieldInfo: a BSMultifieldInfo with all the information about the fields.
* - contentDef: function that returns a Html from the BSMultifieldInfo
*/
def multifieldFormField[F <: BSFieldInfo, M <: BSMultifieldInfo](multifieldInfo: M)(contentDef: M => Html)(implicit
fc: BSFieldConstructor[F]
): Html =
freeFormField(multifieldInfo.globalArgs)(_ => contentDef(multifieldInfo))(fc, multifieldInfo.msgsProv)
}
Loading