Merge pull request #7 from vincentmdealmeida/ValidationV2.3

Modified the polls view for an event to show not only the list of pol…
This commit is contained in:
vincentmdealmeida 2018-06-20 17:42:48 +01:00 committed by GitHub
commit e7440e6d6e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 117 additions and 69 deletions

View file

@ -45,8 +45,8 @@ class CreateNewEventModelAdaptor:
self.form_data = form_data.copy() self.form_data = form_data.copy()
self.user = user self.user = user
# TODO: Call validation func here (incl functionality for verifying CSRF + reCAPTCHA) # TODO: Call validation func here (incl functionality for verifying CSRF + reCAPTCHA)
print("Form Data:") #print("Form Data:")
print(self.form_data) #print(self.form_data)
self.__extractData() self.__extractData()

View file

@ -44,7 +44,7 @@
<div class="form-group"> <div class="form-group">
<label for="name-input" class="col-sm-3 col-md-2 control-label">Name:</label> <!-- This text can be a template variable --> <label for="name-input" class="col-sm-3 col-md-2 control-label">Name:</label> <!-- This text can be a template variable -->
<div class="col-sm-9 col-md-10"> <div class="col-sm-9 col-md-10">
<input type="text" class="form-control input-control" id="name-input" placeholder="Example: My poll" name="name-input" maxlength="255"> <input type="text" class="form-control input-control" id="name-input" placeholder="Example: EU Election" name="name-input" maxlength="255">
<span id="name-input-hint-block" class="help-block"> <span id="name-input-hint-block" class="help-block">
A short and clear name. A short and clear name.
</span> </span>
@ -57,7 +57,7 @@
<div class="form-group"> <div class="form-group">
<label for="identifier-input" class="col-sm-3 col-md-2 control-label">Identifier:</label> <!-- This text can be a template variable --> <label for="identifier-input" class="col-sm-3 col-md-2 control-label">Identifier:</label> <!-- This text can be a template variable -->
<div class="col-sm-9 col-md-10"> <div class="col-sm-9 col-md-10">
<input type="text" class="form-control input-control" id="identifier-input" placeholder="Example: My-poll" name="identifier-input" maxlength="255"> <input type="text" class="form-control input-control" id="identifier-input" placeholder="Example: eu-election" name="identifier-input" maxlength="255">
<span id="identifier-input-help-block" class="help-block"> <span id="identifier-input-help-block" class="help-block">
Used in the election URL, it must only consist of letters, numbers, underscores or hyphens; no whitespace is permitted. Used in the election URL, it must only consist of letters, numbers, underscores or hyphens; no whitespace is permitted.
</span> </span>

View file

@ -4,17 +4,25 @@
{% block event_nav_polls %}active{% endblock %} {% block event_nav_polls %}active{% endblock %}
{% block event_content %} {% block event_content %}
<h2>Related Polls</h2> <h2>Event Polls</h2>
{% if object.polls.all %} {% if object.polls.all %}
<ul class="list-group">
{% for poll in object.polls.all %} {% for poll in object.polls.all %}
<li class="list-group-item"><a href="{% url 'polls:view-poll' event_id=event.id poll_num=forloop.counter %}">{{ poll.question_text }}</a></li> <hr/>
<h3>Poll: {{ poll.question_text }} (<a href="{% url 'polls:view-poll' event_id=event.id poll_num=forloop.counter %}">Edit</a>)</h3>
<br/>
<h4>Poll Options:</h4>
<ul class="list-group">
{% for option in poll.options.all %}
<li class="list-group-item">{{ option.choice_text }}</li>
{% endfor %} {% endfor %}
</ul> </ul>
<p>Minimum Number of Option Selections: {{ poll.min_num_selections }}. Maximum Number of Option Selections: {{ poll.max_num_selections }}.</p>
{% endfor %}
{% else %} {% else %}
<p>No polls are available for this Event.</p> <p>No polls are available for this Event.</p>
{% endif %} {% endif %}
{% if is_organiser %} {% if is_organiser %}
<hr/>
<a href="{% url 'polls:create-poll' event.id %}" class="btn btn-default" role="button">Add Poll</a> <a href="{% url 'polls:create-poll' event.id %}" class="btn btn-default" role="button">Add Poll</a>
{% endif %} {% endif %}
{% endblock %} {% endblock %}

View file

