Skip to content

Commit 589fa0f

Browse files
committed
Merge branch '3.1.x' into 3.2.x
2 parents e580196 + 0221b73 commit 589fa0f

File tree

7 files changed

+115
-74
lines changed

7 files changed

+115
-74
lines changed

grails-plugin-gsp/src/main/groovy/org/grails/plugins/web/taglib/FormTagLib.groovy

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1071,7 +1071,7 @@ class FormTagLib implements ApplicationContextAware, InitializingBean, TagLibrar
10711071
}
10721072
}
10731073
keyValue = processFormFieldValueIfNecessary(selectName, "${keyValue}","option")
1074-
writer << "value=\"${keyValue}\" "
1074+
writer << "value=\"${keyValue.toString().encodeAsHTML()}\" "
10751075

10761076
if(dataAttrsMap) {
10771077
dataAttrsMap.each {key, val->

grails-shell/src/main/groovy/org/grails/cli/GrailsCli.groovy

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -674,7 +674,7 @@ class GrailsCli {
674674
@Canonical
675675
public static class ExecutionContextImpl implements ExecutionContext {
676676
CommandLine commandLine
677-
@Delegate(excludes = 'getConsole') ProjectContext projectContext
677+
@Delegate(excludes = ['getConsole', 'getBaseDir']) ProjectContext projectContext
678678
GrailsConsole console = GrailsConsole.getInstance()
679679

680680
ExecutionContextImpl(CodeGenConfig config) {
@@ -706,6 +706,11 @@ class GrailsCli {
706706
}
707707
}
708708
}
709+
710+
@Override
711+
File getBaseDir() {
712+
this.projectContext?.baseDir ?: new File(".")
713+
}
709714
}
710715

711716
@Canonical

grails-shell/src/main/groovy/org/grails/cli/profile/commands/CreateAppCommand.groovy

Lines changed: 95 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,20 @@ import org.eclipse.aether.graph.Dependency
2727
import org.grails.build.logging.GrailsConsoleAntBuilder
2828
import org.grails.build.parsing.CommandLine
2929
import org.grails.cli.GrailsCli
30-
import org.grails.cli.profile.*
30+
import org.grails.cli.profile.CommandDescription
31+
import org.grails.cli.profile.ExecutionContext
32+
import org.grails.cli.profile.Feature
33+
import org.grails.cli.profile.Profile
34+
import org.grails.cli.profile.ProfileRepository
35+
import org.grails.cli.profile.ProfileRepositoryAware
3136
import org.grails.io.support.FileSystemResource
3237
import org.grails.io.support.Resource
33-
3438
import java.nio.file.FileVisitResult
3539
import java.nio.file.Files
3640
import java.nio.file.Path
3741
import java.nio.file.SimpleFileVisitor
3842
import java.nio.file.attribute.BasicFileAttributes
43+
import java.nio.file.Paths
3944

4045
/**
4146
* Command for creating Grails applications
@@ -60,6 +65,7 @@ class CreateAppCommand extends ArgumentCompletingCommand implements ProfileRepos
6065
Map<String, String> variables = [:]
6166
String appname
6267
String groupname
68+
String defaultpackagename
6369
File targetDirectory
6470
List<String> binaryFileExtensions = ['png','gif','jpg','jpeg','ico','icns','pdf','zip','jar','class']
6571

@@ -202,38 +208,28 @@ class CreateAppCommand extends ArgumentCompletingCommand implements ProfileRepos
202208
files
203209
}
204210

205-
@Override
206-
boolean handle(ExecutionContext executionContext) {
207-
if(profileRepository == null) throw new IllegalStateException("Property 'profileRepository' must be set")
208-
211+
boolean handle(CreateAppCommandObject cmd) {
212+
if (profileRepository == null) throw new IllegalStateException("Property 'profileRepository' must be set")
209213

210-
def mainCommandLine = executionContext.commandLine
211-
def profileName = evaluateProfileName(mainCommandLine)
212-
213-
List<String> validFlags = [INPLACE_FLAG, PROFILE_FLAG, FEATURES_FLAG]
214-
mainCommandLine.undeclaredOptions.each { String key, Object value ->
215-
if (!validFlags.contains(key)) {
216-
List possibleSolutions = validFlags.findAll { it.substring(0, 2) == key.substring(0, 2) }
217-
StringBuilder warning = new StringBuilder("Unrecognized flag: ${key}.")
218-
if (possibleSolutions) {
219-
warning.append(" Possible solutions: ")
220-
warning.append(possibleSolutions.join(", "))
221-
}
222-
GrailsConsole.instance.warn(warning.toString())
223-
}
224-
}
214+
String profileName = cmd.profileName
225215

226216
Profile profileInstance = profileRepository.getProfile(profileName)
227-
if( !validateProfile(profileInstance, profileName, executionContext)) {
217+
if (!validateProfile(profileInstance, profileName)) {
228218
return false
229219
}
230-
List<Feature> features = evaluateFeatures(profileInstance, mainCommandLine).toList()
231-
if(profileInstance) {
232220

233-
if( !initializeVariables(profileInstance, mainCommandLine) ) {
221+
List<Feature> features = evaluateFeatures(profileInstance, cmd.features).toList()
222+
223+
if (profileInstance) {
224+
if (!initializeGroupAndName(cmd.appName, cmd.inplace)) {
234225
return false
235226
}
236-
File projectTargetDirectory = mainCommandLine.hasOption('inplace') || GrailsCli.isInteractiveModeActive() ? new File(".").canonicalFile : new File(appname)
227+
228+
initializeVariables(cmd.appName, defaultpackagename, profileName, cmd.grailsVersion)
229+
230+
Path appFullDirectory = Paths.get(cmd.baseDir.path, appname)
231+
232+
File projectTargetDirectory = cmd.inplace ? new File(".").canonicalFile : appFullDirectory.toAbsolutePath().normalize().toFile()
237233

238234
def profiles = profileRepository.getProfileAndDependencies(profileInstance)
239235

@@ -285,11 +281,11 @@ class CreateAppCommand extends ArgumentCompletingCommand implements ProfileRepos
285281
}
286282

287283
replaceBuildTokens(profileName, profileInstance, features, projectTargetDirectory)
288-
executionContext.console.addStatus(
289-
"${name == 'create-plugin' ? 'Plugin' : 'Application'} created at $projectTargetDirectory.absolutePath"
284+
cmd.console.addStatus(
285+
"${name == 'create-plugin' ? 'Plugin' : 'Application'} created at ${projectTargetDirectory.absolutePath}"
290286
)
291287
if (profileInstance.instructions) {
292-
executionContext.console.addStatus(profileInstance.instructions)
288+
cmd.console.addStatus(profileInstance.instructions)
293289
}
294290
GrailsCli.tiggerAppLoad()
295291
return true
@@ -300,15 +296,53 @@ class CreateAppCommand extends ArgumentCompletingCommand implements ProfileRepos
300296
}
301297
}
302298

303-
protected boolean validateProfile(Profile profileInstance, String profileName, ExecutionContext executionContext) {
299+
@Override
300+
boolean handle(ExecutionContext executionContext) {
301+
CommandLine commandLine = executionContext.commandLine
302+
303+
String profileName = evaluateProfileName(commandLine)
304+
305+
List<String> validFlags = [INPLACE_FLAG, PROFILE_FLAG, FEATURES_FLAG]
306+
commandLine.undeclaredOptions.each { String key, Object value ->
307+
if (!validFlags.contains(key)) {
308+
List possibleSolutions = validFlags.findAll { it.substring(0, 2) == key.substring(0, 2) }
309+
StringBuilder warning = new StringBuilder("Unrecognized flag: ${key}.")
310+
if (possibleSolutions) {
311+
warning.append(" Possible solutions: ")
312+
warning.append(possibleSolutions.join(", "))
313+
}
314+
executionContext.console.warn(warning.toString())
315+
}
316+
}
317+
318+
boolean inPlace = commandLine.hasOption('inplace') || GrailsCli.isInteractiveModeActive()
319+
String appName = commandLine.remainingArgs ? commandLine.remainingArgs[0] : ""
320+
321+
List<String> features = commandLine.optionValue("features")?.toString()?.split(',')?.toList()
322+
323+
CreateAppCommandObject cmd = new CreateAppCommandObject(
324+
appName: appName,
325+
baseDir: executionContext.baseDir,
326+
profileName: profileName,
327+
grailsVersion: Environment.getPackage().getImplementationVersion() ?: GRAILS_VERSION_FALLBACK_IN_IDE_ENVIRONMENTS_FOR_RUNNING_TESTS,
328+
features: features,
329+
inplace: inPlace,
330+
console: executionContext.console
331+
)
332+
333+
return this.handle(cmd)
334+
}
335+
336+
protected boolean validateProfile(Profile profileInstance, String profileName) {
304337
if (profileInstance == null) {
305-
executionContext.console.error("Profile not found for name [$profileName]")
338+
GrailsConsole.instance.error("Profile not found for name [$profileName]")
306339
return false
307340
}
308341
return true
309342
}
310343

311344
private Map<URL, File> unzippedDirectories = new LinkedHashMap<URL, File>()
345+
312346
@CompileDynamic
313347
protected File unzipProfile(AntBuilder ant, Resource location) {
314348

@@ -411,15 +445,15 @@ class CreateAppCommand extends ArgumentCompletingCommand implements ProfileRepos
411445
mainCommandLine.optionValue('profile')?.toString() ?: getDefaultProfile()
412446
}
413447

414-
protected Iterable<Feature> evaluateFeatures(Profile profile, CommandLine commandLine) {
415-
String[] requestedFeatures = commandLine.optionValue("features")?.toString()?.split(',')
416-
if(requestedFeatures) {
417-
List<String> requestedFeaturesList = requestedFeatures.toList()
448+
protected Iterable<Feature> evaluateFeatures(Profile profile, List<String> requestedFeatures) {
449+
if (requestedFeatures) {
418450
List<String> allFeatureNames = profile.features*.name
419-
List<String> validFeatureNames = requestedFeaturesList.intersect(allFeatureNames)
420-
requestedFeaturesList.removeAll(allFeatureNames)
421-
requestedFeaturesList.each { String invalidFeature ->
422-
List possibleSolutions = allFeatureNames.findAll { it.substring(0, 2) == invalidFeature.substring(0, 2) }
451+
List<String> validFeatureNames = requestedFeatures.intersect(allFeatureNames)
452+
requestedFeatures.removeAll(allFeatureNames)
453+
requestedFeatures.each { String invalidFeature ->
454+
List possibleSolutions = allFeatureNames.findAll {
455+
it.substring(0, 2) == invalidFeature.substring(0, 2)
456+
}
423457
StringBuilder warning = new StringBuilder("Feature ${invalidFeature} does not exist in the profile ${profile.name}!")
424458
if (possibleSolutions) {
425459
warning.append(" Possible solutions: ")
@@ -463,49 +497,45 @@ class CreateAppCommand extends ArgumentCompletingCommand implements ProfileRepos
463497
setTo.text = createNewApplicationYml(previousApplicationYml, newApplicationYml)
464498
}
465499
}
466-
467-
protected boolean initializeVariables(Profile profile, CommandLine commandLine) {
468-
String defaultPackage
469-
470-
def args = commandLine.getRemainingArgs()
471-
boolean inPlace = commandLine.hasOption('inplace') || GrailsCli.isInteractiveModeActive()
472500

473-
if(!args && !inPlace) {
501+
protected boolean initializeGroupAndName(String appName, boolean inplace) {
502+
if (!appName && !inplace) {
474503
GrailsConsole.getInstance().error("Specify an application name or use --inplace to create an application in the current directory")
475504
return false
476505
}
477-
String groupAndAppName = args ? args[0] : null
478-
if(inPlace) {
506+
String groupAndAppName = appName
507+
if(inplace) {
479508
appname = new File(".").canonicalFile.name
480509
if(!groupAndAppName) {
481510
groupAndAppName = appname
482511
}
483512
}
484-
513+
485514
if(!groupAndAppName) {
486515
GrailsConsole.getInstance().error("Specify an application name or use --inplace to create an application in the current directory")
487516
return false
488517
}
489518

490519
try {
491-
defaultPackage = establishGroupAndAppName(groupAndAppName)
520+
defaultpackagename = establishGroupAndAppName(groupAndAppName)
492521
} catch (IllegalArgumentException e ) {
493522
GrailsConsole.instance.error(e.message)
494523
return false
495524
}
525+
}
496526

497-
527+
private void initializeVariables(String appname, String defaultPackage, String profileName, String grailsVersion) {
498528
variables.APPNAME = appname
499529

500530
variables['grails.codegen.defaultPackage'] = defaultPackage
501-
variables['grails.codegen.defaultPackage.path'] = defaultPackage.replace('.', '/')
531+
variables['grails.codegen.defaultPackage.path'] = defaultPackage.replace('.', '/')
502532

503533
def projectClassName = GrailsNameUtils.getNameFromScript(appname)
504534
variables['grails.codegen.projectClassName'] = projectClassName
505535
variables['grails.codegen.projectNaturalName'] = GrailsNameUtils.getNaturalName(projectClassName)
506536
variables['grails.codegen.projectName'] = GrailsNameUtils.getScriptName(projectClassName)
507-
variables['grails.profile'] = profile.name
508-
variables['grails.version'] = Environment.getPackage().getImplementationVersion() ?: GRAILS_VERSION_FALLBACK_IN_IDE_ENVIRONMENTS_FOR_RUNNING_TESTS
537+
variables['grails.profile'] = profileName
538+
variables['grails.version'] = grailsVersion
509539
variables['grails.app.name'] = appname
510540
variables['grails.app.group'] = groupname
511541
}
@@ -526,7 +556,7 @@ class CreateAppCommand extends ArgumentCompletingCommand implements ProfileRepos
526556
}
527557

528558
private String createValidPackageName() {
529-
String defaultPackage = appname.split(/[-]+/).collect { String token -> (token.toLowerCase().toCharArray().findAll { char ch -> Character.isJavaIdentifierPart(ch) } as char[]) as String }.join('.')
559+
String defaultPackage = appname.split(/[-]+/).collect { String token -> (token.toLowerCase().toCharArray().findAll { char ch -> Character.isJavaIdentifierPart(ch) } as char[]) as String }.join('.')
530560
if(!GrailsNameUtils.isValidJavaPackage(defaultPackage)) {
531561
throw new IllegalArgumentException("Cannot create a valid package name for [$appname]. Please specify a name that is also a valid Java package.")
532562
}
@@ -635,4 +665,14 @@ class CreateAppCommand extends ArgumentCompletingCommand implements ProfileRepos
635665

636666
return v ? "${artifact.groupId}:${artifact.artifactId}:${v}" : "${artifact.groupId}:${artifact.artifactId}"
637667
}
668+
669+
static class CreateAppCommandObject {
670+
String appName
671+
File baseDir
672+
String profileName
673+
String grailsVersion
674+
List<String> features
675+
boolean inplace = false
676+
GrailsConsole console
677+
}
638678
}

grails-shell/src/main/groovy/org/grails/cli/profile/commands/CreatePluginCommand.groovy

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,8 @@
1717
package org.grails.cli.profile.commands
1818

1919
import groovy.transform.CompileStatic
20-
import org.grails.build.parsing.CommandLine
2120
import org.grails.cli.profile.ExecutionContext
2221
import org.grails.cli.profile.Profile
23-
24-
2522
/**
2623
* A command for creating a plugin
2724
*
@@ -50,16 +47,14 @@ class CreatePluginCommand extends CreateAppCommand {
5047
@Override
5148
protected String getDefaultProfile() { "web-plugin" }
5249

53-
@Override
5450
protected boolean validateProfile(Profile profileInstance, String profileName, ExecutionContext executionContext) {
55-
5651
def pluginProfile = profileInstance.extends.find() { Profile parent -> parent.name == 'plugin' }
5752
if(profileName != 'plugin' && pluginProfile == null) {
5853
executionContext.console.error("No valid plugin profile found for name [$profileName]")
5954
return false
6055
}
6156
else {
62-
return super.validateProfile(profileInstance, profileName, executionContext)
57+
return super.validateProfile(profileInstance, profileName)
6358
}
6459
}
6560
}

grails-shell/src/test/groovy/org/grails/cli/profile/commands/CreateAppCommandSpec.groovy

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,11 @@
11
package org.grails.cli.profile.commands
22

33
import grails.build.logging.GrailsConsole
4-
import org.grails.build.parsing.CommandLine
5-
import org.grails.build.parsing.DefaultCommandLine
64
import org.grails.cli.profile.Feature
75
import org.grails.cli.profile.Profile
86
import org.spockframework.util.StringMessagePrintStream
97
import spock.lang.Shared
108
import spock.lang.Specification
11-
129
/**
1310
* Created by Jim on 7/18/2016.
1411
*/
@@ -39,10 +36,9 @@ class CreateAppCommandSpec extends Specification {
3936
2 * getFeatures() >> [bar]
4037
1 * getRequiredFeatures() >> []
4138
}
42-
CommandLine commandLine = new DefaultCommandLine().parseNew(["create-app", "foo", "-features=foo,bar"] as String[])
4339

4440
when:
45-
Iterable<Feature> features = new CreateAppCommand().evaluateFeatures(profile, commandLine)
41+
Iterable<Feature> features = new CreateAppCommand().evaluateFeatures(profile, ['foo', 'bar'])
4642

4743
then:
4844
features.size() == 1
@@ -63,10 +59,9 @@ class CreateAppCommandSpec extends Specification {
6359
2 * getFeatures() >> [foo, bar]
6460
1 * getRequiredFeatures() >> []
6561
}
66-
CommandLine commandLine = new DefaultCommandLine().parseNew(["create-app", "foo", "-features=foo,bar"] as String[])
6762

6863
when:
69-
Iterable<Feature> features = new CreateAppCommand().evaluateFeatures(profile, commandLine)
64+
Iterable<Feature> features = new CreateAppCommand().evaluateFeatures(profile, ['foo', 'bar'])
7065

7166
then:
7267
features.size() == 2
@@ -85,11 +80,9 @@ class CreateAppCommandSpec extends Specification {
8580
2 * getFeatures() >> [bar]
8681
1 * getRequiredFeatures() >> []
8782
}
88-
CommandLine commandLine = new DefaultCommandLine().parseNew(["create-app", "foo", "-features=mongo"] as String[])
8983

9084
when:
91-
Iterable<Feature> features = new CreateAppCommand().evaluateFeatures(profile, commandLine)
92-
85+
Iterable<Feature> features = new CreateAppCommand().evaluateFeatures(profile, ['mongo'])
9386

9487
then:
9588
features.size() == 0

grails-test-suite-web/src/test/groovy/org/grails/web/taglib/SelectTagTests.groovy

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,14 @@ class SelectTagTests extends AbstractGrailsTagTests {
2222
assertTrue "should have HTML escaped attributes", result.startsWith('<select id="bar&quot; /&gt;&lt;script&gt;alert(&quot;gotcha&quot;)&lt;/script&gt;.genre" name="bar&quot; /&gt;&lt;script&gt;alert(&quot;gotcha&quot;)&lt;/script&gt;.genre" >')
2323
}
2424

25+
void testSelectTagEscapingValue() {
26+
def template = '<g:select id="genre" name="genre" from="${values}" />'
27+
def result = applyTemplate(template, [values: ["\"></option></select><script>alert('hi')</script>"]])
28+
29+
println result
30+
assertTrue "should have HTML escaped values", result.contains('<option value="&quot;&gt;&lt;/option&gt;&lt;/select&gt;&lt;script&gt;alert(&#39;hi&#39;)&lt;/script&gt;" >&quot;&gt;&lt;/option&gt;&lt;/select&gt;&lt;script&gt;alert(&#39;hi&#39;)&lt;/script&gt;</option>')
31+
}
32+
2533
void testSelectUsesExpressionForDisable() {
2634
def template = '<g:set var="flag" value="${true}"/><g:select disabled="${flag}" name="foo" id="foo" from="[1,2,3]" />'
2735
assertOutputContains('disabled="disabled"', template)

0 commit comments

Comments
 (0)