Skip to content

Commit

Permalink
Merge branch 'master' into greenkeeper-mock-express-response-0.2.0
Browse files Browse the repository at this point in the history
  • Loading branch information
scottnath authored Jul 12, 2017
2 parents 475a9e8 + 7cb3a4d commit bcb29f3
Show file tree
Hide file tree
Showing 28 changed files with 559 additions and 246 deletions.
4 changes: 4 additions & 0 deletions config/default.js
Original file line number Diff line number Diff line change
Expand Up @@ -153,5 +153,9 @@ module.exports = {
dest: 'public/files',
settings: {},
public: '/files', // Can include {{dest}} for the dest path
temp: {
dest: 'public/tmp/',
public: '/tmp',
},
},
};
6 changes: 6 additions & 0 deletions config/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,10 @@ module.exports = {
workflows: {
directory: path.join(__dirname, '../tests/fixtures/workflows/good'),
},
storage: {
dest: 'tests/public/files',
temp: {
dest: 'tests/public/tmp/',
},
},
};
28 changes: 28 additions & 0 deletions content-types/bar.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,34 @@ attributes:
inputs:
text:
placeholder: add svc name
- type: email
id: service-email
name: Service Email
inputs:
text:
placeholder: add svc email
- type: file
id: image-file
name: Image Filer
inputs:
file:
settings:
types:
- .png
- .jpg
- .gif
- type: file
id: text-file
name: Text Filer
- type: file
id: bar-file-repeat
name: Bar File Repeater
repeatable: true
- type: text
id: bar-repeat-test
name: Bar Repeat Test
description: Checkin the repeatin
repeatable: true
- type: reference
id: bar-reference
name: Bar reference
Expand Down
162 changes: 152 additions & 10 deletions lib/content/utils.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
'use strict';

const config = require('config');
const _ = require('lodash');

const storage = require('../storage');

/**
Expand Down Expand Up @@ -28,12 +31,25 @@ const fileinputs = attributes => {
return Object.keys(inputs).filter(input => {
return inputs[input].type === 'file'; // only want file inputs
}).map(input => {
const parts = inputs[input].name.split('--');
const parts = _.split(inputs[input].name, '--', 2);
const checkbox = Object.keys(inputs).filter(ipt => {
return inputs[ipt].type === 'checkbox'; // only want file input's checkbox
});
const repeatable = (attr.hasOwnProperty('repeatable') && typeof attr.repeatable === 'object');

return {
'attr': attr.id,
'input': parts[parts.length - 1],
'type': inputs[input].type,
attr: attr.id,
input: parts[parts.length - 1],
type: inputs[input].type,
repeatable,
file: {
name: `${attr.id}--${input}`,
input,
},
checkbox: {
name: `${attr.id}--${checkbox}`,
input: checkbox,
},
};
});
}).filter(filer => {
Expand Down Expand Up @@ -65,21 +81,21 @@ const filepaths = (files, data) => {
if (Array.isArray(files) && files.length > 0) {
files.forEach(filer => {
// if repeatable
if (Array.isArray(content[filer.attr][filer.input])) {
content[filer.attr][filer.input] = content[filer.attr][filer.input].map(inpt => {
if (Array.isArray(content[filer.attr])) {
content[filer.attr] = content[filer.attr].map(inpt => {
const input = inpt;

if (input.value.hasOwnProperty('original') && input.value.original !== '') {
input.value = storage.get(input.value);
if (input[filer.file.input] && input[filer.file.input].hasOwnProperty('value') && input[filer.file.input].value.hasOwnProperty('original') && input[filer.file.input].value.original !== '') {
input[filer.file.input].value = storage.get(input[filer.file.input].value);
}

return input;
});
}
else {
// non-repeatables
if (content[filer.attr] && content[filer.attr][filer.input].value.hasOwnProperty('original') && content[filer.attr][filer.input].value.original !== '') {
content[filer.attr][filer.input].value = storage.get(content[filer.attr][filer.input].value);
if (content[filer.attr] && content[filer.attr][filer.file.input] && content[filer.attr][filer.file.input].value.hasOwnProperty('original') && content[filer.attr][filer.file.input].value.original !== '') {
content[filer.attr][filer.file.input].value = storage.get(content[filer.attr][filer.file.input].value);
}
}
});
Expand All @@ -88,7 +104,133 @@ const filepaths = (files, data) => {
return content;
};

/**
* Captue files after validation error and add to value
*
* @param {array} files - An array of file objects as received from a multi-part form
* @param {object} val - captured non-file form data
* @param {object} attributes - content type attributes
*
* @returns {object} content values with files added in
*/
const filehold = (files, val, attributes) => {
const values = val;

if (!files || !Array.isArray(files) || !values || typeof values !== 'object' || !attributes || typeof attributes !== 'object') {
return values;
}

// get all file inputs from the content type attributes
const inputs = fileinputs(attributes);

// Add file paths to data
if (Array.isArray(inputs) && inputs.length > 0 && Array.isArray(files) && files.length > 0) {
inputs.forEach(filer => {
const filedata = files.find(file => {
return (file.originalname !== '' && file.fieldname === `${filer.attr}--${filer.input}`);
});

if (filedata) {
values[filer.attr] = {
[filer.input]: {
value: {
type: filedata.mimetype,
original: filedata.originalname,
path: `${config.storage.temp.public}/${filedata.originalname}`,
},
},
};
}
else {
const repeaters = files.filter(file => {
return (file.originalname !== '' && file.fieldname.startsWith(`${filer.attr}--${filer.input}--`));
});

if (Array.isArray(repeaters) && repeaters.length > 0) {
if (!values[filer.attr]) {
values[filer.attr] = [];
}

repeaters.forEach(repeater => {
values[filer.attr].push({
[filer.input]: {
value: {
type: repeater.mimetype,
original: repeater.originalname,
path: `${config.storage.temp.public}/${repeater.originalname}`,
},
},
});
});
}
}
});
}

return values;
};


