2021-06-24 03:12:46 +00:00
|
|
|
{% load i18n %}
|
|
|
|
{% load inventree_extras %}
|
|
|
|
|
2021-06-23 10:07:56 +00:00
|
|
|
/**
|
|
|
|
* This file contains code for rendering (and managing) HTML forms
|
|
|
|
* which are served via the django-drf API.
|
|
|
|
*
|
|
|
|
* The django DRF library provides an OPTIONS method for each API endpoint,
|
|
|
|
* which allows us to introspect the available fields at any given endpoint.
|
|
|
|
*
|
|
|
|
* The OPTIONS method provides the following information for each available field:
|
|
|
|
*
|
|
|
|
* - Field name
|
|
|
|
* - Field label (translated)
|
|
|
|
* - Field help text (translated)
|
|
|
|
* - Field type
|
|
|
|
* - Read / write status
|
|
|
|
* - Field required status
|
|
|
|
* - min_value / max_value
|
|
|
|
*/
|
|
|
|
|
2021-06-23 11:41:19 +00:00
|
|
|
/*
|
|
|
|
* Return true if the OPTIONS specify that the user
|
|
|
|
* can perform a GET method at the endpoint.
|
|
|
|
*/
|
|
|
|
function canView(OPTIONS) {
|
|
|
|
|
|
|
|
if ('actions' in OPTIONS) {
|
|
|
|
return ('GET' in OPTIONS.actions);
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Return true if the OPTIONS specify that the user
|
|
|
|
* can perform a POST method at the endpoint
|
|
|
|
*/
|
|
|
|
function canCreate(OPTIONS) {
|
|
|
|
|
|
|
|
if ('actions' in OPTIONS) {
|
|
|
|
return ('POST' in OPTIONS.actions);
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Return true if the OPTIONS specify that the user
|
|
|
|
* can perform a PUT or PATCH method at the endpoint
|
|
|
|
*/
|
|
|
|
function canChange(OPTIONS) {
|
|
|
|
|
|
|
|
if ('actions' in OPTIONS) {
|
|
|
|
return ('PUT' in OPTIONS.actions || 'PATCH' in OPTIONS.actions);
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Return true if the OPTIONS specify that the user
|
|
|
|
* can perform a DELETE method at the endpoint
|
|
|
|
*/
|
|
|
|
function canDelete(OPTIONS) {
|
|
|
|
|
|
|
|
if ('actions' in OPTIONS) {
|
|
|
|
return ('DELETE' in OPTIONS.actions);
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-23 10:07:56 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Get the API endpoint options at the provided URL,
|
|
|
|
* using a HTTP options request.
|
|
|
|
*/
|
2021-06-23 11:41:19 +00:00
|
|
|
function getApiEndpointOptions(url, callback, options={}) {
|
2021-06-23 10:07:56 +00:00
|
|
|
|
2021-06-23 10:30:26 +00:00
|
|
|
// Return the ajax request object
|
2021-06-23 11:41:19 +00:00
|
|
|
$.ajax({
|
2021-06-23 10:07:56 +00:00
|
|
|
url: url,
|
|
|
|
type: 'OPTIONS',
|
|
|
|
contentType: 'application/json',
|
|
|
|
dataType: 'json',
|
|
|
|
accepts: {
|
|
|
|
json: 'application/json',
|
|
|
|
},
|
2021-06-23 11:41:19 +00:00
|
|
|
success: callback,
|
2021-06-23 13:51:11 +00:00
|
|
|
error: function(request, status, error) {
|
|
|
|
// TODO: Handle error
|
|
|
|
console.log(`ERROR in getApiEndpointOptions at '${url}'`);
|
|
|
|
}
|
2021-06-23 10:07:56 +00:00
|
|
|
});
|
|
|
|
}
|
2021-06-23 10:30:26 +00:00
|
|
|
|
|
|
|
|
2021-06-23 11:41:19 +00:00
|
|
|
/*
|
|
|
|
* Request API OPTIONS data from the server,
|
|
|
|
* and construct a modal form based on the response.
|
|
|
|
*
|
|
|
|
* arguments:
|
|
|
|
* - method: The HTTP method e.g. 'PUT', 'POST', 'DELETE',
|
|
|
|
*
|
|
|
|
* options:
|
|
|
|
* - method:
|
|
|
|
*/
|
|
|
|
function constructForm(url, method, options={}) {
|
|
|
|
|
|
|
|
method = method.toUpperCase();
|
|
|
|
|
2021-06-23 13:51:11 +00:00
|
|
|
// Store the method in the options struct
|
|
|
|
options.method = method;
|
|
|
|
|
2021-06-23 11:41:19 +00:00
|
|
|
// Request OPTIONS endpoint from the API
|
|
|
|
getApiEndpointOptions(url, function(OPTIONS) {
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Determine what "type" of form we want to construct,
|
|
|
|
* based on the requested action.
|
|
|
|
*
|
|
|
|
* First we must determine if the user has the correct permissions!
|
|
|
|
*/
|
|
|
|
|
|
|
|
switch (method) {
|
|
|
|
case 'POST':
|
|
|
|
if (canCreate(OPTIONS)) {
|
2021-06-23 13:51:11 +00:00
|
|
|
constructCreateForm(url, OPTIONS.actions.POST, options);
|
2021-06-23 11:41:19 +00:00
|
|
|
} else {
|
|
|
|
// User does not have permission to POST to the endpoint
|
|
|
|
console.log('cannot POST');
|
|
|
|
// TODO
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'PUT':
|
|
|
|
case 'PATCH':
|
|
|
|
if (canChange(OPTIONS)) {
|
2021-06-23 13:51:11 +00:00
|
|
|
constructChangeForm(url, OPTIONS.actions.PUT, options);
|
2021-06-23 11:41:19 +00:00
|
|
|
} else {
|
|
|
|
// User does not have permission to PUT/PATCH to the endpoint
|
|
|
|
// TODO
|
|
|
|
console.log('cannot edit');
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'DELETE':
|
|
|
|
if (canDelete(OPTIONS)) {
|
|
|
|
console.log('delete');
|
|
|
|
} else {
|
|
|
|
// User does not have permission to DELETE to the endpoint
|
|
|
|
// TODO
|
|
|
|
console.log('cannot delete');
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 'GET':
|
|
|
|
if (canView(OPTIONS)) {
|
|
|
|
console.log('view');
|
|
|
|
} else {
|
|
|
|
// User does not have permission to GET to the endpoint
|
|
|
|
// TODO
|
|
|
|
console.log('cannot view');
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
console.log(`constructForm() called with invalid method '${method}'`);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
});
|
2021-06-23 12:11:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-06-23 14:16:30 +00:00
|
|
|
|
|
|
|
function constructFormBody(url, fields, options={}) {
|
2021-06-23 12:11:26 +00:00
|
|
|
|
|
|
|
var html = '';
|
|
|
|
|
2021-06-23 14:00:20 +00:00
|
|
|
var allowed_fields = options.fields || null;
|
|
|
|
var ignored_fields = options.ignore || [];
|
|
|
|
|
|
|
|
if (!ignored_fields.includes('pk')) {
|
|
|
|
ignored_fields.push('pk');
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ignored_fields.includes('id')) {
|
|
|
|
ignored_fields.push('id');
|
|
|
|
}
|
|
|
|
|
2021-06-23 14:06:27 +00:00
|
|
|
// Construct an ordered list of field names
|
|
|
|
var field_names = [];
|
|
|
|
|
|
|
|
if (allowed_fields) {
|
|
|
|
allowed_fields.forEach(function(name) {
|
2021-06-23 14:00:20 +00:00
|
|
|
|
2021-06-23 14:06:27 +00:00
|
|
|
// Only push names which are actually in the set of fields
|
|
|
|
if (name in fields) {
|
|
|
|
|
|
|
|
if (!ignored_fields.includes(name) && !field_names.includes(name)) {
|
|
|
|
field_names.push(name);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
console.log(`WARNING: '${name}' does not match a valid field name.`);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
for (const name in fields) {
|
|
|
|
|
|
|
|
if (!ignored_fields.includes(name) && !field_names.includes(name)) {
|
|
|
|
field_names.push(name);
|
|
|
|
}
|
2021-06-23 13:51:11 +00:00
|
|
|
}
|
2021-06-23 14:06:27 +00:00
|
|
|
}
|
|
|
|
|
2021-06-24 14:36:22 +00:00
|
|
|
// Render selected fields
|
|
|
|
|
2021-06-23 14:13:40 +00:00
|
|
|
for (var idx = 0; idx < field_names.length; idx++) {
|
|
|
|
|
|
|
|
var name = field_names[idx];
|
2021-06-23 13:51:11 +00:00
|
|
|
|
2021-06-23 14:06:27 +00:00
|
|
|
var field = fields[name];
|
2021-06-23 14:13:40 +00:00
|
|
|
|
|
|
|
// Skip field types which are simply not supported
|
|
|
|
switch (field.type) {
|
|
|
|
case 'nested object':
|
|
|
|
continue;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2021-06-23 14:06:27 +00:00
|
|
|
var f = constructField(name, field, options);
|
2021-06-23 12:25:53 +00:00
|
|
|
|
|
|
|
html += f;
|
2021-06-23 14:13:40 +00:00
|
|
|
}
|
2021-06-23 12:11:26 +00:00
|
|
|
|
2021-06-24 03:12:46 +00:00
|
|
|
// TODO: Dynamically create the modals,
|
|
|
|
// so that we can have an infinite number of stacks!
|
2021-06-23 12:11:26 +00:00
|
|
|
var modal = '#modal-form';
|
|
|
|
|
|
|
|
modalEnable(modal, true);
|
|
|
|
|
2021-06-24 03:12:46 +00:00
|
|
|
var title = options.title || '{% trans "Form Title" %}';
|
|
|
|
|
|
|
|
modalSetTitle(modal, title);
|
|
|
|
|
2021-06-24 14:36:22 +00:00
|
|
|
// Insert generated form content
|
2021-06-23 12:11:26 +00:00
|
|
|
$(modal).find('.modal-form-content').html(html);
|
|
|
|
|
|
|
|
$(modal).modal('show');
|
2021-06-23 12:59:27 +00:00
|
|
|
|
|
|
|
attachToggle(modal);
|
2021-06-23 13:36:38 +00:00
|
|
|
attachSelect(modal);
|
2021-06-24 03:12:46 +00:00
|
|
|
|
|
|
|
modalShowSubmitButton(modal, true);
|
2021-06-23 12:11:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-06-23 14:16:30 +00:00
|
|
|
/*
|
|
|
|
* Construct a 'creation' (POST) form, to create a new model in the database.
|
|
|
|
*
|
|
|
|
* arguments:
|
|
|
|
* - fields: The 'actions' object provided by the OPTIONS endpoint
|
|
|
|
*
|
|
|
|
* options:
|
|
|
|
* -
|
|
|
|
*/
|
|
|
|
function constructCreateForm(url, fields, options={}) {
|
|
|
|
|
|
|
|
// We should have enough information to create the form!
|
|
|
|
constructFormBody(url, fields, options);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-06-23 13:51:11 +00:00
|
|
|
/*
|
|
|
|
* Construct a 'change' (PATCH) form, to create a new model in the database.
|
|
|
|
*
|
|
|
|
* arguments:
|
|
|
|
* - fields: The 'actions' object provided by the OPTIONS endpoint
|
|
|
|
*
|
|
|
|
* options:
|
|
|
|
* -
|
|
|
|
*/
|
|
|
|
function constructChangeForm(url, fields, options={}) {
|
|
|
|
|
|
|
|
// Request existing data from the API endpoint
|
|
|
|
$.ajax({
|
|
|
|
url: url,
|
|
|
|
type: 'GET',
|
|
|
|
contentType: 'application/json',
|
|
|
|
dataType: 'json',
|
|
|
|
accepts: {
|
|
|
|
json: 'application/json',
|
|
|
|
},
|
|
|
|
success: function(data) {
|
|
|
|
|
|
|
|
// Push existing 'value' to each field
|
|
|
|
for (const field in data) {
|
|
|
|
|
|
|
|
if (field in fields) {
|
|
|
|
fields[field].value = data[field];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-23 14:16:30 +00:00
|
|
|
constructFormBody(url, fields, options);
|
2021-06-23 13:51:11 +00:00
|
|
|
},
|
|
|
|
error: function(request, status, error) {
|
|
|
|
// TODO: Handle error here
|
|
|
|
console.log(`ERROR in constructChangeForm at '${url}'`);
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-06-23 12:11:26 +00:00
|
|
|
/*
|
|
|
|
* Construct a single form 'field' for rendering in a form.
|
|
|
|
*
|
|
|
|
* arguments:
|
|
|
|
* - name: The 'name' of the field
|
|
|
|
* - parameters: The field parameters supplied by the DRF OPTIONS method
|
|
|
|
*
|
|
|
|
* options:
|
|
|
|
* -
|
|
|
|
*
|
|
|
|
* The function constructs a fieldset which mostly replicates django "crispy" forms:
|
|
|
|
*
|
|
|
|
* - Field name
|
|
|
|
* - Field <input> (depends on specified field type)
|
|
|
|
* - Field description (help text)
|
|
|
|
* - Field errors
|
|
|
|
*/
|
|
|
|
function constructField(name, parameters, options={}) {
|
|
|
|
|
|
|
|
var field_name = `id_${name}`;
|
|
|
|
|
2021-06-23 12:37:45 +00:00
|
|
|
var form_classes = 'form-group';
|
|
|
|
|
|
|
|
if (parameters.errors) {
|
|
|
|
form_classes += ' has-error';
|
|
|
|
}
|
|
|
|
|
|
|
|
var html = `<div id='div_${field_name}' class='${form_classes}'>`;
|
2021-06-23 12:11:26 +00:00
|
|
|
|
|
|
|
// Add a label
|
|
|
|
html += constructLabel(name, parameters);
|
|
|
|
|
|
|
|
html += `<div class='controls'>`;
|
2021-06-23 12:25:53 +00:00
|
|
|
|
2021-06-23 12:11:26 +00:00
|
|
|
html += constructInput(name, parameters, options);
|
2021-06-23 12:37:45 +00:00
|
|
|
|
|
|
|
if (parameters.errors) {
|
|
|
|
html += constructErrorMessage(name, parameters, options);
|
|
|
|
}
|
|
|
|
|
2021-06-23 12:25:53 +00:00
|
|
|
if (parameters.help_text) {
|
|
|
|
html += constructHelpText(name, parameters, options);
|
|
|
|
}
|
2021-06-23 12:37:45 +00:00
|
|
|
|
2021-06-23 12:11:26 +00:00
|
|
|
html += `</div>`; // controls
|
2021-06-23 12:25:53 +00:00
|
|
|
|
2021-06-23 12:11:26 +00:00
|
|
|
html += `</div>`; // form-group
|
2021-06-23 12:25:53 +00:00
|
|
|
|
2021-06-23 12:11:26 +00:00
|
|
|
return html;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Construct a 'label' div
|
|
|
|
*
|
|
|
|
* arguments:
|
|
|
|
* - name: The name of the field
|
|
|
|
* - required: Is this a required field?
|
|
|
|
*/
|
|
|
|
function constructLabel(name, parameters) {
|
|
|
|
|
|
|
|
var label_classes = 'control-label';
|
|
|
|
|
|
|
|
if (parameters.required) {
|
|
|
|
label_classes += ' requiredField';
|
|
|
|
}
|
|
|
|
|
2021-06-23 12:25:53 +00:00
|
|
|
var html = `<label class='${label_classes}' for='id_${name}'>`;
|
2021-06-23 12:11:26 +00:00
|
|
|
|
2021-06-23 12:25:53 +00:00
|
|
|
if (parameters.label) {
|
|
|
|
html += `${parameters.label}`;
|
|
|
|
} else {
|
|
|
|
html += `${name}`;
|
|
|
|
}
|
2021-06-23 12:11:26 +00:00
|
|
|
|
|
|
|
if (parameters.required) {
|
2021-06-23 12:25:53 +00:00
|
|
|
html += `<span class='asteriskField'>*</span>`;
|
2021-06-23 12:11:26 +00:00
|
|
|
}
|
2021-06-23 12:25:53 +00:00
|
|
|
|
2021-06-23 12:11:26 +00:00
|
|
|
html += `</label>`;
|
|
|
|
|
|
|
|
return html;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Construct a form input based on the field parameters
|
|
|
|
*
|
|
|
|
* arguments:
|
|
|
|
* - name: The name of the field
|
|
|
|
* - parameters: Field parameters returned by the OPTIONS method
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
function constructInput(name, parameters, options={}) {
|
|
|
|
|
|
|
|
var html = '';
|
|
|
|
|
2021-06-23 12:59:27 +00:00
|
|
|
var func = null;
|
|
|
|
|
|
|
|
switch (parameters.type) {
|
|
|
|
case 'boolean':
|
|
|
|
func = constructCheckboxInput;
|
|
|
|
break;
|
|
|
|
case 'string':
|
|
|
|
func = constructTextInput;
|
|
|
|
break;
|
|
|
|
case 'url':
|
2021-06-23 13:23:28 +00:00
|
|
|
func = constructTextInput;
|
2021-06-23 12:59:27 +00:00
|
|
|
break;
|
|
|
|
case 'email':
|
2021-06-23 13:27:02 +00:00
|
|
|
func = constructTextInput;
|
2021-06-23 12:59:27 +00:00
|
|
|
break;
|
|
|
|
case 'integer':
|
2021-06-23 13:18:36 +00:00
|
|
|
func = constructNumberInput;
|
2021-06-23 12:59:27 +00:00
|
|
|
break;
|
|
|
|
case 'float':
|
2021-06-23 13:18:36 +00:00
|
|
|
func = constructNumberInput;
|
2021-06-23 12:59:27 +00:00
|
|
|
break;
|
|
|
|
case 'decimal':
|
2021-06-23 13:18:36 +00:00
|
|
|
func = constructNumberInput;
|
2021-06-23 12:59:27 +00:00
|
|
|
break;
|
|
|
|
case 'choice':
|
2021-06-23 13:36:38 +00:00
|
|
|
func = constructChoiceInput;
|
2021-06-23 12:59:27 +00:00
|
|
|
break;
|
2021-06-24 14:36:22 +00:00
|
|
|
case 'related field':
|
|
|
|
func = constructRelatedFieldInput;
|
2021-06-23 12:59:27 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
// Unsupported field type!
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (func != null) {
|
|
|
|
html = func(name, parameters, options);
|
|
|
|
} else {
|
|
|
|
console.log(`WARNING: Unhandled form field type: '${parameters.type}'`);
|
|
|
|
}
|
2021-06-23 12:25:53 +00:00
|
|
|
|
2021-06-23 12:59:27 +00:00
|
|
|
return html;
|
|
|
|
}
|
2021-06-23 12:25:53 +00:00
|
|
|
|
2021-06-23 12:59:27 +00:00
|
|
|
|
2021-06-23 13:18:36 +00:00
|
|
|
// Construct a set of default input options which apply to all input types
|
|
|
|
function constructInputOptions(name, classes, type, parameters) {
|
2021-06-23 12:59:27 +00:00
|
|
|
|
2021-06-23 13:18:36 +00:00
|
|
|
var opts = [];
|
2021-06-23 12:59:27 +00:00
|
|
|
|
2021-06-23 13:18:36 +00:00
|
|
|
opts.push(`id='id_${name}'`);
|
2021-06-23 12:59:27 +00:00
|
|
|
|
2021-06-23 13:18:36 +00:00
|
|
|
opts.push(`class='${classes}'`);
|
2021-06-23 12:59:27 +00:00
|
|
|
|
2021-06-23 13:18:36 +00:00
|
|
|
opts.push(`name='${name}'`);
|
2021-06-23 12:59:27 +00:00
|
|
|
|
2021-06-23 13:18:36 +00:00
|
|
|
opts.push(`type='${type}'`);
|
|
|
|
|
2021-06-23 14:16:30 +00:00
|
|
|
// Read only?
|
|
|
|
if (parameters.read_only) {
|
|
|
|
opts.push(`readonly=''`);
|
|
|
|
}
|
|
|
|
|
2021-06-23 13:51:11 +00:00
|
|
|
if (parameters.value) {
|
2021-06-23 14:00:20 +00:00
|
|
|
// Existing value?
|
2021-06-23 13:51:11 +00:00
|
|
|
opts.push(`value='${parameters.value}'`);
|
2021-06-23 14:00:20 +00:00
|
|
|
} else if (parameters.default) {
|
|
|
|
// Otherwise, a defualt value?
|
|
|
|
opts.push(`value='${parameters.default}'`);
|
2021-06-23 13:51:11 +00:00
|
|
|
}
|
|
|
|
|
2021-06-23 13:18:36 +00:00
|
|
|
// Maximum input length
|
|
|
|
if (parameters.max_length) {
|
|
|
|
opts.push(`maxlength='${parameters.max_length}'`);
|
|
|
|
}
|
2021-06-23 12:59:27 +00:00
|
|
|
|
2021-06-23 13:18:36 +00:00
|
|
|
// Minimum input length
|
|
|
|
if (parameters.min_length) {
|
|
|
|
opts.push(`minlength='${parameters.min_length}'`);
|
|
|
|
}
|
2021-06-23 12:59:27 +00:00
|
|
|
|
2021-06-23 13:18:36 +00:00
|
|
|
// Maximum value
|
|
|
|
if (parameters.max_value != null) {
|
|
|
|
opts.push(`max='${parameters.max_value}'`);
|
|
|
|
}
|
2021-06-23 12:59:27 +00:00
|
|
|
|
2021-06-23 13:18:36 +00:00
|
|
|
// Minimum value
|
|
|
|
if (parameters.min_value != null) {
|
|
|
|
opts.push(`min='${parameters.min_value}'`);
|
2021-06-23 12:25:53 +00:00
|
|
|
}
|
|
|
|
|
2021-06-23 13:18:36 +00:00
|
|
|
// Field is required?
|
2021-06-23 12:59:27 +00:00
|
|
|
if (parameters.required) {
|
2021-06-23 13:18:36 +00:00
|
|
|
opts.push(`required=''`);
|
2021-06-23 12:59:27 +00:00
|
|
|
}
|
2021-06-23 12:37:45 +00:00
|
|
|
|
2021-06-23 13:18:36 +00:00
|
|
|
// Placeholder?
|
|
|
|
if (parameters.placeholder) {
|
|
|
|
opts.push(`placeholder='${parameters.placeholder}'`);
|
|
|
|
}
|
2021-06-23 12:11:26 +00:00
|
|
|
|
2021-06-23 13:18:36 +00:00
|
|
|
return `<input ${opts.join(' ')}>`;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Construct a "checkbox" input
|
|
|
|
function constructCheckboxInput(name, parameters, options={}) {
|
|
|
|
|
|
|
|
return constructInputOptions(
|
|
|
|
name,
|
|
|
|
'checkboxinput',
|
|
|
|
'checkbox',
|
|
|
|
parameters
|
|
|
|
);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Construct a "text" input
|
|
|
|
function constructTextInput(name, parameters, options={}) {
|
|
|
|
|
2021-06-23 13:23:28 +00:00
|
|
|
var classes = '';
|
|
|
|
var type = '';
|
|
|
|
|
|
|
|
switch (parameters.type) {
|
|
|
|
default:
|
|
|
|
classes = 'textinput textInput form-control';
|
|
|
|
type = 'text';
|
|
|
|
break;
|
|
|
|
case 'url':
|
|
|
|
classes = 'urlinput form-control';
|
|
|
|
type = 'url';
|
|
|
|
break;
|
2021-06-23 13:27:02 +00:00
|
|
|
case 'email':
|
|
|
|
classes = 'emailinput form-control';
|
|
|
|
type = 'email';
|
|
|
|
break;
|
2021-06-23 13:23:28 +00:00
|
|
|
}
|
|
|
|
|
2021-06-23 13:18:36 +00:00
|
|
|
return constructInputOptions(
|
|
|
|
name,
|
2021-06-23 13:23:28 +00:00
|
|
|
classes,
|
|
|
|
type,
|
2021-06-23 13:18:36 +00:00
|
|
|
parameters
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Construct a "number" field
|
|
|
|
function constructNumberInput(name, parameters, options={}) {
|
|
|
|
|
|
|
|
return constructInputOptions(
|
|
|
|
name,
|
|
|
|
'numberinput form-control',
|
|
|
|
'number',
|
|
|
|
parameters
|
|
|
|
);
|
2021-06-23 12:11:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-06-23 13:36:38 +00:00
|
|
|
// Construct a "choice" input
|
|
|
|
function constructChoiceInput(name, parameters, options={}) {
|
|
|
|
|
|
|
|
var html = `<select id='id_${name}' class='select form-control' name='${name}'>`;
|
|
|
|
|
|
|
|
var choices = parameters.choices || [];
|
|
|
|
|
|
|
|
// TODO: Select the selected value!
|
|
|
|
|
|
|
|
for (var idx = 0; idx < choices.length; idx++) {
|
|
|
|
|
|
|
|
var choice = choices[idx];
|
|
|
|
|
2021-06-23 13:51:11 +00:00
|
|
|
var selected = '';
|
|
|
|
|
|
|
|
if (parameters.value && parameters.value == choice.value) {
|
|
|
|
selected = ` selected=''`;
|
|
|
|
}
|
|
|
|
|
|
|
|
html += `<option value='${choice.value}'${selected}>`;
|
2021-06-23 13:36:38 +00:00
|
|
|
html += `${choice.display_name}`;
|
|
|
|
html += `</option>`;
|
|
|
|
}
|
|
|
|
|
|
|
|
html += `</select>`;
|
|
|
|
|
|
|
|
return html;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-06-24 14:36:22 +00:00
|
|
|
/*
|
|
|
|
* Construct a "related field" input.
|
|
|
|
* This will create a "select" input which will then, (after form is loaded),
|
|
|
|
* be converted into a select2 input.
|
|
|
|
* This will then be served custom data from the API (as required)...
|
|
|
|
*/
|
|
|
|
function constructRelatedFieldInput(name, parameters, options={}) {
|
|
|
|
|
|
|
|
var html = `<select id='id_${name}' class='select form-control' name='${name}'></select>`;
|
|
|
|
|
|
|
|
// Don't load any options - they will be filled via an AJAX request
|
|
|
|
|
|
|
|
return html;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-06-23 12:11:26 +00:00
|
|
|
/*
|
|
|
|
* Construct a 'help text' div based on the field parameters
|
|
|
|
*
|
|
|
|
* arguments:
|
|
|
|
* - name: The name of the field
|
|
|
|
* - parameters: Field parameters returned by the OPTIONS method
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
function constructHelpText(name, parameters, options={}) {
|
|
|
|
|
|
|
|
var html = `<div id='hint_id_${name}' class='help-block'>${parameters.help_text}</div>`;
|
|
|
|
|
|
|
|
return html;
|
2021-06-23 12:37:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Construct an 'error message' div for the field
|
|
|
|
*
|
|
|
|
* arguments:
|
|
|
|
* - name: The name of the field
|
|
|
|
* - parameters: Field parameters returned by the OPTIONS method
|
|
|
|
*/
|
|
|
|
function constructErrorMessage(name, parameters, options={}) {
|
|
|
|
|
|
|
|
var errors_html = '';
|
|
|
|
|
|
|
|
for (var idx = 0; idx < parameters.errors.length; idx++) {
|
|
|
|
|
|
|
|
var err = parameters.errors[idx];
|
|
|
|
|
|
|
|
var html = '';
|
|
|
|
|
|
|
|
html += `<span id='error_${idx+1}_id_${name}' class='help-block'>`;
|
|
|
|
html += `<strong>${err}</strong>`;
|
|
|
|
html += `</span>`;
|
|
|
|
|
|
|
|
errors_html += html;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return errors_html;
|
2021-06-23 11:41:19 +00:00
|
|
|
}
|