mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-11-06 22:15:48 +01:00
closes #725
This commit is contained in:
@@ -33,7 +33,9 @@
|
|||||||
"heapdump": "^0.3.0",
|
"heapdump": "^0.3.0",
|
||||||
"less": "^2.0.0",
|
"less": "^2.0.0",
|
||||||
"logrotate-stream": "^0.2.3",
|
"logrotate-stream": "^0.2.3",
|
||||||
|
"mime": "^1.3.4",
|
||||||
"mkdirp": "~0.5.0",
|
"mkdirp": "~0.5.0",
|
||||||
|
"mmmagic": "^0.3.13",
|
||||||
"morgan": "^1.3.2",
|
"morgan": "^1.3.2",
|
||||||
"nconf": "~0.7.1",
|
"nconf": "~0.7.1",
|
||||||
"nodebb-plugin-dbsearch": "^0.1.0",
|
"nodebb-plugin-dbsearch": "^0.1.0",
|
||||||
|
|||||||
@@ -64,6 +64,7 @@
|
|||||||
|
|
||||||
"invalid-image-type": "Invalid image type. Allowed types are: %1",
|
"invalid-image-type": "Invalid image type. Allowed types are: %1",
|
||||||
"invalid-image-extension": "Invalid image extension",
|
"invalid-image-extension": "Invalid image extension",
|
||||||
|
"invalid-file-type": "Invalid file type. Allowed types are: %1",
|
||||||
|
|
||||||
"group-name-too-short": "Group name too short",
|
"group-name-too-short": "Group name too short",
|
||||||
"group-already-exists": "Group already exists",
|
"group-already-exists": "Group already exists",
|
||||||
@@ -80,7 +81,6 @@
|
|||||||
"topic-thumbnails-are-disabled": "Topic thumbnails are disabled.",
|
"topic-thumbnails-are-disabled": "Topic thumbnails are disabled.",
|
||||||
"invalid-file": "Invalid File",
|
"invalid-file": "Invalid File",
|
||||||
"uploads-are-disabled": "Uploads are disabled",
|
"uploads-are-disabled": "Uploads are disabled",
|
||||||
"upload-error": "Upload Error : %1",
|
|
||||||
|
|
||||||
"signature-too-long" : "Sorry, your signature cannot be longer than %1 characters.",
|
"signature-too-long" : "Sorry, your signature cannot be longer than %1 characters.",
|
||||||
|
|
||||||
|
|||||||
@@ -316,8 +316,7 @@ define('composer/uploads', ['composer/preview', 'csrf'], function(preview, csrf)
|
|||||||
|
|
||||||
function onUploadError(xhr) {
|
function onUploadError(xhr) {
|
||||||
xhr = maybeParse(xhr);
|
xhr = maybeParse(xhr);
|
||||||
|
app.alertError(xhr.responseText);
|
||||||
app.alertError('[[error:upload-error, ' + xhr.responseText + ']]');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return uploads;
|
return uploads;
|
||||||
|
|||||||
@@ -130,9 +130,7 @@
|
|||||||
return ('' + path).split('.').pop();
|
return ('' + path).split('.').pop();
|
||||||
},
|
},
|
||||||
|
|
||||||
fileMimeType: (function () {
|
extensionMimeTypeMap: {
|
||||||
// we only care about images, for now
|
|
||||||
var map = {
|
|
||||||
"bmp": "image/bmp",
|
"bmp": "image/bmp",
|
||||||
"cmx": "image/x-cmx",
|
"cmx": "image/x-cmx",
|
||||||
"cod": "image/cis-cod",
|
"cod": "image/cis-cod",
|
||||||
@@ -143,6 +141,7 @@
|
|||||||
"jpe": "image/jpeg",
|
"jpe": "image/jpeg",
|
||||||
"jpeg": "image/jpeg",
|
"jpeg": "image/jpeg",
|
||||||
"jpg": "image/jpeg",
|
"jpg": "image/jpeg",
|
||||||
|
"png": "image/png",
|
||||||
"pbm": "image/x-portable-bitmap",
|
"pbm": "image/x-portable-bitmap",
|
||||||
"pgm": "image/x-portable-graymap",
|
"pgm": "image/x-portable-graymap",
|
||||||
"pnm": "image/x-portable-anymap",
|
"pnm": "image/x-portable-anymap",
|
||||||
@@ -155,13 +154,15 @@
|
|||||||
"xbm": "image/x-xbitmap",
|
"xbm": "image/x-xbitmap",
|
||||||
"xpm": "image/x-xpixmap",
|
"xpm": "image/x-xpixmap",
|
||||||
"xwd": "image/x-xwindowdump"
|
"xwd": "image/x-xwindowdump"
|
||||||
};
|
},
|
||||||
|
|
||||||
return function (path) {
|
fileMimeType: function (path) {
|
||||||
var extension = utils.fileExtension(path);
|
utils.extensionToMimeType(utils.fileExtension(path));
|
||||||
return map[extension] || '*';
|
},
|
||||||
};
|
|
||||||
})(),
|
extensionToMimeType: function(extension) {
|
||||||
|
return utils.extensionMimeTypeMap[extension] || '*';
|
||||||
|
},
|
||||||
|
|
||||||
isRelativeUrl: function(url) {
|
isRelativeUrl: function(url) {
|
||||||
var firstChar = url.slice(0, 1);
|
var firstChar = url.slice(0, 1);
|
||||||
|
|||||||
@@ -376,33 +376,26 @@ accountsController.accountSettings = function(req, res, next) {
|
|||||||
accountsController.uploadPicture = function (req, res, next) {
|
accountsController.uploadPicture = function (req, res, next) {
|
||||||
var userPhoto = req.files.files[0];
|
var userPhoto = req.files.files[0];
|
||||||
var uploadSize = parseInt(meta.config.maximumProfileImageSize, 10) || 256;
|
var uploadSize = parseInt(meta.config.maximumProfileImageSize, 10) || 256;
|
||||||
|
|
||||||
if (userPhoto.size > uploadSize * 1024) {
|
|
||||||
fs.unlink(userPhoto.path);
|
|
||||||
return next(new Error('[[error:file-too-big, ' + uploadSize + ']]'));
|
|
||||||
}
|
|
||||||
|
|
||||||
var allowedTypes = ['image/png', 'image/jpeg', 'image/jpg', 'image/gif'];
|
|
||||||
if (allowedTypes.indexOf(userPhoto.type) === -1) {
|
|
||||||
fs.unlink(userPhoto.path);
|
|
||||||
return next(new Error('[[error:invalid-image-type, ' + allowedTypes.join(', ') + ']]'));
|
|
||||||
}
|
|
||||||
|
|
||||||
var extension = path.extname(userPhoto.name);
|
var extension = path.extname(userPhoto.name);
|
||||||
if (!extension) {
|
|
||||||
fs.unlink(userPhoto.path);
|
|
||||||
return next(new Error('[[error:invalid-image-extension]]'));
|
|
||||||
}
|
|
||||||
|
|
||||||
var updateUid = req.user ? req.user.uid : 0;
|
var updateUid = req.user ? req.user.uid : 0;
|
||||||
var imageDimension = parseInt(meta.config.profileImageDimension, 10) || 128;
|
var imageDimension = parseInt(meta.config.profileImageDimension, 10) || 128;
|
||||||
|
var convertToPNG = parseInt(meta.config['profile:convertProfileImageToPNG'], 10) === 1;
|
||||||
|
|
||||||
async.waterfall([
|
async.waterfall([
|
||||||
|
function(next) {
|
||||||
|
next(userPhoto.size > uploadSize * 1024 ? new Error('[[error:file-too-big, ' + uploadSize + ']]') : null);
|
||||||
|
},
|
||||||
|
function(next) {
|
||||||
|
next(!extension ? new Error('[[error:invalid-image-extension]]') : null);
|
||||||
|
},
|
||||||
|
function(next) {
|
||||||
|
file.isFileTypeAllowed(userPhoto.path, ['png', 'jpeg', 'jpg', 'gif'], next);
|
||||||
|
},
|
||||||
function(next) {
|
function(next) {
|
||||||
image.resizeImage(userPhoto.path, extension, imageDimension, imageDimension, next);
|
image.resizeImage(userPhoto.path, extension, imageDimension, imageDimension, next);
|
||||||
},
|
},
|
||||||
function(next) {
|
function(next) {
|
||||||
if (parseInt(meta.config['profile:convertProfileImageToPNG'], 10) === 1) {
|
if (convertToPNG) {
|
||||||
image.convertImageToPng(userPhoto.path, extension, next);
|
image.convertImageToPng(userPhoto.path, extension, next);
|
||||||
} else {
|
} else {
|
||||||
next();
|
next();
|
||||||
@@ -412,7 +405,7 @@ accountsController.uploadPicture = function (req, res, next) {
|
|||||||
user.getUidByUserslug(req.params.userslug, next);
|
user.getUidByUserslug(req.params.userslug, next);
|
||||||
},
|
},
|
||||||
function(uid, next) {
|
function(uid, next) {
|
||||||
if(parseInt(updateUid, 10) === parseInt(uid, 10)) {
|
if (parseInt(updateUid, 10) === parseInt(uid, 10)) {
|
||||||
return next();
|
return next();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -450,7 +443,6 @@ accountsController.uploadPicture = function (req, res, next) {
|
|||||||
return plugins.fireHook('filter:uploadImage', {image: userPhoto, uid: updateUid}, done);
|
return plugins.fireHook('filter:uploadImage', {image: userPhoto, uid: updateUid}, done);
|
||||||
}
|
}
|
||||||
|
|
||||||
var convertToPNG = parseInt(meta.config['profile:convertProfileImageToPNG'], 10) === 1;
|
|
||||||
var filename = updateUid + '-profileimg' + (convertToPNG ? '.png' : extension);
|
var filename = updateUid + '-profileimg' + (convertToPNG ? '.png' : extension);
|
||||||
|
|
||||||
user.getUserField(updateUid, 'uploadedpicture', function (err, oldpicture) {
|
user.getUserField(updateUid, 'uploadedpicture', function (err, oldpicture) {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ var uploadsController = {},
|
|||||||
async = require('async'),
|
async = require('async'),
|
||||||
|
|
||||||
meta = require('../meta'),
|
meta = require('../meta'),
|
||||||
|
file = require('../file'),
|
||||||
plugins = require('../plugins'),
|
plugins = require('../plugins'),
|
||||||
utils = require('../../public/src/utils'),
|
utils = require('../../public/src/utils'),
|
||||||
image = require('../image');
|
image = require('../image');
|
||||||
@@ -42,12 +43,18 @@ uploadsController.upload = function(req, res, filesIterator, next) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
uploadsController.uploadPost = function(req, res, next) {
|
uploadsController.uploadPost = function(req, res, next) {
|
||||||
uploadsController.upload(req, res, function(file, next) {
|
uploadsController.upload(req, res, function(uploadedFile, next) {
|
||||||
if (file.type.match(/image./)) {
|
file.isFileTypeAllowed(uploadedFile.path, file.allowedExtensions(), function(err) {
|
||||||
uploadImage(req.user.uid, file, next);
|
if (err) {
|
||||||
} else {
|
return next(err);
|
||||||
uploadFile(req.user.uid, file, next);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (uploadedFile.type.match(/image./)) {
|
||||||
|
uploadImage(req.user.uid, uploadedFile, next);
|
||||||
|
} else {
|
||||||
|
uploadFile(req.user.uid, uploadedFile, next);
|
||||||
|
}
|
||||||
|
});
|
||||||
}, next);
|
}, next);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -57,18 +64,24 @@ uploadsController.uploadThumb = function(req, res, next) {
|
|||||||
return next(new Error('[[error:topic-thumbnails-are-disabled]]'));
|
return next(new Error('[[error:topic-thumbnails-are-disabled]]'));
|
||||||
}
|
}
|
||||||
|
|
||||||
uploadsController.upload(req, res, function(file, next) {
|
uploadsController.upload(req, res, function(uploadedFile, next) {
|
||||||
if(file.type.match(/image./)) {
|
file.isFileTypeAllowed(uploadedFile.path, file.allowedExtensions(), function(err) {
|
||||||
var size = meta.config.topicThumbSize || 120;
|
|
||||||
image.resizeImage(file.path, path.extname(file.name), size, size, function(err) {
|
|
||||||
if (err) {
|
if (err) {
|
||||||
return next(err);
|
return next(err);
|
||||||
}
|
}
|
||||||
uploadImage(req.user.uid, file, next);
|
|
||||||
|
if (uploadedFile.type.match(/image./)) {
|
||||||
|
var size = meta.config.topicThumbSize || 120;
|
||||||
|
image.resizeImage(uploadedFile.path, path.extname(uploadedFile.name), size, size, function(err) {
|
||||||
|
if (err) {
|
||||||
|
return next(err);
|
||||||
|
}
|
||||||
|
uploadImage(req.user.uid, uploadedFile, next);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
next(new Error('[[error:invalid-file]]'));
|
next(new Error('[[error:invalid-file]]'));
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}, next);
|
}, next);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -88,32 +101,32 @@ function uploadImage(uid, image, callback) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function uploadFile(uid, file, callback) {
|
function uploadFile(uid, uploadedFile, callback) {
|
||||||
if (plugins.hasListeners('filter:uploadFile')) {
|
if (plugins.hasListeners('filter:uploadFile')) {
|
||||||
return plugins.fireHook('filter:uploadFile', {file: file, uid: uid}, callback);
|
return plugins.fireHook('filter:uploadFile', {file: uploadedFile, uid: uid}, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (parseInt(meta.config.allowFileUploads, 10) !== 1) {
|
if (parseInt(meta.config.allowFileUploads, 10) !== 1) {
|
||||||
return callback(new Error('[[error:uploads-are-disabled]]'));
|
return callback(new Error('[[error:uploads-are-disabled]]'));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!file) {
|
if (!uploadedFile) {
|
||||||
return callback(new Error('[[error:invalid-file]]'));
|
return callback(new Error('[[error:invalid-file]]'));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (file.size > parseInt(meta.config.maximumFileSize, 10) * 1024) {
|
if (uploadedFile.size > parseInt(meta.config.maximumFileSize, 10) * 1024) {
|
||||||
return callback(new Error('[[error:file-too-big, ' + meta.config.maximumFileSize + ']]'));
|
return callback(new Error('[[error:file-too-big, ' + meta.config.maximumFileSize + ']]'));
|
||||||
}
|
}
|
||||||
|
|
||||||
var filename = 'upload-' + utils.generateUUID() + path.extname(file.name);
|
var filename = 'upload-' + utils.generateUUID() + path.extname(uploadedFile.name);
|
||||||
require('../file').saveFileToLocal(filename, 'files', file.path, function(err, upload) {
|
file.saveFileToLocal(filename, 'files', uploadedFile.path, function(err, upload) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return callback(err);
|
return callback(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
callback(null, {
|
callback(null, {
|
||||||
url: upload.url,
|
url: upload.url,
|
||||||
name: file.name
|
name: uploadedFile.name
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
43
src/file.js
43
src/file.js
@@ -3,7 +3,12 @@
|
|||||||
var fs = require('fs'),
|
var fs = require('fs'),
|
||||||
nconf = require('nconf'),
|
nconf = require('nconf'),
|
||||||
path = require('path'),
|
path = require('path'),
|
||||||
winston = require('winston');
|
winston = require('winston'),
|
||||||
|
mmmagic = require('mmmagic'),
|
||||||
|
Magic = mmmagic.Magic,
|
||||||
|
mime = require('mime'),
|
||||||
|
|
||||||
|
meta= require('./meta');
|
||||||
|
|
||||||
var file = {};
|
var file = {};
|
||||||
|
|
||||||
@@ -11,7 +16,7 @@ file.saveFileToLocal = function(filename, folder, tempPath, callback) {
|
|||||||
|
|
||||||
var uploadPath = path.join(nconf.get('base_dir'), nconf.get('upload_path'), folder, filename);
|
var uploadPath = path.join(nconf.get('base_dir'), nconf.get('upload_path'), folder, filename);
|
||||||
|
|
||||||
winston.info('Saving file '+ filename +' to : ' + uploadPath);
|
winston.verbose('Saving file '+ filename +' to : ' + uploadPath);
|
||||||
|
|
||||||
var is = fs.createReadStream(tempPath);
|
var is = fs.createReadStream(tempPath);
|
||||||
var os = fs.createWriteStream(uploadPath);
|
var os = fs.createWriteStream(uploadPath);
|
||||||
@@ -30,4 +35,38 @@ file.saveFileToLocal = function(filename, folder, tempPath, callback) {
|
|||||||
is.pipe(os);
|
is.pipe(os);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
file.isFileTypeAllowed = function(path, allowedExtensions, callback) {
|
||||||
|
if (!Array.isArray(allowedExtensions) || !allowedExtensions.length) {
|
||||||
|
return callback();
|
||||||
|
}
|
||||||
|
|
||||||
|
allowedExtensions = allowedExtensions.filter(Boolean).map(function(extension) {
|
||||||
|
return extension.trim();
|
||||||
|
});
|
||||||
|
|
||||||
|
var magic = new Magic(mmmagic.MAGIC_MIME_TYPE);
|
||||||
|
magic.detectFile(path, function(err, mimeType) {
|
||||||
|
if (err) {
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
var uploadedFileExtension = mime.extension(mimeType);
|
||||||
|
|
||||||
|
if (allowedExtensions.indexOf(uploadedFileExtension) === -1) {
|
||||||
|
return callback(new Error('[[error:invalid-file-type, ' + allowedExtensions.join('-') + ']]'));
|
||||||
|
}
|
||||||
|
|
||||||
|
callback();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
file.allowedExtensions = function() {
|
||||||
|
var allowedExtensions = (meta.config.allowedFileExtensions || '').trim();
|
||||||
|
if (!allowedExtensions) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
allowedExtensions = allowedExtensions.split(',');
|
||||||
|
return allowedExtensions;
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = file;
|
module.exports = file;
|
||||||
@@ -119,7 +119,10 @@
|
|||||||
<input type="checkbox" data-field="allowTopicsThumbnail"> <strong>Allow users to upload topic thumbnails</strong>
|
<input type="checkbox" data-field="allowTopicsThumbnail"> <strong>Allow users to upload topic thumbnails</strong>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<strong>Topic Thumb Size</strong><br /> <input type="text" class="form-control" value="120" data-field="topicThumbSize">
|
<strong>Topic Thumb Size</strong><br /> <input type="text" class="form-control" value="120" data-field="topicThumbSize"> <br />
|
||||||
|
|
||||||
|
<strong>Allowed file types, (ie png, pdf, zip). Leave empty to allow all.</strong><br /> <input type="text" class="form-control" value="" data-field="allowedFileExtensions"><br />
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user