Added full input validation to the Create Event including unique event name and slug validation as well as email validation. The appropriate template data required for this from Event and DemoUser model is passed in upon rendering the create_event html template
This commit is contained in:
parent
4ca3398683
commit
7fb52678ef
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