Browse files

Initial commit

  • Loading branch information...
0 parents commit 332c8b8006febdc61c8b969d6420be9ca854cb0d @brunogamacatao committed May 18, 2012
Showing with 3,304 additions and 0 deletions.
  1. BIN .DS_Store
  2. +14 −0 .classpath
  3. +2 −0 .gitignore
  4. +19 −0 .project
  5. +3 −0 .settings/org.codehaus.groovy.eclipse.preferences.prefs
  6. +73 −0 ThumbnailGrailsPlugin.groovy
  7. +5 −0 application.properties
  8. +37 −0 grails-app/conf/BuildConfig.groovy
  9. +24 −0 grails-app/conf/Config.groovy
  10. +43 −0 grails-app/conf/DataSource.groovy
  11. +13 −0 grails-app/conf/UrlMappings.groovy
  12. +15 −0 grails-app/controllers/bgc/thumbnail/ThumbnailController.groovy
  13. +23 −0 grails-app/domain/bgc/thumbnail/Thumbnail.groovy
  14. +71 −0 grails-app/services/bgc/thumbnail/ThumbnailService.groovy
  15. +23 −0 grails-app/taglib/bgc/thumbnail/ThumbnailTagLib.groovy
  16. +11 −0 grails-app/views/error.gsp
  17. +1 −0 grails-thumbnail-0.1.zip.sha1
  18. +1 −0 grails-thumbnail-1.1.zip.sha1
  19. +25 −0 plugin.xml
  20. +9 −0 scripts/_Install.groovy
  21. +5 −0 scripts/_Uninstall.groovy
  22. +10 −0 scripts/_Upgrade.groovy
  23. +13 −0 src/groovy/ThumbnailEditorRegistrar.groovy
  24. +38 −0 src/groovy/bgc/thumbnail/ThumbnailEditor.groovy
  25. BIN src/templates/.DS_Store
  26. BIN src/templates/scaffolding/.DS_Store
  27. +115 −0 src/templates/scaffolding/Controller.groovy
  28. +44 −0 src/templates/scaffolding/_form.gsp
  29. +45 −0 src/templates/scaffolding/create.gsp
  30. +49 −0 src/templates/scaffolding/edit.gsp
  31. +64 −0 src/templates/scaffolding/list.gsp
  32. +270 −0 src/templates/scaffolding/renderEditor.template
  33. +63 −0 src/templates/scaffolding/show.gsp
  34. BIN target/classes/BuildConfig$_run_closure1.class
  35. BIN target/classes/BuildConfig$_run_closure1_closure2.class
  36. BIN target/classes/BuildConfig$_run_closure1_closure3.class
  37. BIN target/classes/BuildConfig$_run_closure1_closure4.class
  38. BIN target/classes/BuildConfig$_run_closure1_closure5.class
  39. BIN target/classes/BuildConfig$_run_closure1_closure5_closure6.class
  40. BIN target/classes/BuildConfig.class
  41. BIN target/classes/Config$_run_closure1.class
  42. BIN target/classes/Config.class
  43. BIN target/classes/DataSource$_run_closure1.class
  44. BIN target/classes/DataSource$_run_closure2.class
  45. BIN target/classes/DataSource$_run_closure3.class
  46. BIN target/classes/DataSource$_run_closure3_closure4.class
  47. BIN target/classes/DataSource$_run_closure3_closure4_closure7.class
  48. BIN target/classes/DataSource$_run_closure3_closure5.class
  49. BIN target/classes/DataSource$_run_closure3_closure5_closure8.class
  50. BIN target/classes/DataSource$_run_closure3_closure6.class
  51. BIN target/classes/DataSource$_run_closure3_closure6_closure9.class
  52. BIN target/classes/DataSource$_run_closure3_closure6_closure9_closure10.class
  53. BIN target/classes/DataSource.class
  54. BIN target/classes/ThumbnailEditorRegistrar.class
  55. BIN target/classes/ThumbnailGrailsPlugin$_closure1.class
  56. BIN target/classes/ThumbnailGrailsPlugin$_closure2.class
  57. BIN target/classes/ThumbnailGrailsPlugin$_closure2_closure8.class
  58. BIN target/classes/ThumbnailGrailsPlugin$_closure3.class
  59. BIN target/classes/ThumbnailGrailsPlugin$_closure4.class
  60. BIN target/classes/ThumbnailGrailsPlugin$_closure5.class
  61. BIN target/classes/ThumbnailGrailsPlugin$_closure6.class
  62. BIN target/classes/ThumbnailGrailsPlugin$_closure7.class
  63. BIN target/classes/ThumbnailGrailsPlugin.class
  64. BIN target/classes/UrlMappings$__clinit__closure1.class
  65. BIN target/classes/UrlMappings$__clinit__closure1_closure2.class
  66. BIN target/classes/UrlMappings$__clinit__closure1_closure2_closure3.class
  67. BIN target/classes/UrlMappings.class
  68. +5 −0 target/classes/application.properties
  69. BIN target/classes/bgc/thumbnail/Thumbnail$__clinit__closure1.class
  70. BIN target/classes/bgc/thumbnail/Thumbnail$__clinit__closure2.class
  71. BIN target/classes/bgc/thumbnail/Thumbnail.class
  72. BIN target/classes/bgc/thumbnail/ThumbnailController$_closure1.class
  73. BIN target/classes/bgc/thumbnail/ThumbnailController.class
  74. BIN target/classes/bgc/thumbnail/ThumbnailEditor.class
  75. BIN target/classes/bgc/thumbnail/ThumbnailService$_getThumbnail_closure1.class
  76. BIN target/classes/bgc/thumbnail/ThumbnailService$_getThumbnail_closure1_closure5.class
  77. BIN target/classes/bgc/thumbnail/ThumbnailService$_getThumbnail_closure1_closure5_closure6.class
  78. BIN target/classes/bgc/thumbnail/ThumbnailService$_getThumbnail_closure1_closure5_closure7.class
  79. BIN target/classes/bgc/thumbnail/ThumbnailService$_getThumbnail_closure2.class
  80. BIN target/classes/bgc/thumbnail/ThumbnailService$_getThumbnail_closure3.class
  81. BIN target/classes/bgc/thumbnail/ThumbnailService$_saveThumbnail_closure4.class
  82. BIN target/classes/bgc/thumbnail/ThumbnailService.class
  83. BIN target/classes/bgc/thumbnail/ThumbnailTagLib$_closure1.class
  84. BIN target/classes/bgc/thumbnail/ThumbnailTagLib.class
  85. +1 −0 target/grails-thumbnail-0.1.zip.sha1
  86. +1 −0 target/grails-thumbnail-1.1.zip.sha1
  87. +17 −0 target/pom.xml
  88. +1 −0 target/pom.xml.sha1
  89. 0 target/stacktrace.log
  90. +33 −0 web-app/WEB-INF/applicationContext.xml
  91. +14 −0 web-app/WEB-INF/sitemesh.xml
  92. +572 −0 web-app/WEB-INF/tld/c.tld
  93. +671 −0 web-app/WEB-INF/tld/fmt.tld
  94. +550 −0 web-app/WEB-INF/tld/grails.tld
  95. +311 −0 web-app/WEB-INF/tld/spring.tld
BIN .DS_Store
Binary file not shown.
14 .classpath
@@ -0,0 +1,14 @@
+<classpath>
+ <classpathentry kind="src" path="src/java"/>
+ <classpathentry kind="src" path="src/groovy"/>
+ <classpathentry kind="src" path="grails-app/conf"/>
+ <classpathentry kind="src" path="grails-app/controllers"/>
+ <classpathentry kind="src" path="grails-app/domain"/>
+ <classpathentry kind="src" path="grails-app/services"/>
+ <classpathentry kind="src" path="grails-app/taglib"/>
+ <classpathentry kind="src" path="test/integration"/>
+ <classpathentry kind="src" path="test/unit"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+ <classpathentry kind="con" path="com.springsource.sts.grails.core.CLASSPATH_CONTAINER"/>
+ <classpathentry kind="output" path="web-app/WEB-INF/classes"/>
+</classpath>
2 .gitignore
@@ -0,0 +1,2 @@
+*.zip
+*.zip.sha
19 .project
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>thumbnail</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>com.springsource.sts.grails.core.nature</nature>
+ <nature>org.eclipse.jdt.groovy.core.groovyNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
3 .settings/org.codehaus.groovy.eclipse.preferences.prefs
@@ -0,0 +1,3 @@
+#Created by grails
+eclipse.preferences.version=1
+groovy.dont.generate.class.files=true
73 ThumbnailGrailsPlugin.groovy
@@ -0,0 +1,73 @@
+class ThumbnailGrailsPlugin {
+ // the plugin version
+ def version = "1.1"
+ // the version or versions of Grails the plugin is designed for
+ def grailsVersion = "2.0 > *"
+ // the other plugins this plugin depends on
+ def dependsOn = [:]
+ // resources that are excluded from plugin packaging
+ def pluginExcludes = [
+ "grails-app/views/error.gsp"
+ ]
+
+ // TODO Fill in these fields
+ def title = "Thumbnail Plugin" // Headline display name of the plugin
+ def author = "Bruno Gama Catao"
+ def authorEmail = ""
+ def description = '''\
+Simple plugin for handling image thumbnails.

Can you give a bit more information here? Does it simply generate thumbnails? Are there tags for displaying them? Does it perform auto-scaling, or cropping, or anything like that? It doesn't need to be a complete feature list, but it would be useful to have a bit more information.

Also note that you can use gdoc wiki syntax in this string, for example blank lines to separate paragraphs, bulleted lists, etc.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+'''
+
+ // URL to the plugin's documentation
+ def documentation = "http://grails.org/plugin/thumbnail"

Please update this to link to the README in the GitHub repository or any generated documentation you might have.

@brunogamacatao
Owner

Thanks, I'll do this now !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+
+ // Extra (optional) plugin metadata
+
+ // License: one of 'APACHE', 'GPL2', 'GPL3'
+// def license = "APACHE"

What license is the plugin?

@brunogamacatao
Owner

Dude, I really don't know anything about licenses.
I want it to be the most unobtrusive license available.
So, I'll read about them and choose the most free around.

Apache 2.0 is pretty liberal and the one that Grails (and many plugins) uses. If you want really liberal, then MIT and BSD fit that bill. Unfortunately the license field only supports APACHE, GPL2 and GPL3 at the moment. If you want to use one of the others, add a LICENSE.txt file in the root of the plugin source containing the relevant license.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+
+ // Details of company behind the plugin (if there is one)
+// def organization = [ name: "My Company", url: "http://www.my-company.com/" ]
+
+ // Any additional developers beyond the author specified above.
+// def developers = [ [ name: "Joe Bloggs", email: "joe@bloggs.net" ]]
+
+ // Location of the plugin's issue tracker.
+// def issueManagement = [ system: "JIRA", url: "http://jira.grails.org/browse/GPMYPLUGIN" ]

Where can users report issues with the plugin?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+
+ // Online location of the plugin's browseable source code.
+// def scm = [ url: "http://svn.grails-plugins.codehaus.org/browse/grails-plugins/" ]

Please update this URL to link to the source repository on GitHub.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+
+ def doWithWebDescriptor = { xml ->
+ // TODO Implement additions to web.xml (optional), this event occurs before
+ }
+
+ def doWithSpring = {
+ thumbnailEditorRegistrar(ThumbnailEditorRegistrar) {
+ thumbnailService = ref("thumbnailService")
+ }
+ }
+
+ def doWithDynamicMethods = { ctx ->
+ // TODO Implement registering dynamic methods to classes (optional)
+ }
+
+ def doWithApplicationContext = { applicationContext ->
+ // TODO Implement post initialization spring config (optional)
+ }
+
+ def onChange = { event ->
+ // TODO Implement code that is executed when any artefact that this plugin is
+ // watching is modified and reloaded. The event contains: event.source,
+ // event.application, event.manager, event.ctx, and event.plugin.
+ }
+
+ def onConfigChange = { event ->
+ // TODO Implement code that is executed when the project configuration changes.
+ // The event is the same as for 'onChange'.
+ }
+
+ def onShutdown = { event ->
+ // TODO Implement code that is executed when the application shuts down (optional)
+ }
+}

