Permalink
Browse files

Inital commit

  • Loading branch information...
Predrag Knezevic
Predrag Knezevic committed May 13, 2011
0 parents commit f503bf3a640df5f521d7ff97d6fd2b34afdce1f7
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" path="src/java"/>
+ <classpathentry kind="src" path="src/groovy"/>
+ <classpathentry kind="src" path="grails-app/conf"/>
+ <classpathentry kind="src" path="grails-app/controllers"/>
+ <classpathentry kind="src" path="grails-app/domain"/>
+ <classpathentry kind="src" path="grails-app/services"/>
+ <classpathentry kind="src" path="grails-app/taglib"/>
+ <classpathentry kind="src" path="grails-app/utils"/>
+ <classpathentry kind="src" path="test/integration"/>
+ <classpathentry kind="src" path="test/unit"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+ <classpathentry kind="con" path="com.springsource.sts.grails.core.CLASSPATH_CONTAINER"/>
+ <classpathentry kind="src" path=".link_to_grails_plugins/browser-detection-0.1/grails-app/taglib">
+ <attributes>
+ <attribute name="com.springsource.sts.grails.core.SOURCE_FOLDER" value="true"/>
+ </attributes>
+ </classpathentry>
+ <classpathentry excluding="BuildConfig.groovy|*DataSource.groovy|UrlMappings.groovy|Config.groovy|BootStrap.groovy|spring/resources.groovy" kind="src" path=".link_to_grails_plugins/browser-detection-0.1/grails-app/conf">
+ <attributes>
+ <attribute name="com.springsource.sts.grails.core.SOURCE_FOLDER" value="true"/>
+ </attributes>
+ </classpathentry>
+ <classpathentry kind="src" path=".link_to_grails_plugins/browser-detection-0.1/grails-app/views">
+ <attributes>
+ <attribute name="com.springsource.sts.grails.core.SOURCE_FOLDER" value="true"/>
+ </attributes>
+ </classpathentry>
+ <classpathentry kind="src" path=".link_to_grails_plugins/browser-detection-0.1/grails-app/controllers">
+ <attributes>
+ <attribute name="com.springsource.sts.grails.core.SOURCE_FOLDER" value="true"/>
+ </attributes>
+ </classpathentry>
+ <classpathentry kind="src" path=".link_to_grails_plugins/browser-detection-0.1/grails-app/services">
+ <attributes>
+ <attribute name="com.springsource.sts.grails.core.SOURCE_FOLDER" value="true"/>
+ </attributes>
+ </classpathentry>
+ <classpathentry kind="output" path="web-app/WEB-INF/classes"/>
+</classpath>
@@ -0,0 +1,5 @@
+/plugin.xml
+/.settings
+/target
+/grails-force-response-download*.zip
+/web-app
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>grails-force-response-download</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.wst.common.project.facet.core.builder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>com.springsource.sts.grails.core.nature</nature>
+ <nature>org.eclipse.jdt.groovy.core.groovyNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ <nature>org.eclipse.wst.common.project.facet.core.nature</nature>
+ </natures>
+ <linkedResources>
+ <link>
+ <name>.link_to_grails_plugins</name>
+ <type>2</type>
+ <locationURI>GRAILS_ROOT/1.3.5/projects/grails-force-response-download/plugins</locationURI>
+ </link>
+ </linkedResources>
+</projectDescription>
@@ -0,0 +1,96 @@
+class ForceResponseDownloadGrailsPlugin {
+ // the plugin version
+ def version = "0.1"
+ // the version or versions of Grails the plugin is designed for
+ def grailsVersion = "1.2 > *"
+ // the other plugins this plugin depends on
+ // the plugin depends on browser-detection plugin, but if I specify it here, grails looks for browserdetection plugin (removes '-')
+ // and it does not find it. Thus, only dependency in BuildConfig remains...
+ //def dependsOn = ["browser-detection":'0.1']
+ // resources that are excluded from plugin packaging
+ def pluginExcludes = [
+ "grails-app/views/error.gsp",
+ "web-app/**",
+ "test/**"
+ ]
+
+ def author = "Predrag Knezevic"
+ def authorEmail = "pedjak@gmail.com"
+ def title = "Force Downloading Controller's Response in Browser"
+
+ def description = '''\\
+The plugin forces browser to open a dialog for downloading content produced within controller's action.
+Although the theory says that this is easily controlled by specifying Content-Disposition HTTP header, the practice shows
+that there are special situations (ofcourse) with IE that has to be handled properly.
+
+Controllers are extended with forceDownload method that takes as parameters a Map specifying download options,
+and a object containing content:
+
+ forceDownload(file:"file", contentType:"application/octet-stream", contentLength: 123, content)
+
+* file specifies the name that will be presented in browser download dialog, if omitted the default value is 'file'
+* contentType is MIME content type that will be sent to browser for the given content, if omitted the default value is application/octet-stream
+* contentLength is optional, but recommended to have - browsers will be able to show proper progress while downloading.
+ If not explicitely specified, it can be read from content object, if it implements size() method
+* content - optional. If omitted then controller's code must write to response stream or render response manually using standard Grails approaches
+'''
+
+ // URL to the plugin's documentation
+ def documentation = "http://grails.org/plugin/force-file-download"
+
+ def observe = ['controllers']
+
+ def doWithDynamicMethods = { ctx ->
+ configureControllers(application)
+ }
+
+ private def configureControllers(application) {
+ def service = application.mainContext.userAgentIdentService
+ application.controllerClasses*.clazz.each { cls ->
+ cls.metaClass.forceDownload = { Map map = [filename:"file", contentType:"application/octet-stream" ], content = null ->
+ def response = delegate.response
+ def isIE = service.isMsie()
+
+ if (delegate.request.isSecure()) {
+ response.addHeader("Pragma", "no-cache")
+ response.addHeader("Expires", "-1")
+ response.addHeader("Cache-Control", "no-cache")
+ } else {
+ response.addHeader("Cache-Control", "private")
+ response.addHeader("Pragma", "public")
+ }
+ response.addHeader("Content-Disposition", "attachment; filename=\"${map.filename}\"");
+ if (isIE) {
+ response.addHeader("Connection", "close");
+ }
+ response.contentType = map.contentType
+ def length = map.contentLength ?: content != null ? (content.metaClass.respondsTo(content, "size") ? content.size() : null) : null
+
+ if (length != null) {
+ response.addHeader("Content-Length", "${length}");
+ }
+ def os = response.outputStream
+ if (content != null) {
+ os << content
+ os.close()
+ }
+ os
+ }
+ }
+ }
+
+ def onChange = { event ->
+ if (event.source && application.isControllerClass(event.source)) {
+ def context = event.ctx
+ if (!context) {
+ if (log.isDebugEnabled()) {
+ log.debug("Application context not found. Can't reload")
+ }
+
+ return
+ }
+ configureControllers(application)
+ }
+ }
+
+}
14 LICENSE
@@ -0,0 +1,14 @@
+Copyright 2011 Predrag Knezevic
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
@@ -0,0 +1,14 @@
+The plugin forces browser to open a dialog for downloading content produced within controller's action.
+Although the theory says that this is easily controlled by specifying Content-Disposition HTTP header, the practice shows
+that there are special situations (ofcourse) with IE that has to be handled properly.
+
+Controllers are extended with forceDownload method that takes as parameters a Map specifying download options,
+and a object containing content:
+
+ forceDownload(file:"file", contentType:"application/octet-stream", contentLength: 123, content)
+
+* file specifies the name that will be presented in browser download dialog, if omitted the default value is 'file'
+* contentType is MIME content type that will be sent to browser for the given content, if omitted the default value is application/octet-stream
+* contentLength is optional, but recommended to have - browsers will be able to show proper progress while downloading.
+ If not explicitely specified, it can be read from content object, if it implements size() method
+* content - optional. If omitted then controller's code must write to response stream or render response manually using standard Grails approaches
@@ -0,0 +1,4 @@
+#Grails Metadata file
+#Fri May 13 13:11:13 CEST 2011
+app.grails.version=1.3.7
+app.name=force-response-download
@@ -0,0 +1,35 @@
+grails.project.class.dir = "target/classes"
+grails.project.test.class.dir = "target/test-classes"
+grails.project.test.reports.dir = "target/test-reports"
+//grails.project.war.file = "target/${appName}-${appVersion}.war"
+grails.project.dependency.resolution = {
+ // inherit Grails' default dependencies
+ inherits("global") {
+ // uncomment to disable ehcache
+ // excludes 'ehcache'
+ }
+ log "warn" // log level of Ivy resolver, either 'error', 'warn', 'info', 'debug' or 'verbose'
+ repositories {
+ grailsPlugins()
+ grailsHome()
+ grailsCentral()
+
+ // uncomment the below to enable remote dependency resolution
+ // from public Maven repositories
+ //mavenLocal()
+ //mavenCentral()
+ //mavenRepo "http://snapshots.repository.codehaus.org"
+ //mavenRepo "http://repository.codehaus.org"
+ //mavenRepo "http://download.java.net/maven/2/"
+ //mavenRepo "http://repository.jboss.com/maven2/"
+ }
+
+ plugins {
+ runtime 'org.grails.plugins:browser-detection:0.1'
+ }
+ dependencies {
+ // specify dependencies here under either 'build', 'compile', 'runtime', 'test' or 'provided' scopes eg.
+
+ // runtime 'mysql:mysql-connector-java:5.1.13'
+ }
+}
@@ -0,0 +1,32 @@
+dataSource {
+ pooled = true
+ driverClassName = "org.hsqldb.jdbcDriver"
+ username = "sa"
+ password = ""
+}
+hibernate {
+ cache.use_second_level_cache = true
+ cache.use_query_cache = true
+ cache.provider_class = 'net.sf.ehcache.hibernate.EhCacheProvider'
+}
+// environment specific settings
+environments {
+ development {
+ dataSource {
+ dbCreate = "create-drop" // one of 'create', 'create-drop','update'
+ url = "jdbc:hsqldb:mem:devDB"
+ }
+ }
+ test {
+ dataSource {
+ dbCreate = "update"
+ url = "jdbc:hsqldb:mem:testDb"
+ }
+ }
+ production {
+ dataSource {
+ dbCreate = "update"
+ url = "jdbc:hsqldb:file:prodDb;shutdown=true"
+ }
+ }
+}
@@ -0,0 +1,10 @@
+//
+// This script is executed by Grails after plugin was installed to project.
+// This script is a Gant script so you can use all special variables provided
+// by Gant (such as 'baseDir' which points on project base dir). You can
+// use 'ant' to access a global instance of AntBuilder
+//
+// For example you can create directory under project tree:
+//
+// ant.mkdir(dir:"${basedir}/grails-app/jobs")
+//
@@ -0,0 +1,5 @@
+//
+// This script is executed by Grails when the plugin is uninstalled from project.
+// Use this script if you intend to do any additional clean-up on uninstall, but
+// beware of messing up SVN directories!
+//
@@ -0,0 +1,10 @@
+//
+// This script is executed by Grails during application upgrade ('grails upgrade'
+// command). This script is a Gant script so you can use all special variables
+// provided by Gant (such as 'baseDir' which points on project base dir). You can
+// use 'ant' to access a global instance of AntBuilder
+//
+// For example you can create directory under project tree:
+//
+// ant.mkdir(dir:"${basedir}/grails-app/jobs")
+//

0 comments on commit f503bf3

Please sign in to comment.