Grails Spring Security: Need if/else logic in GSPs based on user - grails

I'm sure there must be a more elegant way of implementing if/else logic blocks in a GSP based on user info.
Consider the following:
<g:if test="(user logged in) && (this users post) && (post replied to)">
Show options for this post if replied to
</g:if>
<g:elseif test="(user logged in) && (this users post)">
show options if this users post
</g:elseif>
<g:elseif test="(user logged in)">
show options if not this users post
</g:elseif>
<g:else>
show options if no one is logged in
</g:else>
I'm having to use the following to determine if a user is logged in (sec.loggedInUserInfo(field: 'id') != "") I know, I hate it!
I cannot use the nice Spring Security tags as they do not satisfy my requirements and I was trying to implement custom tags but would not be able to implement if/else functionality (or could I)?
Any help would be much appreciated.
EDITED:
The tag I've created:
<g:isOwnerAndStatusEquals post="${post}" status="${Post.REPLIED_TO}">
My post and is replied to
</g:isOwnerAndStatusEquals>
The Tag lib implementation:
class AuthTagLib {
def springSecurityService
def isOwnerAndStatusEquals = { attrs, body ->
def loggedInUser = springSecurityService.currentUser
def post = attrs?.post
def postUser = post?.user
def postStatus = attrs?.status
if(loggedInUser?.id == postUser?.id && job?.status?.equals(thisStatus)) {
out << body()
}
}
}
This above is all fine, but I've no idea, at a high level, how to add if/else to the same tag?

The following appears to be equivalent to your GSP code and does not require you to write any custom tags:
<sec:ifLoggedIn>
<g:if test="(this users post) && (post replied to)">
Show options for this post if replied to
</g:if>
<g:elseif test="(this users post)">
show options if this users post
</g:elseif>
<g:else>
show options if not this users post
</g:else>
</sec:ifLoggedIn>
<sec:ifNotLoggedIn>
show options if no one is logged in
</sec:ifNotLoggedIn>

You can use the SpringSecurityUtils inside the test criteria of the g:if tag. Like this:
<g:if test="${grails.plugin.springsecurity.SpringSecurityUtils.ifAllGranted('ROLE_MYROLE')}"
It will solve the problem since you can combine it with other test. The down-side is that the code becomes somewhat bloated. You can make it look a little bit nicer by adding an import for grails.plugin.springsecurity.SpringSecurityUtils if you use it at multiple places.

Related

Grails best practice on form redirection/rendering

If I have an action def formPage(), I want to be able to save the form on that page, show the form with the previously entered values if there's an error, and show a blank form if it's a success.
My question is: Is it best practice for formPage to call something like saveFormPage(FormPageCommand fpc)? saveFormPage can then render formPage with or without the previously entered values. The problem with this approach is that the user can click the address bar, press enter, and then get a slew of errors since the form hasn't been filled in, and the command object sees all the null or blank values.
Another approach might be to just have a def formPage(FormPageCommand fpc), and ignore errors and just show a blank form if none of the values are filled in.
One might also call saveFormPage(FormPageCommand fpc) and redirect back to formPage() if none of the values are filled in.
What is the best practice here?
Try this example:
Your controller
class MyFormController {
def formPage() {
[cmd: new FormPageCommand()]
}
def saveFormPage(FormPageCommand cmd) {
if (cmd.hasErrors()) {
render view: "formPage", model: [cmd: cmd]
} else {
// Process form
// ......
// Redirect back formPage() to display a new form
redirect action: "formPage"
}
}
}
Your command object
class FormPageCommand {
String optionalValue
String requiredValue
static constraints = {
optionalValue(nullable: true, blank: true)
requiredValue(nullable: false, blank: false)
}
}
Your formPage.gsp
<g:form controller="myForm" action="saveFormPage" method="POST">
<label>Required value *</label>
<g:textField name="requiredValue" value="${cmd.requiredValue}"/>
<g:if test="${cmd.errors.getFieldError('requiredValue')}">
<g:message error="${cmd.errors.getFieldError('requiredValue')}"/>
</g:if>
<label>Optional value</label>
<g:textField name="optionalValue" value="${cmd.optionalValue}"/>
<g:submitButton name="submit"/>
</g:form>
It should serve your need.
Cheers.
I'm not sure I fully understand your question, but to get you started I recommend the book "Grails in Action". It contains a lot of best practices and chapter 7 on data binding and error handling may give you some good advice and ideas.
I encountered this problem before and solved it this way:
My registration form has a button:
<g:submitButton name="register" value="Register" />
So the incoming params map should have the "register" key with "Register" value.
Then I check the existence of this parameter in my controller:
def register(UserRegisterCommand urc) {
if (params.register) {
//some registration actions
}
}
In other words I check in the controller if the "Register" button was clicked, if yes - then perform registration actions. If no - the "register.gsp" is rendered.
I hope this will help.

