ApostropheCMS Contact us form with attachment - node.js

I am new to Apostrophe and trying to create a contact us form with file attachment in Apostrophe by following the tutorial.
https://apostrophecms.org/docs/tutorials/intermediate/forms.html
I have also created the attachment field in my index.js and it works fine from the admin panel.
Now, I am trying to create my own html for the form with file submission.
// in lib/modules/contact-form-widgets/public/js/always.js
apos.define('contact-form-widgets', {
extend: 'apostrophe-widgets',
construct: function(self, options) {
self.play = function($widget, data, options) {
var $form = $widget.find('[data-contact-form]');
var schema = self.options.submitSchema;
var piece = _.cloneDeep(self.options.piece);
return apos.schemas.populate($form, self.schema, self.piece, function(err) {
if (err) {
alert('A problem occurred setting up the contact form.');
return;
}
enableSubmit();
});
function enableSubmit() {
$form.on('submit', function() {
submit();
//I can access file here
// console.log($form.find('file'))
return false;
});
}
function submit() {
return async.series([
convert,
submitToServer
], function(err) {
if (err) {
alert('Something was not right. Please review your submission.');
} else {
// Replace the form with its formerly hidden thank you message
$form.replaceWith($form.find('[data-thank-you]'));
}
});
function convert(callback) {
return apos.schemas.convert($form, schema, piece, callback);
}
function submitToServer(callback) {
return self.api('submit', piece, function(data) {
alert("I AM AT SUBMIT API ")
if (data.status === 'ok') {
// All is well
return callback(null);
}
// API-level error
return callback('error');
}, function(err) {
// Transport-level error
alert("I AM HERE AT API ERROR")
return callback(err);
});
}
}
};
}
});
//and my widget.html is
<div class="form-group">
<input name="custom-file" type="file">
</div>
When I run this I get following errors
user.js:310 Uncaught TypeError: Cannot read property 'serialize' of undefined
at Object.self.getArea (user.js:310)
at Object.self.getSingleton (user.js:303)
at Object.convert (user.js:686)
at user.js:164
at async.js:181
at iterate (async.js:262)
at async.js:274
at async.js:44
at setImmediate.js:27
at runIfPresent (setImmediate.js:46)
My question is, how do I handle file submission? Is there any better approach for this?

This is much easier to do using the apostrophe-pieces-submit-widgets module, which allows you to define a schema for what the user can submit. You can include a field of type attachment in that, and this is demonstrated in the README.

Related

