Jdlrobson has uploaded a new change for review.

  https://gerrit.wikimedia.org/r/53769


Change subject: Story 347: Provide nicer filenames
......................................................................

Story 347: Provide nicer filenames

Filenames are descriptions with date appended accurate to minutes

Change-Id: I8b5f2a78b9f73a23abc505ac424ab3926b81bf6d
---
M MobileFrontend.i18n.php
M MobileFrontend.php
M javascripts/modules/mf-photo.js
M tests/js/test_mf-photo.js
4 files changed, 122 insertions(+), 13 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/MobileFrontend 
refs/changes/69/53769/1

diff --git a/MobileFrontend.i18n.php b/MobileFrontend.i18n.php
index 0b3be89..792e2fc 100644
--- a/MobileFrontend.i18n.php
+++ b/MobileFrontend.i18n.php
@@ -233,6 +233,7 @@
        'mobile-frontend-image-uploading-long' => 'Image still uploading! 
Thanks for your patience.',
        'mobile-frontend-image-uploading-cancel' => '<a href="#">Cancel</a> if 
this is taking too long.',
        'mobile-frontend-photo-upload-error' => 'Error, try again.',
+       'mobile-frontend-photo-upload-error-filename' => 'Error, please provide 
a more descriptive summary.',
        'mobile-frontend-photo-upload-success-article' => 'Success! Your image 
is now live on this page.',
        'mobile-frontend-photo-license' => 'By clicking "Submit", you agree to 
our [//wikimediafoundation.org/wiki/Terms_of_use Terms of Use] and agree to 
release your photo under the [//creativecommons.org/licenses/by-sa/3.0/ 
Creative Commons Attribution-ShareAlike 3.0 License].',
        'mobile-frontend-photo-submit' => 'Submit',
@@ -577,6 +578,7 @@
        'mobile-frontend-image-uploading-long' => 'Text that displays whilst an 
image is taking long to upload',
        'mobile-frontend-image-uploading-cancel' => 'Text saying that user can 
cancel the image upload. Word "cancel" should be a link.',
        'mobile-frontend-photo-upload-error' => 'Text that displays when a 
photo fails to upload',
+       'mobile-frontend-photo-upload-error-filename' => 'Text that displays 
when a photo fails to upload due to filename',
        'mobile-frontend-photo-upload-success-article' => 'Text that displays 
when a photo has been successfully added to a page.',
        'mobile-frontend-photo-license' => 'Text notifying user of license that 
image will be published under. You can change the URL to a "local" Wikipedia 
URL, but you cannot make it point to the country specific CC BY-SA 3.0 
license.',
        'mobile-frontend-photo-submit' => 'Caption for the submit button on the 
photo uplaod form.
diff --git a/MobileFrontend.php b/MobileFrontend.php
index 573a344..7bef703 100644
--- a/MobileFrontend.php
+++ b/MobileFrontend.php
@@ -378,6 +378,7 @@
                'mobile-frontend-photo-article-edit-comment',
                'mobile-frontend-photo-article-donate-comment',
                'mobile-frontend-photo-upload-error',
+               'mobile-frontend-photo-upload-error-filename',
                'mobile-frontend-photo-upload-success-article',
                'mobile-frontend-photo-caption-placeholder',
                'mobile-frontend-image-loading',
diff --git a/javascripts/modules/mf-photo.js b/javascripts/modules/mf-photo.js
index 1c4071e..1c91dbd 100644
--- a/javascripts/modules/mf-photo.js
+++ b/javascripts/modules/mf-photo.js
@@ -52,15 +52,75 @@
                };
        }
 