selecting content to display based on user in Grails with Spring Security

I have a Grails app that has a Domain for People, Location, Pictures and Users. Controllers and Views have been generated. I have my index page sec:tagged so that the admin can see the controllers to upload files, create data, and users.
Users have a username, password and location. When a user logs in I want them to see the files that pertain to their location. There can be multiple users attached to a location. But I want every user with that location to see the associated files. I stared with the #Secure but moved to the sec: tags. Now I just need to limit the display of files based on location. Any advice?
Here is the code I have so far for the user display
<sec:access expression="hasRole('ROLE_USER')">
<% def user = springSecurityService.getCurrentlyLoggedInUser()
def aPerson = user.person
def locations = Location.findAllByPerson(aPerson)
locations.each ${it.picture} %>
<g:select name="picture" from="${aPerson.all()}"/>
</sec:access>
This displays nothing on the User page.
First thing, avoid <% %> at all costs. Secondly, I have no idea what you think this code is supposed to be doing:
<g:select name="picture" from="${aPerson.all()}"/>
Lastly, it would help to see your Domain code to understand the actual relationship between all of them. But moving forward with what you have provided, I'd approach it like this:
controller
def someAction() {
def user = springSecurityService.getCurrentlyLoggedInUser()
def aPerson = user.person
def locations = Location.findAllByPerson(aPerson)
[aPerson: aPerson, locations: locations]
}
view
<!-- only use the expression tag if you actually need an expression -->
<sec:ifAllGranted roles='ROLE_USER'>
<g:each in="${locations}" var="location">
<!-- do something with each location -->
</g:each>
<g:select name="picture" from="${aPerson.all()}"/>
</sec:ifAllGranted>

Open the same webpage, but with another content

Im developing a webapp that is feeded via a server. Its index presents some shops deals, having all of these deals ann id. In the .gsp, i have this code:
<div id="content">
<g:each in='${promos}' var='promo'>
<div id="up_box"></div>
<div class="deal_container">
<div class="images_container"><img src="${createLink(action:'showImage',controller:'image',id:promo.fotoPrincipal.id)}" width="120" height="105" />
</div>
<g:link controller='promotion' action='openPromo' params="[promoid:promo.id]">
<div class="oferta">${promo.name}</div>
</g:link>
<g:link controller='promotion' action='openPromo' params="[promoid:promo.id]">
<div class="flecha">
</div>
</g:link>
</div>
That means, when I click in one of the links, it will open an url like this:
app/promotion/openPromo/3
In case the deal with the id "3" is clicked. I have a "general" deal details page. I want that page to load always, and fulfill its details dinamically, depending the deal the user clicked. How can I do that?
I don't know if I have been clear in my explanation, if i haven't please ask.
Thank you!
Both links will be handled by the openPromo action in the PromotionController and will be passed the id of the promo as promoid.
You can then load the appropriate promo along with any other related offers you want the user to see and give these to the view. Eg:
class PromotionController {
def openPromo() {
Promo promo = Promo.get(params.promoid)
// Load any other relevant data or offers
render (view:'openPromo', model:[promo:promo, <any other model data here>]
}
}
You can then use the model data in your gsp.
You can do it adding that general info to a Template and then render it from the Controller like this
// render a template to the response for the specified model
def theShining = new Book(title: 'The Shining', author: 'Stephen King')
render(template: "book", model: [book: theShining])
You´ve got more info about the render in this link from the Grails documentation.
http://grails.org/doc/latest/ref/Controllers/render.html
It´s easy to do it, you can add a createLink like the first ones whith calls to an action who will render that template.
Hope it helps ;) and good luck!
I did it this way, after asking my workmate:
def openPromo() {
Promotion promo = Promotion.get(params.promoid)
[promo:promo]
}
Almost what you said David, but without rendering, if I name openPromo my gsp.
Thank you!

