Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Merge branch 'master' into http-pipelining

  • Loading branch information...
commit d280228edbc280237f825efdc27426adcc97b03b 2 parents 710b724 + dd671f4
@huntc huntc authored
Showing with 638 additions and 289 deletions.
  1. +1 −1  documentation/manual/Home.md
  2. +1 −1  documentation/manual/detailledTopics/production/ProductionHeroku.md
  3. +0 −7 documentation/manual/hacking/CIServer.md
  4. +15 −0 documentation/manual/hacking/ThirdPartyTools.md
  5. +1 −1  documentation/manual/hacking/_Sidebar.md
  6. BIN  documentation/manual/hacking/images/cloudbees.png
  7. BIN  documentation/manual/hacking/images/yourkit.png
  8. +59 −0 documentation/manual/javaGuide/main/templates/JavaCustomTemplateFormat.md
  9. +2 −2 documentation/manual/javaGuide/main/templates/JavaTemplateUseCases.md
  10. +1 −0  documentation/manual/javaGuide/main/templates/_Sidebar.md
  11. +74 −0 documentation/manual/scalaGuide/main/templates/ScalaCustomTemplateFormat.md
  12. +1 −1  documentation/manual/scalaGuide/main/templates/ScalaTemplateUseCases.md
  13. +1 −0  documentation/manual/scalaGuide/main/templates/_Sidebar.md
  14. +15 −19 documentation/manual/scalaGuide/main/ws/ScalaOpenID.md
  15. +1 −1  framework/project/Build.scala
  16. +8 −5 framework/project/Dependencies.scala
  17. +4 −3 framework/project/Tasks.scala
  18. +1 −1  framework/project/build.properties
  19. +1 −1  framework/skeletons/java-skel/project/build.properties
  20. +1 −1  framework/skeletons/scala-skel/project/build.properties
  21. +5 −1 framework/src/console/src/main/scala/Console.scala
  22. +3 −1 framework/src/play-jdbc/src/main/scala/play/api/db/DB.scala
  23. +1 −1  framework/src/play-json/src/main/scala/play/api/libs/json/JsConstraints.scala
  24. +1 −1  framework/src/play-json/src/main/scala/play/api/libs/json/Writes.scala
  25. +21 −0 framework/src/play-json/src/test/scala/play/api/libs/json/JsonSpec.scala
  26. +12 −1 framework/src/play/src/main/scala/play/api/Configuration.scala
  27. +6 −0 framework/src/play/src/main/scala/play/api/http/ContentTypeOf.scala
  28. +64 −70 framework/src/play/src/main/scala/play/api/templates/Templates.scala
  29. +9 −0 framework/src/play/src/main/scala/play/core/system/ApplicationProvider.scala
  30. +15 −4 framework/src/play/src/test/scala/play/api/ConfigurationSpec.scala
  31. +6 −0 framework/src/play/src/test/scala/play/api/templates/TemplatesSpec.scala
  32. +5 −6 framework/src/sbt-plugin/src/main/scala/PlayCommands.scala
  33. +35 −1 framework/src/sbt-plugin/src/main/scala/PlayEclipse.scala
  34. +3 −2 framework/src/sbt-plugin/src/main/scala/PlayKeys.scala
  35. +8 −4 framework/src/sbt-plugin/src/main/scala/PlayReloader.scala
  36. +11 −5 framework/src/sbt-plugin/src/main/scala/PlaySettings.scala
  37. +2 −1  framework/src/templates-compiler/src/main/scala/play/templates/ScalaTemplateCompiler.scala
  38. +3 −0  framework/src/templates-compiler/src/test/scala/FakeRuntime.scala
  39. +1 −1  framework/src/templates-compiler/src/test/scala/TemplateCompilerSpec.scala
  40. +19 −1 framework/src/templates/src/main/scala/play/api/templates/ScalaTemplate.scala
  41. +1 −1  framework/test/csrftest-scala/project/build.properties
  42. +1 −1  framework/test/integrationtest-java/project/build.properties
  43. +1 −1  framework/test/integrationtest-scala/project/build.properties
  44. +1 −1  framework/test/integrationtest/project/build.properties
  45. +10 −4 play
  46. +1 −1  samples/java/comet-clock/project/build.properties
  47. +1 −1  samples/java/computer-database-jpa/project/build.properties
  48. +1 −1  samples/java/computer-database/project/build.properties
  49. +1 −1  samples/java/forms/project/build.properties
  50. +1 −1  samples/java/helloworld/project/build.properties
  51. +4 −0 samples/java/websocket-chat/app/controllers/Application.java
  52. +1 −55 samples/java/websocket-chat/app/views/chatRoom.scala.html
  53. +55 −0 samples/java/websocket-chat/app/views/chatRoom.scala.js
  54. +5 −4 samples/java/websocket-chat/conf/routes
  55. +1 −1  samples/java/websocket-chat/project/build.properties
  56. +32 −0 samples/java/websocket-chat/test/FunctionalTest.java
  57. +1 −1  samples/java/zentasks/project/build.properties
  58. +1 −1  samples/scala/comet-clock/project/build.properties
  59. +1 −1  samples/scala/comet-live-monitoring/project/build.properties
  60. +1 −1  samples/scala/computer-database/project/build.properties
  61. +1 −1  samples/scala/forms/project/build.properties
  62. +1 −1  samples/scala/helloworld/project/build.properties
  63. +4 −0 samples/scala/websocket-chat/app/controllers/Application.scala
  64. +1 −56 samples/scala/websocket-chat/app/views/chatRoom.scala.html
  65. +56 −0 samples/scala/websocket-chat/app/views/chatRoom.scala.js
  66. +1 −1  samples/scala/websocket-chat/app/views/main.scala.html
  67. +5 −4 samples/scala/websocket-chat/conf/routes
  68. +1 −1  samples/scala/websocket-chat/project/build.properties
  69. +26 −0 samples/scala/websocket-chat/test/ApplicationSpec.scala
  70. +1 −1  samples/scala/zentasks/project/build.properties
  71. +1 −1  samples/workinprogress/akka-chat/project/build.properties
  72. +1 −1  samples/workinprogress/pi-calculator/project/build.properties
  73. +1 −1  samples/workinprogress/twitterstream/project/build.properties
