diff --git a/Dockerfile b/Dockerfile
index 320f278b..12992c44 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -7,4 +7,9 @@ RUN cp -a /tmp/node_modules /dokomo/
ADD . /dokomo/
RUN pip install -r requirements.txt
RUN nodejs node_modules/gulp/bin/gulp.js build
+RUN mkdir -p /var/www/static/dist
+RUN cp -r /dokomo/dokomoforms/static/dist /var/www/static
+RUN cp /dokomo/dokomoforms/static/robots.txt /var/www/static/robots.txt
+RUN cp /dokomo/dokomoforms/static/manifest.json /var/www/static/manifest.json
+RUN cp /dokomo/dokomoforms/static/src/common/img/favicon.png /var/www/static/favicon.png
EXPOSE 8888
diff --git a/README.md b/README.md
index 30c1e12f..42a4d73d 100644
--- a/README.md
+++ b/README.md
@@ -15,7 +15,7 @@ Several solutions exist to handle offline mobile data collection. While this typ
**Dokomo strives to simplify the process by integrating the elements of a data collection effort into a unified system, from creation of mobile-ready surveys to quick analysis and visualization of the collected data.**
-The latest install script is here: https://raw.githubusercontent.com/SEL-Columbia/dokomoforms/v0.2.8/installer.sh
+The latest install script is here: https://raw.githubusercontent.com/SEL-Columbia/dokomoforms/v0.2.9/installer.sh
## Features
diff --git a/docker-compose.yml b/docker-compose.yml
index f976fdb4..087bcc7b 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -9,13 +9,16 @@ nginx:
- ./nginx.conf:/etc/nginx/nginx.conf
- /etc/letsencrypt:/etc/letsencrypt
- /tmp:/tmp
+ volumes_from:
+ - webapp
webapp:
- image: "selcolumbia/dokomoforms:0.2.8"
+ image: "selcolumbia/dokomoforms:0.2.9"
command: bash -c "./docker-wait-for-postgres.sh db && head -c 24 /dev/urandom > cookie_secret && python webapp.py"
links:
- "db:db"
volumes:
- ./local_config.py:/dokomo/local_config.py
+ - /var/www
db:
image: "mdillon/postgis:9.4"
environment:
diff --git a/dokomoforms/static/src/common/js/service-worker.js b/dokomoforms/static/src/common/js/service-worker.js
index 3b5481af..b4064ee4 100644
--- a/dokomoforms/static/src/common/js/service-worker.js
+++ b/dokomoforms/static/src/common/js/service-worker.js
@@ -1 +1,2 @@
// Nothing to see here
+// Eventually this should end up in /dist as well...
diff --git a/dokomoforms/templates/admin_homepage.html b/dokomoforms/templates/admin_homepage.html
index 8fed1b21..0bd40daf 100644
--- a/dokomoforms/templates/admin_homepage.html
+++ b/dokomoforms/templates/admin_homepage.html
@@ -82,7 +82,7 @@
Surveys
{% block extra_scripts %}
-
+
{% end %}
diff --git a/dokomoforms/templates/base.html b/dokomoforms/templates/base.html
index ed7e7db8..8e8e6b0c 100644
--- a/dokomoforms/templates/base.html
+++ b/dokomoforms/templates/base.html
@@ -13,10 +13,10 @@
-
+
-
-
+
+
{{ options.organization }} Surveys -- Powered by DokomoData
@@ -98,7 +98,7 @@
-
+
diff --git a/dokomoforms/templates/enumerate_homepage.html b/dokomoforms/templates/enumerate_homepage.html
index 7ab1a1c4..78d19e6d 100644
--- a/dokomoforms/templates/enumerate_homepage.html
+++ b/dokomoforms/templates/enumerate_homepage.html
@@ -1 +1,51 @@
-Hi you are an enumerator.
+{% from dokomoforms.options import options %}
+
+
+
+
+ {{ options.organization }} Surveys -- Powered by DokomoData
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Enumerator home page.
+
+
+
+
+
+
+
+
+
+
diff --git a/dokomoforms/templates/index.html b/dokomoforms/templates/index.html
index bcae590d..bb8ecca4 100644
--- a/dokomoforms/templates/index.html
+++ b/dokomoforms/templates/index.html
@@ -36,7 +36,7 @@ DokomoData
{% block extra_scripts %}
-
+
{% if message %}
+
-
+
-
+
+
+
{% end %}
diff --git a/installer.sh b/installer.sh
index e5329e26..e10583a7 100755
--- a/installer.sh
+++ b/installer.sh
@@ -1,5 +1,5 @@
#!/usr/bin/env sh
-# Dokomo Forms installer for version 0.2.8
+# Dokomo Forms installer for version 0.2.9
set -e
# Do you have docker installed?
@@ -88,7 +88,10 @@ printf " Installing SSL certificate. Make sure \n"
printf " you have set up the DNS records for \n"
printf " your domain to point to this machine. \n"
printf "========================================\n"
-$SUDO docker run -it --rm -p 443:443 -p 80:80 --name letsencrypt \
+# for some reason these directories need to exist beforehand on Fedora...
+$SUDO mkdir -p /etc/letsencrypt
+$SUDO mkdir -p /var/lib/letsencrypt
+$SUDO docker run -it --rm -p 443:443 -p 80:80 \
-v "/etc/letsencrypt:/etc/letsencrypt:Z" \
-v "/var/lib/letsencrypt:/var/lib/letsencrypt:Z" \
-v "/var/log:/var/log:Z" \
@@ -105,8 +108,8 @@ $SUDO openssl dhparam -out /etc/letsencrypt/live/$LETSENCRYPT_DIR/dhparam.pem 20
printf "========================================\n"
printf " Downloading configuration files \n"
printf "========================================\n"
-$CURL -L https://raw.githubusercontent.com/SEL-Columbia/dokomoforms/v0.2.8/docker-compose.yml > docker-compose.yml
-$CURL -L https://raw.githubusercontent.com/SEL-Columbia/dokomoforms/v0.2.8/nginx.conf > nginx.conf
+$CURL -L https://raw.githubusercontent.com/SEL-Columbia/dokomoforms/v0.2.9/docker-compose.yml > docker-compose.yml
+$CURL -L https://raw.githubusercontent.com/SEL-Columbia/dokomoforms/v0.2.9/nginx.conf > nginx.conf
# Edit the configuration files
printf "========================================\n"
diff --git a/nginx.conf b/nginx.conf
index d8b0cbc8..677cec67 100644
--- a/nginx.conf
+++ b/nginx.conf
@@ -5,6 +5,7 @@ http {
upstream dokomo {
server webapp:8888;
}
+ include /etc/nginx/mime.types;
server {
listen 80;
listen [::]:80;
@@ -35,6 +36,31 @@ http {
root /tmp/letsencrypt-auto;
}
+ location ^~ /static/dist/ {
+ root /var/www;
+ if ($query_string) {
+ expires max;
+ }
+ }
+
+ location = /favicon.png {
+ root /var/www/static;
+ if ($query_string) {
+ expires max;
+ }
+ }
+
+ location = /robots.txt {
+ root /var/www/static;
+ if ($query_string) {
+ expires max;
+ }
+ }
+
+ location = /static/manifest.json {
+ root /var/www;
+ }
+
location / {
proxy_pass_header Server;
proxy_set_header Host $http_host;
diff --git a/package.json b/package.json
index 98e85bed..f2a1d217 100644
--- a/package.json
+++ b/package.json
@@ -12,17 +12,17 @@
"bootstrap": "^3.3.6",
"datatables": "https://github.com/DataTables/DataTables/archive/1.10.9.tar.gz",
"es5-shim": "^4.3.1",
- "highcharts-release": "^4.1.8",
- "jquery": "^2.1.4",
- "leaflet": "^0.7.5",
+ "highcharts-release": "^4.2.1",
+ "jquery": "^2.2.0",
+ "leaflet": "^0.7.7",
"lodash-compat": "^3.10.1",
"lz-string": "^1.4.4",
- "moment": "^2.10.6",
- "node-uuid": "^1.4.3",
- "pouchdb": "^5.1.0",
+ "moment": "^2.11.1",
+ "node-uuid": "^1.4.7",
+ "pouchdb": "^5.2.0",
"pouchdb-upsert": "^2.0.0",
"ratchet": "https://github.com/twbs/ratchet/archive/v2.0.2.tar.gz",
- "react": "^0.14.5",
+ "react": "^0.14.6",
"react-dom": "~0.14.2",
"screenfull": "^3.0.0"
},
@@ -32,39 +32,39 @@
"babel-preset-es2015": "^6.1.18",
"babel-preset-react": "^6.1.18",
"babelify": "^7.2.0",
- "browserify": "^12.0.1",
- "browserify-shim": "^3.8.7",
+ "browserify": "^13.0.0",
+ "browserify-shim": "^3.8.12",
"del": "^2.0.2",
- "eslint-plugin-react": "^3.3.0",
- "event-stream": "^3.3.1",
- "gulp": "^3.8.11",
- "gulp-concat": "^2.5.2",
- "gulp-html-replace": "^1.4.5",
+ "eslint-plugin-react": "^3.15.0",
+ "event-stream": "^3.3.2",
+ "gulp": "^3.9.0",
+ "gulp-concat": "^2.6.0",
+ "gulp-html-replace": "^1.5.5",
"gulp-if": "^2.0.0",
- "gulp-less": "^3.0.3",
- "gulp-livereload": "^3.8.0",
- "gulp-minify-css": "^1.2.1",
+ "gulp-less": "^3.0.5",
+ "gulp-livereload": "^3.8.1",
+ "gulp-minify-css": "^1.2.3",
"gulp-order": "^1.1.1",
"gulp-react": "^3.0.1",
"gulp-rename": "^1.2.2",
"gulp-replace": "^0.5.4",
- "gulp-sourcemaps": "^1.5.2",
+ "gulp-sourcemaps": "^1.6.0",
"gulp-streamify": "^1.0.2",
- "gulp-uglify": "1.4.2",
- "istanbul": "^0.4.0",
- "jest-cli": "^0.7.1",
- "jsdom": "^7.0.2",
- "jshint": "^2.7.0",
+ "gulp-uglify": "1.5.1",
+ "istanbul": "^0.4.2",
+ "jest-cli": "^0.8.2",
+ "jsdom": "^7.2.2",
+ "jshint": "^2.8.0",
"mocha": "^2.2.4",
"mocha-istanbul": "^0.2.0",
"node-underscorify": "0.0.14",
"react-addons-test-utils": "~0.14.2",
"reactify": "^1.1.1",
- "should": "^7.1.1",
+ "should": "^8.1.1",
"supertest": "^1.1.0",
"vinyl-buffer": "^1.0.0",
"vinyl-source-stream": "^1.1.0",
- "watchify": "^3.2.1"
+ "watchify": "^3.7.0"
},
"browserify": {
"transform": [
diff --git a/tests/python/test_demo_mode.py b/tests/python/test_demo_mode.py
index 78fee939..72f0fd6e 100644
--- a/tests/python/test_demo_mode.py
+++ b/tests/python/test_demo_mode.py
@@ -7,6 +7,7 @@
utils = (setUpModule, tearDownModule)
+import dokomoforms.handlers
from dokomoforms.models import Administrator
from dokomoforms.options import options
@@ -20,7 +21,26 @@ def get_app(self):
self.app = Application(self.session, options=options)
return self.app
- def test_logging_in_creates_user(self):
+ def test_logging_in_creates_user_no_https(self):
+ dokomoforms.handlers.demo.options.https = False
+ no_user = (
+ self.session
+ .query(count(Administrator.id))
+ .filter_by(name='demo_user')
+ .scalar()
+ )
+ self.assertEqual(no_user, 0)
+ self.fetch('/demo/login', _logged_in_user=None)
+ user = (
+ self.session
+ .query(count(Administrator.id))
+ .filter_by(name='demo_user')
+ .scalar()
+ )
+ self.assertEqual(user, 1)
+
+ def test_logging_in_creates_user_https(self):
+ dokomoforms.handlers.demo.options.https = True
no_user = (
self.session
.query(count(Administrator.id))
diff --git a/tests/python/test_handlers.py b/tests/python/test_handlers.py
index cabdf43b..7894033c 100644
--- a/tests/python/test_handlers.py
+++ b/tests/python/test_handlers.py
@@ -243,6 +243,43 @@ def test_login_success(self):
1
)
+ def test_login_as_admin_first_time(self):
+ admin_emails = (
+ self.session
+ .query(models.Email)
+ .filter_by(address='admin@email.com')
+ .all()
+ )
+ self.assertEqual(len(admin_emails), 0)
+
+ dokomoforms.handlers.auth.options.https = False
+ dokomoforms.handlers.auth.options.admin_email = 'admin@email.com'
+ with patch.object(handlers.Login, '_async_post') as p:
+ dummy = lambda: None
+ dummy.body = json_encode(
+ {'status': 'okay', 'email': 'admin@email.com'}
+ )
+ p.return_value = tornado.gen.Task(
+ lambda callback=None: callback(dummy)
+ )
+ response = self.fetch(
+ '/user/login?assertion=woah', method='POST', body='',
+ _logged_in_user=None
+ )
+ self.assertEqual(response.code, 200, msg=response.body)
+ self.assertEqual(
+ response.headers['Set-Cookie'].lower().count('secure'),
+ 1
+ )
+
+ new_admin_emails = (
+ self.session
+ .query(models.Email)
+ .filter_by(address='admin@email.com')
+ .all()
+ )
+ self.assertEqual(len(new_admin_emails), 1)
+
def test_login_success_secure_cookie(self):
dokomoforms.handlers.auth.options.https = True
with patch.object(handlers.Login, '_async_post') as p:
@@ -288,6 +325,20 @@ def test_login_fail(self):
)
self.assertEqual(response.code, 400, msg=response.body)
+ def test_check_login_status_logged_out(self):
+ response = self.fetch(
+ '/user/authenticated',
+ method='POST', _logged_in_user=None, body=''
+ )
+ self.assertEqual(response.code, 403)
+
+ def test_check_login_status_logged_in(self):
+ response = self.fetch(
+ '/user/authenticated',
+ method='POST', body=''
+ )
+ self.assertEqual(response.code, 200)
+
class TestBaseHandler(DokoHTTPTest):
def test_clear_user_cookie_if_not_uuid(self):
diff --git a/tests/python/test_selenium.py b/tests/python/test_selenium.py
index fcae1ab7..40eb1572 100644
--- a/tests/python/test_selenium.py
+++ b/tests/python/test_selenium.py
@@ -520,7 +520,9 @@ class TestAdminOverview(AdminTest):
@report_success_status
def test_account_overview_renders_properly(self):
self.get('/')
+
# Recent submissions table
+ self.sleep()
self.assertEqual(
len(self.drv.find_elements_by_class_name('submission-row')),
5
@@ -680,7 +682,7 @@ def test_update_settings(self):
save_btn = self.drv.find_element_by_class_name('btn-save-user')
self.sleep()
save_btn.click()
- self.sleep()
+ self.sleep(2)
self.click(self.drv.find_element_by_id('UserDropdown'))
try:
@@ -1106,7 +1108,7 @@ def test_url_slug_collision(self):
)
try:
self.click(self.drv.find_element_by_class_name('save-survey-url'))
- except (WebDriverException, ValueError):
+ except (WebDriverException, ValueError, TypeError):
# Catch ValueError due to
# https://github.com/SeleniumHQ/selenium/issues/1470
#
@@ -1798,7 +1800,8 @@ def test_single_facility_question(self):
self.wait_for_element(
'.content > span:nth-child(2) > span:nth-child(1)'
' > div:nth-child(1) > button:nth-child(1)',
- by=By.CSS_SELECTOR
+ by=By.CSS_SELECTOR,
+ timeout=10,
)
self.click(
self.drv
@@ -4151,7 +4154,8 @@ def test_add_new_facility_revisit_cuts_out(self):
self.sleep()
self.wait_for_element(
'.btn-add-facility',
- by=By.CSS_SELECTOR
+ by=By.CSS_SELECTOR,
+ timeout=10,
)
# click add button
self.click(
diff --git a/webapp.py b/webapp.py
index f3ce0687..05b9a03e 100755
--- a/webapp.py
+++ b/webapp.py
@@ -379,7 +379,7 @@ def main(msg=None): # pragma: no cover
logging.getLogger('sqlalchemy').setLevel(log_level)
if options.kill:
ensure_that_user_wants_to_drop_schema()
- http_server = tornado.httpserver.HTTPServer(Application())
+ http_server = tornado.httpserver.HTTPServer(Application(), xheaders=True)
tornado.locale.load_gettext_translations(
os.path.join(_pwd, 'locale'), 'dokomoforms'
)