From 8dab8864f62c6915bc7751be734030d0931cc0ad Mon Sep 17 00:00:00 2001 From: pichalite Date: Fri, 17 Feb 2017 19:42:02 +0000 Subject: [PATCH 1/7] Add extensions for cropped images --- public/src/modules/pictureCropper.js | 38 ++++++++------- src/file.js | 19 ++++++-- src/user/picture.js | 72 ++++++++++++++++++++-------- 3 files changed, 87 insertions(+), 42 deletions(-) diff --git a/public/src/modules/pictureCropper.js b/public/src/modules/pictureCropper.js index 40be206c19..b04bc48e97 100644 --- a/public/src/modules/pictureCropper.js +++ b/public/src/modules/pictureCropper.js @@ -18,7 +18,7 @@ define('pictureCropper', ['translator', 'cropper'], function (translator, croppe uploadModal = $(uploadModal); uploadModal.modal('show'); - uploadModal.on('hidden.bs.modal', function () { + uploadModal.on('hidden.bs.modal', function () { uploadModal.remove(); }); @@ -31,9 +31,11 @@ define('pictureCropper', ['translator', 'cropper'], function (translator, croppe }); }; - module.handleImageCrop = function (data, callback) { + module.handleImageCrop = function (data, callback) { $('#crop-picture-modal').remove(); - templates.parse('modals/crop_picture', {url: data.url}, function (cropperHtml) { + templates.parse('modals/crop_picture', { + url: data.url + }, function (cropperHtml) { translator.translate(cropperHtml, function (translated) { var cropperModal = $(translated); cropperModal.modal('show'); @@ -58,28 +60,28 @@ define('pictureCropper', ['translator', 'cropper'], function (translator, croppe cropperModal.find('.reset').on('click', function () { cropperTool.reset(); }); - + cropperModal.find('.crop-btn').on('click', function () { $(this).addClass('disabled'); var imageData = data.imageType ? cropperTool.getCroppedCanvas().toDataURL(data.imageType) : cropperTool.getCroppedCanvas().toDataURL(); - + cropperModal.find('#upload-progress-bar').css('width', '100%'); cropperModal.find('#upload-progress-box').show().removeClass('hide'); - + var socketData = {}; socketData[data.paramName] = data.paramValue; socketData['imageData'] = imageData; - + socket.emit(data.socketMethod, socketData, function (err, imageData) { - if (err) { - cropperModal.find('#upload-progress-box').hide(); - cropperModal.find('.upload-btn').removeClass('disabled'); - cropperModal.find('.crop-btn').removeClass('disabled'); - return app.alertError(err.message); - } - - callback(imageData.url); - cropperModal.modal('hide'); + if (err) { + cropperModal.find('#upload-progress-box').hide(); + cropperModal.find('.upload-btn').removeClass('disabled'); + cropperModal.find('.crop-btn').removeClass('disabled'); + return app.alertError(err.message); + } + + callback(imageData.url); + cropperModal.modal('hide'); }); }); @@ -115,8 +117,8 @@ define('pictureCropper', ['translator', 'cropper'], function (translator, croppe return showAlert('error', '[[uploads:select-file-to-upload]]'); } - var file = fileInput[0].files[0]; - var reader = new FileReader(); + var file = fileInput[0].files[0]; + var reader = new FileReader(); var imageUrl; var imageType = file.type; diff --git a/src/file.js b/src/file.js index 5e13c5b2a9..afdcef82bf 100644 --- a/src/file.js +++ b/src/file.js @@ -6,6 +6,7 @@ var path = require('path'); var winston = require('winston'); var jimp = require('jimp'); var mkdirp = require('mkdirp'); +var mime = require('mime'); var utils = require('../public/src/utils'); @@ -13,8 +14,8 @@ var file = {}; file.saveFileToLocal = function (filename, folder, tempPath, callback) { /* - * remarkable doesn't allow spaces in hyperlinks, once that's fixed, remove this. - */ + * remarkable doesn't allow spaces in hyperlinks, once that's fixed, remove this. + */ filename = filename.split('.'); filename.forEach(function (name, idx) { filename[idx] = utils.slugify(name); @@ -100,7 +101,8 @@ file.existsSync = function (path) { var exists = false; try { exists = fs.statSync(path); - } catch(err) { + } + catch (err) { exists = false; } @@ -110,7 +112,8 @@ file.existsSync = function (path) { file.link = function link(filePath, destPath, cb) { if (process.platform === 'win32') { fs.link(filePath, destPath, cb); - } else { + } + else { fs.symlink(filePath, destPath, 'file', cb); } }; @@ -120,4 +123,12 @@ file.linkDirs = function linkDirs(sourceDir, destDir, callback) { fs.symlink(sourceDir, destDir, type, callback); }; +file.typeToExtension = function (type) { + var extension; + if (type) { + extension = '.' + mime.extension(type); + } + return extension; +}; + module.exports = file; diff --git a/src/user/picture.js b/src/user/picture.js index bc0b8f597f..116724e339 100644 --- a/src/user/picture.js +++ b/src/user/picture.js @@ -8,7 +8,6 @@ var nconf = require('nconf'); var crypto = require('crypto'); var winston = require('winston'); var request = require('request'); -var mime = require('mime'); var plugins = require('../plugins'); var file = require('../file'); @@ -43,7 +42,10 @@ module.exports = function (User) { async.waterfall([ function (next) { if (plugins.hasListeners('filter:uploadImage')) { - return plugins.fireHook('filter:uploadImage', {image: picture, uid: updateUid}, next); + return plugins.fireHook('filter:uploadImage', { + image: picture, + uid: updateUid + }, next); } var filename = updateUid + '-profileimg' + (keepAllVersions ? '-' + Date.now() : '') + (convertToPNG ? '.png' : extension); @@ -79,7 +81,10 @@ module.exports = function (User) { }, function (_image, next) { uploadedImage = _image; - User.setUserFields(updateUid, {uploadedpicture: uploadedImage.url, picture: uploadedImage.url}, next); + User.setUserFields(updateUid, { + uploadedpicture: uploadedImage.url, + picture: uploadedImage.url + }, next); }, function (next) { next(null, uploadedImage); @@ -99,7 +104,7 @@ module.exports = function (User) { var uploadSize = parseInt(meta.config.maximumProfileImageSize, 10) || 256; var size = res.headers['content-length']; var type = res.headers['content-type']; - var extension = mime.extension(type); + var extension = file.typeToExtension(type); if (['png', 'jpeg', 'jpg', 'gif'].indexOf(extension) === -1) { return callback(new Error('[[error:invalid-image-extension]]')); @@ -109,12 +114,21 @@ module.exports = function (User) { return callback(new Error('[[error:file-too-big, ' + uploadSize + ']]')); } - var picture = {url: url, name: ''}; - plugins.fireHook('filter:uploadImage', {image: picture, uid: uid}, function (err, image) { + var picture = { + url: url, + name: '' + }; + plugins.fireHook('filter:uploadImage', { + image: picture, + uid: uid + }, function (err, image) { if (err) { return callback(err); } - User.setUserFields(uid, {uploadedpicture: image.url, picture: image.url}); + User.setUserFields(uid, { + uploadedpicture: image.url, + picture: image.url + }); callback(null, image); }); }); @@ -170,10 +184,14 @@ module.exports = function (User) { }; if (plugins.hasListeners('filter:uploadImage')) { - return plugins.fireHook('filter:uploadImage', {image: image, uid: data.uid}, next); + return plugins.fireHook('filter:uploadImage', { + image: image, + uid: data.uid + }, next); } - var filename = data.uid + '-profilecover' + (keepAllVersions ? '-' + Date.now() : ''); + var extension = file.typeToExtension(data.imageData.slice(5, data.imageData.indexOf('base64') - 1)); + var filename = data.uid + '-profilecover' + (keepAllVersions ? '-' + Date.now() : '') + (extension || ''); async.waterfall([ function (next) { file.isFileTypeAllowed(data.file.path, next); @@ -208,20 +226,25 @@ module.exports = function (User) { winston.error(unlinkErr); } - callback(err); // send back the original error + callback(err); // send back the original error }); } if (data.position) { User.updateCoverPosition(data.uid, data.position, function (err) { - callback(err, {url: url}); + callback(err, { + url: url + }); + }); + } + else { + callback(err, { + url: url }); - } else { - callback(err, {url: url}); } }); }; - + User.uploadCroppedPicture = function (data, callback) { var keepAllVersions = parseInt(meta.config['profile:keepAllUserImages'], 10) === 1; var url, md5sum; @@ -229,7 +252,7 @@ module.exports = function (User) { if (!data.imageData) { return callback(new Error('[[error:invalid-data]]')); } - + async.waterfall([ function (next) { var size = data.file ? data.file.size : data.imageData.length; @@ -260,10 +283,14 @@ module.exports = function (User) { }; if (plugins.hasListeners('filter:uploadImage')) { - return plugins.fireHook('filter:uploadImage', {image: image, uid: data.uid}, next); + return plugins.fireHook('filter:uploadImage', { + image: image, + uid: data.uid + }, next); } - var filename = data.uid + '-profileavatar' + (keepAllVersions ? '-' + Date.now() : ''); + var extension = file.typeToExtension(data.imageData.slice(5, data.imageData.indexOf('base64') - 1)); + var filename = data.uid + '-profileavatar' + (keepAllVersions ? '-' + Date.now() : '') + (extension || ''); async.waterfall([ function (next) { file.isFileTypeAllowed(data.file.path, next); @@ -281,7 +308,10 @@ module.exports = function (User) { }, function (uploadData, next) { url = uploadData.url; - User.setUserFields(data.uid, {uploadedpicture: url, picture: url}, next); + User.setUserFields(data.uid, { + uploadedpicture: url, + picture: url + }, next); }, function (next) { fs.unlink(data.file.path, function (err) { @@ -293,10 +323,12 @@ module.exports = function (User) { } ], function (err) { if (err) { - callback(err); // send back the original error + callback(err); // send back the original error } - callback(err, {url: url}); + callback(err, { + url: url + }); }); }; From 4d755bad0c62bb877c9600aa9bb161ac4e0bc72c Mon Sep 17 00:00:00 2001 From: pichalite Date: Fri, 17 Feb 2017 19:57:18 +0000 Subject: [PATCH 2/7] Use typeToExtension from file.js --- src/controllers/uploads.js | 42 ++++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/src/controllers/uploads.js b/src/controllers/uploads.js index 18b0b63dda..eefbb89d85 100644 --- a/src/controllers/uploads.js +++ b/src/controllers/uploads.js @@ -43,7 +43,8 @@ uploadsController.uploadPost = function (req, res, next) { var isImage = uploadedFile.type.match(/image./); if (isImage) { uploadAsImage(req, uploadedFile, next); - } else { + } + else { uploadAsFile(req, uploadedFile, next); } }, next); @@ -59,7 +60,10 @@ function uploadAsImage(req, uploadedFile, callback) { return next(new Error('[[error:no-privileges]]')); } if (plugins.hasListeners('filter:uploadImage')) { - return plugins.fireHook('filter:uploadImage', {image: uploadedFile, uid: req.uid}, callback); + return plugins.fireHook('filter:uploadImage', { + image: uploadedFile, + uid: req.uid + }, callback); } file.isFileTypeAllowed(uploadedFile.path, next); }, @@ -156,7 +160,10 @@ uploadsController.uploadThumb = function (req, res, next) { } if (plugins.hasListeners('filter:uploadImage')) { - return plugins.fireHook('filter:uploadImage', {image: uploadedFile, uid: req.uid}, next); + return plugins.fireHook('filter:uploadImage', { + image: uploadedFile, + uid: req.uid + }, next); } uploadFile(req.uid, uploadedFile, next); @@ -167,11 +174,17 @@ uploadsController.uploadThumb = function (req, res, next) { uploadsController.uploadGroupCover = function (uid, uploadedFile, callback) { if (plugins.hasListeners('filter:uploadImage')) { - return plugins.fireHook('filter:uploadImage', {image: uploadedFile, uid: uid}, callback); + return plugins.fireHook('filter:uploadImage', { + image: uploadedFile, + uid: uid + }, callback); } if (plugins.hasListeners('filter:uploadFile')) { - return plugins.fireHook('filter:uploadFile', {file: uploadedFile, uid: uid}, callback); + return plugins.fireHook('filter:uploadFile', { + file: uploadedFile, + uid: uid + }, callback); } file.isFileTypeAllowed(uploadedFile.path, function (err) { @@ -184,7 +197,10 @@ uploadsController.uploadGroupCover = function (uid, uploadedFile, callback) { function uploadFile(uid, uploadedFile, callback) { if (plugins.hasListeners('filter:uploadFile')) { - return plugins.fireHook('filter:uploadFile', {file: uploadedFile, uid: uid}, callback); + return plugins.fireHook('filter:uploadFile', { + file: uploadedFile, + uid: uid + }, callback); } if (!uploadedFile) { @@ -197,7 +213,7 @@ function uploadFile(uid, uploadedFile, callback) { if (meta.config.hasOwnProperty('allowedFileExtensions')) { var allowed = file.allowedExtensions(); - var extension = typeToExtension(uploadedFile.type); + var extension = file.typeToExtension(uploadedFile.type); if (!extension || (allowed.length > 0 && allowed.indexOf(extension) === -1)) { return callback(new Error('[[error:invalid-file-type, ' + allowed.join(', ') + ']]')); } @@ -207,7 +223,7 @@ function uploadFile(uid, uploadedFile, callback) { } function saveFileToLocal(uploadedFile, callback) { - var extension = typeToExtension(uploadedFile.type); + var extension = file.typeToExtension(uploadedFile.type); if (!extension) { return callback(new Error('[[error:invalid-extension]]')); } @@ -228,14 +244,6 @@ function saveFileToLocal(uploadedFile, callback) { }); } -function typeToExtension(type) { - var extension; - if (type) { - extension = '.' + mime.extension(type); - } - return extension; -} - function deleteTempFiles(files) { async.each(files, function (file, next) { fs.unlink(file.path, function (err) { @@ -247,6 +255,4 @@ function deleteTempFiles(files) { }); } - - module.exports = uploadsController; From 7f4e4c8e2e9822e0f1157b90cf710f366b5a6bca Mon Sep 17 00:00:00 2001 From: pichalite Date: Fri, 17 Feb 2017 20:02:26 +0000 Subject: [PATCH 3/7] Fix styling --- src/controllers/uploads.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/controllers/uploads.js b/src/controllers/uploads.js index eefbb89d85..923e3cf7d8 100644 --- a/src/controllers/uploads.js +++ b/src/controllers/uploads.js @@ -43,8 +43,7 @@ uploadsController.uploadPost = function (req, res, next) { var isImage = uploadedFile.type.match(/image./); if (isImage) { uploadAsImage(req, uploadedFile, next); - } - else { + } else { uploadAsFile(req, uploadedFile, next); } }, next); From aacd8a242217e1182610646991fb5d855f58288c Mon Sep 17 00:00:00 2001 From: pichalite Date: Fri, 17 Feb 2017 20:36:13 +0000 Subject: [PATCH 4/7] Fix return and tests --- src/user/picture.js | 8 ++++---- test/mocha.opts | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/user/picture.js b/src/user/picture.js index 116724e339..af615a1cc1 100644 --- a/src/user/picture.js +++ b/src/user/picture.js @@ -8,6 +8,7 @@ var nconf = require('nconf'); var crypto = require('crypto'); var winston = require('winston'); var request = require('request'); +var mime = require('mime'); var plugins = require('../plugins'); var file = require('../file'); @@ -104,7 +105,7 @@ module.exports = function (User) { var uploadSize = parseInt(meta.config.maximumProfileImageSize, 10) || 256; var size = res.headers['content-length']; var type = res.headers['content-type']; - var extension = file.typeToExtension(type); + var extension = mime.extension(type); if (['png', 'jpeg', 'jpg', 'gif'].indexOf(extension) === -1) { return callback(new Error('[[error:invalid-image-extension]]')); @@ -236,8 +237,7 @@ module.exports = function (User) { url: url }); }); - } - else { + } else { callback(err, { url: url }); @@ -323,7 +323,7 @@ module.exports = function (User) { } ], function (err) { if (err) { - callback(err); // send back the original error + return callback(err); // send back the original error } callback(err, { diff --git a/test/mocha.opts b/test/mocha.opts index 49399dd418..9455d82707 100644 --- a/test/mocha.opts +++ b/test/mocha.opts @@ -1,2 +1,2 @@ --reporter dot ---timeout 15000 +--timeout 25000 From b33d34f7cf303c0b5d8d7645a889c362203bb0d1 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Sat, 18 Feb 2017 20:27:58 +0300 Subject: [PATCH 5/7] refactor user/picture.js --- src/user/picture.js | 275 +++++++++++++++++++------------------------- test/user.js | 56 ++++++--- 2 files changed, 155 insertions(+), 176 deletions(-) diff --git a/src/user/picture.js b/src/user/picture.js index af615a1cc1..ceb018b917 100644 --- a/src/user/picture.js +++ b/src/user/picture.js @@ -98,41 +98,41 @@ module.exports = function (User) { return callback(new Error('[[error:no-plugin]]')); } - request.head(url, function (err, res) { - if (err) { - return callback(err); - } - var uploadSize = parseInt(meta.config.maximumProfileImageSize, 10) || 256; - var size = res.headers['content-length']; - var type = res.headers['content-type']; - var extension = mime.extension(type); - - if (['png', 'jpeg', 'jpg', 'gif'].indexOf(extension) === -1) { - return callback(new Error('[[error:invalid-image-extension]]')); - } + async.waterfall([ + function (next) { + request.head(url, next); + }, + function (res, body, next) { + var uploadSize = parseInt(meta.config.maximumProfileImageSize, 10) || 256; + var size = res.headers['content-length']; + var type = res.headers['content-type']; + var extension = mime.extension(type); - if (size > uploadSize * 1024) { - return callback(new Error('[[error:file-too-big, ' + uploadSize + ']]')); - } + if (['png', 'jpeg', 'jpg', 'gif'].indexOf(extension) === -1) { + return callback(new Error('[[error:invalid-image-extension]]')); + } - var picture = { - url: url, - name: '' - }; - plugins.fireHook('filter:uploadImage', { - image: picture, - uid: uid - }, function (err, image) { - if (err) { - return callback(err); + if (size > uploadSize * 1024) { + return callback(new Error('[[error:file-too-big, ' + uploadSize + ']]')); } + + plugins.fireHook('filter:uploadImage', { + uid: uid, + image: { + url: url, + name: '' + } + }, next); + }, + function (image, next) { User.setUserFields(uid, { uploadedpicture: image.url, picture: image.url + }, function (err) { + next(err, image); }); - callback(null, image); - }); - }); + } + ], callback); }; User.updateCoverPosition = function (uid, position, callback) { @@ -140,8 +140,12 @@ module.exports = function (User) { }; User.updateCoverPicture = function (data, callback) { - var keepAllVersions = parseInt(meta.config['profile:keepAllUserImages'], 10) === 1; - var url, md5sum; + + var url; + var image = { + name: 'profileCover', + uid: data.uid + }; if (!data.imageData && data.position) { return User.updateCoverPosition(data.uid, data.position, callback); @@ -160,94 +164,42 @@ module.exports = function (User) { } if (data.file) { - return next(); + return setImmediate(next, null, data.file.path); } - md5sum = crypto.createHash('md5'); - md5sum.update(data.imageData); - md5sum = md5sum.digest('hex'); - - data.file = { - path: path.join(os.tmpdir(), md5sum) - }; - - var buffer = new Buffer(data.imageData.slice(data.imageData.indexOf('base64') + 7), 'base64'); - - fs.writeFile(data.file.path, buffer, { - encoding: 'base64' - }, next); + saveImageDataToTempFile(data.imageData, next); }, - function (next) { - var image = { - name: 'profileCover', - path: data.file.path, - uid: data.uid - }; + function (path, next) { + image.path = path; - if (plugins.hasListeners('filter:uploadImage')) { - return plugins.fireHook('filter:uploadImage', { - image: image, - uid: data.uid - }, next); - } - - var extension = file.typeToExtension(data.imageData.slice(5, data.imageData.indexOf('base64') - 1)); - var filename = data.uid + '-profilecover' + (keepAllVersions ? '-' + Date.now() : '') + (extension || ''); - async.waterfall([ - function (next) { - file.isFileTypeAllowed(data.file.path, next); - }, - function (next) { - file.saveFileToLocal(filename, 'profile', image.path, next); - }, - function (upload, next) { - next(null, { - url: nconf.get('relative_path') + upload.url, - name: image.name - }); - } - ], next); + uploadProfileOrCover('profilecover', image, data.imageData, next); }, function (uploadData, next) { url = uploadData.url; User.setUserField(data.uid, 'cover:url', uploadData.url, next); }, function (next) { - fs.unlink(data.file.path, function (err) { - if (err) { - winston.error(err); - } - next(); - }); + if (data.position) { + User.updateCoverPosition(data.uid, data.position, next); + } else { + setImmediate(next); + } } ], function (err) { - if (err) { - return fs.unlink(data.file.path, function (unlinkErr) { - if (unlinkErr) { - winston.error(unlinkErr); - } - - callback(err); // send back the original error - }); - } - - if (data.position) { - User.updateCoverPosition(data.uid, data.position, function (err) { - callback(err, { - url: url - }); - }); - } else { - callback(err, { - url: url - }); - } + deleteFile(image.path); + callback(err, { + url: url + }); }); }; User.uploadCroppedPicture = function (data, callback) { - var keepAllVersions = parseInt(meta.config['profile:keepAllUserImages'], 10) === 1; - var url, md5sum; + + var url; + var image = { + name: 'profileAvatar', + uid: data.uid + }; if (!data.imageData) { return callback(new Error('[[error:invalid-data]]')); @@ -255,56 +207,18 @@ module.exports = function (User) { async.waterfall([ function (next) { - var size = data.file ? data.file.size : data.imageData.length; + var size = data.imageData.length; var uploadSize = parseInt(meta.config.maximumProfileImageSize, 10) || 256; if (size > uploadSize * 1024) { return next(new Error('[[error:file-too-big, ' + meta.config.maximumProfileImageSize + ']]')); } - md5sum = crypto.createHash('md5'); - md5sum.update(data.imageData); - md5sum = md5sum.digest('hex'); - - data.file = { - path: path.join(os.tmpdir(), md5sum) - }; - - var buffer = new Buffer(data.imageData.slice(data.imageData.indexOf('base64') + 7), 'base64'); - - fs.writeFile(data.file.path, buffer, { - encoding: 'base64' - }, next); + saveImageDataToTempFile(data.imageData, next); }, - function (next) { - var image = { - name: 'profileAvatar', - path: data.file.path, - uid: data.uid - }; + function (path, next) { + image.path = path; - if (plugins.hasListeners('filter:uploadImage')) { - return plugins.fireHook('filter:uploadImage', { - image: image, - uid: data.uid - }, next); - } - - var extension = file.typeToExtension(data.imageData.slice(5, data.imageData.indexOf('base64') - 1)); - var filename = data.uid + '-profileavatar' + (keepAllVersions ? '-' + Date.now() : '') + (extension || ''); - async.waterfall([ - function (next) { - file.isFileTypeAllowed(data.file.path, next); - }, - function (next) { - file.saveFileToLocal(filename, 'profile', image.path, next); - }, - function (upload, next) { - next(null, { - url: nconf.get('relative_path') + upload.url, - name: image.name - }); - } - ], next); + uploadProfileOrCover('profileavatar', image, data.imageData, next); }, function (uploadData, next) { url = uploadData.url; @@ -312,26 +226,73 @@ module.exports = function (User) { uploadedpicture: url, picture: url }, next); - }, - function (next) { - fs.unlink(data.file.path, function (err) { - if (err) { - winston.error(err); - } - next(); - }); } ], function (err) { - if (err) { - return callback(err); // send back the original error - } - + deleteFile(image.path); callback(err, { url: url }); }); }; + function saveImageDataToTempFile(imageData, callback) { + var filename = crypto.createHash('md5').update(imageData).digest('hex'); + var filepath = path.join(os.tmpdir(), filename); + + var buffer = new Buffer(imageData.slice(imageData.indexOf('base64') + 7), 'base64'); + + fs.writeFile(filepath, buffer, { + encoding: 'base64' + }, function (err) { + callback(err, filepath); + }); + } + + function uploadProfileOrCover(type, image, imageData, callback) { + if (plugins.hasListeners('filter:uploadImage')) { + return plugins.fireHook('filter:uploadImage', { + image: image, + uid: image.uid + }, callback); + } + var filename = generateProfileImageFilename(image.uid, type, imageData); + saveFileToLocal(filename, image, callback); + } + + function generateProfileImageFilename(uid, type, imageData) { + var extension = file.typeToExtension(imageData.slice(5, imageData.indexOf('base64') - 1)); + var keepAllVersions = parseInt(meta.config['profile:keepAllUserImages'], 10) === 1; + var filename = uid + '-' + type + (keepAllVersions ? '-' + Date.now() : '') + (extension || ''); + return filename; + } + + function saveFileToLocal(filename, image, callback) { + async.waterfall([ + function (next) { + file.isFileTypeAllowed(image.path, next); + }, + function (next) { + file.saveFileToLocal(filename, 'profile', image.path, next); + }, + function (upload, next) { + next(null, { + url: nconf.get('relative_path') + upload.url, + name: image.name + }); + } + ], callback); + } + + function deleteFile(path) { + if (path) { + fs.unlink(path, function (err) { + if (err) { + winston.error(err); + } + }); + } + } + User.removeCoverPicture = function (data, callback) { db.deleteObjectFields('user:' + data.uid, ['cover:url', 'cover:position'], callback); }; diff --git a/test/user.js b/test/user.js index 43b5b3f847..6342509ecf 100644 --- a/test/user.js +++ b/test/user.js @@ -448,6 +448,24 @@ describe('User', function () { }); }); + it('should upload cropped profile picture', function (done) { + var imageData = 'data:image/png;base64,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'; + var socketUser = require('../src/socket.io/user'); + socketUser.uploadCroppedPicture({uid: uid}, {uid: uid, imageData: imageData}, function (err, result) { + assert.ifError(err); + console.log(result); + assert(result.url); + db.getObjectFields('user:' + uid, ['uploadedpicture', 'picture'], function (err, data) { + assert.ifError(err); + console.log(data); + //{ url: '/assets/uploads/profile/4-profileavatar.png' } + assert.equal(result.url, data.uploadedpicture); + assert.equal(result.url, data.picture); + done(); + }); + }); + }); + it('should remove cover image', function (done) { io.emit('user.removeCover', {uid: uid}, function (err) { assert.ifError(err); @@ -508,7 +526,7 @@ describe('User', function () { done(); }); }); - + it('should return error if profile image uploads disabled', function (done) { meta.config.allowProfileImageUploads = 0; var path = require('path'); @@ -517,12 +535,12 @@ describe('User', function () { size: 7189, name: 'logo.png' }; - User.uploadPicture(uid, picture, function (err, uploadedPicture) { + User.uploadPicture(uid, picture, function (err) { assert.equal(err.message, '[[error:profile-image-uploads-disabled]]'); done(); }); }); - + it('should return error if profile image is too big', function (done) { meta.config.allowProfileImageUploads = 1; var path = require('path'); @@ -531,12 +549,12 @@ describe('User', function () { size: 265000, name: 'logo.png' }; - User.uploadPicture(uid, picture, function (err, uploadedPicture) { + User.uploadPicture(uid, picture, function (err) { assert.equal(err.message, '[[error:file-too-big, 256]]'); done(); }); }); - + it('should return error if profile image file has no extension', function (done) { var path = require('path'); var picture = { @@ -544,64 +562,64 @@ describe('User', function () { size: 7189, name: 'logo' }; - User.uploadPicture(uid, picture, function (err, uploadedPicture) { + User.uploadPicture(uid, picture, function (err) { assert.equal(err.message, '[[error:invalid-image-extension]]'); done(); }); }); - + it('should return error if no plugins listening for filter:uploadImage when uploading from url', function (done) { var url = nconf.get('url') + '/logo.png'; - User.uploadFromUrl(uid, url, function (err, uploadedPicture) { + User.uploadFromUrl(uid, url, function (err) { assert.equal(err.message, '[[error:no-plugin]]'); done(); }); }); - + it('should return error if the extension is invalid when uploading from url', function (done) { var url = nconf.get('url') + '/favicon.ico'; - + function filterMethod(data, callback) { data.foo += 5; callback(null, data); } plugins.registerHook('test-plugin', {hook: 'filter:uploadImage', method: filterMethod}); - - User.uploadFromUrl(uid, url, function (err, uploadedPicture) { + + User.uploadFromUrl(uid, url, function (err) { assert.equal(err.message, '[[error:invalid-image-extension]]'); done(); }); }); - + it('should return error if the file is too big when uploading from url', function (done) { var url = nconf.get('url') + '/logo.png'; meta.config.maximumProfileImageSize = 1; - + function filterMethod(data, callback) { data.foo += 5; callback(null, data); } plugins.registerHook('test-plugin', {hook: 'filter:uploadImage', method: filterMethod}); - - User.uploadFromUrl(uid, url, function (err, uploadedPicture) { + + User.uploadFromUrl(uid, url, function (err) { assert.equal(err.message, '[[error:file-too-big, ' + meta.config.maximumProfileImageSize + ']]'); done(); }); }); - + it('should upload picture when uploading from url', function (done) { var url = nconf.get('url') + '/logo.png'; meta.config.maximumProfileImageSize = ''; - + function filterMethod(data, callback) { data.foo += 5; callback(null, {url: url}); } plugins.registerHook('test-plugin', {hook: 'filter:uploadImage', method: filterMethod}); - + User.uploadFromUrl(uid, url, function (err, uploadedPicture) { assert.ifError(err); assert.equal(uploadedPicture.url, url); From 7842c3411cde41e0ab548fe296b45517d2f33146 Mon Sep 17 00:00:00 2001 From: barisusakli Date: Sat, 18 Feb 2017 20:52:45 +0300 Subject: [PATCH 6/7] remove logs --- test/user.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/test/user.js b/test/user.js index 6342509ecf..62a4a0401f 100644 --- a/test/user.js +++ b/test/user.js @@ -453,12 +453,9 @@ describe('User', function () { var socketUser = require('../src/socket.io/user'); socketUser.uploadCroppedPicture({uid: uid}, {uid: uid, imageData: imageData}, function (err, result) { assert.ifError(err); - console.log(result); assert(result.url); db.getObjectFields('user:' + uid, ['uploadedpicture', 'picture'], function (err, data) { assert.ifError(err); - console.log(data); - //{ url: '/assets/uploads/profile/4-profileavatar.png' } assert.equal(result.url, data.uploadedpicture); assert.equal(result.url, data.picture); done(); From 1b43faba1ab0680b6bc753dd596d0c2bf8c65e44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Sun, 19 Feb 2017 02:44:38 +0300 Subject: [PATCH 7/7] closes #5441 --- src/start.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/start.js b/src/start.js index 0a8ed18e67..06ffc93589 100644 --- a/src/start.js +++ b/src/start.js @@ -99,6 +99,7 @@ function setupConfigs() { nconf.set('use_port', !!urlObject.port); nconf.set('relative_path', relativePath); nconf.set('port', urlObject.port || nconf.get('port') || nconf.get('PORT') || (nconf.get('PORT_ENV_VAR') ? nconf.get(nconf.get('PORT_ENV_VAR')) : false) || 4567); + nconf.set('upload_url', '/assets/uploads'); } function printStartupInfo() {