You can remove the hooks that you don't need. This will help keep the plugin descriptor tidy.

@brunogamacatao
Owner

Thanks, I'll remove them.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
5 application.properties
@@ -0,0 +1,5 @@
+#Grails Metadata file
+#Fri May 18 16:21:22 GMT-03:00 2012
+app.grails.version=2.0.3
+app.name=thumbnail
+plugins.svn=1.0.1
37 grails-app/conf/BuildConfig.groovy
@@ -0,0 +1,37 @@
+grails.project.class.dir = "target/classes"
+grails.project.test.class.dir = "target/test-classes"
+grails.project.test.reports.dir = "target/test-reports"
+grails.project.target.level = 1.6
+//grails.project.war.file = "target/${appName}-${appVersion}.war"
+
+grails.project.dependency.resolution = {
+ // inherit Grails' default dependencies
+ inherits("global") {
+ // uncomment to disable ehcache
+ // excludes 'ehcache'
+ }
+ log "warn" // log level of Ivy resolver, either 'error', 'warn', 'info', 'debug' or 'verbose'
+ repositories {
+ grailsCentral()
+ // uncomment the below to enable remote dependency resolution
+ // from public Maven repositories
+ //mavenCentral()
+ //mavenLocal()
+ //mavenRepo "http://snapshots.repository.codehaus.org"
+ //mavenRepo "http://repository.codehaus.org"
+ //mavenRepo "http://download.java.net/maven/2/"
+ //mavenRepo "http://repository.jboss.com/maven2/"
+ }
+ dependencies {
+ // specify dependencies here under either 'build', 'compile', 'runtime', 'test' or 'provided' scopes eg.
+
+ // runtime 'mysql:mysql-connector-java:5.1.5'
+ }
+
+ plugins {
+ build(":tomcat:$grailsVersion",
+ ":release:1.0.0") {
+ export = false
+ }
+ }
+}
24 grails-app/conf/Config.groovy
@@ -0,0 +1,24 @@
+// configuration for plugin testing - will not be included in the plugin zip
+
+log4j = {
+ // Example of changing the log pattern for the default console
+ // appender:
+ //
+ //appenders {
+ // console name:'stdout', layout:pattern(conversionPattern: '%c{2} %m%n')
+ //}
+
+ error 'org.codehaus.groovy.grails.web.servlet', // controllers
+ 'org.codehaus.groovy.grails.web.pages', // GSP
+ 'org.codehaus.groovy.grails.web.sitemesh', // layouts
+ 'org.codehaus.groovy.grails.web.mapping.filter', // URL mapping
+ 'org.codehaus.groovy.grails.web.mapping', // URL mapping
+ 'org.codehaus.groovy.grails.commons', // core / classloading
+ 'org.codehaus.groovy.grails.plugins', // plugins
+ 'org.codehaus.groovy.grails.orm.hibernate', // hibernate integration
+ 'org.springframework',
+ 'org.hibernate',
+ 'net.sf.ehcache.hibernate'
+
+ warn 'org.mortbay.log'
+}
43 grails-app/conf/DataSource.groovy
@@ -0,0 +1,43 @@
+dataSource {
+ pooled = true
+ driverClassName = "org.h2.Driver"
+ username = "sa"
+ password = ""
+}
+hibernate {
+ cache.use_second_level_cache = true
+ cache.use_query_cache = false
+ cache.region.factory_class = 'net.sf.ehcache.hibernate.EhCacheRegionFactory'
+}
+// environment specific settings
+environments {
+ development {
+ dataSource {
+ dbCreate = "create-drop" // one of 'create', 'create-drop', 'update', 'validate', ''
+ url = "jdbc:h2:mem:devDb;MVCC=TRUE"
+ }
+ }
+ test {
+ dataSource {
+ dbCreate = "update"
+ url = "jdbc:h2:mem:testDb;MVCC=TRUE"
+ }
+ }
+ production {
+ dataSource {
+ dbCreate = "update"
+ url = "jdbc:h2:prodDb;MVCC=TRUE"
+ pooled = true
+ properties {
+ maxActive = -1
+ minEvictableIdleTimeMillis=1800000
+ timeBetweenEvictionRunsMillis=1800000
+ numTestsPerEvictionRun=3
+ testOnBorrow=true
+ testWhileIdle=true
+ testOnReturn=true
+ validationQuery="SELECT 1"
+ }
+ }
+ }
+}
13 grails-app/conf/UrlMappings.groovy
@@ -0,0 +1,13 @@
+class UrlMappings {
+
+ static mappings = {
+ "/$controller/$action?/$id?"{
+ constraints {
+ // apply constraints here
+ }
+ }
+
+ "/"(view:"/index")
+ "500"(view:'/error')
+ }
+}
15 grails-app/controllers/bgc/thumbnail/ThumbnailController.groovy
@@ -0,0 +1,15 @@
+package bgc.thumbnail
+
+class ThumbnailController {
+ def burningImageService
+ def thumbnailService
+
+ def show = {
+ def thumbnail = Thumbnail.get(params.id)

It would be best to generate a 404 if the given thumbnail does not exist. Might also be worth logging.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ def width = params.width ? Integer.parseInt(params.width as String) : thumbnail.width
+ def height = params.height ? Integer.parseInt(params.height as String) : thumbnail.height
+
+ response.outputStream << thumbnailService.getThumbnail(thumbnail.id, width, height).image
+ response.outputStream.flush()
+ }
+}
23 grails-app/domain/bgc/thumbnail/Thumbnail.groovy
@@ -0,0 +1,23 @@
+package bgc.thumbnail
+
+class Thumbnail {
+ byte[] image
+ String filename
+ int width
+ int height
+ Thumbnail parentImage
+
+ static constraints = {
+ image(blank: false)
+ filename(blank: false)
+ width(blank: false)
+ height(blank: false)
+ parentImage(nullable: true)
+ }
+
+ static hasMany = [thumbnails: Thumbnail]
+ static mappedBy = [thumbnails: 'parentImage']
+ static mapping = {
+ image sqlType: 'longblob'
+ }
+}
71 grails-app/services/bgc/thumbnail/ThumbnailService.groovy
@@ -0,0 +1,71 @@
+package bgc.thumbnail
+
+class ThumbnailService {
+ static transactional = true
+
+ def burningImageService
+
+ def getThumbnail(def id, def width, def height) {
+ def result = Thumbnail.withCriteria {
+ or {
+ and {
+ eq('id', id)
+ if (width && width > 0) eq('width', width)
+ if (height && height > 0) eq('height', height)
+ }
+ and {
+ eq('parentImage.id', id)
+ if (width && width > 0) eq('width', width)
+ if (height && height > 0) eq('height', height)
+ }
+ }
+ }
+
+ if (result) {
+ return result[0]
+ } else {
+ def parentImage = Thumbnail.get(id)
+ def aspectRatio = (parentImage.width as float) / (parentImage.height as float)
+ def newHeight = (width / aspectRatio) as int
+
+ def tempDir = System.getProperty("java.io.tmpdir")
+ def newImg
+
+ def tempFile = File.createTempFile("img_", parentImage.filename)
+ tempFile.withOutputStream { s ->
+ s << parentImage.image
+ }
+
+ burningImageService.doWith(tempFile.absolutePath, tempDir).execute {
+ it.scaleAccurate(width, newHeight)
+ }
+
+ def newImage = new Thumbnail(
+ image: new File(tempDir, tempFile.getName()).bytes,
+ filename: parentImage.filename,
+ width: width,
+ height: height,
+ parentImage: parentImage).save()
+
+ return newImage
+ }
+ }
+
+ def saveThumbnail(imgFile) {
+ def thumbnail = new Thumbnail()
+ def tempDir = System.getProperty("java.io.tmpdir")
+
+ // Sets the image bytes
+ thumbnail.image = imgFile.bytes
+ // Sets the filename
+ thumbnail.filename = imgFile.getOriginalFilename()
+ // Gets the image dimensions (width and height)
+ burningImageService.doWith(imgFile, tempDir).execute {
+ def img = it.loadedImage.getAsJaiStream()
+ thumbnail.width = img.width
+ thumbnail.height = img.height
+ }
+
+ return thumbnail.save(flush: true, failOnError: true)
+ }
+}
23 grails-app/taglib/bgc/thumbnail/ThumbnailTagLib.groovy
@@ -0,0 +1,23 @@
+package bgc.thumbnail
+
+class ThumbnailTagLib {
+ def thumbnail = { attrs ->
+ def myParams = [:]
+ if (attrs.width) myParams['width'] = attrs.width
+ if (attrs.height) myParams['height'] = attrs.height

Is the id attribute required? If so, this should be documented and the tag should check for its existence. Check out this example from the Cache plugin. Also note the use of the throwTagError() method later in the file. You want something like:

if (!attrs.id) throwTagError("Tag [thumbnail] requires the [id] attribute")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+
+ out << "<img src='"
+ out << g.createLink(
+ action: "show",
+ controller: "thumbnail",
+ id: attrs.id,
+ params: myParams)
+ out << "'"
+
+ if (attrs['class'])
+ out << " class='${attrs['class']}'"
+
+ out << "/>"
+ }
+
+}
11 grails-app/views/error.gsp
@@ -0,0 +1,11 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Grails Runtime Exception</title>
+ <meta name="layout" content="main">
+ <link rel="stylesheet" href="${resource(dir: 'css', file: 'errors.css')}" type="text/css">
+ </head>
+ <body>
+ <g:renderException exception="${exception}" />
+ </body>
+</html>
1 grails-thumbnail-0.1.zip.sha1
@@ -0,0 +1 @@
+76f877715d585feac03741191402f7334178d187
1 grails-thumbnail-1.1.zip.sha1
@@ -0,0 +1 @@
+34f1c9907de39ad5ab9ac9fc0fd6568b3107fa08
25 plugin.xml
@@ -0,0 +1,25 @@
+<plugin name='thumbnail' version='1.1' grailsVersion='2.0 &gt; *'>
+ <author>Bruno Gama Catao</author>
+ <title>Thumbnail Plugin</title>
+ <description>Simple plugin for handling image thumbnails.
+</description>
+ <documentation>http://grails.org/plugin/thumbnail</documentation>
+ <type>ThumbnailGrailsPlugin</type>
+ <resources>
+ <resource>bgc.thumbnail.ThumbnailController</resource>
+ <resource>bgc.thumbnail.Thumbnail</resource>
+ <resource>bgc.thumbnail.ThumbnailService</resource>
+ <resource>bgc.thumbnail.ThumbnailTagLib</resource>
+ </resources>
+ <repositories>
+ <repository name='grailsCentral' url='http://plugins.grails.org' />
+ <repository name='http://repo.grails.org/grails/plugins' url='http://repo.grails.org/grails/plugins/' />
+ <repository name='http://repo.grails.org/grails/core' url='http://repo.grails.org/grails/core/' />
+ <repository name='grailsCore' url='http://svn.codehaus.org/grails/trunk/grails-plugins' />
+ <repository name='mavenCentral' url='http://repo1.maven.org/maven2/' />
+ </repositories>
+ <dependencies />
+ <plugins />
+ <runtimePluginRequirements />
+ <behavior />
+</plugin>
9 scripts/_Install.groovy
@@ -0,0 +1,9 @@
+ant.mkdir(dir:"${basedir}/src/templates")
+ant.mkdir(dir:"${basedir}/src/templates/scaffolding")
+ant.copy(file: "${pluginBasedir}/src/templates/scaffolding/_form.gsp", todir: "${basedir}/src/templates/scaffolding")
+ant.copy(file: "${pluginBasedir}/src/templates/scaffolding/Controller.groovy", todir: "${basedir}/src/templates/scaffolding")
+ant.copy(file: "${pluginBasedir}/src/templates/scaffolding/create.gsp", todir: "${basedir}/src/templates/scaffolding")
+ant.copy(file: "${pluginBasedir}/src/templates/scaffolding/edit.gsp", todir: "${basedir}/src/templates/scaffolding")
+ant.copy(file: "${pluginBasedir}/src/templates/scaffolding/list.gsp", todir: "${basedir}/src/templates/scaffolding")
+ant.copy(file: "${pluginBasedir}/src/templates/scaffolding/renderEditor.template", todir: "${basedir}/src/templates/scaffolding")
+ant.copy(file: "${pluginBasedir}/src/templates/scaffolding/show.gsp", todir: "${basedir}/src/templates/scaffolding")
5 scripts/_Uninstall.groovy
@@ -0,0 +1,5 @@
+//
+// This script is executed by Grails when the plugin is uninstalled from project.
+// Use this script if you intend to do any additional clean-up on uninstall, but
+// beware of messing up SVN directories!
+//
10 scripts/_Upgrade.groovy
@@ -0,0 +1,10 @@
+//
+// This script is executed by Grails during application upgrade ('grails upgrade'
+// command). This script is a Gant script so you can use all special variables
+// provided by Gant (such as 'baseDir' which points on project base dir). You can
+// use 'ant' to access a global instance of AntBuilder
+//
+// For example you can create directory under project tree:
+//
+// ant.mkdir(dir:"${basedir}/grails-app/jobs")
+//
13 src/groovy/ThumbnailEditorRegistrar.groovy
@@ -0,0 +1,13 @@
+import org.springframework.beans.PropertyEditorRegistrar
+import org.springframework.beans.PropertyEditorRegistry
+
+import bgc.thumbnail.Thumbnail
+import bgc.thumbnail.ThumbnailEditor
+
+public class ThumbnailEditorRegistrar implements PropertyEditorRegistrar {
+ def thumbnailService
+
+ public void registerCustomEditors(PropertyEditorRegistry registry) {
+ registry.registerCustomEditor(Thumbnail, new ThumbnailEditor(thumbnailService))
+ }
+}
38 src/groovy/bgc/thumbnail/ThumbnailEditor.groovy
@@ -0,0 +1,38 @@
+package bgc.thumbnail
+
+import java.io.IOException
+import org.springframework.beans.propertyeditors.ByteArrayPropertyEditor
+import org.springframework.web.multipart.MultipartFile
+
+import org.apache.commons.logging.LogFactory
+
+import org.codehaus.groovy.grails.plugins.web.taglib.ApplicationTagLib
+
+class ThumbnailEditor extends ByteArrayPropertyEditor {
+ static final log = LogFactory.getLog(this)
+ def thumbnailService
+ def myValue
+
+ public ThumbnailEditor(thumbnailService) {
+ this.thumbnailService = thumbnailService
+ }
+
+ public void setValue(Object value) {
+ if (value instanceof MultipartFile) {
+ MultipartFile multipartFile = (MultipartFile) value
+ try {
+ myValue = thumbnailService.saveThumbnail(multipartFile)
+ } catch (Exception ex) {
+ myValue = null
+ } catch (IOException ex) {
+ IllegalArgumentException iae = new IllegalArgumentException("Cannot read contents of multipart file")
+ iae.initCause(ex);
+ throw iae;
+ }
+ }
+ }
+
+ public Object getValue() {
+ return myValue
+ }
+}
BIN src/templates/.DS_Store
Binary file not shown.
BIN src/templates/scaffolding/.DS_Store
Binary file not shown.
115 src/templates/scaffolding/Controller.groovy
@@ -0,0 +1,115 @@
+<%=packageName ? "package ${packageName}\n\n" : ''%>import org.springframework.dao.DataIntegrityViolationException
+
+class ${className}Controller {
+
+ static allowedMethods = [save: "POST", update: "POST", delete: "POST"]
+
+ def index() {
+ redirect(action: "list", params: params)
+ }
+
+ def list() {
+ params.max = Math.min(params.max ? params.int('max') : 10, 100)
+ [${propertyName}List: ${className}.list(params), ${propertyName}Total: ${className}.count()]
+ }
+
+ def create() {
+ [${propertyName}: new ${className}(params)]
+ }
+
+ def save() {
+ def ${propertyName} = new ${className}(params)
+ if (!${propertyName}.save(flush: true)) {
+ render(view: "create", model: [${propertyName}: ${propertyName}])
+ return
+ }
+
+ flash.message = message(code: 'default.created.message', args: [message(code: '${domainClass.propertyName}.label', default: '${className}'), ${propertyName}.id])
+ redirect(action: "show", id: ${propertyName}.id)
+ }
+
+ def show() {
+ def ${propertyName} = ${className}.get(params.id)
+ if (!${propertyName}) {
+ flash.message = message(code: 'default.not.found.message', args: [message(code: '${domainClass.propertyName}.label', default: '${className}'), params.id])
+ redirect(action: "list")
+ return
+ }
+
+ [${propertyName}: ${propertyName}]
+ }
+
+ def edit() {
+ def ${propertyName} = ${className}.get(params.id)
+ if (!${propertyName}) {
+ flash.message = message(code: 'default.not.found.message', args: [message(code: '${domainClass.propertyName}.label', default: '${className}'), params.id])
+ redirect(action: "list")
+ return
+ }
+
+ [${propertyName}: ${propertyName}]
+ }
+
+ def update() {
+ def ${propertyName} = ${className}.get(params.id)
+ if (!${propertyName}) {
+ flash.message = message(code: 'default.not.found.message', args: [message(code: '${domainClass.propertyName}.label', default: '${className}'), params.id])
+ redirect(action: "list")
+ return
+ }
+
+ if (params.version) {
+ def version = params.version.toLong()
+ if (${propertyName}.version > version) {<% def lowerCaseName = grails.util.GrailsNameUtils.getPropertyName(className) %>
+ ${propertyName}.errors.rejectValue("version", "default.optimistic.locking.failure",
+ [message(code: '${domainClass.propertyName}.label', default: '${className}')] as Object[],
+ "Another user has updated this ${className} while you were editing")
+ render(view: "edit", model: [${propertyName}: ${propertyName}])
+ return
+ }
+ }
+
+ def thumbnailFields = []
+<%
+ def allowedNames = domainClass.persistentProperties*.name << 'dateCreated' << 'lastUpdated'
+ def props = domainClass.properties.findAll { allowedNames.contains(it.name) }
+ props.each {
+ if (it.type == bgc.thumbnail.Thumbnail) {
+%>
+ thumbnailFields << '${it.name}'
+<%
+ }
+ }
+%>
+ def toExclude = []
+
+ thumbnailFields.each { if (!request.getFile(it) || request.getFile(it).size <= 0) toExclude << it }
+ bindData(${propertyName}, params, [exclude: toExclude])
+
+ if (!${propertyName}.save(flush: true)) {
+ render(view: "edit", model: [${propertyName}: ${propertyName}])
+ return
+ }
+
+ flash.message = message(code: 'default.updated.message', args: [message(code: '${domainClass.propertyName}.label', default: '${className}'), ${propertyName}.id])
+ redirect(action: "show", id: ${propertyName}.id)
+ }
+
+ def delete() {
+ def ${propertyName} = ${className}.get(params.id)
+ if (!${propertyName}) {
+ flash.message = message(code: 'default.not.found.message', args: [message(code: '${domainClass.propertyName}.label', default: '${className}'), params.id])
+ redirect(action: "list")
+ return
+ }
+
+ try {
+ ${propertyName}.delete(flush: true)
+ flash.message = message(code: 'default.deleted.message', args: [message(code: '${domainClass.propertyName}.label', default: '${className}'), params.id])
+ redirect(action: "list")
+ } catch (DataIntegrityViolationException e) {
+ flash.message = message(code: 'default.not.deleted.message', args: [message(code: '${domainClass.propertyName}.label', default: '${className}'), params.id])
+ redirect(action: "show", id: params.id)
+ }
+ }
+}
44 src/templates/scaffolding/_form.gsp
@@ -0,0 +1,44 @@
+<%=packageName%>
+<% import grails.persistence.Event %>
+
+<% excludedProps = Event.allEvents.toList() << 'version' << 'dateCreated' << 'lastUpdated'
+ persistentPropNames = domainClass.persistentProperties*.name
+ boolean hasHibernate = pluginManager?.hasGrailsPlugin('hibernate')
+ if (hasHibernate && org.codehaus.groovy.grails.orm.hibernate.cfg.GrailsDomainBinder.getMapping(domainClass)?.identity?.generator == 'assigned') {
+ persistentPropNames << domainClass.identifier.name
+ }
+ props = domainClass.properties.findAll { persistentPropNames.contains(it.name) && !excludedProps.contains(it.name) }
+ Collections.sort(props, comparator.constructors[0].newInstance([domainClass] as Object[]))
+ for (p in props) {
+ if (p.embedded) {
+ def embeddedPropNames = p.component.persistentProperties*.name
+ def embeddedProps = p.component.properties.findAll { embeddedPropNames.contains(it.name) && !excludedProps.contains(it.name) }
+ Collections.sort(embeddedProps, comparator.constructors[0].newInstance([p.component] as Object[]))
+ %><fieldset class="embedded"><legend><g:message code="${domainClass.propertyName}.${p.name}.label" default="${p.naturalName}" /></legend><%
+ for (ep in p.component.properties) {
+ renderFieldForProperty(ep, p.component, "${p.name}.")
+ }
+ %></fieldset><%
+ } else {
+ renderFieldForProperty(p, domainClass)
+ }
+ }
+
+private renderFieldForProperty(p, owningClass, prefix = "") {
+ boolean hasHibernate = pluginManager?.hasGrailsPlugin('hibernate')
+ boolean display = true
+ boolean required = false
+ if (hasHibernate) {
+ cp = owningClass.constrainedProperties[p.name]
+ display = (cp ? cp.display : true)
+ required = (cp ? !(cp.propertyType in [boolean, Boolean]) && !cp.nullable && (cp.propertyType != String || !cp.blank) : false)
+ }
+ if (display) { %>
+<div class="fieldcontain \${hasErrors(bean: ${propertyName}, field: '${prefix}${p.name}', 'error')} ${required ? 'required' : ''}">
+ <label for="${prefix}${p.name}">
+ <g:message code="${domainClass.propertyName}.${prefix}${p.name}.label" default="${p.naturalName}" />
+ <% if (required) { %><span class="required-indicator">*</span><% } %>
+ </label>
+ ${renderEditor(p)}
+</div>
+<% } } %>
45 src/templates/scaffolding/create.gsp
@@ -0,0 +1,45 @@
+<%=packageName%>
+<%
+ // Changing the multiPart value depending if there's a thumbnail property
+ allowedNames = domainClass.persistentProperties*.name << 'dateCreated' << 'lastUpdated'
+ props = domainClass.properties.findAll { allowedNames.contains(it.name) }
+ props.each { if (it.type == bgc.thumbnail.Thumbnail) multiPart = true }
+%>
+<!doctype html>
+<html>
+ <head>
+ <meta name="layout" content="main">
+ <g:set var="entityName" value="\${message(code: '${domainClass.propertyName}.label', default: '${className}')}" />
+ <title><g:message code="default.create.label" args="[entityName]" /></title>
+ </head>
+ <body>
+ <a href="#create-${domainClass.propertyName}" class="skip" tabindex="-1"><g:message code="default.link.skip.label" default="Skip to content&hellip;"/></a>
+ <div class="nav" role="navigation">
+ <ul>
+ <li><a class="home" href="\${createLink(uri: '/')}"><g:message code="default.home.label"/></a></li>
+ <li><g:link class="list" action="list"><g:message code="default.list.label" args="[entityName]" /></g:link></li>
+ </ul>
+ </div>
+ <div id="create-${domainClass.propertyName}" class="content scaffold-create" role="main">
+ <h1><g:message code="default.create.label" args="[entityName]" /></h1>
+ <g:if test="\${flash.message}">
+ <div class="message" role="status">\${flash.message}</div>
+ </g:if>
+ <g:hasErrors bean="\${${propertyName}}">
+ <ul class="errors" role="alert">
+ <g:eachError bean="\${${propertyName}}" var="error">
+ <li <g:if test="\${error in org.springframework.validation.FieldError}">data-field-id="\${error.field}"</g:if>><g:message error="\${error}"/></li>
+ </g:eachError>
+ </ul>
+ </g:hasErrors>
+ <g:form action="save" <%= multiPart ? ' enctype="multipart/form-data"' : '' %>>
+ <fieldset class="form">
+ <g:render template="form"/>
+ </fieldset>
+ <fieldset class="buttons">
+ <g:submitButton name="create" class="save" value="\${message(code: 'default.button.create.label', default: 'Create')}" />
+ </fieldset>
+ </g:form>
+ </div>
+ </body>
+</html>
49 src/templates/scaffolding/edit.gsp
@@ -0,0 +1,49 @@
+<%=packageName%>
+<%
+ // Changing the multiPart value depending if there's a thumbnail property
+ allowedNames = domainClass.persistentProperties*.name << 'dateCreated' << 'lastUpdated'
+ props = domainClass.properties.findAll { allowedNames.contains(it.name) }
+ props.each { if (it.type == bgc.thumbnail.Thumbnail) multiPart = true }
+%>
+<!doctype html>
+<html>
+ <head>
+ <meta name="layout" content="main">
+ <g:set var="entityName" value="\${message(code: '${domainClass.propertyName}.label', default: '${className}')}" />
+ <title><g:message code="default.edit.label" args="[entityName]" /></title>
+ </head>
+ <body>
+ <a href="#edit-${domainClass.propertyName}" class="skip" tabindex="-1"><g:message code="default.link.skip.label" default="Skip to content&hellip;"/></a>
+ <div class="nav" role="navigation">
+ <ul>
+ <li><a class="home" href="\${createLink(uri: '/')}"><g:message code="default.home.label"/></a></li>
+ <li><g:link class="list" action="list"><g:message code="default.list.label" args="[entityName]" /></g:link></li>
+ <li><g:link class="create" action="create"><g:message code="default.new.label" args="[entityName]" /></g:link></li>
+ </ul>
+ </div>
+ <div id="edit-${domainClass.propertyName}" class="content scaffold-edit" role="main">
+ <h1><g:message code="default.edit.label" args="[entityName]" /></h1>
+ <g:if test="\${flash.message}">
+ <div class="message" role="status">\${flash.message}</div>
+ </g:if>
+ <g:hasErrors bean="\${${propertyName}}">
+ <ul class="errors" role="alert">
+ <g:eachError bean="\${${propertyName}}" var="error">
+ <li <g:if test="\${error in org.springframework.validation.FieldError}">data-field-id="\${error.field}"</g:if>><g:message error="\${error}"/></li>
+ </g:eachError>
+ </ul>
+ </g:hasErrors>
+ <g:form method="post" <%= multiPart ? ' enctype="multipart/form-data"' : '' %>>
+ <g:hiddenField name="id" value="\${${propertyName}?.id}" />
+ <g:hiddenField name="version" value="\${${propertyName}?.version}" />
+ <fieldset class="form">
+ <g:render template="form"/>
+ </fieldset>
+ <fieldset class="buttons">
+ <g:actionSubmit class="save" action="update" value="\${message(code: 'default.button.update.label', default: 'Update')}" />
+ <g:actionSubmit class="delete" action="delete" value="\${message(code: 'default.button.delete.label', default: 'Delete')}" formnovalidate="" onclick="return confirm('\${message(code: 'default.button.delete.confirm.message', default: 'Are you sure?')}');" />
+ </fieldset>
+ </g:form>
+ </div>
+ </body>
+</html>
64 src/templates/scaffolding/list.gsp
@@ -0,0 +1,64 @@
+<% import grails.persistence.Event %>
+<%=packageName%>
+<!doctype html>
+<html>
+ <head>
+ <meta name="layout" content="main">
+ <g:set var="entityName" value="\${message(code: '${domainClass.propertyName}.label', default: '${className}')}" />
+ <title><g:message code="default.list.label" args="[entityName]" /></title>
+ </head>
+ <body>
+ <a href="#list-${domainClass.propertyName}" class="skip" tabindex="-1"><g:message code="default.link.skip.label" default="Skip to content&hellip;"/></a>
+ <div class="nav" role="navigation">
+ <ul>
+ <li><a class="home" href="\${createLink(uri: '/')}"><g:message code="default.home.label"/></a></li>
+ <li><g:link class="create" action="create"><g:message code="default.new.label" args="[entityName]" /></g:link></li>
+ </ul>
+ </div>
+ <div id="list-${domainClass.propertyName}" class="content scaffold-list" role="main">
+ <h1><g:message code="default.list.label" args="[entityName]" /></h1>
+ <g:if test="\${flash.message}">
+ <div class="message" role="status">\${flash.message}</div>
+ </g:if>
+ <table>
+ <thead>
+ <tr>
+ <% excludedProps = Event.allEvents.toList() << 'id' << 'version'
+ allowedNames = domainClass.persistentProperties*.name << 'dateCreated' << 'lastUpdated'
+ props = domainClass.properties.findAll { allowedNames.contains(it.name) && !excludedProps.contains(it.name) && it.type != null && !Collection.isAssignableFrom(it.type) }
+ Collections.sort(props, comparator.constructors[0].newInstance([domainClass] as Object[]))
+ props.eachWithIndex { p, i ->
+ if (i < 6) {
+ if (p.isAssociation()) { %>
+ <th><g:message code="${domainClass.propertyName}.${p.name}.label" default="${p.naturalName}" /></th>
+ <% } else { %>
+ <g:sortableColumn property="${p.name}" title="\${message(code: '${domainClass.propertyName}.${p.name}.label', default: '${p.naturalName}')}" />
+ <% } } } %>
+ </tr>
+ </thead>
+ <tbody>
+ <g:each in="\${${propertyName}List}" status="i" var="${propertyName}">
+ <tr class="\${(i % 2) == 0 ? 'even' : 'odd'}">
+ <% props.eachWithIndex { p, i ->
+ if (i == 0) { %>
+ <td><g:link action="show" id="\${${propertyName}.id}">\${fieldValue(bean: ${propertyName}, field: "${p.name}")}</g:link></td>
+ <% } else if (i < 6) {
+ if (p.type == Boolean || p.type == boolean) { %>
+ <td><g:formatBoolean boolean="\${${propertyName}.${p.name}}" /></td>
+ <% } else if (p.type == Date || p.type == java.sql.Date || p.type == java.sql.Time || p.type == Calendar) { %>
+ <td><g:formatDate date="\${${propertyName}.${p.name}}" /></td>
+ <% } else if (p.type == bgc.thumbnail.Thumbnail) { %>
+ <td><g:thumbnail id="\${${propertyName}?.${p.name}?.id}" width="50" height="50"/></td>
+ <% } else { %>
+ <td>\${fieldValue(bean: ${propertyName}, field: "${p.name}")}</td>
+ <% } } } %>
+ </tr>
+ </g:each>
+ </tbody>
+ </table>
+ <div class="pagination">
+ <g:paginate total="\${${propertyName}Total}" />
+ </div>
+ </div>
+ </body>
+</html>
270 src/templates/scaffolding/renderEditor.template
@@ -0,0 +1,270 @@
+<% if (property.type == Boolean || property.type == boolean)
+ out << renderBooleanEditor(domainClass, property)
+ else if (property.type && Number.isAssignableFrom(property.type) || (property.type?.isPrimitive() && property.type != boolean))
+ out << renderNumberEditor(domainClass, property)
+ else if (property.type == String)
+ out << renderStringEditor(domainClass, property)
+ else if (property.type == Date || property.type == java.sql.Date || property.type == java.sql.Time || property.type == Calendar)
+ out << renderDateEditor(domainClass, property)
+ else if (property.type == URL)
+ out << renderStringEditor(domainClass, property)
+ else if (property.type && property.isEnum())
+ out << renderEnumEditor(domainClass, property)
+ else if (property.type == TimeZone)
+ out << renderSelectTypeEditor("timeZone", domainClass, property)
+ else if (property.type == Locale)
+ out << renderSelectTypeEditor("locale", domainClass, property)
+ else if (property.type == Currency)
+ out << renderSelectTypeEditor("currency", domainClass, property)
+ else if (property.type==([] as Byte[]).class) //TODO: Bug in groovy means i have to do this :(
+ out << renderByteArrayEditor(domainClass, property)
+ else if (property.type==([] as byte[]).class) //TODO: Bug in groovy means i have to do this :(
+ out << renderByteArrayEditor(domainClass, property)
+ else if (property.type == bgc.thumbnail.Thumbnail) {
+ out << "<g:if test=\"\${${domainInstance}.${property.name}}\">"
+ out << "<span class=\"property-value\" aria-labelledby=\"${property.name}-label\"><g:thumbnail id=\"\${${domainInstance}.${property.name}?.id}\"/></span>"
+ out << "</g:if>"
+ out << renderByteArrayEditor(domainClass, property)
+ } else if (property.manyToOne || property.oneToOne)
+ out << renderManyToOne(domainClass, property)
+ else if ((property.oneToMany && !property.bidirectional) || (property.manyToMany && property.isOwningSide())) {
+ def str = renderManyToMany(domainClass, property)
+ if(str != null) {
+ out << str
+ }
+ }
+ else if (property.oneToMany)
+ out << renderOneToMany(domainClass, property)
+
+ private renderEnumEditor(domainClass, property) {
+ def sb = new StringBuilder("")
+ sb << '<g:select name="' << property.name << '"'
+ sb << ' from="${' << "${property.type.name}?.values()" << '}"'
+ sb << ' keys="${' << property.type.name << '.values()*.name()}"'
+ if (isRequired()) sb << ' required=""'
+ sb << ' value="${' << "${domainInstance}?.${property.name}?.name()" << '}"'
+ sb << renderNoSelection(property)
+ sb << '/>'
+ sb as String
+ }
+
+ private renderStringEditor(domainClass, property) {
+ if (!cp) {
+ return "<g:textField name=\"${property.name}\" value=\"\${${domainInstance}?.${property.name}}\" />"
+ } else {
+ def sb = new StringBuilder("")
+ if ("textarea" == cp.widget || (cp.maxSize > 250 && !cp.password && !cp.inList)) {
+ sb << '<g:textArea name="' << property.name << '"'
+ sb << ' cols="40" rows="5"'
+ if (cp.maxSize) sb << ' maxlength="' << cp.maxSize << '"'
+ if (isRequired()) sb << ' required=""'
+ sb << ' value="${' << "${domainInstance}?.${property.name}" << '}"'
+ sb << '/>'
+ } else if (cp.inList) {
+ sb << '<g:select name="' << property.name << '"'
+ sb << ' from="${' << "${domainInstance}.constraints.${property.name}.inList" << '}"'
+ if (isRequired()) sb << ' required=""'
+ sb << ' value="${' << "${domainInstance}?.${property.name}" << '}"'
+ sb << ' valueMessagePrefix="' << "${domainClass.propertyName}.${property.name}" << '"'
+ sb << renderNoSelection(property)
+ sb << '/>'
+ } else {
+ if (cp.password) {
+ sb << '<g:field type="password"'
+ } else if (cp.url) {
+ sb << '<g:field type="url"'
+ } else if (cp.email) {
+ sb << '<g:field type="email"'
+ } else {
+ sb << '<g:textField'
+ }
+ sb << ' name="' << property.name << '"'
+ if (cp.maxSize) sb << ' maxlength="' << cp.maxSize << '"'
+ if (!cp.editable) sb << ' readonly="readonly"'
+ if (cp.matches) sb << ' pattern="${' << "${domainInstance}.constraints.${property.name}.matches" << '}"'
+ if (isRequired()) sb << ' required=""'
+ sb << ' value="${' << "${domainInstance}?.${property.name}" << '}"'
+ sb << '/>'
+ }
+ sb as String
+ }
+ }
+
+ private renderByteArrayEditor(domainClass, property) {
+ return "<input type=\"file\" id=\"${property.name}\" name=\"${property.name}\" />"
+ }
+
+ private renderManyToOne(domainClass,property) {
+ if (property.association) {
+ def sb = new StringBuilder()
+ sb << '<g:select'
+ // id is "x" and name is "x.id" as the label will have for="x" and "." in an id will confuse CSS
+ sb << ' id="' << property.name << '"'
+ sb << ' name="' << property.name << '.id"'
+ sb << ' from="${' << property.type.name << '.list()}"'
+ sb << ' optionKey="id"'
+ if (isRequired()) sb << ' required=""'
+ sb << ' value="${' << "${domainInstance}?.${property.name}" << '?.id}"'
+ sb << ' class="many-to-one"'
+ sb << renderNoSelection(property)
+ sb << '/>'
+ sb as String
+ }
+ }
+
+ private renderManyToMany(domainClass, property) {
+ def cls = property.referencedDomainClass?.clazz
+ if(cls == null) {
+ if(property.type instanceof Collection) {
+ cls = org.springframework.core.GenericCollectionTypeResolver.getCollectionType(property.type)
+ }
+ }
+
+ if(cls != null) {
+ def sb = new StringBuilder()
+ sb << '<g:select name="' << property.name << '"'
+ sb << ' from="${' << cls.name << '.list()}"'
+ sb << ' multiple="multiple"'
+ sb << ' optionKey="id"'
+ sb << ' size="5"'
+ if (isRequired()) sb << ' required=""'
+ sb << ' value="${' << "${domainInstance}?.${property.name}" << '*.id}"'
+ sb << ' class="many-to-many"'
+ sb << '/>'
+ sb as String
+
+ }
+
+ }
+
+ private renderOneToMany(domainClass, property) {
+ def sw = new StringWriter()
+ def pw = new PrintWriter(sw)
+ pw.println()
+ pw.println '<ul class="one-to-many">'
+ pw.println "<g:each in=\"\${${domainInstance}?.${property.name}?}\" var=\"${property.name[0]}\">"
+ pw.println " <li><g:link controller=\"${property.referencedDomainClass.propertyName}\" action=\"show\" id=\"\${${property.name[0]}.id}\">\${${property.name[0]}?.encodeAsHTML()}</g:link></li>"
+ pw.println '</g:each>'
+ pw.println '<li class="add">'
+ pw.println "<g:link controller=\"${property.referencedDomainClass.propertyName}\" action=\"create\" params=\"['${domainClass.propertyName}.id': ${domainInstance}?.id]\">\${message(code: 'default.add.label', args: [message(code: '${property.referencedDomainClass.propertyName}.label', default: '${property.referencedDomainClass.shortName}')])}</g:link>"
+ pw.println '</li>'
+ pw.println '</ul>'
+ return sw.toString()
+ }
+
+ private renderNumberEditor(domainClass, property) {
+ if (!cp) {
+ if (property.type == Byte) {
+ return "<g:select name=\"${property.name}\" from=\"\${-128..127}\" class=\"range\" value=\"\${fieldValue(bean: ${domainInstance}, field: '${property.name}')}\" />"
+ } else {
+ return "<g:field type=\"number\" name=\"${property.name}\" value=\"\${fieldValue(bean: ${domainInstance}, field: '${property.name}')}\" />"
+ }
+ } else {
+ def sb = new StringBuilder()
+ if (cp.range) {
+ sb << '<g:select name="' << property.name << '"'
+ sb << ' from="${' << "${cp.range.from}..${cp.range.to}" << '}"'
+ sb << ' class="range"'
+ if (isRequired()) sb << ' required=""'
+ sb << ' value="${' << "fieldValue(bean: ${domainInstance}, field: '${property.name}')" << '}"'
+ sb << renderNoSelection(property)
+ sb << '/>'
+ } else if (cp.inList) {
+ sb << '<g:select name="' << property.name << '"'
+ sb << ' from="${' << "${domainInstance}.constraints.${property.name}.inList" << '}"'
+ if (isRequired()) sb << ' required=""'
+ sb << ' value="${' << "fieldValue(bean: ${domainInstance}, field: '${property.name}')" << '}"'
+ sb << ' valueMessagePrefix="' << "${domainClass.propertyName}.${property.name}" << '"'
+ sb << renderNoSelection(property)
+ sb << '/>'
+ } else {
+ sb << '<g:field type="number" name="' << property.name << '"'
+ if (cp.scale != null) sb << ' step="' << BigDecimal.valueOf(1).movePointLeft(cp.scale).toString() << '"'
+ if (cp.min != null) sb << ' min="' << cp.min << '"'
+ if (cp.max != null) sb << ' max="' << cp.max << '"'
+ if (isRequired()) sb << ' required=""'
+ sb << ' value="${' << "fieldValue(bean: ${domainInstance}, field: '${property.name}')" << '}"'
+ sb << '/>'
+ }
+ sb as String
+ }
+ }
+
+ private renderBooleanEditor(domainClass, property) {
+ if (!cp) {
+ return "<g:checkBox name=\"${property.name}\" value=\"\${${domainInstance}?.${property.name}}\" />"
+ } else {
+ def sb = new StringBuilder("<g:checkBox name=\"${property.name}\" ")
+ if (cp.widget) sb << "widget=\"${cp.widget}\" ";
+ cp.attributes.each { k, v ->
+ sb << "${k}=\"${v}\" "
+ }
+ sb << "value=\"\${${domainInstance}?.${property.name}}\" />"
+ return sb.toString()
+ }
+ }
+
+ private renderDateEditor(domainClass, property) {
+ def precision = (property.type == Date || property.type == java.sql.Date || property.type == Calendar) ? "day" : "minute";
+ if (!cp) {
+ return "<g:datePicker name=\"${property.name}\" precision=\"${precision}\" value=\"\${${domainInstance}?.${property.name}}\" />"
+ } else {
+ if (!cp.editable) {
+ return "\${${domainInstance}?.${property.name}?.toString()}"
+ } else {
+ def sb = new StringBuilder("<g:datePicker name=\"${property.name}\" ")
+ if (cp.format) sb << "format=\"${cp.format}\" "
+ if (cp.widget) sb << "widget=\"${cp.widget}\" "
+ cp.attributes.each { k, v ->
+ sb << "${k}=\"${v}\" "
+ }
+ if (!cp.attributes.precision){
+ sb << "precision=\"${precision}\" "
+ }
+ sb << " value=\"\${${domainInstance}?.${property.name}}\" ${renderNoSelection(property)} />"
+
+ return sb.toString()
+ }
+ }
+ }
+
+ private renderSelectTypeEditor(type, domainClass,property) {
+ if (!cp) {
+ return "<g:${type}Select name=\"${property.name}\" value=\"\${${domainInstance}?.${property.name}}\" />"
+ } else {
+ def sb = new StringBuilder("<g:${type}Select name=\"${property.name}\" ")
+ if (cp.widget) sb << "widget=\"${cp.widget}\" ";
+ cp.attributes.each { k, v ->
+ sb << "${k}=\"${v}\" "
+ }
+ sb << "value=\"\${${domainInstance}?.${property.name}}\" ${renderNoSelection(property)} />"
+ return sb.toString()
+ }
+ }
+
+ private renderNoSelection(property) {
+ if (isOptional()) {
+ if (property.manyToOne || property.oneToOne) {
+ return " noSelection=\"['null': '']\""
+ } else if (property.type == Date || property.type == java.sql.Date || property.type == java.sql.Time || property.type == Calendar) {
+ return "default=\"none\" noSelection=\"['': '']\""
+ } else {
+ return " noSelection=\"['': '']\""
+ }
+ }
+ return ""
+ }
+
+ private boolean isRequired() {
+ !isOptional()
+ }
+
+ private boolean isOptional() {
+ if(!cp) {
+ return false
+ }
+ else {
+ cp.nullable || (cp.propertyType == String && cp.blank) || cp.propertyType in [boolean, Boolean]
+ }
+ }
+%>
63 src/templates/scaffolding/show.gsp
@@ -0,0 +1,63 @@
+<% import grails.persistence.Event %>
+<%=packageName%>
+<!doctype html>
+<html>
+ <head>
+ <meta name="layout" content="main">
+ <g:set var="entityName" value="\${message(code: '${domainClass.propertyName}.label', default: '${className}')}" />
+ <title><g:message code="default.show.label" args="[entityName]" /></title>
+ </head>
+ <body>
+ <a href="#show-${domainClass.propertyName}" class="skip" tabindex="-1"><g:message code="default.link.skip.label" default="Skip to content&hellip;"/></a>
+ <div class="nav" role="navigation">
+ <ul>
+ <li><a class="home" href="\${createLink(uri: '/')}"><g:message code="default.home.label"/></a></li>
+ <li><g:link class="list" action="list"><g:message code="default.list.label" args="[entityName]" /></g:link></li>
+ <li><g:link class="create" action="create"><g:message code="default.new.label" args="[entityName]" /></g:link></li>
+ </ul>
+ </div>
+ <div id="show-${domainClass.propertyName}" class="content scaffold-show" role="main">
+ <h1><g:message code="default.show.label" args="[entityName]" /></h1>
+ <g:if test="\${flash.message}">
+ <div class="message" role="status">\${flash.message}</div>
+ </g:if>
+ <ol class="property-list ${domainClass.propertyName}">
+ <% excludedProps = Event.allEvents.toList() << 'id' << 'version'
+ allowedNames = domainClass.persistentProperties*.name << 'dateCreated' << 'lastUpdated'
+ props = domainClass.properties.findAll { allowedNames.contains(it.name) && !excludedProps.contains(it.name) }
+ Collections.sort(props, comparator.constructors[0].newInstance([domainClass] as Object[]))
+ props.each { p -> %>
+ <g:if test="\${${propertyName}?.${p.name}}">
+ <li class="fieldcontain">
+ <span id="${p.name}-label" class="property-label"><g:message code="${domainClass.propertyName}.${p.name}.label" default="${p.naturalName}" /></span>
+ <% if (p.isEnum()) { %>
+ <span class="property-value" aria-labelledby="${p.name}-label"><g:fieldValue bean="\${${propertyName}}" field="${p.name}"/></span>
+ <% } else if (p.oneToMany || p.manyToMany) { %>
+ <g:each in="\${${propertyName}.${p.name}}" var="${p.name[0]}">
+ <span class="property-value" aria-labelledby="${p.name}-label"><g:link controller="${p.referencedDomainClass?.propertyName}" action="show" id="\${${p.name[0]}.id}">\${${p.name[0]}?.encodeAsHTML()}</g:link></span>
+ </g:each>
+ <% } else if (p.type == bgc.thumbnail.Thumbnail) { %>
+ <span class="property-value" aria-labelledby="${p.name}-label"><g:thumbnail id="\${${propertyName}?.${p.name}?.id}"/></span>
+ <% } else if (p.manyToOne || p.oneToOne) { %>
+ <span class="property-value" aria-labelledby="${p.name}-label"><g:link controller="${p.referencedDomainClass?.propertyName}" action="show" id="\${${propertyName}?.${p.name}?.id}">\${${propertyName}?.${p.name}?.encodeAsHTML()}</g:link></span>
+ <% } else if (p.type == Boolean || p.type == boolean) { %>
+ <span class="property-value" aria-labelledby="${p.name}-label"><g:formatBoolean boolean="\${${propertyName}?.${p.name}}" /></span>
+ <% } else if (p.type == Date || p.type == java.sql.Date || p.type == java.sql.Time || p.type == Calendar) { %>
+ <span class="property-value" aria-labelledby="${p.name}-label"><g:formatDate date="\${${propertyName}?.${p.name}}" /></span>
+ <% } else if(!p.type.isArray()) { %>
+ <span class="property-value" aria-labelledby="${p.name}-label"><g:fieldValue bean="\${${propertyName}}" field="${p.name}"/></span>
+ <% } %>
+ </li>
+ </g:if>
+ <% } %>
+ </ol>
+ <g:form>
+ <fieldset class="buttons">
+ <g:hiddenField name="id" value="\${${propertyName}?.id}" />
+ <g:link class="edit" action="edit" id="\${${propertyName}?.id}"><g:message code="default.button.edit.label" default="Edit" /></g:link>
+ <g:actionSubmit class="delete" action="delete" value="\${message(code: 'default.button.delete.label', default: 'Delete')}" onclick="return confirm('\${message(code: 'default.button.delete.confirm.message', default: 'Are you sure?')}');" />
+ </fieldset>
+ </g:form>
+ </div>
+ </body>
+</html>
BIN target/classes/BuildConfig$_run_closure1.class
Binary file not shown.
BIN target/classes/BuildConfig$_run_closure1_closure2.class
Binary file not shown.
BIN target/classes/BuildConfig$_run_closure1_closure3.class
Binary file not shown.
BIN target/classes/BuildConfig$_run_closure1_closure4.class
Binary file not shown.
BIN target/classes/BuildConfig$_run_closure1_closure5.class
Binary file not shown.
BIN target/classes/BuildConfig$_run_closure1_closure5_closure6.class
Binary file not shown.
BIN target/classes/BuildConfig.class
Binary file not shown.
BIN target/classes/Config$_run_closure1.class
Binary file not shown.
BIN target/classes/Config.class
Binary file not shown.
BIN target/classes/DataSource$_run_closure1.class
Binary file not shown.
BIN target/classes/DataSource$_run_closure2.class
Binary file not shown.
BIN target/classes/DataSource$_run_closure3.class
Binary file not shown.
BIN target/classes/DataSource$_run_closure3_closure4.class
Binary file not shown.
BIN target/classes/DataSource$_run_closure3_closure4_closure7.class
Binary file not shown.
BIN target/classes/DataSource$_run_closure3_closure5.class
Binary file not shown.
BIN target/classes/DataSource$_run_closure3_closure5_closure8.class
Binary file not shown.
BIN target/classes/DataSource$_run_closure3_closure6.class
Binary file not shown.
BIN target/classes/DataSource$_run_closure3_closure6_closure9.class
Binary file not shown.
BIN target/classes/DataSource$_run_closure3_closure6_closure9_closure10.class
Binary file not shown.
BIN target/classes/DataSource.class
Binary file not shown.
BIN target/classes/ThumbnailEditorRegistrar.class
Binary file not shown.
BIN target/classes/ThumbnailGrailsPlugin$_closure1.class
Binary file not shown.
BIN target/classes/ThumbnailGrailsPlugin$_closure2.class
Binary file not shown.
BIN target/classes/ThumbnailGrailsPlugin$_closure2_closure8.class
Binary file not shown.
BIN target/classes/ThumbnailGrailsPlugin$_closure3.class
Binary file not shown.
BIN target/classes/ThumbnailGrailsPlugin$_closure4.class
Binary file not shown.
BIN target/classes/ThumbnailGrailsPlugin$_closure5.class
Binary file not shown.
BIN target/classes/ThumbnailGrailsPlugin$_closure6.class
Binary file not shown.
BIN target/classes/ThumbnailGrailsPlugin$_closure7.class
Binary file not shown.
BIN target/classes/ThumbnailGrailsPlugin.class
Binary file not shown.
BIN target/classes/UrlMappings$__clinit__closure1.class
Binary file not shown.
BIN target/classes/UrlMappings$__clinit__closure1_closure2.class
Binary file not shown.
BIN target/classes/UrlMappings$__clinit__closure1_closure2_closure3.class
Binary file not shown.
BIN target/classes/UrlMappings.class
Binary file not shown.
5 target/classes/application.properties
@@ -0,0 +1,5 @@
+#Grails Metadata file
+#Fri May 18 16:21:22 GMT-03:00 2012
+app.grails.version=2.0.3
+app.name=thumbnail
+plugins.svn=1.0.1
BIN target/classes/bgc/thumbnail/Thumbnail$__clinit__closure1.class
Binary file not shown.
BIN target/classes/bgc/thumbnail/Thumbnail$__clinit__closure2.class
Binary file not shown.
BIN target/classes/bgc/thumbnail/Thumbnail.class
Binary file not shown.
BIN target/classes/bgc/thumbnail/ThumbnailController$_closure1.class
Binary file not shown.
BIN target/classes/bgc/thumbnail/ThumbnailController.class
Binary file not shown.
BIN target/classes/bgc/thumbnail/ThumbnailEditor.class
Binary file not shown.
BIN target/classes/bgc/thumbnail/ThumbnailService$_getThumbnail_closure1.class
Binary file not shown.
BIN target/classes/bgc/thumbnail/ThumbnailService$_getThumbnail_closure1_closure5.class
Binary file not shown.
BIN target/classes/bgc/thumbnail/ThumbnailService$_getThumbnail_closure1_closure5_closure6.class
Binary file not shown.
BIN target/classes/bgc/thumbnail/ThumbnailService$_getThumbnail_closure1_closure5_closure7.class
Binary file not shown.
BIN target/classes/bgc/thumbnail/ThumbnailService$_getThumbnail_closure2.class
Binary file not shown.
BIN target/classes/bgc/thumbnail/ThumbnailService$_getThumbnail_closure3.class
Binary file not shown.
BIN target/classes/bgc/thumbnail/ThumbnailService$_saveThumbnail_closure4.class
Binary file not shown.
BIN target/classes/bgc/thumbnail/ThumbnailService.class
Binary file not shown.
BIN target/classes/bgc/thumbnail/ThumbnailTagLib$_closure1.class
Binary file not shown.
BIN target/classes/bgc/thumbnail/ThumbnailTagLib.class
Binary file not shown.
1 target/grails-thumbnail-0.1.zip.sha1
@@ -0,0 +1 @@
+76f877715d585feac03741191402f7334178d187
1 target/grails-thumbnail-1.1.zip.sha1
@@ -0,0 +1 @@
+34f1c9907de39ad5ab9ac9fc0fd6568b3107fa08
17 target/pom.xml
@@ -0,0 +1,17 @@
+<project xmlns='http://maven.apache.org/POM/4.0.0' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xsi:schemaLocation='http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd'>
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>org.grails.plugins</groupId>
+ <artifactId>thumbnail</artifactId>
+ <packaging>zip</packaging>
+ <version>1.1</version>
+ <name>Thumbnail Plugin</name>
+ <description>Simple plugin for handling image thumbnails.
+</description>
+ <url>http://grails.org/plugin/thumbnail</url>
+ <developers>
+ <developer>
+ <name>Bruno Gama Catao</name>
+ </developer>
+ </developers>
+ <dependencies />
+</project>
1 target/pom.xml.sha1
@@ -0,0 +1 @@
+e62cce08bfde36b30053e28de488150a5b354ee7
0 target/stacktrace.log
No changes.
33 web-app/WEB-INF/applicationContext.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="
+http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
+
+ <bean id="grailsApplication" class="org.codehaus.groovy.grails.commons.GrailsApplicationFactoryBean">
+ <description>Grails application factory bean</description>
+ <property name="grailsDescriptor" value="/WEB-INF/grails.xml" />
+ <property name="grailsResourceLoader" ref="grailsResourceLoader" />
+ </bean>
+
+ <bean id="pluginManager" class="org.codehaus.groovy.grails.plugins.GrailsPluginManagerFactoryBean">
+ <description>A bean that manages Grails plugins</description>
+ <property name="grailsDescriptor" value="/WEB-INF/grails.xml" />
+ <property name="application" ref="grailsApplication" />
+ </bean>
+
+ <bean id="grailsConfigurator" class="org.codehaus.groovy.grails.commons.spring.GrailsRuntimeConfigurator">
+ <constructor-arg>
+ <ref bean="grailsApplication" />
+ </constructor-arg>
+ <property name="pluginManager" ref="pluginManager" />
+ </bean>
+
+ <bean id="grailsResourceLoader" class="org.codehaus.groovy.grails.commons.GrailsResourceLoaderFactoryBean" />
+
+ <bean id="characterEncodingFilter" class="org.springframework.web.filter.CharacterEncodingFilter">
+ <property name="encoding">
+ <value>utf-8</value>
+ </property>
+ </bean>
+</beans>
14 web-app/WEB-INF/sitemesh.xml
@@ -0,0 +1,14 @@
+<sitemesh>
+ <page-parsers>
+ <parser content-type="text/html"
+ class="org.codehaus.groovy.grails.web.sitemesh.GrailsHTMLPageParser" />
+ <parser content-type="text/html;charset=ISO-8859-1"
+ class="org.codehaus.groovy.grails.web.sitemesh.GrailsHTMLPageParser" />
+ <parser content-type="text/html;charset=UTF-8"
+ class="org.codehaus.groovy.grails.web.sitemesh.GrailsHTMLPageParser" />
+ </page-parsers>
+
+ <decorator-mappers>
+ <mapper class="org.codehaus.groovy.grails.web.sitemesh.GrailsLayoutDecoratorMapper" />
+ </decorator-mappers>
+</sitemesh>
572 web-app/WEB-INF/tld/c.tld
@@ -0,0 +1,572 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<taglib xmlns="http://java.sun.com/xml/ns/javaee"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd"
+ version="2.1">
+
+ <description>JSTL 1.2 core library</description>
+ <display-name>JSTL core</display-name>
+ <tlib-version>1.2</tlib-version>
+ <short-name>c</short-name>
+ <uri>http://java.sun.com/jsp/jstl/core</uri>
+
+ <validator>
+ <description>
+ Provides core validation features for JSTL tags.
+ </description>
+ <validator-class>
+ org.apache.taglibs.standard.tlv.JstlCoreTLV
+ </validator-class>
+ </validator>
+
+ <tag>
+ <description>
+ Catches any Throwable that occurs in its body and optionally
+ exposes it.
+ </description>
+ <name>catch</name>
+ <tag-class>org.apache.taglibs.standard.tag.common.core.CatchTag</tag-class>
+ <body-content>JSP</body-content>
+ <attribute>
+ <description>
+Name of the exported scoped variable for the
+exception thrown from a nested action. The type of the
+scoped variable is the type of the exception thrown.
+ </description>
+ <name>var</name>
+ <required>false</required>
+ <rtexprvalue>false</rtexprvalue>
+ </attribute>
+ </tag>
+
+ <tag>
+ <description>
+ Simple conditional tag that establishes a context for
+ mutually exclusive conditional operations, marked by
+ &lt;when&gt; and &lt;otherwise&gt;
+ </description>
+ <name>choose</name>
+ <tag-class>org.apache.taglibs.standard.tag.common.core.ChooseTag</tag-class>
+ <body-content>JSP</body-content>
+ </tag>
+
+ <tag>
+ <description>
+ Simple conditional tag, which evalutes its body if the
+ supplied condition is true and optionally exposes a Boolean
+ scripting variable representing the evaluation of this condition
+ </description>
+ <name>if</name>
+ <tag-class>org.apache.taglibs.standard.tag.rt.core.IfTag</tag-class>
+ <body-content>JSP</body-content>
+ <attribute>
+ <description>
+The test condition that determines whether or
+not the body content should be processed.
+ </description>
+ <name>test</name>
+ <required>true</required>
+ <rtexprvalue>true</rtexprvalue>
+ <type>boolean</type>
+ </attribute>
+ <attribute>
+ <description>
+Name of the exported scoped variable for the
+resulting value of the test condition. The type
+of the scoped variable is Boolean.
+ </description>
+ <name>var</name>
+ <required>false</required>
+ <rtexprvalue>false</rtexprvalue>
+ </attribute>
+ <attribute>
+ <description>
+Scope for var.
+ </description>
+ <name>scope</name>
+ <required>false</required>
+ <rtexprvalue>false</rtexprvalue>
+ </attribute>
+ </tag>
+
+ <tag>
+ <description>
+ Retrieves an absolute or relative URL and exposes its contents
+ to either the page, a String in 'var', or a Reader in 'varReader'.
+ </description>
+ <name>import</name>
+ <tag-class>org.apache.taglibs.standard.tag.rt.core.ImportTag</tag-class>
+ <tei-class>org.apache.taglibs.standard.tei.ImportTEI</tei-class>
+ <body-content>JSP</body-content>
+ <attribute>
+ <description>
+The URL of the resource to import.
+ </description>
+ <name>url</name>
+ <required>true</required>
+ <rtexprvalue>true</rtexprvalue>
+ </attribute>
+ <attribute>
+ <description>
+Name of the exported scoped variable for the
+resource's content. The type of the scoped
+variable is String.
+ </description>
+ <name>var</name>
+ <required>false</required>
+ <rtexprvalue>false</rtexprvalue>
+ </attribute>
+ <attribute>
+ <description>
+Scope for var.
+ </description>
+ <name>scope</name>
+ <required>false</required>
+ <rtexprvalue>false</rtexprvalue>
+ </attribute>
+ <attribute>
+ <description>
+Name of the exported scoped variable for the
+resource's content. The type of the scoped
+variable is Reader.
+ </description>
+ <name>varReader</name>
+ <required>false</required>
+ <rtexprvalue>false</rtexprvalue>
+ </attribute>
+ <attribute>
+ <description>
+Name of the context when accessing a relative
+URL resource that belongs to a foreign
+context.
+ </description>
+ <name>context</name>
+ <required>false</required>
+ <rtexprvalue>true</rtexprvalue>
+ </attribute>
+ <attribute>
+ <description>
+Character encoding of the content at the input
+resource.
+ </description>
+ <name>charEncoding</name>
+ <required>false</required>
+ <rtexprvalue>true</rtexprvalue>
+ </attribute>
+ </tag>
+
+ <tag>
+ <description>
+ The basic iteration tag, accepting many different
+ collection types and supporting subsetting and other
+ functionality
+ </description>
+ <name>forEach</name>
+ <tag-class>org.apache.taglibs.standard.tag.rt.core.ForEachTag</tag-class>
+ <tei-class>org.apache.taglibs.standard.tei.ForEachTEI</tei-class>
+ <body-content>JSP</body-content>
+ <attribute>
+ <description>
+Collection of items to iterate over.
+ </description>
+ <name>items</name>
+ <required>false</required>
+ <rtexprvalue>true</rtexprvalue>
+ <type>java.lang.Object</type>
+ <deferred-value>
+ <type>java.lang.Object</type>
+ </deferred-value>
+ </attribute>
+ <attribute>
+ <description>
+If items specified:
+Iteration begins at the item located at the
+specified index. First item of the collection has
+index 0.
+If items not specified:
+Iteration begins with index set at the value
+specified.
+ </description>
+ <name>begin</name>
+ <required>false</required>
+ <rtexprvalue>true</rtexprvalue>
+ <type>int</type>
+ </attribute>
+ <attribute>
+ <description>
+If items specified:
+Iteration ends at the item located at the
+specified index (inclusive).
+If items not specified:
+Iteration ends when index reaches the value
+specified.
+ </description>
+ <name>end</name>
+ <required>false</required>
+ <rtexprvalue>true</rtexprvalue>
+ <type>int</type>
+ </attribute>
+ <attribute>
+ <description>
+Iteration will only process every step items of
+the collection, starting with the first one.
+ </description>
+ <name>step</name>
+ <required>false</required>
+ <rtexprvalue>true</rtexprvalue>
+ <type>int</type>
+ </attribute>
+ <attribute>
+ <description>
+Name of the exported scoped variable for the
+current item of the iteration. This scoped
+variable has nested visibility. Its type depends
+on the object of the underlying collection.
+ </description>
+ <name>var</name>
+ <required>false</required>
+ <rtexprvalue>false</rtexprvalue>
+ </attribute>
+ <attribute>
+ <description>
+Name of the exported scoped variable for the
+status of the iteration. Object exported is of type
+javax.servlet.jsp.jstl.core.LoopTagStatus. This scoped variable has nested
+visibility.
+ </description>
+ <name>varStatus</name>
+ <required>false</required>
+ <rtexprvalue>false</rtexprvalue>
+ </attribute>
+ </tag>
+
+ <tag>
+ <description>
+ Iterates over tokens, separated by the supplied delimeters
+ </description>
+ <name>forTokens</name>
+ <tag-class>org.apache.taglibs.standard.tag.rt.core.ForTokensTag</tag-class>
+ <body-content>JSP</body-content>
+ <attribute>
+ <description>
+String of tokens to iterate over.
+ </description>
+ <name>items</name>
+ <required>true</required>
+ <rtexprvalue>true</rtexprvalue>
+ <type>java.lang.String</type>
+ <deferred-value>
+ <type>java.lang.String</type>
+ </deferred-value>
+ </attribute>
+ <attribute>
+ <description>
+The set of delimiters (the characters that
+separate the tokens in the string).
+ </description>
+ <name>delims</name>
+ <required>true</required>
+ <rtexprvalue>true</rtexprvalue>
+ <type>java.lang.String</type>
+ </attribute>
+ <attribute>
+ <description>
+Iteration begins at the token located at the
+specified index. First token has index 0.
+ </description>
+ <name>begin</name>
+ <required>false</required>
+ <rtexprvalue>true</rtexprvalue>
+ <type>int</type>
+ </attribute>
+ <attribute>
+ <description>
+Iteration ends at the token located at the
+specified index (inclusive).
+ </description>
+ <name>end</name>
+ <required>false</required>
+ <rtexprvalue>true</rtexprvalue>
+ <type>int</type>
+ </attribute>
+ <attribute>
+ <description>
+Iteration will only process every step tokens
+of the string, starting with the first one.
+ </description>
+ <name>step</name>
+ <required>false</required>
+ <rtexprvalue>true</rtexprvalue>
+ <type>int</type>
+ </attribute>
+ <attribute>
+ <description>
+Name of the exported scoped variable for the
+current item of the iteration. This scoped
+variable has nested visibility.
+ </description>
+ <name>var</name>
+ <required>false</required>
+ <rtexprvalue>false</rtexprvalue>
+ </attribute>
+ <attribute>
+ <description>
+Name of the exported scoped variable for the
+status of the iteration. Object exported is of
+type
+javax.servlet.jsp.jstl.core.LoopTag
+Status. This scoped variable has nested
+visibility.
+ </description>
+ <name>varStatus</name>
+ <required>false</required>
+ <rtexprvalue>false</rtexprvalue>
+ </attribute>
+ </tag>
+
+ <tag>
+ <description>
+ Like &lt;%= ... &gt;, but for expressions.
+ </description>
+ <name>out</name>
+ <tag-class>org.apache.taglibs.standard.tag.rt.core.OutTag</tag-class>
+ <body-content>JSP</body-content>
+ <attribute>
+ <description>
+Expression to be evaluated.
+ </description>
+ <name>value</name>
+ <required>true</required>
+ <rtexprvalue>true</rtexprvalue>
+ </attribute>
+ <attribute>
+ <description>
+Default value if the resulting value is null.
+ </description>
+ <name>default</name>
+ <required>false</required>
+ <rtexprvalue>true</rtexprvalue>
+ </attribute>
+ <attribute>
+ <description>
+Determines whether characters &lt;,&gt;,&amp;,'," in the
+resulting string should be converted to their
+corresponding character entity codes. Default value is
+true.
+ </description>
+ <name>escapeXml</name>
+ <required>false</required>
+ <rtexprvalue>true</rtexprvalue>
+ </attribute>
+ </tag>
+
+
+ <tag>
+ <description>
+ Subtag of &lt;choose&gt; that follows &lt;when&gt; tags
+ and runs only if all of the prior conditions evaluated to
+ 'false'
+ </description>
+ <name>otherwise</name>
+ <tag-class>org.apache.taglibs.standard.tag.common.core.OtherwiseTag</tag-class>
+ <body-content>JSP</body-content>
+ </tag>
+
+ <tag>
+ <description>
+ Adds a parameter to a containing 'import' tag's URL.
+ </description>
+ <name>param</name>
+ <tag-class>org.apache.taglibs.standard.tag.rt.core.ParamTag</tag-class>
+ <body-content>JSP</body-content>
+ <attribute>
+ <description>
+Name of the query string parameter.
+ </description>
+ <name>name</name>
+ <required>true</required>
+ <rtexprvalue>true</rtexprvalue>
+ </attribute>
+ <attribute>
+ <description>
+Value of the parameter.
+ </description>
+ <name>value</name>
+ <required>false</required>
+ <rtexprvalue>true</rtexprvalue>
+ </attribute>
+ </tag>
+
+ <tag>
+ <description>
+ Redirects to a new URL.
+ </description>
+ <name>redirect</name>
+ <tag-class>org.apache.taglibs.standard.tag.rt.core.RedirectTag</tag-class>
+ <body-content>JSP</body-content>
+ <attribute>
+ <description>
+The URL of the resource to redirect to.
+ </description>
+ <name>url</name>
+ <required>false</required>
+ <rtexprvalue>true</rtexprvalue>
+ </attribute>
+ <attribute>
+ <description>
+Name of the context when redirecting to a relative URL
+resource that belongs to a foreign context.
+ </description>
+ <name>context</name>
+ <required>false</required>
+ <rtexprvalue>true</rtexprvalue>
+ </attribute>
+ </tag>
+
+ <tag>
+ <description>
+ Removes a scoped variable (from a particular scope, if specified).
+ </description>
+ <name>remove</name>
+ <tag-class>org.apache.taglibs.standard.tag.common.core.RemoveTag</tag-class>
+ <body-content>empty</body-content>
+ <attribute>
+ <description>
+Name of the scoped variable to be removed.
+ </description>
+ <name>var</name>
+ <required>true</required>
+ <rtexprvalue>false</rtexprvalue>
+ </attribute>
+ <attribute>
+ <description>
+Scope for var.
+ </description>
+ <name>scope</name>
+ <required>false</required>
+ <rtexprvalue>false</rtexprvalue>
+ </attribute>
+ </tag>
+
+ <tag>
+ <description>
+ Sets the result of an expression evaluation in a 'scope'
+ </description>
+ <name>set</name>
+ <tag-class>org.apache.taglibs.standard.tag.rt.core.SetTag</tag-class>
+ <body-content>JSP</body-content>
+ <attribute>
+ <description>
+Name of the exported scoped variable to hold the value
+specified in the action. The type of the scoped variable is
+whatever type the value expression evaluates to.
+ </description>
+ <name>var</name>
+ <required>false</required>
+ <rtexprvalue>false</rtexprvalue>
+ </attribute>
+ <attribute>
+ <description>
+Expression to be evaluated.
+ </description>
+ <name>value</name>
+ <required>false</required>
+ <rtexprvalue>true</rtexprvalue>
+ <deferred-value>
+ <type>java.lang.Object</type>
+ </deferred-value>
+ </attribute>
+ <attribute>
+ <description>
+Target object whose property will be set. Must evaluate to
+a JavaBeans object with setter property property, or to a
+java.util.Map object.
+ </description>
+ <name>target</name>
+ <required>false</required>
+ <rtexprvalue>true</rtexprvalue>
+ </attribute>
+ <attribute>
+ <description>
+Name of the property to be set in the target object.
+ </description>
+ <name>property</name>
+ <required>false</required>
+ <rtexprvalue>true</rtexprvalue>
+ </attribute>
+ <attribute>
+ <description>
+Scope for var.
+ </description>
+ <name>scope</name>
+ <required>false</required>
+ <rtexprvalue>false</rtexprvalue>
+ </attribute>
+ </tag>
+
+ <tag>
+ <description>
+ Creates a URL with optional query parameters.
+ </description>
+ <name>url</name>
+ <tag-class>org.apache.taglibs.standard.tag.rt.core.UrlTag</tag-class>
+ <body-content>JSP</body-content>
+ <attribute>
+ <description>
+Name of the exported scoped variable for the
+processed url. The type of the scoped variable is
+String.
+ </description>