Skip to content
Open
2 changes: 1 addition & 1 deletion app.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ module.exports = {
app.use(favicon(path.join(__dirname, 'public/img/Softwerkskammer16x16.ico')));
app.use(morgan('combined', {stream: winstonStream}));
app.use(cookieParser());
app.use(bodyparser.urlencoded({extended: true}));
app.use(bodyparser.urlencoded({extended: true, limit: '50mb'}));
app.use(compress());
app.use(serveStatic(path.join(__dirname, 'public'), { maxAge: 600 * 1000 })); // ten minutes

Expand Down
74 changes: 46 additions & 28 deletions lib/activityresults/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
'use strict';
var beans = require('nconf').get('beans');
var bodyParser = require("body-parser");
var ActivityResult = beans.get('activityresult');
var activityresultsPersistence = beans.get('activityresultsPersistence');
var activityresultsService = beans.get('activityresultsService');
Expand Down Expand Up @@ -58,41 +59,58 @@ app.post('/', function (req, res) {
});
});

app.post("/:activityResultName/upload", function (req, res) {
new Form().parse(req, function (err, fields, files) {
if (!files) {
return res.send(400);
var processUploadRequestWithImageUri = function (imageUri, req, res) {
galleryRepository.getMetadataForImage(imageUri, function (err, metadata) {
var date;
if (metadata && metadata.exif) {
date = metadata.exif.dateTime ||
metadata.exif.dateTimeOriginal ||
metadata.exif.dateTimeDigitized ||
new Date();
} else {
date = new Date();
}
var image = files.image[0];
galleryRepository.storeImage(image.path, function (err, imageUri) {

var newPhoto = {
id: uuid.v4(),
uri: galleryApp.path() + imageUri,
timestamp: date,
uploaded_by: req.user.member.state.id
};

activityresultsService.addPhotoToActivityResult(req.params.activityResultName, newPhoto, function (err) {
if (req.header('X-Requested-With') === 'XMLHttpRequest') {
return res.send(200, app.path() + req.params.activityResultName + '/photo/' + newPhoto.id + '/edit');
}
res.location(app.path() + req.params.activityResultName + '/photo/' + newPhoto.id + '/edit');
res.send(303);
});
});
};

app.post("/:activityResultName/upload", bodyParser({limit: '50mb'}), function (req, res) {
if (req.body.photo) {
// DataURL
galleryRepository.storeImageFromDataURL(req.body.photo, function (err, imageUri) {
if (err) {
throw err;
}
galleryRepository.getMetadataForImage(imageUri, function (err, metadata) {
var date;
if (metadata && metadata.exif) {
date = metadata.exif.dateTime ||
metadata.exif.dateTimeOriginal ||
metadata.exif.dateTimeDigitized ||
new Date();
} else {
date = new Date();
processUploadRequestWithImageUri(imageUri, req, res);
});
} else {
new Form().parse(req, function (err, fields, files) {
if (!files) {
return res.send(400);
}
var image = files.image[0];
galleryRepository.storeImage(image.path, function (err, imageUri) {
if (err) {
throw err;
}

var newPhoto = {
id: uuid.v4(),
uri: galleryApp.path() + imageUri,
timestamp: date,
uploaded_by: req.user.member.state.id
};

activityresultsService.addPhotoToActivityResult(req.params.activityResultName, newPhoto, function (err) {
res.location(app.path() + req.params.activityResultName + '/photo/' + newPhoto.id + '/edit');
res.send(303);
});
processUploadRequestWithImageUri(imageUri, req, res);
});
});
});
}
});

app.get("/:activityResultName/photo/:photoId/edit", function (req, res) {
Expand Down
68 changes: 67 additions & 1 deletion lib/activityresults/views/get.jade
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,11 @@ block content
div
button(type="submit").btn.btn-success.pull-right= t('activityresults.submit')
span#btn-cancel.btn.btn-warning= t('activityresults.cancel')
input#resizeOnClient(type='checkbox',name='resizeOnClient',checked='checked')
label(for='resizeOnClient')= t('activityresults.resize_on_client')


script.
script.
function getPreview(files, callback) {
if(!files || !files[0])
return null;
Expand Down Expand Up @@ -96,6 +98,70 @@ block content
span.small(style="color: black")
+thumbnailInfos(img)

script(type='text/javascript').
function resizeImageFromFile(file, MAX_WIDTH, MAX_HEIGHT, callback) {
// Load via FileReader
var fr = new FileReader();
fr.onload = function(e) {
getDimensions(e.target.result, function(width, height, img) {
var newWidth = width;
var newHeight = height;
var aspectRatio = width/height;
// Image is wider than tall
if(width > MAX_WIDTH && aspectRatio >= 1) {
newWidth = MAX_WIDTH;
newHeight = MAX_WIDTH / aspectRatio
} else if(height > MAX_HEIGHT && aspectRatio <= 1) {
newWidth = MAX_HEIGHT * aspectRatio;
newHeight = MAX_HEIGHT;
}
resizeImage(img, newWidth, newHeight, function (canvas) {
callback(canvas.toDataURL('image/jpeg', 0.8));
})
});
}
fr.readAsDataURL(file);
function resizeImage(imgElement, width, height, callback) {
var cvs = document.createElement("canvas");
cvs.width = width;
cvs.height = height;
var ctx = cvs.getContext('2d');
ctx.drawImage(imgElement, 0, 0, width, height);
callback(cvs);
}
function getDimensions(imgData, callback) {
var tmpImg = document.createElement("img");
tmpImg.onload = function () {
callback(tmpImg.width, tmpImg.height, tmpImg);
}
tmpImg.src = imgData;
}
}

$(function() {
var $recordForm = $('#recordForm');
$recordForm.on('submit', function (e) {
if (!$('#resizeOnClient').is(':checked')) {
console.log('Not resizing image on client');
return;
}
var files = $('#input-file')[0].files;
if(!files || !files[0]) {
return;
}
resizeImageFromFile(files[0], 2560, 2560, function (dataURL) {
$.post($recordForm.attr('action'), {
photo: dataURL
}).success(function (data) {
window.location.href = data;
}).error(function (error) {
alert(error);
});
})
e.preventDefault();
});
});

script(type='text/javascript').
var ESC_KEY = 27;
jQuery(function() {
Expand Down
24 changes: 22 additions & 2 deletions lib/gallery/galleryrepositoryService.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ var logger = require('winston').loggers.get('gallery');
var magick = require('imagemagick');
var uuid = require('node-uuid');
var path = require('path');
var tmp = require('tmp');
var dataurl = require('dataurl');

function autoOrient(sourceImagePath, targetPath, callback) {
if (logger.debug) {
Expand All @@ -25,7 +27,7 @@ function scale(sourceImagePath, targetPath, width, height, fn) {
if (logger.debug) {
logger.debug('Scaling `' + sourceImagePath + '\' to ' + width + 'x' + height + ' into `' + targetPath + '\'');
}
magick.convert([sourceImagePath, '-scale', width + '!x' + height + '!', targetPath], function (err, stdout) {
magick.convert([sourceImagePath, '-quality', '75', '-scale', width + '!x' + height + '!', targetPath], function (err, stdout) {
if (err) {
return fn(err, undefined);
}
Expand All @@ -40,7 +42,7 @@ function scaleWidth(sourceImagePath, targetPath, width, fn) {
if (logger.debug) {
logger.debug('Scaling `' + sourceImagePath + '\' to ' + width + 'x undefined into `' + targetPath + '\'');
}
magick.convert([sourceImagePath, '-scale', width, targetPath], function (err, stdout) {
magick.convert([sourceImagePath, '-quality', '75', '-scale', width, targetPath], function (err, stdout) {
if (err) {
return fn(err, undefined);
}
Expand Down Expand Up @@ -75,6 +77,24 @@ module.exports = {
});
},

storeImageFromDataURL: function storeImageFromDataURL(dataURL, callback) {
var _this = this;

var dataPackage = dataurl.parse(dataURL);
if (!dataPackage || !dataPackage.mimetype.match(/image\/(jpg|jpeg)/)) {
return callback(new Error('Not a valid dataURL'));
}
// Write to tmpFile
tmp.file({postfix: '.jpg'}, function (err, path, fd) {
_this.fs().writeFile(path, dataPackage.data, {encoding: dataPackage.encoding}, function (err) {
if (err) {
return callback(err);
}
_this.storeImage(path, callback);
});
});
},

getMetadataForImage: function getMetadataForImage(id, callback) {
var persistentImageFilePath = this.directory() + '/' + id;
magick.readMetadata(persistentImageFilePath, callback);
Expand Down
3 changes: 2 additions & 1 deletion locales/translation-de.json
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,8 @@
"done": "Fertig",
"record_image": "Aufzeichnen",
"submit": "Weiter",
"photoTitlePlaceholder": "Was kann man hier sehen?"
"photoTitlePlaceholder": "Was kann man hier sehen?",
"resize_on_client": "Bild auf Gerät skalieren"
},
"payment": {
"title": "Zahlung",
Expand Down
3 changes: 2 additions & 1 deletion locales/translation-en.json
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,8 @@
"done": "Done",
"photoTitlePlaceholder": "What's on this photo?",
"record_image": "Record",
"submit": "SUBMIT"
"submit": "SUBMIT",
"resize_on_client": "Resize image on client"
},
"payment": {
"title": "Payment",
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"cookie-parser": "1.3.2",
"CoolBeans": "0.0.9",
"csurf": "1.4.0",
"dataurl": "^0.1.0",
"express": "4.7.2",
"express-session": "1.7.2",
"i18next": "1.7.4",
Expand Down Expand Up @@ -64,6 +65,7 @@
"soap-sympa": "0.0.1",
"static-favicon": "1.0.2",
"stripe": "2.8.0",
"tmp": "0.0.24",
"underscore.string": "2.3.3",
"URIjs": "1.13.2",
"useragent": "2.0.9",
Expand Down
17 changes: 17 additions & 0 deletions test/activityresults/activityresults_integration_upload_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,21 @@ describe('/activityresults/:result/upload', function () {
.end(done);
});
});

describe('POST / with dataURL', function () {
it("should store the image via gallery service and redirect to edit", function (done) {
var dataURL = 'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/2wBDAQMDAwQDBAgEBAgQCwkLEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBD/wAARCADIAMgDASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD8qqKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAP/2Q==';
sinon.stub(galleryRepository, 'storeImage', function (tmpFile, callback) {
callback(null, "my-custom-image-id");
});

//noinspection JSLint
request(createApp(1))
.post('/foo/upload')
.send({photo: dataURL})
.expect(303)
.expect('Location', /\/foo\/photo\/[\w+|\-]+\/edit/)
.end(done);
});
});
});
12 changes: 12 additions & 0 deletions test/gallery/galleryrepository_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,18 @@ describe("the gallery repository on real files", function () {
});
});

it('stores a dataurl image', function (done) {
var dataURL = 'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/2wBDAQMDAwQDBAgEBAgQCwkLEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBD/wAARCADIAMgDASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD8qqKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAP/2Q==';
service.storeImageFromDataURL(dataURL, function (err, imageId) {
service.retrieveImage(imageId, function (err) {
if (err) {
done(err);
}
done();
});
});
});

it('provides exif data for a given image', function (done) {
var storedImageId = 'exif_image.jpg';
var imagePath = __dirname + '/fixtures/' + storedImageId;
Expand Down