-       function generateFileName( file, pageTitle ) {
-               // FIXME: deal with long and invalid names
-               var name = 'Lead_Photo_For_' + pageTitle.replace( / /g, '_' ) + 
Math.random(),
-                       extension;
+       // Originally written by Brion for WikiLovesMonuments app
+       function trimUtf8String( str, allowedLength ) {
+               // Count UTF-8 bytes to see where we need to crop long names.
+               var bytes = 0, chars = 0, codeUnit, len, i;
 
-               name = name.replace( String.fromCharCode( 27 ), '-' );
-               name = name.replace( /[\x7f\.\[#<>\[\]\|\{\}\/:]/g, '-' );
-               extension = file.name.slice( file.name.lastIndexOf( '.' ) + 1 );
-               return name + '.' + extension;
+               for ( i = 0; i < str.length; i++ ) {
+                       // JavaScript strings are UTF-16.
+                       codeUnit = str.charCodeAt( i );
+
+                       // http://en.wikipedia.org/wiki/UTF-8#Description
+                       if ( codeUnit < 0x80 ) {
+                               len = 1;
+                       } else if ( codeUnit < 0x800 ) {
+                               len = 2;
+                       } else if ( codeUnit >= 0xd800 && codeUnit < 0xe000 ) {
+                               // 
http://en.wikipedia.org/wiki/UTF-16#Description
+                               // Code point is one half of a surrogate pair.
+                               // This and its partner combine to form a 
single 4 byte character in UTF-8.
+                               len = 4;
+                       } else {
+                               len = 3;
+                       }
+
+                       if ( bytes + len <= allowedLength ) {
+                               bytes += len;
+                               chars++;
+                               if ( len === 4 ) {
+                                       // Skip over second half of surrogate 
pair as a unit.
+                                       chars++;
+                                       i++;
+                               }
+                       } else {
+                               // Ran out of bytes.
+                               return str.substr( 0, chars );
+                       }
+               }
+
+               // We fit!
+               return str;
+       }
+
+       /**
+        * Generates a file name from a description and a date
+        * Removes illegal characters
+        * Respects maximum file name length (240 bytes)
+        *
+        * @param {String} description Data to be preprocessed and added to 
options
+        * @param {Date} date An optional date (defaults to current date)
+        * @return {String} a legal filename
+        */
+       function generateFileName( description, date ) {
+               var allowedLength = 240, // 240 bytes maximum enforced by 
MediaWiki
+                       delimiter = ' ', name, suffix;
+
+               date = date || new Date();
+               name = description.replace( String.fromCharCode( 27 ), 
delimiter );
+               name = name.replace( /[\x7f\.\[#<>\[\]\|\{\}\/:]/g, delimiter );
+
+               function pad( number ) {
+                       return number < 9 ? '0' + number : number;
+               }
+
+               // FIXME: should we really assume jpg?
+               suffix = delimiter + date.getFullYear() + delimiter +
+                       pad( date.getMonth() + 1 ) + delimiter + pad( 
date.getDate() ) + delimiter +
+                       pad( date.getHours() ) + delimiter + pad( 
date.getMinutes() ) + '.jpg';
+
+               allowedLength = allowedLength - suffix.length;
+               return trimUtf8String( name, allowedLength ) + suffix;
        }
 
        PhotoApi = Api.extend( {
@@ -86,9 +146,9 @@
 
                        function doUpload( token ) {
                                var formData = new FormData(), descTextToAppend;
-                               options.fileName = generateFileName( 
options.file, options.pageTitle );
                                descTextToAppend = mw.config.get( 
'wgPhotoUploadAppendToDesc' );
                                descTextToAppend = descTextToAppend ? '\n\n' + 
descTextToAppend : '';
+                               options.fileName = generateFileName( 
options.description );
 
                                formData.append( 'action', 'upload' );
                                formData.append( 'format', 'json' );
@@ -119,13 +179,22 @@
                                                self.emit( 'progress', 
ev.loaded / ev.total );
                                        }
                                } ).done( function( data ) {
-                                       var descriptionUrl = '';
+                                       var descriptionUrl = '',
+                                               warnings = data.upload ? 
data.upload.warnings : false;
                                        if ( !data || !data.upload ) {
                                                // error uploading image
                                                result.reject( data.error ? 
data.error.info : '' );
                                                return;
                                        }
-                                       options.fileName = data.upload.filename 
|| data.upload.warnings.duplicate['0'];
+                                       options.fileName = data.upload.filename;
+                                       if ( warnings ) {
+                                               if ( warnings.duplicate ) {
+                                                       options.fileName = 
warnings.duplicate[ '0' ];
+                                               } else if ( warnings.exists ) {
+                                                       return result.reject( 
'Filename exists',
+                                                               mw.msg( 
'mobile-frontend-photo-upload-error-filename' ) );
+                                               }
+                                       }
                                        // FIXME: API doesn't return this 
information on duplicate images...
                                        if ( data.upload.imageinfo ) {
                                                descriptionUrl = 
data.upload.imageinfo.descriptionurl;
@@ -396,8 +465,8 @@
                                        descriptionUrl: descriptionUrl,
                                        url: self.preview.imageUrl
                                } );
-                       } ).fail( function( err ) {
-                               popup.show( mw.msg( 
'mobile-frontend-photo-upload-error' ), 'toast error' );
+                       } ).fail( function( err, msg ) {
+                               popup.show( msg || mw.msg( 
'mobile-frontend-photo-upload-error' ), 'toast error' );
                                self.log( { action: 'error', errorText: err } );
                                self.emit( 'error' );
                        } );
@@ -454,8 +523,10 @@
        }
 
        M.define( 'photo', {
+               generateFileName: generateFileName,
                isSupported: isSupported,
                PhotoUploader: PhotoUploader,
+               trimUtf8String: trimUtf8String,
                // just for testing
                _needsPhoto: needsPhoto,
                _PhotoUploadProgress: PhotoUploadProgress
diff --git a/tests/js/test_mf-photo.js b/tests/js/test_mf-photo.js
index 05aec3a..df8d348 100644
--- a/tests/js/test_mf-photo.js
+++ b/tests/js/test_mf-photo.js
@@ -63,4 +63,39 @@
        );
 } );
 
+test( 'generateFileName', function() {
+       var date = new Date( 2010, 9, 15, 12, 51 ),
+               name = photo.generateFileName( 'Jon eating bacon next to an 
armadillo', date );
+       strictEqual( name, 'Jon eating bacon next to an armadillo 2010 10 15 12 
51.jpg',
+               'Check file name is description with appended date' );
+} );
+
+test( 'generateFileName test padding', function() {
+       var date = new Date( 2013, 2, 1, 12, 51 ), // note 0 = january
+               name = photo.generateFileName( 'Tomasz eating bacon next to a 
dinosaur', date );
+       strictEqual( name, 'Tomasz eating bacon next to a dinosaur 2013 03 01 
12 51.jpg',
+               'Check file name is description with appended date and numbers 
were padded' );
+} );
+
+test( 'generateFileName long line', function() {
+       var i,
+               longDescription = '',
+               date = new Date( 2013, 2, 1, 12, 51 ), name;
+
+       for ( i=0; i < 240; i++ ) {
+               longDescription += 'a';
+       }
+       name = photo.generateFileName( longDescription, date );
+       strictEqual( name.length, 240, 'Check file name was shortened to the 
minimum length' );
+       strictEqual( name.substr( 233, 7 ), ' 51.jpg', 'ends with date' );
+} );
+
+
+test( 'trimUtf8String', function() {
+       strictEqual( photo.trimUtf8String( 'Just a string', 20 ), 'Just a 
string', 'ascii string fits' );
+       strictEqual( photo.trimUtf8String( 'Just a string', 10 ), 'Just a str', 
'ascii string truncated' );
+       strictEqual( photo.trimUtf8String( 'Júst á stríng', 10 ), 'Júst á s', 
'latin1 string truncated' );
+       strictEqual( photo.trimUtf8String( 'こんにちは', 10 ), 'こんに', 'CJK string 
truncated' );
+} );
+
 }( jQuery, mw.mobileFrontend ) );

-- 
To view, visit https://gerrit.wikimedia.org/r/53769
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I8b5f2a78b9f73a23abc505ac424ab3926b81bf6d
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/MobileFrontend
Gerrit-Branch: master
Gerrit-Owner: Jdlrobson <jrob...@wikimedia.org>

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to