I am trying to override the g:link tag so that I can prefix an extra string. Here is my code:
import org.codehaus.groovy.grails.plugins.web.taglib.*
class ApplicationTagLib {
static namespace = "g"
def link = { attrs, body ->
if("es".equalsIgnoreCase(request.stLocale.language)) {
attrs['controller'] = "es/" + attrs['controller']
}
def applicationTagLib = grailsApplication.mainContext.getBean('org.codehaus.groovy.grails.plugins.web.taglib.ApplicationTagLib')
applicationTagLib.link.call(attrs, body)
}
}
This works fine except for when I add "es/" the resulting path gets translated into es%2F instead of es/ which causes the link to not work.
Is there a way to prevent this from automatically encoding the new slash or a better way to prefix this string to the controller path?
You should be aware that in Grails the controller package (thus it's location in the project's structure path) does not correlate with the default URL mapping - the structure is flattened.
The slash you add to the controller name is thus encoded as it would otherwise form a part of the URL (and thus not map to a controller).
Perhaps the logic for handling different locale be better placed in a controller anyway.
You can add this '/es' prefix in all links generated by grails tags by configuring your UrlMappings.groovy. If you're using the default one, generated by grails create-app command, you can add '/es' in your URL's like this:
class UrlMappings {
static mappings = {
"/es/$controller/$action?/$id?" { // <---------- added '/es' prefix
constraints {
// apply constraints here
}
}
"/"(view: "/index")
"500"(view: '/error')
}
}
To learn more about URL Mappings, see the Grails guide.
Regards
Related
Is there any documentation regarding how to create a custom grails tag that generated groovy code similar to "<g:if>" and "<g:else>"?
Using the normal grails taglib, I'm able to generate html or raw output, but I'm looking to output code that can be interpreted similar to how the if and else tags work.
Here's a sample of the Grails source code for the "if" tag:
#Override
protected void outputStartTag(String envExpression, String testExpression) {
out.print("if(");
out.print(envExpression);
out.print(" && ");
out.print(testExpression);
out.println(") {");
}
How can I do something similar in my own taglib prefix.
Example:
<mine:doSomethingCool key="foo">This text is conditionally shown</mine:doSomethingCool>
In addition to hiding complex logic, I would like this ability so I can use the <g:else> tag after my custom tag.
In general, I would like another tool in my toolbox in addition to the current taglib format.
If for example you only want to render the conditional text when key == 'foo', you could do that like this:
class SomethingTagLib {
static namespace = "ns"
def doSomethingCool = { attrs, body ->
if (attrs.key == 'foo') {
out << body()
}
}
}
You could then use this tag like so:
<ns:doSomethingCool key="foo">This text is conditionally shown<ns:doSomethingCool>
Using Grails 3.0.9
I tried setting up a custom tag, but I can't get it to work. I created the following groovy file in grails-app/taglib: BeanFormGenerator.groovy
class BeanFormGenerator {
def renderList = { attrs, body ->
// reads the 'values' attribute from the attributes list
def list = attrs.values
// iterates and renders list values
list.each {
// uses the implicit 'out' variable to append content to the response
out << "<span class=\"element\"> ${it} </span>"
}
}
}
And I have this call in a gsp file:
<g:renderList values="[1, 2, 3]">check check</g:renderList>
I get the error:
Tag [renderList] does not exist. No tag library found for namespace: g
I tried creating a custom namespace inside of BeanFormGenerator:
static namespace = "beanform"
But this just caused to be treated as markup. What am I doing wrong? The documentation makes it seem like this should be all there is to it. I'm running this inside of IntelliJ community edition if that makes a difference.
Quite simply, to create a tag library create a Groovy class that ends
with the convention TagLib and place it within the grails-app/taglib
directory
grails.github.io/grails-doc/3.0.9/guide/single.html#taglibs
I wrote a private plugin(MyPlugin) that is being used by other applications. It has a controller named MyPluginController and a bunch of REST actions. They all work fine when access through the mainapp. The plugin also comes with a single page app contained in static.html. I could not figure out how to do the UrlMapping for static.html to be accessible like:
http://example.com/mainapp/MyPluginController/static.html
class MyPluginUrlMappings {
static mappings = {
"/$controller/$action?/$id?(.$format)?"{
constraints {
// apply constraints here
}
}
"/"(view:"/index")
"500"(view:'/error')
/* Not sure what do here ???*/
"/MyPluginController/static.html" (redirect: "/static.html", permanent: true)
}
The html is in myplugin's web-app folder. It is easy to rename it to .gsp and make it work but this is grails and I am sure there must be cleaner way of doing it.
I messed around with this a bit yesterday and failed miserably. I want to convert:
"/$controller/$action?/$id?"
To
#in psudo
"/$controller/$id?/$action?"
#ideal regex
"\/(\w+)(\/\d+)?(\/\w+)?"
The most obvious way failed "/$controller/$action?/$id?"
I can write the regex's to do it, but I am having trouble finding a way to using true regexs (I found RegexUrlMapping but could not find out how to use it), and also can't find documentation on how to assign a group to a variable.
My question is 2 parts:
How to I define a URL Resource with a true regex.
How to I bind a "group" to a variable. In other words if I define a regex, how do I bind it to a variable like $controller, $id, $action
I would also like to be able to support the .json notation /user/id.json
Other things I have tried, which I thought would work:
"/$controller$id?$action?"{
constraints {
controller(matches:/\w+/)
id(matches:/\/\d+/)
action(matches:/\/\w+/)
}
}
also tried:
"/$controller/$id?/$action?"{
constraints {
controller(matches:/\w+/)
id(matches:/\d+/)
action(matches:/\w+/)
}
}
The grails way to deal with this is to set
grails.mime.file.extensions = true
in Config.groovy. This will cause Grails to strip off the file extension before applying the URL mappings, but make it available for use by withFormat
def someAction() {
withFormat {
json {
render ([message:"hello"] as JSON)
}
xml {
render(contentType:'text/xml') {
//...
}
}
}
For this you'd just need a URL mapping of "$controller/$id?/$action?"
I'm not aware of any way to use regular expressions in the way you want in the URL mappings, but you could get a forward mapping working using the fact that you can specify closures for parameter values that get evaluated at runtime with access to the other params:
"$controller/$a?/$b?" {
action = { params.b ?: params.a }
id = { params.b ? params.a : null }
}
which says "if b is set then use that as the action and a as the id, otherwise use a as the action and set id to null". But this wouldn't give you a nice reverse mapping, i.e. createLink(controller:'foo', action:'bar', id:1) wouldn't generate anything sensible, you'd have to use createLink(controller:'foo', params:[a:1, b:'bar'])
Edit
A third possibility you could try is to combine the
"/$controller/$id/$action"{
constraints {
controller(matches:/\w+/)
id(matches:/\d+/)
action(matches:/\w+/)
}
}
mapping with a complementary
"/$controller/$action?"{
constraints {
controller(matches:/\w+/)
action(matches:/(?!\d+$)\w+/)
}
}
using negative lookahead to ensure the two mappings are disjoint.
I have my target language in Session["lang"], which is either "en" or "it". I have added this to the Site.master:
<script runat="server">
void Page_Load(object sender, EventArgs e) {
string lang = Session["lang"].ToString();
System.Threading.Thread.CurrentThread.CurrentCulture = System.Globalization.CultureInfo.CreateSpecificCulture(lang);
System.Threading.Thread.CurrentThread.CurrentUICulture = System.Globalization.CultureInfo.CreateSpecificCulture(lang);
}
</script>
Then I'd like to invoke a resource string like this:
<asp:Label ID="Label1" runat="server" Text="<%$ Resources:Global, test %>"></asp:Label>
I have two files in the App_GlobalResources, named Global.resx and Global.en.resx.
The problems is that no matter what is in the lang variable, I always get the results from the main Global.resx, and I never get the english version from Global.en.resx
I am doing this wrong entirely??
I tried putting the System.Threading... part in the Application_PreRequestHandlerExecute method in Global.asax.cs but the result was the same.
Thanks
PS: I am asking about a way to make this work in a simple way. If I was to use the complicate way, I'd go with this: http://helios.ca/2009/05/27/aspnet-mvc-and-localization/
i had the same dilema(how to implement localization) in my asp.net mvc app.
I followed the instructions posted here and it works like a charm.
So i created a folder named Localization under Content and then i create Resources resx files for each language i want to translate. Keep in mind that there is a convention for the resx file names. ie
Resources.resx is the default fall back for everything.
Resources.en-GB.resx is for english GB
Resources.en-US.resx is for english US
etc.
Just make sure you follow the instructions posted in the link to embed and make the Resources available in all places in your app (views, controllers etc)
Edit:
I want to add that i ommited this line from web.config since i wanted to manually set the local from my app.
<globalization uiCulture="auto" culture="auto"/>
Instead i have created the following class:
public class SmartController : Controller
{
public SmartController()
{
System.Threading.Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo("en-US");
System.Threading.Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo("en-US");
}
}
All controllers inherit from this class.
Since this is an administrative set of the locale i have to set it from my apps settings. You could read it from Cookies and set it, or otherwise. This is imo the simplest solution for localization that i have encountered so far.
Once implemented you can refer to any string you add by the following simple line of code, no extra code needed.
<%= Resources.Strings.TranslatedTerm %>
I bet this one is a duplicate.
Anyway - all you need is here (assuming that you are using webforms viewengine (might work with others too, haven't investigated)).
Oh well... here goes my 'summary':
Helpers are just a part. You need to do some modifications with your default view engine too . On createview/createpartialview it should return localizationwebformview which adds a path key to viewdata which is used by htmlhelper to find resourceexpressionsfields and pass them to localizationhelpers class which retrieves desired value.
Little bonus=>
This might be handy if you don't want to recreate resource folders for view subfolders
(in case you modify viewengine.view/partialviewlocationformats):
private static string ReformatVirtualPath(string virtualPath)
{
//This allows NOT to duplicate App_localResources directory
// ~/Views/Shared/Partial/Some/BulltihS/_View.ascx
// turns into =>
// ~/Views/Shared/_View.ascx
var start = #"(~(/?\w*/?){2})";
var end = #"(\w*.as(c|p)x)";
start = Regex.Match(virtualPath, start).Value;
end = Regex.Match(virtualPath, end).Value;
return start + end;
}
usage:
internal static ResourceExpressionFields GetResourceFields
(string expression, string virtualPath)
{
virtualPath = ReformatVirtualPath(virtualPath);
var context = new ExpressionBuilderContext(virtualPath);
var builder = new ResourceExpressionBuilder();
return (ResourceExpressionFields)
builder.ParseExpression(expression, typeof(string), context);
}
EDIT:
but it might be a good idea to avoid App_GlobalResources and App_LocalResources as K. Scott Allen suggests (check Konstantinos answer).