New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

apostrophe-forms

Package Overview
Dependencies
Maintainers
14
Versions
51
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

apostrophe-forms - npm Package Compare versions

Comparing version 0.5.7 to 0.5.8

lib/modules/apostrophe-schemas/views/schemaMacros.html

44

index.js

@@ -20,3 +20,3 @@ /* jshint node:true */

// form field widgets
'textField', 'textareaField', 'selectField', 'radioField', 'checkboxField', 'checkboxesField', 'dateField', 'timeField',
'sectionBreak', 'textField', 'integerField', 'textareaField', 'selectField', 'radioField', 'checkboxField', 'checkboxesField', 'dateField', 'timeField',
// text controls

@@ -66,3 +66,3 @@ 'style', 'bold', 'italic', 'createLink', 'unlink', 'insertUnorderedList', 'insertTable',

type: 'string'
},
}
]

@@ -106,2 +106,3 @@ });

// Adjust the widget used to actually insert the form. We're

@@ -134,2 +135,15 @@ // subclassing snippets here, so we use extendWidget to change

{
name: 'sectionBreak',
label: 'Section Break',
css: 'apostrophe-section-break',
schema: [
{
name: 'break',
label: 'Include Section Break',
type: 'boolean',
def: 'true'
}
]
},
{
name: 'textField',

@@ -153,2 +167,20 @@ label: 'Text Field',

{
name: 'integerField',
label: 'Integer Field',
css: 'apostrophe-text-field',
schema: [
{
name: 'label',
label: 'Label',
type: 'string',
required: true
},
{
name: 'required',
label: 'Required',
type: 'boolean'
}
]
},
{
name: 'textareaField',

@@ -407,3 +439,3 @@ label: 'Text Box Field',

resultEmail.submittedAt = result.submittedAt;
result.formId = form._id;

@@ -445,2 +477,3 @@

}
return res.send({ status: 'ok', replacement: self.render('thankYou', { form: form, result: result }, req) });

@@ -496,4 +529,4 @@ });

//csv exporter: /apos-forms/export
//require('./lib/exporter.js')(self);
//csv exporter: /apos-forms/export-form
require('./lib/exporter.js')(self);

@@ -516,2 +549,1 @@ self.exportPost = function(options){

};

409

lib/exporter.js

@@ -1,250 +0,229 @@

