Skip to content

Commit eccf06a

Browse files
committed
#342 audio data type support
1 parent 317cdae commit eccf06a

24 files changed

+1590
-73
lines changed

grails-app/conf/ApplicationResources.groovy

+11
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,17 @@ modules = {
385385
resource url: 'vendor/large-checkbox/large-checkbox.css'
386386
}
387387

388+
audio {
389+
dependsOn "modernizr"
390+
resource url: 'js/audio.js'
391+
resource url: 'vendor/recorderjs/recorder.js'
392+
// resource url: 'vendor/libmp3lame/libmp3lame.min.js'
393+
}
394+
395+
modernizr {
396+
resource url: "vendor/modernizr/modernizr-custom.js"
397+
}
398+
388399
imageDataType{
389400
resource url: 'js/images.js'
390401
resource url: 'css/images.css'

grails-app/conf/Config.groovy

+1
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ security.cas.adminRole = "ROLE_FC_ADMIN"
9595
security.cas.readOnlyOfficerRole = "ROLE_FC_READ_ONLY"
9696

9797
upload.images.path = "/data/${appName}/images/"
98+
upload.path = "/data/${appName}/"
9899
upload.extensions.blacklist = ['exe','js','php','asp','aspx','com','bat']
99100

100101
app.http.header.userId = "X-ALA-userId"

grails-app/conf/LastEvaluatedConfig.groovy

+1
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@ spatial.wms.url = "${spatial.baseURL}/geoserver/ALA/wms?"
55
spatial.wms.cache.url = "${spatial.baseURL}/geoserver/gwc/service/wms?"
66
ecodata.service.url = "${ecodata.baseURL}/ws"
77
upload.images.url = "${grails.serverURL}/image?id="
8+
upload.file.url = "${grails.serverURL}/file?id="
89
merit.project.url = "${merit.baseURL}/project/index"

grails-app/conf/UrlMappings.groovy

+1
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ class UrlMappings {
9393
"/admin/user/$id"(controller: "user", action: "show") {
9494

9595
}
96+
"/download/file"(controller: "download", action: [GET: "file"])
9697
"/download/$id"(controller: "download", action: [GET: "downloadProjectDataFile"])
9798
"500"(controller:'error', action:'response500')
9899
"404"(controller:'error', action:'response404')

grails-app/controllers/au/org/ala/biocollect/BioActivityController.groovy

+58-23
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ import au.org.ala.biocollect.merit.UserService
1212
import au.org.ala.biocollect.sightings.BieService
1313
import grails.converters.JSON
1414
import groovyx.net.http.ContentType
15+
import org.apache.commons.io.FilenameUtils
1516
import org.codehaus.groovy.grails.web.servlet.mvc.GrailsParameterMap
17+
import org.springframework.web.multipart.MultipartFile
1618

1719
import static org.apache.http.HttpStatus.*
1820
import org.codehaus.groovy.grails.web.json.JSONArray
@@ -96,7 +98,7 @@ class BioActivityController {
9698
if (photoPoints && activityId) {
9799
updatePhotoPoints(activityId, photoPoints)
98100
}
99-
101+
100102
postBody.outputs?.each {
101103
it.data?.multimedia?.each {
102104
String filename
@@ -134,10 +136,10 @@ class BioActivityController {
134136
}
135137
}
136138

137-
render result as JSON
139+
render result as JSON
138140
}
139141

140-
def getProjectActivityCount(String id){
142+
def getProjectActivityCount(String id) {
141143
def result = activityService.getProjectActivityCount(id)
142144
render result as JSON
143145
}
@@ -245,15 +247,15 @@ class BioActivityController {
245247
if (!userId) {
246248
response.status = 401
247249
result = [status: 401, error: "Access denied: User has not been authenticated."]
248-
} else if(projectService.isUserAdminForProject(userId, activity?.projectId) || activityService.isUserOwnerForActivity(userId, activity?.activityId)) {
250+
} else if (projectService.isUserAdminForProject(userId, activity?.projectId) || activityService.isUserOwnerForActivity(userId, activity?.activityId)) {
249251
def resp = activityService.delete(id)
250252
if (resp == SC_OK) {
251253
result = [status: resp, text: 'deleted']
252254
} else {
253255
response.status = resp
254256
result = [status: resp, error: "Error deleting the survey, please try again later."]
255257
}
256-
} else{
258+
} else {
257259
response.status = 401
258260
result = [status: 401, error: "Access denied: User is not an admin or owner of this activity - ${id}"]
259261
}
@@ -320,7 +322,7 @@ class BioActivityController {
320322
def list() {
321323
}
322324

323-
def allRecords (){
325+
def allRecords() {
324326
render(view: 'list', model: [view: 'allrecords', user: userService.user])
325327
}
326328

@@ -341,8 +343,8 @@ class BioActivityController {
341343
GrailsParameterMap queryParams = new GrailsParameterMap([:], request)
342344
Map parsed = commonService.parseParams(params)
343345
parsed.userId = userService.getCurrentUserId()
344-
parsed.each{ key, value ->
345-
if(value != null && value){
346+
parsed.each { key, value ->
347+
if (value != null && value) {
346348
queryParams.put(key, value)
347349
}
348350
}
@@ -358,9 +360,10 @@ class BioActivityController {
358360
queryParams
359361
}
360362

361-
/*
362-
* Search project activities and records
363-
*/
363+
/*
364+
* Search project activities and records
365+
*/
366+
364367
def searchProjectActivities() {
365368
GrailsParameterMap queryParams = constructDefaultSearchParams(params)
366369

@@ -405,12 +408,12 @@ class BioActivityController {
405408
* map points are generated from this function. It requires some client side code to convert the output of this
406409
* function to points.
407410
*/
408-
def getProjectActivitiesRecordsForMapping(){
411+
def getProjectActivitiesRecordsForMapping() {
409412
GrailsParameterMap queryParams = new GrailsParameterMap([:], request)
410413
Map parsed = commonService.parseParams(params)
411414
parsed.userId = userService.getCurrentUserId()
412-
parsed.each{ key, value ->
413-
if(value != null && value){
415+
parsed.each { key, value ->
416+
if (value != null && value) {
414417
queryParams.put(key, value)
415418
}
416419
}
@@ -441,7 +444,7 @@ class BioActivityController {
441444
coordinates : doc.coordinates
442445
]
443446

444-
if(doc.sites && doc.sites.size() > 0){
447+
if (doc.sites && doc.sites.size() > 0) {
445448
result.coordinates = doc.sites[0]?.extent?.geometry?.centre
446449
}
447450

@@ -451,7 +454,7 @@ class BioActivityController {
451454
render([activities: activities, total: searchResult.hits?.total ?: 0] as JSON)
452455
}
453456

454-
def ajaxListForProject(String id){
457+
def ajaxListForProject(String id) {
455458

456459
def model = [:]
457460
def query = [pageSize: params.max ?: 10,
@@ -518,7 +521,7 @@ class BioActivityController {
518521
model.site = model.activity?.siteId ? siteService.get(model.activity.siteId, [view: 'brief']) : null
519522
model.mapFeatures = model.site ? siteService.getMapFeatures(model.site) : []
520523
model.project = projectId ? projectService.get(model.activity.projectId) : null
521-
model.projectSite = model.project.sites?.find{it.siteId == model.project.projectSiteId}
524+
model.projectSite = model.project.sites?.find { it.siteId == model.project.projectSiteId }
522525

523526
// Add the species lists that are relevant to this activity.
524527
model.speciesLists = new JSONArray()
@@ -561,9 +564,8 @@ class BioActivityController {
561564

562565
if (data && !data.error) {
563566
activityService.lookupSpeciesInOutputData(params.pActivityId, params.type, params.listName, data.data)
564-
result = [status:SC_OK, data:data.data]
565-
}
566-
else {
567+
result = [status: SC_OK, data: data.data]
568+
} else {
567569
result = data
568570
}
569571

@@ -573,10 +575,9 @@ class BioActivityController {
573575
def resultJson = result as JSON
574576
render resultJson.toString()
575577
}
576-
}
577-
else {
578+
} else {
578579
response.status = SC_BAD_REQUEST
579-
result = [status: SC_BAD_REQUEST, error:'No file attachment found']
580+
result = [status: SC_BAD_REQUEST, error: 'No file attachment found']
580581
// This is returned to the browswer as a text response due to workaround the warning
581582
// displayed by IE8/9 when JSON is returned from an iframe submit.
582583

@@ -586,6 +587,40 @@ class BioActivityController {
586587
}
587588
}
588589

590+
def uploadFile() {
591+
String stagingDirPath = grailsApplication.config.upload.path
592+
Map result = [:]
593+
if (request.respondsTo('getFile')) {
594+
MultipartFile multipartFile = request.getFile('files')
595+
596+
if (multipartFile?.size) { // will only have size if a file was selected
597+
String filename = multipartFile.getOriginalFilename().replaceAll(' ', '_')
598+
String ext = FilenameUtils.getExtension(filename)
599+
filename = FileUtils.nextUniqueFileName(FilenameUtils.getBaseName(filename) + '.' + ext, stagingDirPath)
600+
601+
File stagingDir = new File(stagingDirPath)
602+
stagingDir.mkdirs()
603+
File file = new File(FileUtils.fullPath(filename, stagingDirPath))
604+
multipartFile.transferTo(file)
605+
606+
Map metadata = [
607+
name : filename,
608+
size : multipartFile.size,
609+
contentType: multipartFile.contentType,
610+
url : FileUtils.encodeUrl(grailsApplication.config.grails.serverURL + "/download/file?filename=", filename),
611+
attribution: '',
612+
notes : '',
613+
status : "active"
614+
]
615+
result = [files: [metadata]]
616+
}
617+
}
618+
619+
response.addHeader('Content-Type', 'text/plain')
620+
def resultJson = result as JSON
621+
render resultJson.toString()
622+
}
623+
589624
private static boolean isProjectActivityClosed(Map projectActivity) {
590625
projectActivity?.endDate && Date.parse("yyyy-MM-dd", projectActivity?.endDate)?.before(new Date())
591626
}

grails-app/controllers/au/org/ala/biocollect/DownloadController.groovy

+21
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package au.org.ala.biocollect
22

33
import au.org.ala.biocollect.merit.WebService
44
import groovyx.net.http.ContentType
5+
import org.apache.commons.io.FilenameUtils
56

67
class DownloadController {
78

@@ -18,4 +19,24 @@ class DownloadController {
1819
webService.proxyGetRequest(response, "${grailsApplication.config.ecodata.service.url}/search/downloadProjectDataFile/${params.id}", true, true)
1920
}
2021
}
22+
23+
def file() {
24+
if (params.id) {
25+
webService.proxyGetRequest(response, "${grailsApplication.config.ecodata.service.url}/document/${params.id}/file", true, true)
26+
} else if (params.filename) {
27+
String path = grailsApplication.config.upload.images.path
28+
File file = new File(FileUtils.fullPath(params.filename, path))
29+
if (file.exists()) {
30+
response.setHeader('Content-Disposition', "Attachment;Filename=\"${params.filename}\"")
31+
if (params.forceDownload?.toBoolean()) {
32+
// set the content type to octet-stream to stop the browser from auto playing known types
33+
response.setContentType('application/octet-stream')
34+
}
35+
response.outputStream << new FileInputStream(file)
36+
response.outputStream.flush()
37+
} else {
38+
response.status = 404
39+
}
40+
}
41+
}
2142
}

grails-app/controllers/au/org/ala/biocollect/merit/ImageController.groovy

+11-35
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package au.org.ala.biocollect.merit
22

3+
import au.org.ala.biocollect.FileUtils
34
import com.drew.imaging.ImageMetadataReader
45
import com.drew.lang.GeoLocation
56
import com.drew.metadata.Directory
@@ -137,16 +138,17 @@ class ImageController {
137138
MultipartFile file = request.getFile('files')
138139
//println "file is " + file
139140
if (file?.size) { // will only have size if a file was selected
140-
def filename = file.getOriginalFilename().replaceAll(' ','_')
141-
def ext = FilenameUtils.getExtension(filename)
142-
filename = nextUniqueFileName(FilenameUtils.getBaseName(filename)+'.'+ext)
141+
String filename = file.getOriginalFilename().replaceAll(' ','_')
142+
String ext = FilenameUtils.getExtension(filename)
143+
String path = grailsApplication.config.upload.images.path
144+
filename = FileUtils.nextUniqueFileName(FilenameUtils.getBaseName(filename)+'.'+ext, path)
143145

144146
def thumbFilename = FilenameUtils.removeExtension(filename) + "-thumb." + ext
145147
//println "filename=${filename}"
146148

147149
def colDir = new File(grailsApplication.config.upload.images.path as String)
148150
colDir.mkdirs()
149-
File f = new File(fullPath(filename))
151+
File f = new File(FileUtils.fullPath(filename, path))
150152
//println "saving ${filename} to ${f.absoluteFile}"
151153
file.transferTo(f)
152154
def exifMd = getExifMetadata(f)
@@ -174,9 +176,9 @@ class ImageController {
174176
decimalLongitude: doubleToString(exifMd.decLng),
175177
verbatimLatitude: exifMd.latitude,
176178
verbatimLongitude: exifMd.longitude,
177-
url: encodeImageURL(grailsApplication.config.upload.images.url,filename),
178-
thumbnail_url: encodeImageURL(grailsApplication.config.upload.images.url, thumbFilename),
179-
delete_url: encodeImageURL(grailsApplication.config.grails.serverURL+"/image/delete?filename=", filename),
179+
url: FileUtils.encodeUrl(grailsApplication.config.upload.images.url,filename),
180+
thumbnail_url: FileUtils.encodeUrl(grailsApplication.config.upload.images.url, thumbFilename),
181+
delete_url: FileUtils.encodeUrl(grailsApplication.config.grails.serverURL+"/image/delete?filename=", filename),
180182
delete_type: 'DELETE',
181183
attribution: ''
182184
]
@@ -195,19 +197,14 @@ class ImageController {
195197
render '{"deleted":true}'
196198
}
197199

198-
def encodeImageURL(prefix, filename) {
199-
def encodedFileName = filename.encodeAsURL().replaceAll('\\+', '%20')
200-
URI uri = new URI(prefix + "/" + encodedFileName)
201-
return uri.toURL();
202-
}
203200

204201
/**
205202
* A convenience method to help serve files in the dev. environment.
206203
* The content type of the file is derived purely from the file extension.
207204
*/
208205
def get() {
209-
210-
File f = new File(fullPath(params.id))
206+
String path = grailsApplication.config.upload.images.path
207+
File f = new File(FileUtils.fullPath(params.id, path))
211208
if (!f.exists()) {
212209
response.status = 404
213210
return
@@ -221,25 +218,4 @@ class ImageController {
221218

222219
}
223220

224-
/**
225-
* We are preserving the file name so the URLs look nicer and the file extension isn't lost.
226-
* As filename are not guaranteed to be unique, we are pre-pending the file with a counter if necessary to
227-
* make it unique.
228-
*/
229-
private String nextUniqueFileName(filename) {
230-
int counter = 0;
231-
String newFilename = filename
232-
File f = new File(fullPath(newFilename))
233-
while (f.exists()) {
234-
newFilename = "${counter}_${filename}"
235-
counter++;
236-
f = new File(fullPath(newFilename))
237-
}
238-
return newFilename;
239-
}
240-
241-
String fullPath(filename) {
242-
243-
return grailsApplication.config.upload.images.path + File.separator + filename
244-
}
245221
}

0 commit comments

Comments
 (0)