Grails controllers pass parameters

My controller is the folowing:
def participated = {
def temp = ConferenceUser.get(params.temp)
def prizes = Prizes.findAllByConferenceUser(temp) // find all rooms where current computer is
def subms = Submissions.findAllByConferenceUser(temp) // find all rooms where current computer is
[temp: temp, priz: prizes, subm: subms]
}
But somehow, when I successfully update a conference value, I wanna go back to the initial page (participated) but I don't know how to pass back the params.temp. (if I do a simple redirect, as the controller is expecting params.temp, it will give me an error because I cannot search prizes with a null object as parameter. So, imagine my update controller is the following:
def update = {
def saveParamshere = params.temp
...
...
(code here)
...
...
redirect(action: "participated", params: [temp: saveParamshere])
}
This code isn't working. How can I successfully go back to my main page and pass in params.temp ?
I think the problem may be, that you are calling update action by submitting form (I suppose). Maybe you are not passing temp value from that form? You can do it by embedding temp as hidden input field into form, or apply it to url by param attribute on form tag.
Using hidden field it might be something like this (in your view file):
<g:form controller="somecontroller" action="update">
(...)
<g:hiddenField name="temp" value="${temp}" />
(...)
</g:form>
Using params attribute:
<g:form controller="somecontroller" action="update" params="[temp : temp]">
(...)
</g:form>
I didn't test any of these so there might be some issues, especially in the second approach.
You could put the params in the flash scope, which lives for two requests, or put them in the session and retrieve them that way.
Here is a link to the grails docs on usage of flash scope:
Grails - Controllers - Controller Scopes

Grails: problem with paging and ordering in a view template

How do I structure my pages and partial templates so that Ajax will play nice with <paginate> and column sorting?
I currently have a search.gsp page with a remoteField that calls a controller to update a template. This all works fine. However, the column sorting and paging actions cause my search.gsp to be completely replaced by the template view.
From my search.gsp:
<div id="searchBox">
Enter a key or phrase: <g:remoteField name="searchBox"
update="resourceSearchResultPanel" paramName="q"
url="[controller:'resourceEntry',action:'searchForResources']"
/>
</div>
<div id="resourceSearchResultPanel" />
My controller handles the search request like so:
def searchForResources = {
params.max = Math.min(params.max ? params.max.toInteger() : 10, 100)
params.offset = params.offset ? params.offset.toInteger() : 0
log.debug "Handling search post action"
def q = params.q ?: null
log.debug "Search phrase is $q"
def searchResults
if (q) {
searchResults = [
results: ResourceEntry.search(q,[offset: params.offset, max: params.max]),
resultCount: ResourceEntry.countHits(q),
q: q.encodeAsHTML()
]
}
render(template:"resourceSearchResultPanel",model:searchResults)
}
The _resourceSearchResultPanel.gsp is just a table with this <paginate> tag:
<g:paginate action="searchForResources" total="${resultCount}" params='["q":"${q}"]' />
The problem is that when the <paginate> tag calls the controller, the entire page is refreshed with the contents of the _resourceSearchResultPanel.gsp template, while I just want the _resourceSearchResultPanel.gsp itself to be refreshed inside search.gsp.
There's no update attribute like there is in the remoteField tag...
The paginate tag doesn't support generating ajax links so you'll have to write your own verison of the tag that calls remoteLink instead of link.
cheers
Lee
I would suggest to you remote pagination plugins for Grails. This would suffice your requirement. For more details please refer to following site:-
http://www.grails.org/plugin/remote-pagination
Please feel free to revert incase of any concern.

Resources