// /*
/*
// CSV EXPORTER
// ============
CSV EXPORTER
============
// 1. Build a criteria object
// 2. Get the form itself
// 3. Set up the csv module (set the http headers)
// 4. Set the column headers based on the form fields
// 5. Loop through database chunks
// 6. Do the work
1. Build a criteria object
2. Get the form itself
3. Set up the csv module (set the http headers)
4. Set the column headers based on the form fields
5. Loop through database chunks
6. Do the work
// */
*/
// var async = require('async'),
// moment = require('moment'),
// stringify = require('csv-stringify'),
// _ = require('lodash'),
// Promise = require('es6-promise').Promise;
var async = require('async'),
moment = require('moment'),
stringify = require('csv-stringify'),
_ = require('lodash'),
Promise = require('es6-promise').Promise;
// module.exports = function(self) {
module.exports = function(self) {
self._app.get(self._action + '/export-form', function(req, res) {
var formsCollection = self._apos.db.collection('aposFormSubmissions');
// ================================================================
// CRITERIA & FORM
// ================================================================
// self._app.post(self._action + '/export', function(req, res) {
// var formsCollection = self._apos.db.collection('aposFormSubmissions');
// check first for `req.query.formId`. We need this to get the form.
if(!req.query.formId){
res.statusCode = 500;
return res.send('Bad request.');
}
var formId = req.query.formId;
// // ================================================================
// // CRITERIA & FORM
// // ================================================================
// build a criteria object, look at `req.query.start` and `req.query.end`
// for possible date constraints.
var criteria = {
_id: formId,
type: 'form'
}
// // check first for `req.body.formId`. We need this to get the form.
// if(!req.body.formId){
// res.statusCode = 500;
// return res.send('Bad request.');
// }
// var formId = req.body.formId;
/*
(1) get form to generate csv headers
(2) generate csv file
*/
async.waterfall([
getForm,
generateCsv
], function(err, result) {
//response has already been sent in generateCsv()
});
// // build a criteria object, look at `req.body.start` and `req.body.end`
// // for possible date constraints.
// var criteria = {
// _id: formId,
// type: 'form'
// }
function getForm(callback){
self._apos.db.collection('aposPages').find(criteria).toArray( function(err, form) {
if(err){
console.err('Error retrieving the form');
callback(err);
}
// /*
// (1) get form to generate csv headers
// (2) generate csv file
// */
// async.waterfall([
// getForm,
// generateCsv
// ], function(err, result) {
// //response has already been sent in generateCsv()
// });
callback(null, form);
});
}
// function getForm(callback){
// self._apos.db.collection('aposPages').find(criteria).toArray( function(err, form) {
// if(err){
// callback(err);
// }
// callback(null, form);
// });
// }
function generateCsv(form, callback){
// ================================================================
// CSV SETUP
// ================================================================
// set up the csv-stringify module and make sure the delimiter is
// set to ','
stringifier = stringify({ delimiter: ',' });
// function generateCsv(form, callback){
// // ================================================================
// // CSV SETUP
// // ================================================================
// // set up the csv-stringify module and make sure the delimiter is
// // set to ','
// stringifier = stringify({ delimiter: ',' });
// set the http headers so that the browser expects a csv file
// download. you can also set the filename.
res.header('content-type','text/csv');
res.header('content-disposition', 'attachment; filename=form-submissions.csv');
// // set the http headers so that the browser expects a csv file
// // download. you can also set the filename.
// res.header('content-type','text/csv');
// res.header('content-disposition', 'attachment; filename=form-submissions.csv');
// open up the stream:
stringifier.pipe(res);
// // open up the stream:
// stringifier.pipe(res);
// prepare some variables for use in the streaming logic.
var currentCount = 0,
chunkSize = 10,
moreSubmissions = true;
// // prepare some variables for use in the streaming logic.
// var currentCount = 0,
// chunkSize = 10,
// // this will get changed but we want a high initial value.
// total = 100;
// ================================================================
// COLUMN HEADERS
// ================================================================
// // ================================================================
// // COLUMN HEADERS
// // ================================================================
// loop through the form and grab each field's fieldId and label.
//var fieldIds = [];
var formItems = _.get(form[0], "body.items");
var labels = _.pluck(formItems, "label");
var labelIds = _.pluck(formItems, "fieldId");
// // loop through the form and grab each field's fieldId and label.
// //var fieldIds = [];
// var formItems = _.get(form[0], "body.items");
// var labels = _.pluck(formItems, "label");
// var labelIds = _.pluck(formItems, "fieldId");
// make your first call to stringifier.write([]) in order to write
// the column headers.
stringifier.write(labels);
// // make your first call to stringifier.write([]) in order to write
// // the column headers.
// stringifier.write(labels);
// ================================================================
// CONTROL FLOW
// ================================================================
async.whilst(
// this decides when to stop running `whilst`
shouldContinue,
// this takes care of csv conversion and streaming
convertAndStream,
// this handles errors or closes the stream at the end.
closeStreamOrError
);
// // ================================================================
// // CONTROL FLOW
// // ================================================================
// async.whilst(
// // this decides when to stop running `whilst`
// shouldContinue,
// // this takes care of csv conversion and streaming
// convertAndStream,
// // this handles errors or closes the stream at the end.
// closeStreamOrError
// );
// determine if we should continue to the next chunk of the DB results
function shouldContinue() {
return moreSubmissions;
}
// // determine if we should continue to the next chunk of the DB results
// function shouldContinue() {
// console.log('SHOULD CONTINUE? ', currentCount);
// return currentCount < total;
// }
// // ================================================================
// // EXPORT LOOP
// // ================================================================
// // using a `chunkSize` and `totalSubmission` sort of variables,
// // loop through the formSubmissions and write each one out as an
// // array of values. MAKE SURE the length is the same as the column
// // length (i.e. if a field has no value, you still need to write '')
// ================================================================
// EXPORT LOOP
// ================================================================
// using a `chunkSize` and `totalSubmission` sort of variables,
// loop through the formSubmissions and write each one out as an
// array of values. MAKE SURE the length is the same as the column
// length (i.e. if a field has no value, you still need to write '')
// // grab some submissions
// function convertAndStream(callback) {
// console.log('CONVERT & STREAM');
// //getSubmissions() is gathering a chunk of data
// getSubmissions(form, chunkSize, currentCount)
// //go through the data chunk and write rows
// .then(function(submissionChunk) {
// _(submissionChunk)
// .each( function(chunk) {
// _.each(chunk, function(row) {
// console.log('writing a row???', row);
// stringifier.write(row);
// });
// });
// grab some submissions
function convertAndStream(callback) {
//getSubmissions() is gathering a chunk of data
getSubmissions(form, chunkSize, currentCount)
//go through the data chunk and write rows
.then(function(submissionChunk) {
_.forEach(submissionChunk, function(chunk) {
//Parse the chunk to form the row we need based on the form fields
var values = parseChunk(chunk)._result;
// // done: run the next loop!
// currentCount += chunkSize;
// return callback(null);
// })
// .catch(function(error) {
// return callback(error);
// });
// }
var row = "";
_.each(values, function(value, index) {
if (index){ //add leading comma
row +=",";
}
// // ================================================================
// // FINISH
// // ================================================================
// // when it's complete or if you've errored out, you need to call
// // `stringifier.end()` to close the stream (otherwise the browser
// // will never know to stop downloading the file).
// function closeStreamOrError(err) {
// if(err){
// res.statusCode = 500;
// }
if (_.isArray(value) && value.length){
row += '"' + value.toString() + '"';
} else {
row += value.toString();
}
});
// // close the stream.
// stringifier.end();
stringifier.write(row + '\n');
});
// //the waterfall will handle error status
// return callback(err);
// }
// done: run the next loop!
currentCount += chunkSize;
return callback(null);
})
.catch(function(error) {
return callback(error);
});
}
// ================================================================
// FINISH
// ================================================================
// when it's complete or if you've errored out, you need to call
// `stringifier.end()` to close the stream (otherwise the browser
// will never know to stop downloading the file).
function closeStreamOrError(err) {
if(err){
console.err('Error closing export form stream');
res.statusCode = 500;
}
// // ================================================================
// // THINGS THAT DO STUFF
// // ================================================================
// close the stream.
stringifier.end();
// // function getSubmissions(criteria, options) {
// // console.log("we are GETTING SUBMISSIONS");
// // return new Promise( function(resolve, reject) {
// // console.log(self._apos.getTaskReq());
// // //TODO directly call mongo db & pass options
// // self.get(self._apos.getTaskReq(), criteria, options, function(err, results) {
// // console.log('did we get inside here');
// // if(err) {
// // console.log('NOOOOOO :(');
// // return reject(err);
// // }
// // total = results.total;
// // return resolve(results.snippets);
// // });
// // });
// // }
//the waterfall will handle error status
return callback(err);
}
// function getSubmissions(form, limit, skip){
// criteria = {
// formId: formId
// }
// //TODO test this formatting when front end date selector is in place
// if(req.body.startDate || req.body.endDate){
// criteria.submittedAt = {};
// ================================================================
// THINGS THAT DO STUFF
// ================================================================
// if(req.body.startDate){
// criteria.submittedAt.$gte = moment(req.body.startDate).format('YYYY-MM-DD HH:mm:ss');
// }
// if(req.body.endDate){
// criteria.submittedAt.$lte = moment(req.body.endDate).format('YYYY-MM-DD HH:mm:ss');
// }
// }
function getSubmissions(form, limit, skip){
criteria = {
formId: formId
}
// // formsCollection.find(criteria).toArray(function(err, submissions){
// // if(err){
// // callback(err);
// // }
// // callback(null, form, submissions);
// // });
// console.log('Submission criteria: ', criteria);
// console.log('limit: ', limit, 'skip: ', skip);
// return new Promise( function(resolve, reject) {
// formsCollection.find(criteria).skip(skip).limit(limit).toArray(function(err, submissions){
// console.log('did we get inside here');
// if(err) {
// console.log('NOOOOOO :(');
// return reject(err);
// }
// console.log('we got SOME submissions: ', submissions);
// total = submissions.total;
// return resolve(submissions);
// });
// });
// }
if(req.query.startDate || req.query.endDate){
criteria.submittedAt = {};
// // given an array of submissions <= chunkSize.length,
// // strip the values of interest from each submission into an array,
// // and bundle all those arrays together in a larger array.
// function parseSubmissions(submissions) {
// return new Promise( function(resolve, reject) {
// var data = _.map(submissions, parseSubmission);
// return resolve( data );
// });
// }
if(req.query.startDate){
criteria.submittedAt.$gte = new Date(moment(req.query.startDate).format('YYYY-MM-DDTHH:mm:ssZ'));
}
if(req.query.endDate){
criteria.submittedAt.$lte = new Date(moment(req.query.endDate).endOf('day').format('YYYY-MM-DDTHH:mm:ssZ'));
}
}
// // do the work of parsing submissions.
// // output should be an array of values corresponding to the following keys (in order):
// function parseSubmission(submission) {
// console.log('parsing submission', submission);
// // this will end up as an array of rows.
// var values = [];
return new Promise( function(resolve, reject) {
formsCollection.find(criteria).skip(skip).limit(limit).toArray(function(err, submissions){
if(err) {
return reject(err);
}
// return values;
// }
// }
// });
// };
if(!_.size(submissions)){
moreSubmissions = false;
}
return resolve(submissions);
});
});
}
// do the work of parsing submission chunk.
// output should be an array of values corresponding to the global form keys:
function parseChunk(chunk) {
return new Promise( function(resolve, reject) {
var row = [];
_.forEach(labelIds, function(field){
row.push(chunk[field].value);
});
return resolve( row );
});
}
}
});
};
{
"name": "apostrophe-forms",
"version": "0.5.7",
"version": "0.5.8",
"description": "Allow your users to build forms on the fly on their Apostrophe sites",

@@ -5,0 +5,0 @@ "main": "index.js",

@@ -5,5 +5,11 @@ apos.widgetPlayers.forms = function($el) {

//add date picker to date field
apos.enhanceDate($('[data-forms-date]'));
apos.enhanceDate($form.find('[data-forms-date]'));
$form.on('submit', function() {
if ($form.find('[data-forms-section-break]').length > 0){
initSections($form);
}
$form.on('submit', function(e) {
e.preventDefault();
console.log("I will try to submit...");
var action = $form.attr('action');

@@ -13,2 +19,80 @@ var result = {};

//Call our sanitize function
sanitizeFields($form, result, errors);
apos.emit('sanitizeForm', $form, result, errors);
//Call our set messages function
setErrorMessages($form, errors, function(){
$.jsonCall(action, result, function(data) {
if (data.status !== 'ok') {
return false;
}
console.log($el, data);
$el.html(data.replacement);
return;
});
});
});
//Make sure all functionality is scoped to this function...
// anything might be required
apos.on('sanitizeFormField', function($field, key, result, errors) {
if ($field.is('[apos-required]')) {
if (!$field.val().toString().length) {
errors.push({
name: key,
message: 'required'
});
}
}
});
// checkboxes build arrays
apos.on('sanitizeFormField', function($field, key, result, errors) {
if ($field.attr('type') !== 'checkbox') {
return;
}
if (!_.has(result, key)) {
result[key] = [];
}
if ($field.prop('checked')) {
var val = $field.attr('value');
result[key].push(val);
}
});
// checkbox groups can have overall min and max
apos.on('sanitizeForm', function($form, result, errors) {
console.log($form);
$form.find('[data-forms-checkboxes]').each(function() {
var $field = $(this);
var min = $field.attr('data-forms-checkbox-min');
var max = $field.attr('data-forms-checkbox-max');
var name = $field.attr('data-forms-field-name');
if(min > 0 || max > 0) {
var checked = 0;
checked = result[name].length;
if((min && (checked < min)) || (max && (checked > max))) {
var message;
if (checked < min) {
message = 'Please select at least ' + min + ' checkboxes.';
} else {
message = 'Please select no more than ' + max + ' checkboxes.';
}
errors.push({
name: name,
message: message
});
}
}
});
});
apos.on('sanitizeForm', function($form, result, errors) {
});
function sanitizeFields($form, result, errors){
$form.find('[data-forms-field-name]').each(function() {

@@ -23,7 +107,12 @@ var $field = $(this);

});
}
apos.emit('sanitizeForm', $form, result, errors);
function setErrorMessages($form, errors, callback){
//remove old errors & display new error message(s)
$('.apos-forms-error-message').remove();
if (!errors.length){
return callback();
}
if (errors.length) {

@@ -45,64 +134,61 @@ var $first;

}
}
$.jsonCall(action, result, function(data) {
if (data.status !== 'ok') {
return false;
}
$el.html(data.replacement);
function initSections($form){
var $sections = $form.find('[data-type="sectionBreak"]');
//Using our functions to get things started.
buildSections($sections, function(){
$sections = $form.find('[data-forms-section]')
$sections.first().toggleClass('active');
//Now add next/prev
addSectionListeners($sections);
});
return false;
});
};
// anything might be required
apos.on('sanitizeFormField', function($field, key, result, errors) {
if ($field.is('[apos-required]')) {
if (!$field.val().toString().length) {
errors.push({
name: key,
message: 'required'
//function to wrap sections of forms appropriately.
function buildSections($sections, callback){
$sections.each(function(){
var $additional = $(this).nextUntil('[data-type="sectionBreak"], [data-forms-arrows]');
var $wholeSection = $(this).add($additional);
$wholeSection.wrapAll('<div class="apos-form-section" data-forms-section></div>');
});
}
}
});
return callback();
};
// checkboxes build arrays
apos.on('sanitizeFormField', function($field, key, result, errors) {
if ($field.attr('type') !== 'checkbox') {
return;
}
if (!_.has(result, key)) {
result[key] = [];
}
if ($field.prop('checked')) {
var val = $field.attr('value');
result[key].push(val);
}
});
//function to init and handle next/prev events.
function addSectionListeners($sections){
var $next = $form.find('[data-forms-next]');
var $prev = $form.find('[data-forms-prev]');
// checkbox groups can have overall min and max
apos.on('sanitizeForm', function($form, result, errors) {
$form.find('[data-forms-checkboxes]').each(function() {
var $field = $(this);
var min = $field.attr('data-forms-checkbox-min');
var max = $field.attr('data-forms-checkbox-max');
var name = $field.attr('data-forms-field-name');
if(min > 0 || max > 0) {
var checked = 0;
checked = result[name].length;
$next.toggleClass('active', true);
$prev.toggleClass('active', true);
if((min && (checked < min)) || (max && (checked > max))) {
var message;
if (checked < min) {
message = 'Please select at least ' + min + ' checkboxes.';
} else {
message = 'Please select no more than ' + max + ' checkboxes.';
}
errors.push({
name: name,
message: message
});
}
}
});
});
//next handler
$next.click(function(){
var currentIndex = $sections.filter('.active').index();
setActiveSection(currentIndex + 1);
});
//prev handler
$prev.click(function(){
var currentIndex = $sections.filter('.active').index();
setActiveSection(currentIndex - 1);
});
//For a given index, set an appropriately active class or return.
function setActiveSection(index){
var errors = [];
var result = {};
//Give other pieces the ability to hook into this change.
sanitizeFields($sections, result, errors);
setErrorMessages($sections, errors, function(){
if (index < 0 || index >= ($sections.length)){
return;
}
$sections.toggleClass('active', false);
$sections.eq(index).toggleClass('active', true);
});
}
};
}
};

@@ -11,3 +11,2 @@ // Editor for all form field widgets. This is

var self = this;
self.type = info.name;

@@ -61,2 +60,3 @@ options.template = '.apos-' + info.css + '-editor';

self.$fields = aposSchemas.findSafe(self.$el, '[data-fields]');
return aposSchemas.populateFields(self.$fields, info.schema, self.data, function() {

@@ -128,5 +128,41 @@ apos.emit('widgetModalReady', self);

};
$.extend(options, optionsArg);
AposSnippets.call(self, options);
self.afterPopulatingEditor = function($el, snippet, callback) {
//add form id to export form
var $exportForm = $el.find('[data-form-export]');
$exportForm.attr('data-form-export', snippet._id);
//enhance date selectors
$dateSelects = $el.find('[data-forms-dates]');
apos.enhanceDate($dateSelects);
$exportForm.on('submit', function(){
var self = $(this);
var query = "formId=" + self.attr('data-form-export');
//optional dates
var $startDate = self.find('fieldset [data-forms-field-name="startDate"]');
var $endDate = self.find('fieldset [data-forms-field-name="endDate"]');
if($startDate.val()){
query += "&startDate=" + $startDate.val();
}
if($endDate.val()){
query += "&endDate=" + $endDate.val();
}
//to download csv file, $.jsonCall and $.ajax calls will not work,
//must call via url
window.open('/apos-forms/export-form?' + query);
return false;
});
return callback();
};
}

@@ -37,3 +37,3 @@ A form builder for the [Apostrophe CMS](http://apostrophenow.org).

// form field widgets
'textField', 'textareaField', 'selectField', 'radioField', 'checkboxField', 'checkboxesField', 'dateField', 'timeField',
'textField', 'integerField', 'textareaField', 'selectField', 'radioField', 'checkboxField', 'checkboxesField', 'dateField', 'timeField',
// text controls

@@ -40,0 +40,0 @@ 'style', 'bold', 'italic', 'createLink', 'unlink', 'insertUnorderedList', 'insertTable',

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc