Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
4f1bf927bb
11 changed files with 269 additions and 120 deletions
|
@ -11,9 +11,9 @@ urlpatterns = [
|
||||||
url(r'^(?P<pk>[0-9a-f-]+)/$', login_required(views.EventDetailView.as_view()), name='view-event'),
|
url(r'^(?P<pk>[0-9a-f-]+)/$', login_required(views.EventDetailView.as_view()), name='view-event'),
|
||||||
url(r'^(?P<pk>[0-9a-f-]+)/polls/$', login_required(views.EventDetailPollsView.as_view()), name='event-polls'),
|
url(r'^(?P<pk>[0-9a-f-]+)/polls/$', login_required(views.EventDetailPollsView.as_view()), name='event-polls'),
|
||||||
url(r'^(?P<pk>[0-9a-f-]+)/entities/$', login_required(views.EventDetailEntitiesView.as_view()), name='event-entities'),
|
url(r'^(?P<pk>[0-9a-f-]+)/entities/$', login_required(views.EventDetailEntitiesView.as_view()), name='event-entities'),
|
||||||
|
url(r'^(?P<pk>[0-9a-f-]+)/results/$', login_required(views.EventDetailResultsView.as_view()), name='event-results'),
|
||||||
url(r'^(?P<pk>[0-9a-f-]+)/advanced/$', login_required(views.EventDetailAdvancedView.as_view()), name='event-advanced'),
|
url(r'^(?P<pk>[0-9a-f-]+)/advanced/$', login_required(views.EventDetailAdvancedView.as_view()), name='event-advanced'),
|
||||||
url(r'^(?P<event_id>[0-9a-f-]+)/end/$', login_required(views.event_end), name='end-event'),
|
url(r'^(?P<event_id>[0-9a-f-]+)/end/$', login_required(views.event_end), name='end-event'),
|
||||||
url(r'^(?P<event_id>[0-9a-f-]+)/results/$', login_required(views.results), name='event-results'),
|
|
||||||
url(r'^(?P<event_id>[0-9a-f-]+)/edit/$', login_required(views.edit_event), name='edit-event'),
|
url(r'^(?P<event_id>[0-9a-f-]+)/edit/$', login_required(views.edit_event), name='edit-event'),
|
||||||
url(r'^(?P<event_id>[0-9a-f-]+)/delete/$', login_required(views.del_event), name='del-event'),
|
url(r'^(?P<event_id>[0-9a-f-]+)/delete/$', login_required(views.del_event), name='del-event'),
|
||||||
url(r'^(?P<event_id>[0-9a-f-]+)/decrypt/$', views.event_trustee_decrypt, name='decrypt-event'),
|
url(r'^(?P<event_id>[0-9a-f-]+)/decrypt/$', views.event_trustee_decrypt, name='decrypt-event'),
|
||||||
|
|
|
@ -38,6 +38,21 @@ class EventDetailView(generic.DetailView):
|
||||||
context['is_organiser'] = (not self.request.user.is_anonymous()) and (self.object.users_organisers.filter(email=self.request.user.email).exists())
|
context['is_organiser'] = (not self.request.user.is_anonymous()) and (self.object.users_organisers.filter(email=self.request.user.email).exists())
|
||||||
context['decrypted'] = self.object.status() == "Decrypted"
|
context['decrypted'] = self.object.status() == "Decrypted"
|
||||||
|
|
||||||
|
# Get the results for all polls
|
||||||
|
polls = self.object.polls.all()
|
||||||
|
|
||||||
|
results = list()
|
||||||
|
for poll in polls:
|
||||||
|
result_json = poll.result_json
|
||||||
|
|
||||||
|
if result_json[len(result_json)-1] == ',':
|
||||||
|
result_json = result_json[0:len(result_json)-1]
|
||||||
|
|
||||||
|
result_json = json.loads(result_json)
|
||||||
|
results.append(result_json)
|
||||||
|
|
||||||
|
context['event_results'] = results
|
||||||
|
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
@ -63,6 +78,10 @@ class PollDetailView(generic.View):
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
class EventDetailResultsView(EventDetailView):
|
||||||
|
template_name = "polls/event_results.html"
|
||||||
|
|
||||||
|
|
||||||
def util_get_poll_by_event_index(event, poll_id):
|
def util_get_poll_by_event_index(event, poll_id):
|
||||||
return event.polls.get(uuid=poll_id)
|
return event.polls.get(uuid=poll_id)
|
||||||
|
|
||||||
|
@ -248,21 +267,6 @@ def event_end(request, event_id):
|
||||||
return HttpResponseRedirect(reverse('polls:view-event', args=[event_id]))
|
return HttpResponseRedirect(reverse('polls:view-event', args=[event_id]))
|
||||||
|
|
||||||
|
|
||||||
# Returns a JSONed version of the results
|
|
||||||
def results(request, event_id):
|
|
||||||
event = get_object_or_404(Event, pk=event_id)
|
|
||||||
polls = event.polls.all()
|
|
||||||
|
|
||||||
results = ""
|
|
||||||
results += "{\"polls\":["
|
|
||||||
for poll in polls:
|
|
||||||
results += poll.result_json
|
|
||||||
|
|
||||||
results += "]}"
|
|
||||||
|
|
||||||
return HttpResponse(results)
|
|
||||||
|
|
||||||
|
|
||||||
def event_trustee_decrypt(request, event_id):
|
def event_trustee_decrypt(request, event_id):
|
||||||
event = get_object_or_404(Event, pk=event_id)
|
event = get_object_or_404(Event, pk=event_id)
|
||||||
access_key = request.GET.get('key', None)
|
access_key = request.GET.get('key', None)
|
||||||
|
@ -334,7 +338,7 @@ def event_trustee_decrypt(request, event_id):
|
||||||
# TODO: Combine partial decryptions and gen results
|
# TODO: Combine partial decryptions and gen results
|
||||||
combine_decryptions_and_tally.delay(event)
|
combine_decryptions_and_tally.delay(event)
|
||||||
|
|
||||||
messages.add_message(request, messages.SUCCESS, 'Your secret key has been successfully submitted')
|
messages.add_message(request, messages.SUCCESS, 'Your partial decryptions have been successfully submitted')
|
||||||
return HttpResponseRedirect(reverse("user_home"))
|
return HttpResponseRedirect(reverse("user_home"))
|
||||||
|
|
||||||
# Without an access key, the client does not have permission to access this page
|
# Without an access key, the client does not have permission to access this page
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
© 2014-2015 See LICENSE
|
© DĒMOS 2, Lancaster University 2014-2018.
|
||||||
|
|
|
@ -44,17 +44,32 @@
|
||||||
<br/>
|
<br/>
|
||||||
<ul class="nav nav-tabs">
|
<ul class="nav nav-tabs">
|
||||||
<li class="{% block event_nav_details %}{% endblock %}">
|
<li class="{% block event_nav_details %}{% endblock %}">
|
||||||
<a href="{% url 'polls:view-event' event.uuid %}"><strong>Summary</strong></a>
|
<a href="{% url 'polls:view-event' event.uuid %}">
|
||||||
|
<span class="glyphicon glyphicon-dashboard"></span> <strong>Summary</strong>
|
||||||
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="{% block event_nav_polls %}{% endblock %}">
|
<li class="{% block event_nav_polls %}{% endblock %}">
|
||||||
<a href="{% url 'polls:event-polls' event.uuid %}"><strong>Polls ({{ object.polls.count }})</strong></a>
|
<a href="{% url 'polls:event-polls' event.uuid %}">
|
||||||
|
<span class="glyphicon glyphicon-list-alt"></span> <strong>Polls ({{ object.polls.count }})</strong>
|
||||||
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="{% block event_nav_organisers %}{% endblock %}">
|
<li class="{% block event_nav_organisers %}{% endblock %}">
|
||||||
<a href="{% url 'polls:event-entities' event.uuid %}"><strong>Entities</strong></a>
|
<a href="{% url 'polls:event-entities' event.uuid %}">
|
||||||
|
<span class="glyphicon glyphicon-user"></span> <strong>Entities</strong>
|
||||||
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
{% if decrypted == True and object.ended == True %}
|
||||||
|
<li class="{% block event_nav_results %}{% endblock %}">
|
||||||
|
<a href="{% url 'polls:event-results' event.uuid %}">
|
||||||
|
<span class="glyphicon glyphicon-stats"></span> <strong>Results</strong>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
{% if is_organiser %}
|
{% if is_organiser %}
|
||||||
<li class="{% block event_nav_launch %}{% endblock %}">
|
<li class="{% block event_nav_launch %}{% endblock %}">
|
||||||
<a href="{% url 'polls:event-advanced' event.uuid %}"><strong>Advanced</strong></a>
|
<a href="{% url 'polls:event-advanced' event.uuid %}">
|
||||||
|
<span class="glyphicon glyphicon-info-sign"></span> <strong>Advanced</strong>
|
||||||
|
</a>
|
||||||
</li>
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
{% block event_nav_organisers %}active{% endblock %}
|
{% block event_nav_organisers %}active{% endblock %}
|
||||||
{% block event_content %}
|
{% block event_content %}
|
||||||
<h2>Event Organisers</h2>
|
<h2>Event Organisers ({{ object.users_organisers.all.count }})</h2>
|
||||||
<hr/>
|
<hr/>
|
||||||
{% if object.users_organisers.all %}
|
{% if object.users_organisers.all %}
|
||||||
<ul class="list-group">
|
<ul class="list-group">
|
||||||
|
@ -16,7 +16,7 @@
|
||||||
<p>No organisers for this Event.</p>
|
<p>No organisers for this Event.</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<hr/>
|
<hr/>
|
||||||
<h2>Event Trustees</h2>
|
<h2>Event Trustees ({{ object.users_trustees.all.count }})</h2>
|
||||||
<hr/>
|
<hr/>
|
||||||
{% if object.users_trustees.all %}
|
{% if object.users_trustees.all %}
|
||||||
<ul class="list-group">
|
<ul class="list-group">
|
||||||
|
@ -28,7 +28,7 @@
|
||||||
<p>No trustees for this Event.</p>
|
<p>No trustees for this Event.</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<hr/>
|
<hr/>
|
||||||
<h2>Voters</h2>
|
<h2>Voters ({{ object.voters.all.count }})</h2>
|
||||||
<hr/>
|
<hr/>
|
||||||
{% if object.voters.all %}
|
{% if object.voters.all %}
|
||||||
<ul class="list-group">
|
<ul class="list-group">
|
||||||
|
|
|
@ -27,7 +27,6 @@
|
||||||
<th class="text-center">No. Polls</th>
|
<th class="text-center">No. Polls</th>
|
||||||
<th class="text-center">Actions</th>
|
<th class="text-center">Actions</th>
|
||||||
<th class="text-center">Status</th>
|
<th class="text-center">Status</th>
|
||||||
<!-- Could also add a delete column to easily remove an event -->
|
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
|
28
allauthdemo/templates/polls/event_results.html
Normal file
28
allauthdemo/templates/polls/event_results.html
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
{% extends "polls/event_detail_base.html" %}
|
||||||
|
{% load staticfiles %}
|
||||||
|
{% load bootstrap3 %}
|
||||||
|
|
||||||
|
{% block event_nav_results %}active{% endblock %}
|
||||||
|
{% block event_content %}
|
||||||
|
{% for poll_result in event_results %}
|
||||||
|
<div class="form-group">
|
||||||
|
<h3 class="marginTopResultTable">{{ poll_result.name }}</h3>
|
||||||
|
<table id="results-table-{{ forloop.counter }}" class="table table-hover">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="resultsCol text-center">Option</th>
|
||||||
|
<th class="resultsCol text-center">Votes</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for option in poll_result.options %}
|
||||||
|
<tr>
|
||||||
|
<td class="resultsCol text-center">{{ option.option }}</td>
|
||||||
|
<td class="resultsCol text-center">{{ option.votes }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
{% endblock %}
|
|
@ -111,9 +111,17 @@
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<button type="button" class="close" data-dismiss="modal">×</button>
|
<button type="button" class="close" data-dismiss="modal">×</button>
|
||||||
<h4 class="modal-title" style="text-align: center"><strong>Ballot</strong></h4>
|
<h4 class="modal-title" style="text-align: center"><strong>Please Select a Ballot</strong></h4>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
|
<div class="choice-group">
|
||||||
|
<a id="choice-A" class="btn btn-sq btn-primary">
|
||||||
|
A
|
||||||
|
</a>
|
||||||
|
<a id="choice-B" class="btn btn-sq btn-warning choice">
|
||||||
|
B
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
||||||
|
|
|
@ -1,68 +1,28 @@
|
||||||
from django.template import RequestContext
|
from django.template import RequestContext
|
||||||
from django.shortcuts import render_to_response, render
|
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
from django.http import HttpResponseRedirect
|
|
||||||
from django.views import generic
|
from django.views import generic
|
||||||
from allauthdemo.polls.models import Event
|
from allauthdemo.polls.models import Event
|
||||||
from django.shortcuts import get_object_or_404, render, render_to_response
|
from django.shortcuts import render_to_response
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def member_index(request):
|
def member_index(request):
|
||||||
return render_to_response("member/member-index.html", RequestContext(request))
|
return render_to_response("member/member-index.html", RequestContext(request))
|
||||||
|
|
||||||
|
|
||||||
#def member_events(request):
|
|
||||||
#self.publisher = get_object_or_404(Publisher, name=self.args[0])
|
|
||||||
#return Book.objects.filter(publisher=self.publisher)
|
|
||||||
#return render_to_response("member/member-events.html", RequestContext(request))
|
|
||||||
|
|
||||||
class MemberEvents(generic.ListView):
|
class MemberEvents(generic.ListView):
|
||||||
model = Event
|
model = Event
|
||||||
template_name = 'member/member-events.html'
|
template_name = 'member/member-events.html'
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super(MemberEvents, self).get_context_data(**kwargs)
|
context = super(MemberEvents, self).get_context_data(**kwargs)
|
||||||
#self.object.organisers.filter(email=self.request.user.email())
|
|
||||||
# no check needed for anon, as url should make sure we're logged in!
|
# no check needed for anon, as url should make sure we're logged in!
|
||||||
return context
|
return context
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
#self.publisher = get_object_or_404(Publisher, name=self.args[0])
|
|
||||||
return self.request.user.organisers.all()
|
return self.request.user.organisers.all()
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def member_action(request):
|
def member_action(request):
|
||||||
return render_to_response("member/member-action.html", RequestContext(request))
|
return render_to_response("member/member-action.html", RequestContext(request))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
'''
|
|
||||||
|
|
||||||
class EventListView(generic.ListView):
|
|
||||||
|
|
||||||
model = Event
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
|
||||||
context = super(EventListView, self).get_context_data(**kwargs)
|
|
||||||
#context['now'] = timezone.now()
|
|
||||||
return context
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
|
||||||
context = super(EventDetailView, self).get_context_data(**kwargs)
|
|
||||||
context['is_organiser'] = ((not self.request.user.is_anonymous()) and (self.object.users.filter(email=self.request.user.email).exists()))
|
|
||||||
#context['now'] = timezone.now()
|
|
||||||
return context
|
|
||||||
|
|
||||||
class PollDetailView(generic.DetailView):
|
|
||||||
|
|
||||||
model = Poll
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
|
||||||
context = super(PollDetailView, self).get_context_data(**kwargs)
|
|
||||||
#context['now'] = timezone.now()
|
|
||||||
context['form'] = VoteForm(instance=self.object)
|
|
||||||
context['poll_count'] = self.object.event.polls.all().count()
|
|
||||||
return context
|
|
||||||
|
|
||||||
'''
|
|
||||||
|
|
|
@ -180,7 +180,32 @@ input[type="file"] {
|
||||||
padding-left: 16px;
|
padding-left: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.resultsCol {
|
||||||
|
width: 50%
|
||||||
|
}
|
||||||
|
|
||||||
|
.marginTopResultTable {
|
||||||
|
margin-top: 1.25em;
|
||||||
|
}
|
||||||
|
|
||||||
/* Voting page */
|
/* Voting page */
|
||||||
.big-checkbox {
|
.big-checkbox {
|
||||||
width: 30px; height: 30px;
|
width: 30px !important; height: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-sq {
|
||||||
|
width: 125px !important;
|
||||||
|
height: 125px !important;
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 115px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.choice {
|
||||||
|
margin-left: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.choice-group {
|
||||||
|
width: 54%;
|
||||||
|
margin: 0 auto;
|
||||||
}
|
}
|
|
@ -1,3 +1,18 @@
|
||||||
|
function showDialogWithText(titleTxt, bodyTxt) {
|
||||||
|
var modalDialog = $('#modalDialog');
|
||||||
|
var title = modalDialog.find('.modal-title');
|
||||||
|
var body = modalDialog.find('.modal-body');
|
||||||
|
|
||||||
|
title.text(titleTxt);
|
||||||
|
|
||||||
|
var p = document.createElement("p");
|
||||||
|
p.innerHTML = bodyTxt;
|
||||||
|
body.empty();
|
||||||
|
body.append( p );
|
||||||
|
|
||||||
|
modalDialog.modal('show');
|
||||||
|
}
|
||||||
|
|
||||||
// This should stop people ticking more than the maximum permitted
|
// This should stop people ticking more than the maximum permitted
|
||||||
function updateCheckboxInteractivity() {
|
function updateCheckboxInteractivity() {
|
||||||
var inputs = $("label input[type=checkbox]");
|
var inputs = $("label input[type=checkbox]");
|
||||||
|
@ -52,21 +67,13 @@ function isVotingInputValid() {
|
||||||
// This will highlight when people haven't selected enough options
|
// This will highlight when people haven't selected enough options
|
||||||
|
|
||||||
if(!valid) {
|
if(!valid) {
|
||||||
var modalDialog = $('#modalDialog');
|
let errText = "You've only selected " + selectedCount
|
||||||
var title = modalDialog.find('.modal-title');
|
|
||||||
var body = modalDialog.find('.modal-body');
|
|
||||||
var errText = "You've only selected " + selectedCount
|
|
||||||
+ " option(s). The minimum number you need to select is " + MIN_SELECTIONS
|
+ " option(s). The minimum number you need to select is " + MIN_SELECTIONS
|
||||||
+ " and the maximum is " + MAX_SELECTIONS + ". Please go back and correct this.";
|
+ " and the maximum is " + MAX_SELECTIONS + ". Please go back and correct this.";
|
||||||
|
|
||||||
title.text('Voting Error');
|
let titleTxt = 'Voting Error';
|
||||||
|
|
||||||
var p = document.createElement("p");
|
showDialogWithText(titleTxt, errText);
|
||||||
p.innerHTML = errText;
|
|
||||||
body.empty();
|
|
||||||
body.append( p );
|
|
||||||
|
|
||||||
modalDialog.modal('show');
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,7 +95,6 @@ function genBlankVote() {
|
||||||
return vote;
|
return vote;
|
||||||
}
|
}
|
||||||
|
|
||||||
var progress = 0;
|
|
||||||
var progressBar = document.getElementById("progress-bar");
|
var progressBar = document.getElementById("progress-bar");
|
||||||
|
|
||||||
// Based on the user's vote in the current poll, this generates a ballot which
|
// Based on the user's vote in the current poll, this generates a ballot which
|
||||||
|
@ -125,15 +131,21 @@ function generateBallot() {
|
||||||
checkboxInputs.each(function() {
|
checkboxInputs.each(function() {
|
||||||
var checkbox = $(this);
|
var checkbox = $(this);
|
||||||
|
|
||||||
// Push the selected option values to an array
|
// Push the selected option values (ones that have been checked) to an array
|
||||||
if(checkbox.prop('checked')) {
|
if(checkbox.prop('checked')) {
|
||||||
unencryptedVotes.push(checkbox.val());
|
unencryptedVotes.push(checkbox.val());
|
||||||
}
|
}
|
||||||
// For whatever hasn't been selected, push a blank vote to the array
|
});
|
||||||
else {
|
|
||||||
|
// If there is a dif between the num selected and the max allowed, push blank votes to the array to pad this
|
||||||
|
// to prevent information leakage
|
||||||
|
if(unencryptedVotes.length < MAX_SELECTIONS) {
|
||||||
|
let blankVotesToPush = MAX_SELECTIONS - unencryptedVotes.length;
|
||||||
|
|
||||||
|
for(let i = 0; i < blankVotesToPush; i++) {
|
||||||
unencryptedVotes.push(genBlankVote());
|
unencryptedVotes.push(genBlankVote());
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
// Encrypt all of the votes for this ballot
|
// Encrypt all of the votes for this ballot
|
||||||
var encryptedVotes = [];
|
var encryptedVotes = [];
|
||||||
|
@ -163,12 +175,9 @@ function generateBallot() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
var ballot = {
|
return {
|
||||||
encryptedVotes: encryptedVotes
|
encryptedVotes: encryptedVotes
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
return ballot;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$('#gen-ballots-btn').click(function() {
|
$('#gen-ballots-btn').click(function() {
|
||||||
|
@ -181,28 +190,19 @@ $('#gen-ballots-btn').click(function() {
|
||||||
$('#progress-bar-description').toggleClass('hidden');
|
$('#progress-bar-description').toggleClass('hidden');
|
||||||
$('#progress-bar-container').toggleClass('hidden');
|
$('#progress-bar-container').toggleClass('hidden');
|
||||||
|
|
||||||
setTimeout(generateBallotsAndShowUsr, 50);
|
setTimeout(generateBallotsAndShowUsr, 25);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function voteSuccessfullyReceived() {
|
function voteSuccessfullyReceived() {
|
||||||
var modalDialog = $('#modalDialog');
|
let titleTxt = 'Vote Successfully Received';
|
||||||
var title = modalDialog.find('.modal-title');
|
let bodyText = "Thank you for voting!";
|
||||||
var body = modalDialog.find('.modal-body');
|
|
||||||
|
|
||||||
title.text('Vote Successfully Received');
|
|
||||||
var bodyText = "Thank you for voting!";
|
|
||||||
|
|
||||||
if(POLL_NUM !== POLL_COUNT) {
|
if(POLL_NUM !== POLL_COUNT) {
|
||||||
bodyText += " You can vote on the next poll by closing down this dialog and clicking 'Next Poll'.";
|
bodyText += " You can vote on the next poll by closing down this dialog and clicking 'Next Poll'.";
|
||||||
}
|
}
|
||||||
|
|
||||||
var p = document.createElement("p");
|
showDialogWithText(titleTxt, bodyText);
|
||||||
p.innerHTML = bodyText;
|
|
||||||
body.empty();
|
|
||||||
body.append( p );
|
|
||||||
|
|
||||||
modalDialog.modal('show');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var CSRF = $( "input[name='csrfmiddlewaretoken']" ).val();
|
var CSRF = $( "input[name='csrfmiddlewaretoken']" ).val();
|
||||||
|
@ -230,25 +230,135 @@ function sendBallotToServer(ballot) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var bytestostring = function(b) {
|
||||||
|
var s = "";
|
||||||
|
var len = b.length;
|
||||||
|
var ch;
|
||||||
|
for (var i = 0; i < len; i++) {
|
||||||
|
ch = b[i];
|
||||||
|
s += ((ch >>> 4) & 15).toString(16);
|
||||||
|
s += (ch & 15).toString(16);
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
};
|
||||||
|
|
||||||
|
var stringtobytes = function(s) {
|
||||||
|
var b = [];
|
||||||
|
for (var i = 0; i < s.length; i++) {
|
||||||
|
b.push(s.charCodeAt(i));
|
||||||
|
}
|
||||||
|
return b;
|
||||||
|
};
|
||||||
|
|
||||||
|
function SHA256Hash(bytes, toStr) {
|
||||||
|
var ctx = new CTX();
|
||||||
|
|
||||||
|
var R = [];
|
||||||
|
var H = new ctx.HASH256();
|
||||||
|
|
||||||
|
H.process_array(bytes);
|
||||||
|
R = H.hash();
|
||||||
|
|
||||||
|
if (R.length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(toStr) {
|
||||||
|
// If toStr is true we return the stringified version of the bytes of the hash
|
||||||
|
return bytestostring(R);
|
||||||
|
} else {
|
||||||
|
// If toStr is false we return the bytes of the hash
|
||||||
|
return R;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FAO Ben: Called once the ballot has been sent to the back-end and dialog has closed
|
||||||
|
function onAfterBallotSend() {
|
||||||
|
// TODO: FAO Ben: Implement QR func here.
|
||||||
|
// TODO: Currently, there is a dialog already implemented in the event_vote.html page which is
|
||||||
|
// TODO: used for voting error information but could be used to display the QR code using JS in
|
||||||
|
// TODO: a similar way that showBallotChoiceDialog does.
|
||||||
|
}
|
||||||
|
|
||||||
|
function processBallotSelection(selection, selectionHash, successFn) {
|
||||||
|
// Dispatch the ballot to the server
|
||||||
|
sendBallotToServer(selection);
|
||||||
|
|
||||||
|
// Close the choice selection dialog
|
||||||
|
var modalDialog = $('#modalDialog');
|
||||||
|
modal.modal('hide');
|
||||||
|
|
||||||
|
// Call the successfn currently with the selection hash but this may not be needed
|
||||||
|
successFn(selectionHash);
|
||||||
|
}
|
||||||
|
|
||||||
|
function showBallotChoiceDialog(ballotA, ballotB) {
|
||||||
|
// Output hashes of the 2 ballots
|
||||||
|
const BALLOT_A_HASH = SHA256Hash(stringtobytes(JSON.stringify(ballotA)), true);
|
||||||
|
const BALLOT_B_HASH = SHA256Hash(stringtobytes(JSON.stringify(ballotB)), true);
|
||||||
|
|
||||||
|
// With the ballots and their hashes generated, we can display the ballot choice dialog
|
||||||
|
var modalDialog = $('#modalDialog');
|
||||||
|
var title = modalDialog.find('.modal-title');
|
||||||
|
var body = modalDialog.find('.modal-body');
|
||||||
|
body.empty();
|
||||||
|
title.text('Please Select a Ballot');
|
||||||
|
|
||||||
|
// Generate the body of the dialog which consists of a button for A and for B as well as their hashes
|
||||||
|
var choiceGroupDiv = document.createElement('div');
|
||||||
|
choiceGroupDiv.setAttribute('class', 'choice-group');
|
||||||
|
|
||||||
|
var btnChoiceA = document.createElement('a');
|
||||||
|
btnChoiceA.setAttribute('id', 'choice-A');
|
||||||
|
btnChoiceA.setAttribute('class', 'btn btn-sq btn-primary');
|
||||||
|
btnChoiceA.innerHTML = 'A';
|
||||||
|
choiceGroupDiv.append(btnChoiceA);
|
||||||
|
|
||||||
|
var btnChoiceB = document.createElement('a');
|
||||||
|
btnChoiceB.setAttribute('id', 'choice-B');
|
||||||
|
btnChoiceB.setAttribute('class', 'btn btn-sq btn-warning choice');
|
||||||
|
btnChoiceB.innerHTML = 'B';
|
||||||
|
choiceGroupDiv.append(btnChoiceB);
|
||||||
|
|
||||||
|
// ----------------------------------------------
|
||||||
|
|
||||||
|
var hashGroupDiv = document.createElement('div');
|
||||||
|
var br = document.createElement('br');
|
||||||
|
hashGroupDiv.append( br );
|
||||||
|
|
||||||
|
var hashA = document.createElement("span");
|
||||||
|
hashA.innerHTML = "Hash A: " + BALLOT_A_HASH;
|
||||||
|
hashGroupDiv.append( hashA );
|
||||||
|
|
||||||
|
var br2 = document.createElement('br');
|
||||||
|
hashGroupDiv.append( br2 );
|
||||||
|
|
||||||
|
var hashB = document.createElement("span");
|
||||||
|
hashB.innerHTML = "Hash B: " + BALLOT_B_HASH;
|
||||||
|
hashGroupDiv.append( hashB );
|
||||||
|
|
||||||
|
// -----------------------------------------------
|
||||||
|
|
||||||
|
body.append(choiceGroupDiv);
|
||||||
|
body.append(hashGroupDiv);
|
||||||
|
|
||||||
|
modalDialog.modal('show');
|
||||||
|
|
||||||
|
// Register callback functions for the selection of either A or B
|
||||||
|
$('#choice-A').click(function(e) {
|
||||||
|
processBallotSelection(ballotA, BALLOT_A_HASH, onAfterBallotSend);
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#choice-B').click(function(e) {
|
||||||
|
processBallotSelection(ballotB, BALLOT_B_HASH, onAfterBallotSend);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function generateBallotB(ballotA) {
|
function generateBallotB(ballotA) {
|
||||||
var ballotB = generateBallot();
|
var ballotB = generateBallot();
|
||||||
progressBar.setAttribute("style", "width: 100%;");
|
progressBar.setAttribute("style", "width: 100%;");
|
||||||
|
|
||||||
var ballots = {
|
showBallotChoiceDialog(ballotA, ballotB);
|
||||||
A : ballotA,
|
|
||||||
B : ballotB
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO: Implement ballot choice UI and QR func here. At the moment the code automatically
|
|
||||||
// TODO: submits the first ballot (as if the user selected it) to the server but this needs updating.
|
|
||||||
// TODO: Currently, there is a dialog already implemented in the event_vote.html page which is
|
|
||||||
// TODO: used for voting error information but could be used to display the ballot choices.
|
|
||||||
// This delay allows the execution thread to update the above CSS on the progress bar
|
|
||||||
var selectedBallot = ballots.A;
|
|
||||||
|
|
||||||
setTimeout(function () {
|
|
||||||
sendBallotToServer(selectedBallot);
|
|
||||||
}, 50);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateBallotsAndShowUsr() {
|
function generateBallotsAndShowUsr() {
|
||||||
|
@ -262,5 +372,5 @@ function generateBallotsAndShowUsr() {
|
||||||
// This delay allows the execution thread to update the above CSS on the progress bar
|
// This delay allows the execution thread to update the above CSS on the progress bar
|
||||||
setTimeout(function () {
|
setTimeout(function () {
|
||||||
generateBallotB(ballotA);
|
generateBallotB(ballotA);
|
||||||
}, 125);
|
}, 150);
|
||||||
}
|
}
|
Reference in a new issue