General housekeeping such as adding package.json for Node server, bash script for launching a celery worker and updating the readme to assist with launching the DEMOS2 app. Updated some models to include UI helper functions. Main work done is around event preparation - with a Celery worker running and the Node server, trustees are now emailed a link to prepare events. The event detail page has also had a bit of an overhaul to include additional information and to make it easier to use
This commit is contained in:
parent
de9eaa7881
commit
f82a380fa4
21 changed files with 337 additions and 696 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1,6 +1,8 @@
|
||||||
*.pyc
|
*.pyc
|
||||||
*.sqlite
|
*.sqlite
|
||||||
*.sqlite3
|
*.sqlite3
|
||||||
|
package-lock.json
|
||||||
|
node_modules/
|
||||||
__pycache__
|
__pycache__
|
||||||
migrations/
|
migrations/
|
||||||
build
|
build
|
||||||
|
|
529
Node/package-lock.json
generated
529
Node/package-lock.json
generated
|
@ -1,529 +0,0 @@
|
||||||
{
|
|
||||||
"requires": true,
|
|
||||||
"lockfileVersion": 1,
|
|
||||||
"dependencies": {
|
|
||||||
"accepts": {
|
|
||||||
"version": "1.3.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.4.tgz",
|
|
||||||
"integrity": "sha1-hiRnWMfdbSGmR0/whKR0DsBesh8=",
|
|
||||||
"requires": {
|
|
||||||
"mime-types": "2.1.17",
|
|
||||||
"negotiator": "0.6.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"append-field": {
|
|
||||||
"version": "0.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/append-field/-/append-field-0.1.0.tgz",
|
|
||||||
"integrity": "sha1-bdxY+gg8e8VF08WZWygwzCNm1Eo="
|
|
||||||
},
|
|
||||||
"array-flatten": {
|
|
||||||
"version": "1.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
|
|
||||||
"integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
|
|
||||||
},
|
|
||||||
"body-parser": {
|
|
||||||
"version": "1.18.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz",
|
|
||||||
"integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=",
|
|
||||||
"requires": {
|
|
||||||
"bytes": "3.0.0",
|
|
||||||
"content-type": "1.0.4",
|
|
||||||
"debug": "2.6.9",
|
|
||||||
"depd": "1.1.1",
|
|
||||||
"http-errors": "1.6.2",
|
|
||||||
"iconv-lite": "0.4.19",
|
|
||||||
"on-finished": "2.3.0",
|
|
||||||
"qs": "6.5.1",
|
|
||||||
"raw-body": "2.3.2",
|
|
||||||
"type-is": "1.6.15"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"busboy": {
|
|
||||||
"version": "0.2.14",
|
|
||||||
"resolved": "https://registry.npmjs.org/busboy/-/busboy-0.2.14.tgz",
|
|
||||||
"integrity": "sha1-bCpiLvz0fFe7vh4qnDetNseSVFM=",
|
|
||||||
"requires": {
|
|
||||||
"dicer": "0.2.5",
|
|
||||||
"readable-stream": "1.1.14"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"bytes": {
|
|
||||||
"version": "3.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
|
|
||||||
"integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg="
|
|
||||||
},
|
|
||||||
"concat-stream": {
|
|
||||||
"version": "1.6.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.0.tgz",
|
|
||||||
"integrity": "sha1-CqxmL9Ur54lk1VMvaUeE5wEQrPc=",
|
|
||||||
"requires": {
|
|
||||||
"inherits": "2.0.3",
|
|
||||||
"readable-stream": "2.3.3",
|
|
||||||
"typedarray": "0.0.6"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"isarray": {
|
|
||||||
"version": "1.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
|
||||||
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
|
|
||||||
},
|
|
||||||
"readable-stream": {
|
|
||||||
"version": "2.3.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz",
|
|
||||||
"integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==",
|
|
||||||
"requires": {
|
|
||||||
"core-util-is": "1.0.2",
|
|
||||||
"inherits": "2.0.3",
|
|
||||||
"isarray": "1.0.0",
|
|
||||||
"process-nextick-args": "1.0.7",
|
|
||||||
"safe-buffer": "5.1.1",
|
|
||||||
"string_decoder": "1.0.3",
|
|
||||||
"util-deprecate": "1.0.2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"string_decoder": {
|
|
||||||
"version": "1.0.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
|
|
||||||
"integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
|
|
||||||
"requires": {
|
|
||||||
"safe-buffer": "5.1.1"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"content-disposition": {
|
|
||||||
"version": "0.5.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz",
|
|
||||||
"integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ="
|
|
||||||
},
|
|
||||||
"content-type": {
|
|
||||||
"version": "1.0.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
|
|
||||||
"integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
|
|
||||||
},
|
|
||||||
"cookie": {
|
|
||||||
"version": "0.3.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
|
|
||||||
"integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s="
|
|
||||||
},
|
|
||||||
"cookie-parser": {
|
|
||||||
"version": "1.4.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.3.tgz",
|
|
||||||
"integrity": "sha1-D+MfoZ0AC5X0qt8fU/3CuKIDuqU=",
|
|
||||||
"requires": {
|
|
||||||
"cookie": "0.3.1",
|
|
||||||
"cookie-signature": "1.0.6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"cookie-signature": {
|
|
||||||
"version": "1.0.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
|
|
||||||
"integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
|
|
||||||
},
|
|
||||||
"core-util-is": {
|
|
||||||
"version": "1.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
|
|
||||||
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
|
|
||||||
},
|
|
||||||
"cors": {
|
|
||||||
"version": "2.8.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.4.tgz",
|
|
||||||
"integrity": "sha1-K9OB8usgECAQXNUOpZ2mMJBpRoY=",
|
|
||||||
"requires": {
|
|
||||||
"object-assign": "4.1.1",
|
|
||||||
"vary": "1.1.2"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"object-assign": {
|
|
||||||
"version": "4.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
|
||||||
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"debug": {
|
|
||||||
"version": "2.6.9",
|
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
|
||||||
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
|
||||||
"requires": {
|
|
||||||
"ms": "2.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"depd": {
|
|
||||||
"version": "1.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz",
|
|
||||||
"integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k="
|
|
||||||
},
|
|
||||||
"destroy": {
|
|
||||||
"version": "1.0.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
|
|
||||||
"integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
|
|
||||||
},
|
|
||||||
"dicer": {
|
|
||||||
"version": "0.2.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/dicer/-/dicer-0.2.5.tgz",
|
|
||||||
"integrity": "sha1-WZbAhrszIYyBLAkL3cCc0S+stw8=",
|
|
||||||
"requires": {
|
|
||||||
"readable-stream": "1.1.14",
|
|
||||||
"streamsearch": "0.1.2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ee-first": {
|
|
||||||
"version": "1.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
|
||||||
"integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
|
|
||||||
},
|
|
||||||
"encodeurl": {
|
|
||||||
"version": "1.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.1.tgz",
|
|
||||||
"integrity": "sha1-eePVhlU0aQn+bw9Fpd5oEDspTSA="
|
|
||||||
},
|
|
||||||
"escape-html": {
|
|
||||||
"version": "1.0.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
|
|
||||||
"integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
|
|
||||||
},
|
|
||||||
"etag": {
|
|
||||||
"version": "1.8.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
|
|
||||||
"integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
|
|
||||||
},
|
|
||||||
"express": {
|
|
||||||
"version": "4.16.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/express/-/express-4.16.2.tgz",
|
|
||||||
"integrity": "sha1-41xt/i1kt9ygpc1PIXgb4ymeB2w=",
|
|
||||||
"requires": {
|
|
||||||
"accepts": "1.3.4",
|
|
||||||
"array-flatten": "1.1.1",
|
|
||||||
"body-parser": "1.18.2",
|
|
||||||
"content-disposition": "0.5.2",
|
|
||||||
"content-type": "1.0.4",
|
|
||||||
"cookie": "0.3.1",
|
|
||||||
"cookie-signature": "1.0.6",
|
|
||||||
"debug": "2.6.9",
|
|
||||||
"depd": "1.1.1",
|
|
||||||
"encodeurl": "1.0.1",
|
|
||||||
"escape-html": "1.0.3",
|
|
||||||
"etag": "1.8.1",
|
|
||||||
"finalhandler": "1.1.0",
|
|
||||||
"fresh": "0.5.2",
|
|
||||||
"merge-descriptors": "1.0.1",
|
|
||||||
"methods": "1.1.2",
|
|
||||||
"on-finished": "2.3.0",
|
|
||||||
"parseurl": "1.3.2",
|
|
||||||
"path-to-regexp": "0.1.7",
|
|
||||||
"proxy-addr": "2.0.2",
|
|
||||||
"qs": "6.5.1",
|
|
||||||
"range-parser": "1.2.0",
|
|
||||||
"safe-buffer": "5.1.1",
|
|
||||||
"send": "0.16.1",
|
|
||||||
"serve-static": "1.13.1",
|
|
||||||
"setprototypeof": "1.1.0",
|
|
||||||
"statuses": "1.3.1",
|
|
||||||
"type-is": "1.6.15",
|
|
||||||
"utils-merge": "1.0.1",
|
|
||||||
"vary": "1.1.2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"finalhandler": {
|
|
||||||
"version": "1.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.0.tgz",
|
|
||||||
"integrity": "sha1-zgtoVbRYU+eRsvzGgARtiCU91/U=",
|
|
||||||
"requires": {
|
|
||||||
"debug": "2.6.9",
|
|
||||||
"encodeurl": "1.0.1",
|
|
||||||
"escape-html": "1.0.3",
|
|
||||||
"on-finished": "2.3.0",
|
|
||||||
"parseurl": "1.3.2",
|
|
||||||
"statuses": "1.3.1",
|
|
||||||
"unpipe": "1.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"forwarded": {
|
|
||||||
"version": "0.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
|
|
||||||
"integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ="
|
|
||||||
},
|
|
||||||
"fresh": {
|
|
||||||
"version": "0.5.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
|
|
||||||
"integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
|
|
||||||
},
|
|
||||||
"http-errors": {
|
|
||||||
"version": "1.6.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz",
|
|
||||||
"integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=",
|
|
||||||
"requires": {
|
|
||||||
"depd": "1.1.1",
|
|
||||||
"inherits": "2.0.3",
|
|
||||||
"setprototypeof": "1.0.3",
|
|
||||||
"statuses": "1.3.1"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"setprototypeof": {
|
|
||||||
"version": "1.0.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz",
|
|
||||||
"integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ="
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"iconv-lite": {
|
|
||||||
"version": "0.4.19",
|
|
||||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz",
|
|
||||||
"integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ=="
|
|
||||||
},
|
|
||||||
"inherits": {
|
|
||||||
"version": "2.0.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
|
|
||||||
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
|
|
||||||
},
|
|
||||||
"ipaddr.js": {
|
|
||||||
"version": "1.5.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.5.2.tgz",
|
|
||||||
"integrity": "sha1-1LUFvemUaYfM8PxY2QEP+WB+P6A="
|
|
||||||
},
|
|
||||||
"isarray": {
|
|
||||||
"version": "0.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
|
|
||||||
"integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
|
|
||||||
},
|
|
||||||
"media-typer": {
|
|
||||||
"version": "0.3.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
|
|
||||||
"integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
|
|
||||||
},
|
|
||||||
"merge-descriptors": {
|
|
||||||
"version": "1.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
|
|
||||||
"integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
|
|
||||||
},
|
|
||||||
"methods": {
|
|
||||||
"version": "1.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
|
|
||||||
"integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
|
|
||||||
},
|
|
||||||
"milagro-crypto-js": {
|
|
||||||
"version": "file:milagro-crypto-js"
|
|
||||||
},
|
|
||||||
"mime": {
|
|
||||||
"version": "1.4.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz",
|
|
||||||
"integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ=="
|
|
||||||
},
|
|
||||||
"mime-db": {
|
|
||||||
"version": "1.30.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.30.0.tgz",
|
|
||||||
"integrity": "sha1-dMZD2i3Z1qRTmZY0ZbJtXKfXHwE="
|
|
||||||
},
|
|
||||||
"mime-types": {
|
|
||||||
"version": "2.1.17",
|
|
||||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz",
|
|
||||||
"integrity": "sha1-Cdejk/A+mVp5+K+Fe3Cp4KsWVXo=",
|
|
||||||
"requires": {
|
|
||||||
"mime-db": "1.30.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"minimist": {
|
|
||||||
"version": "0.0.8",
|
|
||||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
|
|
||||||
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
|
|
||||||
},
|
|
||||||
"mkdirp": {
|
|
||||||
"version": "0.5.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
|
|
||||||
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
|
|
||||||
"requires": {
|
|
||||||
"minimist": "0.0.8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ms": {
|
|
||||||
"version": "2.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
|
||||||
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
|
|
||||||
},
|
|
||||||
"multer": {
|
|
||||||
"version": "1.3.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/multer/-/multer-1.3.0.tgz",
|
|
||||||
"integrity": "sha1-CSsmcPaEb6SRSWXvyM+Uwg/sbNI=",
|
|
||||||
"requires": {
|
|
||||||
"append-field": "0.1.0",
|
|
||||||
"busboy": "0.2.14",
|
|
||||||
"concat-stream": "1.6.0",
|
|
||||||
"mkdirp": "0.5.1",
|
|
||||||
"object-assign": "3.0.0",
|
|
||||||
"on-finished": "2.3.0",
|
|
||||||
"type-is": "1.6.15",
|
|
||||||
"xtend": "4.0.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"negotiator": {
|
|
||||||
"version": "0.6.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz",
|
|
||||||
"integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk="
|
|
||||||
},
|
|
||||||
"object-assign": {
|
|
||||||
"version": "3.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz",
|
|
||||||
"integrity": "sha1-m+3VygiXlJvKR+f/QIBi1Un1h/I="
|
|
||||||
},
|
|
||||||
"on-finished": {
|
|
||||||
"version": "2.3.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
|
|
||||||
"integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
|
|
||||||
"requires": {
|
|
||||||
"ee-first": "1.1.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"parseurl": {
|
|
||||||
"version": "1.3.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz",
|
|
||||||
"integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M="
|
|
||||||
},
|
|
||||||
"path-to-regexp": {
|
|
||||||
"version": "0.1.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
|
|
||||||
"integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
|
|
||||||
},
|
|
||||||
"process-nextick-args": {
|
|
||||||
"version": "1.0.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz",
|
|
||||||
"integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M="
|
|
||||||
},
|
|
||||||
"proxy-addr": {
|
|
||||||
"version": "2.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.2.tgz",
|
|
||||||
"integrity": "sha1-ZXFQT0e7mI7IGAJT+F3X4UlSvew=",
|
|
||||||
"requires": {
|
|
||||||
"forwarded": "0.1.2",
|
|
||||||
"ipaddr.js": "1.5.2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"qs": {
|
|
||||||
"version": "6.5.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz",
|
|
||||||
"integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A=="
|
|
||||||
},
|
|
||||||
"range-parser": {
|
|
||||||
"version": "1.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz",
|
|
||||||
"integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4="
|
|
||||||
},
|
|
||||||
"raw-body": {
|
|
||||||
"version": "2.3.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz",
|
|
||||||
"integrity": "sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k=",
|
|
||||||
"requires": {
|
|
||||||
"bytes": "3.0.0",
|
|
||||||
"http-errors": "1.6.2",
|
|
||||||
"iconv-lite": "0.4.19",
|
|
||||||
"unpipe": "1.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"readable-stream": {
|
|
||||||
"version": "1.1.14",
|
|
||||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
|
|
||||||
"integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=",
|
|
||||||
"requires": {
|
|
||||||
"core-util-is": "1.0.2",
|
|
||||||
"inherits": "2.0.3",
|
|
||||||
"isarray": "0.0.1",
|
|
||||||
"string_decoder": "0.10.31"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"safe-buffer": {
|
|
||||||
"version": "5.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
|
|
||||||
"integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg=="
|
|
||||||
},
|
|
||||||
"send": {
|
|
||||||
"version": "0.16.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/send/-/send-0.16.1.tgz",
|
|
||||||
"integrity": "sha512-ElCLJdJIKPk6ux/Hocwhk7NFHpI3pVm/IZOYWqUmoxcgeyM+MpxHHKhb8QmlJDX1pU6WrgaHBkVNm73Sv7uc2A==",
|
|
||||||
"requires": {
|
|
||||||
"debug": "2.6.9",
|
|
||||||
"depd": "1.1.1",
|
|
||||||
"destroy": "1.0.4",
|
|
||||||
"encodeurl": "1.0.1",
|
|
||||||
"escape-html": "1.0.3",
|
|
||||||
"etag": "1.8.1",
|
|
||||||
"fresh": "0.5.2",
|
|
||||||
"http-errors": "1.6.2",
|
|
||||||
"mime": "1.4.1",
|
|
||||||
"ms": "2.0.0",
|
|
||||||
"on-finished": "2.3.0",
|
|
||||||
"range-parser": "1.2.0",
|
|
||||||
"statuses": "1.3.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"serve-static": {
|
|
||||||
"version": "1.13.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.1.tgz",
|
|
||||||
"integrity": "sha512-hSMUZrsPa/I09VYFJwa627JJkNs0NrfL1Uzuup+GqHfToR2KcsXFymXSV90hoyw3M+msjFuQly+YzIH/q0MGlQ==",
|
|
||||||
"requires": {
|
|
||||||
"encodeurl": "1.0.1",
|
|
||||||
"escape-html": "1.0.3",
|
|
||||||
"parseurl": "1.3.2",
|
|
||||||
"send": "0.16.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"setprototypeof": {
|
|
||||||
"version": "1.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz",
|
|
||||||
"integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ=="
|
|
||||||
},
|
|
||||||
"statuses": {
|
|
||||||
"version": "1.3.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz",
|
|
||||||
"integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4="
|
|
||||||
},
|
|
||||||
"streamsearch": {
|
|
||||||
"version": "0.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz",
|
|
||||||
"integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo="
|
|
||||||
},
|
|
||||||
"string_decoder": {
|
|
||||||
"version": "0.10.31",
|
|
||||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
|
|
||||||
"integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ="
|
|
||||||
},
|
|
||||||
"type-is": {
|
|
||||||
"version": "1.6.15",
|
|
||||||
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.15.tgz",
|
|
||||||
"integrity": "sha1-yrEPtJCeRByChC6v4a1kbIGARBA=",
|
|
||||||
"requires": {
|
|
||||||
"media-typer": "0.3.0",
|
|
||||||
"mime-types": "2.1.17"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"typedarray": {
|
|
||||||
"version": "0.0.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
|
|
||||||
"integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
|
|
||||||
},
|
|
||||||
"unpipe": {
|
|
||||||
"version": "1.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
|
|
||||||
"integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
|
|
||||||
},
|
|
||||||
"util-deprecate": {
|
|
||||||
"version": "1.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
|
||||||
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
|
|
||||||
},
|
|
||||||
"utils-merge": {
|
|
||||||
"version": "1.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
|
|
||||||
"integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
|
|
||||||
},
|
|
||||||
"vary": {
|
|
||||||
"version": "1.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
|
||||||
"integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
|
|
||||||
},
|
|
||||||
"xtend": {
|
|
||||||
"version": "4.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz",
|
|
||||||
"integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68="
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
23
Node/package.json
Normal file
23
Node/package.json
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
{
|
||||||
|
"name": "Node DEMOS2 Cryptography Server",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "Exposes Cryptographic Functionality from Various Endpoints",
|
||||||
|
"main": "index.js",
|
||||||
|
"directories": {
|
||||||
|
"test": "test"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/vincentmdealmeida/DEMOS2"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "Bingsheng Zang, Thomas Smith",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"express": "^4.16.3",
|
||||||
|
"milagro-crypto-js": "git+https://github.com/milagro-crypto/milagro-crypto-js.git"
|
||||||
|
}
|
||||||
|
}
|
36
README.md
36
README.md
|
@ -1,10 +1,10 @@
|
||||||
# DEMOS2
|
# DEMOS2
|
||||||
|
|
||||||
Prototype Django based e-voting application, to demonstrate DEMOS2's client-side encryption e-voting.
|
Prototype Django-based e-voting application, to demonstrate DEMOS2's client-side encryption e-voting.
|
||||||
|
|
||||||
The previous repository for DEMOS2 by Carey Williams can be found at: https://github.com/CareyJWilliams/DEMOS2
|
The previous repository for DEMOS2 by Carey Williams can be found at: https://github.com/CareyJWilliams/DEMOS2
|
||||||
|
|
||||||
### Dependencies
|
### Main Application Dependencies
|
||||||
|
|
||||||
Python: Version 2.7 (Anything higher than this will not currently work)
|
Python: Version 2.7 (Anything higher than this will not currently work)
|
||||||
Python packages: Specified in 'requirements.txt' - PyCharm will detect these dependencies and offer installation
|
Python packages: Specified in 'requirements.txt' - PyCharm will detect these dependencies and offer installation
|
||||||
|
@ -17,9 +17,23 @@ Finally, with all the above dependencies in place, you can simply issue the foll
|
||||||
python manage.py migrate
|
python manage.py migrate
|
||||||
|
|
||||||
'aullauthdemo/settings.py' specifies the Google reCAPTCHA site key and private key which will need changing when deployed
|
'aullauthdemo/settings.py' specifies the Google reCAPTCHA site key and private key which will need changing when deployed
|
||||||
onto a new domain.
|
onto a new domain. There is also a DOMAIN setting within the file that needs updating during deployment as things
|
||||||
|
like email functionality depend on this setting for correct URL generation during event preparation etc.
|
||||||
|
|
||||||
### Running the server and creating a new user account
|
Emails from the application are currently sent from the following email account which can be updated within the settings:
|
||||||
|
|
||||||
|
demos2.no.reply@gmail.com
|
||||||
|
|
||||||
|
### NodeJS Dependencies
|
||||||
|
|
||||||
|
The Node.js crypto server depends on the milagro-crypto-js and express modules. A package.json file can be found in the
|
||||||
|
Node/ directory with these dependency requirements and therefore from this folder you can run:
|
||||||
|
|
||||||
|
npm install
|
||||||
|
|
||||||
|
Once the dependencies have been installed, you can then run the node server as per the below instructions.
|
||||||
|
|
||||||
|
### Step 1: Running the Python app and creating a new user account
|
||||||
|
|
||||||
You can run the server with the following command:
|
You can run the server with the following command:
|
||||||
|
|
||||||
|
@ -33,8 +47,16 @@ You can then click on 'Join' to create a new user account. Currently, a server e
|
||||||
email account saying something like 'Too Many Attempts'. Rest assured that the account will have been created. Navigate
|
email account saying something like 'Too Many Attempts'. Rest assured that the account will have been created. Navigate
|
||||||
back to the home page and you should be able to log in. This will hopefully be fixed in a future version.
|
back to the home page and you should be able to log in. This will hopefully be fixed in a future version.
|
||||||
|
|
||||||
### Other
|
### Step 2: Running Celery
|
||||||
|
|
||||||
This was included in the previous readme and may be required:
|
Celery is used to run tasks asynchronously and the DEMOS2 application can't run without this application. A bash script
|
||||||
|
called 'start_celery_worker.sh' is provided to make starting a worker as easy as possible:
|
||||||
|
|
||||||
The Node.js encryption server depends on the milagro-crypto-js library. Download the source and follow the instructions for installation: https://github.com/milagro-crypto/milagro-crypto-js. To install, place the package's files (`package.json` level) into the directory Node/milagro-crypto-js (a new folder) then run `npm install` in the Node folder. This should install dependencies including the local package you just added.
|
./start_celery_worker.sh
|
||||||
|
|
||||||
|
### Step 3: Running the NodeJS Server
|
||||||
|
|
||||||
|
The NodeJS server exposes a lot of cryptographic operations that the application depends on throughout. To run the
|
||||||
|
server, issue the following command line request from the Node/ folder:
|
||||||
|
|
||||||
|
node index.js
|
||||||
|
|
|
@ -4,18 +4,21 @@ import subprocess
|
||||||
import json
|
import json
|
||||||
import urllib2
|
import urllib2
|
||||||
|
|
||||||
#change this file name etc., temporary change to get it working for the meantime
|
|
||||||
'''
|
'''
|
||||||
|
|
||||||
All functions in this file have been re-implemenented by Thomas Smith
|
All functions in this file have been re-implemenented by Thomas Smith
|
||||||
|
|
||||||
|
File then updated by Vincent de Almeida. Changes include:
|
||||||
|
-Update filename to 'crypto_rpc' to reflect the RPC nature of the methods
|
||||||
|
|
||||||
'''
|
'''
|
||||||
def param():
|
def param():
|
||||||
jsondict = json.load(urllib2.urlopen('http://localhost:8080/param'))
|
url = 'http://localhost:8080/param' # RPC URL
|
||||||
|
jsondict = json.load(urllib2.urlopen(url))
|
||||||
return json.dumps(jsondict)
|
return json.dumps(jsondict)
|
||||||
|
|
||||||
def combpk(amount, pks):
|
def combpk(amount, pks):
|
||||||
url = 'http://localhost:8080/cmpkstring'
|
url = 'http://localhost:8080/cmpkstring' # RPC URL
|
||||||
querystring = '?number='+str(amount)
|
querystring = '?number='+str(amount)
|
||||||
for pk in pks:
|
for pk in pks:
|
||||||
querystring += '&PK='+pk
|
querystring += '&PK='+pk
|
||||||
|
@ -26,7 +29,7 @@ def combpk(amount, pks):
|
||||||
return json.dumps(jsondict)
|
return json.dumps(jsondict)
|
||||||
|
|
||||||
def addec(amount, ciphers):
|
def addec(amount, ciphers):
|
||||||
url = 'http://localhost:8080/addec'
|
url = 'http://localhost:8080/addec' # RPC URL
|
||||||
querystring = '?number='+str(amount)
|
querystring = '?number='+str(amount)
|
||||||
c1s = ciphers['c1s']
|
c1s = ciphers['c1s']
|
||||||
c2s = ciphers['c2s']
|
c2s = ciphers['c2s']
|
||||||
|
@ -40,7 +43,7 @@ def addec(amount, ciphers):
|
||||||
return json.dumps(jsondict)
|
return json.dumps(jsondict)
|
||||||
|
|
||||||
def tally(amount, param, decs, cipher):
|
def tally(amount, param, decs, cipher):
|
||||||
url = 'http://localhost:8080/tally'
|
url = 'http://localhost:8080/tally' # RPC URL
|
||||||
querystring = '?number='+str(amount)
|
querystring = '?number='+str(amount)
|
||||||
querystring += '¶m='+urllib2.quote(str(param))
|
querystring += '¶m='+urllib2.quote(str(param))
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from datetime import datetime
|
import json
|
||||||
|
|
||||||
|
from django.core.mail import send_mail
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
|
||||||
|
@ -9,9 +10,17 @@ from allauthdemo.auth.models import DemoUser
|
||||||
|
|
||||||
class EmailUser(models.Model):
|
class EmailUser(models.Model):
|
||||||
email = models.CharField(max_length=80, unique=True)
|
email = models.CharField(max_length=80, unique=True)
|
||||||
|
|
||||||
|
def send_email(self, subject, message, from_email=None):
|
||||||
|
"""
|
||||||
|
Sends an email to this User.
|
||||||
|
"""
|
||||||
|
send_mail(subject, message, from_email, [self.email])
|
||||||
|
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
return self.email
|
return self.email
|
||||||
|
|
||||||
|
|
||||||
class Event(models.Model):
|
class Event(models.Model):
|
||||||
users_organisers = models.ManyToManyField(DemoUser, blank=True, related_name="organisers")
|
users_organisers = models.ManyToManyField(DemoUser, blank=True, related_name="organisers")
|
||||||
users_trustees = models.ManyToManyField(EmailUser, blank=True, related_name="trustees")
|
users_trustees = models.ManyToManyField(EmailUser, blank=True, related_name="trustees")
|
||||||
|
@ -26,11 +35,32 @@ class Event(models.Model):
|
||||||
c_email = models.CharField(max_length=512, blank=True)
|
c_email = models.CharField(max_length=512, blank=True)
|
||||||
trustees = models.CharField(max_length=4096)
|
trustees = models.CharField(max_length=4096)
|
||||||
|
|
||||||
|
def EID_hr(self):
|
||||||
|
EID_json = json.loads(self.EID)
|
||||||
|
return EID_json['hr']
|
||||||
|
|
||||||
|
def EID_crypto(self):
|
||||||
|
EID_json = json.loads(self.EID)
|
||||||
|
EID_crypto_str = EID_json['crypto']
|
||||||
|
return json.loads(EID_crypto_str)
|
||||||
|
|
||||||
def duration(self):
|
def duration(self):
|
||||||
duration_str = self.start_time.strftime("%d-%m-%y %H:%M")
|
duration_str = self.start_time_formatted()
|
||||||
duration_str = duration_str + " - " + self.end_time.strftime("%d-%m-%y %H:%M %Z")
|
duration_str = duration_str + " - " + self.end_time_formatted_utc()
|
||||||
return duration_str
|
return duration_str
|
||||||
|
|
||||||
|
def start_time_formatted(self):
|
||||||
|
return self.start_time.strftime("%d-%m-%y %H:%M")
|
||||||
|
|
||||||
|
def start_time_formatted_utc(self):
|
||||||
|
return self.start_time.strftime("%d-%m-%y %H:%M %Z")
|
||||||
|
|
||||||
|
def end_time_formatted(self):
|
||||||
|
return self.end_time.strftime("%d-%m-%y %H:%M")
|
||||||
|
|
||||||
|
def end_time_formatted_utc(self):
|
||||||
|
return self.end_time.strftime("%d-%m-%y %H:%M %Z")
|
||||||
|
|
||||||
def status(self):
|
def status(self):
|
||||||
status_str = ""
|
status_str = ""
|
||||||
|
|
||||||
|
@ -113,14 +143,4 @@ class Organiser(models.Model):
|
||||||
email = models.CharField(max_length=100, blank=False, null=False)
|
email = models.CharField(max_length=100, blank=False, null=False)
|
||||||
event = models.ForeignKey(Event, on_delete=models.CASCADE)
|
event = models.ForeignKey(Event, on_delete=models.CASCADE)
|
||||||
|
|
||||||
'''
|
|
||||||
class Organiser(models.Model):
|
|
||||||
user = models.ForeignKey(DemoUser, on_delete=models.CASCADE)
|
|
||||||
event = models.ForeignKey(Event, on_delete=models.CASCADE)
|
|
||||||
|
|
||||||
class Trustee(models.Model):
|
|
||||||
user = models.ForeignKey(DemoUser, on_delete=models.CASCADE)
|
|
||||||
event = models.ForeignKey(Event, on_delete=models.CASCADE)
|
|
||||||
'''
|
|
||||||
#class EventOrganisers():
|
|
||||||
#event = models.ForeignKey(Event, on_delete=models.CASCADE)
|
|
||||||
|
|
|
@ -1,14 +1,29 @@
|
||||||
from __future__ import absolute_import
|
from __future__ import absolute_import
|
||||||
import csv
|
|
||||||
from os import urandom
|
|
||||||
import base64
|
import base64
|
||||||
from io import StringIO
|
import json
|
||||||
|
from os import urandom
|
||||||
from celery import task
|
from celery import task
|
||||||
|
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from django.core.validators import EmailValidator
|
from django.core.validators import EmailValidator
|
||||||
from django.core.mail import send_mail
|
from django.core.mail import send_mail
|
||||||
from allauthdemo.polls.models import Ballot, Event, EmailUser, AccessKey
|
from django.conf import settings
|
||||||
from .cpp_calls import param, combpk, addec, tally
|
|
||||||
|
from allauthdemo.polls.models import AccessKey
|
||||||
|
|
||||||
|
from .crypto_rpc import param, combpk, addec, tally
|
||||||
|
|
||||||
|
'''
|
||||||
|
Goal: This py file defines celery tasks that can be initiated
|
||||||
|
|
||||||
|
The following tasks were re-implemented by Thomas Smith: generate_event_param, tally_results, generate_combpk, generate_enc
|
||||||
|
|
||||||
|
This file was also updated by Vincent de Almeida
|
||||||
|
'''
|
||||||
|
|
||||||
|
# Will store the result of the initial cal to param() from .cpp_calls
|
||||||
|
group_param = None
|
||||||
|
|
||||||
def is_valid_email(email):
|
def is_valid_email(email):
|
||||||
try:
|
try:
|
||||||
|
@ -23,36 +38,59 @@ def create_ballots(poll):
|
||||||
for voter in poll.event.voters.all():
|
for voter in poll.event.voters.all():
|
||||||
ballot = poll.ballots.create(voter=voter, poll=poll)
|
ballot = poll.ballots.create(voter=voter, poll=poll)
|
||||||
|
|
||||||
|
'''
|
||||||
|
Will generate a key for accessing either the event preparation page or the voting page
|
||||||
|
'''
|
||||||
|
def gen_access_key():
|
||||||
|
return base64.urlsafe_b64encode(urandom(16)).decode('utf-8')
|
||||||
|
|
||||||
|
'''
|
||||||
|
Emails an event preparation URL containing an access key for all of the trustees for an event
|
||||||
|
'''
|
||||||
@task()
|
@task()
|
||||||
def create_voters(csvfile, event):
|
def email_trustees_prep(trustees, event):
|
||||||
print("Creating voters for event " + event.title)
|
email_subject = "Key Generation and Preparation for Event '" + event.title + "'"
|
||||||
reader = csv.reader(csvfile, delimiter=',')
|
|
||||||
string = ""
|
# Plain text email - this could be replaced for a HTML-based email in the future
|
||||||
for row in reader:
|
email_body = "Please visit the following URL to prepare the event and generate your trustee secret key:\n\n"
|
||||||
email = string.join(row)
|
url_base = "http://" + settings.DOMAIN + "/event/" + str(event.pk) + "/prepare/?key="
|
||||||
print(email)
|
email_body = email_body + url_base
|
||||||
#testvoter = EmailUser.objects.get_or_create(email='notarealemail@live.com')[0]
|
|
||||||
#event.voters.add(testvoter)
|
for trustee in trustees:
|
||||||
if (is_valid_email(email)):
|
# Generate a key and create an AccessKey object
|
||||||
voter = EmailUser.objects.get_or_create(email=email)[0]
|
key = gen_access_key()
|
||||||
event.voters.add(voter)
|
AccessKey.objects.create(user=trustee, event=event, key=key)
|
||||||
key = base64.urlsafe_b64encode(urandom(16)).decode('utf-8')
|
|
||||||
|
trustee.send_email(email_subject, email_body + key)
|
||||||
|
|
||||||
|
'''
|
||||||
|
Emails the access keys for all of the voters for an event
|
||||||
|
'''
|
||||||
|
@task()
|
||||||
|
def email_voters_a_key(voters, event):
|
||||||
|
email_subject = "Voting Access for Event '" + event.title + "'"
|
||||||
|
email_body = 'Key: '
|
||||||
|
|
||||||
|
for voter in voters:
|
||||||
|
# Generate a key and create an AccessKey object
|
||||||
|
key = gen_access_key()
|
||||||
AccessKey.objects.create(user=voter, event=event, key=key)
|
AccessKey.objects.create(user=voter, event=event, key=key)
|
||||||
send_mail(
|
|
||||||
'Your Voting Key',
|
voter.send_email(email_subject, email_body + key)
|
||||||
'Key: ' + key,
|
|
||||||
'from@example.com',
|
|
||||||
[string.join(row)],
|
|
||||||
fail_silently=False,
|
|
||||||
)
|
|
||||||
'''
|
'''
|
||||||
|
Updates the EID of an event to contain 2 event IDs: a human readable one (hr) and a crypto one (GP from param())
|
||||||
Starting here: functions re-implemented by Thomas Smith
|
|
||||||
|
|
||||||
'''
|
'''
|
||||||
@task()
|
@task()
|
||||||
def generate_event_param(event):
|
def update_EID(event):
|
||||||
event.EID = param()
|
global group_param
|
||||||
|
if group_param is None:
|
||||||
|
group_param = param()
|
||||||
|
|
||||||
|
EID = {}
|
||||||
|
EID['hr'] = event.EID
|
||||||
|
EID['crypto'] = group_param
|
||||||
|
event.EID = json.dumps(EID)
|
||||||
event.save()
|
event.save()
|
||||||
|
|
||||||
@task()
|
@task()
|
||||||
|
@ -98,20 +136,4 @@ def generate_enc(poll):
|
||||||
poll.enc = addec(amount, ciphers)
|
poll.enc = addec(amount, ciphers)
|
||||||
poll.save()
|
poll.save()
|
||||||
|
|
||||||
'''
|
|
||||||
|
|
||||||
End of re-implemented code
|
|
||||||
|
|
||||||
'''
|
|
||||||
|
|
||||||
@task()
|
|
||||||
def add(x, y):
|
|
||||||
return x + y
|
|
||||||
|
|
||||||
@task()
|
|
||||||
def mul(x, y):
|
|
||||||
return x * y
|
|
||||||
|
|
||||||
@task()
|
|
||||||
def xsum(numbers):
|
|
||||||
return sum(numbers)
|
|
||||||
|
|
|
@ -1,13 +1,9 @@
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from django.utils.dateparse import parse_datetime
|
from django.utils.dateparse import parse_datetime
|
||||||
|
|
||||||
from allauthdemo.polls.models import Event
|
from allauthdemo.polls.models import Event, Poll, PollOption, EmailUser
|
||||||
from allauthdemo.polls.models import Poll
|
|
||||||
from allauthdemo.polls.models import PollOption
|
|
||||||
from allauthdemo.polls.models import EmailUser
|
|
||||||
from allauthdemo.auth.models import DemoUser
|
from allauthdemo.auth.models import DemoUser
|
||||||
|
|
||||||
'''
|
'''
|
||||||
|
@ -382,6 +378,7 @@ class EventModelAdaptor:
|
||||||
errors_summary = errors_summary + str(i + 1) + " "
|
errors_summary = errors_summary + str(i + 1) + " "
|
||||||
|
|
||||||
self.invalid_form_fields['polls_data'] = {'val': polls_json}
|
self.invalid_form_fields['polls_data'] = {'val': polls_json}
|
||||||
|
self.invalid_form_fields['poll_count'] = {'val': poll_count}
|
||||||
|
|
||||||
if not valid and len(errors_summary) > 34:
|
if not valid and len(errors_summary) > 34:
|
||||||
errors_summary = errors_summary + "and can be corrected by editing them."
|
errors_summary = errors_summary + "and can be corrected by editing them."
|
||||||
|
@ -536,7 +533,7 @@ class EventModelAdaptor:
|
||||||
organisers_list = self.form_data.pop('organiser-email-input')
|
organisers_list = self.form_data.pop('organiser-email-input')
|
||||||
|
|
||||||
for organiser in organisers_list:
|
for organiser in organisers_list:
|
||||||
if organiser != '' and DemoUser.objects.filter(email=organiser).count() == 1:
|
if organiser != '' and DemoUser.objects.filter(email=organiser).exists():
|
||||||
self.organisers.append(DemoUser.objects.filter(email=organiser).get())
|
self.organisers.append(DemoUser.objects.filter(email=organiser).get())
|
||||||
|
|
||||||
# Extract the list of trustees
|
# Extract the list of trustees
|
||||||
|
@ -544,7 +541,7 @@ class EventModelAdaptor:
|
||||||
|
|
||||||
for trustee in trustees_list:
|
for trustee in trustees_list:
|
||||||
if trustee != '':
|
if trustee != '':
|
||||||
if EmailUser.objects.filter(email=trustee).count() == 1:
|
if EmailUser.objects.filter(email=trustee).exists():
|
||||||
self.trustees.append(EmailUser.objects.filter(email=trustee).get())
|
self.trustees.append(EmailUser.objects.filter(email=trustee).get())
|
||||||
else:
|
else:
|
||||||
self.trustees.append(EmailUser(email=trustee))
|
self.trustees.append(EmailUser(email=trustee))
|
||||||
|
@ -555,7 +552,7 @@ class EventModelAdaptor:
|
||||||
|
|
||||||
for voter_email in voters_email_list:
|
for voter_email in voters_email_list:
|
||||||
if voter_email != '':
|
if voter_email != '':
|
||||||
if EmailUser.objects.filter(email=voter_email).count() == 1:
|
if EmailUser.objects.filter(email=voter_email).exists():
|
||||||
self.voters.append(EmailUser.objects.filter(email=voter_email).get())
|
self.voters.append(EmailUser.objects.filter(email=voter_email).get())
|
||||||
else:
|
else:
|
||||||
self.voters.append(EmailUser(email=voter_email))
|
self.voters.append(EmailUser(email=voter_email))
|
||||||
|
@ -634,14 +631,15 @@ class EventModelAdaptor:
|
||||||
|
|
||||||
# Add the list of trustees to the event, making sure they're instantiated
|
# Add the list of trustees to the event, making sure they're instantiated
|
||||||
for trustee in self.trustees:
|
for trustee in self.trustees:
|
||||||
if EmailUser.objects.filter(email=trustee.email).count() == 0:
|
if not EmailUser.objects.filter(email=trustee.email).exists():
|
||||||
trustee.save()
|
trustee.save()
|
||||||
|
|
||||||
self.event.users_trustees = self.trustees
|
self.event.users_trustees = self.trustees
|
||||||
|
|
||||||
# Add the list of voters to the event, making sure they're instantiated
|
# Add the list of voters to the event, making sure they're instantiated
|
||||||
|
# Additionally, generating the AccessKey for voters
|
||||||
for voter in self.voters:
|
for voter in self.voters:
|
||||||
if EmailUser.objects.filter(email=voter.email).count() == 0:
|
if not EmailUser.objects.filter(email=voter.email).exists():
|
||||||
voter.save()
|
voter.save()
|
||||||
|
|
||||||
self.event.voters = self.voters
|
self.event.voters = self.voters
|
||||||
|
@ -655,10 +653,10 @@ class EventModelAdaptor:
|
||||||
|
|
||||||
self.event.save()
|
self.event.save()
|
||||||
|
|
||||||
# Finally perform a data clean up
|
# Finally return a reference to the event
|
||||||
self.__clear_data()
|
return self.event
|
||||||
|
|
||||||
def __clear_data(self):
|
def clear_data(self):
|
||||||
self.form_data = None
|
self.form_data = None
|
||||||
self.form_data_validation = None
|
self.form_data_validation = None
|
||||||
self.invalid_form_fields = {}
|
self.invalid_form_fields = {}
|
||||||
|
|
|
@ -17,8 +17,7 @@ from .forms import EventForm, PollForm, OptionFormset, QuestionFormset, Organise
|
||||||
from .models import Event, Poll, PollOption, EmailUser, Ballot, TrusteeKey, Decryption
|
from .models import Event, Poll, PollOption, EmailUser, Ballot, TrusteeKey, Decryption
|
||||||
from allauthdemo.auth.models import DemoUser
|
from allauthdemo.auth.models import DemoUser
|
||||||
|
|
||||||
from .tasks import create_voters, create_ballots, generate_event_param, generate_combpk, generate_enc, tally_results
|
from .tasks import email_trustees_prep, update_EID, generate_combpk, generate_enc, tally_results
|
||||||
from .cpp_calls import param, addec, combpk, tally
|
|
||||||
|
|
||||||
from .utils.EventModelAdaptor import EventModelAdaptor
|
from .utils.EventModelAdaptor import EventModelAdaptor
|
||||||
|
|
||||||
|
@ -38,6 +37,7 @@ class EventDetailView(generic.DetailView):
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super(EventDetailView, self).get_context_data(**kwargs)
|
context = super(EventDetailView, self).get_context_data(**kwargs)
|
||||||
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['now'] = timezone.now()
|
#context['now'] = timezone.now()
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
@ -153,24 +153,38 @@ def view_poll(request, event_id, poll_num):
|
||||||
})
|
})
|
||||||
|
|
||||||
def event_trustee_setup(request, event_id):
|
def event_trustee_setup(request, event_id):
|
||||||
|
# Obtain the event and the event preparation access key that's been supplied
|
||||||
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)
|
||||||
if (access_key):
|
|
||||||
|
# If the a_key is present, check it's valid and related to a trustee EmailUser instance for this event
|
||||||
|
if access_key:
|
||||||
email_key = event.keys.filter(key=access_key)
|
email_key = event.keys.filter(key=access_key)
|
||||||
if (email_key.exists() and event.users_trustees.filter(email=email_key[0].user.email).exists()):
|
if email_key.exists() and event.users_trustees.filter(email=email_key[0].user.email).exists():
|
||||||
if (TrusteeKey.objects.filter(event=event, user=email_key[0].user).exists()):
|
if TrusteeKey.objects.filter(event=event, user=email_key[0].user).exists():
|
||||||
messages.add_message(request, messages.WARNING, 'You have already submitted your key for this event')
|
messages.add_message(request, messages.WARNING, 'You have already submitted your key for this event')
|
||||||
return HttpResponseRedirect(reverse("user_home"))
|
return HttpResponseRedirect(reverse("user_home"))
|
||||||
if (request.method == "POST"):
|
if request.method == "POST":
|
||||||
form = EventSetupForm(request.POST)
|
form = EventSetupForm(request.POST)
|
||||||
if (form.is_valid()):
|
|
||||||
|
# If form data is valid, create a TrusteeKey object with the supplied public key
|
||||||
|
if form.is_valid():
|
||||||
public_key = request.POST["public_key"]
|
public_key = request.POST["public_key"]
|
||||||
key = TrusteeKey.objects.get_or_create(event=event, user=email_key[0].user)[0]
|
key = TrusteeKey.objects.get_or_create(event=event, user=email_key[0].user)[0]
|
||||||
key.key = public_key
|
key.key = public_key
|
||||||
key.save()
|
key.save()
|
||||||
if (event.trustee_keys.count() == event.users_trustees.count()): # ready for combpk
|
|
||||||
|
# When all trustees have supplied their public key, we can combine them to create a master key
|
||||||
|
# The event will now be ready to receive votes on the various polls that have been defined -
|
||||||
|
# voters therefore need to be informed
|
||||||
|
if event.trustee_keys.count() == event.users_trustees.count():
|
||||||
generate_combpk.delay(event)
|
generate_combpk.delay(event)
|
||||||
messages.add_message(request, messages.SUCCESS, 'You have successfully submitted your public key for this event')
|
# TODO: Create Celery task that generates voting URLs for voters as well as creates the ballots
|
||||||
|
|
||||||
|
success_msg = 'You have successfully submitted your public key for this event'
|
||||||
|
messages.add_message(request, messages.SUCCESS, success_msg)
|
||||||
|
|
||||||
|
# This re-direct may not be appropriate for trustees that don't have logins
|
||||||
return HttpResponseRedirect(reverse("user_home"))
|
return HttpResponseRedirect(reverse("user_home"))
|
||||||
else:
|
else:
|
||||||
form = EventSetupForm()
|
form = EventSetupForm()
|
||||||
|
@ -247,11 +261,11 @@ def manage_questions(request, event_id):
|
||||||
poll.save()
|
poll.save()
|
||||||
formset = OptionFormset(request.POST, prefix="formset_organiser", instance=poll)
|
formset = OptionFormset(request.POST, prefix="formset_organiser", instance=poll)
|
||||||
if formset.is_valid():
|
if formset.is_valid():
|
||||||
for form in formset:
|
|
||||||
formset.save()
|
formset.save()
|
||||||
create_ballots.delay(poll)
|
#create_ballots.delay(poll)
|
||||||
messages.add_message(request, messages.SUCCESS, 'Poll created successfully')
|
messages.add_message(request, messages.SUCCESS, 'Poll created successfully')
|
||||||
return HttpResponseRedirect(reverse('polls:view-poll', kwargs={'event_id': poll.event_id, 'poll_num': event.polls.count() }))
|
return HttpResponseRedirect(reverse('polls:event-polls', args=[poll.event_id]))
|
||||||
|
|
||||||
return render(request, "polls/create_poll.html", {"event": event, "question_form": form, "option_formset": formset})
|
return render(request, "polls/create_poll.html", {"event": event, "question_form": form, "option_formset": formset})
|
||||||
|
|
||||||
elif request.method == "GET":
|
elif request.method == "GET":
|
||||||
|
@ -296,17 +310,28 @@ def create_event(request):
|
||||||
'''Process form data based on above results'''
|
'''Process form data based on above results'''
|
||||||
if result['success']:
|
if result['success']:
|
||||||
if form_data_valid:
|
if form_data_valid:
|
||||||
|
# Create the new event using the form data
|
||||||
adaptor.extractData()
|
adaptor.extractData()
|
||||||
adaptor.updateModel()
|
new_event = adaptor.updateModel()
|
||||||
|
|
||||||
|
# Update the EID to include the GP in its EID
|
||||||
|
update_EID.delay(new_event)
|
||||||
|
|
||||||
|
# Send an email to all trustees for event preparation
|
||||||
|
trustees = new_event.users_trustees.all()
|
||||||
|
email_trustees_prep.delay(trustees, new_event)
|
||||||
|
|
||||||
|
adaptor.clear_data()
|
||||||
|
|
||||||
return HttpResponseRedirect(reverse('polls:index'))
|
return HttpResponseRedirect(reverse('polls:index'))
|
||||||
else:
|
else:
|
||||||
invalid_fields = adaptor.getInvalidFormFields()
|
invalid_fields = adaptor.getInvalidFormFields()
|
||||||
|
adaptor.clear_data()
|
||||||
return render_invalid(request, events, demo_users, invalid_fields)
|
return render_invalid(request, events, demo_users, invalid_fields)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
invalid_fields = adaptor.getInvalidFormFields()
|
invalid_fields = adaptor.getInvalidFormFields()
|
||||||
invalid_fields['recaptcha'] = {'error': 'The reCAPTCHA server validation failed, please try again.'}
|
invalid_fields['recaptcha'] = {'error': 'The reCAPTCHA server validation failed, please try again.'}
|
||||||
|
adaptor.clear_data()
|
||||||
return render_invalid(request, events, demo_users, invalid_fields)
|
return render_invalid(request, events, demo_users, invalid_fields)
|
||||||
|
|
||||||
elif request.method == "GET":
|
elif request.method == "GET":
|
||||||
|
|
|
@ -26,6 +26,9 @@ TEMPLATE_DEBUG = True
|
||||||
|
|
||||||
ALLOWED_HOSTS = ['web.server.com']
|
ALLOWED_HOSTS = ['web.server.com']
|
||||||
|
|
||||||
|
# Domain the application is deployed on (Needs changing for production)
|
||||||
|
# This must not include the protocol nor any trailing slashes as application code should just add this in
|
||||||
|
DOMAIN = "127.0.0.1:8000"
|
||||||
|
|
||||||
# Application definition
|
# Application definition
|
||||||
|
|
||||||
|
@ -163,8 +166,8 @@ MESSAGE_STORAGE = 'django.contrib.messages.storage.session.SessionStorage'
|
||||||
#EMAIL_PORT = 1025
|
#EMAIL_PORT = 1025
|
||||||
|
|
||||||
EMAIL_HOST = 'smtp.gmail.com'
|
EMAIL_HOST = 'smtp.gmail.com'
|
||||||
EMAIL_HOST_USER = 'username@gmail.com'
|
EMAIL_HOST_USER = 'demos2.no.reply@gmail.com'
|
||||||
EMAIL_HOST_PASSWORD = 'password'
|
EMAIL_HOST_PASSWORD = 'Demos2LancsUni'
|
||||||
EMAIL_PORT = 587
|
EMAIL_PORT = 587
|
||||||
EMAIL_USE_TLS = True
|
EMAIL_USE_TLS = True
|
||||||
|
|
||||||
|
|
|
@ -150,7 +150,7 @@
|
||||||
//new function
|
//new function
|
||||||
demosEncrypt.generateKeys = function() {
|
demosEncrypt.generateKeys = function() {
|
||||||
parameter = $("#event-param").val();
|
parameter = $("#event-param").val();
|
||||||
var tempParams = JSON.parse(parameter);
|
var tempParams = JSON.parse(JSON.parse(parameter).crypto);
|
||||||
//the full objects need to be initalised as per the library, then copy the values we need into it
|
//the full objects need to be initalised as per the library, then copy the values we need into it
|
||||||
//I follow Bingsheng's code as to what objects are used in the parameter object
|
//I follow Bingsheng's code as to what objects are used in the parameter object
|
||||||
var ctx = new CTX("BN254CX"); //new context we can use
|
var ctx = new CTX("BN254CX"); //new context we can use
|
||||||
|
|
|
@ -26,11 +26,13 @@
|
||||||
}
|
}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
{% if invalid_fields.poll_count %}pollCount = {{ invalid_fields.poll_count.val }};{% endif %}
|
||||||
</script>
|
</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 to enable automatic translations -->
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="page-header">
|
<div class="page-header">
|
||||||
<h2>Create New Event with Polls</h2>
|
<h2>Create New Event with Polls</h2>
|
||||||
|
@ -419,7 +421,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- { Hidden field: Number of Polls } -->
|
<!-- { Hidden field: Number of Polls } -->
|
||||||
<input type="number" id="poll-count-input" name="poll-count-input" class="hidden">
|
<input type="number" id="poll-count-input" name="poll-count-input" class="hidden" {% if invalid_fields.poll_count %}value="{{ invalid_fields.poll_count.val }}"{% endif %}>
|
||||||
<!-- Organisers -->
|
<!-- Organisers -->
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="organisers-input" class="col-sm-3 col-md-2 control-label">Organisers:</label> <!-- This text can be a template variable -->
|
<label for="organisers-input" class="col-sm-3 col-md-2 control-label">Organisers:</label> <!-- This text can be a template variable -->
|
||||||
|
|
|
@ -6,30 +6,45 @@
|
||||||
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<a href="{% url 'polls:index' %}"><< Back to Events List</a>
|
<a href="{% url 'polls:index' %}"><< Back to Events List</a>
|
||||||
|
{% if is_organiser %}
|
||||||
|
<div>
|
||||||
|
<!-- Heading -->
|
||||||
|
<div class="col-xs-7 col-sm-9 col-md-10">
|
||||||
|
<h2>Event: {{object.title}}</h2>
|
||||||
|
</div>
|
||||||
|
<!-- Edit Button -->
|
||||||
|
<div class="col-xs-5 col-sm-3 col-md-2 marginTopEditButton">
|
||||||
|
<a href="{% url 'polls:edit-event' event.id %}" class="btn btn-primary" style="float: right;">
|
||||||
|
<span class="fa fa-pencil"></span> Edit
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<br/>
|
||||||
|
<h2>Event: {{object.title}}</h2>
|
||||||
|
{% endif %}
|
||||||
|
<div class="overviewPadding {% if is_organiser %}marginTopOverview{% endif %}">
|
||||||
|
<hr/>
|
||||||
|
<span>By: {{object.users_organisers.all}}</span>
|
||||||
|
<br/>
|
||||||
|
<span>Ready / Prepared: {{ object.prepared }}</span>
|
||||||
|
<br/>
|
||||||
|
<span>Event Status: {{ object.status }}</span>
|
||||||
|
</div>
|
||||||
<br/>
|
<br/>
|
||||||
<h1>Event: {{object.title}}</h1>
|
|
||||||
<p>By {{object.users_organisers.all}}</p>
|
|
||||||
<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.id %}">Details</a>
|
<a href="{% url 'polls:view-event' event.id %}"><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.id %}">Polls</a>
|
<a href="{% url 'polls:event-polls' event.id %}"><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-organisers' event.id %}">Organisers</a>
|
<a href="{% url 'polls:event-organisers' event.id %}"><strong>Entities</strong></a>
|
||||||
</li>
|
</li>
|
||||||
{% if event.prepared == False and is_trustee %}
|
|
||||||
<li class="">
|
|
||||||
<a href="{% url 'polls:prepare-event' event.id %}">Prepare Event</a>
|
|
||||||
</li>
|
|
||||||
{% endif %}
|
|
||||||
{% if is_organiser %}
|
{% if is_organiser %}
|
||||||
<li class="">
|
|
||||||
<a href="{% url 'polls:edit-event' event.id %}"><span class="fa fa-pencil"></span> Edit Event</a>
|
|
||||||
</li>
|
|
||||||
<li class="{% block event_nav_launch %}{% endblock %}">
|
<li class="{% block event_nav_launch %}{% endblock %}">
|
||||||
<a href="{% url 'polls:launch-event' event.id %}">Launch Event</a>
|
<a href="{% url 'polls:launch-event' event.id %}"><strong>Advanced</strong></a>
|
||||||
</li>
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
|
@ -4,10 +4,17 @@
|
||||||
|
|
||||||
{% block event_nav_details %}active{% endblock %}
|
{% block event_nav_details %}active{% endblock %}
|
||||||
{% block event_content %}
|
{% block event_content %}
|
||||||
<h2>Event Details</h2>
|
|
||||||
<span>Ready/Prepared: {{ event.prepared }}</span>
|
|
||||||
<br/>
|
<br/>
|
||||||
<span>Start Time: {{ event.start_time }}</span>
|
<span><strong>Start Time:</strong> {{ event.start_time_formatted_utc }}</span>
|
||||||
<br />
|
<br />
|
||||||
<span>End Time: {{ event.end_time }}</span>
|
<span><strong>End Time:</strong> {{ event.end_time_formatted_utc }}</span>
|
||||||
|
<br/>
|
||||||
|
<br/>
|
||||||
|
<span><strong>Number of Polls:</strong> {{ event.polls.count }}</span>
|
||||||
|
<br/>
|
||||||
|
<span><strong>Number of Organisers:</strong> {{ event.users_organisers.count }}</span>
|
||||||
|
<br/>
|
||||||
|
<span><strong>Number of Trustees:</strong> {{ event.users_trustees.count }}</span>
|
||||||
|
<br/>
|
||||||
|
<span><strong>Number of Voters:</strong> {{ event.voters.count }}</span>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -4,11 +4,16 @@
|
||||||
|
|
||||||
{% block event_nav_launch %}active{% endblock %}
|
{% block event_nav_launch %}active{% endblock %}
|
||||||
{% block event_content %}
|
{% block event_content %}
|
||||||
<h2>Event Details</h2>
|
|
||||||
<span>Trustee keys: {{ event.trustee_keys.count }} / {{ event.users_trustees.count }}</span>
|
|
||||||
<br/>
|
<br/>
|
||||||
<span>EID (param): {{ event.EID }} </span>
|
<span><strong>Trustee Keys Received:</strong> {{ event.trustee_keys.count }} / {{ event.users_trustees.count }}</span>
|
||||||
<br />
|
<br />
|
||||||
<span>Public key: {{ event.public_key }} </span>
|
<br/>
|
||||||
|
<span><strong>Human-readable Event ID:</strong> {{ event.EID_hr }} </span>
|
||||||
|
<br />
|
||||||
|
<br/>
|
||||||
|
<span><strong>Group Param ID:</strong> {{ event.EID_crypto }} </span>
|
||||||
|
<br />
|
||||||
|
<br/>
|
||||||
|
<span><strong>Public key:</strong> {{ event.public_key }} </span>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -5,6 +5,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</h2>
|
||||||
|
<hr/>
|
||||||
{% if object.users_organisers.all %}
|
{% if object.users_organisers.all %}
|
||||||
<ul class="list-group">
|
<ul class="list-group">
|
||||||
{% for user in object.users_organisers.all %}
|
{% for user in object.users_organisers.all %}
|
||||||
|
@ -14,7 +15,9 @@
|
||||||
{% else %}
|
{% else %}
|
||||||
<p>No organisers for this Event.</p>
|
<p>No organisers for this Event.</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
<hr/>
|
||||||
<h2>Event Trustees</h2>
|
<h2>Event Trustees</h2>
|
||||||
|
<hr/>
|
||||||
{% if object.users_trustees.all %}
|
{% if object.users_trustees.all %}
|
||||||
<ul class="list-group">
|
<ul class="list-group">
|
||||||
{% for user in object.users_trustees.all %}
|
{% for user in object.users_trustees.all %}
|
||||||
|
@ -24,7 +27,9 @@
|
||||||
{% else %}
|
{% else %}
|
||||||
<p>No trustees for this Event.</p>
|
<p>No trustees for this Event.</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
<hr/>
|
||||||
<h2>Voters</h2>
|
<h2>Voters</h2>
|
||||||
|
<hr/>
|
||||||
{% if object.voters.all %}
|
{% if object.voters.all %}
|
||||||
<ul class="list-group">
|
<ul class="list-group">
|
||||||
{% for voter in object.voters.all %}
|
{% for voter in object.voters.all %}
|
||||||
|
|
|
@ -4,10 +4,8 @@
|
||||||
|
|
||||||
{% block event_nav_polls %}active{% endblock %}
|
{% block event_nav_polls %}active{% endblock %}
|
||||||
{% block event_content %}
|
{% block event_content %}
|
||||||
<h2>Event Polls</h2>
|
|
||||||
{% if object.polls.all %}
|
{% if object.polls.all %}
|
||||||
{% for poll in object.polls.all %}
|
{% for poll in object.polls.all %}
|
||||||
<hr/>
|
|
||||||
<h3>Poll: {{ poll.question_text }} (<a href="{% url 'polls:view-poll' event_id=event.id poll_num=forloop.counter %}">Edit</a>)</h3>
|
<h3>Poll: {{ poll.question_text }} (<a href="{% url 'polls:view-poll' event_id=event.id poll_num=forloop.counter %}">Edit</a>)</h3>
|
||||||
<br/>
|
<br/>
|
||||||
<h4>Poll Options:</h4>
|
<h4>Poll Options:</h4>
|
||||||
|
@ -17,12 +15,12 @@
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
<p>Minimum Number of Option Selections: {{ poll.min_num_selections }}. Maximum Number of Option Selections: {{ poll.max_num_selections }}.</p>
|
<p>Minimum Number of Option Selections: {{ poll.min_num_selections }}. Maximum Number of Option Selections: {{ poll.max_num_selections }}.</p>
|
||||||
|
<hr/>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% else %}
|
{% else %}
|
||||||
<p>No polls are available for this Event.</p>
|
<p>No polls are available for this Event.</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if is_organiser %}
|
{% if is_organiser %}
|
||||||
<hr/>
|
|
||||||
<a href="{% url 'polls:create-poll' event.id %}" class="btn btn-default" role="button">Add Poll</a>
|
<a href="{% url 'polls:create-poll' event.id %}" class="btn btn-default" role="button">Add Poll</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -7,28 +7,31 @@
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<h1>Event: {{event.title}}</h1>
|
<h2>Trustee Event Setup for Event '{{ event.title }}'</h2>
|
||||||
<h2>Trustee Setup</h2>
|
<hr/>
|
||||||
<div class="panel panel-default">
|
<div class="panel panel-default">
|
||||||
<div class="panel-heading">Public Key</div>
|
<div class="panel-heading"><strong>Step 1: Generate Your Secret Key</strong></div>
|
||||||
|
<div class="panel panel-body">
|
||||||
|
<input id="secret-key" class="textinput textInput form-control" type="text"/>
|
||||||
|
<input id="event-param" type="text" value="{{event.EID}}" hidden/>
|
||||||
|
<div class="alert alert-warning" role="alert" style="margin-top: 0.75em;">
|
||||||
|
<strong>Warning:</strong> This key can <strong>NOT</strong> be recalculated if forgotten or lost! Ensure you back this up.
|
||||||
|
</div>
|
||||||
|
<button id="keygen-btn" onclick="demosEncrypt.generateKeys()" class="btn btn-success">Generate</button>
|
||||||
|
<a id="download-btn" role="button" href="#" class="btn btn-primary" disabled>Download</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading"><strong>Step 2: Submit Your Public Key</strong></div>
|
||||||
<div class="panel panel-body">
|
<div class="panel panel-body">
|
||||||
{% load crispy_forms_tags %}
|
{% load crispy_forms_tags %}
|
||||||
<form method="post" action="" class="">
|
<form method="post" action="" class="">
|
||||||
{% crispy form %}
|
{% crispy form %}
|
||||||
<input id="public-submit" class="btn btn-default" type="submit" value="Submit" disabled="true"/>
|
<p>Ensure your secret key is backed up before submitting.</p>
|
||||||
|
<input id="public-submit" class="btn btn-danger" type="submit" value="Submit" disabled/>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="panel panel-default">
|
|
||||||
<div class="panel-heading">Secret Key</div>
|
|
||||||
<div class="panel panel-body">
|
|
||||||
<input id="secret-key" class="textinput textInput form-control" type="text"></input>
|
|
||||||
<input id="event-param" type="text" value="{{event.EID}}" hidden></input>
|
|
||||||
<p>Make a backup of this secret key before submitting your public key. This can NOT be recalculated if forgotten!</p>
|
|
||||||
<button id="keygen-btn" onclick="demosEncrypt.generateKeys()" class="btn btn-default">Generate</button>
|
|
||||||
<a id="download-btn" role="button" href="#" class="btn btn-default" disabled>Download</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
1
start_celery_worker.sh
Executable file
1
start_celery_worker.sh
Executable file
|
@ -0,0 +1 @@
|
||||||
|
celery -A allauthdemo worker -l info
|
|
@ -154,7 +154,7 @@ input[type="file"] {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Events List page */
|
/* Events List page / Events Detail */
|
||||||
|
|
||||||
.statusBtn {
|
.statusBtn {
|
||||||
width: 74px;
|
width: 74px;
|
||||||
|
@ -167,3 +167,15 @@ input[type="file"] {
|
||||||
.marginTopCreateButton {
|
.marginTopCreateButton {
|
||||||
margin-top: 1.75em;
|
margin-top: 1.75em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.marginTopEditButton {
|
||||||
|
margin-top: 0.85em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.marginTopOverview {
|
||||||
|
margin-top: 5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.overviewPadding {
|
||||||
|
padding-left: 16px;
|
||||||
|
}
|
|
@ -705,7 +705,11 @@ function processFileChange(event) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
document.getElementById('files').addEventListener('change', processFileChange, false);
|
var filesHandle = document.getElementById('files');
|
||||||
|
|
||||||
|
if(filesHandle) {
|
||||||
|
filesHandle.addEventListener('change', processFileChange, false);
|
||||||
|
}
|
||||||
|
|
||||||
// reCAPTCHA
|
// reCAPTCHA
|
||||||
|
|
||||||
|
|
Reference in a new issue