Grails - How to fine tweak events/actions? - grails

I'm very new to Grails so pardon me, what I want to do may be very obvious to others. It will help me understand Grails and how the underlying magic works (or not work). Answers must be in Groovy/Grails, no javascript since I understand how javascript works.
Say I have the files:
User.groovy
UserController.groovy
I'm using scaffolding to CRUD the records in the USER table. Now I want to tweak it a bit.
in file User.groovy:
class User {
String name
static constraints = {
name blank: false
}
}
I want UserController.groovy(Is this the file I should edit?) to check if the submitted username is Bill, then automatically replace username with William then continue to create record in database.
In the web form I type in the name field: Bill and click SUBMIT
After the database is updated, I check the record and the username should be William in the USER table database.
Question #1 (Basic) How can I do this?
Now for something a bit trickier, what if after the William record is created in db#1, and I want to connect to a different db#2 and insert William in the USER table there?
So when I click SUBMIT once, both records will be inserted in 2 different databases almost simultaneously? What this action would do is insert the record in db#1 then connect to db#2 and then do the same insert in serial.
Question #2 (Intermediate) Where do I look in the Grails folders/files to modify this action behavior?

You have to create view which is associated with your controller. In that view, you have to create form, that will enable users to enter username, something like this:
<g:form controller="user" action="save">
<g:textField name="username"/>
<g:submitButton name="save" value="save"/>
</g:form>
Now when user sumbits the form, save action of user controller is invoked. All parameters that where passed to controller action is being held in params map. So in your save action, you can access username like this:
def save = {
def user = new User(username: params.username)
// OR
def user = new User(params)
// you can change username like so
user.username = "William"
user.save()
}
This is very quick example, I advise you to study documentation and some tutorials maybe which will give you more knowledge of all the concepts and tools that Grails provides for you.
With writing user to two databases there is a slight problem. Grails doesn't provide ability to have multiple databases connected to your app out of the box. There is a plugin, called Datasources which allows you to define multiple data sources in your application, but the drawback is that you have to define which domain classes are connected to which data source. Thus you can't have a user domain class for your primary database and secondary, you have to create two domain classes, one for each data source.

I'll just answer the basic :)
Yup, you can do it in UserController.groovy. Say, you have this form:
<g:form action="someAction" method="post">
<input type="text" name="name" ... />
...
</g:form>
In the controller, you can have an action like this (there are multiple possible approaches):
def someAction = {
def nameVar = params.name // get the text this way
// process nameVar here
}
As for the database access... depends how you want to approach it. Grails suggests that you do something like:
User theUser = //get the user
theUser.name = //the value
theUser.save(flush:true)
However, I've encountered time and again some issues with Hibernate so I don't always use that approach. Instead, I do the usual Java-like programming like:
...
theUser.executeUpdate("UPDATE <table_name> ...")
theUser.save(flush: true)
...
Then just select from the database
...
User.executeQuery("SELECT ...")
...
Hope this helps ;)

For Q#1 you should modify the save function in your controller. There you can check the parameters for a specific username or modify the new created user object as you like.
For Q#2 i reccomend you to have a look at the Datasources plugin

Q1: This is really a job for GORM Hibernate events as it involves business logic constraint for you domain.
beforeInsert(){
this.name = "William"
}
Q2: Using multiple data sources is integrated in Grails 2.0. No need for the plugin.

Related

connect two gsp screens to three domains in grails

