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>
Related
I come from a java programming background. Currently trying to learn RoR by building a little test app. I have a database setup with a dropdown menu populated from the database. How would I get user choice from the dropdown menu?
In java it would be something like a JButton with an actionlistenr attached to it. I have my submit button figured out, but how do I attach it to my dropdown menu such that when the button is pressed it gets the user's current choice? My action/view are listed below. The end idea here is that the user selects something from the dropdown menu, clicks the submit button, and then the submit button calls another action and passes the user's choice along to to the action.
This is my index action in my controller:
def index
#ccmh = Ccmh.all
#column_names = Ccmh.columns.map {|a| a.name}
end
The action gets the model's database. It also gets the column names of the database in an array format.
This is index.html.erb:
<h1>Make Your Selections</h1>
<p>
Y-Axis
</p>
<select name="id">
<%= #ccmh.each do |abc|%>
<option value="<% abc.id %>"><%= abc.description %></option>
<% end %>
</select>
<p>
X-Axis
</p>
<select name="id">
<% #column_names.each do |a|%>
<option value ="<%#column_names.index%>"><%= a %></option>
<% end %>
</select>
<br>
<br>
<button name ="button" type ="submit">Button</button>
This creates 2 dropdown menus. One which is populatd with the value of the description column of the databse, the other is the column names themselves. The idea is that the user selects some data from the database, and then the data will be passed to another view class and rendered as a graph. Not 100% sure how I want to handle doing that as of yet, but I figured some context may help.
It needs to be in a form element. The form will determine which action the button will issue a post or get to, along with other things like the verb (post, get etc.)
Rails has helpers for these. If you post your controller and view code we can offer advice on how to make it efficient and workable.
Also try generating a scaffold. This should give you an idea how to wire up forms, buttons and inputs.
In the command-line:
rails generate scaffold Post name:string title:string content:text
would generate a model and database migration for a posts database table. It will also generate controller methods and views to allow you to edit and create them.
rake db:migrate
will create the table in your database. you can now restart your sever and play with the new pages. Try navigating to /posts and looking at the new files in the controllers and views directories.
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.
edit-
I am trying to return a list of portfolios, along with the last 5 publications attached to each portfolio from my 2 domain classes. I am getting the last total 5 publications back and each list displays all 5. The query is not returning that particular instances own publications. Kellys great ideas put back into another track.
I have created the method in the portfolio controller which is the hasMany side to the publications which belongsTo the portfolio domain class.
I just cant seem to get the portfolios to list their own publications. If I change the eq portfolios, it all works fine, except each portfolio list shows the same publications.
How do I load each portfolio and list the last 5 publications. This is rendered from the portfolio/list page as a partial.Is this the issue maybe. Should I just render it from a new view which is not associated with the portfolio list action??
Newbie to grails and have read and read the doc's, just cant seem to get the params query to return correctly. Help
def _webList (){
//def per = Portfolio.properties
def portfolios = Portfolio.list(params.id)
def results = Publication.withCriteria {
eq('published', 'Yes')
order('lastUpdated', 'desc')
maxResults(5)
}
def reportscount = Publication.count()
[ portfolios: portfolios, results: results, reportscount: reportscount]
}
I can show the the sql log if needed.
EDIT
The following code is the entire partial from file _webList.gsp. The top div -alert loads on the page, but the content within the div property-list portfolio fails to load. Using Kelly's hibernate criteria produces the query in the sql log but not results or styles or anything are return to the view??. weird.!
<div class="alert alert-info" xmlns="http://www.w3.org/1999/html">Permissions apply to <strong>editing</strong> publications.<br>
<div style="display: inline;"><p>Click portfolio name to read or edit publications. Total number of sites: <strong>${rsNumb}</strong> | Total number of publications: <strong>${reportscount}</strong> </p>
</div>
</div>
<div class="property-list portfolio">
<g:each in="${portfolios}" var="portfolioInstance">
<div class="site-listing">
<div><span class="label">Site Name:</span><g:link action="show" id="${portfolioInstance?.id }">${portfolioInstance?.portfolioName?.encodeAsHTML()}</g:link></div>
<div><span class="label">Site Description: </span>${portfolioInstance?.portdescrip?.encodeAsHTML() }</div> <br>
<div><span class="label">Site Administrator: </span>${portfolioInstance?.profile?.portfolioAdmin?.encodeAsHTML() }</div> <br>
<div><span class="label"> Total publications:</span><span class="badge badge-success"> ${portfolioInstance?.publications?.size()}</span> </div>
<!-- whatever else you need here -->
<!-- now iterate through the pubs -->
<g:if test="${portfolioInstance?.publications}">
<g:set var="publicationInstance" />
<ul class="site-publication">
<li class="fieldcontain">
<span id="publications-label" class="property-label"><g:message code="portfolio.publications.label" default="Last 5 published publications:" /></span>
<g:each in="${portfolioInstance.publications}" var="publicationInstance">
${publicationInstance?.id}
<span class="property-value" aria-labelledby="publications-label"><g:link controller="publication" action="show" id="${publicationInstance.id}">${publicationInstance?.encodeAsHTML()}</g:link></span>
<!-- and again whatever else you need here -->
</g:each>
</g:if>
</g:each>
</div>
EDIT - sql log below
Hibernate: select this_.id as id5_1_, this_.version as version5_1_, this_.date_created as date3_5_1_, this_.last_updated as last4_5_1_,
this_.portdescrip as portdesc5_5_1_, this_.portfolio_name as portfolio6_5_1_, this_.portpublished as portpubl7_5_1_, this_.profile_id as profile8_5_1_,
this_.status as status5_1_,
publicatio1_.portfolio_id as portfolio5_5_3_,
publicatio1_.id as id3_, publicatio1_.id as id2_0_,
publicatio1_.version as version2_0_,
publicatio1_.date_created as date3_2_0_,
publicatio1_.last_updated as last4_2_0_,
publicatio1_.portfolio_id as portfolio5_2_0_,
publicatio1_.publication_content as publicat6_2_0_,
publicatio1_.publication_name as publicat7_2_0_,
publicatio1_.published as published2_0_,
publicatio1_.publisheddate as publishe9_2_0_,
publicatio1_.publishedemail as publish10_2_0_,
publicatio1_.pubproduct_id as pubproduct11_2_0_
from portfolio this_ left outer join publication publicatio1_
on this_.id=publicatio1_.portfolio_id where (this_.status=?)
and (publicatio1_.published=?) order by publicatio1_.last_updated desc
You are getting the java.lang.ClassCastException because portfolios is a list and portfolio in the Publication class (probably) isn't, it's probably an id (long); can't cast a list in any meaningful way to compare to a long in eq ('portfolio', portfolios)
You should not need two separate queries since the domain classes are related.
--EDIT--
Editing to not use separate action and just use list action. I've not been able to get the include to work either, but below is pretty much what I've in dozens of cases. If there is some reason you can't do this then maybe a new question on just using the include mechanism might generate some attention.
I'm not sure what your current list action looks like. This is how I would code a list method to get ALL Portfolios and their last 5 Publications. No need for any parameters because I'm returning ALL portfolios.
//PortfolioController
def list (){
def portfolios = Portfolio.createCriteria().list {
//if you needed to filter the list by for example portfolio status or something you could add that here
or {
eq('status','ACTIVE')
eq('status','PENDING')
}
publications(org.hibernate.criterion.CriteriaSpecification.LEFT_JOIN) {
eq("published", "Yes")
order("lastUpdated", "desc")
firstResult(5)
}
}
[portfolios: portfolios, portfolioCount:portfolios.size()]
}
Now the Publications come pre-attached to their Portfolio.
The LEFT_JOIN part of the above insures you get back a list of portfolios with ONLY those Publications attached that meet the criteria; if you leave that out it defaults to an inner join and when you iterate you will get ALL of the Publications for that Portfolio (even if they don't meet the criteria).
Then in your gsp iterate through the portfolios - it can either be directly in the list.gsp or in a template rendered in list.gsp. If you put it in a template called _webList.gsp you would render it in the list.gsp as
<g:render template="weblist" model="['portfolios': portfolios]" />
This is either in list.gsp or _webList.gsp - I would start with it directly in list.gsp to make sure it's all working.
<g:each in="${portfolios}" var="portfolioInstance" status="i">
${portfolioInstance?.portfolioName?.encodeAsHTML()
<!-- whatever else you need here -->
<!-- now iterate through the pubs -->
<g:each in="${portfolioInstance.publications"} var="publicationInstance" status="j">
${publicationInstance.id}
<!-- and again whatever else you need here -->
</g:each>
</g:each>
--EDIT--
firstResult(5) seems to do the trick.
--EDIT--
You'll notice I have the maxResults(5) commented in there - I'm having trouble getting that to work correctly. It seems to control the number of portfolios that are returned even though it is in the association block. Maybe someone else will see this and add that piece of the puzzle - or tinker with it yourself. I'll keep trying and update if I figure it out.
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!
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