Skip to content
This repository has been archived by the owner on Nov 29, 2023. It is now read-only.

Commit

Permalink
fixed: iOS camera input gives the same filename to all files causing …
Browse files Browse the repository at this point in the history
…files to overwrite each other

kobotoolbox/enketo-express#374
  • Loading branch information
MartijnR committed Dec 31, 2015
1 parent c7b7b30 commit f25e3d2
Show file tree
Hide file tree
Showing 7 changed files with 80 additions and 10 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ Change Log
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).

[4.4.6] - 2015-12-31
--------------------
##### Fixed
- Files from iOS camera app overwrite each other if in the same record because filenames are the same.

[4.4.5] - 2015-12-22
--------------------
##### Changed
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "enketo-core",
"description": "Extensible Enketo core containing the form logic engine and responsive form styling",
"homepage": "https://enketo.org",
"version": "4.4.5",
"version": "4.4.6",
"license": "Apache-2.0",
"os": [ "darwin", "linux" ],
"keywords": [ "enketo", "OpenRosa", "ODK", "XForms" ],
Expand Down
3 changes: 2 additions & 1 deletion src/js/Form.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ define( function( require, exports, module ) {
var widgets = require( './widgets-controller' );
var $ = require( 'jquery' );
var Promise = require( 'lie' );
var utils = require( './utils' );
require( './plugins' );
require( './extend' );
require( 'jquery-touchswipe' );
Expand Down Expand Up @@ -2006,7 +2007,7 @@ define( function( require, exports, module ) {

// set file input values to the actual name of file (without c://fakepath or anything like that)
if ( n.val.length > 0 && n.inputType === 'file' && $input[ 0 ].files[ 0 ] && $input[ 0 ].files[ 0 ].size > 0 ) {
n.val = $input[ 0 ].files[ 0 ].name;
n.val = utils.getFilename( $input[ 0 ].files[ 0 ], $input[ 0 ].dataset.filenamePostfix );
}

if ( eventType === 'validate' ) {
Expand Down
17 changes: 14 additions & 3 deletions src/js/file-manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ define( function( require, exports, module ) {
'use strict';
var Promise = require( 'lie' );
var $ = require( 'jquery' );
var utils = require( './utils' );

var supported = typeof FileReader !== 'undefined',
notSupportedAdvisoryMsg = '';
Expand Down Expand Up @@ -90,13 +91,23 @@ define( function( require, exports, module ) {
* @return {[File]} array of files
*/
function getCurrentFiles() {
var file,
files = [];
var file;
var newFilename;
var files = [];

// first get any files inside file input elements
$( 'form.or input[type="file"]' ).each( function() {
file = this.files[ 0 ];
if ( file ) {
if ( file && file.name ) {
// Correct file names by adding a unique-ish postfix
// First create a clone, because the name property is immutable
// TODO: in the future, when browser support increase we can invoke
// the File constructor to do this.
newFilename = utils.getFilename( file, this.dataset.filenamePostfix );
file = new Blob( [ file ], {
type: file.type
} );
file.name = newFilename;
files.push( file );
}
} );
Expand Down
20 changes: 20 additions & 0 deletions src/js/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,28 @@ define( function( require, exports, module ) {
return str;
}

// Because iOS gives any camera-provided file the same filename, we need to a
// unique-ified filename.
//
// See https://github.com/kobotoolbox/enketo-express/issues/374
function getFilename( file, postfix ) {
var filenameParts;
if ( typeof file === 'object' && file.name ) {
postfix = postfix || '';
filenameParts = file.name.split( '.' );
if ( filenameParts.length > 1 ) {
filenameParts[ filenameParts.length - 2 ] += postfix;
} else if ( filenameParts.length === 1 ) {
filenameParts[ 0 ] += postfix;
}
return filenameParts.join( '.' );
}
return '';
}

module.exports = {
parseFunctionFromExpression: parseFunctionFromExpression,
stripQuotes: stripQuotes,
getFilename: getFilename
};
} );
11 changes: 6 additions & 5 deletions src/widget/file/filepicker.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ define( function( require, exports, module ) {
var $ = require( 'jquery' );
var Widget = require( '../../js/Widget' );
var fileManager = require( '../../js/file-manager' );
var utils = require( '../../js/utils' );

var pluginName = 'filepicker';

Expand Down Expand Up @@ -109,8 +110,10 @@ define( function( require, exports, module ) {
$( this.element ).on( 'change.propagate.' + this.namespace, function( event ) {
var file;
var fileName;
var postfix;
var $input = $( this );
var loadedFileName = $input.attr( 'data-loaded-file-name' );
var now = new Date();

// trigger eventhandler to update instance value
if ( event.namespace === 'propagate' ) {
Expand All @@ -122,7 +125,9 @@ define( function( require, exports, module ) {

// get the file
file = this.files[ 0 ];
fileName = that._getFileName( file );
postfix = '-' + now.getHours() + '_' + now.getMinutes() + '_' + now.getSeconds();
this.dataset.filenamePostfix = postfix;
fileName = utils.getFilename( file, postfix );

// process the file
fileManager.getFileUrl( file )
Expand All @@ -148,10 +153,6 @@ define( function( require, exports, module ) {
} );
};

Filepicker.prototype._getFileName = function( file ) {
return ( typeof file === 'object' && file.name ) ? file.name : '';
};

Filepicker.prototype._showFileName = function( fileName ) {
this.$fakeInput.text( fileName );
};
Expand Down
32 changes: 32 additions & 0 deletions test/spec/utils.browser-spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* In a future version of PhantomJS with DOMParser support for Blobs, these tests can move to the regular spec.
*/

var utils = require( '../../src/js/utils' );

describe( 'return postfixed filenames', function() {

[
[ 'myname', '-mypostfix', 'myname-mypostfix' ],
[ 'myname.jpg', '-mypostfix', 'myname-mypostfix.jpg' ],
[ 'myname.dot.jpg', '-mypostfix', 'myname.dot-mypostfix.jpg' ],
[ 'myname.000', '-mypostfix', 'myname-mypostfix.000' ],
[ undefined, 'mypostfix', '' ],
[ null, 'mypostfix', '' ],
[ false, 'mypostfix', '' ],
[ 'myname', undefined, 'myname' ],
[ 'myname', null, 'myname' ],
[ 'myname', false, 'myname' ]
].forEach( function( test ) {
var file = new Blob( [ 'a' ], {
type: 'text'
} );
file.name = test[ 0 ];
var postfix = test[ 1 ];
var expected = test[ 2 ];

it( 'returns the filename ' + expected + ' from ' + file.name + ' and ' + postfix, function() {
expect( utils.getFilename( file, postfix ) ).toEqual( expected );
} );
} );
} );

0 comments on commit f25e3d2

Please sign in to comment.