I have Grails application there is button onclicking which it is forwarded to a action which is authenticated by spring security and first goes to login page if a user is not logged in and then come to the expected page.
I want to set header after the button is clicked and want it in login page only if it had been redirected here after clicking on the button normally it shouldn't be there on login page.
I have redirected to another function from GSP and there I am setting header and from there I have redirected to the actuall function.
For the first time when server runs the value comes but after that it dissappears.
Please guide me.
My code:-
gsp code :
<g:form controller="project" action="addFundraiserUrl" id="${project.id}" params="['fr':currentFundraiser.username]">
<input type="submit" value="Join Us" class="col-md-12 col-sm-12 col-xs-12 inviteteammember text-center btn btn-default btn-md manage-team"/>
</g:form>
controller code :-
def addFundraiserUrl () {
response.setHeader 'X-JoinUs', 'true'
redirect (action:'addFundRaiser', params:params)
}
#Secured(['IS_AUTHENTICATED_FULLY'])
def addFundRaiser(){
def project = projectService.getProjectById(params.id)
User user = userService.getCurrentUser()
def fundraiser = project.user.username
def iscampaignAdmin = userService.isCampaignBeneficiaryOrAdmin(project, user)
def message = projectService.getFundRaisersForTeam(project, user)
def title = projectService.getVanityTitleFromId(params.id)
def name = userService.getVanityNameFromUsername(fundraiser, params.id)
if (iscampaignAdmin) {
flash.prj_mngprj_message = message
redirect (action: 'manageproject', params:['projectTitle':title])
} else {
flash.prj_mngprj_message = message
redirect (action: 'show', params:['projectTitle':title,'fr':name])
}
}
Related
I'm running into a complex scenario where we are using a legacy database with composite keys and the client wants to be able alter two of the databases pks, "expenseDate" and "adjustmentNumber". In order to be able to change the pks, I've have to use a HQL query. Now of course this has caused another slew of issues with validation. My work around was to populate a domain so that I could validate against it.
So far everything works fine until we have a validation error that I'd like to return to the UI.
I have the following URL which uses the recoveryDetail controller and the edit action to render the page.
http://localhost:8080/pisr/recoveryDetail/edit?division=ALBANY&peid=PI0003&orgkey=14046701&expenseDate=07-22-2015&adjustmentNumber=1
Edit action
def edit() {
//Parse clean url expense date
params.expenseDate = new SimpleDateFormat('MM-dd-yyyy').parse(params.expenseDate)
def recoveryDetailInstance = RecoveryDetail.get(new RecoveryDetail(params))
if(recoveryDetailInstance == null) {
redirect(uri:'/')
return
}
[recoveryDetailInstance: recoveryDetailInstance, disabled: isdisabled(recoveryDetailInstance.batchOverride)]
}
And the following update action.
Update action
#Transactional
def update() {
params.pk_expenseDate = getDateParser(params.pk_expenseDate)
params.expenseDate = getDateParser(params.expenseDate)
params.adjustmentNumber = getAdjustementNumber(params)
RecoveryDetail recoveryDetailInstance = new RecoveryDetail(params);
recoveryDetailInstance.division = params.pk_division
recoveryDetailInstance.peid = params.pk_peid
recoveryDetailInstance.orgkey = params.pk_orgkey
recoveryDetailInstance .validate()
if(recoveryDetailInstance .hasErrors()) {
flash.message = "test"
respond view: "edit", model:[recoveryDetailInstance:recoveryDetailInstance]
return
} else {
def sqlParams = [
pk_division:params.pk_division,
pk_peid:params.pk_peid,
pk_orgkey:params.pk_orgkey,
pk_expenseDate:params.pk_expenseDate,
pk_adjustmentNumber:params.int('pk_adjustmentNumber'),
approved:YesNoTypes.valueOf(params.approved),
batchOverride:YesNoTypes.valueOf(params.batchOverride),
adjustmentFlag:params.adjustmentFlag,
adjustmentNumber:params.adjustmentNumber,
projectHours:new BigDecimal(params.projectHours),
percentEffort:new BigDecimal(params.percentEffort),
totalHours:new BigDecimal(params.totalHours),
expenseDate:params.expenseDate
]
RecoveryDetail.executeUpdate(recoveryDetailQuery, sqlParams)
}
Edit gsp
<g:form class="form-horizontal" url="[resource:recoveryDetailInstance, action:'update']" method="PUT">
<!-- hidden fields contain params from url (composite key)-->
<g:hiddenField name="pk_division" value="${recoveryDetailInstance?.division}"/>
<g:hiddenField name="pk_peid" value="${recoveryDetailInstance?.peid}"/>
<g:hiddenField name="pk_orgkey" value="${recoveryDetailInstance?.orgkey}"/>
<g:hiddenField name="pk_expenseDate" value="${formatDate(format:'MM/dd/yyyy',date:recoveryDetailInstance?.expenseDate)}" />
<g:hiddenField name="pk_adjustmentNumber" value="${recoveryDetailInstance?.adjustmentNumber}"/>
<div class="row">
<div class="col-md-6">
<g:render template="form" model="[recoveryDetailInstance: recoveryDetailInstance, 'mode':'edit']"/>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<g:actionSubmit class="btn btn-primary" action="update" disabled="${disabled}" value="${message(code: 'default.button.update.label', default: 'Update')}"/>
</div>
</div>
</g:form>
The problem
When a user causes a validation error that triggers a server side response, I run into the following problems with the different return types.
redirect - this is returning the flash message, but redirects the gsp to the edit action which in turn fires the initializer query and replaces all the form data with the original data.
Example -
redirect (controller:"recoveryDetail", action:"edit", params:["division":params.pk_division, "peid":params.pk_peid, "orgkey": params.pk_orgkey, "expenseDate":params.expenseDate.format("MM-dd-yyyy"), "adjustmentNumber":params.adjustmentNumber])
respond - So I assumed I needed to just use respond, well it resulted in the following.
URL was changed to http://localhost:8080/pisr/recoveryDetail/update dropping all parameters and a 404 page was returned.
Example
flash.message = "test"
respond view: "edit", model:[recoverDetailInstance:recoverDetailInstance]
return
So my question
How do I throw a server side validation error and return it to the page with the data entered by the user?
You may add the parameters back into the redirect or render call.
redirect (..., params: params)
https://grails.github.io/grails-doc/latest/ref/Controllers/redirect.html
Also, I'd suggest using a service instead of the controller method. Services are already transactional. I would also throw an exception if the hasErrors is true. The exception message or object can be your payload back to the user.
Solution was to use render without the use of params like so.
if(recoveryDetailInstance.hasErrors()) {
render view: "edit", model:[recoveryDetailInstance:recoveryDetailInstance]
return
}
Is it possible to pass 'params' in a redirect at the end of a Webflow? Basically the purpose of this variable or param passing from controller to controller is this, I want to have a variable or ${param.xyz} available on the view page only if the flow has been completed.
class Example1Controller{
def startFlow = {
begin {
....
}
....
....
finished {
action {
flash.message = 'success'
}
redirect(controller: 'example2', action: 'myaccount', params: [author: "Stephen King"])
}
}
}
OTHER CONTROLLER
class Example2Controller{
def myaccount() {
def here = $params.author
return [me:here]
}
}
GSP VIEW
<html>
<body>
<g:if test="${params.me}">
<p>This is what I want to display: **${me}**</p>
<p>But it must come from the first controller, from the flow.</p>
</g:if>
</body>
</html>
Basically the purpose of all this variable passing from controller to controller is this. I want to have a variable or ${param.} available on the view page only if the flow has been completed.
You can use hiddenField .
<g:hiddenField name="myField" value="myValue" />
you can pass value from Example1Controller to Example1Gsp(as hideenField) and from that GSP you can get value in your Example2Controller.
If I remember right, we did this before but we used the flow scope/ flow variables. Something like:
def myFlow = {
fin {
redirect: (controller: "xxx", action: "yyy", params: [someValue: flow.someValue])
}
}
Then, in the receiving end, something like:
def yyy = {
[ aaa: params.someValue ]
}
I have create.gsp for creating domain objects. Besides normal html body and headers I have two div's. One for displaying command object errors and one for data. I want to submit the form with AJAX. When I submit the form with
<g:formRemote name="formName" update="errorsDiv" url="[controller:myController', action:'checkAndForward']">
this gets called:
def checkAndForward= {CmdObject p ->
if (p.hasErrors()) {
render(template:"/error",params:params,model :[p:p])
} else {
forward controller: "test", action: "save", params:params
}
}
def save = {
id=myservice.getData()
render(view: "show", id:id)
}
This is working and the save action is called in the controller but ..
The problem is that after 'save' action 'show.gsp' is displayed into where errors div is located.
I would like to have only 'show' page refreshed as if I was calling /save from browser (with posted params of course)
I am running a query to check if an entity id exists for multiple account ids. If the result set is not null, then I need to either throw an error or display a flash message.
The code for the method is as below:
def save() {
def SAMLInfoInstance = new SAMLInfo(params)
def account = Account.get(params?.accountId)
SAMLInfoInstance.setAccount(account)
def samlInfoInstanceList = SAMLInfo.executeQuery("from SAMLInfo " +
" where account.id <> ? " +
" and entityId = ?", [SAMLInfoInstance.accountId, SAMLInfoInstance.entityId])
if (samlInfoInstanceList?.size > 0){
flash.message = message(code: 'test.not.created.message', args: [message(code: 'SAMLInfo.label', default: 'SAMLInfo'), SAMLInfoInstance.entityId])
/*flash.message = "default.not.created.message"
flash.args = ["SAMLInfo", SAMLInfoInstance.entityId]
flash.default = "SAMLInfo cannot be created"
*/
render(view: "create", model: [SAMLInfoInstance: SAMLInfoInstance])
return
}
if (!SAMLInfoInstance.save(flush: true)) {
render(view: "create", model: [SAMLInfoInstance: SAMLInfoInstance])
return
}
flash.message = message(code: 'default.created.message', args: [message(code: 'SAMLInfo.label', default: 'SAMLInfo'), SAMLInfoInstance.entityId])
redirect(action: "list", id: SAMLInfoInstance.account.id)
}
In my view I render the flash message and the errors in the following manner:
<g:if test="${flash.message}">
<br/>
<div class="message" role="status">
<g:message code="${flash.message}" args="${flash.args}" default="${flash.default}"/>
</div>
<br/>
</g:if>
<br/>
<g:renderErrors bean="${SAMLInfoInstance}" as="list" />
In my message.properties file, I have the following line:
test.not.created.message=The SP url {1} is not allowed for this account. Please enter a different value.
When I run this code, the flash message displays as the string I pass as message i.e. "test.not.created.message". Also, this string is passed on to display whenever I navigate to any other page which displays flash.message.
I am new to grails and groovy and would appreciate any help with this.
Thanks!
2 problems then:
1 - the message is not being retrieved from your message.properties:
You must have other message.properties files in your project. Give it a check. Because if it's not found, grails shows the code itself instead of the message, since it did not find one. Maybe it's looking for your message in some other properties file, like the one specific for your Locale (ex: pt_BR or en_US). Other than that, you're doing it right using the message(code:...) construct.
2 - Your flash message doesn't disappear:
Instead of flash.message, use request.message.
I think the example you want to follow is here.
You only need to resolve the message once ie in the controller or in the view.
So in the controller:
flash.message = "test.not.created.message"
flash.args = ["SAMLInfo"]
flash.default = "<default text>"
And in the view:
<g:message code="${flash.message}" args="${flash.args}"
default="${flash.default}"/>
The flash scope is cleared at the end of the next request which may explain why you still see the message on the next page.
This is in the gsp
<g:if test="${hasError}">
<div class="errors">
<g:renderErrors bean="${eventInstance}" />
</div>
</g:if>
<g:else >
<div id="messageBox" class="message" style="display:none;">
<g:message code="legalevent.save.success" args="[entityName]" default="Event saved successfully" />
</div>
</g:else>
<g:formRemote name="eventForm" id="eventForm" url="[controller : 'search', action : 'saveLegalEvent']"
update="eventFormDiv" action="${createLink(controller: 'search', action: 'saveLegalEvent')}" method="POST"
onSuccess="jQuery('#messageBox').show()">
I am rendering a page for update with this :
def saveLegalEvent = {
def paramsView = params
def eventPattern = /(event\.).*/
def event = LegalEvent.findByLevId(params["levId"])
def corrTxt = params["corrTxt"] as CorrectionText
if(corrTxt.getCorrId()){
corrTxt = CorrectionText.findByCorrId(corrTxt.getCorrId())
}
event.setCorrTxt(corrTxt)
event.properties = params["event"]
def dataList = []
def hasError = false
def validated = event.validate()
validated &= event.validateHistoryParams()
if(validated)
event.save(flush:true)
else
hasError = true
def errorsView = event.errors
render(view:'leform', model:[attributeDataInstanceList:event.tags, lecInstance:event.leCode, eventInstance:event, hasError: hasError])
}
validateHistoryParams validate some more params that are usually not needed in the domain class.
def validateHistoryParams = { ->
if(!changeRef || !changeRef.trim()) {
this.errors.rejectValue('changeRef', 'event.changeRef.blank')
}
if(!corrTxt || !(corrTxt.corrTxt.trim() || corrTxt.corrId )) {
this.errors.rejectValue('corrTxt', 'event.corrTxt.null')
}
!(this.hasErrors())
}
The problem with all this is that the errors are not rendered in the gsp.
All other tags are rendered fine, when debugging I can see that the errors are actually in the error stack. But in the end, the tag isn't rendering them.
As you can see, there is no redirection, so I can't understand why the errors would somehow be erased between the response creation and the rendering ...
In your Groovy code, parameter returned is named hasError, and GSP checks for hasErrors. I'd recommend not to use extra variables, and just query the bean itself in GSP.
I also believe that you need to have that errors div inside the formRemote element in order to re-render after form submission.