View
2  documentation/manual/Home.md
@@ -58,7 +58,7 @@
## Hacking Play
1. [[Building Play from source | BuildingFromSource]]
-1. [[CI server at Cloudbees | CIServer]]
+1. [[3rd Party Tooling | ThirdPartyTools]]
1. [[Repositories | Repositories]]
1. [[Issue tracker | Issues]]
1. [[Contributor guidelines | Guidelines]]
View
2  documentation/manual/detailledTopics/production/ProductionHeroku.md
@@ -90,7 +90,7 @@ $ heroku open
## Connecting to a database
-Heroku provides a number of relational an NoSQL databases through [Heroku Add-ons](http://addons.heroku.com). Play applications on Heroku are automatically provisioned a [Heroku Postgres](https://addons.heroku.com/heroku-postgresql) database. To configure your Play 2 application to use the Heroku Postgres database, first add the PostgreSQL JDBC driver to your application dependencies (`project/Build.scala`):
+Heroku provides a number of relational and NoSQL databases through [Heroku Add-ons](http://addons.heroku.com). Play applications on Heroku are automatically provisioned a [Heroku Postgres](https://addons.heroku.com/heroku-postgresql) database. To configure your Play 2 application to use the Heroku Postgres database, first add the PostgreSQL JDBC driver to your application dependencies (`project/Build.scala`):
```scala
"postgresql" % "postgresql" % "9.1-901-1.jdbc4"
View
7 documentation/manual/hacking/CIServer.md
@@ -1,7 +0,0 @@
-# Continuous integration server
-
-Our continuous integration runs on [[Cloudbees|http://www.cloudbees.com/]].
-
-[[http://www.cloudbees.com/sites/all/themes/custom/cloudbees_zen/logo-color.png]]
-
-[[https://playframework2.ci.cloudbees.com/]]
View
15 documentation/manual/hacking/ThirdPartyTools.md
@@ -0,0 +1,15 @@
+A big THANK YOU! to these sponsors for their support of open source projects.
+
+# Continuous Integration
+
+[[images/cloudbees.png]]
+
+Our continuous integration runs on [[Cloudbees|http://www.cloudbees.com/]]. We not only run CI on major release and master branches, but we also perform github pull request validation using CloudBees functionality.
+
+[[https://playframework2.ci.cloudbees.com/]]
+
+# Profiling
+
+[[images/yourkit.png]]
+
+We are using [[YourKit|http://www.yourkit.com/overview/index.jsp]] for profiling our Java and Scala code. YourKit really helps us keep Play's resource usage to the minimum that you'd expect.
View
2  documentation/manual/hacking/_Sidebar.md
@@ -1,7 +1,7 @@
### Hacking Play
- [[Building Play from source | BuildingFromSource]]
-- [[CI server at Cloudbees | CIServer]]
+- [[3rd Party Tools | ThirdPartyTools]]
- [[Repositories | Repositories]]
- [[Issues tracker | Issues]]
- [[Contributor guidelines | Guidelines]]
View
BIN  documentation/manual/hacking/images/cloudbees.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN  documentation/manual/hacking/images/yourkit.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
59 documentation/manual/javaGuide/main/templates/JavaCustomTemplateFormat.md
@@ -0,0 +1,59 @@
+# Adding support for a custom format to the template engine
+
+The built-in template engine supports common template formats (HTML, XML, etc.) but you can easily add support for your own formats, if needed. This page summarizes the steps to follow to support a custom format.
+
+## Overview of the the templating process
+
+The template engine builds its result by appending static and dynamic content parts of a template. Consider for instance the following template:
+
+```
+foo @bar baz
+```
+
+It consists in two static parts (`foo ` and ` baz`) around one dynamic part (`bar`). The template engine concatenates these parts together to build its result. Actually, in order to prevent cross-site scripting attacks, the value of `bar` can be escaped before being concatenated to the rest of the result. This escaping process is specific to each format: e.g. in the case of HTML you want to transform “<” into “&amp;lt;”.
+
+How does the template engine know which format correspond to a template file? It looks at its extension: e.g. if it ends with `.scala.html` it associates the HTML format to the file.
+
+In summary, to support your own template format you need to perform the following steps:
+
+* Implement the text integration process for the format ;
+* Associate a file extension to the format.
+
+## Implement a format
+
+Implement the `play.templates.Format<A>` interface that has the methods `A raw(String text)` and `A escape(String text)` that will be used to integrate static and dynamic template parts, respectively.
+
+The type parameter `A` of the format defines the result type of the template rendering, e.g. `Html` for a HTML template. This type must be a subtype of the `play.templates.Appendable<A>` trait that defines how to concatenates parts together.
+
+For convenience, Play provides a `play.api.templates.BufferedContent<A>` abstract class that implements `play.templates.Appendable<A>` using a `StringBuilder` to build its result and that implements the `play.mvc.Content` interface so Play knows how to serialize it as an HTTP response body.
+
+In short, you need to write to classes: one defining the result (implementing `play.templates.Appendable<A>`) and one defining the text integration process (implementing `play.templates.Format<A>`). For instance, here is how the HTML format could be defined:
+
+```java
+public class Html extends BufferedContent<Html> {
+ public Html(StringBuilder buffer) {
+ super(buffer);
+ }
+ String contentType() {
+ return "text/html";
+ }
+}
+
+public class HtmlFormat implements Format<Html> {
+ Html raw(String text: String) { … }
+ Html escape(String text) { … }
+ public static final HtmlFormat instance = new HtmlFormat(); // The build process needs a static reference to the format (see the next section)
+}
+```
+
+## Associate a file extension to the format
+
+The templates are compiled into a `.scala` files by the build process just before compiling the whole application sources. The `sbt.PlayKeys.templatesTypes` key is a sbt setting of type `Map[String, String]` defining the mapping between file extensions and template formats. For instance, if you want Play to use your onw HTML format implementation you have to write the following in your build file to associate the `.scala.html` files to your custom `my.HtmlFormat` format:
+
+```scala
+templatesTypes += ("html" -> "my.HtmlFormat.instance")
+```
+
+Note that the right side of the arrow contains the fully qualified name of a static value of type `play.templates.Format<?>`.
+
+> **Next:** [[HTTP form submission and validation | JavaForms]]
View
4 documentation/manual/javaGuide/main/templates/JavaTemplateUseCases.md
@@ -127,7 +127,7 @@ Again, there’s nothing special here. You can just call any other template you
```
## moreScripts and moreStyles equivalents
-
+> **Next:** [[HTTP form submission and validation | ScalaForms]]
To define old moreScripts or moreStyles variables equivalents (like on Play! 1.x) on a Scala template, you can define a variable in the main template like this :
```html
@@ -183,4 +183,4 @@ And on an extended template that not need an extra script, just like this :
}
```
-> **Next:** [[HTTP form submission and validation | JavaForms]]
+> **Next:** [[Custom formats | JavaCustomTemplateFormat]]
View
1  documentation/manual/javaGuide/main/templates/_Sidebar.md
@@ -2,6 +2,7 @@
- [[Templates syntax | JavaTemplates]]
- [[Common use cases | JavaTemplateUseCases]]
+- [[Custom formats | JavaCustomTemplateFormat]]
### Main concepts
View
74 documentation/manual/scalaGuide/main/templates/ScalaCustomTemplateFormat.md
@@ -0,0 +1,74 @@
+# Adding support for a custom format to the template engine
+
+The built-in template engine supports common template formats (HTML, XML, etc.) but you can easily add support for your own formats, if needed. This page summarizes the steps to follow to support a custom format.
+
+## Overview of the the templating process
+
+The template engine builds its result by appending static and dynamic content parts of a template. Consider for instance the following template:
+
+```
+foo @bar baz
+```
+
+It consists in two static parts (`foo ` and ` baz`) around one dynamic part (`bar`). The template engine concatenates these parts together to build its result. Actually, in order to prevent cross-site scripting attacks, the value of `bar` can be escaped before being concatenated to the rest of the result. This escaping process is specific to each format: e.g. in the case of HTML you want to transform “<” into “&amp;lt;”.
+
+How does the template engine know which format correspond to a template file? It looks at its extension: e.g. if it ends with `.scala.html` it associates the HTML format to the file.
+
+Finally, you usually want your template files to be used as the body of your HTTP responses, so you have to define how to make a Play result from a template rendering result.
+
+In summary, to support your own template format you need to perform the following steps:
+
+* Implement the text integration process for the format ;
+* Associate a file extension to the format ;
+* Eventually tell Play how to send the result of a template rendering as an HTTP response body.
+
+## Implement a format
+
+Implement the `play.templates.Format[A]` trait that has the methods `raw(text: String): A` and `escape(text: String): A` that will be used to integrate static and dynamic template parts, respectively.
+
+The type parameter `A` of the format defines the result type of the template rendering, e.g. `Html` for a HTML template. This type must be a subtype of the `play.templates.Appendable[A]` trait that defines how to concatenates parts together.
+
+For convenience, Play provides a `play.api.templates.BufferedContent[A]` abstract class that implements `play.templates.Appendable[A]` using a `StringBuilder` to build its result and that implements the `play.api.mvc.Content` trait so Play knows how to serialize it as an HTTP response body (see the last section of this page for details).
+
+In short, you need to write to classes: one defining the result (implementing `play.templates.Appendable[A]`) and one defining the text integration process (implementing `play.templates.Format[A]`). For instance, here is how the HTML format is defined:
+
+```scala
+// The `Html` result type. We extend `BufferedContent[Html]` rather than just `Appendable[Html]` so
+// Play knows how to make an HTTP result from a `Html` value
+class Html(buffer: StringBuilder) extends BufferedContent[Html](buffer) {
+ val contentType = MimeTypes.HTML
+}
+
+object HtmlFormat extends Format[Html] {
+ def raw(text: String): Html = …
+ def escape(text: String): Html = …
+}
+```
+
+## Associate a file extension to the format
+
+The templates are compiled into a `.scala` files by the build process just before compiling the whole application sources. The `sbt.PlayKeys.templatesTypes` key is a sbt setting of type `Map[String, String]` defining the mapping between file extensions and template formats. For instance, if HTML was not supported out of the box by Play, you would have to write the following in your build file to associate the `.scala.html` files to the `play.api.templates.HtmlFormat` format:
+
+```scala
+templatesTypes += ("html" -> "play.api.templates.HtmlFormat")
+```
+
+Note that the right side of the arrow contains the fully qualified name of a value of type `play.templates.Format[_]`.
+
+## Tell Play how to make an HTTP result from a template result type
+
+Play can write an HTTP response body for any value of type `A` for which it exists an implicit `play.api.http.Writeable[A]` value. So all you need is to define such a value for your template result type. For instance, here is how to define such a value for HTTP:
+
+```scala
+implicit def writableHttp(implicit codec: Codec): Writeable[Http] =
+ Writeable[Http](result => codec.encode(result.body), Some(ContentTypes.HTTP))
+```
+
+> **Note:** if your template result type extends `play.api.templates.BufferedContent` you only need to define an
+> implicit `play.api.http.ContentTypeOf` value:
+> ```scala
+> implicit def contentTypeHttp(implicit codec: Codec): ContentTypeOf[Http] =
+> ContentTypeOf[Http](Some(ContentTypes.HTTP))
+> ```
+
+> **Next:** [[HTTP form submission and validation | ScalaForms]]
View
2  documentation/manual/scalaGuide/main/templates/ScalaTemplateUseCases.md
@@ -181,4 +181,4 @@ And on an extended template that not need an extra script, just like this :
}
```
-> **Next:** [[HTTP form submission and validation | ScalaForms]]
+> **Next:** [[Custom format | ScalaCustomTemplateFormat]]
View
1  documentation/manual/scalaGuide/main/templates/_Sidebar.md
@@ -2,6 +2,7 @@
- [[Scala templates syntax | ScalaTemplates]]
- [[Common use cases | ScalaTemplateUseCases]]
+- [[Custom format | ScalaCustomTemplateFormat]]
### Main concepts
View
34 documentation/manual/scalaGuide/main/ws/ScalaOpenID.md
@@ -15,10 +15,10 @@ Step 1 may be omitted if all your users are using the same OpenID provider (for
The OpenID API has two important functions:
-* `OpenID.redirectURL` calculates the URL where you should redirect the user. It involves fetching the user's OpenID page, this is why it returns a `Promise[String]` rather than a `String`. If the OpenID is invalid, the returned `Promise` will be a `Thrown`.
-* `OpenID.verifiedId` needs an implicit `Request`, and inspects it to establish the user information, including his verified OpenID. It will do a call to the OpenID server to check the authenticity of the information, this is why it returns a `Promise[UserInfo]` rather than just `UserInfo`. If the information is not correct or if the server check is false (for example if the redirect URL has been forged), the returned `Promise` will be a `Thrown`.
+* `OpenID.redirectURL` calculates the URL where you should redirect the user. It involves fetching the user's OpenID page asynchronously, this is why it returns a `Future[String]`. If the OpenID is invalid, the returned `Future` will fail.
+* `OpenID.verifiedId` needs an implicit `Request` and inspects it to establish the user information, including his verified OpenID. It will do a call to the OpenID server asynchronously to check the authenticity of the information, this is why a `Future[UserInfo]` is returned. If the information is not correct or if the server check is false (for example if the redirect URL has been forged), the returned `Future` will fail.
-In any case, when the `Promise` you get is a `Thrown`, you should look at the `Throwable` and redirect back the user to the login page with relevant information.
+If the `Future` fails, you can define a fallback, which redirects back the user to the login page or return a `BadRequest`.
Here is an example of usage (from a controller):
@@ -34,27 +34,23 @@ def loginPost = Action { implicit request =>
error => {
Logger.info("bad request " + error.toString)
BadRequest(error.toString)
- },
- {
- case (openid) => AsyncResult(OpenID.redirectURL(openid, routes.Application.openIDCallback.absoluteURL())
- .extend( _.value match {
- case Redeemed(url) => Redirect(url)
- case Thrown(t) => Redirect(routes.Application.login)
- }))
+ }, {
+ case (openid) => AsyncResult {
+ val url = OpenID.redirectURL(openid,
+ routes.Application.openIDCallback.absoluteURL())
+ url.map(a => Redirect(a)).
+ fallbackTo(Future(Redirect(routes.Application.login)))
+ }
}
)
}
def openIDCallback = Action { implicit request =>
AsyncResult(
- OpenID.verifiedId.extend( _.value match {
- case Redeemed(info) => Ok(info.id + "\n" + info.attributes)
- case Thrown(t) => {
- // Here you should look at the error, and give feedback to the user
- Redirect(routes.Application.login)
- }
- })
- )
+ OpenID.verifiedId.map((info: UserInfo) =>
+ Ok(info.id + "\n" + info.attributes)).
+ fallbackTo(Future(Forbidden))
+ )
}
```
@@ -76,4 +72,4 @@ OpenID.redirectURL(
Attributes will then be available in the `UserInfo` provided by the OpenID server.
-> **Next:** [[OAuth | ScalaOAuth]]
+> **Next:** [[OAuth | ScalaOAuth]]
View
2  framework/project/Build.scala
@@ -26,7 +26,7 @@ object BuildSettings {
val buildScalaVersion = propOr("scala.version", "2.10.0")
// TODO - Try to compute this from SBT...
val buildScalaVersionForSbt = propOr("play.sbt.scala.version", "2.9.2")
- val buildSbtVersion = propOr("play.sbt.version", "0.12.2")
+ val buildSbtVersion = propOr("play.sbt.version", "0.12.3")
val buildSbtMajorVersion = "0.12"
val buildSbtVersionBinaryCompatible = "0.12"
View
13 framework/project/Dependencies.scala
@@ -6,12 +6,15 @@ object Dependencies {
val specsBuild = "org.specs2" %% "specs2" % "1.14"
val scalaIoFileBuild = "com.github.scala-incubator.io" %% "scala-io-file" % "0.4.2"
val scalaIoFileSbt = "com.github.scala-incubator.io" %% "scala-io-file" % "0.4.1" exclude ("javax.transaction", "jta")
+ val guava = "com.google.guava" % "guava" % "13.0.1"
val jdbcDeps = Seq(
- ("com.jolbox" % "bonecp" % "0.7.1.RELEASE" notTransitive ())
- .exclude("com.google.guava", "guava")
- .exclude("org.slf4j", "slf4j-api"),
+ "com.jolbox" % "bonecp" % "0.7.1.RELEASE" exclude ("com.google.guava", "guava"),
+
+ // bonecp needs it, but due to guavas stupid version numbering of older versions ("r08"), we need to explicitly
+ // declare a dependency on the newer version so that ivy can know which one to include
+ guava,
"com.h2database" % "h2" % "1.3.168",
@@ -50,7 +53,7 @@ object Dependencies {
.exclude("com.google.guava", "guava")
.exclude("javassist", "javassist"),
- "com.google.guava" % "guava" % "13.0.1",
+ guava,
"com.google.code.findbugs" % "jsr305" % "2.0.1",
@@ -168,7 +171,7 @@ object Dependencies {
specsBuild % "test")
val testDependencies = Seq(
- "junit" % "junit-dep" % "4.10",
+ "junit" % "junit" % "4.11",
specsBuild,
"com.novocode" % "junit-interface" % "0.10-M2",
("com.google.guava" % "guava" % "10.0.1" notTransitive()),
View
7 framework/project/Tasks.scala
@@ -148,7 +148,8 @@ object Tasks {
(file("src/anorm/src/main/scala") ** "*.scala").get ++
(file("src/play-filters-helpers/src/main/scala") ** "*.scala").get ++
(file("src/play-jdbc/src/main/scala") ** "*.scala").get ++
- (file("src/play/target/scala-" + sbv + "/src_managed/main/views/html/helper") ** "*.scala").get
+ (file("src/play/target/scala-" + sbv + "/src_managed/main/views/html/helper") ** "*.scala").get ++
+ (file("src/templates/src/main/scala") ** "*.scala").get
val options = Seq("-sourcepath", base.getAbsolutePath, "-doc-source-url", "https://github.com/playframework/Play20/tree/" + BuildSettings.buildVersion + "/framework€{FILE_PATH}.scala")
new Scaladoc(10, cs.scalac)("Play " + BuildSettings.buildVersion + " Scala API", sourceFiles, classpath.map(_.data) ++ allJars, file("../documentation/api/scala"), options, s.log)
@@ -192,9 +193,9 @@ object Tasks {
(sourceDirectory ** "*.scala.html").get.foreach {
template =>
- val compile = compiler.getDeclaredMethod("compile", classOf[java.io.File], classOf[java.io.File], classOf[java.io.File], classOf[String], classOf[String], classOf[String])
+ val compile = compiler.getDeclaredMethod("compile", classOf[java.io.File], classOf[java.io.File], classOf[java.io.File], classOf[String], classOf[String])
try {
- compile.invoke(null, template, sourceDirectory, generatedDir, "play.api.templates.Html", "play.api.templates.HtmlFormat", "import play.api.templates._\nimport play.api.templates.PlayMagic._")
+ compile.invoke(null, template, sourceDirectory, generatedDir, "play.api.templates.HtmlFormat", "import play.api.templates._\nimport play.api.templates.PlayMagic._")
} catch {
case e: java.lang.reflect.InvocationTargetException => {
streams.log.error("Compilation failed for %s".format(template))
View
2  framework/project/build.properties
@@ -1 +1 @@
-sbt.version=0.12.2
+sbt.version=0.12.3
View
2  framework/skeletons/java-skel/project/build.properties
@@ -1 +1 @@
-sbt.version=0.12.2
+sbt.version=0.12.3
View
2  framework/skeletons/scala-skel/project/build.properties
@@ -1 +1 @@
-sbt.version=0.12.2
+sbt.version=0.12.3
View
6 framework/src/console/src/main/scala/Console.scala
@@ -19,7 +19,11 @@ object Console {
|| __/|_|\____|\__ (_)
||_| |__/
|
- |""".stripMargin) + ("play! " + play.core.PlayVersion.current + " (using Java " + System.getProperty("java.version") + " and Scala " + play.core.PlayVersion.scalaVersion + "), http://www.playframework.com")
+ |""".stripMargin) +
+ ("play! " + play.core.PlayVersion.current +
+ " built with Scala " + play.core.PlayVersion.scalaVersion +
+ " (running Java " + System.getProperty("java.version") + ")," +
+ " http://www.playframework.com")
// -- Commands
View
4 framework/src/play-jdbc/src/main/scala/play/api/db/DB.scala
@@ -12,7 +12,7 @@ import javax.sql._
import com.jolbox.bonecp._
import com.jolbox.bonecp.hooks._
-import scala.util.control.NonFatal
+import scala.util.control.{NonFatal, ControlThrowable}
/**
* The Play Database API manages several connection pools.
@@ -101,6 +101,7 @@ trait DBApi {
connection.commit()
r
} catch {
+ case e: ControlThrowable => connection.commit(); throw e
case NonFatal(e) => connection.rollback(); throw e
}
}
@@ -379,6 +380,7 @@ private[db] class BoneCPApi(configuration: Configuration, classloader: ClassLoad
datasource.setIdleMaxAge(conf.getMilliseconds("idleMaxAge").getOrElse(1000 * 60 * 10), java.util.concurrent.TimeUnit.MILLISECONDS)
datasource.setMaxConnectionAge(conf.getMilliseconds("maxConnectionAge").getOrElse(1000 * 60 * 60), java.util.concurrent.TimeUnit.MILLISECONDS)
datasource.setDisableJMX(conf.getBoolean("disableJMX").getOrElse(true))
+ datasource.setStatisticsEnabled(conf.getBoolean("statisticsEnabled").getOrElse(false))
datasource.setIdleConnectionTestPeriod(conf.getMilliseconds("idleConnectionTestPeriod").getOrElse(1000 * 60), java.util.concurrent.TimeUnit.MILLISECONDS)
// Release helper threads has caused users issues, and the feature has been removed altogether from BoneCP 0.8.0
// (which is yet to be released) because it offered no real performance advantage. Once we upgrade to BoneCP 0.8.x,
View
2  framework/src/play-json/src/main/scala/play/api/libs/json/JsConstraints.scala
@@ -189,7 +189,7 @@ trait PathWrites {
def jsPickBranchUpdate(path: JsPath, wrs: OWrites[JsValue]): OWrites[JsValue] =
OWrites[JsValue]{ js =>
JsPath.createObj(
- path -> path(js).headOption.flatMap( js => js.asOpt[JsObject].map( obj => obj ++ wrs.writes(obj) ) ).getOrElse(JsNull)
+ path -> path(js).headOption.flatMap( js => js.asOpt[JsObject].map( obj => obj.deepMerge(wrs.writes(obj)) ) ).getOrElse(JsNull)
)
}
View
2  framework/src/play-json/src/main/scala/play/api/libs/json/Writes.scala
@@ -44,7 +44,7 @@ object OWrites extends PathWrites with ConstraintWrites {
implicit val functionalCanBuildOWrites:FunctionalCanBuild[OWrites] = new FunctionalCanBuild[OWrites] {
- def apply[A,B](wa: OWrites[A], wb: OWrites[B]):OWrites[A~B] = OWrites[A~B]{ case a ~ b => wa.writes(a) ++ wb.writes(b)}
+ def apply[A,B](wa: OWrites[A], wb: OWrites[B]):OWrites[A~B] = OWrites[A~B]{ case a ~ b => wa.writes(a).deepMerge(wb.writes(b)) }
}
View
21 framework/src/play-json/src/test/scala/play/api/libs/json/JsonSpec.scala
@@ -241,6 +241,27 @@ object JsonSpec extends Specification {
)
)
}
+
+ "write in 2nd level" in {
+ case class TestCase(id: String, attr1: String, attr2: String)
+
+ val js = Json.obj(
+ "id" -> "my-id",
+ "data" -> Json.obj(
+ "attr1" -> "foo",
+ "attr2" -> "bar"
+ )
+ )
+
+ implicit val testCaseWrites: Writes[TestCase] = (
+ (__ \ "id").write[String] and
+ (__ \ "data" \ "attr1").write[String] and
+ (__ \ "data" \ "attr2").write[String]
+ )(unlift(TestCase.unapply))
+
+ Json.toJson(TestCase("my-id", "foo", "bar")) must beEqualTo(js)
+
+ }
}
View
13 framework/src/play/src/main/scala/play/api/Configuration.scala
@@ -72,7 +72,18 @@ object Configuration {
* Create a ConfigFactory object from the data passed as a Map.
*/
def from(data: Map[String, Any]) = {
- Configuration(ConfigFactory.parseMap(data.asJava))
+
+ def asJavaRecursively[A](data: Map[A, Any]): Map[A, Any] = {
+ data.mapValues { value =>
+ value match {
+ case v: Map[_, _] => asJavaRecursively(v).asJava
+ case v: Iterable[_] => v.asJava
+ case v => v
+ }
+ }
+ }
+
+ Configuration(ConfigFactory.parseMap(asJavaRecursively[String](data).asJava))
}
private def configError(origin: ConfigOrigin, message: String, e: Option[Throwable] = None): PlayException = {
View
6 framework/src/play/src/main/scala/play/api/http/ContentTypeOf.scala
@@ -59,6 +59,12 @@ trait DefaultContentTypeOfs {
}
/**
+ * Default content type for `JavaScript` values.
+ */
+ implicit def contentTypeOf_JavaScript(implicit codec: Codec): ContentTypeOf[JavaScript] =
+ ContentTypeOf[JavaScript](Some(ContentTypes.JAVASCRIPT))
+
+ /**
* Default content type for `String` values (`text/plain`).
*/
implicit def contentTypeOf_String(implicit codec: Codec): ContentTypeOf[String] = {
View
134 framework/src/play/src/main/scala/play/api/templates/Templates.scala
@@ -2,36 +2,36 @@ package play.api.templates
import play.api.mvc._
import play.templates._
+import play.api.http.MimeTypes
+import org.apache.commons.lang3.StringEscapeUtils
+
/**
- * Content type used in default HTML templates.
- *
- * @param buffer the HTML text
+ * Appendable content using a StringBuilder.
+ * @param buffer StringBuilder to use
+ * @tparam A self-type
*/
-class Html(val buffer: StringBuilder) extends Appendable[Html] with Content with play.mvc.Content {
+abstract class BufferedContent[A <: BufferedContent[A]](private val buffer: StringBuilder) extends Appendable[A] with Content with play.mvc.Content { this: A =>
- /**
- * Appends another Html fragment to this, modifying this.
- */
- def +=(other: Html): Html = {
+ def +=(other: A) = {
buffer.append(other.buffer)
this
}
- @deprecated(message="Use += method instead.", since="2012/12")
- def +(other: Html): Html = {
- this += other
- }
+ override def toString = buffer.toString()
+
+ def body = toString
- override def toString = buffer.toString
+}
+/**
+ * Content type used in default HTML templates.
+ */
+class Html(buffer: StringBuilder) extends BufferedContent[Html](buffer) {
/**
- * Content type of HTML (`text/html`).
+ * Content type of HTML.
*/
- def contentType: String = "text/html"
-
- def body: String = toString
-
+ val contentType = MimeTypes.HTML
}
/**
@@ -84,34 +84,12 @@ object HtmlFormat extends Format[Html] {
/**
* Content type used in default text templates.
- *
- * @param text The plain text.
*/
-class Txt(text: String) extends Appendable[Txt] with Content with play.mvc.Content {
- private val buffer = new StringBuilder(text)
-
- /**
- * Appends another text fragment to this, modifying this.
- */
- def +=(other: Txt): Txt = {
- buffer.append(other.buffer)
- this
- }
-
- @deprecated(message="Use += method instead.", since="2012/12")
- def +(other: Txt): Txt = {
- this += other
- }
-
- override def toString = buffer.toString
-
+class Txt(buffer: StringBuilder) extends BufferedContent[Txt](buffer) {
/**
* Content type of text (`text/plain`).
*/
- def contentType = "text/plain"
-
- def body = toString
-
+ def contentType = MimeTypes.TEXT
}
/**
@@ -123,14 +101,14 @@ object Txt {
* Creates a text fragment with initial content specified.
*/
def apply(text: String): Txt = {
- new Txt(text)
+ new Txt(new StringBuilder(text))
}
/**
* Creates an empty text fragment.
*/
- def empty = new Txt("")
+ def empty = new Txt(new StringBuilder)
}
@@ -153,34 +131,12 @@ object TxtFormat extends Format[Txt] {
/**
* Content type used in default XML templates.
- *
- * @param text the plain xml text
*/
-class Xml(text: String) extends Appendable[Xml] with Content with play.mvc.Content {
- private val buffer = new StringBuilder(text)
-
- /**
- * Appends another XML fragment to this, modifying this.
- */
- def +=(other: Xml): Xml = {
- buffer.append(other.buffer)
- this
- }
-
- @deprecated(message="Use += method instead.", since="2012/12")
- def +(other: Xml): Xml = {
- this += other
- }
-
- override def toString = buffer.toString
-
+class Xml(buffer: StringBuilder) extends BufferedContent[Xml](buffer) {
/**
* Content type of XML (`application/xml`).
*/
- def contentType = "application/xml"
-
- def body = toString
-
+ def contentType = MimeTypes.XML
}
/**
@@ -192,13 +148,13 @@ object Xml {
* Creates an XML fragment with initial content specified.
*/
def apply(text: String): Xml = {
- new Xml(text)
+ new Xml(new StringBuilder(text))
}
/**
* Create an empty XML fragment.
*/
- def empty = new Xml("")
+ def empty = new Xml(new StringBuilder)
}
@@ -219,6 +175,44 @@ object XmlFormat extends Format[Xml] {
}
+/**
+ * Type used in default JavaScript templates.
+ */
+class JavaScript(buffer: StringBuilder) extends BufferedContent[JavaScript](buffer) {
+ /**
+ * Content type of JavaScript
+ */
+ val contentType = MimeTypes.JAVASCRIPT
+}
+
+/**
+ * Helper for JavaScript utility methods.
+ */
+object JavaScript {
+ /**
+ * Creates a JavaScript fragment with initial content specified
+ */
+ def apply(content: String) = new JavaScript(new StringBuilder(content))
+}
+
+/**
+ * Formatter for JavaScript content.
+ */
+object JavaScriptFormat extends Format[JavaScript] {
+ /**
+ * Integrate `text` without performing any escaping process.
+ * @param text Text to integrate
+ */
+ def raw(text: String): JavaScript = JavaScript(text)
+
+ /**
+ * Escapes `text` using JavaScript String rules.
+ * @param text Text to integrate
+ */
+ def escape(text: String): JavaScript = JavaScript(StringEscapeUtils.escapeEcmaScript(text))
+
+}
+
/** Defines a magic helper for Play templates. */
object PlayMagic {
View
9 framework/src/play/src/main/scala/play/core/system/ApplicationProvider.scala
@@ -95,6 +95,14 @@ class ReloadableApplication(sbtLink: SBTLink) extends ApplicationProvider {
synchronized {
+ // Let's load the application on another thread
+ // as we are now on the Netty IO thread.
+ //
+ // Because we are on DEV mode here, it doesn't really matter
+ // but it's more coherent with the way it works in PROD mode.
+ implicit val ec = play.core.Execution.internalContext
+ Await.result(scala.concurrent.Future {
+
val reloaded = sbtLink.reload match {
case t: Throwable => Left(t)
case cl: ClassLoader => Right(Some(cl))
@@ -156,6 +164,7 @@ class ReloadableApplication(sbtLink: SBTLink) extends ApplicationProvider {
maybeApplication.getOrElse(lastState)
}
+ }, Duration.Inf)
}
}
View
19 framework/src/play/src/test/scala/play/api/ConfigurationSpec.scala
@@ -4,21 +4,32 @@ import org.specs2.mutable.Specification
object ConfigurationSpec extends Specification {
- def exampleConfig = Configuration.from(Map("foo.bar1" -> "value1", "foo.bar2" -> "value2", "blah" -> "value3"))
+ def exampleConfig = Configuration.from(
+ Map(
+ "foo.bar1" -> "value1",
+ "foo.bar2" -> "value2",
+ "blah" -> List("value3", "value4", "value5"),
+ "blah2" -> Map(
+ "blah3" -> Map(
+ "blah4" -> "value6"
+ )
+ )
+ )
+ )
"Configuration" should {
"be accessible as an entry set" in {
val map = Map(exampleConfig.entrySet.toList:_*)
- map.keySet must contain("foo.bar1", "foo.bar2", "blah").only
+ map.keySet must contain("foo.bar1", "foo.bar2", "blah", "blah2.blah3.blah4").only
}
"make all paths accessible" in {
- exampleConfig.keys must contain("foo.bar1", "foo.bar2", "blah").only
+ exampleConfig.keys must contain("foo.bar1", "foo.bar2", "blah", "blah2.blah3.blah4").only
}
"make all sub keys accessible" in {
- exampleConfig.subKeys must contain("foo", "blah").only
+ exampleConfig.subKeys must contain("foo", "blah", "blah2").only
}
}
View
6 framework/src/play/src/test/scala/play/api/templates/TemplatesSpec.scala
@@ -27,4 +27,10 @@ object TemplatesSpec extends Specification {
}
}
+ "JavaScriptFormat" should {
+ """escape ''', '"' and '\'""" in {
+ JavaScriptFormat.escape("""foo ' bar " baz \""").body must equalTo ("""foo \' bar \" baz \\""")
+ }
+ }
+
}
View
11 framework/src/sbt-plugin/src/main/scala/PlayCommands.scala
@@ -295,25 +295,24 @@ exec java $* -cp $classpath """ + customFileName.map(fn => "-Dconfig.file=`dirna
}
- val ScalaTemplates = (state: State, sourceDirectory: File, generatedDir: File, templateTypes: PartialFunction[String, (String, String)], additionalImports: Seq[String]) => {
+ val ScalaTemplates = (state: State, sourceDirectory: File, generatedDir: File, templateTypes: Map[String, String], additionalImports: Seq[String]) => {
import play.templates._
- val templateExt: PartialFunction[File, (File, String, String, String)] = {
- case p if templateTypes.isDefinedAt(p.name.split('.').last) =>
+ val templateExt: PartialFunction[File, (File, String, String)] = {
+ case p if templateTypes.contains(p.name.split('.').last) =>
val extension = p.name.split('.').last
val exts = templateTypes(extension)
- (p, extension, exts._1, exts._2)
+ (p, extension, exts)
}
(generatedDir ** "*.template.scala").get.map(GeneratedSource(_)).foreach(_.sync())
try {
(sourceDirectory ** "*.scala.*").get.collect(templateExt).foreach {
- case (template, extension, t, format) =>
+ case (template, extension, format) =>
ScalaTemplateCompiler.compile(
template,
sourceDirectory,
generatedDir,
- t,
format,
additionalImports.map("import " + _.replace("%format%", extension)).mkString("\n"))
}
View
36 framework/src/sbt-plugin/src/main/scala/PlayEclipse.scala
@@ -1,6 +1,10 @@
package sbt
import Keys._
+import PlayKeys._
+
+import java.io.{ FileWriter, Writer }
+import java.util.Properties
trait PlayEclipse {
this: PlayCommands =>
@@ -120,8 +124,38 @@ trait PlayEclipse {
EclipseKeys.createSrc := EclipseCreateSrc.Default,
EclipseKeys.eclipseOutput := Some(".target"),
EclipseKeys.projectFlavor := flavor,
- EclipseKeys.preTasks := Seq(compile in Compile),
+ EclipseKeys.preTasks := Seq(compile in Compile) ++ Seq(scalaIdePlay2Prefs),
EclipseKeys.projectTransformerFactories := projectTransformers,
EclipseKeys.classpathTransformerFactories := classPathTransformers)
}
+}
+
+object PlayEclipse {
+
+ def saveScalaIdePlay2Prefs(ref: ProjectRef, structure: Load.BuildStructure, baseDir: File) = {
+ def fileWriterMkdirs(file: File): FileWriter = {
+ file.getParentFile.mkdirs()
+ new FileWriter(file)
+ }
+
+ def saveProperties(file: File, settings: Seq[(String, String)]): Unit =
+ if (!settings.isEmpty) {
+ val properties = new Properties
+ for ((key, value) <- settings) properties.setProperty(key, value)
+ val writer = fileWriterMkdirs(file);
+ try {
+ properties.store(writer, "Generated by sbt-plugin")
+ } finally {
+ writer.close();
+ }
+ }
+
+ Project.getProject(ref, structure).foreach { p =>
+ (templatesImport in ref get structure.data).foreach { imports =>
+ val value = imports.mkString ("import ", "\nimport ", "\n")
+ val properties = Seq(("eclipse.preferences.version", "1"), ("templateImports", value))
+ saveProperties(baseDir / ".settings" / "org.scala-ide.play2.prefs", properties)
+ }
+ }
+ }
}
View
5 framework/src/sbt-plugin/src/main/scala/PlayKeys.scala
@@ -50,7 +50,7 @@ trait PlayKeys {
val ebeanEnabled = SettingKey[Boolean]("play-ebean-enabled")
- val templatesTypes = SettingKey[PartialFunction[String, (String, String)]]("play-templates-formats")
+ val templatesTypes = SettingKey[Map[String, String]]("play-templates-formats")
val closureCompilerOptions = SettingKey[Seq[String]]("play-closure-compiler-options")
@@ -68,6 +68,7 @@ trait PlayKeys {
val devSettings = SettingKey[Seq[(String,String)]]("play-dev-settings")
+ val scalaIdePlay2Prefs = TaskKey[Unit]("scala-ide-play2-prefs")
}
object PlayKeys extends PlayKeys
@@ -77,4 +78,4 @@ trait PlayInternalKeys {
val buildRequire = TaskKey[Seq[(File, File)]]("play-build-require-assets")
val playCompileEverything = TaskKey[Seq[sbt.inc.Analysis]]("play-compile-everything")
val playPackageEverything = TaskKey[Seq[File]]("play-package-everything")
-}
+}
View
12 framework/src/sbt-plugin/src/main/scala/PlayReloader.scala
@@ -346,13 +346,17 @@ trait PlayReloader {
Incomplete.allExceptions(incomplete).headOption.map {
case e: PlayException => e
case e: xsbti.CompileFailed => {
- getProblems(incomplete).headOption.map(CompilationException(_)).getOrElse {
- UnexpectedException(Some("Compilation failed without reporting any problem!?"), Some(e))
- }
+ getProblems(incomplete)
+ .filter(_.severity == xsbti.Severity.Error)
+ .headOption
+ .map(CompilationException(_))
+ .getOrElse {
+ UnexpectedException(Some("The compilation failed without reporting any problem!"), Some(e))
+ }
}
case e: Exception => UnexpectedException(unexpected = Some(e))
}.getOrElse {
- UnexpectedException(Some("Compilation task failed without any exception!?"))
+ UnexpectedException(Some("The compilation task failed without any exception!"))
}
}
.right.map { compilationResult =>
View
16 framework/src/sbt-plugin/src/main/scala/PlaySettings.scala
@@ -2,6 +2,7 @@ package sbt
import Keys._
import PlayKeys._
+import PlayEclipse._
trait PlaySettings {
this: PlayCommands with PlayPositionMapper with PlayRun =>
@@ -217,10 +218,15 @@ trait PlaySettings {
templatesImport := Seq("play.api.templates._", "play.api.templates.PlayMagic._"),
- templatesTypes := {
- case "html" => ("play.api.templates.Html", "play.api.templates.HtmlFormat")
- case "txt" => ("play.api.templates.Txt", "play.api.templates.TxtFormat")
- case "xml" => ("play.api.templates.Xml", "play.api.templates.XmlFormat")
- })
+ scalaIdePlay2Prefs <<= (state, thisProjectRef, baseDirectory) map { (s, r, baseDir) => saveScalaIdePlay2Prefs(r, Project structure s, baseDir) },
+
+ templatesTypes := Map(
+ "html" -> "play.api.templates.HtmlFormat",
+ "txt" -> "play.api.templates.TxtFormat",
+ "xml" -> "play.api.templates.XmlFormat",
+ "js" -> "play.api.templates.JavaScriptFormat"
+ )
+
+ )
}
View
3  framework/src/templates-compiler/src/main/scala/play/templates/ScalaTemplateCompiler.scala
@@ -170,7 +170,8 @@ package play.templates {
case class Block(whitespace: String, args: Option[PosString], content: Seq[TemplateTree]) extends ScalaExpPart with Positional
case class Value(ident: PosString, block: Block) extends Positional
- def compile(source: File, sourceDirectory: File, generatedDirectory: File, resultType: String, formatterType: String, additionalImports: String = "") = {
+ def compile(source: File, sourceDirectory: File, generatedDirectory: File, formatterType: String, additionalImports: String = "") = {
+ val resultType = formatterType + ".Appendable"
val (templateName, generatedSource) = generatedFile(source, sourceDirectory, generatedDirectory)
if (generatedSource.needRecompilation) {
val generated = parseAndGenerateCode(templateName, Path(source).byteArray, source.getAbsolutePath, resultType, formatterType, additionalImports)
View
3  framework/src/templates-compiler/src/test/scala/FakeRuntime.scala
@@ -1,3 +1,5 @@
+// TODO Get rid of this file which just demonstrates how harmful copy-pasting is.
+
package play.api.templates {
trait Template0[Result] { def render(): Result }
@@ -35,6 +37,7 @@ package play.templates {
}
trait Format[T <: Appendable[T]] {
+ type Appendable = T
def raw(text: String): T
def escape(text: String): T
}
View
2  framework/src/templates-compiler/src/test/scala/TemplateCompilerSpec.scala
@@ -127,7 +127,7 @@ object Helper {
def compile[T](templateName: String, className: String): T = {
val templateFile = new File(sourceDir, templateName)
- val Some(generated) = templateCompiler.compile(templateFile, sourceDir, generatedDir, "play.templates.test.Helper.Html", "play.templates.test.Helper.HtmlFormat")
+ val Some(generated) = templateCompiler.compile(templateFile, sourceDir, generatedDir, "play.templates.test.Helper.HtmlFormat")
val mapper = GeneratedSource(generated)
View
20 framework/src/templates/src/main/scala/play/api/templates/ScalaTemplate.scala
@@ -31,14 +31,32 @@ package play.templates {
import reflect.ClassTag
+/**
+ * A type that has a binary `+=` operation.
+ */
trait Appendable[T] {
def +=(other: T): T
- override def equals(x: Any): Boolean = super.equals(x)
+ override def equals(x: Any): Boolean = super.equals(x) // FIXME Why do we need these overrides?
override def hashCode() = super.hashCode()
}
+/**
+ * A template format defines how to properly integrate content for a type `T` (e.g. to prevent cross-site scripting attacks)
+ * @tparam T The underlying type that this format applies to.
+ */
trait Format[T <: Appendable[T]] {
+ type Appendable = T
+
+ /**
+ * Integrate `text` without performing any escaping process.
+ * @param text Text to integrate
+ */
def raw(text: String): T
+
+ /**
+ * Integrate `text` after escaping special characters. e.g. for HTML, “<” becomes “&amp;lt;”
+ * @param text Text to integrate
+ */
def escape(text: String): T
}
View
2  framework/test/csrftest-scala/project/build.properties
@@ -1 +1 @@
-sbt.version=0.12.2
+sbt.version=0.12.3
View
2  framework/test/integrationtest-java/project/build.properties
@@ -1 +1 @@
-sbt.version=0.12.2
+sbt.version=0.12.3
View
2  framework/test/integrationtest-scala/project/build.properties
@@ -1 +1 @@
-sbt.version=0.12.2
+sbt.version=0.12.3
View
2  framework/test/integrationtest/project/build.properties
@@ -1 +1 @@
-sbt.version=0.12.2
+sbt.version=0.12.3
View
14 play
@@ -1,16 +1,22 @@
#! /usr/bin/env sh
PRG="$0"
+# resolve relative/absolute symlinks
while [ -h "$PRG" ] ; do
- PRG=`readlink "$PRG"`
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG="`dirname "$PRG"`/$link"
+ fi
done
-
dir=`dirname $PRG`
if [ -z "$JAVA_HOME" ]; then
- JAVA="java"
+ JAVA="java"
else
- JAVA="$JAVA_HOME/bin/java"
+ JAVA="$JAVA_HOME/bin/java"
fi
if [ -f conf/application.conf -o -f conf/reference.conf ] || [ -d project ]; then
View
2  samples/java/comet-clock/project/build.properties
@@ -1 +1 @@
-sbt.version=0.12.2
+sbt.version=0.12.3
View
2  samples/java/computer-database-jpa/project/build.properties
@@ -1 +1 @@
-sbt.version=0.12.2
+sbt.version=0.12.3
View
2  samples/java/computer-database/project/build.properties
@@ -1 +1 @@
-sbt.version=0.12.2
+sbt.version=0.12.3
View
2  samples/java/forms/project/build.properties
@@ -1 +1 @@
-sbt.version=0.12.2
+sbt.version=0.12.3
View
2  samples/java/helloworld/project/build.properties
@@ -1 +1 @@
-sbt.version=0.12.2
+sbt.version=0.12.3
View
4 samples/java/websocket-chat/app/controllers/Application.java
@@ -29,6 +29,10 @@ public static Result chatRoom(String username) {
}
return ok(chatRoom.render(username));
}
+
+ public static Result chatRoomJs(String username) {
+ return ok(views.js.chatRoom.render(username));
+ }
/**
* Handle the chat websocket.
View
56 samples/java/websocket-chat/app/views/chatRoom.scala.html
@@ -25,60 +25,6 @@
</div>
</div>
- <script type="text/javascript" charset="utf-8">
-
- $(function() {
- var WS = window['MozWebSocket'] ? MozWebSocket : WebSocket
- var chatSocket = new WS("@routes.Application.chat(username).webSocketURL(request)")
-
- var sendMessage = function() {
- chatSocket.send(JSON.stringify(
- {text: $("#talk").val()}
- ))
- $("#talk").val('')
- }
-
- var receiveEvent = function(event) {
- var data = JSON.parse(event.data)
-
- // Handle errors
- if(data.error) {
- chatSocket.close()
- $("#onError span").text(data.error)
- $("#onError").show()
- return
- } else {
- $("#onChat").show()
- }
-
- // Create the message element
- var el = $('<div class="message"><span></span><p></p></div>')
- $("span", el).text(data.user)
- $("p", el).text(data.message)
- $(el).addClass(data.kind)
- if(data.user == '@username') $(el).addClass('me')
- $('#messages').append(el)
-
- // Update the members list
- $("#members").html('')
- $(data.members).each(function() {
- $("#members").append('<li>' + this + '</li>')
- })
- }
-
- var handleReturnKey = function(e) {
- if(e.charCode == 13 || e.keyCode == 13) {
- e.preventDefault()
- sendMessage()
- }
- }
-
- $("#talk").keypress(handleReturnKey)
-
- chatSocket.onmessage = receiveEvent
-
- })
-
- </script>
+ <script type="text/javascript" charset="utf-8" src="@routes.Application.chatRoomJs(username)"></script>
}
View
55 samples/java/websocket-chat/app/views/chatRoom.scala.js
@@ -0,0 +1,55 @@
+@(username: String)
+
+$(function() {
+ var WS = window['MozWebSocket'] ? MozWebSocket : WebSocket
+ var chatSocket = new WS("@routes.Application.chat(username).webSocketURL(request)")
+
+ var sendMessage = function() {
+ chatSocket.send(JSON.stringify(
+ {text: $("#talk").val()}
+ ))
+ $("#talk").val('')
+ }
+
+ var receiveEvent = function(event) {
+ var data = JSON.parse(event.data)
+
+ // Handle errors
+ if(data.error) {
+ chatSocket.close()
+ $("#onError span").text(data.error)
+ $("#onError").show()
+ return
+ } else {
+ $("#onChat").show()
+ }
+
+ // Create the message element
+ var el = $('<div class="message"><span></span><p></p></div>')
+ $("span", el).text(data.user)
+ $("p", el).text(data.message)
+ $(el).addClass(data.kind)
+ if(data.user == '@username') $(el).addClass('me')
+ $('#messages').append(el)
+
+ // Update the members list
+ $("#members").html('')
+ $(data.members).each(function() {
+ var li = document.createElement('li');
+ li.textContent = this;
+ $("#members").append(li);
+ })
+ }
+
+ var handleReturnKey = function(e) {
+ if(e.charCode == 13 || e.keyCode == 13) {
+ e.preventDefault()
+ sendMessage()
+ }
+ }
+
+ $("#talk").keypress(handleReturnKey)
+
+ chatSocket.onmessage = receiveEvent
+
+})
View
9 samples/java/websocket-chat/conf/routes
@@ -3,9 +3,10 @@
# ~~~~
# Home page
-GET / controllers.Application.index()
-GET /room controllers.Application.chatRoom(username: String ?= null)
-GET /room/chat controllers.Application.chat(username)
+GET / controllers.Application.index()
+GET /room controllers.Application.chatRoom(username: String ?= null)
+GET /room/chat controllers.Application.chat(username)
+GET /assets/javascripts/chatroom.js controllers.Application.chatRoomJs(username)
# Map static resources from the /public folder to the /assets URL path
-GET /assets/*file controllers.Assets.at(path="/public", file)
+GET /assets/*file controllers.Assets.at(path="/public", file)
View
2  samples/java/websocket-chat/project/build.properties
@@ -1 +1 @@
-sbt.version=0.12.2
+sbt.version=0.12.3
View
32 samples/java/websocket-chat/test/FunctionalTest.java
@@ -0,0 +1,32 @@
+import org.junit.Test;
+
+import play.mvc.Result;
+
+import static org.fest.assertions.Assertions.assertThat;
+import static play.test.Helpers.*;
+
+
+public class FunctionalTest {
+ @Test
+ public void sendJavaScript() {
+ running(fakeApplication(), new Runnable() {
+ @Override
+ public void run() {
+ Result result = callAction(controllers.routes.ref.Application.chatRoomJs("'"));
+ assertThat(status(result)).isEqualTo(OK);
+ assertThat(contentType(result)).isEqualTo("text/javascript");
+ }
+ });
+ }
+
+ @Test
+ public void resistToXSS() {
+ running(fakeApplication(), new Runnable() {
+ @Override
+ public void run() {
+ Result result = callAction(controllers.routes.ref.Application.chatRoomJs("'"));
+ assertThat(contentAsString(result)).contains("if(data.user == '\\'')");
+ }
+ });
+ }
+}
View
2  samples/java/zentasks/project/build.properties
@@ -1 +1 @@
-sbt.version=0.12.2
+sbt.version=0.12.3
View
2  samples/scala/comet-clock/project/build.properties
@@ -1 +1 @@
-sbt.version=0.12.2
+sbt.version=0.12.3
View
2  samples/scala/comet-live-monitoring/project/build.properties
@@ -1 +1 @@
-sbt.version=0.12.2
+sbt.version=0.12.3
View
2  samples/scala/computer-database/project/build.properties
@@ -1 +1 @@
-sbt.version=0.12.2
+sbt.version=0.12.3
View
2  samples/scala/forms/project/build.properties
@@ -1 +1 @@
-sbt.version=0.12.2
+sbt.version=0.12.3
View
2  samples/scala/helloworld/project/build.properties
@@ -1 +1 @@
-sbt.version=0.12.2
+sbt.version=0.12.3
View
4 samples/scala/websocket-chat/app/controllers/Application.scala
@@ -32,6 +32,10 @@ object Application extends Controller {
)
}
}
+
+ def chatRoomJs(username: String) = Action { implicit request =>
+ Ok(views.js.chatRoom(username))
+ }
/**
* Handles the chat websocket.
View
57 samples/scala/websocket-chat/app/views/chatRoom.scala.html
@@ -25,61 +25,6 @@
</div>
</div>
- <script type="text/javascript" charset="utf-8">
-
- $(function() {
-
- var WS = window['MozWebSocket'] ? MozWebSocket : WebSocket
- var chatSocket = new WS("@routes.Application.chat(username).webSocketURL()")
-
- var sendMessage = function() {
- chatSocket.send(JSON.stringify(
- {text: $("#talk").val()}
- ))
- $("#talk").val('')
- }
-
- var receiveEvent = function(event) {
- var data = JSON.parse(event.data)
-
- // Handle errors
- if(data.error) {
- chatSocket.close()
- $("#onError span").text(data.error)
- $("#onError").show()
- return
- } else {
- $("#onChat").show()
- }
-
- // Create the message element
- var el = $('<div class="message"><span></span><p></p></div>')
- $("span", el).text(data.user)
- $("p", el).text(data.message)
- $(el).addClass(data.kind)
- if(data.user == '@username') $(el).addClass('me')
- $('#messages').append(el)
-
- // Update the members list
- $("#members").html('')
- $(data.members).each(function() {
- $("#members").append('<li>' + this + '</li>')
- })
- }
-
- var handleReturnKey = function(e) {
- if(e.charCode == 13 || e.keyCode == 13) {
- e.preventDefault()
- sendMessage()
- }
- }
-
- $("#talk").keypress(handleReturnKey)
-
- chatSocket.onmessage = receiveEvent
-
- })
-
- </script>
+ <script type="text/javascript" charset="utf-8" src="@routes.Application.chatRoomJs(username)"></script>
}
View
56 samples/scala/websocket-chat/app/views/chatRoom.scala.js
@@ -0,0 +1,56 @@
+@(username: String)(implicit r: RequestHeader)
+
+$(function() {
+
+ var WS = window['MozWebSocket'] ? MozWebSocket : WebSocket
+ var chatSocket = new WS("@routes.Application.chat(username).webSocketURL()")
+
+ var sendMessage = function() {
+ chatSocket.send(JSON.stringify(
+ {text: $("#talk").val()}
+ ))
+ $("#talk").val('')
+ }
+
+ var receiveEvent = function(event) {
+ var data = JSON.parse(event.data)
+
+ // Handle errors
+ if(data.error) {
+ chatSocket.close()
+ $("#onError span").text(data.error)
+ $("#onError").show()
+ return
+ } else {
+ $("#onChat").show()
+ }
+
+ // Create the message element
+ var el = $('<div class="message"><span></span><p></p></div>')
+ $("span", el).text(data.user)
+ $("p", el).text(data.message)
+ $(el).addClass(data.kind)
+ if(data.user == '@username') $(el).addClass('me')
+ $('#messages').append(el)
+
+ // Update the members list
+ $("#members").html('')
+ $(data.members).each(function() {
+ var li = document.createElement('li');
+ li.textContent = this;
+ $("#members").append(li);
+ })
+ }
+
+ var handleReturnKey = function(e) {
+ if(e.charCode == 13 || e.keyCode == 13) {
+ e.preventDefault()
+ sendMessage()
+ }
+ }
+
+ $("#talk").keypress(handleReturnKey)
+
+ chatSocket.onmessage = receiveEvent
+
+})
View
2  samples/scala/websocket-chat/app/views/main.scala.html
@@ -23,7 +23,7 @@
<a href="@routes.Application.index()">Disconnect</a>
</p>
}.getOrElse {
- <form action="@routes.Application.chatRoom()" class="pull-right">
+ <form action="@routes.Application.chatRoom(None)" class="pull-right">
<input id="username" name="username" class="input-small" type="text" placeholder="Username">
<button class="btn" type="submit">Sign in</button>
</form>
View
9 samples/scala/websocket-chat/conf/routes
@@ -3,9 +3,10 @@
# ~~~~
# Home page
-GET / controllers.Application.index
-GET /room controllers.Application.chatRoom(username: Option[String] ?= None)
-GET /room/chat controllers.Application.chat(username)
+GET / controllers.Application.index
+GET /room controllers.Application.chatRoom(username: Option[String])
+GET /room/chat controllers.Application.chat(username)
+GET /assets/javascripts/chatroom.js controllers.Application.chatRoomJs(username: String)
# Map static resources from the /public folder to the /assets URL path
-GET /assets/*file controllers.Assets.at(path="/public", file)
+GET /assets/*file controllers.Assets.at(path="/public", file)
View
2  samples/scala/websocket-chat/project/build.properties
@@ -1 +1 @@
-sbt.version=0.12.2
+sbt.version=0.12.3
View
26 samples/scala/websocket-chat/test/ApplicationSpec.scala
@@ -0,0 +1,26 @@
+package test
+
+import org.specs2.mutable.Specification
+
+import play.api.test._
+import play.api.test.Helpers._
+
+class ApplicationSpec extends Specification {
+
+ "Application" should {
+ "Send JavaScript content" in {
+ running(FakeApplication()) {
+ val js = route(FakeRequest(GET, "/assets/javascripts/chatroom.js?username=julien")).get
+ status(js) must equalTo (OK)
+ contentType(js) must beSome.which(_ == "text/javascript")
+ }
+ }
+ "Resist to XSS attacks" in {
+ running(FakeApplication()) {
+ val js = route(FakeRequest(GET, "/assets/javascripts/chatroom.js?username='")).get
+ contentAsString(js).contains("""if(data.user == '\'')""") must beTrue
+ }
+ }
+ }
+
+}
View
2  samples/scala/zentasks/project/build.properties
@@ -1 +1 @@
-sbt.version=0.12.2
+sbt.version=0.12.3
View
2  samples/workinprogress/akka-chat/project/build.properties
@@ -1 +1 @@
-sbt.version=0.12.2
+sbt.version=0.12.3
View
2  samples/workinprogress/pi-calculator/project/build.properties
@@ -1 +1 @@
-sbt.version=0.12.2
+sbt.version=0.12.3
View
2  samples/workinprogress/twitterstream/project/build.properties
@@ -1 +1 @@
-sbt.version=0.12.2
+sbt.version=0.12.3
Please sign in to comment.
Something went wrong with that request. Please try again.