Automatic language detection by browser - grails

How can I detect the language of the browser and automatically display the correctly localized version of my grails website depending on that value.
I put this in to Index action
Locale locale = new Locale(params.lang)
cookieLocaleResolver.setLocale(request, response, (Locale)
session.getAttribute('locale'))
{
render controller: "home", action: "index"
return
}
And I got exception--
Error 500: Executing action [index] of controller [socialworking.HomeController] caused exception: null
Servlet: grails
URI: /socialworking/grails/home.dispatch
Exception Message:
Caused by:
Class: Unknown

First off, you should put that in a filter in grails-app/conf directory. Create a filter if you don't already have one.
MyFilters.groovy
class MyFilters {
def filters = {
setLocale(controller:'*', action:'*') {
before = {
// Your logic here
}
}
}
}
Your logic here could look in many ways, but here is a try:
String langToSet = 'en';
if ( params.lang && params.lang in validLanguages )
langToSet = params.lang;
else if ( session.lang ) {
langToSet = session.lang;
}
else if ( ... ) // Cookie lang is set ( User might have accessed the site before and you have stored their preferred lang )
// Get cookie lang
Locale locale = new Locale( langToUse)
org.springframework.web.servlet.support.RequestContextUtils.getLocaleResolver(request).setLocale(request, response, locale);
// Set the cookie lang
...
// We set the session lang
session.lang = langToSet
Note that the above is not a complete implementation but it is almost. The cookie stuff and validLanguages you should be able to figure out what they do.
I hope that helps!

Related

Grails/SpringSecurity core and customizing logout behaviour

I'm using grails 2.3.7 with SpringSecurityCore 2.0 .. I have two separate signon screens tailored for specific devices with the appropriate one triggered by accessing a specific controller. To do this I customized the loginController ..
/**
* Show the login page.
*/
def auth() {
def config = SpringSecurityUtils.securityConfig
if (springSecurityService.isLoggedIn()) {
redirect uri: config.successHandler.defaultTargetUrl
return
}
String whereFrom = session.SPRING_SECURITY_SAVED_REQUEST.requestURI
def rdt = whereFrom.contains('RDT')
// Redirect for RDT as required ..
String view = rdt ? 'rauth' : 'auth'
String postUrl = "${request.contextPath}${config.apf.filterProcessesUrl}"
session.rdt = rdt
render view: view, model: [postUrl: postUrl,
rememberMeParameter: config.rememberMe.parameter]
}
which seems to work well .. On logout I want again to redirect to an appropriate screen .. I'm trying to use the session attribute I store on login along with a (admittedly old) link I found (http://grails.1312388.n4.nabble.com/Parameter-quot-logoutSuccessUrl-quot-in-spring-security-core-td2264147.html) to redirect back to an appropriate page ..
/**
* Index action. Redirects to the Spring security logout uri.
*/
def index() {
if (!request.post && SpringSecurityUtils.getSecurityConfig().logout.postOnly) {
response.sendError HttpServletResponse.SC_METHOD_NOT_ALLOWED // 405
return
}
// TODO put any pre-logout code here
def rdt = session.rdt
session.rdt = null
// redirect uri: "/j_spring_security_logout?spring-security-redirect=$logoutUrl"
if (rdt) {
def link = g.createLink(controller: "RDT")
def redirectUrl = "${SpringSecurityUtils.securityConfig.logout.filterProcessesUrl}?spring-security-redirect=${link}"
redirectStrategy.sendRedirect request, response, redirectUrl
} else {
redirectStrategy.sendRedirect request, response, SpringSecurityUtils.securityConfig.logout.filterProcessesUrl // '/j_spring_security_logout'
}
response.flushBuffer()
}
Both options return me to the 'default' auth login screen and not my alternate rauth one even with the addition of the extra parameter .. How can I route back to an appropriate screen ?? Thanks
In the end I manually set the session variables to null, invalidate the session and a standard redirect ... works ..

How to do proper data binding in Grails