so I am working on a web app when the users click on create on the list screen, it takes them to a page where they have to enter some information and then they click on "next" and it will take them to another gsp page where they have to enter data for two domains but none of the data are stored yet in the tables but when they click on "create" button, everything get stored in the database
I was looking for examples but couldnt find any.
I know how to call the record and edit it since all the domains or the tables share id number so I can use it to retrive data. but my problem is when I transfer from the first gsp screen to another I want to save the instance and then when the users click on create, data goes to the three tables
any idea how to do that? I am still beginner and trying to learn
thank you
It sounds kind confusing what you wanna do and I am not sure I completely understood. Because you want to change the whole page, and yet, not to lose those previous answers, right?. Is there a specific reason why you want to do this?
I believe you could sent all the information from the first gsp to a controller and save everything in the "session" (e.g session.name = params.name, session.age = params.age), redirect/render the other gsp and later on, you get the info back from the session plus the info that just came and save everything. This is probably not a very good solution, but this is the only way I figured this out.
:)
Just an idea, I haven't used it until now, but I will some day in the future to modify the dialog-flow in my current application...:
Wouldn't that be a good example for the webflow-plugin?
This is what command objects are used for. Any time you have a collection of data that you want to collect in a form and then "generate" multiple domain objects from, then this is the way.
The idea is to create a single class that has all the information that you want to collect on the form. You post the filled form data back to the controller save action which then validates that the data is complete using the command object constraints. Once you are happy with everything, you then use the data in the command object to create/update your domain objects.
We consider the use of command objects a Grails best practice. You can provide custom validation support in the command object that looks for and validates relationships in the data that are difficult to do with the domain objects. We often write factory methods in the command class that produce a new or updated domain object, making it very convenient for unit testing.
See the "Command Objects" section of the "Web Layer" in the manual for details. In Grails 2, command objects are classes with the #Validateable annotation and in Grails 3 they implement the Validateable trait. If you declare your command class as an inner class in the controller, then it is automatically validateable. We've found that we prefer to declare them in src/groovy rather than as inner classes because they are easier for someone unfamiliar with the code to find.
So amongst all of these answers you have your answer but in all honesty I think it is well beyond your own comprehension at this point.
Assuming you have this example as a form
http://code.runnable.com/UevQr3zfd_oaAAGn/jquery-ui-tabs
On tab 1 you have
Name
Age
On tab 2 you have
Address
Postcode
Then you have two domain class
Class User {
String name
String age
Address address
}
Class Address {
String address1
String postcode
}
So a user has name age and also binded to address, whilst address has address1 and postcode
now your controller action
def save(MyBean bean) {
Address address = new Address(bean.loadAddress()).save()
User user = new User()
def userMap = bean.loadUser()
user.age=userMap.age
user.name=userMap.name
//The above object that got saved first
user.addresss=address
user.save()
render "hopefully this should have saved it as expected"
}
In src/main/groovy/yourPackage/MyBean.groovy
package yourPackage
import grails.validation.Validateable
Class MyBean implements Validateable{
String name
String age
Address address
String address1
String postcode
//Now declare your constraints like your domainClass add validator where required to add additional verification/validation to your objects sent back
//This will return all the objects required as a map to save address domain class
protected Map loadAddress() {
Map results=[:]
results.with {
address1=address1
postcode=postcode
}
return results
}
//this will return the user object
protected Map loadUser() {
Map results=[:]
results.with {
name=name
age=age
}
}
}
Other fairly complex validation bean examples:
PhotosBean CustomerChatBean ScheduleBaseBean
Other points of reference:
As I say I think as a beginner this may take you a while to get your head around but hoping with what is provided it will become a lot clearer now
E2A
It is really complicated!! can I have two gsp screens instead of jquery tabs
That doesn't make much sense.
You can have two actions which one just passes params onto 2nd gsp ?
So
def TestController {
def index() {
render view: page1
}
//where page 1 is the first form and submits to action2
//action2 picks up parmas from page1
def action2() {
render view: page2, model:[params:params]
}
}
in page2.gsp you have
<g:form action="action3">
<g:hiddenField name="originalName" value="${params.originalValue}"/>
<g:hiddenField name="originalName2" value="${params.originalValue2}"/>
<g:hiddenField name="originalName3" value="${params.originalValue3}"/>
Then your actual form content
The problem with doing this this way, is does action2 need to verify params received from page1 ? if so it needs to either render original page or page2 depending.
Once submitted to page2 the hiddenFields can be tampered with by end user so what was validated may be invalid now. You will need some form of a way of revalidating all those again.
Using validation methods above you could just call the validate() functions or maybe build some md5 check of initial values vs what is now sent from page2.
Either way if you don't care about validation and just want to see it work then above is the simplest way.
can I have two gsp screens instead of jquery tabs
in page1 you can just do <g:include action="page2"> and include a 2nd gsp within first but in all honesty page 1 could have just contained both actions in 1 page. which is why it don't make sense

How can I pre-populate a hidden form field in grails?

