Relative URL to server path conversion - Grails - grails

In a custom tag, I am receiving as a parameter the url of a file, which I need to open.
I have this
/content/data.html
which is the output from
${createLinkTo(dir:'content',file:'data.html')}:
and I need the 'server path':
C:\mygrailsapp\web-app\content\data.html

You can use the Spring application context to find resources. This works if it's under web-app folder:
class FooController {
def grailsApplication
def myAction = {
String path = params.path // '/content/data.html'
def resource = grailsApplication.mainContext.getResource(path)
String text = resource.inputStream.text
...
}
}

Related

Open or download URL link in Grails Controller

I want to render or download a URL that links to a PDF in a Grails controller method. I'm okay with either opening this is in a new or the same tab, or just downloading it. How is this done in grails?
So far I have:
render(url: "http://test.com/my.pdf")
However, I get errors with this and other ways I've tried, such as rendering a response with content. Any clues?
Yes you can absolutely do it easily:
First get the file from the URL (if you don't have a local file) for example:
class FooService {
File getFileFromURL(String url, String filename) {
String tempPath = "./temp" // make sure this directory exists
File file = new File(tempPath + "/" + filename)
FileOutputStream fos = new FileOutputStream(file)
fos.write(new URL(url).getBytes())
fos.close()
file.deleteOnExit()
return file
}
}
Now in your controller, do this to allow user to automatically download your PDF file:
class FooController {
def fooService
def download() {
String filename = "my.pdf"
// You can skip this if you already have that file in the same server
File file = fooService.getFileFromURL("http://test.com/my.pdf", filename)
response.setContentType("application/octet-stream")
response.setHeader("Content-disposition", "${params.contentDisposition}; filename=${filename}")
response.outputStream << file.readBytes()
return
}
}
Now as the user will hit /foo/download the file will be dowloaded automatically.
One option is
class ExampleController {
def download() {
redirect(url: "http://www.pdf995.com/samples/pdf.pdf")
}
}
Going to localhost:8080/appName/example/download will, depending on the users browser preferences, either download the file or open the file in the same tab for reading.
I works with grails 2.5.0

Grails Include template from external location

How do I include external GSPs or template in the GSP file when the template to be included is not under views folder?
Yes, you can easily do that. Here you go:
import grails.gsp.PageRenderer
class MyLib {
static namespace = "foo"
static defaultEncodeAs = "raw"
PageRenderer groovyPageRenderer
def externalTemplate = { attrs, body ->
String externalFilePath = attrs.externalPath
/*
* Put content of that external template to a file inside grails-app/views folder
* with a temporary unique name appended by current timestamp
*/
String temporaryFileName = "_external-" + System.currentTimeMillis() + ".gsp"
File temporaryFile = new File("./grails-app/views/temp/$temporaryFileName")
/*
* Copy content of external file path to the temporary file in views folder.
* This is required since the groovyPageRenderer can compile any GSP located inside
* the views folder.
*/
temporaryFile.text << new File(externalFilePath).text
/*
* Now compile the content of the external GSP code and render it
*/
out << groovyPageRenderer.render([template: "/temp/$temporaryFileName", model: attrs.model])
// Delete the file finally
temporaryFile.delete()
}
}
Now in your actual GSP where you want to include the external GSP, you can write so:
<body>
<foo:externalTemplate externalPath="/home/user/anyExternalFile.gsp" model="${[status: 1}" />
</body>
I know I am late for this reply but I encountered this problem when we tried to put report views outside of the views folder.
We couldn't use the above method because we are running a jar package and we couldn't create files inside views folder.
Here is the solution on Grails 4
first inject
def groovyPagesTemplateEngine
def groovyPageLayoutFinder
then in your controller
File externalFile = new File("/path/to/file.gsp")
if(externalFile && externalFile.exists()){
GroovyPageView groovyPageView = new GroovyPageView()
LinkedHashMap model = [:]
Template template = groovyPagesTemplateEngine.createTemplate(externalFile.text, externalFileName)
groovyPageView.setServletContext(getServletContext())
groovyPageView.setTemplate(template)
groovyPageView.setApplicationContext(getApplicationContext())
groovyPageView.setTemplateEngine(groovyPagesTemplateEngine)
groovyPageView.afterPropertiesSet()
request.setAttribute GrailsLayoutDecoratorMapper.LAYOUT_ATTRIBUTE, null
GrailsLayoutView grailsLayoutView = new GrailsLayoutView(groovyPageLayoutFinder, groovyPageView)
grailsLayoutView.render model, webRequest.getCurrentRequest(), webRequest.getResponse()
webRequest.renderView = false
return
}
else {
// something that shows error
render "not found"
}

Putting an object on a Request in a Grails Integration Test

In a Grails project I am looking at here, a filter puts a Domain object on the request...
class TokenFilters {
def filters = {
all( uri: '/hiphop/**' ) {
before = {
MyToken myToken = ...
request.myToken = myToken
MyToken looks like:
class MyToken {
String id
String token
static mapping = {
token( index: true )
id( generator: 'uuid' )
}
...
}
In my controller, the myToken is pulled off the request.
MyController {
myaction {
MyToken accessToken = request.myToken
All fine. I wish to write an integration test for the controller.
#Test
void testLogin() {
def mc = new MyController()
def myToken = new MyToken(1234);
// set the request parameters
mc.request.parameters = [myToken:myToken];
def message = mc.action();
assertTrue(message.indexOf("trans") > 0)
}
When I run this, I get:
Failure: testLogin(MyTests)
| java.lang.IllegalArgumentException: Parameter map value must be single value or array of type [java.lang.String]
at testLogin(MyTests.groovy:40)
So it looks like Grails will only let me a String or a single value and doesn't like me putting an object on the request in the Filter. Even thou it lets me put on the same object type in a Filter.
I'd really like to test this without going to Functional tests. Please help. I am using Grails 2.2.1
Thanks
The problem is that your code is passing parameters to the controller. Your emulating an HTTP request which can't handle objects. What you can do is:
mc.request.parameters = [myToken: '1234']
and then you're controller/filter would pull out the 1234 and look up MyToken. If you were testing the controller forwarding then you can put objects in the request. Not the other way around.
I see now that part of the problem is that you're trying to test a controller that is assuming data coming from a filter.
You've omitted some code, but assuming you are extending ControllerUnitTestCase then you have access to a mock request object. You should be able to simply do:
#Test
void testLogin() {
def mc = new MyController()
def myToken = new MyToken(1234);
// set the request parameters
request.myToken = myToken
def message = mc.action();
assertTrue(message.indexOf("trans") > 0)
}

grailsApplication null in Service

I have a Service in my Grails application. However I need to reach the config for some configuration in my application. But when I am trying to use def grailsApplication in my Service it still gets null.
My service is under "Services".
class RelationService {
def grailsApplication
private String XML_DATE_FORMAT = "yyyy-MM-dd"
private String token = 'hej123'
private String tokenName
String WebserviceHost = 'xxx'
def getRequest(end_url) {
// Set token and tokenName and call communicationsUtil
setToken();
ComObject cu = new ComObject(tokenName)
// Set string and get the xml data
String url_string = "http://" + WebserviceHost + end_url
URL url = new URL(url_string)
def xml = cu.performGet(url, token)
return xml
}
private def setToken() {
tokenName = grailsApplication.config.authentication.header.name.toString()
try {
token = RequestUtil.getCookie(grailsApplication.config.authentication.cookie.token).toString()
}
catch (NoClassDefFoundError e) {
println "Could not set token, runs on default instead.. " + e.getMessage()
}
if(grailsApplication.config.webservice_host[GrailsUtil.environment].toString() != '[:]')
WebserviceHost = grailsApplication.config.webservice_host[GrailsUtil.environment].toString()
}
}
I have looked on Inject grails application configuration into service but it doesn't give me an answer as everything seems correct.
However I call my Service like this: def xml = new RelationService().getRequest(url)
EDIT:
Forgot to type my error, which is: Cannot get property 'config' on null object
Your service is correct but the way you are calling it is not:
def xml = new RelationService().getRequest(url)
Because you are instantiating a new object "manually "you are actually bypassing the injection made by Spring and so the "grailsApplication" object is null.
What you need to do is injecting your service using Spring like this:
class MyController{
def relationService
def home(){
def xml = relationService.getRequest(...)
}
}

How to secure acces to user uploaded files with a filter in Grails?

I am building a Grails application which uses the Spring Security Core Plugin.
I have two applications roles. ROLE_USER and ROLE_ADMIN
Users can upload files which are stored in a directory called files
External users should not see any file
ROLE_ADMIN users can see every uploaded file.
ROLE_USER user should be allowed only in certain cases
A file url request should look like this
http://localhost:8080/MyApp/files/patient1-1.png
For the first case I have set in conf/Config.groovy the next url interceptor
grails.plugins.springsecurity.interceptUrlMap = [
'/files/**': ['ROLE_USER']
]
For the second and third case I created the next file conf/MyFilters
class MyFilters {
def springSecurityService
public currentUser() { return User.get(springSecurityService.principal.id);}
public userRoles() { return springSecurityService.principal.authorities*.authority }
def filters = {
fileFilter(uri: '/files/*') {
before = {
println "Here"
def String url = request.getRequestURL()
if(url.contains("files/patient")) {
if(!userRoles().contains(Role.ROLE_ADMIN)) {
if(PLAIN ROLE USER IS NOT ALLOWED) {
redirect(action:'login')
return false;
}
}
}
}
after = {
}
afterView = {
}
}
}
}
However, it does not seem to get triggered. I never see the Here print out.
Any idea what am I doing wrong?
You shouldn't mix Grails filters with Spring Security - everything is doable from Spring Security. Are you using the "InterceptUrlMap" config type (grails.plugins.springsecurity.securityConfigType = "InterceptUrlMap")? By default it uses annotations, so the securityConfigType setting would be ignored.
If you're using annotations you can add this url pattern to the staticRules config option:
grails.plugins.springsecurity.controllerAnnotations.staticRules = [
'/files/**': ['ROLE_USER']
]
Try running grails clean to force a full compile; it might be a simple as some code being out of sync.

Resources