I'm trying to do standard flow with adding entity to DB. Flow should look like this:
User opens link example.co/connection/putForm
edit all fields
submit (POST) to example.co/connection/put
if no errors then he is redirected to ../conncetion/index otherwise he should see previous form with all fields filled (step 2) and error message
For now my code looks like this:
def putForm() {
[
providers: Provider.findAll(),
cities : City.findAll()
]
}
#Transactional
def put() {
// not important part of parsing fields from params
def provider = Provider.get(params.provider)
def startTime = parseStartTime(params)
def path = parsePath(params)
def departurePlace = params.departurePlace
def connection = new Connection(provider: provider, startTime: startTime, departurePlace: departurePlace, path: path)
if (connection.save()) {
redirect controller: 'connection', action: 'index', params: [addedConnection: connection.id] // this part is OK
} else {
render view: 'putForm', params: params, model: [connection: connection] // this sucks... look below
}
}
The problem is that I need to render view putForm but from link .../connection/put. This cause problem that after this render all text fields are empty (step 4 above). Also I have ugly link.
Does Grails has any pattern for this common situation?
PS I cannot use scaffolding.
You're not that far off.. try this:
def putForm() {
[
providers: Provider.findAll(),
cities : City.findAll(),
connection: new Connection() // everything defaulted to empty or whatever you want the default to be
]
}
#Transactional
def put( Connection connection ) {
// NOTE: by putting the connection as the parameter to this action,
// all params.X that match a property X in the connection will auto-
// populate, even the Provider, assuming the value of params.provider.id
// is the id of a provider or blank (in which case
// connection.provider will be null.
// Skip this now
//def provider = Provider.get(params.provider)
//def startTime = parseStartTime(params)
//def path = parsePath(params)
//def departurePlace = params.departurePlace
//def connection = new Connection(provider: provider,
// startTime: startTime, departurePlace: departurePlace, path: path)
if (connection.save()) {
redirect controller: 'connection', action: 'index',
params: [addedConnection: connection.id] // this part is OK
} else {
render view: 'putForm', model: [
providers: Provider.findAll(),
cities : City.findAll(),
connection: connection]
}
}
The thing you need now is to make sure your putForm.gsp actually uses the values you sent down. You should put in things like:
<g:input name="path" type="text"
value="${fieldValue( bean:connection, field:'path' )}" />
and
<g:select name="provider.id" from="${providers}" // note the .id in places here
value="${connection.provider?.id ?: ''}"
noSelection="['':'None']"/>
Note that these will populate with whatever is in the connection sent down each time the page is rendered. So the first time it'll just have the default values, and if it has to rerender due to errors, it'll have the connection values that failed validation.
Hope this helps.

Logic block in Grails URLMappings

My site has urls like 'http://someRandomUsername.mysite.com'.
Sometimes users will try urls like
'http://www.someRandomeUsername.mysite.com'. I'd like to have some
logic in my url mappings to deal with this.
With the mappings below when I hit the page , with or without the
unneeded www, I get:
2012-03-01 14:52:16,014 [http-8080-5] ERROR [localhost].[/ambit] -
Unhandled exception occurred whilst decorating page
java.lang.IllegalArgumentException: URL mapping must either provide a
controller or view name to map to!
Any idea how to accomplish this? The mapping is below.
Thanks!
Jason
static mappings = {
name publicMap: "/$action?/$id?" {
def ret = UrlMappings.check(request)
controller = ret.controller
userName = ret.userName
}
}
static check =
{ request ->
def tokens = request?.serverName?.split(/\./) as List ?: []
def ret = [controller:'info']
if(tokens.size() > 3 && token[0] == 'www')
{
ret.userName = tokens[1]
ret.controller = 'redirect'
ret.action = 'removeWWW'
}
else if(tokens.size() == 3)
{
ret.userName = tokens[0]
ret.controller = 'info'
}
return ret
}
Honestly, like DmitryB said, the best way to do this is via the web server, whether it's IIS, Apache, or Tomcat.
Having said that, I feel the best way to accomplish this in Grails would be using filters.
You could create something like this in your ~/conf directory:
public class StripFilters {
def filters = {
stripWWWFilter(controller: '*', action: '*') {
before = {
def tokens = request.serverName.tokenize(/\./) ?: []
if(tokens.size() > 3 && tokens[0] == 'www') {
def url = request.request.requestURL.toString().replace('www.', '')
redirect([url:url, params: [userName: tokens[1]], permanent: true])
return false
}
}
}
}
}
This should do the trick.

grails webflow error

