Skip to content

Commit b83bdcd

Browse files
restore g:javascript and g:escapeJavascript tags
1 parent 8b65ed3 commit b83bdcd

File tree

3 files changed

+364
-0
lines changed

3 files changed

+364
-0
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ class GroovyPagesGrailsPlugin extends Plugin {
7676
CountryTagLib,
7777
FormatTagLib,
7878
FormTagLib,
79+
JavascriptTagLib,
7980
RenderTagLib,
8081
UrlMappingTagLib,
8182
ValidationTagLib,
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
/*
2+
* Copyright 2004-2005 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.grails.plugins.web.taglib
17+
18+
import grails.artefact.TagLibrary
19+
import grails.gsp.TagLib
20+
import grails.plugins.GrailsPluginManager
21+
import org.springframework.context.ApplicationContext
22+
import org.springframework.context.ApplicationContextAware
23+
24+
import javax.annotation.PostConstruct
25+
26+
/**
27+
* Javascript tags.
28+
*
29+
* @author Graeme Rocher
30+
*/
31+
@TagLib
32+
class JavascriptTagLib implements ApplicationContextAware, TagLibrary {
33+
ApplicationContext applicationContext
34+
35+
GrailsPluginManager pluginManager
36+
37+
boolean hasResourceProcessor = false
38+
39+
static encodeAsForTags = [escapeJavascript: 'JavaScript',
40+
javascript: [expressionCodec:"JavaScript", scriptletCodec:"JavaScript", replaceOnly:true]]
41+
42+
@PostConstruct
43+
private void initHasResourceProcessor() {
44+
hasResourceProcessor = applicationContext.containsBean('grailsResourceProcessor')
45+
}
46+
47+
/**
48+
* Includes a javascript src file, library or inline script
49+
* if the tag has no 'src' or 'library' attributes its assumed to be an inline script:<br/>
50+
*
51+
* &lt;g:javascript&gt;alert('hello')&lt;/g:javascript&gt;<br/>
52+
*
53+
* The 'library' attribute will attempt to use the library mappings defined above to import the
54+
* right js files and not duplicate imports eg.<br/>
55+
*
56+
* &lt;g:javascript library="scriptaculous" /&gt; // imports all the necessary js for the scriptaculous library<br/>
57+
*
58+
* The 'src' attribute will merely import the js file but within the right context (ie inside the /js/ directory of
59+
* the Grails application:<br/>
60+
*
61+
* &lt;g:javascript src="myscript.js" /&gt; // actually imports '/app/js/myscript.js'
62+
*
63+
* @attr src The name of the javascript file to import. Will look in web-app/js dir
64+
* @attr library The name of the library to include. e.g. "jquery", "prototype", "scriptaculous", "yahoo" or "dojo"
65+
* @attr plugin The plugin to look for the javascript in
66+
* @attr contextPath the context path to use (relative to the application context path). Defaults to "" or path to the plugin for a plugin view or template.
67+
* @attr base specifies the full base url to prepend to the library name
68+
*/
69+
Closure javascript = { attrs, body ->
70+
if (attrs.src) {
71+
javascriptInclude(attrs)
72+
} else {
73+
if (hasResourceProcessor) {
74+
out << r.script(Collections.EMPTY_MAP, body)
75+
} else {
76+
out.println '<script type="text/javascript">'
77+
out << body()
78+
out.println()
79+
out.println '</script>'
80+
}
81+
}
82+
}
83+
84+
private javascriptInclude(attrs) {
85+
def requestPluginContext
86+
if (attrs.plugin) {
87+
requestPluginContext = pluginManager.getPluginPath(attrs.remove('plugin')) ?: ''
88+
}
89+
else {
90+
if (attrs.contextPath != null) {
91+
requestPluginContext = attrs.remove('contextPath').toString()
92+
}
93+
else {
94+
requestPluginContext = pageScope.pluginContextPath ?: ''
95+
}
96+
}
97+
98+
if (attrs.base) {
99+
attrs.uri = attrs.remove('base') + attrs.remove('src')
100+
} else {
101+
def appBase = grailsAttributes.getApplicationUri(request)
102+
if (!appBase.endsWith('/')) {
103+
appBase += '/'
104+
}
105+
def reqResCtx = ''
106+
if (requestPluginContext) {
107+
reqResCtx = (requestPluginContext.startsWith("/") ? requestPluginContext.substring(1) : requestPluginContext) + '/'
108+
}
109+
attrs.uri = appBase + reqResCtx + 'js/'+attrs.remove('src')
110+
}
111+
out << g.external(attrs)
112+
}
113+
114+
/**
115+
* Escapes a javascript string replacing single/double quotes and new lines.<br/>
116+
*
117+
* &lt;g:escapeJavascript&gt;This is some "text" to be escaped&lt;/g:escapeJavascript&gt;
118+
*/
119+
Closure escapeJavascript = { attrs, body ->
120+
if (body) {
121+
out << body()
122+
}
123+
else if (attrs.value) {
124+
out << attrs.value
125+
}
126+
}
127+
}
Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
package org.grails.web.taglib
2+
3+
import grails.util.GrailsUtil
4+
import org.grails.core.artefact.UrlMappingsArtefactHandler
5+
import org.grails.gsp.GroovyPageBinding
6+
import org.grails.web.util.GrailsApplicationAttributes
7+
import org.springframework.web.util.WebUtils
8+
9+
class JavascriptTagLibTests extends AbstractGrailsTagTests {
10+
11+
private static final String EOL = new String([(char)13,(char)10] as char[])
12+
13+
protected void onSetUp() {
14+
gcl.parseClass('''
15+
class TestController {}
16+
''')
17+
}
18+
19+
protected void onInit() {
20+
def urlMappingsClass = gcl.parseClass('''\
21+
class TestUrlMappings {
22+
static mappings = {
23+
"/$controller/$action?/$id?" {}
24+
"/people/details/$var1"(controller: 'person', action: 'show')
25+
}
26+
}
27+
''')
28+
grailsApplication.addArtefact(UrlMappingsArtefactHandler.TYPE, urlMappingsClass)
29+
}
30+
31+
void testJavascriptIncludeWithPluginAttribute() {
32+
def template = '<g:javascript src="foo.js" plugin="controllers" />'
33+
def grailsVersion = GrailsUtil.getGrailsVersion()
34+
assertOutputContains "<script src=\"/plugins/controllers-$grailsVersion/js/foo.js\" type=\"text/javascript\"></script>", template
35+
}
36+
37+
void testJavascriptInclude() {
38+
def template = '<g:javascript src="foo.js" />'
39+
assertOutputContains '<script src="/js/foo.js" type="text/javascript"></script>', template
40+
}
41+
42+
void testJavascriptIncludeWithPlugin() {
43+
def template = '<g:javascript src="foo.js" />'
44+
request.setAttribute(GrailsApplicationAttributes.PAGE_SCOPE, new GroovyPageBinding("/plugin/one"))
45+
assertOutputContains '<script src="/plugin/one/js/foo.js" type="text/javascript"></script>', template
46+
}
47+
48+
void testJavascriptIncludeWithContextPathSpecified() {
49+
def template = '<g:javascript src="foo.js" />'
50+
51+
request.setAttribute(GrailsApplicationAttributes.PAGE_SCOPE, new GroovyPageBinding("/plugin/one"))
52+
assertOutputContains '<script src="/plugin/one/js/foo.js" type="text/javascript"></script>', template
53+
54+
template = '<g:javascript src="foo.js" contextPath="/foo" />'
55+
assertOutputContains '<script src="/foo/js/foo.js" type="text/javascript"></script>', template
56+
}
57+
58+
void testJavascriptIncludeWithPluginNoLeadingSlash() {
59+
def template = '<g:javascript src="foo.js" />'
60+
request.setAttribute(GrailsApplicationAttributes.PAGE_SCOPE, new GroovyPageBinding("plugin/one"))
61+
assertOutputContains '<script src="/plugin/one/js/foo.js" type="text/javascript"></script>' + EOL, template
62+
}
63+
64+
void testPluginAwareJSSrc() {
65+
StringWriter sw = new StringWriter()
66+
withTag("javascript", sw) {tag ->
67+
setupPluginController(tag)
68+
def attrs = [src: 'lib.js']
69+
tag.call(attrs) {}
70+
}
71+
assertEquals("<script src=\"/myapp/plugins/myplugin/js/lib.js\" type=\"text/javascript\"></script>" + EOL, sw.toString())
72+
}
73+
74+
void testPluginAwareJSSrcTrailingSlash() {
75+
StringWriter sw = new StringWriter()
76+
withTag("javascript", sw) {tag ->
77+
setupPluginController(tag)
78+
setRequestContext('/otherapp/')
79+
def attrs = [src: 'lib.js']
80+
tag.call(attrs) {}
81+
}
82+
assertEquals("<script src=\"/otherapp/plugins/myplugin/js/lib.js\" type=\"text/javascript\"></script>" + EOL, sw.toString())
83+
}
84+
85+
void testJSSrc() {
86+
StringWriter sw = new StringWriter()
87+
withTag("javascript", sw) {tag ->
88+
def attrs = [src: 'lib.js']
89+
setRequestContext()
90+
tag.call(attrs) {}
91+
}
92+
assertEquals("<script src=\"/myapp/js/lib.js\" type=\"text/javascript\"></script>" + EOL, sw.toString())
93+
}
94+
95+
void testJSSrcTrailingSlash() {
96+
StringWriter sw = new StringWriter()
97+
withTag("javascript", sw) {tag ->
98+
def attrs = [src: 'lib.js']
99+
setRequestContext('/otherapp/')
100+
tag.call(attrs) {}
101+
}
102+
assertEquals("<script src=\"/otherapp/js/lib.js\" type=\"text/javascript\"></script>" + EOL, sw.toString())
103+
}
104+
105+
void testJSSrcWithNoController() {
106+
StringWriter sw = new StringWriter()
107+
withTag("javascript", sw) {tag ->
108+
def attrs = [src: 'lib.js']
109+
setRequestContext()
110+
request.setAttribute(GrailsApplicationAttributes.CONTROLLER, null)
111+
tag.call(attrs) {}
112+
}
113+
assertEquals("<script src=\"/myapp/js/lib.js\" type=\"text/javascript\"></script>" + EOL, sw.toString())
114+
}
115+
116+
void testJSWithBody() {
117+
StringWriter sw = new StringWriter()
118+
withTag("javascript", sw) {tag ->
119+
setRequestContext()
120+
tag.call([:]) {"do.this();"}
121+
}
122+
assertEquals("<script type=\"text/javascript\">" + EOL + "do.this();" + EOL + "</script>" + EOL, sw.toString())
123+
}
124+
125+
void testJSSrcWithBase() {
126+
StringWriter sw = new StringWriter()
127+
withTag("javascript", sw) {tag ->
128+
def attrs = [src: 'mylib.js', base: 'http://testserver/static/']
129+
setRequestContext()
130+
tag.call(attrs) {}
131+
}
132+
assertEquals("<script src=\"http://testserver/static/mylib.js\" type=\"text/javascript\"></script>" + EOL, sw.toString())
133+
}
134+
135+
def setRequestContext() {
136+
setRequestContext("/myapp")
137+
}
138+
139+
def setRequestContext(path) {
140+
request.setAttribute(WebUtils.INCLUDE_CONTEXT_PATH_ATTRIBUTE, path)
141+
}
142+
143+
def setupPluginController(tag) {
144+
setRequestContext()
145+
request.setAttribute(GrailsApplicationAttributes.PAGE_SCOPE, new GroovyPageBinding("plugins/myplugin"))
146+
}
147+
148+
void testEscapeJavascript() {
149+
StringWriter sw = new StringWriter()
150+
151+
withTag("escapeJavascript", sw) {tag ->
152+
tag.call(Collections.EMPTY_MAP, "This is some \"text\" to be 'escaped'")
153+
}
154+
assertEquals("This is some \\u0022text\\u0022 to be \\u0027escaped\\u0027", sw.toString())
155+
}
156+
157+
void testJavascriptExpressionCodec() {
158+
def template = '''<g:javascript>var value='${'<>'}';</g:javascript>'''
159+
assertOutputEquals('''<script type="text/javascript">\r\nvar value='\\u003c\\u003e';\r\n</script>\r\n''', template)
160+
}
161+
162+
void testJavascriptExpressionNoCodec() {
163+
def template = '''<g:javascript encodeAs="none">var value='${'<>'}';</g:javascript>'''
164+
assertOutputEquals('''<script type="text/javascript">\r\nvar value='<>';\r\n</script>\r\n''', template)
165+
}
166+
167+
void testJavascriptExpressionRawCodec() {
168+
def template = '''<g:javascript encodeAs="raw">var value='${'<>'}';</g:javascript>'''
169+
assertOutputEquals('''<script type="text/javascript">\r\nvar value='<>';\r\n</script>\r\n''', template)
170+
}
171+
172+
void testJavascriptExpressionEncodeAsRaw() {
173+
def template = '''<g:javascript>var value='${'<>'.encodeAsRaw()}';</g:javascript>'''
174+
assertOutputEquals('''<script type="text/javascript">\r\nvar value='<>';\r\n</script>\r\n''', template)
175+
}
176+
177+
void testJavascriptExpressionRaw() {
178+
def template = '''<g:javascript>var value='${raw('<>')}';</g:javascript>'''
179+
assertOutputEquals('''<script type="text/javascript">\r\nvar value='<>';\r\n</script>\r\n''', template)
180+
}
181+
182+
// GRAILS-10985
183+
void testJavascriptExpressionRawAndEscaped() {
184+
withConfig("grails.views.default.codec='HTML'") {
185+
def template = '''<g:javascript>var value='${raw('<>'.intern())}${'<>'.intern()}';</g:javascript>'''
186+
assertOutputEquals('''<script type="text/javascript">\r\nvar value='<>\\u003c\\u003e';\r\n</script>\r\n''', template)
187+
}
188+
}
189+
190+
void testJavascriptExpressionNoneDefaultCodecLegacySettings() {
191+
withConfig("grails.views.default.codec='none'") {
192+
def template = '''<g:javascript>var value='${'<>'}';</g:javascript>'''
193+
assertOutputEquals('''<script type="text/javascript">\r\nvar value='<>';\r\n</script>\r\n''', template)
194+
}
195+
}
196+
197+
void testJavascriptExpressionNoneDefaultCodecNewSettings() {
198+
withConfig('''
199+
grails {
200+
views {
201+
gsp {
202+
codecs {
203+
expression = 'none'
204+
scriptlet = 'none'
205+
taglib = 'none'
206+
staticparts = 'none'
207+
}
208+
}
209+
}
210+
}
211+
''') {
212+
def template = '''<g:javascript>var value='${'<>'}';</g:javascript>'''
213+
assertOutputEquals('''<script type="text/javascript">\r\nvar value='<>';\r\n</script>\r\n''', template)
214+
}
215+
}
216+
217+
void testJavascriptExpressionHtmlDefaultCodecNewSettings() {
218+
withConfig('''
219+
grails {
220+
views {
221+
gsp {
222+
codecs {
223+
expression = 'html'
224+
scriptlet = 'html'
225+
taglib = 'none'
226+
staticparts = 'none'
227+
}
228+
}
229+
}
230+
}
231+
''') {
232+
def template = '''<g:javascript>var value='${'<>'}';</g:javascript>'''
233+
assertOutputEquals('''<script type="text/javascript">\r\nvar value='\\u003c\\u003e';\r\n</script>\r\n''', template)
234+
}
235+
}
236+
}

0 commit comments

Comments
 (0)