TypeError: validator.escape is not a function - (express-validator#6.12.1 package)

Codecademy video: link
Explanation:
As part of my Codecademy Back-End Engineer training, I have to do a project outside of their platform. The goal of this project is to make sure a node application is protected from common web attacks.
One challenge I faced was securing the code from Cross-Site Scripting (XSS) attacks. To do this, I used a package called express-validator#6.12.1. The code uses a function called validator.escape which is supposed to protect against any malicious code being inserted into an input form. However, I am getting an error in the console when I try to use it.
Terminal output :
TypeError: validator.escape is not a function
Here is the code :
const validator = require("express-validator");
app.post("/public_forum", function (request, response) {
if (request.session.loggedin) {
var comment = validator.escape(request.body.comment);
var username = request.session.username;
if (comment) {
db.all(
`INSERT INTO public_forum (username,message) VALUES ('${username}','${comment}')`,
(err, rows) => {
console.log(err);
}
);
db.all(`SELECT username,message FROM public_forum`, (err, rows) => {
console.log(rows);
console.log(err);
response.render("forum", { rows });
});
} else {
db.all(`SELECT username,message FROM public_forum`, (err, rows) => {
console.log(rows);
console.log(err);
response.render("forum", { rows });
});
}
comment = "";
} else {
response.redirect("/");
}
comment = "";
//response.end();
});
In the video of Codecademy, the guy uses this function.
Try with:
const {check, validationResult} = require('express-validator');
app.post('/public_forum', async function (request, response) {
if (request.session.loggedin) {
await check('comment').trim().escape().run(req);
const validationResult = await validationResult(req);
if (validationResult.isEmpty()) {
// Good to go...
const { comment } = req.body;
}
...
Link to official docs
I have implemented your code. I tried to add both a malicious and safe comment, but I got an error message on my browser that said, "Port 4000 Not Found." Every time I run the code, it kills the port. So I have implemented another code that works well based on what you sent me.
// This code defines a post request handler for the "/public_forum" endpoint.
app.post('/public_forum', async function (request, response) {
// Check if the user is logged in by checking the session data.
if (request.session.loggedin) {
// Trim and escape the incoming comment.
await check('comment').trim().escape().run(request);
// Get the validation result of the incoming comment.
const errors = validationResult(request);
// If the validation result contains errors, return a 400 status with the errors in a JSON format.
if (!errors.isEmpty()) {
return response.status(400).json({ errors: errors.array() });
}
// Get the comment from the request body.
const { comment } = request.body;
// If a valid comment exists, insert it into the "public_forum" database table.
if (comment) {
db.run(
`INSERT INTO public_forum (username,message) VALUES (?,?)`, [request.session.username, comment],
(err) => {
// If an error occurs while inserting the comment, log the error.
if (err) {
console.error(err);
}
}
);
}
// Select all the rows from the "public_forum" table.
db.all(`SELECT username,message FROM public_forum`, (err, rows) => {
// If an error occurs while selecting the rows, log the error.
if (err) {
console.error(err);
}
// Log the selected rows.
console.log(rows);
// Render the "forum" template, passing in the selected rows as a parameter.
response.render("forum", { rows });
});
} else {
// If the user is not logged in, redirect them to the homepage.
response.redirect("/");
}
});

Live search using autocomplete node.js and express.js

community. I am trying to implement a live search using the autocomplete library but every try is unsuccessful. I get every time a 500 server error. Every assistant is appreciated because I am new in coding.
I have a simple model for an article with title and body and I would like to show suggestions when the user search for an article
model/article.js
// Method to construct the json result set
module.exports.buildResultSet=function(docs) {
var result = [];
for(var object in docs){
result.push(docs[object]);
}
return result;
}
routes/article.js
router.get('/search', function(req, res){
encyclopediaModel.getMyArticlesByName(theRequester, function (pError, pFoundedArticles) {
if (!pError) {
// Method to construct the json result set
var result = encyclopediaModel.buildResultSet(pFoundedArticles);
res.json(result);
} else {
return res.json(JSON.stringify(pError), {
'Content-Type': 'application/json'
}, 404);
}
},req.query.title)
});
//Ajax call
$("#search-query").autocomplete({
source: function (request, response) {
$.ajax({
url: "/encyclopedia/search",
type: "GET",
data: request, // request is the value of search input
success: function (data) {
response( data );
console.log('success', data);
}
});
},
// The minimum number of characters a user must type before a search is performed.
minLength: 3,
// set an onFocus event to show the result on input field when result is focused
focus: function (event, ui) {
this.value = ui.item.label;
// Prevent other event from not being execute
event.preventDefault();
},
select: function (event, ui) {
}
});
<input id="search-query" type="text" placeholder="Articles...">
module.exports.getMyArticlesByName = function (requester, callback, pTitle){
var regex = new RegExp(pTitle["term"], 'i');
article.find({title: regex}, { 'title': 1 }).sort({"updated_at":-1}).sort({"created_at":-1}).limit(20).exec(callback);
}

Pull mongo data to ejs template

I have the following basic document in mongo:
connecting to: test
> db.car.find();
{ "_id" : ObjectId("5657c6acf4175001ccfd0ea8"), "make" : "VW" }
I am using express, mongodb native client (not mongoose) and ejs.
collection.find().toArray(function (err, result) {
if (err) {
console.log(err);
} else if (result.length) {
console.log('Found:', result);
mk = result;
console.log('mk = ', mk);
} else {
console.log('No document(s) found with defined "find" criteria!');
}
//Close connection
db.close();
});
}
});
Here is the render code:
// index page
app.get('/', function(req, res) {
res.render('pages/index', { make: result });
});
And i want to pass the data make: VW into my index.ejs file:
<h2>Cars:</h2>
<h3>My favorite make is <%= make %>
This should be real simple and straightforward but i just don't understand how to pass the "mk" variable (which i can console.log to the screen) to be rendered by the ejs view?
You should use the find method inside the route (or with a callback) to get the result and render it:
app.get('/', function(req, res) {
collection.find().toArray(function(err, result) {
if(err) {
console.log(err);
res.status('400').send({error: err});
} else if(result.length) {
console.log('Found:', result);
mk = result;
console.log('mk = ', mk);
res.render('pages/index', {make: mk});
} else {
console.log('No document(s) found with defined "find" criteria!');
res.status('400').send({error: 'No document(s) found'});
}
//Close connection
db.close();
});
});
Very simple, your are outputting an array of JSON objects
Here is one way to visualise it:
[{a:b},{a:b},{a:b},{a:b}];
If you want the first result it would be array[0].a
so you simply need to call it this way:
<%= make[0].(your database key here) =>
//example
<%= make[0].name =>
A more sensible way to get this done would be to iterate through the array and output only the result you want on the server side. If you send all your data to your client you might have security issues depending on what you send.
It should be simple, but you are defining mk just like this: mk = result So because you want to pass a variable to an ejs file you need
"var mk = result"
Have a good day, Ben.

Trying to implement callback, or some way to wait until my get request is done. This is in node.js express

Im trying to implement some way to stop my code to redirect me before I get the response from the omdb api I am using.
My function for making a search for a movie and saving all titles in a session looks like this:
app.post('/search', isLoggedIn, function(req, res) {
function getMovies(arg, callback){
console.log('In getMovies');
console.log('searching for '+arg);
omdb.search(arg, function(err, movies) {
if(err) {
return console.error(err);
}
if(movies.length < 1) {
return console.log('No movies were found!');
}
var titles = [];
movies.forEach(function(movie) {
// If title exists in array, dont push.
if(titles.indexOf(movie.title) > -1){
console.log('skipped duplicate title of '+movie.title);
}
else{
titles.push(movie.title);
console.log('pushed '+movie.title);
}
});
// Saves the titles in a session
req.session.titles = titles;
console.log(req.session.titles);
});
// Done with the API request
callback();
}
var title = req.body.title;
getMovies(title, function() {
console.log('Done with API request, redirecting to GET SEARCH');
res.redirect('/search');
});
});
However I dont know if I implement callback in the right way, because I think there can be a problem with the api request actually executing before the callback, but not finishing before. And therefor the callback is working..
So I just want 2 things from this question. Does my callback work? And what can I do if a callback won't solve this problem?
Thankful for all answers in the right direction.
Add
callback();
To, like this
omdb.search(arg, function(err, movies) {
if (err) {
return console.error(err);
}
if (movies.length < 1) {
return console.log('No movies were found!');
}
var titles = [];
movies.forEach(function(movie) {
// If title exists in array, dont push.
if (titles.indexOf(movie.title) > -1) {
console.log('skipped duplicate title of ' + movie.title);
} else {
titles.push(movie.title);
console.log('pushed ' + movie.title);
}
});
// Saves the titles in a session
req.session.titles = titles;
callback();
});
omdb.search is asynchronous function that's why callback executed before omdb.search

How to use Knockout Mapping plugin in Hottowel

i was able to install ko.mapping in VisualStudio but when i try to map some Json Data in my view it does not work. can anyone tell me what i am doing wrong ?
here is my viewmodel
define(['plugins/router', 'knockout', 'services/logger', 'durandal/app', 'mapping'], function (router, ko, logger, app, mapping) {
//#region Internal Methods
function activate() {
logger.log('Google Books View Activated', null, 'books', true);
return true;
}
//#endregion
//==jquery=================================================
function attached() {
}//-->end of viewAttached()
//========VIEWMODEL========================================
var ViewModel = function (data) {
activate = activate;
attached = attached;
title = 'google Books';
};
return new ViewModel();
});
and here ist an working example in Jsfiddle
I don't think that you don't need to return a new View Model. you just need to return the view model.
define(['plugins/router', 'knockout', 'services/logger', 'durandal/app', 'mapping'],
function (router, ko, logger, app, mapping) {
var books = ko.observableArray();
function activate() {
getBooks().then(function(){
logger.log('Google Books View Activated', null, 'books', true);
return true;
});
}
function attached() {
}
function getBooks(){
$.getJSON(url, function (data) {
vm.books(ko.mapping.fromJS(data));
return true;
});
}
var vm = {
activate : activate,
attached : attached,
title : 'google Books',
books: books
};
return vm;
});
EDIT
To find requirejs errors add to your main.js file. It should help in tracking down requirejs module loading errors.
requirejs.onError = function (err) {
console.log(err.requireType);
if (err.requireType === 'timeout') {
console.log('modules: ' + err.requireModules);
}
throw err;
};

Resources