diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml
deleted file mode 100644
index 57e6102..0000000
--- a/.github/workflows/gradle.yml
+++ /dev/null
@@ -1,43 +0,0 @@
-name: Gradle
-on:
- push:
- branches: [grails3, grails4, grails5, grails6]
- pull_request:
- branches: [grails3, grails4, grails5, grails6]
-permissions:
- contents: read
-jobs:
- build:
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v4
- - id: java
- name: Pick Java version from Gradle wrapper major
- shell: bash
- run: |
- GRADLE_VER=$(grep -oE 'gradle-[0-9]+(\.[0-9]+)*' gradle/wrapper/gradle-wrapper.properties | head -1 | sed 's/gradle-//')
- GRADLE_MAJOR=$(echo "$GRADLE_VER" | cut -d. -f1)
- if [ -z "$GRADLE_MAJOR" ] || [ "$GRADLE_MAJOR" -le 4 ]; then
- JAVA=8
- elif [ "$GRADLE_MAJOR" -le 6 ]; then
- JAVA=11
- else
- JAVA=17
- fi
- echo "Detected Gradle $GRADLE_VER (major=$GRADLE_MAJOR), using Java $JAVA"
- echo "version=$JAVA" >> "$GITHUB_OUTPUT"
- - uses: actions/setup-java@v4
- with:
- distribution: temurin
- java-version: ${{ steps.java.outputs.version }}
- - uses: actions/cache@v4
- with:
- path: |
- ~/.gradle/caches
- ~/.gradle/wrapper
- key: ${{ runner.os }}-gradle-${{ steps.java.outputs.version }}-${{ hashFiles('**/*.gradle*', '**/settings.gradle*', '**/gradle.properties', 'gradle/wrapper/gradle-wrapper.properties') }}
- restore-keys: |
- ${{ runner.os }}-gradle-${{ steps.java.outputs.version }}-
- ${{ runner.os }}-gradle-
- - run: chmod +x ./gradlew
- - run: ./gradlew --no-daemon check
\ No newline at end of file
diff --git a/.github/workflows/grails8.yml b/.github/workflows/grails8.yml
new file mode 100644
index 0000000..c421ff5
--- /dev/null
+++ b/.github/workflows/grails8.yml
@@ -0,0 +1,77 @@
+name: Grails 8 CI
+
+on:
+ push:
+ branches: [ grails8 ]
+ pull_request:
+ branches: [ grails8 ]
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
+jobs:
+ test:
+ runs-on: ubuntu-latest
+ strategy:
+ fail-fast: false
+ matrix:
+ app: [ initial, complete ]
+ name: ${{ matrix.app }}
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Set up JDK 21
+ uses: actions/setup-java@v4
+ with:
+ distribution: temurin
+ java-version: '21'
+
+ - name: Set up Gradle
+ uses: gradle/actions/setup-gradle@v4
+
+ - name: Unit tests
+ working-directory: ${{ matrix.app }}
+ run: |
+ chmod +x gradlew
+ ./gradlew test --console=plain --stacktrace
+
+ - name: Integration tests (complete only)
+ if: matrix.app == 'complete'
+ working-directory: complete
+ run: ./gradlew integrationTest --console=plain --stacktrace
+
+ - name: Startup smoke check (complete only)
+ if: matrix.app == 'complete'
+ working-directory: complete
+ run: |
+ ./gradlew bootRun --console=plain > boot.log 2>&1 &
+ APP_PID=$!
+ cleanup() { kill "$APP_PID" 2>/dev/null || true; }
+ trap cleanup EXIT
+ for i in $(seq 1 60); do
+ if grep -Eq 'Tomcat started on port.*8080' boot.log; then
+ echo "Application started on port 8080"
+ code=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:8080/ || true)
+ echo "GET / -> HTTP ${code}"
+ echo "$code" | grep -Eq '^[0-9]{3}$'
+ exit 0
+ fi
+ if grep -q 'APPLICATION FAILED TO START' boot.log; then
+ echo "Application failed to start"
+ cat boot.log
+ exit 1
+ fi
+ sleep 5
+ done
+ echo "Timed out waiting for application start"
+ cat boot.log
+ exit 1
+
+ - name: Upload test reports
+ if: always()
+ uses: actions/upload-artifact@v4
+ with:
+ name: test-reports-${{ matrix.app }}
+ path: ${{ matrix.app }}/build/reports/tests/
+ if-no-files-found: ignore
diff --git a/README.md b/README.md
index 8354f3b..5495040 100644
--- a/README.md
+++ b/README.md
@@ -1,11 +1,103 @@
-# Send Email and Spock Spring
+# grails-email
-This repository contains the worked sample app for the [Send Email and Spock Spring](https://grails.apache.org/guides/grails-email/4/guide/index.html) guide on the Apache Grails site.
+Sample app for **Sending Email from a Grails 8 Application (and Testing It)** (Apache Grails `8.0.0-SNAPSHOT`, JDK 21).
-## Run the example
+The guide shows how to send mail from a Grails 8 service over SMTP (Grails Mail plugin) and through a provider (SendGrid / AWS SES), how to inject and externalize mail configuration, and how to verify the send interaction using Spock Spring integration tests — without dispatching real email.
-`./gradlew bootRun`
+## Layout
-## Maintained at apache/grails-static-website
+| Directory | What it is |
+|-----------|------------|
+| `initial/` | Grails 8 REST API starter from [start.grails.org](https://start.grails.org) (`views-json`, `spock`). Work through the guide starting here. |
+| `complete/` | Finished sample — `EmailService` boundary, SMTP / SendGrid / SES implementations, `POST /mail/send`, and a Spock Spring integration spec that mocks the sender. |
-The guide narrative is maintained at https://github.com/apache/grails-static-website. Changes to the guide content go there; this repo holds the worked sample app only.
+## Running
+
+```bash
+git clone -b grails8 https://github.com/grails-guides/grails-email.git
+cd grails-email/complete
+./gradlew test integrationTest
+```
+
+To follow the guide step by step, start from `initial/`:
+
+```bash
+cd grails-email/initial
+./gradlew test
+```
+
+Run the finished app locally (SMTP defaults to `localhost:1025` — use [MailHog](https://github.com/mailhog/MailHog) or similar for manual verification):
+
+```bash
+cd grails-email/complete
+./gradlew bootRun
+```
+
+Example endpoint:
+
+```bash
+curl -X POST http://localhost:8080/mail/send \
+ -H 'Content-Type: application/json' \
+ -d '{"recipient":"user@example.com","subject":"Hello","textBody":"Hi there"}'
+```
+
+Switch providers via `application.yml` or environment variables:
+
+| Provider | Set |
+|----------|-----|
+| SMTP (default) | `EMAIL_PROVIDER=smtp` plus `SMTP_HOST`, `SMTP_PORT`, etc. |
+| SendGrid | `EMAIL_PROVIDER=sendgrid`, `SENDGRID_APIKEY`, `SENDGRID_FROM_EMAIL` |
+| AWS SES | `EMAIL_PROVIDER=ses`, `AWS_REGION`, `AWS_SOURCE` (+ standard AWS credentials) |
+
+The integration spec (`MailControllerSpec`) uses a Spock mock for `EmailService` and asserts the message is built and the sender is invoked — no live email is sent in CI.
+
+## Requirements
+
+- **JDK 21** (Temurin recommended; the Gradle build enforces Java 21+)
+- Optional: local SMTP catcher on port **1025** for manual `bootRun` verification
+
+If Gradle reports *"Run this build using a Java 21 or newer JVM"*, switch your shell to JDK 21:
+
+```bash
+sdk install java 21.0.6-tem
+sdk default java 21.0.6-tem
+java -version # should show 21.x
+```
+
+## The pattern, in one place
+
+```yaml
+# application.yml — pick a provider and externalize secrets
+email:
+ provider: ${EMAIL_PROVIDER:smtp}
+
+grails:
+ mail:
+ host: ${SMTP_HOST:localhost}
+ port: ${SMTP_PORT:1025}
+ default:
+ from: ${SMTP_FROM:no-reply@example.com}
+```
+
+```groovy
+// grails-app/conf/spring/resources.groovy — inject the right implementation
+beans = {
+ def provider = application.config.getProperty('email.provider', String, 'smtp')
+ switch (provider) {
+ case 'sendgrid': emailService(SendGridEmailService); break
+ case 'ses': emailService(AwsSesEmailService); break
+ case 'smtp': emailService(SmtpEmailService); break
+ default: throw new IllegalArgumentException("Unsupported email.provider: ${provider}")
+ }
+}
+```
+
+Grails 8 uses `org.grails.plugins:grails-mail` 5.x from the Grails plugin repository — compatible with Apache Grails 8.
+
+## Guide prose
+
+Published narrative lives on [grails.apache.org/guides](https://grails.apache.org/guides/) in [apache/grails-static-website](https://github.com/apache/grails-static-website) under `guides/grails-email/v8/`.
+
+## CI
+
+GitHub Actions (`.github/workflows/grails8.yml`) runs `./gradlew test` for `initial` and `complete`, and `./gradlew integrationTest` for `complete`, on pushes and PRs to the `grails8` branch.
diff --git a/build.gradle b/build.gradle
deleted file mode 100644
index f4b92f8..0000000
--- a/build.gradle
+++ /dev/null
@@ -1,10 +0,0 @@
-plugins {
- id 'org.asciidoctor.convert' version '1.5.7'
-}
-
-asciidoctorj {
- version = '1.5.6'
-}
-
-apply from:"https://raw.githubusercontent.com/grails/grails-guides/master/gradle/guide-build.gradle"
-
diff --git a/complete/.gitignore b/complete/.gitignore
index f33ace2..bec2c1b 100644
--- a/complete/.gitignore
+++ b/complete/.gitignore
@@ -10,3 +10,4 @@ out/
.project
.settings
.classpath
+*.log
diff --git a/complete/build.gradle b/complete/build.gradle
index ceb634b..80ff57c 100644
--- a/complete/build.gradle
+++ b/complete/build.gradle
@@ -1,155 +1,140 @@
-import org.gradle.api.tasks.testing.logging.TestExceptionFormat
-import org.gradle.api.tasks.testing.logging.TestLogEvent
-
buildscript {
repositories {
- mavenLocal()
- maven { url "https://repo.grails.org/grails/core" }
+ mavenCentral()
+ maven {
+ url = 'https://repo.grails.org/grails/restricted'
+ }
+ maven {
+ url = 'https://repository.apache.org/content/groups/snapshots'
+ content {
+ includeVersionByRegex('org[.]apache[.]grails.*', '.*', '.*-SNAPSHOT')
+ }
+ content {
+ includeVersionByRegex('org[.]apache[.]groovy.*', 'groovy.*', '.*-SNAPSHOT')
+ }
+ mavenContent {
+ snapshotsOnly()
+ }
+ }
+ maven {
+ url = 'https://repository.apache.org/content/groups/staging'
+ content {
+ includeVersionByRegex('org[.]apache[.]grails[.]gradle', 'grails-publish', '.*')
+ }
+ content {
+ includeVersionByRegex('org[.]apache[.]groovy.*', 'groovy.*', '.*')
+ }
+ mavenContent {
+ releasesOnly()
+ }
+ }
}
dependencies {
- classpath "org.grails:grails-gradle-plugin:$grailsVersion"
- classpath "org.grails.plugins:hibernate5:7.1.0"
- classpath "org.grails.plugins:views-gradle:2.0.0.RC2"
+ classpath platform("org.apache.grails:grails-bom:$grailsVersion")
+ classpath "org.apache.grails:grails-gradle-plugins"
}
}
-version "0.1"
-group "example.grails"
+plugins {
+ id "war"
+ id "idea"
+ id "eclipse"
+}
+
+apply plugin: "org.apache.grails.gradle.grails-web"
+apply plugin: "org.apache.grails.gradle.grails-gson"
+apply plugin: "org.apache.grails.gradle.grails-gsp"
-apply plugin:"eclipse"
-apply plugin:"idea"
-apply plugin:"war"
-apply plugin:"org.grails.grails-web"
-apply plugin:"org.grails.plugins.views-json"
+group = "example"
repositories {
- mavenLocal()
mavenCentral()
- maven { url "https://repo.grails.org/grails/core" }
-}
-
-configurations {
- developmentOnly
- runtimeClasspath {
- extendsFrom developmentOnly
+ maven {
+ url = 'https://repo.grails.org/grails/restricted'
+ }
+ maven {
+ url = 'https://repo.grails.org/grails/core'
+ }
+ maven {
+ url = 'https://repository.apache.org/content/groups/snapshots'
+ content {
+ includeVersionByRegex('org[.]apache[.]grails.*', '.*', '.*-SNAPSHOT')
+ }
+ content {
+ includeVersionByRegex('org[.]apache[.]groovy.*', 'groovy.*', '.*-SNAPSHOT')
+ }
+ mavenContent {
+ snapshotsOnly()
+ }
+ }
+ maven {
+ url = 'https://repository.apache.org/content/groups/staging'
+ content {
+ includeVersionByRegex('org[.]apache[.]grails[.]gradle', 'grails-publish', '.*')
+ }
+ content {
+ includeVersionByRegex('org[.]apache[.]groovy.*', 'groovy.*', '.*')
+ }
+ mavenContent {
+ releasesOnly()
+ }
}
}
+
dependencies {
- developmentOnly("org.springframework.boot:spring-boot-devtools")
- implementation "org.springframework.boot:spring-boot-starter-logging"
+ profile "org.apache.grails.profiles:rest-api"
+ developmentOnly "org.springframework.boot:spring-boot-devtools"
+ implementation platform("org.apache.grails:grails-bom:$grailsVersion")
+ implementation "org.apache.grails:grails-core"
+ implementation "org.apache.grails:grails-databinding"
+ implementation "org.apache.grails:grails-events"
+ implementation "org.apache.grails:grails-interceptors"
+ implementation "org.apache.grails:grails-logging"
+ implementation "org.apache.grails:grails-rest-transforms"
+ implementation "org.apache.grails:grails-services"
+ implementation "org.apache.grails:grails-url-mappings"
+ implementation "org.apache.grails:grails-views-gson"
+ implementation "org.apache.grails:grails-web-boot"
+ implementation "org.apache.grails:grails-gsp"
implementation "org.springframework.boot:spring-boot-autoconfigure"
- implementation "org.grails:grails-core"
+ implementation "org.springframework.boot:spring-boot-starter"
implementation "org.springframework.boot:spring-boot-starter-actuator"
+ implementation "org.springframework.boot:spring-boot-starter-logging"
implementation "org.springframework.boot:spring-boot-starter-tomcat"
- implementation "org.grails:grails-plugin-url-mappings"
- implementation "org.grails:grails-plugin-rest"
- implementation "org.grails:grails-plugin-codecs"
- implementation "org.grails:grails-plugin-interceptors"
- implementation "org.grails:grails-plugin-services"
- implementation "org.grails:grails-plugin-datasource"
- implementation "org.grails:grails-plugin-databinding"
- implementation "org.grails:grails-web-boot"
- implementation "org.grails:grails-logging"
- implementation "org.grails.plugins:cache"
- implementation "org.grails.plugins:async"
- implementation "org.grails.plugins:events"
- implementation "org.grails.plugins:hibernate5"
- implementation "org.hibernate:hibernate-core:5.5.7.Final"
- implementation "org.grails.plugins:views-json"
- implementation "org.grails.plugins:views-json-templates"
- console "org.grails:grails-console"
- profile "org.grails.profiles:rest-api"
- runtimeOnly "org.glassfish.web:el-impl:2.1.2-b03"
- runtimeOnly "com.h2database:h2"
- runtimeOnly "org.apache.tomcat:tomcat-jdbc"
- testImplementation "org.grails:grails-gorm-testing-support"
- testImplementation "org.grails:grails-web-testing-support"
- testImplementation "org.grails.plugins:geb:1.1.2"
- testRuntimeOnly "org.seleniumhq.selenium:selenium-chrome-driver:2.47.1"
- testImplementation "org.seleniumhq.selenium:htmlunit-driver:2.35.1"
- testRuntime 'net.sourceforge.htmlunit:htmlunit:2.35.0'
- //tag::awsses[]
- compile 'software.amazon.awssdk:ses:2.10.24'
- //end::awsses[]
+ implementation "org.springframework.boot:spring-boot-starter-validation"
+ implementation "org.springframework.boot:spring-boot-tomcat"
+ console "org.apache.grails:grails-console"
+ //tag::mailPlugin[]
+ implementation "org.grails.plugins:grails-mail:$mailPluginVersion"
+ //end::mailPlugin[]
//tag::sendgrid[]
- compile 'com.sendgrid:sendgrid-java:4.1.2'
+ implementation "com.sendgrid:sendgrid-java:$sendgridVersion"
//end::sendgrid[]
-
- testImplementation "io.micronaut:micronaut-http-client"
-
+ //tag::awsses[]
+ implementation "software.amazon.awssdk:ses:$awsSesVersion"
+ //end::awsses[]
+ testImplementation "org.apache.grails:grails-testing-support-views-gson"
+ testImplementation "org.apache.grails:grails-testing-support-web"
+ testImplementation "org.mockito:mockito-core"
+ testImplementation "org.spockframework:spock-core"
//tag::dependencySpockSpring[]
- testCompile 'org.spockframework:spock-spring:1.3-groovy-2.5'
+ testImplementation "org.spockframework:spock-spring"
//end::dependencySpockSpring[]
-}
-bootRun {
- jvmArgs(
- '-Dspring.output.ansi.enabled=always',
- '-noverify',
- '-XX:TieredStopAtLevel=1',
- '-Xmx1024m')
- sourceResources sourceSets.main
- String springProfilesActive = 'spring.profiles.active'
- systemProperty springProfilesActive, System.getProperty(springProfilesActive)
- //tag::awsprops[]
- if (System.getProperty('AWS_SOURCE')) {
- systemProperty "AWS_SOURCE", System.getProperty('AWS_SOURCE')
- }
- if (System.getProperty('AWS_SOURCE')) {
- systemProperty "AWS_REGION", System.getProperty('AWS_REGION')
- }
- //end::awsprops[]
- //tag::sendgridprops[]
- if (System.getProperty('SENDGRID_APIKEY')) {
- systemProperty "SENDGRID_APIKEY", System.getProperty('SENDGRID_APIKEY')
- }
- if (System.getProperty('SENDGRID_FROM_EMAIL')) {
- systemProperty "SENDGRID_FROM_EMAIL", System.getProperty('SENDGRID_FROM_EMAIL')
- }
- //end::sendgridprops[]
+ integrationTestImplementation "org.apache.grails:grails-testing-support-views-gson"
+ integrationTestImplementation "org.apache.grails:grails-testing-support-web"
+ integrationTestImplementation "org.spockframework:spock-core"
+ integrationTestImplementation "org.spockframework:spock-spring"
}
-tasks.withType(Test) {
- systemProperty "geb.env", System.getProperty('geb.env')
- systemProperty "geb.build.reportsDir", reporting.file("geb/integrationTest")
- systemProperty "webdriver.chrome.driver", System.getProperty('webdriver.chrome.driver')
- systemProperty "webdriver.gecko.driver", System.getProperty('webdriver.gecko.driver')
+java {
+ toolchain {
+ languageVersion = JavaLanguageVersion.of(21)
+ }
}
-tasks.withType(Test) {
- testLogging {
- // set options for log level LIFECYCLE
- events TestLogEvent.FAILED,
- TestLogEvent.PASSED,
- TestLogEvent.SKIPPED,
- TestLogEvent.STANDARD_OUT
- exceptionFormat TestExceptionFormat.FULL
- showExceptions true
- showCauses true
- showStackTraces true
-
- // set options for log level DEBUG and INFO
- debug {
- events TestLogEvent.STARTED,
- TestLogEvent.FAILED,
- TestLogEvent.PASSED,
- TestLogEvent.SKIPPED,
- TestLogEvent.STANDARD_ERROR,
- TestLogEvent.STANDARD_OUT
- exceptionFormat TestExceptionFormat.FULL
- }
- info.events = debug.events
- info.exceptionFormat = debug.exceptionFormat
+compileJava.options.release = 21
- afterSuite { desc, result ->
- if (!desc.parent) { // will match the outermost suite
- def output = "Results: ${result.resultType} (${result.testCount} tests, ${result.successfulTestCount} successes, ${result.failedTestCount} failures, ${result.skippedTestCount} skipped)"
- def startItem = '| ', endItem = ' |'
- def repeatLength = startItem.length() + output.length() + endItem.length()
- println('\n' + ('-' * repeatLength) + '\n' + startItem + output + endItem + '\n' + ('-' * repeatLength))
- }
- }
- }
+tasks.withType(Test).configureEach {
+ useJUnitPlatform()
}
-
-tasks.withType(Test) { useJUnitPlatform() }
diff --git a/complete/gradle.properties b/complete/gradle.properties
index 37eb3c8..230640b 100644
--- a/complete/gradle.properties
+++ b/complete/gradle.properties
@@ -1,6 +1,9 @@
-grailsVersion=5.0.0
-gormVersion=7.1.0.M5
-groovyVersion=3.0.7
+grailsVersion=8.0.0-SNAPSHOT
+version=0.1
+org.gradle.caching=true
org.gradle.daemon=true
org.gradle.parallel=true
org.gradle.jvmargs=-Dfile.encoding=UTF-8 -Xmx1024M
+sendgridVersion=4.9.3
+awsSesVersion=2.29.45
+mailPluginVersion=5.0.3
diff --git a/complete/gradle/wrapper/gradle-wrapper.jar b/complete/gradle/wrapper/gradle-wrapper.jar
index 7454180..1b33c55 100644
Binary files a/complete/gradle/wrapper/gradle-wrapper.jar and b/complete/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/complete/gradle/wrapper/gradle-wrapper.properties b/complete/gradle/wrapper/gradle-wrapper.properties
index 549d844..aaaabb3 100644
--- a/complete/gradle/wrapper/gradle-wrapper.properties
+++ b/complete/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.9-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.4-bin.zip
+networkTimeout=10000
+validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/complete/gradlew b/complete/gradlew
index c53aefa..23d15a9 100755
--- a/complete/gradlew
+++ b/complete/gradlew
@@ -1,7 +1,7 @@
#!/bin/sh
#
-# Copyright 2015-2021 the original authors.
+# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -15,6 +15,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
+# SPDX-License-Identifier: Apache-2.0
+#
##############################################################################
#
@@ -32,10 +34,10 @@
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
-# * expansions $var, ${var}, ${var:-default}, ${var+SET},
-# ${var#prefix}, ${var%suffix}, and $( cmd );
-# * compound commands having a testable exit status, especially case;
-# * various built-in commands including command, set, and ulimit.
+# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
+# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
+# * compound commands having a testable exit status, especially «case»;
+# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
@@ -55,7 +57,7 @@
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
-# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
@@ -80,13 +82,11 @@ do
esac
done
-APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
-
-APP_NAME="Gradle"
+# This is normally unused
+# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
-
-# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
+APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
@@ -114,7 +114,7 @@ case "$( uname )" in #(
NONSTOP* ) nonstop=true ;;
esac
-CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+CLASSPATH="\\\"\\\""
# Determine the Java command to use to start the JVM.
@@ -133,22 +133,29 @@ location of your Java installation."
fi
else
JAVACMD=java
- which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+ if ! command -v java >/dev/null 2>&1
+ then
+ die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
+ fi
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
+ # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
+ # shellcheck disable=SC2039,SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
+ # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
+ # shellcheck disable=SC2039,SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
@@ -193,18 +200,28 @@ if "$cygwin" || "$msys" ; then
done
fi
-# Collect all arguments for the java command;
-# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
-# shell script including quotes and variable substitutions, so put them in
-# double quotes to make sure that they get re-expanded; and
-# * put everything else in single quotes, so that it's not re-expanded.
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Collect all arguments for the java command:
+# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
+# and any embedded shellness will be escaped.
+# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
+# treated as '${Hostname}' itself on the command line.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
- org.gradle.wrapper.GradleWrapperMain \
+ -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
"$@"
+# Stop when "xargs" is not available.
+if ! command -v xargs >/dev/null 2>&1
+then
+ die "xargs is not available"
+fi
+
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
diff --git a/complete/gradlew.bat b/complete/gradlew.bat
old mode 100755
new mode 100644
index ac1b06f..db3a6ac
--- a/complete/gradlew.bat
+++ b/complete/gradlew.bat
@@ -1,89 +1,94 @@
-@rem
-@rem Copyright 2015 the original author or authors.
-@rem
-@rem Licensed under the Apache License, Version 2.0 (the "License");
-@rem you may not use this file except in compliance with the License.
-@rem You may obtain a copy of the License at
-@rem
-@rem https://www.apache.org/licenses/LICENSE-2.0
-@rem
-@rem Unless required by applicable law or agreed to in writing, software
-@rem distributed under the License is distributed on an "AS IS" BASIS,
-@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-@rem See the License for the specific language governing permissions and
-@rem limitations under the License.
-@rem
-
-@if "%DEBUG%" == "" @echo off
-@rem ##########################################################################
-@rem
-@rem Gradle startup script for Windows
-@rem
-@rem ##########################################################################
-
-@rem Set local scope for the variables with windows NT shell
-if "%OS%"=="Windows_NT" setlocal
-
-set DIRNAME=%~dp0
-if "%DIRNAME%" == "" set DIRNAME=.
-set APP_BASE_NAME=%~n0
-set APP_HOME=%DIRNAME%
-
-@rem Resolve any "." and ".." in APP_HOME to make it shorter.
-for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
-
-@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
-
-@rem Find java.exe
-if defined JAVA_HOME goto findJavaFromJavaHome
-
-set JAVA_EXE=java.exe
-%JAVA_EXE% -version >NUL 2>&1
-if "%ERRORLEVEL%" == "0" goto execute
-
-echo.
-echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
-
-goto fail
-
-:findJavaFromJavaHome
-set JAVA_HOME=%JAVA_HOME:"=%
-set JAVA_EXE=%JAVA_HOME%/bin/java.exe
-
-if exist "%JAVA_EXE%" goto execute
-
-echo.
-echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
-
-goto fail
-
-:execute
-@rem Setup the command line
-
-set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
-
-
-@rem Execute Gradle
-"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
-
-:end
-@rem End local scope for the variables with windows NT shell
-if "%ERRORLEVEL%"=="0" goto mainEnd
-
-:fail
-rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
-rem the _cmd.exe /c_ return code!
-if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
-exit /b 1
-
-:mainEnd
-if "%OS%"=="Windows_NT" endlocal
-
-:omega
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+@rem SPDX-License-Identifier: Apache-2.0
+@rem
+
+@if "%DEBUG%"=="" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%"=="" set DIRNAME=.
+@rem This is normally unused
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if %ERRORLEVEL% equ 0 goto execute
+
+echo. 1>&2
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
+echo. 1>&2
+echo Please set the JAVA_HOME variable in your environment to match the 1>&2
+echo location of your Java installation. 1>&2
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo. 1>&2
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
+echo. 1>&2
+echo Please set the JAVA_HOME variable in your environment to match the 1>&2
+echo location of your Java installation. 1>&2
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if %ERRORLEVEL% equ 0 goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+set EXIT_CODE=%ERRORLEVEL%
+if %EXIT_CODE% equ 0 set EXIT_CODE=1
+if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
+exit /b %EXIT_CODE%
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/complete/grails-app/conf/application-development.yml b/complete/grails-app/conf/application-development.yml
new file mode 100644
index 0000000..3b5238d
--- /dev/null
+++ b/complete/grails-app/conf/application-development.yml
@@ -0,0 +1,4 @@
+---
+logging:
+ level:
+ example: DEBUG
diff --git a/complete/grails-app/conf/application.yml b/complete/grails-app/conf/application.yml
index bcd17fe..56affcb 100644
--- a/complete/grails-app/conf/application.yml
+++ b/complete/grails-app/conf/application.yml
@@ -1,121 +1,65 @@
----
-grails:
- profile: rest-api
- codegen:
- defaultPackage: example.grails
- gorm:
- reactor:
- # Whether to translate GORM events into Reactor events
- # Disabled by default for performance reasons
- events: false
info:
- app:
- name: '@info.app.name@'
- version: '@info.app.version@'
- grailsVersion: '@info.app.grailsVersion@'
-spring:
- main:
- banner-mode: "off"
- groovy:
- template:
- check-template-location: false
-
-# Spring Actuator Endpoints are Disabled by Default
-endpoints:
- enabled: false
- jmx:
- enabled: true
-
----
+ app:
+ name: '@info.app.name@'
+ version: '@info.app.version@'
+ grailsVersion: '@info.app.grailsVersion@'
grails:
- mime:
- disable:
- accept:
- header:
- userAgents:
- - Gecko
- - WebKit
- - Presto
- - Trident
- types:
- json:
- - application/json
- - text/json
- hal:
- - application/hal+json
- - application/hal+xml
- xml:
- - text/xml
- - application/xml
- atom: application/atom+xml
- css: text/css
- csv: text/csv
- js: text/javascript
- rss: application/rss+xml
- text: text/plain
- all: '*/*'
- urlmapping:
- cache:
- maxsize: 1000
- controllers:
- defaultScope: singleton
- converters:
- encoding: UTF-8
-
----
-hibernate:
- cache:
- queries: false
- use_second_level_cache: false
- use_query_cache: false
-dataSource:
- pooled: true
- jmxExport: true
- driverClassName: org.h2.Driver
- username: sa
- password: ''
-
-environments:
- development:
- dataSource:
- dbCreate: create-drop
- url: jdbc:h2:mem:devDb;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE
- test:
- dataSource:
- dbCreate: update
- url: jdbc:h2:mem:testDb;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE
- production:
- dataSource:
- dbCreate: none
- url: jdbc:h2:./prodDb;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE
- properties:
- jmxEnabled: true
- initialSize: 5
- maxActive: 50
- minIdle: 5
- maxIdle: 25
- maxWait: 10000
- maxAge: 600000
- timeBetweenEvictionRunsMillis: 5000
- minEvictableIdleTimeMillis: 60000
- validationQuery: SELECT 1
- validationQueryTimeout: 3
- validationInterval: 15000
- testOnBorrow: true
- testWhileIdle: true
- testOnReturn: false
- jdbcInterceptors: ConnectionState
- defaultTransactionIsolation: 2 # TRANSACTION_READ_COMMITTED
----
-#tag::awsses[]
-aws:
- ses:
- source: '${AWS_SOURCE}'
- region: '${AWS_REGION}'
-#end::awsses[]
----
+ codegen:
+ defaultPackage: example
+ profile: rest-api
+ mime:
+ disable:
+ accept:
+ header:
+ userAgents:
+ - Gecko
+ - WebKit
+ - Presto
+ - Trident
+ types:
+ json:
+ - application/json
+ - text/json
+ hal:
+ - application/hal+json
+ - application/hal+xml
+ xml:
+ - text/xml
+ - application/xml
+ atom: application/atom+xml
+ css: text/css
+ csv: text/csv
+ js: text/javascript
+ rss: application/rss+xml
+ text: text/plain
+ all: '*/*'
+ views:
+ default:
+ codec: html
+ #tag::mailConfig[]
+ mail:
+ host: ${SMTP_HOST:localhost}
+ port: ${SMTP_PORT:1025}
+ username: ${SMTP_USERNAME:}
+ password: ${SMTP_PASSWORD:}
+ default:
+ from: ${SMTP_FROM:no-reply@example.com}
+ props:
+ mail.smtp.auth: ${SMTP_AUTH:false}
+ mail.smtp.starttls.enable: ${SMTP_STARTTLS:false}
+ #end::mailConfig[]
+#tag::emailProvider[]
+email:
+ provider: ${EMAIL_PROVIDER:smtp}
+#end::emailProvider[]
#tag::sendgrid[]
sendgrid:
- api: '${SENDGRID_APIKEY}'
- from: '${SENDGRID_FROM_EMAIL}'
+ api: ${SENDGRID_APIKEY:}
+ from: ${SENDGRID_FROM_EMAIL:}
#end::sendgrid[]
+#tag::awsses[]
+aws:
+ ses:
+ source: ${AWS_SOURCE:}
+ region: ${AWS_REGION:}
+#end::awsses[]
diff --git a/complete/grails-app/conf/logback-spring.xml b/complete/grails-app/conf/logback-spring.xml
new file mode 100644
index 0000000..73b1187
--- /dev/null
+++ b/complete/grails-app/conf/logback-spring.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/complete/grails-app/conf/logback.groovy b/complete/grails-app/conf/logback.groovy
deleted file mode 100644
index d380c53..0000000
--- a/complete/grails-app/conf/logback.groovy
+++ /dev/null
@@ -1,40 +0,0 @@
-import grails.util.BuildSettings
-import grails.util.Environment
-import org.springframework.boot.logging.logback.ColorConverter
-import org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter
-
-import java.nio.charset.Charset
-
-conversionRule 'clr', ColorConverter
-conversionRule 'wex', WhitespaceThrowableProxyConverter
-
-// See http://logback.qos.ch/manual/groovy.html for details on configuration
-appender('STDOUT', ConsoleAppender) {
- encoder(PatternLayoutEncoder) {
- charset = Charset.forName('UTF-8')
-
- pattern =
- '%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} ' + // Date
- '%clr(%5p) ' + // Log level
- '%clr(---){faint} %clr([%15.15t]){faint} ' + // Thread
- '%clr(%-40.40logger{39}){cyan} %clr(:){faint} ' + // Logger
- '%m%n%wex' // Message
- }
-}
-
-def targetDir = BuildSettings.TARGET_DIR
-if (Environment.isDevelopmentMode() && targetDir != null) {
- appender("FULL_STACKTRACE", FileAppender) {
- file = "${targetDir}/stacktrace.log"
- append = true
- encoder(PatternLayoutEncoder) {
- pattern = "%level %logger - %msg%n"
- }
- }
- logger("StackTrace", ERROR, ['FULL_STACKTRACE'], false)
-
-}
-root(ERROR, ['STDOUT'])
-//tag::logger[]
-logger('example.grails', INFO, ['STDOUT'], false)
-//end::logger[]
\ No newline at end of file
diff --git a/complete/grails-app/conf/spring/resources.groovy b/complete/grails-app/conf/spring/resources.groovy
index da49cb6..975fdf8 100644
--- a/complete/grails-app/conf/spring/resources.groovy
+++ b/complete/grails-app/conf/spring/resources.groovy
@@ -1,10 +1,26 @@
-import example.grails.AwsSesMailService
-import example.grails.SendGridEmailService
+import example.AwsSesEmailService
+import example.SendGridEmailService
+import example.SmtpEmailService
+import grails.util.Environment
beans = {
- if ( System.getProperty('SENDGRID_FROM_EMAIL') && System.getProperty('SENDGRID_APIKEY') ) {
- emailService(SendGridEmailService)
- } else if (System.getProperty('AWS_REGION') && System.getProperty('AWS_SOURCE')) {
- emailService(AwsSesMailService)
+ if (Environment.current == Environment.TEST) {
+ return
}
+ //tag::emailServiceBeans[]
+ def provider = application.config.getProperty('email.provider', String, 'smtp')
+ switch (provider) {
+ case 'sendgrid':
+ emailService(SendGridEmailService)
+ break
+ case 'ses':
+ emailService(AwsSesEmailService)
+ break
+ case 'smtp':
+ emailService(SmtpEmailService)
+ break
+ default:
+ throw new IllegalArgumentException("Unsupported email.provider: ${provider}")
+ }
+ //end::emailServiceBeans[]
}
diff --git a/initial/grails-app/controllers/example/grails/ApplicationController.groovy b/complete/grails-app/controllers/example/ApplicationController.groovy
similarity index 74%
rename from initial/grails-app/controllers/example/grails/ApplicationController.groovy
rename to complete/grails-app/controllers/example/ApplicationController.groovy
index 5282b82..1196b06 100644
--- a/initial/grails-app/controllers/example/grails/ApplicationController.groovy
+++ b/complete/grails-app/controllers/example/ApplicationController.groovy
@@ -1,7 +1,8 @@
-package example.grails
+package example
import grails.core.GrailsApplication
-import grails.plugins.*
+import grails.plugins.GrailsPluginManager
+import grails.plugins.PluginManagerAware
class ApplicationController implements PluginManagerAware {
diff --git a/complete/grails-app/controllers/example/grails/EmailCmd.groovy b/complete/grails-app/controllers/example/EmailCmd.groovy
similarity index 96%
rename from complete/grails-app/controllers/example/grails/EmailCmd.groovy
rename to complete/grails-app/controllers/example/EmailCmd.groovy
index 67f2e5c..0675da4 100644
--- a/complete/grails-app/controllers/example/grails/EmailCmd.groovy
+++ b/complete/grails-app/controllers/example/EmailCmd.groovy
@@ -1,4 +1,4 @@
-package example.grails
+package example
import grails.compiler.GrailsCompileStatic
import grails.validation.Validateable
diff --git a/complete/grails-app/controllers/example/grails/MailController.groovy b/complete/grails-app/controllers/example/MailController.groovy
similarity index 56%
rename from complete/grails-app/controllers/example/grails/MailController.groovy
rename to complete/grails-app/controllers/example/MailController.groovy
index 5b73a5e..c858e88 100644
--- a/complete/grails-app/controllers/example/grails/MailController.groovy
+++ b/complete/grails-app/controllers/example/MailController.groovy
@@ -1,8 +1,10 @@
-package example.grails
+package example
import groovy.transform.CompileStatic
import groovy.util.logging.Slf4j
+import static org.springframework.http.HttpStatus.UNPROCESSABLE_ENTITY
+
@Slf4j
@CompileStatic
class MailController {
@@ -12,12 +14,12 @@ class MailController {
static allowedMethods = [send: 'POST']
def send(EmailCmd cmd) {
- if ( cmd.hasErrors() ) {
- render status: 422
+ if (cmd.hasErrors()) {
+ respond cmd.errors, view: '/application/errors', status: UNPROCESSABLE_ENTITY
return
}
- log.info '{}', cmd.toString()
+ log.info 'Sending mail'
emailService.send(cmd)
render status: 200
}
-}
\ No newline at end of file
+}
diff --git a/complete/grails-app/controllers/example/UrlMappings.groovy b/complete/grails-app/controllers/example/UrlMappings.groovy
new file mode 100644
index 0000000..b3c1368
--- /dev/null
+++ b/complete/grails-app/controllers/example/UrlMappings.groovy
@@ -0,0 +1,19 @@
+package example
+
+class UrlMappings {
+
+ static mappings = {
+ "/$controller/$action?/$id?(.$format)?"{
+ constraints {
+ }
+ }
+
+ //tag::urlmapping[]
+ post "/mail/send"(controller: 'mail', action: 'send')
+ //end::urlmapping[]
+
+ "/"(controller: 'application', action: 'index')
+ "500"(view: '/error')
+ "404"(view: '/notFound')
+ }
+}
diff --git a/complete/grails-app/controllers/example/grails/UrlMappings.groovy b/complete/grails-app/controllers/example/grails/UrlMappings.groovy
deleted file mode 100644
index 3a149e4..0000000
--- a/complete/grails-app/controllers/example/grails/UrlMappings.groovy
+++ /dev/null
@@ -1,21 +0,0 @@
-package example.grails
-
-class UrlMappings {
-
- static mappings = {
- delete "/$controller/$id(.$format)?"(action:"delete")
- get "/$controller(.$format)?"(action:"index")
- get "/$controller/$id(.$format)?"(action:"show")
- post "/$controller(.$format)?"(action:"save")
- put "/$controller/$id(.$format)?"(action:"update")
- patch "/$controller/$id(.$format)?"(action:"patch")
-
- //tag::urlmapping[]
- post "/mail/send"(controller: 'mail', action: 'send')
- //end::urlmapping[]
-
- "/"(controller: 'application', action:'index')
- "500"(view: '/error')
- "404"(view: '/notFound')
- }
-}
diff --git a/complete/grails-app/i18n/messages.properties b/complete/grails-app/i18n/messages.properties
index b045136..821c67d 100644
--- a/complete/grails-app/i18n/messages.properties
+++ b/complete/grails-app/i18n/messages.properties
@@ -1,56 +1,3 @@
-default.doesnt.match.message=Property [{0}] of class [{1}] with value [{2}] does not match the required pattern [{3}]
-default.invalid.url.message=Property [{0}] of class [{1}] with value [{2}] is not a valid URL
-default.invalid.creditCard.message=Property [{0}] of class [{1}] with value [{2}] is not a valid credit card number
-default.invalid.email.message=Property [{0}] of class [{1}] with value [{2}] is not a valid e-mail address
-default.invalid.range.message=Property [{0}] of class [{1}] with value [{2}] does not fall within the valid range from [{3}] to [{4}]
-default.invalid.size.message=Property [{0}] of class [{1}] with value [{2}] does not fall within the valid size range from [{3}] to [{4}]
-default.invalid.max.message=Property [{0}] of class [{1}] with value [{2}] exceeds maximum value [{3}]
-default.invalid.min.message=Property [{0}] of class [{1}] with value [{2}] is less than minimum value [{3}]
-default.invalid.max.size.message=Property [{0}] of class [{1}] with value [{2}] exceeds the maximum size of [{3}]
-default.invalid.min.size.message=Property [{0}] of class [{1}] with value [{2}] is less than the minimum size of [{3}]
-default.invalid.validator.message=Property [{0}] of class [{1}] with value [{2}] does not pass custom validation
-default.not.inlist.message=Property [{0}] of class [{1}] with value [{2}] is not contained within the list [{3}]
-default.blank.message=Property [{0}] of class [{1}] cannot be blank
-default.not.equal.message=Property [{0}] of class [{1}] with value [{2}] cannot equal [{3}]
-default.null.message=Property [{0}] of class [{1}] cannot be null
-default.not.unique.message=Property [{0}] of class [{1}] with value [{2}] must be unique
-
-default.paginate.prev=Previous
-default.paginate.next=Next
-default.boolean.true=True
-default.boolean.false=False
-default.date.format=yyyy-MM-dd HH:mm:ss z
-default.number.format=0
-
default.created.message={0} {1} created
default.updated.message={0} {1} updated
default.deleted.message={0} {1} deleted
-default.not.deleted.message={0} {1} could not be deleted
-default.not.found.message={0} not found with id {1}
-default.optimistic.locking.failure=Another user has updated this {0} while you were editing
-
-default.home.label=Home
-default.list.label={0} List
-default.add.label=Add {0}
-default.new.label=New {0}
-default.create.label=Create {0}
-default.show.label=Show {0}
-default.edit.label=Edit {0}
-
-default.button.create.label=Create
-default.button.edit.label=Edit
-default.button.update.label=Update
-default.button.delete.label=Delete
-default.button.delete.confirm.message=Are you sure?
-
-# Data binding errors. Use "typeMismatch.$className.$propertyName to customize (eg typeMismatch.Book.author)
-typeMismatch.java.net.URL=Property {0} must be a valid URL
-typeMismatch.java.net.URI=Property {0} must be a valid URI
-typeMismatch.java.util.Date=Property {0} must be a valid Date
-typeMismatch.java.lang.Double=Property {0} must be a valid number
-typeMismatch.java.lang.Integer=Property {0} must be a valid number
-typeMismatch.java.lang.Long=Property {0} must be a valid number
-typeMismatch.java.lang.Short=Property {0} must be a valid number
-typeMismatch.java.math.BigDecimal=Property {0} must be a valid number
-typeMismatch.java.math.BigInteger=Property {0} must be a valid number
-typeMismatch=Property {0} is type-mismatched
diff --git a/complete/grails-app/i18n/messages_cs.properties b/complete/grails-app/i18n/messages_cs.properties
deleted file mode 100644
index 7345531..0000000
--- a/complete/grails-app/i18n/messages_cs.properties
+++ /dev/null
@@ -1,55 +0,0 @@
-default.doesnt.match.message=Položka [{0}] třídy [{1}] o hodnotě [{2}] neodpovídá požadovanému vzoru [{3}]
-default.invalid.url.message=Položka [{0}] třídy [{1}] o hodnotě [{2}] není validní URL
-default.invalid.creditCard.message=Položka [{0}] třídy [{1}] o hodnotě [{2}] není validní číslo kreditní karty
-default.invalid.email.message=Položka [{0}] třídy [{1}] o hodnotě [{2}] není validní emailová adresa
-default.invalid.range.message=Položka [{0}] třídy [{1}] o hodnotě [{2}] není v povoleném rozmezí od [{3}] do [{4}]
-default.invalid.size.message=Položka [{0}] třídy [{1}] o hodnotě [{2}] není v povoleném rozmezí od [{3}] do [{4}]
-default.invalid.max.message=Položka [{0}] třídy [{1}] o hodnotě [{2}] překračuje maximální povolenou hodnotu [{3}]
-default.invalid.min.message=Položka [{0}] třídy [{1}] o hodnotě [{2}] je menší než minimální povolená hodnota [{3}]
-default.invalid.max.size.message=Položka [{0}] třídy [{1}] o hodnotě [{2}] překračuje maximální velikost [{3}]
-default.invalid.min.size.message=Položka [{0}] třídy [{1}] o hodnotě [{2}] je menší než minimální velikost [{3}]
-default.invalid.validator.message=Položka [{0}] třídy [{1}] o hodnotě [{2}] neprošla validací
-default.not.inlist.message=Položka [{0}] třídy [{1}] o hodnotě [{2}] není obsažena v seznamu [{3}]
-default.blank.message=Položka [{0}] třídy [{1}] nemůže být prázdná
-default.not.equal.message=Položka [{0}] třídy [{1}] o hodnotě [{2}] nemůže být stejná jako [{3}]
-default.null.message=Položka [{0}] třídy [{1}] nemůže být prázdná
-default.not.unique.message=Položka [{0}] třídy [{1}] o hodnotě [{2}] musí být unikátní
-
-default.paginate.prev=Předcházející
-default.paginate.next=Následující
-default.boolean.true=Pravda
-default.boolean.false=Nepravda
-default.date.format=dd. MM. yyyy HH:mm:ss z
-default.number.format=0
-
-default.created.message={0} {1} vytvořeno
-default.updated.message={0} {1} aktualizováno
-default.deleted.message={0} {1} smazáno
-default.not.deleted.message={0} {1} nelze smazat
-default.not.found.message={0} nenalezen s id {1}
-default.optimistic.locking.failure=Jiný uživatel aktualizoval záznam {0}, právě když byl vámi editován
-
-default.home.label=Domů
-default.list.label={0} Seznam
-default.add.label=Přidat {0}
-default.new.label=Nový {0}
-default.create.label=Vytvořit {0}
-default.show.label=Ukázat {0}
-default.edit.label=Editovat {0}
-
-default.button.create.label=Vytvoř
-default.button.edit.label=Edituj
-default.button.update.label=Aktualizuj
-default.button.delete.label=Smaž
-default.button.delete.confirm.message=Jste si jistý?
-
-# Data binding errors. Use "typeMismatch.$className.$propertyName to customize (eg typeMismatch.Book.author)
-typeMismatch.java.net.URL=Položka {0} musí být validní URL
-typeMismatch.java.net.URI=Položka {0} musí být validní URI
-typeMismatch.java.util.Date=Položka {0} musí být validní datum
-typeMismatch.java.lang.Double=Položka {0} musí být validní desetinné číslo
-typeMismatch.java.lang.Integer=Položka {0} musí být validní číslo
-typeMismatch.java.lang.Long=Položka {0} musí být validní číslo
-typeMismatch.java.lang.Short=Položka {0} musí být validní číslo
-typeMismatch.java.math.BigDecimal=Položka {0} musí být validní číslo
-typeMismatch.java.math.BigInteger=Položka {0} musí být validní číslo
diff --git a/complete/grails-app/i18n/messages_da.properties b/complete/grails-app/i18n/messages_da.properties
deleted file mode 100644
index 858b229..0000000
--- a/complete/grails-app/i18n/messages_da.properties
+++ /dev/null
@@ -1,56 +0,0 @@
-default.doesnt.match.message=Feltet [{0}] i klassen [{1}] som har værdien [{2}] overholder ikke mønsteret [{3}]
-default.invalid.url.message=Feltet [{0}] i klassen [{1}] som har værdien [{2}] er ikke en gyldig URL
-default.invalid.creditCard.message=Feltet [{0}] i klassen [{1}] som har værdien [{2}] er ikke et gyldigt kreditkortnummer
-default.invalid.email.message=Feltet [{0}] i klassen [{1}] som har værdien [{2}] er ikke en gyldig e-mail adresse
-default.invalid.range.message=Feltet [{0}] i klassen [{1}] som har værdien [{2}] ligger ikke inden for intervallet fra [{3}] til [{4}]
-default.invalid.size.message=Feltet [{0}] i klassen [{1}] som har værdien [{2}] ligger ikke inden for størrelsen fra [{3}] til [{4}]
-default.invalid.max.message=Feltet [{0}] i klassen [{1}] som har værdien [{2}] overstiger den maksimale værdi [{3}]
-default.invalid.min.message=Feltet [{0}] i klassen [{1}] som har værdien [{2}] er under den minimale værdi [{3}]
-default.invalid.max.size.message=Feltet [{0}] i klassen [{1}] som har værdien [{2}] overstiger den maksimale størrelse på [{3}]
-default.invalid.min.size.message=Feltet [{0}] i klassen [{1}] som har værdien [{2}] er under den minimale størrelse på [{3}]
-default.invalid.validator.message=Feltet [{0}] i klassen [{1}] som har værdien [{2}] overholder ikke den brugerdefinerede validering
-default.not.inlist.message=Feltet [{0}] i klassen [{1}] som har værdien [{2}] findes ikke i listen [{3}]
-default.blank.message=Feltet [{0}] i klassen [{1}] kan ikke være tom
-default.not.equal.message=Feltet [{0}] i klassen [{1}] som har værdien [{2}] må ikke være [{3}]
-default.null.message=Feltet [{0}] i klassen [{1}] kan ikke være null
-default.not.unique.message=Feltet [{0}] i klassen [{1}] som har værdien [{2}] skal være unik
-
-default.paginate.prev=Forrige
-default.paginate.next=Næste
-default.boolean.true=Sand
-default.boolean.false=Falsk
-default.date.format=yyyy-MM-dd HH:mm:ss z
-default.number.format=0
-
-default.created.message={0} {1} oprettet
-default.updated.message={0} {1} opdateret
-default.deleted.message={0} {1} slettet
-default.not.deleted.message={0} {1} kunne ikke slettes
-default.not.found.message={0} med id {1} er ikke fundet
-default.optimistic.locking.failure=En anden bruger har opdateret denne {0} imens du har lavet rettelser
-
-default.home.label=Hjem
-default.list.label={0} Liste
-default.add.label=Tilføj {0}
-default.new.label=Ny {0}
-default.create.label=Opret {0}
-default.show.label=Vis {0}
-default.edit.label=Ret {0}
-
-default.button.create.label=Opret
-default.button.edit.label=Ret
-default.button.update.label=Opdater
-default.button.delete.label=Slet
-default.button.delete.confirm.message=Er du sikker?
-
-# Databindingsfejl. Brug "typeMismatch.$className.$propertyName for at passe til en given klasse (f.eks typeMismatch.Book.author)
-typeMismatch.java.net.URL=Feltet {0} skal være en valid URL
-typeMismatch.java.net.URI=Feltet {0} skal være en valid URI
-typeMismatch.java.util.Date=Feltet {0} skal være en valid Dato
-typeMismatch.java.lang.Double=Feltet {0} skal være et valid tal
-typeMismatch.java.lang.Integer=Feltet {0} skal være et valid tal
-typeMismatch.java.lang.Long=Feltet {0} skal være et valid tal
-typeMismatch.java.lang.Short=Feltet {0} skal være et valid tal
-typeMismatch.java.math.BigDecimal=Feltet {0} skal være et valid tal
-typeMismatch.java.math.BigInteger=Feltet {0} skal være et valid tal
-
diff --git a/complete/grails-app/i18n/messages_de.properties b/complete/grails-app/i18n/messages_de.properties
deleted file mode 100644
index 0f7bfe9..0000000
--- a/complete/grails-app/i18n/messages_de.properties
+++ /dev/null
@@ -1,55 +0,0 @@
-default.doesnt.match.message=Die Eigenschaft [{0}] des Typs [{1}] mit dem Wert [{2}] entspricht nicht dem vorgegebenen Muster [{3}]
-default.invalid.url.message=Die Eigenschaft [{0}] des Typs [{1}] mit dem Wert [{2}] ist keine gültige URL
-default.invalid.creditCard.message=Das Die Eigenschaft [{0}] des Typs [{1}] mit dem Wert [{2}] ist keine gültige Kreditkartennummer
-default.invalid.email.message=Die Eigenschaft [{0}] des Typs [{1}] mit dem Wert [{2}] ist keine gültige E-Mail Adresse
-default.invalid.range.message=Die Eigenschaft [{0}] des Typs [{1}] mit dem Wert [{2}] ist nicht im Wertebereich von [{3}] bis [{4}]
-default.invalid.size.message=Die Eigenschaft [{0}] des Typs [{1}] mit dem Wert [{2}] ist nicht im Wertebereich von [{3}] bis [{4}]
-default.invalid.max.message=Die Eigenschaft [{0}] des Typs [{1}] mit dem Wert [{2}] ist größer als der Höchstwert von [{3}]
-default.invalid.min.message=Die Eigenschaft [{0}] des Typs [{1}] mit dem Wert [{2}] ist kleiner als der Mindestwert von [{3}]
-default.invalid.max.size.message=Die Eigenschaft [{0}] des Typs [{1}] mit dem Wert [{2}] übersteigt den Höchstwert von [{3}]
-default.invalid.min.size.message=Die Eigenschaft [{0}] des Typs [{1}] mit dem Wert [{2}] unterschreitet den Mindestwert von [{3}]
-default.invalid.validator.message=Die Eigenschaft [{0}] des Typs [{1}] mit dem Wert [{2}] ist ungültig
-default.not.inlist.message=Die Eigenschaft [{0}] des Typs [{1}] mit dem Wert [{2}] ist nicht in der Liste [{3}] enthalten.
-default.blank.message=Die Eigenschaft [{0}] des Typs [{1}] darf nicht leer sein
-default.not.equal.message=Die Eigenschaft [{0}] des Typs [{1}] mit dem Wert [{2}] darf nicht gleich [{3}] sein
-default.null.message=Die Eigenschaft [{0}] des Typs [{1}] darf nicht null sein
-default.not.unique.message=Die Eigenschaft [{0}] des Typs [{1}] mit dem Wert [{2}] darf nur einmal vorkommen
-
-default.paginate.prev=Vorherige
-default.paginate.next=Nächste
-default.boolean.true=Wahr
-default.boolean.false=Falsch
-default.date.format=dd.MM.yyyy HH:mm:ss z
-default.number.format=0
-
-default.created.message={0} {1} wurde angelegt
-default.updated.message={0} {1} wurde geändert
-default.deleted.message={0} {1} wurde gelöscht
-default.not.deleted.message={0} {1} konnte nicht gelöscht werden
-default.not.found.message={0} mit der id {1} wurde nicht gefunden
-default.optimistic.locking.failure=Ein anderer Benutzer hat das {0} Object geändert während Sie es bearbeitet haben
-
-default.home.label=Home
-default.list.label={0} Liste
-default.add.label={0} hinzufügen
-default.new.label={0} anlegen
-default.create.label={0} anlegen
-default.show.label={0} anzeigen
-default.edit.label={0} bearbeiten
-
-default.button.create.label=Anlegen
-default.button.edit.label=Bearbeiten
-default.button.update.label=Aktualisieren
-default.button.delete.label=Löschen
-default.button.delete.confirm.message=Sind Sie sicher?
-
-# Data binding errors. Use "typeMismatch.$className.$propertyName to customize (eg typeMismatch.Book.author)
-typeMismatch.java.net.URL=Die Eigenschaft {0} muss eine gültige URL sein
-typeMismatch.java.net.URI=Die Eigenschaft {0} muss eine gültige URI sein
-typeMismatch.java.util.Date=Die Eigenschaft {0} muss ein gültiges Datum sein
-typeMismatch.java.lang.Double=Die Eigenschaft {0} muss eine gültige Zahl sein
-typeMismatch.java.lang.Integer=Die Eigenschaft {0} muss eine gültige Zahl sein
-typeMismatch.java.lang.Long=Die Eigenschaft {0} muss eine gültige Zahl sein
-typeMismatch.java.lang.Short=Die Eigenschaft {0} muss eine gültige Zahl sein
-typeMismatch.java.math.BigDecimal=Die Eigenschaft {0} muss eine gültige Zahl sein
-typeMismatch.java.math.BigInteger=Die Eigenschaft {0} muss eine gültige Zahl sein
diff --git a/complete/grails-app/i18n/messages_es.properties b/complete/grails-app/i18n/messages_es.properties
deleted file mode 100644
index f0ede53..0000000
--- a/complete/grails-app/i18n/messages_es.properties
+++ /dev/null
@@ -1,55 +0,0 @@
-default.doesnt.match.message=La propiedad [{0}] de la clase [{1}] con valor [{2}] no corresponde al patrón [{3}]
-default.invalid.url.message=La propiedad [{0}] de la clase [{1}] con valor [{2}] no es una URL válida
-default.invalid.creditCard.message=La propiedad [{0}] de la clase [{1}] con valor [{2}] no es un número de tarjeta de crédito válida
-default.invalid.email.message=La propiedad [{0}] de la clase [{1}] con valor [{2}] no es una dirección de correo electrónico válida
-default.invalid.range.message=La propiedad [{0}] de la clase [{1}] con valor [{2}] no entra en el rango válido de [{3}] a [{4}]
-default.invalid.size.message=La propiedad [{0}] de la clase [{1}] con valor [{2}] no entra en el tamaño válido de [{3}] a [{4}]
-default.invalid.max.message=La propiedad [{0}] de la clase [{1}] con valor [{2}] excede el valor máximo [{3}]
-default.invalid.min.message=La propiedad [{0}] de la clase [{1}] con valor [{2}] es menos que el valor mínimo [{3}]
-default.invalid.max.size.message=La propiedad [{0}] de la clase [{1}] con valor [{2}] excede el tamaño máximo de [{3}]
-default.invalid.min.size.message=La propiedad [{0}] de la clase [{1}] con valor [{2}] es menor que el tamaño mínimo de [{3}]
-default.invalid.validator.message=La propiedad [{0}] de la clase [{1}] con valor [{2}] no es válido
-default.not.inlist.message=La propiedad [{0}] de la clase [{1}] con valor [{2}] no esta contenido dentro de la lista [{3}]
-default.blank.message=La propiedad [{0}] de la clase [{1}] no puede ser vacía
-default.not.equal.message=La propiedad [{0}] de la clase [{1}] con valor [{2}] no puede igualar a [{3}]
-default.null.message=La propiedad [{0}] de la clase [{1}] no puede ser nulo
-default.not.unique.message=La propiedad [{0}] de la clase [{1}] con valor [{2}] debe ser única
-
-default.paginate.prev=Anterior
-default.paginate.next=Siguiente
-default.boolean.true=Verdadero
-default.boolean.false=Falso
-default.date.format=yyyy-MM-dd HH:mm:ss z
-default.number.format=0
-
-default.created.message={0} {1} creado
-default.updated.message={0} {1} actualizado
-default.deleted.message={0} {1} eliminado
-default.not.deleted.message={0} {1} no puede eliminarse
-default.not.found.message=No se encuentra {0} con id {1}
-default.optimistic.locking.failure=Mientras usted editaba, otro usuario ha actualizado su {0}
-
-default.home.label=Principal
-default.list.label={0} Lista
-default.add.label=Agregar {0}
-default.new.label=Nuevo {0}
-default.create.label=Crear {0}
-default.show.label=Mostrar {0}
-default.edit.label=Editar {0}
-
-default.button.create.label=Crear
-default.button.edit.label=Editar
-default.button.update.label=Actualizar
-default.button.delete.label=Eliminar
-default.button.delete.confirm.message=¿Está usted seguro?
-
-# Data binding errors. Use "typeMismatch.$className.$propertyName to customize (eg typeMismatch.Book.author)
-typeMismatch.java.net.URL=La propiedad {0} debe ser una URL válida
-typeMismatch.java.net.URI=La propiedad {0} debe ser una URI válida
-typeMismatch.java.util.Date=La propiedad {0} debe ser una fecha válida
-typeMismatch.java.lang.Double=La propiedad {0} debe ser un número válido
-typeMismatch.java.lang.Integer=La propiedad {0} debe ser un número válido
-typeMismatch.java.lang.Long=La propiedad {0} debe ser un número válido
-typeMismatch.java.lang.Short=La propiedad {0} debe ser un número válido
-typeMismatch.java.math.BigDecimal=La propiedad {0} debe ser un número válido
-typeMismatch.java.math.BigInteger=La propiedad {0} debe ser un número válido
\ No newline at end of file
diff --git a/complete/grails-app/i18n/messages_fr.properties b/complete/grails-app/i18n/messages_fr.properties
deleted file mode 100644
index 5572164..0000000
--- a/complete/grails-app/i18n/messages_fr.properties
+++ /dev/null
@@ -1,19 +0,0 @@
-default.doesnt.match.message=La propriété [{0}] de la classe [{1}] avec la valeur [{2}] ne correspond pas au pattern [{3}]
-default.invalid.url.message=La propriété [{0}] de la classe [{1}] avec la valeur [{2}] n'est pas une URL valide
-default.invalid.creditCard.message=La propriété [{0}] de la classe [{1}] avec la valeur [{2}] n'est pas un numéro de carte de crédit valide
-default.invalid.email.message=La propriété [{0}] de la classe [{1}] avec la valeur [{2}] n'est pas une adresse e-mail valide
-default.invalid.range.message=La propriété [{0}] de la classe [{1}] avec la valeur [{2}] n'est pas contenue dans l'intervalle [{3}] à [{4}]
-default.invalid.size.message=La propriété [{0}] de la classe [{1}] avec la valeur [{2}] n'est pas contenue dans l'intervalle [{3}] à [{4}]
-default.invalid.max.message=La propriété [{0}] de la classe [{1}] avec la valeur [{2}] est supérieure à la valeur maximum [{3}]
-default.invalid.min.message=La propriété [{0}] de la classe [{1}] avec la valeur [{2}] est inférieure à la valeur minimum [{3}]
-default.invalid.max.size.message=La propriété [{0}] de la classe [{1}] avec la valeur [{2}] est supérieure à la valeur maximum [{3}]
-default.invalid.min.size.message=La propriété [{0}] de la classe [{1}] avec la valeur [{2}] est inférieure à la valeur minimum [{3}]
-default.invalid.validator.message=La propriété [{0}] de la classe [{1}] avec la valeur [{2}] n'est pas valide
-default.not.inlist.message=La propriété [{0}] de la classe [{1}] avec la valeur [{2}] ne fait pas partie de la liste [{3}]
-default.blank.message=La propriété [{0}] de la classe [{1}] ne peut pas être vide
-default.not.equal.message=La propriété [{0}] de la classe [{1}] avec la valeur [{2}] ne peut pas être égale à [{3}]
-default.null.message=La propriété [{0}] de la classe [{1}] ne peut pas être nulle
-default.not.unique.message=La propriété [{0}] de la classe [{1}] avec la valeur [{2}] doit être unique
-
-default.paginate.prev=Précédent
-default.paginate.next=Suivant
diff --git a/complete/grails-app/i18n/messages_it.properties b/complete/grails-app/i18n/messages_it.properties
deleted file mode 100644
index a90f1c7..0000000
--- a/complete/grails-app/i18n/messages_it.properties
+++ /dev/null
@@ -1,55 +0,0 @@
-default.doesnt.match.message=La proprietà [{0}] della classe [{1}] con valore [{2}] non corrisponde al pattern [{3}]
-default.invalid.url.message=La proprietà [{0}] della classe [{1}] con valore [{2}] non è un URL valido
-default.invalid.creditCard.message=La proprietà [{0}] della classe [{1}] con valore [{2}] non è un numero di carta di credito valido
-default.invalid.email.message=La proprietà [{0}] della classe [{1}] con valore [{2}] non è un indirizzo email valido
-default.invalid.range.message=La proprietà [{0}] della classe [{1}] con valore [{2}] non rientra nell'intervallo valido da [{3}] a [{4}]
-default.invalid.size.message=La proprietà [{0}] della classe [{1}] con valore [{2}] non rientra nell'intervallo di dimensioni valide da [{3}] a [{4}]
-default.invalid.max.message=La proprietà [{0}] della classe [{1}] con valore [{2}] è maggiore di [{3}]
-default.invalid.min.message=La proprietà [{0}] della classe [{1}] con valore [{2}] è minore di [{3}]
-default.invalid.max.size.message=La proprietà [{0}] della classe [{1}] con valore [{2}] è maggiore di [{3}]
-default.invalid.min.size.message=La proprietà [{0}] della classe [{1}] con valore [{2}] è minore di [{3}]
-default.invalid.validator.message=La proprietà [{0}] della classe [{1}] con valore [{2}] non è valida
-default.not.inlist.message=La proprietà [{0}] della classe [{1}] con valore [{2}] non è contenuta nella lista [{3}]
-default.blank.message=La proprietà [{0}] della classe [{1}] non può essere vuota
-default.not.equal.message=La proprietà [{0}] della classe [{1}] con valore [{2}] non può essere uguale a [{3}]
-default.null.message=La proprietà [{0}] della classe [{1}] non può essere null
-default.not.unique.message=La proprietà [{0}] della classe [{1}] con valore [{2}] deve essere unica
-
-default.paginate.prev=Precedente
-default.paginate.next=Successivo
-default.boolean.true=Vero
-default.boolean.false=Falso
-default.date.format=dd/MM/yyyy HH:mm:ss z
-default.number.format=0
-
-default.created.message={0} {1} creato
-default.updated.message={0} {1} aggiornato
-default.deleted.message={0} {1} eliminato
-default.not.deleted.message={0} {1} non può essere eliminato
-default.not.found.message={0} non trovato con id {1}
-default.optimistic.locking.failure=Un altro utente ha aggiornato questo {0} mentre si era in modifica
-
-default.home.label=Home
-default.list.label={0} Elenco
-default.add.label=Aggiungi {0}
-default.new.label=Nuovo {0}
-default.create.label=Crea {0}
-default.show.label=Mostra {0}
-default.edit.label=Modifica {0}
-
-default.button.create.label=Crea
-default.button.edit.label=Modifica
-default.button.update.label=Aggiorna
-default.button.delete.label=Elimina
-default.button.delete.confirm.message=Si è sicuri?
-
-# Data binding errors. Usa "typeMismatch.$className.$propertyName per la personalizzazione (es typeMismatch.Book.author)
-typeMismatch.java.net.URL=La proprietà {0} deve essere un URL valido
-typeMismatch.java.net.URI=La proprietà {0} deve essere un URI valido
-typeMismatch.java.util.Date=La proprietà {0} deve essere una data valida
-typeMismatch.java.lang.Double=La proprietà {0} deve essere un numero valido
-typeMismatch.java.lang.Integer=La proprietà {0} deve essere un numero valido
-typeMismatch.java.lang.Long=La proprietà {0} deve essere un numero valido
-typeMismatch.java.lang.Short=La proprietà {0} deve essere un numero valido
-typeMismatch.java.math.BigDecimal=La proprietà {0} deve essere un numero valido
-typeMismatch.java.math.BigInteger=La proprietà {0} deve essere un numero valido
diff --git a/complete/grails-app/i18n/messages_ja.properties b/complete/grails-app/i18n/messages_ja.properties
deleted file mode 100644
index d9e9b78..0000000
--- a/complete/grails-app/i18n/messages_ja.properties
+++ /dev/null
@@ -1,55 +0,0 @@
-default.doesnt.match.message=クラス[{1}]プロパティ[{0}]の値[{2}]は、[{3}]パターンと一致していません。
-default.invalid.url.message=クラス[{1}]プロパティ[{0}]の値[{2}]は、有効なURLではありません。
-default.invalid.creditCard.message=クラス[{1}]プロパティ[{0}]の値[{2}]は、有効なクレジットカード番号ではありません。
-default.invalid.email.message=クラス[{1}]プロパティ[{0}]の値[{2}]は、有効なメールアドレスではありません。
-default.invalid.range.message=クラス[{1}]プロパティ[{0}]の値[{2}]は、[{3}]から[{4}]範囲内を指定してください。
-default.invalid.size.message=クラス[{1}]プロパティ[{0}]の値[{2}]は、[{3}]から[{4}]以内を指定してください。
-default.invalid.max.message=クラス[{1}]プロパティ[{0}]の値[{2}]は、最大値[{3}]より大きいです。
-default.invalid.min.message=クラス[{1}]プロパティ[{0}]の値[{2}]は、最小値[{3}]より小さいです。
-default.invalid.max.size.message=クラス[{1}]プロパティ[{0}]の値[{2}]は、最大値[{3}]より大きいです。
-default.invalid.min.size.message=クラス[{1}]プロパティ[{0}]の値[{2}]は、最小値[{3}]より小さいです。
-default.invalid.validator.message=クラス[{1}]プロパティ[{0}]の値[{2}]は、カスタムバリデーションを通過できません。
-default.not.inlist.message=クラス[{1}]プロパティ[{0}]の値[{2}]は、[{3}]リスト内に存在しません。
-default.blank.message=[{1}]クラスのプロパティ[{0}]の空白は許可されません。
-default.not.equal.message=クラス[{1}]プロパティ[{0}]の値[{2}]に[{3}]は許可されません。
-default.null.message=[{1}]クラスのプロパティ[{0}]にnullは許可されません。
-default.not.unique.message=クラス[{1}]プロパティ[{0}]の値[{2}]は既に使用されています。
-
-default.paginate.prev=戻る
-default.paginate.next=次へ
-default.boolean.true=はい
-default.boolean.false=いいえ
-default.date.format=yyyy/MM/dd HH:mm:ss z
-default.number.format=0
-
-default.created.message={0}(id:{1})を作成しました。
-default.updated.message={0}(id:{1})を更新しました。
-default.deleted.message={0}(id:{1})を削除しました。
-default.not.deleted.message={0}(id:{1})は削除できませんでした。
-default.not.found.message={0}(id:{1})は見つかりませんでした。
-default.optimistic.locking.failure=この{0}は編集中に他のユーザによって先に更新されています。
-
-default.home.label=ホーム
-default.list.label={0}リスト
-default.add.label={0}を追加
-default.new.label={0}を新規作成
-default.create.label={0}を作成
-default.show.label={0}詳細
-default.edit.label={0}を編集
-
-default.button.create.label=作成
-default.button.edit.label=編集
-default.button.update.label=更新
-default.button.delete.label=削除
-default.button.delete.confirm.message=本当に削除してよろしいですか?
-
-# Data binding errors. Use "typeMismatch.$className.$propertyName to customize (eg typeMismatch.Book.author)
-typeMismatch.java.net.URL={0}は有効なURLでなければなりません。
-typeMismatch.java.net.URI={0}は有効なURIでなければなりません。
-typeMismatch.java.util.Date={0}は有効な日付でなければなりません。
-typeMismatch.java.lang.Double={0}は有効な数値でなければなりません。
-typeMismatch.java.lang.Integer={0}は有効な数値でなければなりません。
-typeMismatch.java.lang.Long={0}は有効な数値でなければなりません。
-typeMismatch.java.lang.Short={0}は有効な数値でなければなりません。
-typeMismatch.java.math.BigDecimal={0}は有効な数値でなければなりません。
-typeMismatch.java.math.BigInteger={0}は有効な数値でなければなりません。
diff --git a/complete/grails-app/i18n/messages_nb.properties b/complete/grails-app/i18n/messages_nb.properties
deleted file mode 100644
index 47a8a1a..0000000
--- a/complete/grails-app/i18n/messages_nb.properties
+++ /dev/null
@@ -1,56 +0,0 @@
-default.doesnt.match.message=Feltet [{0}] i klassen [{1}] med verdien [{2}] overholder ikke mønsteret [{3}]
-default.invalid.url.message=Feltet [{0}] i klassen [{1}] med verdien [{2}] er ikke en gyldig URL
-default.invalid.creditCard.message=Feltet [{0}] i klassen [{1}] med verdien [{2}] er ikke et gyldig kredittkortnummer
-default.invalid.email.message=Feltet [{0}] i klassen [{1}] med verdien [{2}] er ikke en gyldig epostadresse
-default.invalid.range.message=Feltet [{0}] i klassen [{1}] med verdien [{2}] er ikke innenfor intervallet [{3}] til [{4}]
-default.invalid.size.message=Feltet [{0}] i klassen [{1}] med verdien [{2}] er ikke innenfor intervallet [{3}] til [{4}]
-default.invalid.max.message=Feltet [{0}] i klassen [{1}] med verdien [{2}] overstiger maksimumsverdien på [{3}]
-default.invalid.min.message=Feltet [{0}] i klassen [{1}] med verdien [{2}] er under minimumsverdien på [{3}]
-default.invalid.max.size.message=Feltet [{0}] i klassen [{1}] med verdien [{2}] overstiger maksimumslengden på [{3}]
-default.invalid.min.size.message=Feltet [{0}] i klassen [{1}] med verdien [{2}] er kortere enn minimumslengden på [{3}]
-default.invalid.validator.message=Feltet [{0}] i klassen [{1}] med verdien [{2}] overholder ikke den brukerdefinerte valideringen
-default.not.inlist.message=Feltet [{0}] i klassen [{1}] med verdien [{2}] finnes ikke i listen [{3}]
-default.blank.message=Feltet [{0}] i klassen [{1}] kan ikke være tom
-default.not.equal.message=Feltet [{0}] i klassen [{1}] med verdien [{2}] kan ikke være [{3}]
-default.null.message=Feltet [{0}] i klassen [{1}] kan ikke være null
-default.not.unique.message=Feltet [{0}] i klassen [{1}] med verdien [{2}] må være unik
-
-default.paginate.prev=Forrige
-default.paginate.next=Neste
-default.boolean.true=Ja
-default.boolean.false=Nei
-default.date.format=dd.MM.yyyy HH:mm:ss z
-default.number.format=0
-
-default.created.message={0} {1} opprettet
-default.updated.message={0} {1} oppdatert
-default.deleted.message={0} {1} slettet
-default.not.deleted.message={0} {1} kunne ikke slettes
-default.not.found.message={0} med id {1} ble ikke funnet
-default.optimistic.locking.failure=En annen bruker har oppdatert denne {0} mens du redigerte
-
-default.home.label=Hjem
-default.list.label={0}liste
-default.add.label=Legg til {0}
-default.new.label=Ny {0}
-default.create.label=Opprett {0}
-default.show.label=Vis {0}
-default.edit.label=Endre {0}
-
-default.button.create.label=Opprett
-default.button.edit.label=Endre
-default.button.update.label=Oppdater
-default.button.delete.label=Slett
-default.button.delete.confirm.message=Er du sikker?
-
-# Data binding errors. Use "typeMismatch.$className.$propertyName to customize (eg typeMismatch.Book.author)
-typeMismatch.java.net.URL=Feltet {0} må være en gyldig URL
-typeMismatch.java.net.URI=Feltet {0} må være en gyldig URI
-typeMismatch.java.util.Date=Feltet {0} må være en gyldig dato
-typeMismatch.java.lang.Double=Feltet {0} må være et gyldig tall
-typeMismatch.java.lang.Integer=Feltet {0} må være et gyldig heltall
-typeMismatch.java.lang.Long=Feltet {0} må være et gyldig heltall
-typeMismatch.java.lang.Short=Feltet {0} må være et gyldig heltall
-typeMismatch.java.math.BigDecimal=Feltet {0} må være et gyldig tall
-typeMismatch.java.math.BigInteger=Feltet {0} må være et gyldig heltall
-
diff --git a/complete/grails-app/i18n/messages_nl.properties b/complete/grails-app/i18n/messages_nl.properties
deleted file mode 100644
index cd5cc94..0000000
--- a/complete/grails-app/i18n/messages_nl.properties
+++ /dev/null
@@ -1,55 +0,0 @@
-default.doesnt.match.message=Attribuut [{0}] van entiteit [{1}] met waarde [{2}] komt niet overeen met het vereiste patroon [{3}]
-default.invalid.url.message=Attribuut [{0}] van entiteit [{1}] met waarde [{2}] is geen geldige URL
-default.invalid.creditCard.message=Attribuut [{0}] van entiteit [{1}] met waarde [{2}] is geen geldig credit card nummer
-default.invalid.email.message=Attribuut [{0}] van entiteit [{1}] met waarde [{2}] is geen geldig e-mailadres
-default.invalid.range.message=Attribuut [{0}] van entiteit [{1}] met waarde [{2}] valt niet in de geldige waardenreeks van [{3}] tot [{4}]
-default.invalid.size.message=Attribuut [{0}] van entiteit [{1}] met waarde [{2}] valt niet in de geldige grootte van [{3}] tot [{4}]
-default.invalid.max.message=Attribuut [{0}] van entiteit [{1}] met waarde [{2}] overschrijdt de maximumwaarde [{3}]
-default.invalid.min.message=Attribuut [{0}] van entiteit [{1}] met waarde [{2}] is minder dan de minimumwaarde [{3}]
-default.invalid.max.size.message=Attribuut [{0}] van entiteit [{1}] met waarde [{2}] overschrijdt de maximumgrootte van [{3}]
-default.invalid.min.size.message=Attribuut [{0}] van entiteit [{1}] met waarde [{2}] is minder dan minimumgrootte van [{3}]
-default.invalid.validator.message=Attribuut [{0}] van entiteit [{1}] met waarde [{2}] is niet geldig
-default.not.inlist.message=Attribuut [{0}] van entiteit [{1}] met waarde [{2}] komt niet voor in de lijst [{3}]
-default.blank.message=Attribuut [{0}] van entiteit [{1}] mag niet leeg zijn
-default.not.equal.message=Attribuut [{0}] van entiteit [{1}] met waarde [{2}] mag niet gelijk zijn aan [{3}]
-default.null.message=Attribuut [{0}] van entiteit [{1}] mag niet leeg zijn
-default.not.unique.message=Attribuut [{0}] van entiteit [{1}] met waarde [{2}] moet uniek zijn
-
-default.paginate.prev=Vorige
-default.paginate.next=Volgende
-default.boolean.true=Ja
-default.boolean.false=Nee
-default.date.format=dd-MM-yyyy HH:mm:ss z
-default.number.format=0
-
-default.created.message={0} {1} ingevoerd
-default.updated.message={0} {1} gewijzigd
-default.deleted.message={0} {1} verwijderd
-default.not.deleted.message={0} {1} kon niet worden verwijderd
-default.not.found.message={0} met id {1} kon niet worden gevonden
-default.optimistic.locking.failure=Een andere gebruiker heeft deze {0} al gewijzigd
-
-default.home.label=Home
-default.list.label={0} Overzicht
-default.add.label=Toevoegen {0}
-default.new.label=Invoeren {0}
-default.create.label=Invoeren {0}
-default.show.label=Details {0}
-default.edit.label=Wijzigen {0}
-
-default.button.create.label=Invoeren
-default.button.edit.label=Wijzigen
-default.button.update.label=Opslaan
-default.button.delete.label=Verwijderen
-default.button.delete.confirm.message=Weet je het zeker?
-
-# Data binding errors. Use "typeMismatch.$className.$propertyName to customize (eg typeMismatch.Book.author)
-typeMismatch.java.net.URL=Attribuut {0} is geen geldige URL
-typeMismatch.java.net.URI=Attribuut {0} is geen geldige URI
-typeMismatch.java.util.Date=Attribuut {0} is geen geldige datum
-typeMismatch.java.lang.Double=Attribuut {0} is geen geldig nummer
-typeMismatch.java.lang.Integer=Attribuut {0} is geen geldig nummer
-typeMismatch.java.lang.Long=Attribuut {0} is geen geldig nummer
-typeMismatch.java.lang.Short=Attribuut {0} is geen geldig nummer
-typeMismatch.java.math.BigDecimal=Attribuut {0} is geen geldig nummer
-typeMismatch.java.math.BigInteger=Attribuut {0} is geen geldig nummer
diff --git a/complete/grails-app/i18n/messages_pl.properties b/complete/grails-app/i18n/messages_pl.properties
deleted file mode 100644
index 959296c..0000000
--- a/complete/grails-app/i18n/messages_pl.properties
+++ /dev/null
@@ -1,59 +0,0 @@
-#
-# Translated by Matthias Hryniszak - padcom@gmail.com
-#
-
-default.doesnt.match.message=Właściwość [{0}] klasy [{1}] o wartości [{2}] nie pasuje do wymaganego wzorca [{3}]
-default.invalid.url.message=Właściwość [{0}] klasy [{1}] o wartości [{2}] jest niepoprawnym adresem URL
-default.invalid.creditCard.message=Właściwość [{0}] klasy [{1}] with value [{2}] nie jest poprawnym numerem karty kredytowej
-default.invalid.email.message=Właściwość [{0}] klasy [{1}] o wartości [{2}] nie jest poprawnym adresem e-mail
-default.invalid.range.message=Właściwość [{0}] klasy [{1}] o wartości [{2}] nie zawiera się zakładanym zakresie od [{3}] do [{4}]
-default.invalid.size.message=Właściwość [{0}] klasy [{1}] o wartości [{2}] nie zawiera się w zakładanym zakresie rozmiarów od [{3}] do [{4}]
-default.invalid.max.message=Właściwość [{0}] klasy [{1}] o wartości [{2}] przekracza maksymalną wartość [{3}]
-default.invalid.min.message=Właściwość [{0}] klasy [{1}] o wartości [{2}] jest mniejsza niż minimalna wartość [{3}]
-default.invalid.max.size.message=Właściwość [{0}] klasy [{1}] o wartości [{2}] przekracza maksymalny rozmiar [{3}]
-default.invalid.min.size.message=Właściwość [{0}] klasy [{1}] o wartości [{2}] jest mniejsza niż minimalny rozmiar [{3}]
-default.invalid.validator.message=Właściwość [{0}] klasy [{1}] o wartości [{2}] nie spełnia założonych niestandardowych warunków
-default.not.inlist.message=Właściwość [{0}] klasy [{1}] o wartości [{2}] nie zawiera się w liście [{3}]
-default.blank.message=Właściwość [{0}] klasy [{1}] nie może być pusta
-default.not.equal.message=Właściwość [{0}] klasy [{1}] o wartości [{2}] nie może równać się [{3}]
-default.null.message=Właściwość [{0}] klasy [{1}] nie może być null
-default.not.unique.message=Właściwość [{0}] klasy [{1}] o wartości [{2}] musi być unikalna
-
-default.paginate.prev=Poprzedni
-default.paginate.next=Następny
-default.boolean.true=Prawda
-default.boolean.false=Fałsz
-default.date.format=yyyy-MM-dd HH:mm:ss z
-default.number.format=0
-
-default.created.message=Utworzono {0} {1}
-default.updated.message=Zaktualizowano {0} {1}
-default.deleted.message=Usunięto {0} {1}
-default.not.deleted.message={0} {1} nie mógł zostać usunięty
-default.not.found.message=Nie znaleziono {0} o id {1}
-default.optimistic.locking.failure=Inny użytkownik zaktualizował ten obiekt {0} w trakcie twoich zmian
-
-default.home.label=Strona domowa
-default.list.label=Lista {0}
-default.add.label=Dodaj {0}
-default.new.label=Utwórz {0}
-default.create.label=Utwórz {0}
-default.show.label=Pokaż {0}
-default.edit.label=Edytuj {0}
-
-default.button.create.label=Utwórz
-default.button.edit.label=Edytuj
-default.button.update.label=Zaktualizuj
-default.button.delete.label=Usuń
-default.button.delete.confirm.message=Czy jesteś pewien?
-
-# Data binding errors. Use "typeMismatch.$className.$propertyName to customize (eg typeMismatch.Book.author)
-typeMismatch.java.net.URL=Właściwość {0} musi być poprawnym adresem URL
-typeMismatch.java.net.URI=Właściwość {0} musi być poprawnym adresem URI
-typeMismatch.java.util.Date=Właściwość {0} musi być poprawną datą
-typeMismatch.java.lang.Double=Właściwość {0} musi być poprawnyą liczbą
-typeMismatch.java.lang.Integer=Właściwość {0} musi być poprawnyą liczbą
-typeMismatch.java.lang.Long=Właściwość {0} musi być poprawnyą liczbą
-typeMismatch.java.lang.Short=Właściwość {0} musi być poprawnyą liczbą
-typeMismatch.java.math.BigDecimal=Właściwość {0} musi być poprawnyą liczbą
-typeMismatch.java.math.BigInteger=Właściwość {0} musi być poprawnyą liczbą
diff --git a/complete/grails-app/i18n/messages_pt_BR.properties b/complete/grails-app/i18n/messages_pt_BR.properties
deleted file mode 100644
index b5044e2..0000000
--- a/complete/grails-app/i18n/messages_pt_BR.properties
+++ /dev/null
@@ -1,59 +0,0 @@
-#
-# Translated by Lucas Teixeira - lucastex@gmail.com
-#
-
-default.doesnt.match.message=O campo [{0}] da classe [{1}] com o valor [{2}] não atende ao padrão definido [{3}]
-default.invalid.url.message=O campo [{0}] da classe [{1}] com o valor [{2}] não é uma URL válida
-default.invalid.creditCard.message=O campo [{0}] da classe [{1}] com o valor [{2}] não é um número válido de cartão de crédito
-default.invalid.email.message=O campo [{0}] da classe [{1}] com o valor [{2}] não é um endereço de email válido.
-default.invalid.range.message=O campo [{0}] da classe [{1}] com o valor [{2}] não está entre a faixa de valores válida de [{3}] até [{4}]
-default.invalid.size.message=O campo [{0}] da classe [{1}] com o valor [{2}] não está na faixa de tamanho válida de [{3}] até [{4}]
-default.invalid.max.message=O campo [{0}] da classe [{1}] com o valor [{2}] ultrapassa o valor máximo [{3}]
-default.invalid.min.message=O campo [{0}] da classe [{1}] com o valor [{2}] não atinge o valor mínimo [{3}]
-default.invalid.max.size.message=O campo [{0}] da classe [{1}] com o valor [{2}] ultrapassa o tamanho máximo de [{3}]
-default.invalid.min.size.message=O campo [{0}] da classe [{1}] com o valor [{2}] não atinge o tamanho mínimo de [{3}]
-default.invalid.validator.message=O campo [{0}] da classe [{1}] com o valor [{2}] não passou na validação
-default.not.inlist.message=O campo [{0}] da classe [{1}] com o valor [{2}] não é um valor dentre os permitidos na lista [{3}]
-default.blank.message=O campo [{0}] da classe [{1}] não pode ficar em branco
-default.not.equal.message=O campo [{0}] da classe [{1}] com o valor [{2}] não pode ser igual a [{3}]
-default.null.message=O campo [{0}] da classe [{1}] não pode ser vazio
-default.not.unique.message=O campo [{0}] da classe [{1}] com o valor [{2}] deve ser único
-
-default.paginate.prev=Anterior
-default.paginate.next=Próximo
-default.boolean.true=Sim
-default.boolean.false=Não
-default.date.format=dd/MM/yyyy HH:mm:ss z
-default.number.format=0
-
-default.created.message={0} {1} criado
-default.updated.message={0} {1} atualizado
-default.deleted.message={0} {1} removido
-default.not.deleted.message={0} {1} não pode ser removido
-default.not.found.message={0} não foi encontrado com o id {1}
-default.optimistic.locking.failure=Outro usuário atualizou este [{0}] enquanto você tentou salvá-lo
-
-default.home.label=Principal
-default.list.label={0} Listagem
-default.add.label=Adicionar {0}
-default.new.label=Novo {0}
-default.create.label=Criar {0}
-default.show.label=Ver {0}
-default.edit.label=Editar {0}
-
-default.button.create.label=Criar
-default.button.edit.label=Editar
-default.button.update.label=Alterar
-default.button.delete.label=Remover
-default.button.delete.confirm.message=Tem certeza?
-
-# Mensagens de erro em atribuição de valores. Use "typeMismatch.$className.$propertyName" para customizar (eg typeMismatch.Book.author)
-typeMismatch.java.net.URL=O campo {0} deve ser uma URL válida.
-typeMismatch.java.net.URI=O campo {0} deve ser uma URI válida.
-typeMismatch.java.util.Date=O campo {0} deve ser uma data válida
-typeMismatch.java.lang.Double=O campo {0} deve ser um número válido.
-typeMismatch.java.lang.Integer=O campo {0} deve ser um número válido.
-typeMismatch.java.lang.Long=O campo {0} deve ser um número válido.
-typeMismatch.java.lang.Short=O campo {0} deve ser um número válido.
-typeMismatch.java.math.BigDecimal=O campo {0} deve ser um número válido.
-typeMismatch.java.math.BigInteger=O campo {0} deve ser um número válido.
diff --git a/complete/grails-app/i18n/messages_pt_PT.properties b/complete/grails-app/i18n/messages_pt_PT.properties
deleted file mode 100644
index a386070..0000000
--- a/complete/grails-app/i18n/messages_pt_PT.properties
+++ /dev/null
@@ -1,34 +0,0 @@
-#
-# translation by miguel.ping@gmail.com, based on pt_BR translation by Lucas Teixeira - lucastex@gmail.com
-#
-
-default.doesnt.match.message=O campo [{0}] da classe [{1}] com o valor [{2}] não corresponde ao padrão definido [{3}]
-default.invalid.url.message=O campo [{0}] da classe [{1}] com o valor [{2}] não é um URL válido
-default.invalid.creditCard.message=O campo [{0}] da classe [{1}] com o valor [{2}] não é um número válido de cartão de crédito
-default.invalid.email.message=O campo [{0}] da classe [{1}] com o valor [{2}] não é um endereço de email válido.
-default.invalid.range.message=O campo [{0}] da classe [{1}] com o valor [{2}] não está dentro dos limites de valores válidos de [{3}] a [{4}]
-default.invalid.size.message=O campo [{0}] da classe [{1}] com o valor [{2}] está fora dos limites de tamanho válido de [{3}] a [{4}]
-default.invalid.max.message=O campo [{0}] da classe [{1}] com o valor [{2}] ultrapassa o valor máximo [{3}]
-default.invalid.min.message=O campo [{0}] da classe [{1}] com o valor [{2}] não atinge o valor mínimo [{3}]
-default.invalid.max.size.message=O campo [{0}] da classe [{1}] com o valor [{2}] ultrapassa o tamanho máximo de [{3}]
-default.invalid.min.size.message=O campo [{0}] da classe [{1}] com o valor [{2}] não atinge o tamanho mínimo de [{3}]
-default.invalid.validator.message=O campo [{0}] da classe [{1}] com o valor [{2}] não passou na validação
-default.not.inlist.message=O campo [{0}] da classe [{1}] com o valor [{2}] não se encontra nos valores permitidos da lista [{3}]
-default.blank.message=O campo [{0}] da classe [{1}] não pode ser vazio
-default.not.equal.message=O campo [{0}] da classe [{1}] com o valor [{2}] não pode ser igual a [{3}]
-default.null.message=O campo [{0}] da classe [{1}] não pode ser vazio
-default.not.unique.message=O campo [{0}] da classe [{1}] com o valor [{2}] deve ser único
-
-default.paginate.prev=Anterior
-default.paginate.next=Próximo
-
-# Mensagens de erro em atribuição de valores. Use "typeMismatch.$className.$propertyName" para personalizar(eg typeMismatch.Book.author)
-typeMismatch.java.net.URL=O campo {0} deve ser um URL válido.
-typeMismatch.java.net.URI=O campo {0} deve ser um URI válido.
-typeMismatch.java.util.Date=O campo {0} deve ser uma data válida
-typeMismatch.java.lang.Double=O campo {0} deve ser um número válido.
-typeMismatch.java.lang.Integer=O campo {0} deve ser um número válido.
-typeMismatch.java.lang.Long=O campo {0} deve ser um número valido.
-typeMismatch.java.lang.Short=O campo {0} deve ser um número válido.
-typeMismatch.java.math.BigDecimal=O campo {0} deve ser um número válido.
-typeMismatch.java.math.BigInteger=O campo {0} deve ser um número válido.
diff --git a/complete/grails-app/i18n/messages_ru.properties b/complete/grails-app/i18n/messages_ru.properties
deleted file mode 100644
index 53a4bdc..0000000
--- a/complete/grails-app/i18n/messages_ru.properties
+++ /dev/null
@@ -1,31 +0,0 @@
-default.doesnt.match.message=Значение [{2}] поля [{0}] класса [{1}] не соответствует образцу [{3}]
-default.invalid.url.message=Значение [{2}] поля [{0}] класса [{1}] не является допустимым URL-адресом
-default.invalid.creditCard.message=Значение [{2}] поля [{0}] класса [{1}] не является допустимым номером кредитной карты
-default.invalid.email.message=Значение [{2}] поля [{0}] класса [{1}] не является допустимым e-mail адресом
-default.invalid.range.message=Значение [{2}] поля [{0}] класса [{1}] не попадает в допустимый интервал от [{3}] до [{4}]
-default.invalid.size.message=Размер поля [{0}] класса [{1}] (значение: [{2}]) не попадает в допустимый интервал от [{3}] до [{4}]
-default.invalid.max.message=Значение [{2}] поля [{0}] класса [{1}] больше чем максимально допустимое значение [{3}]
-default.invalid.min.message=Значение [{2}] поля [{0}] класса [{1}] меньше чем минимально допустимое значение [{3}]
-default.invalid.max.size.message=Размер поля [{0}] класса [{1}] (значение: [{2}]) больше чем максимально допустимый размер [{3}]
-default.invalid.min.size.message=Размер поля [{0}] класса [{1}] (значение: [{2}]) меньше чем минимально допустимый размер [{3}]
-default.invalid.validator.message=Значение [{2}] поля [{0}] класса [{1}] не допустимо
-default.not.inlist.message=Значение [{2}] поля [{0}] класса [{1}] не попадает в список допустимых значений [{3}]
-default.blank.message=Поле [{0}] класса [{1}] не может быть пустым
-default.not.equal.message=Значение [{2}] поля [{0}] класса [{1}] не может быть равно [{3}]
-default.null.message=Поле [{0}] класса [{1}] не может иметь значение null
-default.not.unique.message=Значение [{2}] поля [{0}] класса [{1}] должно быть уникальным
-
-default.paginate.prev=Предыдушая страница
-default.paginate.next=Следующая страница
-
-# Ошибки при присвоении данных. Для точной настройки для полей классов используйте
-# формат "typeMismatch.$className.$propertyName" (например, typeMismatch.Book.author)
-typeMismatch.java.net.URL=Значение поля {0} не является допустимым URL
-typeMismatch.java.net.URI=Значение поля {0} не является допустимым URI
-typeMismatch.java.util.Date=Значение поля {0} не является допустимой датой
-typeMismatch.java.lang.Double=Значение поля {0} не является допустимым числом
-typeMismatch.java.lang.Integer=Значение поля {0} не является допустимым числом
-typeMismatch.java.lang.Long=Значение поля {0} не является допустимым числом
-typeMismatch.java.lang.Short=Значение поля {0} не является допустимым числом
-typeMismatch.java.math.BigDecimal=Значение поля {0} не является допустимым числом
-typeMismatch.java.math.BigInteger=Значение поля {0} не является допустимым числом
diff --git a/complete/grails-app/i18n/messages_sk.properties b/complete/grails-app/i18n/messages_sk.properties
deleted file mode 100644
index 73ee0dc..0000000
--- a/complete/grails-app/i18n/messages_sk.properties
+++ /dev/null
@@ -1,56 +0,0 @@
-default.doesnt.match.message=Položka [{0}] triedy [{1}] s hodnotou [{2}] nezodpovedá požadovanému formátu [{3}]
-default.invalid.url.message=Položka [{0}] triedy [{1}] s hodnotou [{2}] nie je platná URL adresa
-default.invalid.creditCard.message=Položka [{0}] triedy [{1}] s hodnotou [{2}] nie je platné číslo kreditnej karty
-default.invalid.email.message=Položka [{0}] triedy [{1}] s hodnotou [{2}] nie je platná emailová adresa
-default.invalid.range.message=Položka [{0}] triedy [{1}] s hodnotou [{2}] nie je v povolenom rozmedzí od [{3}] do [{4}]
-default.invalid.size.message=Položka [{0}] triedy [{1}] s hodnotou [{2}] nie je v povolenom rozmedzí od [{3}] do [{4}]
-default.invalid.max.message=Položka [{0}] triedy [{1}] s hodnotou [{2}] prekračuje maximálnu povolenú hodnotu [{3}]
-default.invalid.min.message=Položka [{0}] triedy [{1}] s hodnotou [{2}] je menšia ako minimálna povolená hodnota [{3}]
-default.invalid.max.size.message=Položka [{0}] triedy [{1}] s hodnotou [{2}] prekračuje maximálnu veľkosť [{3}]
-default.invalid.min.size.message=Položka [{0}] triedy [{1}] s hodnotou [{2}] je menšia ako minimálna veľkosť [{3}]
-default.invalid.validator.message=Položka [{0}] triedy [{1}] s hodnotou [{2}] neprešla validáciou
-default.not.inlist.message=Položka [{0}] triedy [{1}] s hodnotou [{2}] nie je obsiahnutá v zozname [{3}]
-default.blank.message=Položka [{0}] triedy [{1}] nemôže byť prázdna
-default.not.equal.message=Položka [{0}] triedy [{1}] s hodnotou [{2}] nemôže byť rovnaká ako [{3}]
-default.null.message=Položka [{0}] triedy [{1}] nemôže byť prázdna
-default.not.unique.message=Položka [{0}] triedy [{1}] s hodnotou [{2}] musí byť unikátna
-
-default.paginate.prev=Predchádzajúce
-default.paginate.next=Nasledujúce
-default.boolean.true=Pravda
-default.boolean.false=Nepravda
-default.date.format=dd. MM. yyyy HH:mm:ss z
-default.number.format=0
-
-default.created.message={0} {1} vytvorené
-default.updated.message={0} {1} aktualizované
-default.deleted.message={0} {1} vymazané
-default.not.deleted.message={0} {1} nemožno zmazať
-default.not.found.message={0} nenájdené s id {1}
-default.optimistic.locking.failure=Iný používateľ aktualizoval záznam {0}, práve keď bol vami editovaný
-
-default.home.label=Domov
-default.list.label={0} Zoznam
-default.add.label=Pridať {0}
-default.new.label=Nový {0}
-default.create.label=Vytvoriť {0}
-default.show.label=Ukázať {0}
-default.edit.label=Editovať {0}
-
-default.button.create.label=Vytvor
-default.button.edit.label=Edituj
-default.button.update.label=Aktualizuj
-default.button.delete.label=Zmaž
-default.button.delete.confirm.message=Ste si istý?
-
-# Data binding errors. Use "typeMismatch.$className.$propertyName to customize (eg typeMismatch.Book.author)
-typeMismatch.java.net.URL=Položka {0} musí byť platná URL adresa
-typeMismatch.java.net.URI=Položka {0} musí byť platná URI adresa
-typeMismatch.java.util.Date=Položka {0} musí byť platný dátum
-typeMismatch.java.lang.Double=Položka {0} musí byť desatinné číslo
-typeMismatch.java.lang.Integer=Položka {0} musí byť celé číslo
-typeMismatch.java.lang.Long=Položka {0} musí byť celé číslo
-typeMismatch.java.lang.Short=Položka {0} musí byť celé číslo
-typeMismatch.java.math.BigDecimal=Položka {0} musí byť desatinné číslo
-typeMismatch.java.math.BigInteger=Položka {0} musí byť celé číslo
-typeMismatch=Položka {0} má nezhodný typ
diff --git a/complete/grails-app/i18n/messages_sv.properties b/complete/grails-app/i18n/messages_sv.properties
deleted file mode 100644
index 61899d7..0000000
--- a/complete/grails-app/i18n/messages_sv.properties
+++ /dev/null
@@ -1,55 +0,0 @@
-default.doesnt.match.message=Attributet [{0}] för klassen [{1}] med värde [{2}] matchar inte mot uttrycket [{3}]
-default.invalid.url.message=Attributet [{0}] för klassen [{1}] med värde [{2}] är inte en giltig URL
-default.invalid.creditCard.message=Attributet [{0}] för klassen [{1}] med värde [{2}] är inte ett giltigt kreditkortsnummer
-default.invalid.email.message=Attributet [{0}] för klassen [{1}] med värde [{2}] är inte en giltig e-postadress
-default.invalid.range.message=Attributet [{0}] för klassen [{1}] med värde [{2}] är inte inom intervallet [{3}] till [{4}]
-default.invalid.size.message=Attributet [{0}] för klassen [{1}] med värde [{2}] har en storlek som inte är inom [{3}] till [{4}]
-default.invalid.max.message=Attributet [{0}] för klassen [{1}] med värde [{2}] överskrider maxvärdet [{3}]
-default.invalid.min.message=Attributet [{0}] för klassen [{1}] med värde [{2}] är mindre än minimivärdet [{3}]
-default.invalid.max.size.message=Attributet [{0}] för klassen [{1}] med värde [{2}] överskrider maxstorleken [{3}]
-default.invalid.min.size.message=Attributet [{0}] för klassen [{1}] med värde [{2}] är mindre än minimistorleken [{3}]
-default.invalid.validator.message=Attributet [{0}] för klassen [{1}] med värde [{2}] är inte giltigt enligt anpassad regel
-default.not.inlist.message=Attributet [{0}] för klassen [{1}] med värde [{2}] är inte giltigt, måste vara ett av [{3}]
-default.blank.message=Attributet [{0}] för klassen [{1}] får inte vara tomt
-default.not.equal.message=Attributet [{0}] för klassen [{1}] med värde [{2}] får inte vara lika med [{3}]
-default.null.message=Attributet [{0}] för klassen [{1}] får inte vara tomt
-default.not.unique.message=Attributet [{0}] för klassen [{1}] med värde [{2}] måste vara unikt
-
-default.paginate.prev=Föregående
-default.paginate.next=Nästa
-default.boolean.true=Sant
-default.boolean.false=Falskt
-default.date.format=yyyy-MM-dd HH:mm:ss z
-default.number.format=0
-
-default.created.message={0} {1} skapades
-default.updated.message={0} {1} uppdaterades
-default.deleted.message={0} {1} borttagen
-default.not.deleted.message={0} {1} kunde inte tas bort
-default.not.found.message={0} med id {1} kunde inte hittas
-default.optimistic.locking.failure=En annan användare har uppdaterat det här {0} objektet medan du redigerade det
-
-default.home.label=Hem
-default.list.label= {0} - Lista
-default.add.label=Lägg till {0}
-default.new.label=Skapa {0}
-default.create.label=Skapa {0}
-default.show.label=Visa {0}
-default.edit.label=Ändra {0}
-
-default.button.create.label=Skapa
-default.button.edit.label=Ändra
-default.button.update.label=Uppdatera
-default.button.delete.label=Ta bort
-default.button.delete.confirm.message=Är du säker?
-
-# Data binding errors. Use "typeMismatch.$className.$propertyName to customize (eg typeMismatch.Book.author)
-typeMismatch.java.net.URL=Värdet för {0} måste vara en giltig URL
-typeMismatch.java.net.URI=Värdet för {0} måste vara en giltig URI
-typeMismatch.java.util.Date=Värdet {0} måste vara ett giltigt datum
-typeMismatch.java.lang.Double=Värdet {0} måste vara ett giltigt nummer
-typeMismatch.java.lang.Integer=Värdet {0} måste vara ett giltigt heltal
-typeMismatch.java.lang.Long=Värdet {0} måste vara ett giltigt heltal
-typeMismatch.java.lang.Short=Värdet {0} måste vara ett giltigt heltal
-typeMismatch.java.math.BigDecimal=Värdet {0} måste vara ett giltigt nummer
-typeMismatch.java.math.BigInteger=Värdet {0} måste vara ett giltigt heltal
\ No newline at end of file
diff --git a/complete/grails-app/i18n/messages_th.properties b/complete/grails-app/i18n/messages_th.properties
deleted file mode 100644
index fd0dbb6..0000000
--- a/complete/grails-app/i18n/messages_th.properties
+++ /dev/null
@@ -1,55 +0,0 @@
-default.doesnt.match.message=คุณสมบัติ [{0}] ของคลาส [{1}] ซึ่งมีค่าเป็น [{2}] ไม่ถูกต้องตามรูปแบบที่กำหนดไว้ใน [{3}]
-default.invalid.url.message=คุณสมบัติ [{0}] ของคลาส [{1}] ซึ่งมีค่าเป็น [{2}] ไม่ถูกต้องตามรูปแบบ URL
-default.invalid.creditCard.message=คุณสมบัติ [{0}] ของคลาส [{1}] ซึ่งมีค่าเป็น [{2}] ไม่ถูกต้องตามรูปแบบหมายเลขบัตรเครดิต
-default.invalid.email.message=คุณสมบัติ [{0}] ของคลาส [{1}] ซึ่งมีค่าเป็น [{2}] ไม่ถูกต้องตามรูปแบบอีเมล์
-default.invalid.range.message=คุณสมบัติ [{0}] ของคลาส [{1}] ซึ่งมีค่าเป็น [{2}] ไม่ได้มีค่าที่ถูกต้องในช่วงจาก [{3}] ถึง [{4}]
-default.invalid.size.message=คุณสมบัติ [{0}] ของคลาส [{1}] ซึ่งมีค่าเป็น [{2}] ไม่ได้มีขนาดที่ถูกต้องในช่วงจาก [{3}] ถึง [{4}]
-default.invalid.max.message=คุณสมบัติ [{0}] ของคลาส [{1}] ซึ่งมีค่าเป็น [{2}] มีค่าเกิดกว่าค่ามากสุด [{3}]
-default.invalid.min.message=คุณสมบัติ [{0}] ของคลาส [{1}] ซึ่งมีค่าเป็น [{2}] มีค่าน้อยกว่าค่าต่ำสุด [{3}]
-default.invalid.max.size.message=คุณสมบัติ [{0}] ของคลาส [{1}] ซึ่งมีค่าเป็น [{2}] มีขนาดเกินกว่าขนาดมากสุดของ [{3}]
-default.invalid.min.size.message=คุณสมบัติ [{0}] ของคลาส [{1}] ซึ่งมีค่าเป็น [{2}] มีขนาดต่ำกว่าขนาดต่ำสุดของ [{3}]
-default.invalid.validator.message=คุณสมบัติ [{0}] ของคลาส [{1}] ซึ่งมีค่าเป็น [{2}] ไม่ผ่านการทวนสอบค่าที่ตั้งขึ้น
-default.not.inlist.message=คุณสมบัติ [{0}] ของคลาส [{1}] ซึ่งมีค่าเป็น [{2}] ไม่ได้อยู่ในรายการต่อไปนี้ [{3}]
-default.blank.message=คุณสมบัติ [{0}] ของคลาส [{1}] ไม่สามารถเป็นค่าว่างได้
-default.not.equal.message=คุณสมบัติ [{0}] ของคลาส [{1}] ซึ่งมีค่าเป็น [{2}] ไม่สามารถเท่ากับ [{3}] ได้
-default.null.message=คุณสมบัติ [{0}] ของคลาส [{1}] ไม่สามารถเป็น null ได้
-default.not.unique.message=คุณสมบัติ [{0}] ของคลาส [{1}] ซึ่งมีค่าเป็น [{2}] จะต้องไม่ซ้ำ (unique)
-
-default.paginate.prev=ก่อนหน้า
-default.paginate.next=ถัดไป
-default.boolean.true=จริง
-default.boolean.false=เท็จ
-default.date.format=dd-MM-yyyy HH:mm:ss z
-default.number.format=0
-
-default.created.message=สร้าง {0} {1} เรียบร้อยแล้ว
-default.updated.message=ปรับปรุง {0} {1} เรียบร้อยแล้ว
-default.deleted.message=ลบ {0} {1} เรียบร้อยแล้ว
-default.not.deleted.message=ไม่สามารถลบ {0} {1}
-default.not.found.message=ไม่พบ {0} ด้วย id {1} นี้
-default.optimistic.locking.failure=มีผู้ใช้ท่านอื่นปรับปรุง {0} ขณะที่คุณกำลังแก้ไขข้อมูลอยู่
-
-default.home.label=หน้าแรก
-default.list.label=รายการ {0}
-default.add.label=เพิ่ม {0}
-default.new.label=สร้าง {0} ใหม่
-default.create.label=สร้าง {0}
-default.show.label=แสดง {0}
-default.edit.label=แก้ไข {0}
-
-default.button.create.label=สร้าง
-default.button.edit.label=แก้ไข
-default.button.update.label=ปรับปรุง
-default.button.delete.label=ลบ
-default.button.delete.confirm.message=คุณแน่ใจหรือไม่ ?
-
-# Data binding errors. Use "typeMismatch.$className.$propertyName to customize (eg typeMismatch.Book.author)
-typeMismatch.java.net.URL=คุณสมบัติ '{0}' จะต้องเป็นค่า URL ที่ถูกต้อง
-typeMismatch.java.net.URI=คุณสมบัติ '{0}' จะต้องเป็นค่า URI ที่ถูกต้อง
-typeMismatch.java.util.Date=คุณสมบัติ '{0}' จะต้องมีค่าเป็นวันที่
-typeMismatch.java.lang.Double=คุณสมบัติ '{0}' จะต้องมีค่าเป็นจำนวนประเภท Double
-typeMismatch.java.lang.Integer=คุณสมบัติ '{0}' จะต้องมีค่าเป็นจำนวนประเภท Integer
-typeMismatch.java.lang.Long=คุณสมบัติ '{0}' จะต้องมีค่าเป็นจำนวนประเภท Long
-typeMismatch.java.lang.Short=คุณสมบัติ '{0}' จะต้องมีค่าเป็นจำนวนประเภท Short
-typeMismatch.java.math.BigDecimal=คุณสมบัติ '{0}' จะต้องมีค่าเป็นจำนวนประเภท BigDecimal
-typeMismatch.java.math.BigInteger=คุณสมบัติ '{0}' จะต้องมีค่าเป็นจำนวนประเภท BigInteger
diff --git a/complete/grails-app/i18n/messages_zh_CN.properties b/complete/grails-app/i18n/messages_zh_CN.properties
deleted file mode 100644
index b89bc93..0000000
--- a/complete/grails-app/i18n/messages_zh_CN.properties
+++ /dev/null
@@ -1,18 +0,0 @@
-default.blank.message=[{1}]\u7C7B\u7684\u5C5E\u6027[{0}]\u4E0D\u80FD\u4E3A\u7A7A
-default.doesnt.match.message=[{1}]\u7C7B\u7684\u5C5E\u6027[{0}]\u7684\u503C[{2}]\u4E0E\u5B9A\u4E49\u7684\u6A21\u5F0F [{3}]\u4E0D\u5339\u914D
-default.invalid.creditCard.message=[{1}]\u7C7B\u7684\u5C5E\u6027[{0}]\u7684\u503C[{2}]\u4E0D\u662F\u4E00\u4E2A\u6709\u6548\u7684\u4FE1\u7528\u5361\u53F7
-default.invalid.email.message=[{1}]\u7C7B\u7684\u5C5E\u6027[{0}]\u7684\u503C[{2}]\u4E0D\u662F\u4E00\u4E2A\u5408\u6CD5\u7684\u7535\u5B50\u90AE\u4EF6\u5730\u5740
-default.invalid.max.message=[{1}]\u7C7B\u7684\u5C5E\u6027[{0}]\u7684\u503C[{2}]\u6BD4\u6700\u5927\u503C [{3}]\u8FD8\u5927
-default.invalid.max.size.message=[{1}]\u7C7B\u7684\u5C5E\u6027[{0}]\u7684\u503C[{2}]\u7684\u5927\u5C0F\u6BD4\u6700\u5927\u503C [{3}]\u8FD8\u5927
-default.invalid.min.message=[{1}]\u7C7B\u7684\u5C5E\u6027[{0}]\u7684\u503C[{2}]\u6BD4\u6700\u5C0F\u503C [{3}]\u8FD8\u5C0F
-default.invalid.min.size.message=[{1}]\u7C7B\u7684\u5C5E\u6027[{0}]\u7684\u503C[{2}]\u7684\u5927\u5C0F\u6BD4\u6700\u5C0F\u503C [{3}]\u8FD8\u5C0F
-default.invalid.range.message=[{1}]\u7C7B\u7684\u5C5E\u6027[{0}]\u7684\u503C[{2}]\u4E0D\u5728\u5408\u6CD5\u7684\u8303\u56F4\u5185( [{3}] \uFF5E [{4}] )
-default.invalid.size.message=[{1}]\u7C7B\u7684\u5C5E\u6027[{0}]\u7684\u503C[{2}]\u7684\u5927\u5C0F\u4E0D\u5728\u5408\u6CD5\u7684\u8303\u56F4\u5185( [{3}] \uFF5E [{4}] )
-default.invalid.url.message=[{1}]\u7C7B\u7684\u5C5E\u6027[{0}]\u7684\u503C[{2}]\u4E0D\u662F\u4E00\u4E2A\u5408\u6CD5\u7684URL
-default.invalid.validator.message=[{1}]\u7C7B\u7684\u5C5E\u6027[{0}]\u7684\u503C[{2}]\u672A\u80FD\u901A\u8FC7\u81EA\u5B9A\u4E49\u7684\u9A8C\u8BC1
-default.not.equal.message=[{1}]\u7C7B\u7684\u5C5E\u6027[{0}]\u7684\u503C[{2}]\u4E0E[{3}]\u4E0D\u76F8\u7B49
-default.not.inlist.message=[{1}]\u7C7B\u7684\u5C5E\u6027[{0}]\u7684\u503C[{2}]\u4E0D\u5728\u5217\u8868\u7684\u53D6\u503C\u8303\u56F4\u5185
-default.not.unique.message=[{1}]\u7C7B\u7684\u5C5E\u6027[{0}]\u7684\u503C[{2}]\u5FC5\u987B\u662F\u552F\u4E00\u7684
-default.null.message=[{1}]\u7C7B\u7684\u5C5E\u6027[{0}]\u4E0D\u80FD\u4E3Anull
-default.paginate.next=\u4E0B\u9875
-default.paginate.prev=\u4E0A\u9875
diff --git a/complete/grails-app/init/example/grails/Application.groovy b/complete/grails-app/init/example/Application.groovy
similarity index 85%
rename from complete/grails-app/init/example/grails/Application.groovy
rename to complete/grails-app/init/example/Application.groovy
index 34fb380..1e735f8 100644
--- a/complete/grails-app/init/example/grails/Application.groovy
+++ b/complete/grails-app/init/example/Application.groovy
@@ -1,14 +1,14 @@
-package example.grails
+package example
import grails.boot.GrailsApp
import grails.boot.config.GrailsAutoConfiguration
import groovy.transform.CompileStatic
import org.springframework.context.annotation.ComponentScan
-@ComponentScan('example.grails')
+@ComponentScan('example')
@CompileStatic
class Application extends GrailsAutoConfiguration {
static void main(String[] args) {
GrailsApp.run(Application, args)
}
-}
\ No newline at end of file
+}
diff --git a/complete/grails-app/init/example/grails/BootStrap.groovy b/complete/grails-app/init/example/BootStrap.groovy
similarity index 53%
rename from complete/grails-app/init/example/grails/BootStrap.groovy
rename to complete/grails-app/init/example/BootStrap.groovy
index 4542bd1..fcfa6be 100644
--- a/complete/grails-app/init/example/grails/BootStrap.groovy
+++ b/complete/grails-app/init/example/BootStrap.groovy
@@ -1,13 +1,10 @@
-package example.grails
+package example
-import groovy.transform.CompileStatic
-
-@CompileStatic
class BootStrap {
def init = { servletContext ->
-
}
+
def destroy = {
}
}
diff --git a/complete/grails-app/services/example/SmtpEmailService.groovy b/complete/grails-app/services/example/SmtpEmailService.groovy
new file mode 100644
index 0000000..2556127
--- /dev/null
+++ b/complete/grails-app/services/example/SmtpEmailService.groovy
@@ -0,0 +1,36 @@
+package example
+
+import grails.plugins.mail.MailService
+import groovy.transform.CompileStatic
+import groovy.util.logging.Slf4j
+
+@Slf4j
+@CompileStatic
+class SmtpEmailService implements EmailService { // <1>
+
+ MailService mailService
+
+ @Override
+ void send(Email email) {
+ mailService.sendMail {
+ to email.recipient
+ subject email.subject
+ if (email.textBody) {
+ text email.textBody
+ }
+ if (email.htmlBody) {
+ html email.htmlBody
+ }
+ if (email.replyTo) {
+ replyTo email.replyTo
+ }
+ if (email.cc) {
+ cc email.cc as String[]
+ }
+ if (email.bcc) {
+ bcc email.bcc as String[]
+ }
+ }
+ log.debug('SMTP message handed off to MailService for {}', email.recipient)
+ }
+}
diff --git a/complete/grails-app/views/application/_errors.gson b/complete/grails-app/views/application/_errors.gson
new file mode 100644
index 0000000..15a6e3c
--- /dev/null
+++ b/complete/grails-app/views/application/_errors.gson
@@ -0,0 +1,42 @@
+import org.springframework.validation.*
+
+/**
+ * Renders validation errors according to vnd.error: https://github.com/blongden/vnd.error
+ */
+model {
+ Errors errors
+}
+
+response.status UNPROCESSABLE_ENTITY
+
+json {
+ Errors errorsObject = (Errors)this.errors
+ def allErrors = errorsObject.allErrors
+ int errorCount = allErrors.size()
+ def resourcePath = g.createLink(controller: params.controller, action: params.action)
+ def resourceLink = g.link(controller: params.controller, action: params.action, absolute: true)
+ if(errorCount == 1) {
+ def error = allErrors.iterator().next()
+ message messageSource.getMessage(error, locale)
+ path resourcePath
+ _links {
+ self {
+ href resourceLink
+ }
+ }
+ }
+ else {
+ total errorCount
+ _embedded {
+ errors(allErrors) { ObjectError error ->
+ message messageSource.getMessage(error, locale)
+ path resourcePath
+ _links {
+ self {
+ href resourceLink
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/complete/grails-app/views/application/_object.gson b/complete/grails-app/views/application/_object.gson
new file mode 100644
index 0000000..114568f
--- /dev/null
+++ b/complete/grails-app/views/application/_object.gson
@@ -0,0 +1,5 @@
+import groovy.transform.*
+
+@Field Object object
+
+json g.render(object)
diff --git a/complete/grails-app/views/application/index.gson b/complete/grails-app/views/application/index.gson
index 61f9643..ba4a6dc 100644
--- a/complete/grails-app/views/application/index.gson
+++ b/complete/grails-app/views/application/index.gson
@@ -1,33 +1,10 @@
-import grails.core.*
-import grails.util.*
-import grails.plugins.*
-import org.grails.core.artefact.*
+import example.ApplicationController
model {
- GrailsApplication grailsApplication
- GrailsPluginManager pluginManager
+ ApplicationController controller
}
json {
- message "Welcome to Grails!"
- environment Environment.current.name
- appversion grailsApplication.metadata.getApplicationVersion()
- grailsversion GrailsUtil.grailsVersion
- appprofile grailsApplication.config.getProperty('grails.profile')
- groovyversion GroovySystem.getVersion()
- jvmversion System.getProperty('java.version')
- reloadingagentenabled Environment.reloadingAgentEnabled
- artefacts (
- controllers: grailsApplication.getArtefactInfo(ControllerArtefactHandler.TYPE).classesByName.size(),
- domains: grailsApplication.getArtefactInfo(DomainClassArtefactHandler.TYPE).classesByName.size(),
- services: grailsApplication.getArtefactInfo(ServiceArtefactHandler.TYPE).classesByName.size()
- )
- controllers grailsApplication.getArtefacts(ControllerArtefactHandler.TYPE), { GrailsClass c ->
- name c.fullName
- logicalPropertyName c.logicalPropertyName
- }
- plugins pluginManager.allPlugins, { GrailsPlugin plugin ->
- name plugin.name
- version plugin.version
- }
+ message controller.grailsApplication.config.getProperty('info.app.name', String, 'example')
+ grailsVersion controller.grailsApplication.metadata.getGrailsVersion()
}
diff --git a/complete/grails-app/views/error.gson b/complete/grails-app/views/error.gson
index 9448828..4d4488b 100644
--- a/complete/grails-app/views/error.gson
+++ b/complete/grails-app/views/error.gson
@@ -1,6 +1,8 @@
-response.status 500
+model {
+ Throwable exception
+}
-json {
- message "Internal server error"
- error 500
-}
\ No newline at end of file
+json {
+ message 'Internal server error'
+ status 500
+}
diff --git a/complete/grails-app/views/home/index.gson b/complete/grails-app/views/home/index.gson
deleted file mode 100644
index ed0be68..0000000
--- a/complete/grails-app/views/home/index.gson
+++ /dev/null
@@ -1,7 +0,0 @@
-model {
- String name
-}
-
-json {
- username name
-}
\ No newline at end of file
diff --git a/complete/grails-app/views/notFound.gson b/complete/grails-app/views/notFound.gson
index 1a710b2..1415787 100644
--- a/complete/grails-app/views/notFound.gson
+++ b/complete/grails-app/views/notFound.gson
@@ -1,6 +1,4 @@
-response.status 404
-
json {
- message "Not Found"
- error 404
-}
\ No newline at end of file
+ message 'Not found'
+ status 404
+}
diff --git a/complete/grails-forge-cli.yml b/complete/grails-forge-cli.yml
new file mode 100644
index 0000000..c820319
--- /dev/null
+++ b/complete/grails-forge-cli.yml
@@ -0,0 +1,5 @@
+# grails create-app example --profile=rest-api --inplace -f views-json,spock
+applicationType: rest-api
+defaultPackage: example
+name: example
+grailsVersion: 8.0.0-SNAPSHOT
diff --git a/complete/grails-wrapper.jar b/complete/grails-wrapper.jar
deleted file mode 100644
index bc85146..0000000
Binary files a/complete/grails-wrapper.jar and /dev/null differ
diff --git a/complete/grailsw b/complete/grailsw
deleted file mode 100755
index 8d0cc12..0000000
--- a/complete/grailsw
+++ /dev/null
@@ -1,152 +0,0 @@
-#!/usr/bin/env bash
-
-##############################################################################
-##
-## Grails start up script for UN*X
-##
-##############################################################################
-
-# Add default JVM options here. You can also use JAVA_OPTS and GRAILS_OPTS to pass JVM options to this script.
-DEFAULT_JVM_OPTS='"-XX:+TieredCompilation" "-XX:TieredStopAtLevel=1" "-XX:CICompilerCount=3"'
-
-
-# Use the maximum available, or set MAX_FD != -1 to use that value.
-MAX_FD="maximum"
-
-warn ( ) {
- echo "$*"
-}
-
-die ( ) {
- echo
- echo "$*"
- echo
- exit 1
-}
-
-# OS specific support (must be 'true' or 'false').
-cygwin=false
-msys=false
-darwin=false
-case "`uname`" in
- CYGWIN* )
- cygwin=true
- ;;
- Darwin* )
- darwin=true
- ;;
- MINGW* )
- msys=true
- ;;
-esac
-
-# Attempt to set APP_HOME
-# Resolve links: $0 may be a link
-PRG="$0"
-# Need this for relative symlinks.
-while [ -h "$PRG" ] ; do
- ls=`ls -ld "$PRG"`
- link=`expr "$ls" : '.*-> \(.*\)$'`
- if expr "$link" : '/.*' > /dev/null; then
- PRG="$link"
- else
- PRG=`dirname "$PRG"`"/$link"
- fi
-done
-SAVED="`pwd`"
-cd "`dirname \"$PRG\"`/" >/dev/null
-APP_HOME="`pwd -P`"
-cd "$SAVED" >/dev/null
-
-JAR_PATH=$APP_HOME/grails-wrapper.jar
-
-# Determine the Java command to use to start the JVM.
-if [ -n "$JAVA_HOME" ] ; then
- if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
- # IBM's JDK on AIX uses strange locations for the executables
- JAVACMD="$JAVA_HOME/jre/sh/java"
- else
- JAVACMD="$JAVA_HOME/bin/java"
- fi
- if [ ! -x "$JAVACMD" ] ; then
- die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
-
-Please set the JAVA_HOME variable in your environment to match the
-location of your Java installation."
- fi
-else
- JAVACMD="java"
- which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
-
-Please set the JAVA_HOME variable in your environment to match the
-location of your Java installation."
-fi
-
-# Increase the maximum file descriptors if we can.
-if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
- MAX_FD_LIMIT=`ulimit -H -n`
- if [ $? -eq 0 ] ; then
- if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
- MAX_FD="$MAX_FD_LIMIT"
- fi
- ulimit -n $MAX_FD
- if [ $? -ne 0 ] ; then
- warn "Could not set maximum file descriptor limit: $MAX_FD"
- fi
- else
- warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
- fi
-fi
-
-# For Cygwin, switch paths to Windows format before running java
-if $cygwin ; then
- APP_HOME=`cygpath --path --mixed "$APP_HOME"`
- JAVACMD=`cygpath --unix "$JAVACMD"`
- JAR_PATH=`cygpath --path --mixed "$JAR_PATH"`
-
- # We build the pattern for arguments to be converted via cygpath
- ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
- SEP=""
- for dir in $ROOTDIRSRAW ; do
- ROOTDIRS="$ROOTDIRS$SEP$dir"
- SEP="|"
- done
- OURCYGPATTERN="(^($ROOTDIRS))"
- # Add a user-defined pattern to the cygpath arguments
- if [ "$GRAILS_CYGPATTERN" != "" ] ; then
- OURCYGPATTERN="$OURCYGPATTERN|($GRAILS_CYGPATTERN)"
- fi
- # Now convert the arguments - kludge to limit ourselves to /bin/sh
- i=0
- for arg in "$@" ; do
- CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
- CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
-
- if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
- eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
- else
- eval `echo args$i`="\"$arg\""
- fi
- i=$((i+1))
- done
- case $i in
- (0) set -- ;;
- (1) set -- "$args0" ;;
- (2) set -- "$args0" "$args1" ;;
- (3) set -- "$args0" "$args1" "$args2" ;;
- (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
- (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
- (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
- (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
- (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
- (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
- esac
-fi
-
-# Split up the JVM_OPTS And GRAILS_OPTS values into an array, following the shell quoting and substitution rules
-function splitJvmOpts() {
- JVM_OPTS=("$@")
-}
-eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRAILS_OPTS
-
-exec "$JAVACMD" -jar "${JVM_OPTS[@]}" "$JAR_PATH" "$@"
diff --git a/complete/grailsw.bat b/complete/grailsw.bat
deleted file mode 100755
index c48c384..0000000
--- a/complete/grailsw.bat
+++ /dev/null
@@ -1,89 +0,0 @@
-@if "%DEBUG%" == "" @echo off
-@rem ##########################################################################
-@rem
-@rem Grails startup script for Windows
-@rem
-@rem ##########################################################################
-
-@rem Set local scope for the variables with windows NT shell
-if "%OS%"=="Windows_NT" setlocal
-
-@rem Add default JVM options here. You can also use JAVA_OPTS and GRAILS_OPTS to pass JVM options to this script.
-set DEFAULT_JVM_OPTS="-XX:+TieredCompilation" "-XX:TieredStopAtLevel=1" "-XX:CICompilerCount=3"
-
-set DIRNAME=%~dp0
-if "%DIRNAME%" == "" set DIRNAME=.
-set APP_BASE_NAME=%~n0
-set APP_HOME=%DIRNAME%
-
-@rem Find java.exe
-if defined JAVA_HOME goto findJavaFromJavaHome
-
-set JAVA_EXE=java.exe
-%JAVA_EXE% -version >NUL 2>&1
-if "%ERRORLEVEL%" == "0" goto init
-
-echo.
-echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
-
-goto fail
-
-:findJavaFromJavaHome
-set JAVA_HOME=%JAVA_HOME:"=%
-set JAVA_EXE=%JAVA_HOME%/bin/java.exe
-
-if exist "%JAVA_EXE%" goto init
-
-echo.
-echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
-
-goto fail
-
-:init
-@rem Get command-line arguments, handling Windowz variants
-
-if not "%OS%" == "Windows_NT" goto win9xME_args
-if "%@eval[2+2]" == "4" goto 4NT_args
-
-:win9xME_args
-@rem Slurp the command line arguments.
-set CMD_LINE_ARGS=
-set _SKIP=2
-
-:win9xME_args_slurp
-if "x%~1" == "x" goto execute
-
-set CMD_LINE_ARGS=%*
-goto execute
-
-:4NT_args
-@rem Get arguments from the 4NT Shell from JP Software
-set CMD_LINE_ARGS=%$
-
-:execute
-@rem Setup the command line
-set JAR_PATH=%APP_HOME%/grails-wrapper.jar
-
-@rem Execute Grails
-"%JAVA_EXE%" -jar %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRAILS_OPTS% %JAR_PATH% %CMD_LINE_ARGS%
-
-:end
-@rem End local scope for the variables with windows NT shell
-if "%ERRORLEVEL%"=="0" goto mainEnd
-
-:fail
-rem Set variable GRAILS_EXIT_CONSOLE if you need the _script_ return code instead of
-rem the _cmd.exe /c_ return code!
-if not "" == "%GRAILS_EXIT_CONSOLE%" exit 1
-exit /b 1
-
-:mainEnd
-if "%OS%"=="Windows_NT" endlocal
-
-:omega
diff --git a/complete/src/integration-test/groovy/example/MailControllerSpec.groovy b/complete/src/integration-test/groovy/example/MailControllerSpec.groovy
new file mode 100644
index 0000000..9c4efb6
--- /dev/null
+++ b/complete/src/integration-test/groovy/example/MailControllerSpec.groovy
@@ -0,0 +1,108 @@
+package example
+
+import grails.testing.mixin.integration.Integration
+import groovy.json.JsonSlurper
+import org.springframework.beans.factory.annotation.Autowired
+import org.springframework.boot.test.context.TestConfiguration
+import org.springframework.context.annotation.Bean
+import org.springframework.context.annotation.Primary
+import org.springframework.http.HttpStatus
+import org.springframework.http.ResponseEntity
+import org.springframework.web.client.HttpClientErrorException
+import org.springframework.web.client.RestTemplate
+import spock.lang.Specification
+import spock.mock.DetachedMockFactory
+
+@Integration
+class MailControllerSpec extends Specification {
+
+ @Autowired
+ EmailService emailService
+
+ void '/mail/send invokes email service with expected message'() {
+ when:
+ RestTemplate client = new RestTemplate()
+ ResponseEntity