@ -1,4 +1,5 @@
// Form submission and validation // Form submission and validation
var updateModes = {create: 1, update: 2, delete: 3};
var submitBtn = $("#submit-event-create"); var submitBtn = $("#submit-event-create");
var submitBtnLabel = "Create Event"; var submitBtnLabel = "Create Event";
var submitBtnWaitLabel = "Please wait..."; var submitBtnWaitLabel = "Please wait...";
@ -10,6 +11,8 @@ var generalErrorBlock = document.getElementById('all-errors-help-block');
var errors = []; var errors = [];
var create = true; var create = true;
var pollCount = 0; var pollCount = 0;
var pollIndex = 0;
var pollEditActive = false;
var numOfOpts = 2; var numOfOpts = 2;
function finalisePolls() { function finalisePolls() {
@ -278,12 +281,12 @@ function isPollQValid() {
var valid = true; var valid = true;
// Check question is valid // Check question is valid
var question = $('#question-name-input-' + pollCount).val(); var question = $('#question-name-input-' + pollIndex).val();
if(question === '') { if(question === '') {
checkAndAddError({ checkAndAddError({
error: "Question / Statement for the poll is blank.", error: "Question / Statement for the poll is blank.",
helpBlockId: "question-input-error-block-" + pollCount helpBlockId: "question-input-error-block-" + pollIndex
}); });
valid = false; valid = false;
@ -294,8 +297,8 @@ function isPollQValid() {
function isPollOptionsValid() { function isPollOptionsValid() {
var valid = true; var valid = true;
var optsInputs = $('.option-formset #option-name-input-' + pollCount); var optsInputs = $('.option-formset #option-name-input-' + pollIndex);
var helpBlockId = "options-input-error-block-" + pollCount; var helpBlockId = "options-input-error-block-" + pollIndex;
if(numOfOpts < 1) { if(numOfOpts < 1) {
checkAndAddError({ checkAndAddError({
@ -337,10 +340,10 @@ function isPollOptionsValid() {
function isMinMaxSelectionValid() { function isMinMaxSelectionValid() {
var valid = true; var valid = true;
var minInput = $('#minimum-input-' + pollCount); var minInput = $('#minimum-input-' + pollIndex);
var minInputMinAttr = parseInt(minInput[0].min); var minInputMinAttr = parseInt(minInput[0].min);
var minInputVal = minInput.val(); var minInputVal = minInput.val();
var helpBlockId = "selections-input-error-block-" + pollCount; var helpBlockId = "selections-input-error-block-" + pollIndex;
var errorStr = ""; var errorStr = "";
if(minInputVal === "" || minInputVal < minInputMinAttr) { if(minInputVal === "" || minInputVal < minInputMinAttr) {
@ -351,7 +354,7 @@ function isMinMaxSelectionValid() {
valid = false; valid = false;
} }
var maxInput = $('#maximum-input-' + pollCount); var maxInput = $('#maximum-input-' + pollIndex);
var maxInputMinAttr = parseInt(maxInput[0].min); var maxInputMinAttr = parseInt(maxInput[0].min);
var maxInputVal = maxInput.val(); var maxInputVal = maxInput.val();
@ -365,7 +368,7 @@ function isMinMaxSelectionValid() {
valid = false; valid = false;
} else if (maxInputVal > numOfOpts) { } else if (maxInputVal > numOfOpts) {
if (errorStr !== '') { if (errorStr !== '') {
errorStr = errorStr + " and the same applies to the maximum"; errorStr = errorStr + " and the maximum cannot be more than the number of options (" + numOfOpts + ")";
} else { } else {
errorStr = "The maximum option selection value (" + maxInputVal + ") cannot be more than the number of options (" + numOfOpts + ")"; errorStr = "The maximum option selection value (" + maxInputVal + ") cannot be more than the number of options (" + numOfOpts + ")";
} }
@ -803,14 +806,14 @@ function updateFormset(formset) { // Ported from DEMOS 1. Updates the row number
var forms = formset.children('.formset-form:not(.formset-form-empty, .formset-form-removed)'); var forms = formset.children('.formset-form:not(.formset-form-empty, .formset-form-removed)');
var removedForms = formset.children('.formset-form.formset-form-removed'); var removedForms = formset.children('.formset-form.formset-form-removed');
forms.each(function(index) { forms.each(function(index) {
updateForm($(this), index); updateForm($(this), index, updateModes.update);
}); });
removedForms.each(function(index) { removedForms.each(function(index) {
updateForm($(this), forms.length + index); updateForm($(this), forms.length + index, updateModes.delete);
}); });
} }
function updateForm(form, formIndex) { // Ported from DEMOS 1. function updateForm(form, formIndex, mode) { // Ported from DEMOS 1.
// Specific update for option forms // Specific update for option forms
var mayBeTextInput = form.find('input:text')[0]; var mayBeTextInput = form.find('input:text')[0];
if(mayBeTextInput !== undefined && mayBeTextInput.placeholder !== undefined) { if(mayBeTextInput !== undefined && mayBeTextInput.placeholder !== undefined) {
@ -825,6 +828,16 @@ function updateForm(form, formIndex) { // Ported from DEMOS 1.
var formset = form.parent('.formset'); var formset = form.parent('.formset');
var formsetPrefix = formset.attr('data-formset-prefix'); var formsetPrefix = formset.attr('data-formset-prefix');
if (formsetPrefix === 'polls' && mode === updateModes.update) {
// Get a reference to the fields that need updating from the form including the table
var formFields = form.find('.formset-form-fields:first >');
var table = form.find('.table:first');
// Perform the ID updates on the fields based on the poll index
performFormInputUpdates(formFields, table, formIndex);
}
var formPrefix = formsetPrefix + '-' + formIndex; var formPrefix = formsetPrefix + '-' + formIndex;
var formPrefixRegex = new RegExp(formsetPrefix + '-(?:__prefix__|\\d+)'); var formPrefixRegex = new RegExp(formsetPrefix + '-(?:__prefix__|\\d+)');
form.find('*').addBack().each(function(index, element) { form.find('*').addBack().each(function(index, element) {
@ -848,54 +861,61 @@ function manageTotalForms(formset, value) { // Ported from DEMOS1.
addButton.prop('disabled', parseInt(totalForms.val()) - removedForms.length >= parseInt(maxNumForms.val())); addButton.prop('disabled', parseInt(totalForms.val()) - removedForms.length >= parseInt(maxNumForms.val()));
} }
function updateQuestionFormInputs(form) { function updatePollFormInputs(form) {
// Update the name and IDs of all the dialog input fields // Obtain the cloned input fields for the dialog in order to update them
var clonedFields = form.find('.formset-form-fields:first >'); var clonedFields = form.find('.formset-form-fields:first >');
// Update the table ID // Obtain a reference to the options table
var table = form.find('.table:first'); var table = form.find('.table:first');
table.attr("id", "options-table-" + pollCount);
// Perform the ID updates on the fields based on the poll index
performFormInputUpdates(clonedFields, table, pollIndex);
}
function performFormInputUpdates(fields, table, index) {
// Update the table ID
table.attr("id", "options-table-" + index);
// Update the poll question / statement ID // Update the poll question / statement ID
clonedFields.find(".dialogQ:first") fields.find(".dialogQ:first")
.attr("id", "question-name-input-" + pollCount) .attr("id", "question-name-input-" + index)
.attr("name", "question-name-input-" + pollCount); .attr("name", "question-name-input-" + index);
// Update one of the help block IDs for various sections of the dialog // Update one of the help block IDs for various sections of the dialog
var pollQuestionErrorHelpBlock = clonedFields.find("#question-input-error-block"); var pollQuestionErrorHelpBlock = fields.find("#question-input-error-block");
pollQuestionErrorHelpBlock.attr("id", "question-input-error-block-" + pollCount); pollQuestionErrorHelpBlock.attr("id", "question-input-error-block-" + index);
var pollOptionsErrorHelpBlock = clonedFields.find("#options-input-error-block"); var pollOptionsErrorHelpBlock = fields.find("#options-input-error-block");
pollOptionsErrorHelpBlock.attr("id", "options-input-error-block-" + pollCount); pollOptionsErrorHelpBlock.attr("id", "options-input-error-block-" + index);
var pollSelectionsErrorHelpBlock = clonedFields.find("#selections-input-error-block"); var pollSelectionsErrorHelpBlock = fields.find("#selections-input-error-block");
pollSelectionsErrorHelpBlock.attr("id", "selections-input-error-block-" + pollCount); pollSelectionsErrorHelpBlock.attr("id", "selections-input-error-block-" + index);
// Update the poll option input IDs // Update the poll option input IDs
var optsInputs = clonedFields.find(".dialogO"); var optsInputs = fields.find(".dialogO");
for(var i = 0; i < optsInputs.length; i++) { for(var i = 0; i < optsInputs.length; i++) {
var input = optsInputs[i]; var input = optsInputs[i];
input.id = "option-name-input-" + pollCount; input.id = "option-name-input-" + index;
input.name = "option-name-input-" + pollCount; input.name = "option-name-input-" + index;
} }
// Update the data-formset-prefix for correct referencing // Update the data-formset-prefix for correct referencing
var dataFormsetPrefix = "options-" + pollCount; var dataFormsetPrefix = "options-" + index;
var optionFormSet = clonedFields.find(".option-formset"); var optionFormSet = fields.find(".option-formset");
optionFormSet.attr("data-formset-prefix", dataFormsetPrefix); optionFormSet.attr("data-formset-prefix", dataFormsetPrefix);
var addPollOptBtn = clonedFields.find('.formset-add'); var addPollOptBtn = fields.find('.formset-add');
addPollOptBtn.attr("data-formset-prefix", dataFormsetPrefix); addPollOptBtn.attr("data-formset-prefix", dataFormsetPrefix);
// Update the poll min and max selection // Update the poll min and max selection
clonedFields.find(".min-input:first") fields.find(".min-input:first")
.attr("id", "minimum-input-" + pollCount) .attr("id", "minimum-input-" + index)
.attr("name", "minimum-input-" + pollCount); .attr("name", "minimum-input-" + index);
clonedFields.find(".max-input:first") fields.find(".max-input:first")
.attr("id", "maximum-input-" + pollCount) .attr("id", "maximum-input-" + index)
.attr("name", "maximum-input-" + pollCount); .attr("name", "maximum-input-" + index);
} }
function isDialogFormValid() { function isDialogFormValid() {
@ -904,7 +924,7 @@ function isDialogFormValid() {
var minMaxSelValid = true; var minMaxSelValid = true;
// Check question is valid // Check question is valid
var pollQErrorHelpBlockId = "question-input-error-block-" + pollCount; var pollQErrorHelpBlockId = "question-input-error-block-" + pollIndex;
pollQValid = isPollQValid(); pollQValid = isPollQValid();
if(pollQValid === true) { if(pollQValid === true) {
@ -914,7 +934,7 @@ function isDialogFormValid() {
} }
// Check opts are valid // Check opts are valid
var pollOptsErrorHelpBlockId = "options-input-error-block-" + pollCount; var pollOptsErrorHelpBlockId = "options-input-error-block-" + pollIndex;
optsValid = isPollOptionsValid(); optsValid = isPollOptionsValid();
if(optsValid === true) { if(optsValid === true) {
@ -924,7 +944,7 @@ function isDialogFormValid() {
} }
// Check min and max selections are valid // Check min and max selections are valid
var pollSelErrorHelpBlockId = "selections-input-error-block-" + pollCount; var pollSelErrorHelpBlockId = "selections-input-error-block-" + pollIndex;
minMaxSelValid = isMinMaxSelectionValid(); minMaxSelValid = isMinMaxSelectionValid();
if(minMaxSelValid === true) { if(minMaxSelValid === true) {
@ -937,8 +957,8 @@ function isDialogFormValid() {
} }
function updateSelectionsMaxAtrr() { function updateSelectionsMaxAtrr() {
var minInput = $('#minimum-input-' + pollCount); var minInput = $('#minimum-input-' + pollIndex);
var maxInput = $('#maximum-input-' + pollCount); var maxInput = $('#maximum-input-' + pollIndex);
// Get the vals from the selection inputs and update them if they exceed the new max // Get the vals from the selection inputs and update them if they exceed the new max
var minInputVal = minInput.val(); var minInputVal = minInput.val();
@ -967,23 +987,29 @@ $('.formset-add').click(function (e) { // Ported from DEMOS1
switch (formsetPrefix) { switch (formsetPrefix) {
case "polls": case "polls":
// Update the IDs and names of all of the cloned input form fields based on the number of polls // Set the index
updateQuestionFormInputs(form); pollIndex = pollCount;
// 2 is the default number shown upon the launch of the dialog // Update the IDs and names of all of the cloned input form fields based on the number of polls
updatePollFormInputs(form);
// 2 is the default number of opts shown upon the launch of the dialog
numOfOpts = 2; numOfOpts = 2;
// New poll is being created so edit mode hasn't been activated
pollEditActive = false;
break; break;
case "options-" + pollCount: case "options-" + pollIndex:
numOfOpts++; numOfOpts++;
updateSelectionsMaxAtrr(); updateSelectionsMaxAtrr();
clearError("options-input-error-block-" + pollCount); clearError("options-input-error-block-" + pollIndex);
break; break;
} }
var formIndex = formset.children('.formset-form:not(.formset-form-empty)').length; var formIndex = formset.children('.formset-form:not(.formset-form-empty)').length;
formset.append(form); formset.append(form);
updateForm(form, formIndex); updateForm(form, formIndex, updateModes.create);
emptyFormCheckedInputs.each(function (index) { emptyFormCheckedInputs.each(function (index) {
$(this).prop('checked', true); $(this).prop('checked', true);
}); });
@ -1010,19 +1036,23 @@ $('.formset-form-remove').click(function (e) { // Ported from DEMOS1
form.remove(); form.remove();
manageTotalForms(formset, -1); manageTotalForms(formset, -1);
} }
// We need to reduce the poll count if we've removed a poll
if(formPrefix === "poll") {
pollCount--;
}
// Update the formset and inform that a form has been removed
updateFormset(formset); updateFormset(formset);
formset.trigger('formsetFormRemoved'); formset.trigger('formsetFormRemoved');
// Perform validation now that a row has been removed // Perform validation and other operations now that a row has been removed based on the affected table
switch (formPrefix) { switch (formPrefix) {
case 'poll':
// TODO: A poll has been removed so we need to update the poll count
break;
case 'option': case 'option':
// Decrement the number of total options and validate the options list // Decrement the number of total options and validate the options list
numOfOpts--; numOfOpts--;
updateSelectionsMaxAtrr(); updateSelectionsMaxAtrr();
validateFormField(isPollOptionsValid, "options-input-error-block-" + pollCount); validateFormField(isPollOptionsValid, "options-input-error-block-" + pollIndex);
break; break;
case 'organiser': case 'organiser':
validateFormField(areOrganisersEmailsValid, "organisers-input-error-block"); validateFormField(areOrganisersEmailsValid, "organisers-input-error-block");
@ -1037,25 +1067,35 @@ $('.formset-form-save').click(function (e) {
var dialogValid = isDialogFormValid(); var dialogValid = isDialogFormValid();
if(dialogValid === true) { if(dialogValid === true) {
// TODO: Clear errors
var modal = $(this).closest('.modal'); var modal = $(this).closest('.modal');
var form = modal.data('form'); var form = modal.data('form');
var name = $('#question-name-input-' + pollCount).val(); var name = $('#question-name-input-' + pollIndex).val();
form.find('.formset-form-name:first').text(name); form.find('.formset-form-name:first').text(name);
modal.data('formSave', true); modal.data('formSave', true);
modal.modal('hide'); modal.modal('hide');
if(!pollEditActive) {
// Increment the poll count and clear any validation errors // Increment the poll count and clear any validation errors
pollCount++; pollCount++;
clearError("polls-input-error-block");
} else { } else {
//highlightErrors(); pollEditActive = false;
}
clearError("polls-input-error-block");
} }
}); });
function extractPollIndexFromId(id) {
var idSplitArray = id.split('-');
pollIndex = parseInt(idSplitArray[3]);
}
$('.formset-form-edit').click(function (e) { $('.formset-form-edit').click(function (e) {
var form = $(this).closest('.formset-form'); var form = $(this).closest('.formset-form');
var questionNameInput = form.find('.formset-form-fields:first > .dialogFormField > .dialogQ');
extractPollIndexFromId(questionNameInput.attr('id'));
$('#formset-modal').data('form', form).modal('show'); $('#formset-modal').data('form', form).modal('show');
pollEditActive = true;
}); });
$('#formset-modal').on('show.bs.modal', function (e) { // Ported from DEMOS1 $('#formset-modal').on('show.bs.modal', function (e) { // Ported from DEMOS1
@ -1074,7 +1114,7 @@ $('#formset-modal').on('show.bs.modal', function (e) { // Ported from DEMOS1
formset.trigger('formsetModalShow', [modalBody]); formset.trigger('formsetModalShow', [modalBody]);
// Attach an event handler for poll option row sorting // Attach an event handler for poll option row sorting
$("#options-table-" + pollCount).sortable({ $("#options-table-" + pollIndex).sortable({
items: "tr", items: "tr",
update: update update: update
}); });