/**
* Compare files from form against existing data
*
* @param {array} files - An array of file objects as received from a multi-part form
* @param {object} inserted - formatted data object with files, ready for storage
* @param {object} val - existing data
* @param {object} attributes - content type attributes
*
* @returns {object} content values with files added in
*/
const filecompare = (files, inserted, val, attributes) => {
const values = val;
const data = inserted;

if (!files || !Array.isArray(files) || !data || typeof data !== 'object' || !values || typeof values !== 'object' || !attributes || typeof attributes !== 'object') {
return data;
}

// get all file inputs from the content type attributes
const finputs = fileinputs(attributes);

// Add file paths to data
if (Array.isArray(finputs) && finputs.length > 0) {
finputs.forEach(finput => {
const filedata = files.find(file => {
return (file.originalname !== '' && file.fieldname === `${finput.attr}--${finput.input}`);
});

const checkbox = finput.checkbox;
const input = finput.file;

if (finput.repeatable) {
// do repeatable thingies
}
else {
// nothing new/deleted - grab file content from previous if it exists
if (!_.get(data, `${finput.attr}.${checkbox.input}`, false) && !filedata && _.get(val, `value.${finput.attr}.${input.input}`, false)) {
_.set(data, `${finput.attr}.${input.input}`, _.get(val, `value.${finput.attr}.${input.input}`));
}

// delete checkbox checked, clear file/delete
if (_.get(data, `${finput.attr}.${checkbox.input}`, false)) {
// clear checkbox data
delete data[finput.attr][checkbox.input];

// clear file data if there is any
if (_.get(data, `${finput.attr}.${input.input}`, false)) {
delete data[finput.attr][input.input];
}
}
}
});
}

return data;
};

module.exports = {
fileinputs,
filepaths,
filehold,
filecompare,
};
2 changes: 1 addition & 1 deletion lib/init/authenticated.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const config = require('config');
*
*/
const authenticated = (req, res, next) => {
if ((!req.isAuthenticated || !req.isAuthenticated()) && !req.url.startsWith('/create-admin') && req.url !== config.authentication.login.path && !req.url.startsWith('/css') && !req.url.startsWith('/js') && !req.url.startsWith('/images') && !req.url.startsWith('/favicon') && !req.url.startsWith('/api') && req.url !== '/robots.txt') {
if ((!req.isAuthenticated || !req.isAuthenticated()) && !req.url.startsWith('/create-admin') && req.url !== config.authentication.login.path && !req.url.startsWith('/css') && !req.url.startsWith('/js') && !req.url.startsWith('/images') && !req.url.startsWith('/favicon') && !req.url.startsWith('/api') && !req.url.startsWith('/tmp') && req.url !== '/robots.txt') {
res.redirect('/login');
}
else {
Expand Down
34 changes: 34 additions & 0 deletions lib/init/routes.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,38 @@
'use strict';

const _ = require('lodash');
const config = require('config');
const multer = require('multer');
const mkdirp = require('mkdirp');

const content = require('../content/middleware');
const users = require('../users/middleware');

module.exports = (app) => {
return new Promise(res => {
// temp file storage location
const dest = config.storage.temp.dest || 'public/tmp/';

// multer form handling setup
const upload = multer({
storage: multer.diskStorage({
destination: (req, file, cb) => {
cb(null, dest);
},
filename: (req, file, cb) => {
cb(null, `/${file.originalname}`);
},
}),
});

// creates temp storage location if it does not exist
mkdirp.sync(dest);

// creates final storage location when using file storage
if (config.storage.type === 'fs' && typeof config.storage.dest === 'string' && config.storage.dest !== '') {
mkdirp.sync(config.storage.dest);
}

// add all content types and flows to request object
app.use((req, response, next) => {
const request = req;
Expand All @@ -16,12 +43,19 @@ module.exports = (app) => {
next();
});

// content type landing
app.use('/content/:type', content);

// save a piece of content
app.use('/content/:type/save', upload.any());

// piece of content actions
app.use('/content/:type/:id', content);
app.use('/content/:type/:id/:revision', content);
app.use('/content/:type/:id/:revision/edit', content);
app.use('/content/:type/:id/:revision/approve', content);

// individual user
app.use('/users/:id', users);

res(app);
Expand Down
Loading

0 comments on commit bcb29f3

Please sign in to comment.