Merge pull request #3 from vincentmdealmeida/ValidationV2
Added full input validation to the Create Event including unique even…
This commit is contained in:
commit
6fc6628995
4 changed files with 613 additions and 106 deletions
|
@ -1,17 +1,12 @@
|
||||||
import os
|
|
||||||
from io import StringIO
|
from io import StringIO
|
||||||
from django.shortcuts import render
|
|
||||||
from django.forms import inlineformset_factory, formset_factory
|
|
||||||
from django.contrib.auth.decorators import user_passes_test
|
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.http import HttpResponseRedirect, HttpResponse, Http404
|
from django.http import HttpResponseRedirect, HttpResponse, Http404
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from django.shortcuts import get_object_or_404, render, render_to_response
|
from django.shortcuts import get_object_or_404, render, render_to_response
|
||||||
from django.template import RequestContext
|
|
||||||
from django.core.exceptions import ObjectDoesNotExist
|
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.views import generic
|
from django.views import generic
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.core import serializers
|
||||||
|
|
||||||
from .forms import EventForm, PollForm, OptionFormset, QuestionFormset, OrganiserFormSet, TrusteeFormSet, VoteForm, EventSetupForm, EventEditForm, DecryptionFormset, DecryptionFormSetHelper
|
from .forms import EventForm, PollForm, OptionFormset, QuestionFormset, OrganiserFormSet, TrusteeFormSet, VoteForm, EventSetupForm, EventEditForm, DecryptionFormset, DecryptionFormSetHelper
|
||||||
from .models import Event, Poll, PollOption, EmailUser, Ballot, TrusteeKey, Decryption
|
from .models import Event, Poll, PollOption, EmailUser, Ballot, TrusteeKey, Decryption
|
||||||
|
@ -304,21 +299,12 @@ def create_event(request):
|
||||||
|
|
||||||
return HttpResponseRedirect("/event/")
|
return HttpResponseRedirect("/event/")
|
||||||
elif request.method == "GET":
|
elif request.method == "GET":
|
||||||
#form = EventForm()
|
# Obtain context data for the rendering of the html template
|
||||||
#organiser_formset = OrganiserFormSet(prefix="formset_organiser", initial=[{'email': request.user.email }])
|
events = Event.objects.all()
|
||||||
#trustee_formset = TrusteeFormSet(prefix="formset_trustee", initial=[{'email': request.user.email }])
|
demo_users = DemoUser.objects.all()
|
||||||
# Create the formset, specifying the form and formset we want to use.
|
|
||||||
'''return render(request,
|
|
||||||
"polls/create_event.html",
|
|
||||||
{
|
|
||||||
"event": event,
|
|
||||||
"form": form,
|
|
||||||
"organiser_formset": organiser_formset,
|
|
||||||
"trustee_formset": trustee_formset,
|
|
||||||
"G_R_SITE_KEY": settings.RECAPTCHA_PUBLIC_KEY
|
|
||||||
})'''
|
|
||||||
|
|
||||||
return render(request, "polls/create_event.html", {"G_R_SITE_KEY": settings.RECAPTCHA_PUBLIC_KEY, "user_email": request.user.email})
|
# Render the template
|
||||||
|
return render(request, "polls/create_event.html", {"G_R_SITE_KEY": settings.RECAPTCHA_PUBLIC_KEY, "user_email": request.user.email, "events": events, "demo_users": demo_users})
|
||||||
else:
|
else:
|
||||||
return HttpResponseNotAllowed()
|
return HttpResponseNotAllowed()
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,29 @@
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
// This events list is generated for later input validation
|
||||||
|
var events_list = [
|
||||||
|
{% for event in events %}
|
||||||
|
{% if not forloop.first %},{% endif %}
|
||||||
|
{
|
||||||
|
title: "{{ event.title }}",
|
||||||
|
slug: "{{ event.EID }}"
|
||||||
|
}
|
||||||
|
{% endfor %}
|
||||||
|
];
|
||||||
|
|
||||||
|
// This list of demo user emails will be used for input validation
|
||||||
|
var user_emails = [
|
||||||
|
{% for user in demo_users %}
|
||||||
|
{% if not forloop.first %},{% endif %}
|
||||||
|
{
|
||||||
|
email: "{{ user.email }}"
|
||||||
|
}
|
||||||
|
{% endfor %}
|
||||||
|
];
|
||||||
|
</script>
|
||||||
|
|
||||||
<!-- The following UI was ported from the Election Authority UI in DEMOS1 by Vincent de Almeida -->
|
<!-- The following UI was ported from the Election Authority UI in DEMOS1 by Vincent de Almeida -->
|
||||||
<!-- The DEMOS1 repository can be found at: https://github.com/mlevogiannis/demos-voting -->
|
<!-- The DEMOS1 repository can be found at: https://github.com/mlevogiannis/demos-voting -->
|
||||||
<!-- TODO: look into i18n translations as this was a feature implemented in DEMOS1 -->
|
<!-- TODO: look into i18n translations as this was a feature implemented in DEMOS1 -->
|
||||||
|
@ -19,31 +42,33 @@
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<!-- TODO: Have not imported the if form not valid template code as this needs further investigating -->
|
<!-- TODO: Have not imported the if form not valid template code as this needs further investigating -->
|
||||||
<!-- Name -->
|
<!-- Name -->
|
||||||
<div class="form-group"> <!-- Excluded class(missing %s): { if election_form.name.errors }has-error{ endif } -->
|
<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: My poll" name="name-input" maxlength="255">
|
||||||
<span id="name-input-help-block" class="help-block">
|
<span id="name-input-hint-block" class="help-block">
|
||||||
<!-- Error handling / input validation has been removed temporarily and would be placed here -->
|
|
||||||
A short and clear name.
|
A short and clear name.
|
||||||
<!-- TODO: Alignment is potentially slightly too much to the left -->
|
</span>
|
||||||
|
<span id="name-input-error-block" class="help-block errorText">
|
||||||
|
<!-- Errors flagged here -->
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- Slug / Identifier -->
|
<!-- Slug / Identifier -->
|
||||||
<div class="form-group"> <!-- Excluded class(missing %s): { if election_form.slug.errors }has-error{ endif } -->
|
<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: My-poll" name="identifier-input" maxlength="255">
|
||||||
<span id="identifier-input-help-block" class="help-block">
|
<span id="identifier-input-help-block" class="help-block">
|
||||||
<!-- Error handling / input validation has been removed temporarily and would be placed here -->
|
|
||||||
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.
|
||||||
<!-- TODO: Alignment is potentially slightly too much to the left -->
|
</span>
|
||||||
|
<span id="identifier-input-error-block" class="help-block errorText">
|
||||||
|
<!-- Errors flagged here -->
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- Voting start time -->
|
<!-- Voting start time -->
|
||||||
<div class="form-group "> <!-- Excluded class(missing %s): { if election_form.voting_starts_at.errors }has-error{ endif } -->
|
<div class="form-group ">
|
||||||
<label for="vote-start-input" class="col-sm-3 col-md-2 control-label">Voting starts at:</label>
|
<label for="vote-start-input" class="col-sm-3 col-md-2 control-label">Voting starts at:</label>
|
||||||
<div class="col-sm-9 col-md-10">
|
<div class="col-sm-9 col-md-10">
|
||||||
<div class="input-group date">
|
<div class="input-group date">
|
||||||
|
@ -55,13 +80,15 @@
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<span id="vote-start-input-help-block" class="help-block">
|
<span id="vote-start-input-help-block" class="help-block">
|
||||||
<!-- Error handling / input validation has been removed temporarily and would be placed here -->
|
|
||||||
Date and time when registered voters can commence voting. This includes the UTC offset starting with '+'.
|
Date and time when registered voters can commence voting. This includes the UTC offset starting with '+'.
|
||||||
</span>
|
</span>
|
||||||
|
<span id="vote-start-input-error-block" class="help-block errorText">
|
||||||
|
<!-- Errors flagged here -->
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- Voting end time -->
|
<!-- Voting end time -->
|
||||||
<div class="form-group "> <!-- Excluded class(missing %s): { if election_form.voting_ends_at.errors }has-error{ endif } -->
|
<div class="form-group ">
|
||||||
<label for="vote-end-input" class="col-sm-3 col-md-2 control-label">Voting ends at:</label>
|
<label for="vote-end-input" class="col-sm-3 col-md-2 control-label">Voting ends at:</label>
|
||||||
<div class="col-sm-9 col-md-10">
|
<div class="col-sm-9 col-md-10">
|
||||||
<div class="input-group date">
|
<div class="input-group date">
|
||||||
|
@ -73,9 +100,11 @@
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<span id="vote-end-input-help-block" class="help-block">
|
<span id="vote-end-input-help-block" class="help-block">
|
||||||
<!-- Error handling / input validation has been removed temporarily and would be placed here -->
|
|
||||||
Date and time when registered voters can no longer vote. This includes the UTC offset starting with '+'.
|
Date and time when registered voters can no longer vote. This includes the UTC offset starting with '+'.
|
||||||
</span>
|
</span>
|
||||||
|
<span id="vote-end-input-error-block" class="help-block errorText">
|
||||||
|
<!-- Errors flagged here -->
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- Question / Statement -->
|
<!-- Question / Statement -->
|
||||||
|
@ -84,9 +113,10 @@
|
||||||
<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="question-input" placeholder="Example: Elections for the European Parliament" name="question-input" maxlength="200">
|
<input type="text" class="form-control input-control" id="question-input" placeholder="Example: Elections for the European Parliament" name="question-input" maxlength="200">
|
||||||
<span id="question-input-help-block" class="help-block">
|
<span id="question-input-help-block" class="help-block">
|
||||||
<!-- Error handling / input validation has been removed temporarily and would be placed here -->
|
|
||||||
Question / Statement that will be put forward to voters along with the below options.
|
Question / Statement that will be put forward to voters along with the below options.
|
||||||
<!-- TODO: Alignment is potentially slightly too much to the left -->
|
</span>
|
||||||
|
<span id="question-input-error-block" class="help-block errorText">
|
||||||
|
<!-- Errors flagged here -->
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -94,14 +124,13 @@
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="options-input" class="col-sm-3 col-md-2 control-label">Options:</label> <!-- This text can be a template variable -->
|
<label for="options-input" class="col-sm-3 col-md-2 control-label">Options:</label> <!-- This text can be a template variable -->
|
||||||
<div class="col-sm-9 col-md-10">
|
<div class="col-sm-9 col-md-10">
|
||||||
<div class="form-group"> <!-- Excluded class(missing %s): { if option_formset.non_form_errors }has-error{ endif }-->
|
<div class="form-group">
|
||||||
<table id="options-input-table" class="table table-hover">
|
<table id="options-input-table" class="table table-hover">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th class="text-center">#</th>
|
<th class="text-center">#</th>
|
||||||
<th>Option</th>
|
<th>Option</th>
|
||||||
<th class="text-center">Actions</th>
|
<th class="text-center">Actions</th>
|
||||||
<!--Not sure what this does so disabling it: <th class="hidden">{ option_formset.management_form }</th> -->
|
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody id="sort" class="formset option-formset" data-formset-prefix="questions" data-formset-type="inline">
|
<tbody id="sort" class="formset option-formset" data-formset-prefix="questions" data-formset-type="inline">
|
||||||
|
@ -113,10 +142,9 @@
|
||||||
</th>
|
</th>
|
||||||
<!-- Option Label -->
|
<!-- Option Label -->
|
||||||
<td>
|
<td>
|
||||||
<div> <!-- Has error conditional class removed -->
|
<div>
|
||||||
<!-- TODO: Add an invisible screen reader label to associate with this and other inputs -->
|
<!-- TODO: Add an invisible screen reader label to associate with this and other inputs -->
|
||||||
<input type="text" class="form-control input-sm input-control" placeholder="Example: Candidate 1" id="option-name-input" name="option-name-input" maxlength="200">
|
<input type="text" class="form-control input-sm input-control" placeholder="Example: Candidate 1" id="option-name-input" name="option-name-input" maxlength="200">
|
||||||
<!-- Error handling / input validation has been removed temporarily and would be placed here -->
|
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<!-- Delete Action -->
|
<!-- Delete Action -->
|
||||||
|
@ -134,10 +162,9 @@
|
||||||
</th>
|
</th>
|
||||||
<!-- Option Label -->
|
<!-- Option Label -->
|
||||||
<td>
|
<td>
|
||||||
<div> <!-- Has error conditional class removed -->
|
<div>
|
||||||
<!-- TODO: Add an invisible screen reader label to associate with this and other inputs -->
|
<!-- TODO: Add an invisible screen reader label to associate with this and other inputs -->
|
||||||
<input type="text" class="form-control input-sm input-control" placeholder="Example: Candidate 2" id="option-name-input" name="option-name-input" maxlength="200">
|
<input type="text" class="form-control input-sm input-control" placeholder="Example: Candidate 2" id="option-name-input" name="option-name-input" maxlength="200">
|
||||||
<!-- Error handling / input validation has been removed temporarily and would be placed here -->
|
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<!-- Delete Action -->
|
<!-- Delete Action -->
|
||||||
|
@ -155,10 +182,9 @@
|
||||||
</th>
|
</th>
|
||||||
<!-- Option Label -->
|
<!-- Option Label -->
|
||||||
<td>
|
<td>
|
||||||
<div> <!-- Has error conditional class removed -->
|
<div>
|
||||||
<!-- TODO: Add an invisible screen reader label to associate with this and other inputs -->
|
<!-- TODO: Add an invisible screen reader label to associate with this and other inputs -->
|
||||||
<input type="text" class="form-control input-sm input-control" placeholder="Example: Candidate X" id="option-name-input" name="option-name-input" maxlength="200">
|
<input type="text" class="form-control input-sm input-control" placeholder="Example: Candidate X" id="option-name-input" name="option-name-input" maxlength="200">
|
||||||
<!-- Error handling / input validation has been removed temporarily and would be placed here -->
|
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<!-- Delete Action -->
|
<!-- Delete Action -->
|
||||||
|
@ -177,9 +203,11 @@
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<span id="question-input-help-block" class="help-block">
|
<span id="question-input-help-block" class="help-block">
|
||||||
<!-- Error handling / input validation has been removed temporarily and would be placed here -->
|
|
||||||
Drag and drop to re-order options.
|
Drag and drop to re-order options.
|
||||||
</span>
|
</span>
|
||||||
|
<span id="options-input-error-block" class="help-block errorText">
|
||||||
|
<!-- Errors flagged here -->
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -191,19 +219,18 @@
|
||||||
<div class="col-xs-6">
|
<div class="col-xs-6">
|
||||||
<label class="sr-only" for="minimum-input">Minimum</label>
|
<label class="sr-only" for="minimum-input">Minimum</label>
|
||||||
<input type="number" class="form-control input-control" id="minimum-input" placeholder="Minimum" value="" name="minimum-input" min="0"> <!-- TODO: Max should be set to the number of options -->
|
<input type="number" class="form-control input-control" id="minimum-input" placeholder="Minimum" value="" name="minimum-input" min="0"> <!-- TODO: Max should be set to the number of options -->
|
||||||
<!-- Error handling / input validation has been removed temporarily and would be placed here -->
|
|
||||||
</div>
|
</div>
|
||||||
<div class="col-xs-6">
|
<div class="col-xs-6">
|
||||||
<label class="sr-only" for="maximum-input">Maximum</label>
|
<label class="sr-only" for="maximum-input">Maximum</label>
|
||||||
<input type="number" class="form-control input-control" id="maximum-input" placeholder="Maximum" value="" name="maximum-input" min="1"> <!-- TODO: Max should be set to the number of options -->
|
<input type="number" class="form-control input-control" id="maximum-input" placeholder="Maximum" value="" name="maximum-input" min="1"> <!-- TODO: Max should be set to the number of options -->
|
||||||
<!-- Error handling / input validation has been removed temporarily and would be placed here -->
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<span id="question-input-help-block" class="help-block">
|
<span id="question-input-help-block" class="help-block">
|
||||||
<!-- Error handling / input validation has been removed temporarily and would be placed here -->
|
|
||||||
Minimum and maximum number of option selections that a voter can make for the specified question / statement.
|
Minimum and maximum number of option selections that a voter can make for the specified question / statement.
|
||||||
<!-- TODO: Alignment is potentially slightly too much to the left -->
|
|
||||||
</span>
|
</span>
|
||||||
|
<span id="selections-input-error-block" class="help-block errorText">
|
||||||
|
<!-- Errors flagged here -->
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- Organisers -->
|
<!-- Organisers -->
|
||||||
|
@ -217,7 +244,6 @@
|
||||||
<th class="text-center">#</th>
|
<th class="text-center">#</th>
|
||||||
<th>Email</th>
|
<th>Email</th>
|
||||||
<th class="text-center">Actions</th>
|
<th class="text-center">Actions</th>
|
||||||
<!--Not sure what this does so disabling it: <th class="hidden">{ option_formset.management_form }</th> -->
|
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody class="formset organiser-formset" data-formset-prefix="organisers" data-formset-type="inline">
|
<tbody class="formset organiser-formset" data-formset-prefix="organisers" data-formset-type="inline">
|
||||||
|
@ -229,10 +255,9 @@
|
||||||
</th>
|
</th>
|
||||||
<td>
|
<td>
|
||||||
<!-- Email -->
|
<!-- Email -->
|
||||||
<div> <!-- Has error conditional class removed -->
|
<div>
|
||||||
<!-- TODO: Add an invisible screen reader label to associate with this and other inputs -->
|
<!-- TODO: Add an invisible screen reader label to associate with this and other inputs -->
|
||||||
<input type="text" class="form-control input-sm input-control" placeholder="Example: organiser@example.com" id="organiser-email-input" name="organiser-email-input" value="{{ user_email }}" maxlength="255">
|
<input type="text" class="form-control input-sm input-control" placeholder="Example: organiser@example.com" id="organiser-email-input" name="organiser-email-input" value="{{ user_email }}" maxlength="255">
|
||||||
<!-- Error handling / input validation has been removed temporarily and would be placed here -->
|
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td class="formset-form-actions text-center">
|
<td class="formset-form-actions text-center">
|
||||||
|
@ -250,10 +275,9 @@
|
||||||
</th>
|
</th>
|
||||||
<td>
|
<td>
|
||||||
<!-- Email -->
|
<!-- Email -->
|
||||||
<div> <!-- Has error conditional class removed -->
|
<div>
|
||||||
<!-- TODO: Add an invisible screen reader label to associate with this and other inputs -->
|
<!-- TODO: Add an invisible screen reader label to associate with this and other inputs -->
|
||||||
<input type="text" class="form-control input-sm input-control" placeholder="Example: organiser@example.com" id="organiser-email-input" name="organiser-email-input" maxlength="255">
|
<input type="text" class="form-control input-sm input-control" placeholder="Example: organiser@example.com" id="organiser-email-input" name="organiser-email-input" maxlength="255">
|
||||||
<!-- Error handling / input validation has been removed temporarily and would be placed here -->
|
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td class="formset-form-actions text-center">
|
<td class="formset-form-actions text-center">
|
||||||
|
@ -270,10 +294,9 @@
|
||||||
</th>
|
</th>
|
||||||
<td>
|
<td>
|
||||||
<!-- Email -->
|
<!-- Email -->
|
||||||
<div> <!-- Has error conditional class removed -->
|
<div>
|
||||||
<!-- TODO: Add an invisible screen reader label to associate with this and other inputs -->
|
<!-- TODO: Add an invisible screen reader label to associate with this and other inputs -->
|
||||||
<input type="text" class="form-control input-sm input-control" placeholder="Example: organiser@example.com" id="organiser-email-input" name="organiser-email-input" maxlength="255">
|
<input type="text" class="form-control input-sm input-control" placeholder="Example: organiserX@example.com" id="organiser-email-input" name="organiser-email-input" maxlength="255">
|
||||||
<!-- Error handling / input validation has been removed temporarily and would be placed here -->
|
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td class="formset-form-actions text-center">
|
<td class="formset-form-actions text-center">
|
||||||
|
@ -291,9 +314,11 @@
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<span id="question-input-help-block" class="help-block">
|
<span id="question-input-help-block" class="help-block">
|
||||||
<!-- Error handling / input validation has been removed temporarily and would be placed here -->
|
|
||||||
Drag and drop to re-order emails.
|
Drag and drop to re-order emails.
|
||||||
</span>
|
</span>
|
||||||
|
<span id="organisers-input-error-block" class="help-block errorText">
|
||||||
|
<!-- Errors flagged here -->
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -308,7 +333,6 @@
|
||||||
<th class="text-center">#</th>
|
<th class="text-center">#</th>
|
||||||
<th>Email</th>
|
<th>Email</th>
|
||||||
<th class="text-center">Actions</th>
|
<th class="text-center">Actions</th>
|
||||||
<!--Not sure what this does so disabling it: <th class="hidden">{ option_formset.management_form }</th> -->
|
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody class="formset trustee-formset" data-formset-prefix="trustees" data-formset-type="inline">
|
<tbody class="formset trustee-formset" data-formset-prefix="trustees" data-formset-type="inline">
|
||||||
|
@ -323,7 +347,6 @@
|
||||||
<div> <!-- Has error conditional class removed -->
|
<div> <!-- Has error conditional class removed -->
|
||||||
<!-- TODO: Add an invisible screen reader label to associate with this and other inputs -->
|
<!-- TODO: Add an invisible screen reader label to associate with this and other inputs -->
|
||||||
<input type="text" class="form-control input-sm input-control" placeholder="Example: trustee@example.com" id="trustee-email-input" name="trustee-email-input" value="{{ user_email }}" maxlength="255">
|
<input type="text" class="form-control input-sm input-control" placeholder="Example: trustee@example.com" id="trustee-email-input" name="trustee-email-input" value="{{ user_email }}" maxlength="255">
|
||||||
<!-- Error handling / input validation has been removed temporarily and would be placed here -->
|
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td class="formset-form-actions text-center">
|
<td class="formset-form-actions text-center">
|
||||||
|
@ -344,7 +367,6 @@
|
||||||
<div> <!-- Has error conditional class removed -->
|
<div> <!-- Has error conditional class removed -->
|
||||||
<!-- TODO: Add an invisible screen reader label to associate with this and other inputs -->
|
<!-- TODO: Add an invisible screen reader label to associate with this and other inputs -->
|
||||||
<input type="text" class="form-control input-sm input-control" placeholder="Example: trustee@example.com" id="trustee-email-input" name="trustee-email-input" maxlength="255">
|
<input type="text" class="form-control input-sm input-control" placeholder="Example: trustee@example.com" id="trustee-email-input" name="trustee-email-input" maxlength="255">
|
||||||
<!-- Error handling / input validation has been removed temporarily and would be placed here -->
|
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td class="formset-form-actions text-center">
|
<td class="formset-form-actions text-center">
|
||||||
|
@ -363,8 +385,7 @@
|
||||||
<!-- Email -->
|
<!-- Email -->
|
||||||
<div> <!-- Has error conditional class removed -->
|
<div> <!-- Has error conditional class removed -->
|
||||||
<!-- TODO: Add an invisible screen reader label to associate with this and other inputs -->
|
<!-- TODO: Add an invisible screen reader label to associate with this and other inputs -->
|
||||||
<input type="text" class="form-control input-sm input-control" placeholder="Example: trustee@example.com" id="trustee-email-input" name="trustee-email-input" maxlength="255">
|
<input type="text" class="form-control input-sm input-control" placeholder="Example: trusteeX@example.com" id="trustee-email-input" name="trustee-email-input" maxlength="255">
|
||||||
<!-- Error handling / input validation has been removed temporarily and would be placed here -->
|
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td class="formset-form-actions text-center">
|
<td class="formset-form-actions text-center">
|
||||||
|
@ -381,10 +402,12 @@
|
||||||
Add Trustee Email
|
Add Trustee Email
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<span id="question-input-help-block" class="help-block">
|
<span id="trustees-input-help-block" class="help-block">
|
||||||
<!-- Error handling / input validation has been removed temporarily and would be placed here -->
|
|
||||||
Drag and drop to re-order emails.
|
Drag and drop to re-order emails.
|
||||||
</span>
|
</span>
|
||||||
|
<span id="trustees-input-error-block" class="help-block errorText">
|
||||||
|
<!-- Errors flagged here -->
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -394,15 +417,16 @@
|
||||||
<div class="col-sm-9 col-md-10">
|
<div class="col-sm-9 col-md-10">
|
||||||
<textarea class="form-control input-control" id="voters-list-input" placeholder="alice@example.com, bob@example.com..." name="voters-list-input" rows="4"></textarea>
|
<textarea class="form-control input-control" id="voters-list-input" placeholder="alice@example.com, bob@example.com..." name="voters-list-input" rows="4"></textarea>
|
||||||
<span id="voters-list-input-help-block" class="help-block">
|
<span id="voters-list-input-help-block" class="help-block">
|
||||||
<!-- Error handling / input validation has been removed temporarily and would be placed here -->
|
|
||||||
Manually enter email addresses separated with commas. Alternatively, you can also upload a CSV file:
|
Manually enter email addresses separated with commas. Alternatively, you can also upload a CSV file:
|
||||||
<!-- TODO: Alignment is potentially slightly too much to the left -->
|
|
||||||
</span>
|
</span>
|
||||||
<label for="files" class="btn btn-primary">
|
<label for="files" class="btn btn-primary">
|
||||||
Upload CSV
|
Upload CSV
|
||||||
</label>
|
</label>
|
||||||
<input type="file" id="files" name="file" class="btn-info">
|
<input type="file" id="files" name="file" class="btn-info">
|
||||||
<h4 id="result" class="hidden"></h4>
|
<h4 id="result" class="hidden"></h4>
|
||||||
|
<span id="voters-input-error-block" class="help-block errorText">
|
||||||
|
<!-- Errors flagged here -->
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- reCAPTCHA -->
|
<!-- reCAPTCHA -->
|
||||||
|
@ -414,15 +438,16 @@
|
||||||
data-expired-callback="reCExpiredCallback"
|
data-expired-callback="reCExpiredCallback"
|
||||||
data-sitekey="{{ G_R_SITE_KEY }}"></div> <!-- Need to finish server implementation and import key from settings -->
|
data-sitekey="{{ G_R_SITE_KEY }}"></div> <!-- Need to finish server implementation and import key from settings -->
|
||||||
<span id="recaptcha-input-help-block" class="help-block">
|
<span id="recaptcha-input-help-block" class="help-block">
|
||||||
<!-- Error handling / input validation has been removed temporarily and would be placed here -->
|
|
||||||
Tick the box to prove that you're not a robot.
|
Tick the box to prove that you're not a robot.
|
||||||
<!-- TODO: Alignment is potentially slightly too much to the left -->
|
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<hr>
|
<hr>
|
||||||
<input class="btn btn-success" type="submit" value="Create Event" id="submit-event-create" disabled/>
|
<input class="btn btn-success" type="submit" value="Create Event" id="submit-event-create" disabled/>
|
||||||
<input class="btn btn-danger" type="button" value="Cancel" id="cancel-event-create" onclick="location.href='{% url 'polls:index' %}'" />
|
<input class="btn btn-danger" type="button" value="Cancel" id="cancel-event-create" onclick="location.href='{% url 'polls:index' %}'" />
|
||||||
|
<span id="all-errors-help-block" class="help-block errorText">
|
||||||
|
<!-- Place a summary of all errors here -->
|
||||||
|
</span>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -135,6 +135,8 @@ div.formset_object {
|
||||||
|
|
||||||
/* Error */
|
/* Error */
|
||||||
.errorText {
|
.errorText {
|
||||||
|
margin-top: 0.5em;
|
||||||
|
font-weight: bold;
|
||||||
color: red;
|
color: red;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,14 @@
|
||||||
// Form submission and validation
|
// Form submission and validation
|
||||||
var submitBtn = $("#submit-event-create");
|
var submitBtn = $("#submit-event-create");
|
||||||
var dateRegex = /^[0-9]{4}-(((0[13578]|(10|12))-(0[1-9]|[1-2][0-9]|3[0-1]))|(02-(0[1-9]|[1-2][0-9]))|((0[469]|11)-(0[1-9]|[1-2][0-9]|30)))\s[0-9]{2}:[0-9]{2}\s\+[0-9]{2}:[0-9]{2}$/;
|
var submitBtnLabel = "Create Event";
|
||||||
|
var submitBtnWaitLabel = "Please wait...";
|
||||||
|
var submitBtnErrLabel = "Errors Found";
|
||||||
|
var dateRegex = /^[0-9]{4}-(((0[13578]|(10|12))-(0[1-9]|[1-2][0-9]|3[0-1]))|(02-(0[1-9]|[1-2][0-9]))|((0[469]|11)-(0[1-9]|[1-2][0-9]|30)))\s[0-9]?[0-9]:[0-9]{2}\s\+[0-9]{2}:[0-9]{2}$/;
|
||||||
var emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
var emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||||
var reCaptchaValid = false;
|
var reCaptchaValid = false;
|
||||||
|
var generalErrorBlock = document.getElementById('all-errors-help-block');
|
||||||
|
|
||||||
|
var errors = [];
|
||||||
|
|
||||||
$("#election-form").submit(function(e) {
|
$("#election-form").submit(function(e) {
|
||||||
// Intercept submission of form and temporarily suspend it
|
// Intercept submission of form and temporarily suspend it
|
||||||
|
@ -11,7 +17,7 @@ $("#election-form").submit(function(e) {
|
||||||
|
|
||||||
// Get a reference to the submit button
|
// Get a reference to the submit button
|
||||||
submitBtn.prop('disabled', true);
|
submitBtn.prop('disabled', true);
|
||||||
submitBtn.val('Please wait...');
|
submitBtn.val(submitBtnWaitLabel);
|
||||||
|
|
||||||
// Disable the cancel button during validation
|
// Disable the cancel button during validation
|
||||||
var cancelBtn = $("#cancel-event-create");
|
var cancelBtn = $("#cancel-event-create");
|
||||||
|
@ -21,91 +27,575 @@ $("#election-form").submit(function(e) {
|
||||||
var formDataValid = isFormValid();
|
var formDataValid = isFormValid();
|
||||||
|
|
||||||
if( formDataValid === true ) {
|
if( formDataValid === true ) {
|
||||||
|
clearErrors();
|
||||||
form.submit();
|
form.submit();
|
||||||
} else {
|
} else {
|
||||||
submitBtn.val('Errors Found');
|
submitBtn.val(submitBtnErrLabel);
|
||||||
cancelBtn.removeAttr('disabled');
|
cancelBtn.removeAttr('disabled');
|
||||||
|
highlightErrors();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function validateForm() {
|
||||||
|
var formDataValid = isFormValid();
|
||||||
|
|
||||||
|
if( formDataValid === true ) {
|
||||||
|
clearErrors();
|
||||||
|
submitBtn.removeAttr('disabled');
|
||||||
|
submitBtn.val(submitBtnLabel);
|
||||||
|
} else {
|
||||||
|
submitBtn.val(submitBtnErrLabel);
|
||||||
|
highlightErrors();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function isFormValid() {
|
function isFormValid() {
|
||||||
var nameValid = isNameValid();
|
var nameValid = isNameValid();
|
||||||
var slugValid = isSlugValid();
|
var slugValid = isSlugValid();
|
||||||
var voteStartValid = isVoteStartValid();
|
var voteStartValid = isVoteStartValid();
|
||||||
var voteEndValid = isVoteEndValid();
|
var voteEndValid = isVoteEndValid();
|
||||||
var pollOptsValid = arePollsAndOptsValid();
|
var pollOptsValid = isPollAndOptsValid();
|
||||||
var minSelectionValid = isMinSelectionValid();
|
|
||||||
var maxSelectionValid = isMaxSelectionValid();
|
|
||||||
var organisersEmailsValid = areOrganisersEmailsValid();
|
var organisersEmailsValid = areOrganisersEmailsValid();
|
||||||
var trusteesEmailsValid = areTrusteesEmailsValid();
|
var trusteesEmailsValid = areTrusteesEmailsValid();
|
||||||
var votersListValid = isVotersListValid();
|
var votersListValid = isVotersListValid();
|
||||||
var reCaptchaValid = isReCaptchaStillValid();
|
|
||||||
|
|
||||||
return nameValid && slugValid && voteStartValid && voteEndValid
|
return nameValid && slugValid && voteStartValid && voteEndValid && pollOptsValid
|
||||||
&& pollOptsValid && minSelectionValid && maxSelectionValid
|
&& organisersEmailsValid && trusteesEmailsValid && votersListValid;
|
||||||
&& organisersEmailsValid && trusteesEmailsValid && votersListValid
|
}
|
||||||
&& reCaptchaValid;
|
|
||||||
|
function validateFormField(validationFn, helpBlockId) {
|
||||||
|
var valid = validationFn();
|
||||||
|
|
||||||
|
if(valid === false) {
|
||||||
|
highlightError(helpBlockId);
|
||||||
|
} else {
|
||||||
|
clearError(helpBlockId);
|
||||||
|
|
||||||
|
if(reCaptchaValid === true) {
|
||||||
|
if(submitBtn.val() === submitBtnErrLabel) {
|
||||||
|
clearErrors();
|
||||||
|
}
|
||||||
|
|
||||||
|
submitBtn.removeAttr('disabled');
|
||||||
|
submitBtn.val(submitBtnLabel);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function isNameValid() {
|
function isNameValid() {
|
||||||
// Based on a list of names supplied
|
// Based on a list of names supplied from the create_event html template
|
||||||
return true;
|
if(events_list !== undefined) {
|
||||||
|
var valid = true;
|
||||||
|
var event_name = $('#name-input').val();
|
||||||
|
|
||||||
|
if(event_name === '') {
|
||||||
|
checkAndAddError({
|
||||||
|
error: "The event name field is blank.",
|
||||||
|
helpBlockId: "name-input-error-block"
|
||||||
|
});
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(var i = 0; i < events_list.length; i++) {
|
||||||
|
var name = events_list[i].title;
|
||||||
|
|
||||||
|
if(name === event_name) {
|
||||||
|
valid = false;
|
||||||
|
|
||||||
|
// We need to flag this error to the user by generating an error that's
|
||||||
|
// later rendered
|
||||||
|
checkAndAddError({
|
||||||
|
error: "The event name '" + event_name + "' is already in use.",
|
||||||
|
helpBlockId: "name-input-error-block"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return valid;
|
||||||
|
} else {
|
||||||
|
// Can't perform validation
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$('#name-input').on('input', function (e) { // Validation performed with every keystroke
|
||||||
|
validateFormField(isNameValid, "name-input-error-block");
|
||||||
|
});
|
||||||
|
|
||||||
function isSlugValid() {
|
function isSlugValid() {
|
||||||
return true;
|
// Based on a list of identifiers supplied from the create_event html template
|
||||||
|
if(events_list !== undefined) {
|
||||||
|
var valid = true;
|
||||||
|
var event_slug = $('#identifier-input').val();
|
||||||
|
|
||||||
|
if(event_slug === '') {
|
||||||
|
checkAndAddError({
|
||||||
|
error: "The event slug field is blank.",
|
||||||
|
helpBlockId: "identifier-input-error-block"
|
||||||
|
});
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(var i = 0; i < events_list.length; i++) {
|
||||||
|
var slug = events_list[i].slug;
|
||||||
|
|
||||||
|
if(slug === event_slug) {
|
||||||
|
valid = false;
|
||||||
|
|
||||||
|
// We need to flag this error to the user by generating an error that's
|
||||||
|
// later rendered
|
||||||
|
checkAndAddError({
|
||||||
|
error: "The event slug '" + event_slug + "' is already in use.",
|
||||||
|
helpBlockId: "identifier-input-error-block"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return valid;
|
||||||
|
} else {
|
||||||
|
// Can't perform validation
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$('#identifier-input').on('input', function(e) {
|
||||||
|
validateFormField(isSlugValid, "identifier-input-error-block");
|
||||||
|
});
|
||||||
|
|
||||||
function isVoteStartValid() {
|
function isVoteStartValid() {
|
||||||
|
var helpBlockId = "vote-start-input-error-block";
|
||||||
var start_date_time = $('#vote-start-input').val();
|
var start_date_time = $('#vote-start-input').val();
|
||||||
return isDateValid(start_date_time);
|
var valid = isDateValid(start_date_time);
|
||||||
|
|
||||||
|
if(valid === false) {
|
||||||
|
checkAndAddError({
|
||||||
|
error: "The voting start date and time format is invalid.",
|
||||||
|
helpBlockId: helpBlockId
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
clearError(helpBlockId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return valid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$('#vote-start-input').change(function(e) {
|
||||||
|
validateFormField(isVoteStartValid, "vote-start-input-error-block");
|
||||||
|
});
|
||||||
|
|
||||||
|
$( "#vote-start-input" ).click(function() {
|
||||||
|
$( "#vote-start-input" ).change();
|
||||||
|
});
|
||||||
|
|
||||||
function isVoteEndValid() {
|
function isVoteEndValid() {
|
||||||
var end_date_time = $('#vote-end-input').val();
|
var end_date_time = $('#vote-end-input').val();
|
||||||
return isDateValid(end_date_time);
|
var valid = isDateValid(end_date_time);
|
||||||
|
|
||||||
|
if(valid === false) {
|
||||||
|
checkAndAddError({
|
||||||
|
error: "The voting end date and time format is invalid.",
|
||||||
|
helpBlockId: "vote-end-input-error-block"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return valid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$('#vote-end-input').change(function(e) {
|
||||||
|
validateFormField(isVoteEndValid, "vote-end-input-error-block");
|
||||||
|
});
|
||||||
|
|
||||||
|
$( "#vote-end-input" ).click(function() {
|
||||||
|
$( "#vote-end-input" ).change();
|
||||||
|
});
|
||||||
|
|
||||||
function isDateValid(date_time) {
|
function isDateValid(date_time) {
|
||||||
return dateRegex.test(date_time);
|
return dateRegex.test(date_time);
|
||||||
}
|
}
|
||||||
|
|
||||||
function arePollsAndOptsValid() {
|
function isPollAndOptsValid() {
|
||||||
// Future validation could be added here
|
var pollValid = true;
|
||||||
return true;
|
var optsValid = true;
|
||||||
|
var minMaxSelValid = true;
|
||||||
|
|
||||||
|
// Check question is valid
|
||||||
|
pollValid = isPollValid();
|
||||||
|
|
||||||
|
// Check opts are valid
|
||||||
|
optsValid = isPollOptionsValid();
|
||||||
|
|
||||||
|
// Check min and max selections are valid
|
||||||
|
minMaxSelValid = isMinMaxSelectionValid();
|
||||||
|
|
||||||
|
return pollValid && optsValid && minMaxSelValid;
|
||||||
}
|
}
|
||||||
|
|
||||||
function isMinSelectionValid() {
|
function isPollValid() {
|
||||||
return true;
|
var valid = true;
|
||||||
|
|
||||||
|
// Check question is valid
|
||||||
|
var question = $('#question-input').val();
|
||||||
|
|
||||||
|
if(question === '') {
|
||||||
|
checkAndAddError({
|
||||||
|
error: "Question / Statement for the poll is blank.",
|
||||||
|
helpBlockId: "question-input-error-block"
|
||||||
|
});
|
||||||
|
|
||||||
|
valid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return valid;
|
||||||
}
|
}
|
||||||
|
|
||||||
function isMaxSelectionValid() {
|
function isPollOptionsValid() {
|
||||||
return true;
|
var valid = true;
|
||||||
|
var optsInputs = $('.option-formset #option-name-input');
|
||||||
|
var helpBlockId = "options-input-error-block";
|
||||||
|
|
||||||
|
var index = 0;
|
||||||
|
var errorStr = "Option ";
|
||||||
|
for(var i = 0; i < optsInputs.length; i++) {
|
||||||
|
var input = optsInputs[i];
|
||||||
|
|
||||||
|
if(input.placeholder.indexOf("X") === -1) {
|
||||||
|
|
||||||
|
if(input.value === ''){
|
||||||
|
errorStr = errorStr + (index+1) + " ";
|
||||||
|
|
||||||
|
valid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(valid === false) {
|
||||||
|
errorStr = errorStr + " is blank.";
|
||||||
|
|
||||||
|
checkAndAddError({
|
||||||
|
error: errorStr,
|
||||||
|
helpBlockId: helpBlockId
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return valid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$('#question-input').on('input', function (e) {
|
||||||
|
validateFormField(isPollValid, "question-input-error-block");
|
||||||
|
});
|
||||||
|
|
||||||
|
$('.option-formset #option-name-input').on('input', function(e) {
|
||||||
|
validateFormField(isPollOptionsValid, "options-input-error-block");
|
||||||
|
});
|
||||||
|
|
||||||
|
function isMinMaxSelectionValid() {
|
||||||
|
var valid = true;
|
||||||
|
var minInput = $('#minimum-input');
|
||||||
|
var minInputMinAttr = parseInt(minInput[0].min);
|
||||||
|
var minInputVal = minInput.val();
|
||||||
|
var helpBlockId = "selections-input-error-block";
|
||||||
|
var errorStr = "";
|
||||||
|
|
||||||
|
if(minInputVal < minInputMinAttr) {
|
||||||
|
errorStr = "The minimum option selection cannot be less than " + minInputMinAttr;
|
||||||
|
valid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var maxInput = $('#maximum-input');
|
||||||
|
var maxInputMinAttr = parseInt(maxInput[0].min);
|
||||||
|
var maxInputVal = maxInput.val();
|
||||||
|
|
||||||
|
if(maxInputVal < maxInputMinAttr) {
|
||||||
|
if(errorStr !== '') {
|
||||||
|
errorStr = errorStr + " and the maximum cannot be less than " + maxInputMinAttr;
|
||||||
|
} else {
|
||||||
|
errorStr = "The maximum option selection cannot be less than " + maxInputMinAttr;
|
||||||
|
}
|
||||||
|
|
||||||
|
valid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(valid === false) {
|
||||||
|
errorStr = errorStr + ".";
|
||||||
|
|
||||||
|
checkAndAddError({
|
||||||
|
error: errorStr,
|
||||||
|
helpBlockId: helpBlockId
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return valid;
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#minimum-input, #maximum-input').on('input', function(e) {
|
||||||
|
validateFormField(isMinMaxSelectionValid, "selections-input-error-block");
|
||||||
|
});
|
||||||
|
|
||||||
function areOrganisersEmailsValid() {
|
function areOrganisersEmailsValid() {
|
||||||
return true;
|
var valid = true;
|
||||||
|
var organiserInputs = $('.organiser-formset #organiser-email-input');
|
||||||
|
var helpBlockId = "organisers-input-error-block";
|
||||||
|
|
||||||
|
var index = 0;
|
||||||
|
var errorBlankStr = "Organiser ";
|
||||||
|
var errorInvalidStr = "Organiser ";
|
||||||
|
var errorNotUserStr = "";
|
||||||
|
for(var i = 0; i < organiserInputs.length; i++) {
|
||||||
|
var input = organiserInputs[i];
|
||||||
|
|
||||||
|
if(input.placeholder.indexOf("X") === -1) {
|
||||||
|
// Check if the input field is blank
|
||||||
|
if(input.value === ''){
|
||||||
|
errorBlankStr = errorBlankStr + (index+1) + " ";
|
||||||
|
|
||||||
|
valid = false;
|
||||||
|
} else {
|
||||||
|
// Ensure that any email supplied is of a valid format
|
||||||
|
if (emailRegex.test(input.value) === false) {
|
||||||
|
errorInvalidStr = errorInvalidStr + (index + 1) + " ";
|
||||||
|
|
||||||
|
valid = false;
|
||||||
|
} else {
|
||||||
|
// If the email format is valid, ensure that an email of a registered DemoUser is being
|
||||||
|
// supplied and not a random email address
|
||||||
|
var foundMatch = user_emails.some(function (obj) {
|
||||||
|
return obj.email === input.value;
|
||||||
|
});
|
||||||
|
|
||||||
|
if(!foundMatch) {
|
||||||
|
errorNotUserStr = input.value + " is not a registered user and cannot be an organiser.";
|
||||||
|
valid = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(valid === false) {
|
||||||
|
var errorStr = "";
|
||||||
|
|
||||||
|
// Will be greater than 10 if either a blank input or invalid input has been detected (10 char is the base
|
||||||
|
// length of the original err strings)
|
||||||
|
if( errorBlankStr.length > 10 ) {
|
||||||
|
errorStr = errorBlankStr + " email is blank. ";
|
||||||
|
}
|
||||||
|
|
||||||
|
if( errorInvalidStr.length > 10 ) {
|
||||||
|
errorStr = errorStr + errorInvalidStr + " email is invalid. ";
|
||||||
|
}
|
||||||
|
|
||||||
|
// This means an invalid user has been detected
|
||||||
|
if(errorNotUserStr.length > 0) {
|
||||||
|
errorStr = errorStr + errorNotUserStr;
|
||||||
|
}
|
||||||
|
|
||||||
|
checkAndAddError({
|
||||||
|
error: errorStr,
|
||||||
|
helpBlockId: helpBlockId
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return valid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$('.organiser-formset #organiser-email-input').on('input', function(e) {
|
||||||
|
validateFormField(areOrganisersEmailsValid, "organisers-input-error-block");
|
||||||
|
});
|
||||||
|
|
||||||
function areTrusteesEmailsValid() {
|
function areTrusteesEmailsValid() {
|
||||||
return true;
|
var valid = true;
|
||||||
|
var trusteeInputs = $('.trustee-formset #trustee-email-input');
|
||||||
|
var helpBlockId = "trustees-input-error-block";
|
||||||
|
|
||||||
|
var index = 0;
|
||||||
|
var errorBlankStr = "Trustee ";
|
||||||
|
var errorInvalidStr = "Trustee ";
|
||||||
|
for(var i = 0; i < trusteeInputs.length; i++) {
|
||||||
|
var input = trusteeInputs[i];
|
||||||
|
|
||||||
|
if(input.placeholder.indexOf("X") === -1) {
|
||||||
|
// Check if the input field is blank
|
||||||
|
if(input.value === ''){
|
||||||
|
errorBlankStr = errorBlankStr + (index+1) + " ";
|
||||||
|
|
||||||
|
valid = false;
|
||||||
|
} else if (emailRegex.test(input.value) === false) {
|
||||||
|
errorInvalidStr = errorInvalidStr + (index+1) + " ";
|
||||||
|
|
||||||
|
valid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(valid === false) {
|
||||||
|
var errorStr = "";
|
||||||
|
|
||||||
|
// Will be greater than 8 if either a blank input or invalid input has been detected (8 char is the base
|
||||||
|
// length of the original err strings)
|
||||||
|
if( errorBlankStr.length > 8 ) {
|
||||||
|
errorStr = errorBlankStr + " email is blank. ";
|
||||||
|
}
|
||||||
|
|
||||||
|
if( errorInvalidStr.length > 8 ) {
|
||||||
|
errorStr = errorStr + errorInvalidStr + " email is invalid.";
|
||||||
|
}
|
||||||
|
|
||||||
|
checkAndAddError({
|
||||||
|
error: errorStr,
|
||||||
|
helpBlockId: helpBlockId
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return valid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$('.trustee-formset #trustee-email-input').on('input', function(e) {
|
||||||
|
validateFormField(areTrusteesEmailsValid, "trustees-input-error-block");
|
||||||
|
});
|
||||||
|
|
||||||
function isVotersListValid() {
|
function isVotersListValid() {
|
||||||
return true;
|
var valid = true;
|
||||||
}
|
var votersInputVal = $('#voters-list-input').val();
|
||||||
|
|
||||||
function isReCaptchaStillValid() {
|
// Check if the text area is blank
|
||||||
return true;
|
if(votersInputVal === '') {
|
||||||
}
|
checkAndAddError({
|
||||||
|
error: "The voters list is blank.",
|
||||||
|
helpBlockId: "voters-input-error-block"
|
||||||
|
});
|
||||||
|
|
||||||
$('.input-control').on('input', function(e) {
|
return false;
|
||||||
if(reCaptchaValid === true) {
|
|
||||||
submitBtn.val('Create Event');
|
|
||||||
submitBtn.removeAttr('disabled');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var errorStr = "";
|
||||||
|
var invalidCount = 0;
|
||||||
|
|
||||||
|
// Check whether one or multiple emails have been supplied
|
||||||
|
if(votersInputVal.indexOf(',') === -1) {
|
||||||
|
// Check the validity of the single email address
|
||||||
|
if(emailRegex.test(votersInputVal) === false) {
|
||||||
|
errorStr = errorStr + votersInputVal + " ";
|
||||||
|
valid = false;
|
||||||
|
invalidCount++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Proceed to check if the data within the text area is valid csv
|
||||||
|
var csvParseOutput = Papa.parse(votersInputVal);
|
||||||
|
|
||||||
|
if (csvParseOutput.errors.length > 0) {
|
||||||
|
checkAndAddError({
|
||||||
|
error: "The voters list contains invalid data. It should be a csv list containing voter email addresses.",
|
||||||
|
helpBlockId: "voters-input-error-block"
|
||||||
|
});
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that the emails supplied are valid email addresses (using a basic regex)
|
||||||
|
var votersEmails = csvParseOutput.data[0];
|
||||||
|
|
||||||
|
for(var i = 0; i < votersEmails.length; i++) {
|
||||||
|
var voter_email = votersEmails[i].replace(' ', '');
|
||||||
|
|
||||||
|
if (emailRegex.test(voter_email) === false) {
|
||||||
|
errorStr = errorStr + voter_email + " ";
|
||||||
|
valid = false;
|
||||||
|
invalidCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(valid === false) {
|
||||||
|
if(invalidCount > 1) {
|
||||||
|
errorStr = errorStr + "are invalid email addresses.";
|
||||||
|
} else {
|
||||||
|
errorStr = errorStr + "is an invalid email address.";
|
||||||
|
}
|
||||||
|
|
||||||
|
checkAndAddError({
|
||||||
|
error: errorStr,
|
||||||
|
helpBlockId: "voters-input-error-block"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return valid;
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#voters-list-input').change(function(e) {
|
||||||
|
validateFormField(isVotersListValid, "voters-input-error-block");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function checkAndAddError(newError) { // Ensures that an error hasn't already been pushed
|
||||||
|
var found = errors.some(function(error) {
|
||||||
|
return error.error === newError.error && error.helpBlockId === newError.helpBlockId
|
||||||
|
});
|
||||||
|
|
||||||
|
if(!found) {
|
||||||
|
errors.push(newError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function highlightErrors() {
|
||||||
|
// Generate the general list of errors
|
||||||
|
var baseGeneralString = "Errors were found in the form as follows:\n";
|
||||||
|
generalErrorBlock.appendChild(document.createTextNode(baseGeneralString));
|
||||||
|
generalErrorBlock.appendChild(makeErrorUL());
|
||||||
|
}
|
||||||
|
|
||||||
|
function highlightError(helpBlockId) {
|
||||||
|
for(var i = 0; i < errors.length; i++) {
|
||||||
|
var error = errors[i];
|
||||||
|
if(helpBlockId === error.helpBlockId) {
|
||||||
|
$('#' + helpBlockId).html(error.error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeErrorUL() {
|
||||||
|
// Create the list element:
|
||||||
|
var list = document.createElement('ul');
|
||||||
|
|
||||||
|
for(var i = 0; i < errors.length; i++) {
|
||||||
|
// Perform list item generation
|
||||||
|
// Create the list item:
|
||||||
|
var item = document.createElement('li');
|
||||||
|
|
||||||
|
// Set its contents:
|
||||||
|
var errorText = errors[i].error;
|
||||||
|
item.appendChild(document.createTextNode(errorText));
|
||||||
|
|
||||||
|
// Add it to the list:
|
||||||
|
list.appendChild(item);
|
||||||
|
|
||||||
|
// Populate the error's associated error block with the data
|
||||||
|
$('#' + errors[i].helpBlockId).html(errorText);
|
||||||
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearErrors() {
|
||||||
|
// Clear the errors array
|
||||||
|
errors.splice(0,errors.length);
|
||||||
|
|
||||||
|
// Clear the general list of errors
|
||||||
|
$('#all-errors-help-block').html('');
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearError(helpBlockId) {
|
||||||
|
$('#' + helpBlockId).html('');
|
||||||
|
|
||||||
|
errors = errors.filter(e => e.helpBlockId !== helpBlockId);
|
||||||
|
}
|
||||||
|
|
||||||
// File handling
|
// File handling
|
||||||
|
|
||||||
function processFileChange(event) {
|
function processFileChange(event) {
|
||||||
|
@ -145,7 +635,7 @@ function processFileChange(event) {
|
||||||
$('#result').removeClass("hidden").html(
|
$('#result').removeClass("hidden").html(
|
||||||
totalNumEmails + " email(s) have been successfully uploaded.");
|
totalNumEmails + " email(s) have been successfully uploaded.");
|
||||||
|
|
||||||
$('#voters-list-input').html(emails.join(', '));
|
$('#voters-list-input').val(emails.join(', '));
|
||||||
} else {
|
} else {
|
||||||
// There were errors, so inform the user
|
// There were errors, so inform the user
|
||||||
$('#result')
|
$('#result')
|
||||||
|
@ -163,9 +653,8 @@ document.getElementById('files').addEventListener('change', processFileChange, f
|
||||||
// reCAPTCHA
|
// reCAPTCHA
|
||||||
|
|
||||||
function reCVerificationCallback() {
|
function reCVerificationCallback() {
|
||||||
// TODO: call isFormValid before doing this and highlighting errors if any found
|
|
||||||
reCaptchaValid = true;
|
reCaptchaValid = true;
|
||||||
submitBtn.removeAttr('disabled');
|
validateForm();
|
||||||
}
|
}
|
||||||
|
|
||||||
function reCExpiredCallback() {
|
function reCExpiredCallback() {
|
||||||
|
@ -270,9 +759,14 @@ function updateFormset(formset) { // Ported from DEMOS 1. Updates the row number
|
||||||
function updateForm(form, formIndex) { // Ported from DEMOS 1.
|
function updateForm(form, formIndex) { // 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.placeholder !== undefined
|
if(mayBeTextInput.placeholder !== undefined) {
|
||||||
&& mayBeTextInput.placeholder.indexOf("Candidate") > -1) {
|
if( mayBeTextInput.placeholder.indexOf("Candidate") > -1) {
|
||||||
mayBeTextInput.placeholder = "Example: Candidate " + (formIndex + 1);
|
mayBeTextInput.placeholder = "Example: Candidate " + (formIndex + 1);
|
||||||
|
} else if (mayBeTextInput.placeholder.indexOf("trusteeX") > -1) {
|
||||||
|
mayBeTextInput.placeholder = "Example: trustee@example.com";
|
||||||
|
} else if (mayBeTextInput.placeholder.indexOf("organiserX") > -1) {
|
||||||
|
mayBeTextInput.placeholder = "Example: organiser@example.com";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var formset = form.parent('.formset');
|
var formset = form.parent('.formset');
|
||||||
|
|
Reference in a new issue