Browse Source

Support for custom fields

Brendan Abolivier 7 years ago
parent
commit
1133285ce1
Signed by: Brendan Abolivier <contact@brendanabolivier.com> GPG key ID: 8EF1500759F70623
2 changed files with 210 additions and 65 deletions
  1. 173
    65
      front/form.js
  2. 37
    0
      server.js

+ 173
- 65
front/form.js View File

@@ -1,16 +1,27 @@
1
+// Consts for readability
2
+const REQUIRED      = true;
3
+const NOT_REQUIRED  = false;
4
+
5
+
6
+var prefix = 'form'
7
+
1 8
 var items = {
2
-    name: 'form_name',
3
-    addr: 'form_addr',
4
-    subj: 'form_subj',
5
-    text: 'form_text',
9
+    name: 'name',
10
+    addr: 'addr',
11
+    subj: 'subj',
12
+    text: 'text',
6 13
 };
7 14
 
8
-var server  = getServer();
9
-var token   = "";
10
-var labels  = true;
11
-var lang    = {};
15
+var DOMFields = {};
16
+
17
+var server  		= getServer();
18
+var token   		= "";
19
+var labels  		= true;
20
+var lang    		= [];
21
+var customFields 	= {};
12 22
 
13 23
 var xhr = {
24
+	customFields: new XMLHttpRequest(),
14 25
     lang: new XMLHttpRequest(),
15 26
     token: new XMLHttpRequest(),
16 27
     send: new XMLHttpRequest()
@@ -18,6 +29,15 @@ var xhr = {
18 29
 
19 30
 // XHR callbacks
20 31
 
32
+xhr.customFields.onreadystatechange = function() {
33
+	if(xhr.customFields.readyState == XMLHttpRequest.DONE) {
34
+        customFields = JSON.parse(xhr.customFields.responseText);
35
+        for(let field in customFields) {
36
+            customFields[field].name = field;
37
+        }
38
+	}
39
+};
40
+
21 41
 xhr.token.onreadystatechange = function() {
22 42
     if(xhr.token.readyState == XMLHttpRequest.DONE) {
23 43
         token = xhr.token.responseText;
@@ -73,6 +93,8 @@ function getServer() {
73 93
 function generateForm(id) {
74 94
     // Get translated strings
75 95
     getLangSync();
96
+	// Get custom fields if defined in the configuration
97
+	getCustomFieldsSync();
76 98
     
77 99
     var el = document.getElementById(id);
78 100
     
@@ -84,20 +106,42 @@ function generateForm(id) {
84 106
     status.setAttribute('id', 'form_status');
85 107
     el.appendChild(status);
86 108
     
87
-    var input = {
88
-        name: getField(items.name, lang.form_name_label, false, 'input'),
89
-        addr: getField(items.addr, lang.form_addr_label, true, 'input'),
90
-        subj: getField(items.subj, lang.form_subj_label, false, 'input'),
91
-        text: getField(items.text, lang.form_mesg_label, false, 'textarea')
109
+    DOMFields = {
110
+        name: getField({
111
+            name: items.name,
112
+            label: lang.form_name_label,
113
+            type: 'text'
114
+        }, REQUIRED),
115
+        addr: getField({
116
+            name: items.addr,
117
+            label: lang.form_addr_label,
118
+            type: 'email'
119
+        }, REQUIRED),
120
+        subj: getField({
121
+            name: items.subj,
122
+            label: lang.form_subj_label,
123
+            type: 'text'
124
+        }, REQUIRED),
125
+        text: getField({
126
+            name: items.text,
127
+            label: lang.form_mesg_label,
128
+            type: 'textarea'
129
+        }, REQUIRED)
92 130
     };
93 131
     
94
-    // Adding nodes to document
132
+    // Adding custom fields
95 133
     
96
-    el.appendChild(input.name);
97
-    el.appendChild(input.addr);
98
-    el.appendChild(input.subj);
99
-    el.appendChild(input.text);
134
+    for(let fieldName in customFields) {
135
+        let field = customFields[fieldName];
136
+        DOMFields[fieldName] = getField(field, NOT_REQUIRED);
137
+    }
100 138
     
139
+    // Adding nodes to document
140
+
141
+    for(let field in DOMFields) {
142
+        el.appendChild(DOMFields[field]);
143
+    }
144
+
101 145
     // Adding submit button
102 146
     
103 147
     el.appendChild(getSubmitButton('form_subm', lang.form_subm_label));
@@ -108,63 +152,103 @@ function generateForm(id) {
108 152
 }
109 153
 
110 154
 
111
-// Returns a form field
112
-// id: field HTML identifier
113
-// placeholder: placeholder text
114
-// email: boolean: is it an email field?
115
-// type: 'input' or 'textarea'
116
-// return: a div node containing a label and an input text field
117
-function getField(id, placeholder, email, type) {
118
-    var field = document.createElement('div');
119
-    
120
-    field.setAttribute('id', id); // TODO: configurable prefix
155
+function getField(fieldInfos, required) {
156
+    var block = document.createElement('div');
157
+    
158
+    block.setAttribute('id', fieldInfos.name);
159
+    
160
+    let field = {};
161
+    
162
+    switch(fieldInfos.type) {
163
+        case 'text':        field = getTextField(fieldInfos, required);
164
+                            break;
165
+        case 'textarea':    field = getTextarea(fieldInfos, required);
166
+                            break;
167
+        case 'email':       field = getEmailField(fieldInfos, required);
168
+                            break;
169
+        case 'select':      field = getSelectField(fieldInfos, required);
170
+                            break;
171
+    }
172
+
121 173
     if(labels) {
122
-        field.appendChild(getLabel(id, placeholder, type));
174
+        block.appendChild(getLabel(fieldInfos.label, field.id));
123 175
     }
124
-    field.appendChild(getInputField(id, placeholder, email, type));
176
+
177
+    block.appendChild(field);
125 178
     
126
-    return field;
179
+    return block;
127 180
 }
128 181
 
129 182
 
130 183
 // Returns a label
131
-// id: field HTML identifier
132 184
 // content: label's inner content
133
-// type: 'input' or 'textarea'
185
+// id: field HTML identifier
134 186
 // return: a label node the field's description
135
-function getLabel(id, content, type) {
187
+function getLabel(content, id) {
136 188
     var label = document.createElement('label');
137 189
     
138
-    label.setAttribute('for', id + '_' + type);
190
+    label.setAttribute('for', id);
139 191
     label.innerHTML = content;
140 192
     
141 193
     return label;
142 194
 }
143 195
 
144 196
 
145
-// Returns an input text field
146
-// id: field HTML identifier
147
-// placeholder: placeholder text, field description
148
-// email: boolean: is it an email field?
149
-// type: 'input' or 'textarea'
150
-// return: an input text or email field (depending on "email"'s value') with an
151
-//         HTML id and a placeholder text
152
-function getInputField(id, placeholder, email, type) {
153
-    var input = document.createElement(type);
154
-    
155
-    if(!type.localeCompare('input')) { // Set input type if input
156
-        if(email) {
157
-            input.setAttribute('type', 'email');
158
-        } else {
159
-            input.setAttribute('type', 'text');
160
-        }
197
+function getSelectField(fieldInfos, required) {
198
+    let field = document.createElement('select');
199
+
200
+    if(required) {
201
+        field.setAttribute('required', 'required');
161 202
     }
203
+    field.setAttribute('id', prefix + '_' + fieldInfos.name + '_select');
204
+
205
+    let index = 0;
162 206
     
163
-    input.setAttribute('required', 'required');
164
-    input.setAttribute('placeholder', placeholder);
165
-    input.setAttribute('id', id + '_' + type);
207
+    // Add all options to select
208
+    for(let choice of fieldInfos.options) {
209
+        let option = document.createElement('option');
210
+        option.setAttribute('value', index);
211
+        option.innerHTML = choice;
212
+        field.appendChild(option);
213
+        index++;
214
+    }
215
+
216
+    return field
217
+}
218
+
219
+
220
+function getTextField(fieldInfos, required) {
221
+    return getBaseInputField(fieldInfos, required, 'text');
222
+}
223
+
224
+
225
+function getEmailField(fieldInfos, required) {
226
+    return getBaseInputField(fieldInfos, required, 'email');
227
+}
228
+
229
+
230
+function getBaseInputField(fieldInfos, required, type) {
231
+    let field = getBaseField(fieldInfos, required, 'input')
232
+    field.setAttribute('type', type);
233
+    return field;
234
+}
235
+
236
+
237
+function getTextarea(fieldInfos, required) {
238
+    return getBaseField(fieldInfos, required, 'textarea');
239
+}
240
+
241
+
242
+function getBaseField(fieldInfos, required, tag) {
243
+    let field = document.createElement(tag);
166 244
     
167
-    return input;
245
+    if(required) {
246
+        field.setAttribute('required', 'required');
247
+    }
248
+    field.setAttribute('placeholder', fieldInfos.label);
249
+    field.setAttribute('id', prefix + '_' + fieldInfos.name + '_' + tag);
250
+    
251
+    return field;
168 252
 }
169 253
 
170 254
 
@@ -210,23 +294,38 @@ function sendForm() {
210 294
 // Fetch form inputs from HTML elements
211 295
 // return: an object containing all the user's input
212 296
 function getFormData() {
213
-    return {
214
-        name: document.getElementById(items.name + '_input').value,
215
-        addr: document.getElementById(items.addr + '_input').value,
216
-        subj: document.getElementById(items.subj + '_input').value,
217
-        text: document.getElementById(items.text + '_textarea').value,
218
-        token: token
297
+    let data = {};
298
+    data.token = token;
299
+    data.custom = {};
300
+
301
+    // Custom fields
302
+    // Select the field
303
+    let index = 0;
304
+
305
+    if(labels) {
306
+        index = 1;
219 307
     }
308
+    
309
+    for(let field in DOMFields) {
310
+        let el = DOMFields[field].children[index];
311
+        if(field in customFields) {
312
+            data.custom[field] = el.value;
313
+        } else {
314
+            data[field] = el.value;
315
+        }
316
+    }
317
+
318
+    return data;
220 319
 }
221 320
 
222 321
 
223 322
 // Empties the form fields
224 323
 // return: nothing
225 324
 function cleanForm() {
226
-    document.getElementById(items.name + '_input').value = '';
227
-    document.getElementById(items.addr + '_input').value = '';
228
-    document.getElementById(items.subj + '_input').value = '';
229
-    document.getElementById(items.text + '_textarea').value = '';
325
+    document.getElementById(prefix + '_' + items.name + '_input').value = '';
326
+    document.getElementById(prefix + '_' + items.addr + '_input').value = '';
327
+    document.getElementById(prefix + '_' + items.subj + '_input').value = '';
328
+    document.getElementById(prefix + '_' + items.text + '_textarea').value = '';
230 329
 }
231 330
 
232 331
 
@@ -243,4 +342,13 @@ function getToken() {
243 342
 function getLangSync() {
244 343
     xhr.lang.open('GET', server + '/lang', false);
245 344
     xhr.lang.send();
345
+}
346
+
347
+
348
+// Asks the server for the custom fields if there's one or more set in the
349
+// configuration file
350
+// return: nothing
351
+function getCustomFieldsSync() {
352
+	xhr.customFields.open('GET', server + '/fields', false);
353
+	xhr.customFields.send();
246 354
 }

+ 37
- 0
server.js View File

@@ -96,6 +96,8 @@ app.post('/send', function(req, res, next) {
96 96
         replyTo: req.body.name + ' <' + req.body.addr + '>',
97 97
         html: req.body.text
98 98
     };
99
+
100
+    params.custom = processCustom(req.body.custom);
99 101
     
100 102
     // Replacing the mail's content with HTML from the pug template
101 103
     // Commenting the line below will bypass the generation and only user the
@@ -141,6 +143,19 @@ app.get('/lang', function(req, res, next) {
141 143
 });
142 144
 
143 145
 
146
+// A request on /fields sends data on custom fields.
147
+app.get('/fields', function(req, res, next) {
148
+	// Response will be JSON
149
+    res.header('Access-Control-Allow-Headers', 'Content-Type');
150
+
151
+	// Send an array anyway, its length will determine if we need to display any
152
+	let customFields = settings.customFields || [];
153
+	
154
+	// Send custom fields data
155
+	res.status(200).send(customFields);
156
+});
157
+
158
+
144 159
 // Use either the default port or the one chosen by the user (PORT env variable)
145 160
 var port = process.env.PORT || 1970;
146 161
 // Same for the host (using the HOST env variable)
@@ -265,4 +280,26 @@ function cleanTokens() {
265 280
     }
266 281
     
267 282
     log.info(lang.log_cleared_token);
283
+}
284
+
285
+
286
+function processCustom(custom) {
287
+    let fields = {};
288
+    
289
+    for(let field in custom) {
290
+        let type = settings.customFields[field].type;
291
+
292
+        switch(type) {
293
+            case 'select':  custom[field] = settings.customFields[field]
294
+                                            .options[custom[field]];
295
+                            break;
296
+        }
297
+
298
+        fields[field] = {
299
+            value: custom[field],
300
+            label: settings.customFields[field].label
301
+        }
302
+    }
303
+    
304
+    return fields;
268 305
 }