I have just gotten started with Grails and I have a very basic application running. I want to pre-populate a hidden form field with a random string.
What is the best way to do this? I have looked at the taglib but I am not sure what the best practice is for this sort of thing. Should I create a class in the src/java or src/groovy folder or is there a better way to get this done?
Lastly, and I know this is a very basic question, but if I do create a class or taglib, how exactly is that called from within the .gsp page?
Thanks!
If your action looks like this
def create() { [orgInstance: new Org(params)] }
it means that a new Org object is passed to your view which can be referenced as orgInstance
Since the model [orgInstance: new Org(params)] is a map, you can simply add another parameter:
def create() { [orgInstance: new Org(params), hiddenValue: 'something random'] }
This can be used in your .gsp in the following way:
<input type="hidden" name="test" value="${hiddenValue}" />
Regarding your other question: a custom taglib is used in the same way as the other Grails-Tags: <g:myTag ...>...</g:myTag> . You can change the namespacegto whatever you like -g` is the default. See the documentation for more details: http://grails.org/doc/latest/ref/Tag%20Libraries/Usage.html

How to create multiple domain objects from a GSP page

I have a Person class with two properties: name and address. I want to build a GSP page which allows for 10 users to be created at one time. This is how I'm implementing it and was wondering if there is a better way:
First, make 20 text boxes in the GSP page - 10 with someperson.name and 10 with someperson.address field names (make these in a loop or code them all individually, doesn't matter).
Second, process the submitted data in the controller. The someperson object has the submitted data, but in a not-so-nice structure ([name: ['Bob', 'John'], address: ['Address 1', 'Address 2']]), so I call transpose() on this to be able to access name, address pairs.
Then, build a list of Person objects using the pairs obtained from the previous step and validate/save them.
Finally, if validation fails (name cannot be null) then do something... don't know what yet! I'm thinking of passing the collection of Person objects to the GSP where they are iterated using a loop and if hasErrors then show them... Don't know how to highlight the fields which failed validation...
So, is there a better way (I should probably ask WHAT IS the better way)?
You should use Grails' data-binding support by declaring a command object like this
class PersonCommand {
List<Person> people = []
}
If you construct your form so that the request parameters are named like this:
person[0].name=bob
person[0].address=england
person[1].name=john
person[1].address=ireland
The data will be automatically bound to the personCommand argument of this controller action
class MyController {
def savePeople = {PersonCommand personCommand->
}
}
If you call personCommand.validate() it might in turn call validate() on each Person in people (I'm not sure). If it doesn't you can do this yourself by calling
boolean allPersonsValid = personCommand.people.every {it.validate()}
At this point you'll know whether all Person instances are valid. If they are not, you should pass the PersonCommand back to the GSP and you can use the Grails tags:
<g:eachError>
<g:hasErrors>
<g:renderErrors>
to highlight the fields in errors. If you're not exactly sure how to use these tags to do the highlight, I suggest you run grails generate-all for a domain class and look at the GSP code it generates.

Apostrophe CMS: Engine Creation

I'm attempting to create a Product Engine for Apostrophe. I'm having trouble extending the Page Settings form, at the moment I want to add a simple textarea to add a synopsis to the page - eventually I want to add Product settings but I need to get the basics working first.
I've created a form and a settings partial, it's displaying fine and saving the data (with the help of a little hack - might not be correct). The trouble I'm having is when you edit a page the data is not being pulled back in to the form. To be honest I probably doing something fundamentally wrong but I lack experience in Symfony.
My table schema
ccProduct:
tableName: cc_product
actAs:
Timestampable: ~
columns:
page_id:
type: integer
notnull: true
synopsis:
type: text
relations:
Page:
class: aPage
local: page_id
foreign: id
type: one
onDelete: CASCADE
My form ccProductEngineForm.class.php
class ccProductEngineForm extends ccProductForm
{
public function __construct($object = null, $options = array(), $CSRFSecret = null)
{
// when editing the page the values are not show, this is an attempt to get it to work - it still doesn't :(
$page_id = sfContext::getInstance()->getRequest()->getParameter('id');
sfContext::getInstance()->getRequest()->setParameter('page_id', $page_id);
$ccProduct = Doctrine::getTable('ccProduct')->findOneByPageId($page_id);
if ($ccProduct) {
sfContext::getInstance()->getRequest()->setParameter('id', $ccProduct->getId());
}
// aPageForm object is passed in
parent::__construct(null, $options, $CSRFSecret); // construct normally
//$this->mergeForm(new aPageForm($object, $options, $CSRFSecret)); // merge the aPageForm - Nope, ignore it!?
}
public function setup() {
parent::setup();
$this->useFields(array('synopsis'));
$this->widgetSchema->setNameFormat('enginesettings[%s]');
$this->widgetSchema->setFormFormatterName('aPageSettings');
}
protected function doSave($con = null)
{
// page_id is missing! possible bug? BaseaActions.class.php ~ 520
$this->values['page_id'] = sfContext::getInstance()->getRequest()->getParameter('enginesettings[pageid]');
parent::doSave($con);
}
}
Thanks in advance for any help
EDIT:
Thanks for your answer Tom, I'll try to add a little more detail.
I was aware that a page object is passed into the Engine, but I wasn't exactly sure what do to with it - see my confused line of code:
//$this->mergeForm(new aPageForm($object, $options, $CSRFSecret)); // merge the aPageForm - Nope, ignore it!?
To clarify my 'product' is a page that uses the ccProduct engine. I now want to add extra information to that page. Does that make sense? In your words..
Are you trying to actually create a unique product that has its sole "home" on a product engine page? That's what subclassing ccProductForm would do
Yes :)
EDIT 2:
Following Tom's first suggestion (Apostrophe CMS: Engine Creation) I was able to extend the aPage table with my extra fields and the Engine is now saving these.
However, the standard aPageTable::getPagesInfo function isn't returning the fields I saved. I assume I'll have to select these separately?
EDIT 3:
aPageTable::retrieveBySlug() will do the job :)
REVISITED
I decided to revisit this and try Tom's second approach..
The other approach (if for whatever reason you don't want extra columns in aPage) is to keep your ccProduct table and fetch the relevant one
I managed to get this working, my ccProductEngine form constructor now looks like this..
class ccProductEngineForm extends ccProductForm
{
public function __construct($aPage = null, $options = array(), $CSRFSecret = null)
{
$page_id = $aPage->getId();
if ($page_id) {
$product = Doctrine_Core::getTable('ccProduct')->findOneByPage_id($page_id);
if ($product) {
$ccProduct = $product;
} else {
$ccProduct = new ccProduct();
}
}
parent::__construct($ccProduct, $options, $CSRFSecret);
}
I hope this helps someone :)
The main thing to remember is that your engine settings form receives a page object as the first parameter to the constructor, and you need to associate whatever your data is with that page object. Usually the engine settings form is a subclass of aPageForm, but it does not have to be. All that is required is that you associate your product object(s) with the page object in some way. Depending on your goals you probably want a refClass that creates a one-to-many relationship between product engine pages and products, and a form for manipulating those relationships.
From your code it is difficult for me to guess what you really want to do. Are you trying to actually create a unique product that has its sole "home" on a product engine page? That's what subclassing ccProductForm would do. Or do you just want to select an existing product from the product table and associate it with each engine page? Or do you want to select one or more products and associate them with the engine page?
Stuffing things into the request object is definitely not the way to go (:
Please clarify and I can help you further.
Tom Boutell, Apostrophe senior developer
There are two approaches you could follow here.
One is to just extend the schema of aPage in your project level config/doctrine/schema.yml file:
aPage:
columns:
synopsis:
type: text
Now every aPage object has a synopsis column, which will be null by default, and your engine settings form can just manipulate that one column. Your engine form subclasses aPageForm. You don't need a constructor at all (the default one will suit you), and your configure() method is just:
$this->useFields(array('synopsis'));
Boom, you have a textarea for the synopsis that appears when the page type is set to this engine. You don't need a ccProduct table at all.
The other approach (if for whatever reason you don't want extra columns in aPage) is to keep your ccProduct table and fetch the relevant one. Your engine form class then does not subclass aPageForm, and your constructor has to use the page passed to it to fetch the related ccProduct object (using a Doctrine relation) or create a new one if there is none yet. This is not difficult, but so far it looks like you can keep it even simpler by just adding a column to aPage.

Grails with SpringSecurity, check if the current user can access controller / action

I'm currently developing a menu for my application that should be able to display only the controllers that the current user can access (requestmap defined in the database).
How can I check if the current user has access to a specific controller and action?
To check roles in view :
Spring security plugin provides ifAllGranted, ifAnyGranted, ifNoneGranted etc., tags to check roles
For example, to check Admin Role of logged in User :
<sec:ifLoggedIn>
<sec:ifAllGranted roles="ROLE_ADMIN">
Admin resource
</sec:ifAllGranted>
</sec:ifLoggedIn>
(tested in grails-2.2.2 and springSecurityCorePlugin-1.2.7.3)
org.grails.plugins.springsecurity.service.AuthenticateService authenticateService = new org.grails.plugins.springsecurity.service.AuthenticateService()
def isAdmin = authenticateService.ifAllGranted('ROLE_ADMIN')
if(isAdmin) {
println 'I am Admin'
}
This question is pretty old, but I thought I'd post at least an answer that seems to work with Grails 2.0. If you are using the spring security plugin, there's a tag lib included called grails.plugins.springsecurity.SecurityTagLib.
The tag-lib has a protected method, hasAccess() which can take the same params map that you give the g:link tag. So, if you extend SecurityTagLib, you can call hasAccess() and get the behavior you want. Why this isn't externalized into a service that can be injected is beyond me as the functionality seems to fulfill an obvious need.
We use this to wrap the g:link tag and only generate a link of the user has access to the target page:
def link = { attrs, body ->
if( hasAccess(attrs.clone(), "link") ) {
out << g.link(attrs, body)
}
else {
out << body()
}
}
When dealing with permissions in views and taglibs, you can use the AuthorizeTagLib that's provided by the plugin.
For example, if you don't want a menu item to appear in your list for unauthenticated users, you might use:
<g:isLoggedIn>
<li>Restricted Link</li>
</g:isLoggedIn>
If you have more specific roles defined and those roles are tied to your controller/action request mapping, you can use other tags, such as:
<g:ifAllGranted role="ROLE_ADMINISTRATOR">
<li>Administrator Link</li>
</g:ifAllGranted>
In my experience, there's not yet a good way to tie the request mapping to your markup - I think you're going to have to use some of the above tags to limit access to content within a particular GSP.
I think that Burt Beckwith has a future modification (and is currently providing a beta version) to the plugin that integrates some ACL stuff that might solve this problem better in the future, but for now, I think the best approach is a hybrid request map + GSP tags one.
Not sure of the situation when this question was originally asked, but now you can check to see if a user is in a specific role by using SpringSecurityUtils.ifAllGranted() which takes a single String which is a comma delimited list of roles. It will return true if the current user belongs to all of them.
if(SpringSecurityUtils.ifAllGranted('ROLE_ADMIN,ROLE_USER')) {
Obviously, you can simply pass one role to the function if that is all you need. SpringSecurityUtils also has methods like ifAnyGranted, ifNotGranted, etc, so it should work for whatever it is you are trying to accomplish.
SpringSecurityUtils is a static API, so you don't need to create a private member named springSecurityUtils or anything like that.
You have to configure the file config/SecurityConfig.groovy (if it does not exists, create it, this overrides the default Security Configuration)
Add this entry:
requestMapString = """\
CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
PATTERN_TYPE_APACHE_ANT
/=IS_AUTHENTICATED_REMEMBERED
/login/auth=IS_AUTHENTICATED_ANONYMOUSLY
/login/authajax=IS_AUTHENTICATED_ANONYMOUSLY
/login/authfail=IS_AUTHENTICATED_ANONYMOUSLY
/js/**=IS_AUTHENTICATED_ANONYMOUSLY
/css/**=IS_AUTHENTICATED_ANONYMOUSLY
/images/**=IS_AUTHENTICATED_ANONYMOUSLY
/plugins/**=IS_AUTHENTICATED_ANONYMOUSLY
/**=IS_AUTHENTICATED_REMEMBERED
"""
This is means that you have to log in to enter the site. But all the resources (css, js, images, etc) is accessed without authentification.
If you want specific role only enter specific controller:
For example, for UserController:
/user/**=ROLE_ADMIN
/role/**=ROLE_ADMIN
For more information: http://www.grails.org/AcegiSecurity+Plugin+-+Securing+URLs
Regards
As far as I can tell, there doesn't look like there's an easy way to do it.
You can inject an instance of the grails AuthenticatedVetoableDecisionManager which is a concrete class of spring's AbstractAccessDecisionManager by doing this:
def accessDecisionManager
This has a "decide" method on it that takes 3 parameters
decide(Authentication authentication, Object object, ConfigAttributeDefinition config)
This is probably the method that you'd need to call and pass in the right things to figure out if the user with the auth creds can access that "object" (which looks like it's normally a request/response). Some additional digging around might prove out something workable here.
Short term, it's probably easier to use the ifAnyGranted taglib as another poster mentions.
I'm not sure about in Groovy, but in Java (so I assume Groovy too...) you could do (minus NPE checks):
GrantedAuthority[] authorities = SecurityContextHolder.getContext().getAuthentication().getAuthorities();
boolean isAdmin = false;
for(GrantedAuthority authority : authorities) {
String role = authority.getAuthority();
if(role != null && role.equals("ROLE_ADMIN")) {
isAdmin = true;
break;
}
}
As for knowing whether or not the action is supported, you'd have to call the RequestMap service to get the roles for the mapping and see if it contains the found user role.

Resources