i have this grails webflow
def currencyDescriptionFlow = {
start {
action {
flow.messageCommand = new MessageCommand()
flow.currencyId = params.id
flow.languageList = Language.findAll([sort: 'codeDescription', order: 'asc'])
}
on('success').to('description')
}
description{
action{
flow.DescriptionMessageList = Message.findAllByKey(flow.currencyId + '_Currency')
}
on('add').to('addSubmit')
on('finish').to('currencyShow')
}
addSubmit{
action {
MessageCommand cmd ->
flow.messageCommand = cmd
if (!flow.messageCommand.validate()){
error()
}
}
on('error').to('description')
on('success').to('description')
}
saveMessage{
action{
def str = flow.currencyId + '_Currency'
messageSevice.descriptionMultiLanguage(str, params.description, params.Language)
}
on('error').to('description')
on('success').to('description')
}
currencyShow{
redirect(controller:'currency', action: "show", id: flow.currencyId)
}
}
when i click the link to redirect to this page, ann error occurs
Error 500: No transition was matched on the event(s) signaled by the [1] action(s)
that executed in this action state 'description' of flow 'message/currencyDescription';
transitions must be defined to handle action result outcomes -- possible flow
configuration error? Note: the eventIds signaled were: 'array<String>
['success']', while the supported set of transitional criteria for this action
state is 'array<TransitionCriteria>[add, finish]'
Servlet: grails
URI: /adm-appserver-manager/grails/message/currencyDescription.dispatch
Exception Message: No transition was matched on the event(s) signaled by the [1]
action(s) that executed in this action state 'description' of flow 'message/currencyDescription';
transitions must be defined to handle action result outcomes -- possible flow
configuration error? Note: the eventIds signaled were: 'array<String>
['success']', while the supported set of transitional criteria for this action state
is 'array<TransitionCriteria>[add, finish]'
Caused by: No transition was matched on the event(s) signaled by the [1] action(s)
that executed in this action state 'description' of flow 'message/currencyDescription';
transitions must be defined to handle action result outcomes -- possible flow
configuration error? Note: the eventIds signaled were: 'array<String>['success']',
while the supported set of transitional criteria for this action state is
'array<TransitionCriteria>[add, finish]'
Class: Unknown
At Line: [-1]
Code Snippet:
Note:
where language is a table in the database
MessageCommand is command object
messageService is a service
descriptionMultiLanguage method in message service for saving message
i try this way of writing code in another action and controoler and it works without any error.
by debugging this code i found the error on (
on('add').to('addSubmit')
on('finish').to('currencyShow')
)
when i remove these 2 lines , no problem found
def currencyDescriptionFlow = {
start {
action {
flow.messageCommand = new MessageCommand()
flow.currencyId = params.id
flow.languageList = Language.findAll([sort: 'codeDescription', order: 'asc'])
flow.DescriptionMessageList = Message.findAllByKey(flow.currencyId + '_Currency')
}
on('success').to('description')
}
description{
on('add').to('addSubmit')
on('finish').to('currencyShow')
}
addSubmit{
action {
MessageCommand cmd ->
flow.messageCommand = cmd
if (!flow.messageCommand.validate()){
error()
}
flow.message = null
}
on('error').to('description')
on('success').to('saveMessage')
}
saveMessage{
action{
Message messageInstance = new Message(language:flow.messageCommand.language,value:flow.messageCommand.value,key:flow.currencyId + '_Currency')
if (!messageInstance.save(flush:true)){
flow.message = "${message(code: 'error.adding.description')}"
error()
}
flow.DescriptionMessageList = Message.findAllByKey(flow.currencyId + '_Currency')
}
on('error').to('description')
on('success').to('description')
}
currencyShow{
redirect(controller:'currency', action: "show", id: flow.currencyId)
}
}

Generating absolute URLs in symfony tasks

I have a quick question regarding the creation of absolute URLs within a symfony task.
Basically I have the following:
/**
* This function returns the link to a route
*
* #param $context context from which to create the config from
* #param $routingText this contains the text to be routed
* #param $object if we are generating an object route we need to pass the object
* #param boolean $absolute - whether to generate an absolute path
* #return string
*/
public static function getUrlFromContext($routingText, $object = null, $application = null, $debug = false,
$absolute = false, $htmlSuffix=true)
{
$currentApplication = sfConfig::get('sf_app');
$currentEnvironment = sfConfig::get('sf_environment');
$context = sfContext::getInstance();
$switchedContext = false;
if (!is_null($application) && $context->getConfiguration()->getApplication() != $application)
{
$configuration = ProjectConfiguration::getApplicationConfiguration($application, $currentEnvironment,
$debug);
$routing = sfContext::createInstance($configuration)->getRouting();
$switchedContext = true;
}
else
{
$routing = $context->getRouting();
}
if (is_object($object))
{
$route = $routing->generate($routingText, $object, $absolute);
}
else
{
$route = $routing->generate($routingText, null, $absolute);
}
if ($switchedContext)
{
sfContext::switchTo($currentApplication);
}
if (strcasecmp($application, 'frontend') == 0 && strcasecmp($currentEnvironment, 'prod') == 0)
{
$route = preg_replace("!/{$currentApplication}(_{$currentEnvironment})?\.php!", $application, $route);
}
else
{
$route = preg_replace("/$currentApplication/", $application, $route);
}
return $route;
}
This allows me to create a URL for any application simply by toggling the context. The big problem I'm having is when creating absolute URLs in a symfony task.
When creating a route in a task I am getting the following:
http://./symfony/symfony/omg-news/omg-news-channel/test002.html
My assumption is that symfony is trying to guess the domain name from the referrer, which when using symfony tasks is non-existent.
The URL is supposed to look like this:
http://trunk.dev/frontend_dev.php/omg-news/omg-news-channel/test002.html
Has anyone been able to create a route which represents an absolute URL from a symfony task? If so have you also encountered this problem, how did you manage to overcome it?
The documentation answers this question. It's enough to edit the factories.yml configuration file:
all:
routing:
class: sfPatternRouting
param:
generate_shortest_url: true
extra_parameters_as_query_string: true
context:
host: example.org
See this similar question where I posted the following answer:
/**
* Gets routing with the host url set to the url of the production server
* #return sfPatternRouting
*/
protected function getProductionRouting()
{
$routing = $this->getRouting();
$routingOptions = $routing->getOptions();
$routingOptions['context']['host'] = 'www.example.com';
$routing->initialize($this->dispatcher, $routing->getCache(), $routingOptions);
return $routing;
}
This method is added to our base task class where we add other common project specific task methods.
Look like you can also trick the Routing only in your task:
sfConfig::set('sf_factory_request_parameters', array('relative_url_root' => "", 'no_script_name' => true));
sfContext::createInstance($this->configuration);
This way you do not have to alter your main config.

Resources