From c213dd0811c2b45203c6e6fadc6b68bdbf5fa7ff Mon Sep 17 00:00:00 2001 From: Oliver Marks Date: Sat, 20 Aug 2016 19:45:28 +0100 Subject: [PATCH] contact form, newer styles and various other tweaks --- Dockerfile | 60 ++- README.md | 3 + config/cron/maidstone-hackspace | 3 + config/letsencrypt/generate.sh | 2 + .../nginx/docker-maidstone-hackspace.org.uk | 28 ++ config/nginx/maidstone-hackspace.org.uk | 105 +++++ config/uwsgi/maidstone-hackspace.org.uk.ini | 16 + docker-compose.yml | 71 ++- website/authorize.py | 409 ------------------ website/config/logger.py | 10 + website/config/settings.py | 21 +- website/config/settings_docker.py | 32 +- website/constants.py | 16 +- website/data/badges.py | 1 - website/data/equipment.py | 1 - website/data/members.py | 1 - website/data/site_user.py | 22 +- website/data/sql/__init__.py | 0 website/data/sql/badges.py | 54 +++ website/data/sql/donate.py | 37 ++ website/data/sql/equipment.py | 43 ++ website/data/sql/fetch_user_badges.sql | 0 website/data/sql/get_user_bio.sql | 0 .../data/sql/get_user_by_oauth_username.sql | 0 website/data/sql/get_user_credentials.sql | 0 website/data/sql/get_user_detail.sql | 0 website/data/sql/get_user_password_reset.sql | 0 website/data/sql/get_users.sql | 0 website/data/sql/member_list.sql | 0 website/data/sql/members.py | 35 ++ website/data/sql/pledge_totals.sql | 0 website/data/sql/profile.py | 28 ++ website/data/sql/site_user.py | 154 +++++++ website/generate.py | 8 +- website/html/blog.htm | 121 ------ website/html/index.htm | 142 ------ website/index.py | 12 +- website/libs/image_fetcher.py | 28 ++ website/libs/mail.py | 47 +- website/libs/payments.py | 53 +-- website/libs/recapture.py | 18 + website/migrate.py | 4 +- website/pages/__init__.py | 17 +- website/pages/calendar.py | 2 +- website/pages/chat.py | 2 +- website/pages/contact.py | 56 +++ website/pages/core/authorize.py | 78 ++-- website/pages/core/login_pages.py | 10 +- website/pages/donate.py | 4 +- website/pages/examples.py | 26 ++ website/pages/google_groups.py | 2 +- website/pages/homepage.py | 25 +- website/pages/members.py | 6 +- website/pages/profile.py | 80 ++-- website/setup.py | 0 website/static/css/default.css | 275 +----------- website/static/css/materialize.css | 0 website/static/css/sprite-action-white.css | 0 website/static/css/sprite-content-white.css | 0 .../static/css/sprite-navigation-white.css | 0 website/{ => static}/html/404.htm | 0 website/static/html/blog.htm | 123 ++++++ website/{ => static}/html/canvas.js | 0 website/{ => static}/html/chat.htm | 78 ++-- website/{ => static}/html/competition.htm | 90 ++-- website/{ => static}/html/donate.htm | 82 ++-- website/{ => static}/html/icon.png | Bin website/static/html/index.htm | 144 ++++++ website/{ => static}/html/rock.png | Bin website/static/images/background.jpg | Bin website/static/images/background.png | Bin website/static/images/badges/art_hacker.png | Bin website/static/images/badges/backer.png | Bin 0 -> 1203 bytes .../static/images/badges/circuit_hacker.png | Bin website/static/images/badges/code_hacker.png | Bin website/static/images/badges/craft_hacker.png | Bin website/static/images/badges/founder.png | Bin website/static/images/badges/handyman.png | Bin website/static/images/badges/member.png | Bin website/static/images/badges/pledged.png | Bin website/static/images/badges/teacher.png | Bin 0 -> 983 bytes website/static/images/badges/trainer.png | Bin website/static/images/banners/audio_board.jpg | Bin .../static/images/banners/emf_2016_sign.jpg | Bin 0 -> 241616 bytes .../images/banners/emf_2016_sign_large.jpg | Bin 0 -> 572866 bytes .../images/banners/hackspace-banner.png | Bin website/static/images/banners/indiegogo.png | Bin website/static/images/banners/microscope.jpg | Bin .../images/banners/object_avoiding_robot.jpg | Bin .../static/images/banners/rocket_camera.jpg | Bin .../screw_sorting_competition_banner.jpg | Bin .../static/images/css/sprite-action-white.png | Bin .../images/css/sprite-content-white.png | Bin .../images/css/sprite-navigation-white.png | Bin website/static/images/example-01.jpg | Bin website/static/images/example-02.jpg | Bin website/static/images/example-03.jpg | Bin website/static/images/favicon.png | Bin website/static/images/hackspace-banner.png | Bin website/static/images/hackspace.png | Bin website/static/images/hackspace_960x540.png | Bin website/static/images/hackspace_960x960.png | Bin website/static/images/icon.png | Bin .../images/membership_card_background.png | Bin website/static/images/oauth/google.png | Bin website/static/images/password_strength.png | Bin .../photos/meetup-malta-inn-31-07-2005.jpg | Bin website/static/images/rock.png | Bin website/static/images/tile-01.jpg | Bin website/static/images/tile-02.jpg | Bin website/static/images/tiles/malta-inn.jpg | Bin .../tiles/meetup-malta-inn-31-07-2005.jpg | Bin website/static/js/canvas.js | 0 website/static/js/default.js | 2 +- website/static/js/jquery-2.1.4.min.js | 0 website/static/js/jquery-2.2.3.min.js | 0 website/static/js/materialize.js | 1 + website/static/js/materialize.min.js | 1 + website/tests/test_create_users.py | 72 ++- website/widgets/contact_form.py | 125 ++++++ website/widgets/form_simple.py | 69 +++ website/widgets/google_groups.py | 1 + website/widgets/google_groups_signup.py | 19 +- website/widgets/header_strip.py | 6 +- website/widgets/info_box.py | 5 +- website/widgets/like.py | 8 +- website/widgets/login_box.py | 41 -- website/widgets/member_card.py | 6 +- website/widgets/member_tiles.py | 6 +- website/widgets/page.py | 11 +- website/widgets/recapture.py | 18 + website/widgets/tiles.py | 11 +- 132 files changed, 1683 insertions(+), 1404 deletions(-) create mode 100644 config/cron/maidstone-hackspace create mode 100644 config/letsencrypt/generate.sh create mode 100644 config/nginx/docker-maidstone-hackspace.org.uk create mode 100644 config/nginx/maidstone-hackspace.org.uk create mode 100644 config/uwsgi/maidstone-hackspace.org.uk.ini delete mode 100644 website/authorize.py create mode 100644 website/config/logger.py create mode 100755 website/data/sql/__init__.py create mode 100755 website/data/sql/badges.py create mode 100755 website/data/sql/donate.py create mode 100755 website/data/sql/equipment.py mode change 100644 => 100755 website/data/sql/fetch_user_badges.sql mode change 100644 => 100755 website/data/sql/get_user_bio.sql mode change 100644 => 100755 website/data/sql/get_user_by_oauth_username.sql mode change 100644 => 100755 website/data/sql/get_user_credentials.sql mode change 100644 => 100755 website/data/sql/get_user_detail.sql mode change 100644 => 100755 website/data/sql/get_user_password_reset.sql mode change 100644 => 100755 website/data/sql/get_users.sql mode change 100644 => 100755 website/data/sql/member_list.sql create mode 100755 website/data/sql/members.py mode change 100644 => 100755 website/data/sql/pledge_totals.sql create mode 100755 website/data/sql/profile.py create mode 100755 website/data/sql/site_user.py delete mode 100644 website/html/blog.htm delete mode 100644 website/html/index.htm create mode 100644 website/libs/image_fetcher.py create mode 100644 website/libs/recapture.py create mode 100644 website/pages/contact.py mode change 100755 => 100644 website/setup.py mode change 100644 => 100755 website/static/css/default.css mode change 100644 => 100755 website/static/css/materialize.css mode change 100644 => 100755 website/static/css/sprite-action-white.css mode change 100644 => 100755 website/static/css/sprite-content-white.css mode change 100644 => 100755 website/static/css/sprite-navigation-white.css rename website/{ => static}/html/404.htm (100%) mode change 100644 => 100755 create mode 100755 website/static/html/blog.htm rename website/{ => static}/html/canvas.js (100%) rename website/{ => static}/html/chat.htm (66%) mode change 100644 => 100755 rename website/{ => static}/html/competition.htm (72%) mode change 100644 => 100755 rename website/{ => static}/html/donate.htm (60%) mode change 100644 => 100755 rename website/{ => static}/html/icon.png (100%) create mode 100755 website/static/html/index.htm rename website/{ => static}/html/rock.png (100%) mode change 100644 => 100755 website/static/images/background.jpg mode change 100644 => 100755 website/static/images/background.png mode change 100644 => 100755 website/static/images/badges/art_hacker.png create mode 100755 website/static/images/badges/backer.png mode change 100644 => 100755 website/static/images/badges/circuit_hacker.png mode change 100644 => 100755 website/static/images/badges/code_hacker.png mode change 100644 => 100755 website/static/images/badges/craft_hacker.png mode change 100644 => 100755 website/static/images/badges/founder.png mode change 100644 => 100755 website/static/images/badges/handyman.png mode change 100644 => 100755 website/static/images/badges/member.png mode change 100644 => 100755 website/static/images/badges/pledged.png create mode 100755 website/static/images/badges/teacher.png mode change 100644 => 100755 website/static/images/badges/trainer.png mode change 100644 => 100755 website/static/images/banners/audio_board.jpg create mode 100644 website/static/images/banners/emf_2016_sign.jpg create mode 100644 website/static/images/banners/emf_2016_sign_large.jpg mode change 100644 => 100755 website/static/images/banners/hackspace-banner.png mode change 100644 => 100755 website/static/images/banners/indiegogo.png mode change 100644 => 100755 website/static/images/banners/microscope.jpg mode change 100644 => 100755 website/static/images/banners/object_avoiding_robot.jpg mode change 100644 => 100755 website/static/images/banners/rocket_camera.jpg mode change 100644 => 100755 website/static/images/competitions/screw_sorting_competition_banner.jpg mode change 100644 => 100755 website/static/images/css/sprite-action-white.png mode change 100644 => 100755 website/static/images/css/sprite-content-white.png mode change 100644 => 100755 website/static/images/css/sprite-navigation-white.png mode change 100644 => 100755 website/static/images/example-01.jpg mode change 100644 => 100755 website/static/images/example-02.jpg mode change 100644 => 100755 website/static/images/example-03.jpg mode change 100644 => 100755 website/static/images/favicon.png mode change 100644 => 100755 website/static/images/hackspace-banner.png mode change 100644 => 100755 website/static/images/hackspace.png mode change 100644 => 100755 website/static/images/hackspace_960x540.png mode change 100644 => 100755 website/static/images/hackspace_960x960.png mode change 100644 => 100755 website/static/images/icon.png mode change 100644 => 100755 website/static/images/membership_card_background.png mode change 100644 => 100755 website/static/images/oauth/google.png mode change 100644 => 100755 website/static/images/password_strength.png mode change 100644 => 100755 website/static/images/photos/meetup-malta-inn-31-07-2005.jpg mode change 100644 => 100755 website/static/images/rock.png mode change 100644 => 100755 website/static/images/tile-01.jpg mode change 100644 => 100755 website/static/images/tile-02.jpg mode change 100644 => 100755 website/static/images/tiles/malta-inn.jpg mode change 100644 => 100755 website/static/images/tiles/meetup-malta-inn-31-07-2005.jpg mode change 100644 => 100755 website/static/js/canvas.js mode change 100644 => 100755 website/static/js/default.js mode change 100644 => 100755 website/static/js/jquery-2.1.4.min.js mode change 100644 => 100755 website/static/js/jquery-2.2.3.min.js create mode 100755 website/static/js/materialize.js create mode 100755 website/static/js/materialize.min.js create mode 100644 website/widgets/contact_form.py create mode 100644 website/widgets/form_simple.py delete mode 100644 website/widgets/login_box.py create mode 100644 website/widgets/recapture.py diff --git a/Dockerfile b/Dockerfile index 0dd7b04..a452c81 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,34 +1,52 @@ -# fabricad -# -# VERSION 0.0.1 +FROM olymk2/uwsgi -FROM ubuntu:16.04 -MAINTAINER Oliver Marks "olymk2@gmail.com" +ENV SERVER_ENVIRONMENT DEVELOPMENT +ENV SITE_FOLDER /etc/sites/mysite/ -ENV SERVER_ENVIRONMENT DOCKER +#COPY website/config/nginx/maidstone-hackspace.org.uk.ini /etc/sites/nginx/maidstone-hackspace.org.uk.ini +#COPY website/config/uwsgi/maidstone-hackspace.org.uk.ini /etc/sites/uwsgi/maidstone-hackspace.org.uk.ini + +RUN apk add --update --no-cache libssl1.0 libxml2 libxslt ca-certificates +RUN apk add --update --no-cache py-psycopg2 py-lxml py-flask py-pillow py-openssl py-cffi + +RUN apk add --update --no-cache build-base make bzr python3-dev libffi-dev openssl-dev libxml2-dev libxslt-dev && \ + pip3 install lxml && \ + pip3 install --no-cache-dir lxml dateutils requests requests-oauthlib mailer gocardless paypalrestsdk pytz nose2 oauthlib flask flask-login pymysql misaka && \ + pip3 install --no-cache-dir bzr+lp:scaffold/trunk#egg=scaffold && \ + apk del build-base make bzr python3-dev libffi-dev openssl-dev libxml2-dev libxslt-dev + +# RUN pip3 install --no-cache-dir dateutils requests requests-oauthlib gocardless paypalrestsdk pytz nose2 oauthlib flask flask-login pymysql misaka +# RUN pip3 install --no-cache-dir bzr+lp:scaffold/trunk#egg=scaffold + +#CMD ["setup.sh"] +#ENTRYPOINT ["setup.sh"] # make sure the package repository is up to date -RUN \ - apt-get update && \ - apt-get upgrade -y && \ - apt-get install -y libssl-dev libffi-dev nano && \ - apt-get install -y software-properties-common python-software-properties && \ - apt-get install -y software-properties-common python-pip python-dev python-nose2 && \ - apt-get install -y python-mysqldb python-psycopg2 python-requests-oauthlib python-dateutil python-requests python-lxml python-flask python-flask-login python-pillow && \ - apt-get install -y cssmin slimit && \ - add-apt-repository -y ppa:oly/ppa && \ - apt-get update && \ - apt-get install -y python-scaffold +#RUN \ +# apt-get update && \ +# apt-get upgrade -y && \ +# apt-get install -y libssl-dev libffi-dev nano && \ +# apt-get install -y software-properties-common python-software-properties && \ +# apt-get install -y software-properties-common python-pip python-dev python-nose2 && \ +# apt-get install -y python-mysqldb python-psycopg2 python-requests-oauthlib python-dateutil python-requests python-lxml python-flask python-flask-login python-pillow && \ +# apt-get install -y cssmin slimit && \ +# add-apt-repository -y ppa:oly/ppa && \ +# apt-get update && \ +# apt-get install -y python-scaffold + -RUN pip install gocardless paypalrestsdk pytz #allow access to flask -EXPOSE 5000 5002 -WORKDIR /var/www/website/ +#EXPOSE 5000 5002 +#WORKDIR /var/www/website/ + + #RUN /bin/sh -c 'cd /var/www/site; python index.py' -ENTRYPOINT /bin/sh -c 'python index.py' +#ENTRYPOINT /bin/sh -c 'scaffold import && python index.py' #docker build -t mhackspace . #docker run -d --name=mhackspace_container --restart=always mhackspace +#docker run -it -v /etc/uwsgi/apps-enabled/:/etc/uwsgi/apps-enabled/ -v sockets:/data/sockets --entrypoint sh --name mhackspace olymk2/mhackspace +#docker run -it -v /etc/uwsgi/apps-enabled/:/etc/uwsgi/apps-enabled/ -v sockets:/data/sockets -v /var/www/test.maidstone-hackspace.org.uk/site:/var/www --name mhackspace olymk2/mhackspace #accesss on dockerip 172.17.0.?:5000 #https://hub.docker.com/r/olymk2/mhackspace/ diff --git a/README.md b/README.md index 159ba29..1889d88 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,9 @@ The simplest way to setup this site is to use docker-compose so please install t and make sure the quick start guide works https://docs.docker.com/machine/get-started/ then you can use the commands below to test and make changes. docker-compose up + + docker volume create --name sockets + docker run -it -v /etc/uwsgi/apps-enabled/:/etc/uwsgi/apps-enabled/ -v /var/www:/var/www -v sockets:/data/sockets --name mhackspace olymk2/mhackspace If you plan on making large changes consider discussing it first so you dont waste your own time. diff --git a/config/cron/maidstone-hackspace b/config/cron/maidstone-hackspace new file mode 100644 index 0000000..34ba869 --- /dev/null +++ b/config/cron/maidstone-hackspace @@ -0,0 +1,3 @@ +PATH=/usr/local/sbin:/usr/local/bin +MAILTO=contact@maidstone-hackspace.org.uk +0 2 * * * python /var/www/maidstone-hackspace.org.uk/site/generate.py diff --git a/config/letsencrypt/generate.sh b/config/letsencrypt/generate.sh new file mode 100644 index 0000000..b373a99 --- /dev/null +++ b/config/letsencrypt/generate.sh @@ -0,0 +1,2 @@ +./letsencrypt-auto certonly -d maidstone-hackspace.org.uk -d live.maidstone-hackspace.org.uk +./letsencrypt-auto certonly -d test.maidstone-hackspace.org.uk diff --git a/config/nginx/docker-maidstone-hackspace.org.uk b/config/nginx/docker-maidstone-hackspace.org.uk new file mode 100644 index 0000000..c880750 --- /dev/null +++ b/config/nginx/docker-maidstone-hackspace.org.uk @@ -0,0 +1,28 @@ +server { + listen 80; + server_name maidstone-hackspace.org.uk live.maidstone-hackspace.org.uk www.maidstone-hackspace.org.uk; + root /var/www/static/html; + + error_page 404 = /404.htm; + + location = / { + # match uri equalling / only for index, if anything is after / go to next location + try_files $uri $uri/index.htm; + } + + location / { + # if the first root location fails try this one instead and fallback to uwsgi + try_files $uri $uri.htm $uri/ @uwsgi_fallback; + #error_page 404 = @fallback; + } + + location /static/ { + alias /var/www/static/; + log_not_found on; + } + + location @uwsgi_fallback { + include uwsgi_params; + uwsgi_pass unix:///data/sockets/maidstone_hackspace.sock; + } +} diff --git a/config/nginx/maidstone-hackspace.org.uk b/config/nginx/maidstone-hackspace.org.uk new file mode 100644 index 0000000..7324405 --- /dev/null +++ b/config/nginx/maidstone-hackspace.org.uk @@ -0,0 +1,105 @@ +server { + listen 443 ssl http2; + server_name maidstone-hackspace.org.uk live.maidstone-hackspace.org.uk www.maidstone-hackspace.org.uk; + root /var/www/live-maidstone-hackspace.org.uk/site/html; + + ssl_certificate /etc/letsencrypt/live/live.maidstone-hackspace.org.uk/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/live.maidstone-hackspace.org.uk/privkey.pem; + + ssl_stapling on; + ssl_stapling_verify on; + ssl_protocols TLSv1.2; + ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256'; + ssl_prefer_server_ciphers on; + ssl_session_timeout 1d; + ssl_session_cache shared:SSL:50m; + ssl_session_tickets on; + ssl_dhparam /etc/ssl/certs/dhparam.pem; + + resolver 8.8.8.8; + + server_tokens off; + add_header X-Frame-Options SAMEORIGIN; + add_header X-Content-Type-Options nosniff; + add_header X-XSS-Protection "1; mode=block"; + add_header Strict-Transport-Security max-age=15768000; + + error_page 404 = /404.htm; + + location = / { + # match uri equalling / only for index, if anything is after / go to next location + try_files $uri $uri/index.htm; + } + + + location / { + try_files $uri $uri.htm $uri/ @uwsgi_fallback; + #error_page 404 = @fallback; + } + + location /static { + alias /var/www/live-maidstone-hackspace.org.uk/site/static; + #expires 1d; + #add_header Pragma public; + #add_header Cache-Control "public"; + } + + location @uwsgi_fallback { + include uwsgi_params; + uwsgi_pass unix:///data/sockets/live-maidstone_hackspace.sock; + } +} + +server { + listen 443 ssl http2; + server_name test.maidstone-hackspace.org.uk; + root /var/www/test-maidstone-hackspace.org.uk/site/html; + + + ssl_certificate /etc/letsencrypt/live/test.maidstone-hackspace.org.uk/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/test.maidstone-hackspace.org.uk/privkey.pem; + + ssl_stapling on; + ssl_stapling_verify on; + ssl_protocols TLSv1.2; + ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256'; + ssl_prefer_server_ciphers on; + ssl_session_timeout 1d; + ssl_session_cache shared:SSL:50m; + ssl_session_tickets off; + ssl_dhparam /etc/ssl/certs/dhparam.pem; + + resolver 8.8.8.8; + + server_tokens off; + add_header X-Frame-Options SAMEORIGIN; + add_header X-Content-Type-Options nosniff; + add_header X-XSS-Protection "1; mode=block"; + add_header Strict-Transport-Security max-age=15768000; + + error_page 404 = /404.htm; + + location = / { + # match uri equalling / only for index, if anything is after / go to next location + try_files $uri $uri/index.htm; + } + + location / { + try_files $uri $uri.htm $uri/ @uwsgi_fallback; + #error_page 404 = @fallback; + } + + location /static { + alias /var/www/test-maidstone-hackspace.org.uk/site/static; + expires 1d; + add_header Pragma public; + add_header Cache-Control "public"; + } + + + location @uwsgi_fallback { + include uwsgi_params; + uwsgi_pass unix:///data/sockets/test-maidstone_hackspace.sock; + } + +} diff --git a/config/uwsgi/maidstone-hackspace.org.uk.ini b/config/uwsgi/maidstone-hackspace.org.uk.ini new file mode 100644 index 0000000..0c9f4be --- /dev/null +++ b/config/uwsgi/maidstone-hackspace.org.uk.ini @@ -0,0 +1,16 @@ +[uwsgi] +module = wsgi +chdir = /var/www/ +base = /var/www/ +touch-reload = /etc/uwsgi/apps-enabled/maidstone-hackspace.org.uk.ini +master = true +processes = 3 +logto = /tmp/maidstone_hackspace.log +plugin = python3 +socket = /data/sockets/maidstone_hackspace.sock +chmod-socket = 660 +vacuum = true +die-on-term = true + +# only for development +python-autoreload = 3 diff --git a/docker-compose.yml b/docker-compose.yml index ba64b65..b7cde75 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,25 +1,50 @@ +version: '2' -nginx: - image: olymk2/mhackspace - #name: mhackspace - links: - - db:database - dns: - - 8.8.8.8 - - 8.8.4.4 - ports: - - "5000:5000" - volumes: - - .:/var/www - restart: always +services: + mhackspace_uwsgi: + image: olymk2/mhackspace + restart: unless-stopped + network_mode: bridge + links: + - mariadb:mariadb + - mhackspace_web:nginx + - mhackspace_mail:mail_server + volumes: + - sockets:/data/sockets + - ./website/:/var/www + - ./config/uwsgi/maidstone-hackspace.org.uk.ini:/etc/uwsgi/apps-enabled/maidstone-hackspace.org.uk.ini -db: - image: mariadb - ports: - - "3300:3306" - environment: - MYSQL_DATABASE: maidstone_hackspace - MYSQL_USER: mhackspace - MYSQL_PASSWORD: mhackspace - MYSQL_ROOT_PASSWORD: mhackspace - restart: always + mhackspace_web: + image: olymk2/nginx + restart: unless-stopped + network_mode: bridge + dns: + - 8.8.8.8 + - 8.8.4.4 + volumes: + - ./website/:/var/www + - sockets:/data/sockets + - /etc/ssl/certs/:/etc/ssl/certs/ + - ./config/nginx/docker-maidstone-hackspace.org.uk:/etc/nginx/sites-enabled/docker-maidstone-hackspace.org.uk + restart: always + + mariadb: + image: mariadb + network_mode: bridge + ports: + - "3300:3306" + environment: + MYSQL_DATABASE: maidstone_hackspace + MYSQL_USER: mhackspace + MYSQL_PASSWORD: mhackspace + MYSQL_ROOT_PASSWORD: mhackspace + restart: unless-stopped + + mhackspace_mail: + image: mailhog/mailhog + network_mode: bridge + restart: unless-stopped + +volumes: + sockets: + driver: local diff --git a/website/authorize.py b/website/authorize.py deleted file mode 100644 index 53b14eb..0000000 --- a/website/authorize.py +++ /dev/null @@ -1,409 +0,0 @@ -import os -import uuid -import hashlib -import datetime - -from werkzeug.security import generate_password_hash, check_password_hash -from flask import session, flash -from flask import redirect, abort -from flask import make_response -from flask import request -from flask import Blueprint -from flask.ext.login import current_user, LoginManager, login_required, UserMixin, login_user, logout_user, make_secure_token -from requests_oauthlib import OAuth2Session -from requests_oauthlib.compliance_fixes import facebook_compliance_fix - -from scaffold import web -from libs.mail import sendmail -from pages import header, footer -from data import site_user -from config.settings import * -from constants import * - -web.load_widgets('widgets') - -authorize_pages = Blueprint('authorize_pages', __name__, template_folder='templates') - -login_manager = LoginManager() -login_manager.login_view = '/login' - -oauth_lookup = {'google':1, 'github':2, 'facebook':3} - - -def is_weak_password(password1, password2): - if password1 != password2: - password1 = password2 = None - return True - - # TODO check length and chars - - password1 = password2 = None - return False - - -def todict(data): - new_dict = {} - for key, value in data.items(): - new_dict[key] = value - return new_dict - - -class User(UserMixin): - def __init__(self, user_id, active=True): - print user_id - self.id = None - user_details = site_user.get_user_details({'id': user_id}).get() - self.active = False - print 'user' - print user_details - if user_details: - #~ self.check_password(user_details.get('password')) - self.id = user_id - self.name = user_details.get('username') - print self.name - self.active = active - - def get_id(self): - return self.id - - def is_active(self): - return self.active - - def is_authenticated(self): - return self.active - - def get_auth_token(self): - return make_secure_token(self.name, key='deterministic') - - -@login_manager.user_loader -def load_user(userid): - """Flask user loader hook, internal to flask login""" - return User(userid) - - -@login_manager.token_loader -def load_token(request): - token = request.headers.get('Authorization') - if token is None: - token = request.args.get('token') - - if token is not None: - username, password = token.split(":") # naive token - print username - print password - user_entry = User.get(username) - if (user_entry is not None): - user = User(user_entry[0], user_entry[1]) - - if (user.password == password): - return user - return None - -#~ def auth_required(): - #~ if not session.get('user_id'): - #~ redirect(domain + '/login', 301) - -@authorize_pages.route("/register", methods=['GET']) -def register_form(): - header('Register for access') - web.page.create('Register for access') - #~ web.page.section(web.register_form.create().render()) - - web.form.create('Register new user account', '/register') - web.form.append(name='name', label='Your full name', placeholder='Ralf Green', value='') - web.form.append(name='email', label='Valid Email', placeholder='ralf@maidstone-hackspace.org.uk', value='') - web.form.append(input_type='password', name='password', label='Password', placeholder='quick brown fox jumped over', value='') - web.form.append(input_type='password', name='password_confirm', label='Password Confirm', placeholder='quick brown fox jumped over', value='') - - web.page.section(web.form.render()) - web.template.body.append(web.page.render()) - - return make_response(footer()) - -@authorize_pages.route("/register", methods=['POST']) -def register_submit(): - data = {} - data['email'] = request.form.get('email') - data['username'] = request.form.get('email') - data['first_name'] = request.form.get('name').strip().split()[0] - data['last_name'] = request.form.get('name').strip().split()[-1] - - data['password'] = request.form.get('password') - data['password_confirm'] = request.form.get('password') - - data['password'] = generate_password_hash(request.form.get('password')) - #TODO password strength tests - if is_weak_password(request.form.get('password'), request.form.get('password_confirm')): - redirect('/register') - - header('Your account has been registered') - web.page.create('Your account has been registered') - - new_user = site_user.create() - new_user.execute(data) - flash('Your account has now been created') - - web.template.body.append(web.page.render()) - return make_response(footer()) - -@authorize_pages.route("/oauth///", methods=['GET']) -@authorize_pages.route("/oauth//", methods=['GET']) -@authorize_pages.route("/oauth//", methods=['GET']) -@authorize_pages.route("/oauth/", methods=['GET']) -def oauth(provider, start_oauth_login=False): - oauth_verify = True - oauth_provider = oauth_conf.get(provider) - oauth_access_type = '' - oauth_approval_prompt = '' - if oauth_live is False: - oauth_verify = False - oauth_access_type = 'offline' - oauth_approval_prompt = "force" - os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = '1' - - if start_oauth_login: - print oauth_provider.get('redirect_uri') - oauth_session = OAuth2Session( - oauth_provider.get('client_id'), - scope=oauth_provider.get('scope'), - redirect_uri=oauth_provider.get('redirect_uri')) - - if provider == 'facebook': - oauth_session = facebook_compliance_fix(oauth_session) - - authorization_url, state = oauth_session.authorization_url( - oauth_provider.get('auth_uri'), - access_type=oauth_access_type, - approval_prompt=oauth_approval_prompt) - - # State is used to prevent CSRF, keep this for later, make sure oauth returns to the same url. - # if testing and oauth_state errors make sure you logged in with localhost and not 127.0.0.1 - session['oauth_state'] = state - session.modified = True - return redirect(authorization_url) - - # allready authorised so lets handle the callback - oauth_session = OAuth2Session( - oauth_provider.get('client_id'), - state=session['oauth_state'], - redirect_uri=oauth_provider.get('redirect_uri')) - - print '----------' - print oauth_provider.get('redirect_uri') - print request.url - - if provider == 'facebook': - oauth_session = facebook_compliance_fix(oauth_session) - - - # code error is todo with authorisation response - oauth_session.fetch_token( - oauth_provider.get('token_uri'), - client_secret=oauth_provider.get('client_secret'), - authorization_response=request.url, - verify=oauth_verify) - - # Fetch a protected resource, i.e. user profile - response = oauth_session.get(oauth_provider.get('user_uri')) - oauth_response = response.json() - - print 'oauth response' - print oauth_response - - oauth_id = oauth_response.get('login') or oauth_response.get('id') - provider_id = oauth_lookup.get(provider) - oauth_user = site_user.fetch_oauth_login({ - 'username': oauth_id or '', - 'provider': provider_id - }).get() - - if oauth_user: - user_details = site_user.get_user_details({ - 'id': oauth_user.get('user_id') - }).get() - - # we have matched a user so login and redirect - if user_details: - login_user(User(user_details.get('user_id'))) - # no E-Mail so lets ask the user to set there email before allowing login - if not user_details.get('email'): - return redirect('/profile/change_email') - return redirect('/profile') - - flash('Your new profile has been created, and your now logged in') - - print 'current user' - print current_user.get_id() - if current_user.get_id(): - # link oauth to users account - site_user.create_oauth_login().execute({ - 'user_id': current_user.get_id(), - 'username': oauth_id or '', - 'provider': provider_id}) - return redirect('/profile') - - print oauth_response - print '-----' - print oauth_response.get('email') or '' - # create new user from oauth information - - new_user_details = { - 'password': 'oauth', - 'profile_image': oauth_response.get('picture'), - 'username': oauth_id, - 'first_name': oauth_response.get('given_name') or '', - 'last_name': oauth_response.get('family_name') or ''} - - if oauth_response.get('email'): - new_user_details['email']= oauth_response.get('email') - - user_id = site_user.create().execute(new_user_details) - - # register oauth login creation - site_user.create_oauth_login().execute({ - 'user_id': user_id, - 'username': oauth_id or '', - 'provider': provider_id}) - - login_user(User(user_id)) - site_user.update_last_login().execute({'id': user_id}) - if not user_id: - flash('Failed to create user') - return redirect('/login') - return redirect('/profile') - - -@authorize_pages.route("/change-password/", methods=['GET']) -@authorize_pages.route("/change-password", methods=['GET']) -def change_password(code=None): - #if we have a code this is a password reset, so try and login the user first - print code - site_user.delete_password_reset().execute({}) - if code: - - user_details = site_user.get_user_by_reset_code({'reset_code': code}).get() - - print user_details - if not user_details: - #invalid code so pretend the page does not even exist - return abort(404) - #check the code has not expired - #datetime.datetime.now() + datetime.timedelta(minutes=15) - has_date_expired = user_details.get('created') + datetime.timedelta(minutes=60) - if has_date_expired < datetime.datetime.now(): - print 'date expired' - #date expired so clean up and pretend the page does not exist - return abort(404) - #challenge passed so login the user so they can change there password - login_user( - User(user_details.get('user_id')) - ) - session['user_id'] = str(user_details.get('user_id')) - - web.template.create('Maidstone Hackspace - Profile') - header('User profile') - web.page.create('Change password') - web.page.section( - web.change_password_box.create().render() - ) - web.template.body.append(web.page.render()) - return make_response(footer()) - -@login_required -@authorize_pages.route("/change-password", methods=['POST']) -def change_password_submit(code=None): - if not session.get('user_id'): - abort(404) - user_details = site_user.authorize({ - 'id': session.get('user_id')}).get() - - if is_weak_password(request.form.get('password'), request.form.get('password_confirm')): - print 'password not strong enough' - redirect('/login') - - pw_hash = generate_password_hash(request.form.get('password')) - - site_user.change_password().execute({'id': user_details.get('user_id'), 'password': pw_hash}) - - web.template.create('Maidstone Hackspace - Profile') - header('User Profile') - web.page.create('Password change complete') - web.page.section( - 'Your password has successfull been changed' - ) - web.template.body.append(web.page.render()) - return make_response(footer()) - - -@authorize_pages.route("/reset-password", methods=['GET']) -def reset_password(): - web.template.create('Maidstone Hackspace - Login') - header('Members Login') - web.page.create('Forgot password reset') - web.page.section( - web.password_box.create('Please enter your E-Mail account', reset=True).render() - ) - web.template.body.append(web.page.render()) - return make_response(footer()) - -@authorize_pages.route("/reset-password", methods=['POST']) -def reset_password_submit(): - user_details = site_user.get_by_username({ - 'email': request.form.get('email')}).get() - - reset_code = hashlib.sha256(str(uuid.uuid4())).hexdigest() - - if user_details: - site_user.create_password_reset() \ - .on_duplicate() \ - .execute({ - 'user_id': str(user_details.get('user_id')), - 'reset_code': reset_code}) - - l=web.link.create(title='Change password', content='Click to change password',link="{domain}change-password/{resetcode}".format(**{'domain':app_domain, 'resetcode': reset_code})).render() - - body = "Please follow the link below to change your password.\n" + l - body += "{domain}change-password/{resetcode}".format(**{'domain':app_domain, 'resetcode': reset_code}) - sendmail().send( - from_address='no-reply@maidstone-hackspace.org.uk', - to_address='oly@leela', - subject="Reset password request", - body=body) - - # display success page, dont give away anything about if the email is actually registered - web.template.create('Maidstone Hackspace - Password reset') - header('Password reset') - web.page.create('Password reset sent') - web.page.section( - web.paragraph.create('If this E-Mail is registered you will shortly be reciving an E-Mail with reset details').render() - ) - web.template.body.append(web.page.render()) - return make_response(footer()) - -#~ @authorize_pages.route("/login", methods=['GET']) -#~ def login_screen(): - #~ web.template.create('Maidstone Hackspace - Login') - #~ header('Members Login') - #~ web.page.create('Member Login') - #~ web.page.section( - #~ web.login_box.create().enable_oauth('google').enable_oauth('facebook').enable_oauth('github').render() - #~ ) - #~ web.template.body.append(web.page.render()) - #~ return make_response(footer()) - - -@authorize_pages.route("/login/failure", methods=['GET']) -def login_Failure(): - web.template.create('%s - Login' % site_name) - header('Login Failure') - web.page.create('Login Failure') - #~ web.template.body.append(web.messages.render()) - web.template.body.append(web.page.render()) - return make_response(footer()) - - -@authorize_pages.route("/logout") -def logout(): - logout_user() - return redirect('/') diff --git a/website/config/logger.py b/website/config/logger.py new file mode 100644 index 0000000..556332a --- /dev/null +++ b/website/config/logger.py @@ -0,0 +1,10 @@ +import os +import logging + +log = logging.getLogger('Maidstone Hackspace') +log.setLevel(logging.DEBUG) +#~ log.propagate = False +if os.path.exists('/tmp/maidstone_hackspace.log'): + ch = logging.FileHandler('/tmp/maidstone_hackspace.log') + ch.setLevel(logging.DEBUG) + log.addHandler(ch) diff --git a/website/config/settings.py b/website/config/settings.py index 9ec013e..fae27dc 100644 --- a/website/config/settings.py +++ b/website/config/settings.py @@ -1,6 +1,10 @@ import os +import constants +import socket from scaffold.core.data.database import db from scaffold import web +from libs import mail + schema = 'https:' domain = '127.0.0.1' @@ -35,27 +39,27 @@ google_calendar_api_key = '' if os.environ.get('SERVER_ENVIRONMENT') =='DOCKER': if os.path.exists('config/settings_docker.py'): - print 'Using settings for docker enviroment' - from settings_docker import * + print('Using settings for docker enviroment') + from config.settings_docker import * else: if os.path.exists('config/settings_dev.py'): - print 'Using settings for dev enviroment' - from settings_dev import * + print('Using settings for dev enviroment') + from config.settings_dev import * if os.path.exists('config/settings_testing.py'): print('Using settings for test enviroment') - from settings_testing import * + from config.settings_testing import * if os.path.exists('config/settings_live.py'): print('Using settings for live enviroment') - from settings_live import * + from config.settings_live import * with web.template as setup: #css for jquery, material sprite sheet and custom css - setup.persistent_header('') setup.persistent_header('') + setup.persistent_header('') setup.persistent_header('') setup.persistent_header('') setup.persistent_header('') @@ -65,6 +69,7 @@ with web.template as setup: setup.persistent_header('') setup.persistent_header('') setup.persistent_header('') + setup.persistent_header('') setup.persistent_header('') #other favicon etc @@ -75,5 +80,5 @@ with web.template as setup: domain=domain, port=port) - db.config(database) +mail.sendmail.set_server(email_server) diff --git a/website/config/settings_docker.py b/website/config/settings_docker.py index 9a00449..6a7466d 100644 --- a/website/config/settings_docker.py +++ b/website/config/settings_docker.py @@ -1,3 +1,5 @@ +import os +import socket database = { 'charset': 'utf8', @@ -43,8 +45,28 @@ payment_providers = { google_calendar_id = 'contact@maidstone-hackspace.org.uk' google_calendar_api_key = 'AIzaSyA98JvRDmplA9lVLZeKwrs1f2k17resLy0' -app_domain = 'http://localhost:5000' + +google_captcha = { + 'secret': '', + 'site': '' +} + +# TODO in scaffold remove when commited +def get_ip_from_hostname(hostname, schema='http'): + try: + '%s://%s' % (schema, socket.gethostbyname('nginx')) + except socket.gaierror: + return '%s://%s' % (schema, '127.0.0.1') + +app_domain = 'http://%s' % socket.gethostbyname('nginx') app_email_template_path = 'templates/email/' +schema = 'https:' +domain = get_ip_from_hostname('nginx') +port = '' +rel_uri = '//' + domain +app_domain = 'http:%s' % rel_uri +app_email_template_path = 'templates/email/' +site_name = 'Maidstone Hackspace' flask_secret_key = '4466ae96-849f-4fbe-a469-3295bf1a13f5' @@ -81,3 +103,11 @@ oauth_conf = { } } +email_server = { + 'username': '', + 'password': '', + 'host': 'mail_server', + 'port': 1025, + 'use_tls': False, + 'from': 'support@maidstone-hackspace.org.uk', + 'to': 'support@maidstone-hackspace.org.uk'} diff --git a/website/constants.py b/website/constants.py index 25fadf7..7a8f79d 100644 --- a/website/constants.py +++ b/website/constants.py @@ -3,7 +3,7 @@ page_menu = [ ('Home', '/'), ('Chat', '/chat'), ('Donate', '/donate'), - ('Contact', '#mailing-list-signup')] + ('Contact', '/contact-us')] nav_for_authenticated_user = ( ('Profile', '/profile'), @@ -14,7 +14,8 @@ nav_for_authenticated_user = ( ) banner_images = [ - ('/static/images/banners/hackspace-banner.png', '', '', ''), + ('/static/images/banners/hackspace-banner.png', 'Maidstone Hackspace', 'Maidstone Hackspace', ''), + ('/static/images/banners/emf_2016_sign.jpg', 'EMF CAMP 2016', 'EMF CAMP 2016', ''), ('/static/images/banners/audio_board.jpg', 'Audio board', 'Audio board', ''), ('/static/images/banners/microscope.jpg', '', 'Microscope', ''), ('/static/images/banners/object_avoiding_robot.jpg', '', 'Object avoiding robot', ''), @@ -42,6 +43,9 @@ rss_feeds = [{ }, { 'author':'Mike McRoberts', 'url': 'http://thearduinoguy.org/?feed=rss2' + }, { + 'author': 'James', + 'url': 'https://feeds.feedburner.com/projects-jl' }] kent_hackspace = [ @@ -58,14 +62,6 @@ maker_events = [ email = 'support@maidstone-hackspace.org.uk' -email_server = { - 'user': '', - 'password': '', - 'host': 'email-smtp.us-east-1.amazonaws.com', - 'port': 465, - 'from': 'support@maidstone-hackspace.org.uk', - 'to': 'support@maidstone-hackspace.org.uk'} - badge_lookup = { 1: 'member', 2: 'backer', diff --git a/website/data/badges.py b/website/data/badges.py index e7911d1..72ff2ab 100644 --- a/website/data/badges.py +++ b/website/data/badges.py @@ -23,7 +23,6 @@ class assign_badge(insert_data): class fetch_badges(select_data): #~ debug = True table = 'badges' - required = {} columns = {'id', 'name'} class fetch_badge(select_data): diff --git a/website/data/equipment.py b/website/data/equipment.py index fc054e9..7e550de 100644 --- a/website/data/equipment.py +++ b/website/data/equipment.py @@ -28,7 +28,6 @@ class get_requests(select_data): debug = True #~ limit_rows = False pagination_rows = 100 - required = {} #~ query_str = 'select id, user_id, name, description, url, price, count(user_id) as quantity from maidstone_hackspace.requests group by name' query_str = 'select id, user_id, name, description, url, price as quantity from maidstone_hackspace.requests order by name' columns = {} diff --git a/website/data/members.py b/website/data/members.py index c3d7627..2c3ed46 100644 --- a/website/data/members.py +++ b/website/data/members.py @@ -9,7 +9,6 @@ query_builder.query_path = os.path.abspath('./data/sql/') class get_members(select_data): - required = {} query_file = 'member_list.sql' columns = {} diff --git a/website/data/site_user.py b/website/data/site_user.py index 9917d37..2b2c1df 100644 --- a/website/data/site_user.py +++ b/website/data/site_user.py @@ -76,7 +76,6 @@ class delete_password_reset(delete_data): """clean up expired password resets""" table = 'user_password_reset' sql_where = 'where DATE_ADD(created, INTERVAL 1 HOUR) < now()' - required = {} class create_password_reset(insert_data): @@ -89,14 +88,15 @@ class get_user_by_reset_code(select_data): columns_where = ['reset_code'] class change_password(update_data): + debug=True table = 'users' required = {'id', 'password'} - columns_where = ['password'] + columns = ['password'] + #~ columns_where = ['password'] sql_where = 'id=%(id)s' class get_users(select_data): - required = {} query_file = 'get_users.sql' @@ -119,9 +119,9 @@ class get_by_email(select_data): class get_by_username(select_data): - required = {'email'} + required = {'username'} query_file = 'get_user_credentials.sql' - columns_where = {'email'} + columns_where = {'username'} class authorize(select_data): required = {'id'} @@ -138,6 +138,18 @@ class create_oauth_login(insert_data): def calculated_data(self): return {'registered': time.strftime('%Y-%m-%d %H:%M:%S')} +class get_registered_oauth_providers(select_data): + table = 'user_oauth' + columns = {'username', 'provider', 'last_login'} + required = {'user_id'} + query_file = 'get_user_by_oauth_username.sql' + columns_where = {'user_id',} + + def calculated_data(self): + return { + 'last_login': time.strftime('%Y-%m-%d %H:%M:%S') + } + class update_oauth_login(update_data): table = 'user_oauth' columns = {'username', 'provider', 'last_login'} diff --git a/website/data/sql/__init__.py b/website/data/sql/__init__.py new file mode 100755 index 0000000..e69de29 diff --git a/website/data/sql/badges.py b/website/data/sql/badges.py new file mode 100755 index 0000000..72ff2ab --- /dev/null +++ b/website/data/sql/badges.py @@ -0,0 +1,54 @@ +import os +import time +from collections import defaultdict +from scaffold.core.data.select import select_data +from scaffold.core.data.insert import insert_data +from scaffold.core.data.update import update_data +from scaffold.core.data.delete import delete_data +from scaffold.core.data.sql import query_builder + +query_builder.query_path = os.path.abspath('./data/sql/') + + +class create_badge(insert_data): + table = 'badges' + required = {'name'} + columns = {'name'} + +class assign_badge(insert_data): + table = 'user_badges' + required = {'user_id', 'badge_id'} + columns = {'user_id', 'badge_id'} + +class fetch_badges(select_data): + #~ debug = True + table = 'badges' + columns = {'id', 'name'} + +class fetch_badge(select_data): + #~ debug = True + table = 'user_badges' + required = {'user_id'} + columns = {'user_id', 'badge_id'} + columns_where = {'user_id', 'badge_id'} + columns_optional_where = {'user_id', 'badge_id'} + columns_optional = {'user_id', 'badge_id'} + +class fetch_user_badges(select_data): + #~ debug = True + table = 'user_badges' + columns = {'user_id', 'badge_id'} + #~ columns_where = {'user_id'} + columns_optional_where = {'user_id', 'badge_id'} + #~ columns_optional = {'user_id', 'badge_id'} + +def fetch_user_badges_grouped(): + badge_lookup = defaultdict(list) + for badge in fetch_user_badges(): + badge_lookup[badge.get('user_id')].append(badge.get('badge_id')) + return badge_lookup + +class remove_badge(delete_data): + table = 'user_badges' + required = {'id'} + columns = {'id'} diff --git a/website/data/sql/donate.py b/website/data/sql/donate.py new file mode 100755 index 0000000..bb4c782 --- /dev/null +++ b/website/data/sql/donate.py @@ -0,0 +1,37 @@ +import os + +from scaffold.core.data.select import select_data +from scaffold.core.data.insert import insert_data +#~ from scaffold.core.data.update import update_data +#~ from scaffold.core.data.delete import delete_data +from scaffold.core.data.sql import query_builder + +query_builder.query_path = os.path.abspath('./data/sql/') + +class get_pledge(select_data): + debug = True + table = 'pledges' + columns = {'id', 'name', 'total'} + required = {'name'} + +class get_pledges(select_data): + debug = True + #~ table = 'pledges' + query_file = 'pledge_totals.sql' + required = {'environment'} + columns_where = {'expired', 'environment'} + grouping = {'name'} + +class add_pledge(insert_data): + debug = True + table = 'pledges' + required = {'name'} + columns = {'name'} + +class add_payment(insert_data): + debug = True + table = 'pledge_amounts' + required = {'provider_id', 'pledge_id', 'reference', 'amount', 'environment'} + columns = {'provider_id', 'pledge_id', 'reference', 'amount', 'environment'} + + diff --git a/website/data/sql/equipment.py b/website/data/sql/equipment.py new file mode 100755 index 0000000..7e550de --- /dev/null +++ b/website/data/sql/equipment.py @@ -0,0 +1,43 @@ +import os +import sys +sys.path.append(os.path.abspath('../../../../scaffold/')) +sys.path.insert(0,os.path.abspath('../../../../scaffold/')) +from scaffold.core.data.select import select_data +from scaffold.core.data.insert import insert_data +from scaffold.core.data.update import update_data +#~ from scaffold.core.data.delete import delete_data +from scaffold.core.data.sql import query_builder + +query_builder.query_path = os.path.abspath('./data/sql/') + +class create(insert_data): + table = 'requests' + required = {'user_id', 'name'} + columns = {'user_id', 'name'} + columns_optional = {'price', 'description', 'url'} + +class update(update_data): + #~ debug = True + table = 'requests' + required = {'id', 'user_id'} + columns = {'user_id', 'name'} + columns_where = {'id'} + columns_optional = {'price', 'description', 'url'} + +class get_requests(select_data): + debug = True + #~ limit_rows = False + pagination_rows = 100 + #~ query_str = 'select id, user_id, name, description, url, price, count(user_id) as quantity from maidstone_hackspace.requests group by name' + query_str = 'select id, user_id, name, description, url, price as quantity from maidstone_hackspace.requests order by name' + columns = {} + + +class get_request(select_data): + table = 'requests' + required = {'id'} + columns = {'*'} + #query_str = 'select id, user_id, name, description, url, price, count(user_id) as quantity from maidstone_hackspace.requests group by name' + columns_where = {'id'} + #columns = {} + diff --git a/website/data/sql/fetch_user_badges.sql b/website/data/sql/fetch_user_badges.sql old mode 100644 new mode 100755 diff --git a/website/data/sql/get_user_bio.sql b/website/data/sql/get_user_bio.sql old mode 100644 new mode 100755 diff --git a/website/data/sql/get_user_by_oauth_username.sql b/website/data/sql/get_user_by_oauth_username.sql old mode 100644 new mode 100755 diff --git a/website/data/sql/get_user_credentials.sql b/website/data/sql/get_user_credentials.sql old mode 100644 new mode 100755 diff --git a/website/data/sql/get_user_detail.sql b/website/data/sql/get_user_detail.sql old mode 100644 new mode 100755 diff --git a/website/data/sql/get_user_password_reset.sql b/website/data/sql/get_user_password_reset.sql old mode 100644 new mode 100755 diff --git a/website/data/sql/get_users.sql b/website/data/sql/get_users.sql old mode 100644 new mode 100755 diff --git a/website/data/sql/member_list.sql b/website/data/sql/member_list.sql old mode 100644 new mode 100755 diff --git a/website/data/sql/members.py b/website/data/sql/members.py new file mode 100755 index 0000000..2c3ed46 --- /dev/null +++ b/website/data/sql/members.py @@ -0,0 +1,35 @@ +import os +from scaffold.core.data.select import select_data +#~ from scaffold.core.data.insert import insert_data +from scaffold.core.data.update import update_data +#~ from scaffold.core.data.delete import delete_data +from scaffold.core.data.sql import query_builder + +query_builder.query_path = os.path.abspath('./data/sql/') + + +class get_members(select_data): + query_file = 'member_list.sql' + columns = {} + +class get_member_profile(select_data): + required = {'id'} + query_file = 'get_users.sql' + columns_where = {'id'} + +class fetch_member_badges(select_data): + required = {'id'} + query_file = 'fetch_user_badges.sql' + columns_where = {'id'} + +class update_membership_status(update_data): + debug = True + query_str = "update `users` set `status`=%(status)s where id=%(user_id)s" + required = {'user_id', 'status'} + columns_where = {} + +class fetch_member_subscription(select_data): + debug = True + required = {'user_id'} + query_str = 'select provider_id, subscription_reference from user_membership' + columns_where = {'user_id'} diff --git a/website/data/sql/pledge_totals.sql b/website/data/sql/pledge_totals.sql old mode 100644 new mode 100755 diff --git a/website/data/sql/profile.py b/website/data/sql/profile.py new file mode 100755 index 0000000..8bf9801 --- /dev/null +++ b/website/data/sql/profile.py @@ -0,0 +1,28 @@ +import os + +from scaffold.core.data.select import select_data +from scaffold.core.data.insert import insert_data +from scaffold.core.data.update import update_data +#~ from scaffold.core.data.delete import delete_data +from scaffold.core.data.sql import query_builder + +query_builder.query_path = os.path.abspath('./data/sql/') + +class fetch_users(select_data): + query_file = 'get_users.sql' + +class update_description(update_data): + #~ debug = True + table = 'user_detail' + required = {'user_id', 'description', 'skills'} + columns = {'user_id', 'description', 'skills'} + columns_optional = {'description', 'skills'} + columns_where = {'user_id'} + +class create_description(insert_data): + #~ debug = True + table = 'user_detail' + required = {'user_id'} + columns = {'user_id'} + columns_optional = {'description', 'skills'} + diff --git a/website/data/sql/site_user.py b/website/data/sql/site_user.py new file mode 100755 index 0000000..505dabb --- /dev/null +++ b/website/data/sql/site_user.py @@ -0,0 +1,154 @@ +import os +import time +from scaffold.core.data.select import select_data +from scaffold.core.data.insert import insert_data +from scaffold.core.data.update import update_data +from scaffold.core.data.delete import delete_data +from scaffold.core.data.sql import query_builder + +query_builder.query_path = os.path.abspath('./data/sql/') + + +class create_basic_user(insert_data): + """not able to actually log in but registered on the system""" + table = 'users' + required = {'first_name', 'last_name'} + columns = {'first_name', 'last_name'} + + def calculated_data(self): + return {'created': time.strftime('%Y-%m-%d %H:%M:%S')} + + def set(self, data): + data['created'] = time.strftime('%Y-%m-%d %H:%M:%S') + super(create_basic_user, self).set(data) + +class create(insert_data): + table = 'users' + required = {'password', 'username', 'first_name', 'last_name', 'created'} + columns = {'password', 'username', 'first_name', 'last_name', 'created'} + columns_optional = {'profile_image'} + + def calculated_data(self): + return {'created': time.strftime('%Y-%m-%d %H:%M:%S')} + + def set(self, data): + data['created'] = time.strftime('%Y-%m-%d %H:%M:%S') + super(create, self).set(data) + +class update_last_login(update_data): + #~ debug = True + query_str = "update `users` set `last_login`=now()" + required = {'id'} + columns_where = {'id'} + +class update_user_email(update_data): + #~ debug = True + query_str = "update `users` set `email`=%(email)s" + required = {'id', 'email'} + columns_where = {'id'} + +class update_membership_status(update_data): + #~ debug = True + query_str = "update `users` set `status`=%(status)s where id=%(user_id)s" + required = {'user_id', 'status'} + columns_where = {} + +class create_membership(insert_data): + #~ debug = True + table = 'user_membership' + required = {'user_id', 'subscription_reference', 'status', 'amount', 'join_date'} + columns = {'user_id', 'subscription_reference', 'status', 'amount', 'join_date'} + columns_where = {} + +class update_membership(update_data): + #~ debug = True + query_str = u""" + update user_membership set + status=%(status)s, + subscription_reference=%(subscription_reference)s, + amount=%(amount)s, + join_date=%(join_date)s + where id=%(user_id)s""" + required = {'subscription_reference', 'status', 'amount', 'join_date'} + columns_where = {} + +class delete_password_reset(delete_data): + """clean up expired password resets""" + table = 'user_password_reset' + sql_where = 'where DATE_ADD(created, INTERVAL 1 HOUR) < now()' + + +class create_password_reset(insert_data): + table = 'user_password_reset' + required = {'user_id', 'reset_code'} + +class get_user_by_reset_code(select_data): + required = {'reset_code'} + query_file = 'get_user_password_reset.sql' + columns_where = ['reset_code'] + +class change_password(update_data): + table = 'users' + required = {'id', 'password'} + columns_where = ['password'] + sql_where = 'id=%(id)s' + + +class get_users(select_data): + query_file = 'get_users.sql' + + +class get_user_bio(select_data): + #~ debug = True + required = {'id'} + query_file = 'get_user_bio.sql' + columns_where = {'user_id'} + +class get_user_details(select_data): + #~ debug = True + required = {'id'} + query_file = 'get_user_detail.sql' + columns_where = {'users.id'} + +class get_by_email(select_data): + required = {'email'} + query_file = 'get_users.sql' + columns_where = {'email'} + + +class get_by_username(select_data): + required = {'email'} + query_file = 'get_user_credentials.sql' + columns_where = {'email'} + +class authorize(select_data): + required = {'id'} + query_file = 'get_user_credentials.sql' + columns_where = {'id'} + + +class create_oauth_login(insert_data): + #~ debug = True + table = 'user_oauth' + required = {'username', 'provider', 'user_id'} + columns = {'username', 'provider', 'user_id', 'registered'} + + def calculated_data(self): + return {'registered': time.strftime('%Y-%m-%d %H:%M:%S')} + +class update_oauth_login(update_data): + table = 'user_oauth' + columns = {'username', 'provider', 'last_login'} + required = {'username', 'provider'} + query_file = 'get_user_by_oauth_username.sql' + columns_where = {'username', 'provider'} + + def calculated_data(self): + return { + 'last_login': time.strftime('%Y-%m-%d %H:%M:%S') + } + +class fetch_oauth_login(select_data): + required = {'username', 'provider'} + query_file = 'get_user_by_oauth_username.sql' + columns_where = {'username', 'provider'} diff --git a/website/generate.py b/website/generate.py index b56678e..6747130 100644 --- a/website/generate.py +++ b/website/generate.py @@ -3,9 +3,6 @@ import sys import codecs import argparse -sys.path.append(os.path.abspath('../../../scaffold/')) -sys.path.insert(0,os.path.abspath('../../../scaffold/')) - from scaffold import web web.load_widgets('widgets') @@ -33,7 +30,7 @@ def generate_rss(): if __name__ == "__main__": parser = argparse.ArgumentParser(description='Generate static pages') - parser.add_argument('--folder', dest='folder', default='./html/' ,nargs='?', help='output folder') + parser.add_argument('--folder', dest='folder', default='./static/html/' ,nargs='?', help='output folder') #module, function, output file pages_list = ( @@ -44,7 +41,6 @@ if __name__ == "__main__": ('pages.competition', 'index', 'competition.htm')) args = parser.parse_args() - print args.folder for module, page, filename in pages_list: page_module = __import__(module, globals(), locals(), page) @@ -56,5 +52,5 @@ if __name__ == "__main__": print('Failed to Generate %s%s' % (args.folder, filename)) import traceback exc_type, exc_value, exc_traceback = sys.exc_info() - traceback.print_tb(exc_traceback, limit=5, file=sys.stdout) + traceback.print_tb(exc_traceback, limit=10, file=sys.stdout) diff --git a/website/html/blog.htm b/website/html/blog.htm deleted file mode 100644 index c3b9c9c..0000000 --- a/website/html/blog.htm +++ /dev/null @@ -1,121 +0,0 @@ - - - - - - - - - - - - - - - - -
- -
- -
-
test1
test2
-
-

UAV Project By Mike McRoberts

So as you can probably tell I’ve been a bit busy last few months. However, I’ve been very busy with Arduino related stuff for both Medway Makers and Maidstone Hackspace, […]

Attiny85 UFO Escape Keychain Game By Ilya Titov

I finally had some free time to make something fun and decided to write a new game for the Attiny85 ssd1306 keychain.  I reused the sleep, display and interrupts code from the “Breakout” game I created a while ago. This time I wanted a more addictive game. The game had to be playable using just … Continue reading Attiny85 UFO Escape Keychain Game

15 Facebook scams you WONT BELIEVE people fell for By Mathew Beddow

Over the past few weeks, I have been ‘secretly’ unfollowing people on my Facebook feed that re-post or share click-bait posts or wildly unbelievable posts. However, I have noticed that I am rapidly shedding ‘friends’ so I thought this would … Continue reading

Class for generating a grid to position and size windows By Oliver Marks

This is a helper class to split an area up into increasingly smaller areas.

DIY 3D Delta Printer for Ceramic - Introduction By Simon Ridley

In October last year, I got the opportunity to build a RepRap Huxley pro 3D printer for a community crafts centre based in Ashford, Kent. The craft centre wanted a means to demonstrate to its hub of artists how to incorporate new technologies such as 3D printing into their studio work, to create never before seen creations. By building the Huxley it provided me the confidence to build my own 3D printer, which in a series of posts I'm going to discuss further.

Attiny85 Charlieplexed Makerbot Snow Shades By Ilya Titov

I decided to light up the Snow Shades by MakerBot for a party using an Attiny and some LEDs. I mocked up LED position and animation: Then worked out the wiring using the Attiny’s 5 pins. Modified the model in Blender to allow space for the microchip and LEDs. Model files on Thingiverse. Using strands … Continue reading Attiny85 Charlieplexed Makerbot Snow Shades

FLiR Lepton Thermal Camera Module By Mike McRoberts

My FLiR Lepton Thermal Camera Module has finally arrived from the USA. I managed to hook it up to my Raspberry Pi yesterday and successfully receive thermal images from it. The […]

XCB loading and displaying images By Oliver Marks

Helper class which will load in a png and convert it ready for display in a X window.

I’ll try not to drone on… By Mathew Beddow

It would seem that everyone and their dog are getting their own Quadcopter or quadrotor (often misreported in the media as a drone, but that’s another story). A recent build day ran by Reading Hackspace allowed me to jump into their … Continue reading

Recovering Deleted Tweets By Simon Ridley

Occasionally you may find that a notable Twitter profile may remove tweets, or be shut down before preservation can be initiated by the forensic examiner. Should this be the case, you may be required to refer to caching services such as Google Cache or Twicsy for example. I recently had need to evidence data found on Twicsy, however the web interface isn't exactly forensic friendly when it comes to how it displays the data. Twicsy.com is a Twitter picture search engine, which appears to duplicate the original textual data from a tweet containing an image, and stores this information on their own web server. The image from a tweet is simply referred to from the source, and if the tweet is deleted or the profile removed, you'll find the image won't exist any more. However the textual data does still remains on Twicsy's website despite the original tweet not existing. After discovering this I wrote a ruby script to extract each of the archived tweets and place it into a format which is readable.

Retro Ramblings By Mike McRoberts

Crikey, nearly 3 weeks since my last post. Apologies if you’ve been waiting on part 3 of The Arduino Academy, but I’ve got slightly sidetracked lately with several things. Firstly, […]

XCB utility method for examing available methods By Oliver Marks

Simple extended dir function for inspecting xcb.

Loading CSV markers data into Google Maps API By Ilya Titov

Here is a nifty solution designed to populate a google map with geo location markers and infowindow popup boxes. Please enable iframes to view the content First you’ll need to get a Google Maps API developer key. Then we need to prep the data — we need a csv table with a title, content, latitude … Continue reading Loading CSV markers data into Google Maps API

When Microsoft calls a Vulnerability a “Feature” By Mathew Beddow

Also known as “When responsible disclosure gets you no-where, make them listen by going public” but it didn’t have such a good ring to it. So, to the meat of the business. I have a Nokia Lumia 920 which a … Continue reading

The Arduino Academy – Lesson 2 – Basic Outputs By Mike McRoberts

So I present to you Lesson 2 from The Arduino Academy – Basic Outputs: Look out for further lessons: Lesson 3 – Basic Inputs (Digital) Lesson 4 – Basic Inputs […]

Review of "Effective Python" By Oliver Marks

Short review of "Effective Python"

Blue Screen of Death Caused by Wacom Driver By Ilya Titov

So if you get a blue screen of death when using a Wacom Intuos Pro tablet on your desktop, the Wacom driver may be to blame. The error message reads DRIVER_POWER_STATE_FAILURE 0x0000009f In my case the issue was preceeded by the tablet switching off when I plugged the usb cable in. To fix the issue, … Continue reading Blue Screen of Death Caused by Wacom Driver

Backlinks and SEO By Mathew Beddow

Following a recent discussion I had with a university dive club member about a request to remove a back-link from a now dead forum from a travel insurance company, I thought I’d take this opportunity to delve into the mystical … Continue reading

The Arduino Acadamy – Lesson 1 – An Introduction to the Arduino By Mike McRoberts

So, here is my first video for The Arduino Academy. This lesson will introduce you to the #Arduino, tell you what an Arduino is and what you can use it […]

X Desktop Tutorial. By Oliver Marks

Using X build a desktop, learn to package and deploy it and create a login screen.

Virtual Tour Online Embedder By Ilya Titov

Please enable iframes to view content. There are plenty of ways to use your equirectangular panoramas online. The main downside of the programs offering this functionality is the fiddly time consuming process you have to go through to publish the panoramas online, whether they have been saved as an .swf or as an html package. … Continue reading Virtual Tour Online Embedder

Arduino Video Tutorials By Mike McRoberts

Sorry for it being such a long time since my last post. I have been so busy with other non-Arduino related things that I’ve barely had time to update the […]

Handling X keyboard events By Oliver Marks

Example demonstrating handling X keybaord events.

Hover compatibility of dropdown menus on touch devices. By Ilya Titov

I’ve seen different attempts to make the dropdown menus work intuitively on touch devices and while some are trying to utilise the double tap (not the best as this triggers zoom) and various jquery contraptions, all you need is for the top level link to ignore the first tap and let the user see the … Continue reading Hover compatibility of dropdown menus on touch devices.

8MHz Node Test By Mike McRoberts

I’ve resurrected the Sensor Node project and I am experimenting in reducing the power consumption even further. This time I’ve removed the 3.3v voltage regulator from the circuit and I’ve […]

Handling X mouse events By Oliver Marks

Example demonstrating handling X mouse events.

Use Wacom tablet’s buttons to streamline your workflow By Ilya Titov

Design software is becoming more functional and sophisticated but as far as productivity goes — us humans still have to press the buttons to make stuff happen. Don’t get me wrong, if you are just starting out in design college it is ok to mouse through the menus to get to a function for those … Continue reading Use Wacom tablet’s buttons to streamline your workflow

Creating a simple window By Oliver Marks

Example on creating new window and attaching them to the root window.

Using an ESP8266 as a time source (Part 2) By Mike McRoberts

Since my last attempt at using an ESP8266 to get the date and time from the internet I’ve tried out another module, this time with the NodeMCU firmware. This is […]

Attiny85 Breakout Keychain Game By Ilya Titov

So, what can you do with Attiny’s 5 i/o pins? UPDATE: New game, “UFO Escape” side scroller I saw this great Attiny OLED project on http://tinusaur.wordpress.com/ and decided to try out the screen (It will be very handy as a mode display for a remote for my http://orb.photo/ project) So I set out to make … Continue reading Attiny85 Breakout Keychain Game

Review of "App Accomplished" By Oliver Marks

Short review of "App Accomplished"

Using an ESP8266 as a time source (Part 1) By Mike McRoberts

So i’ve obtained some ESP8266 WiFi modules lately and have been having a play with them. If you’ve not heard of the ESP8266 they are tiny serial controlled WiFi modules […]

Ceiling Tile Pixel Array By Ilya Titov

This is a 500 RGB pixels ceiling I made, the project was done in March 2013 and has been working non stop every day since then. The ceiling is installed in the kids department of The Golden Boot shoe shop.  

Querying display information By Oliver Marks

Example on detecting screen sizes and number of screens in a multi head setup.

ATMega328P Power Saving Techniques By Mike McRoberts

I was sent a link to to the following great article on power saving techniques for microprocessors and in particular the ATMEga328P which is the chip used in the Arduino. […]

Attiny85 Canon DSLR IR Remote By Ilya Titov

A nice use for the Attiny85 — a canon DSLR infrared remote. Parts required Attiny85 microcontroller NPN transistor 3.3 or 5 V voltage regulator Pushbutton Infrared led 12v car key/alarm battery Some copper clad board Design the PCB   Tweak the design for better looks in Illustrator (Download printable PDF) Print and transfer the pcb … Continue reading Attiny85 Canon DSLR IR Remote

Querying Extensions By Oliver Marks

Query available extensions and checking for availability.

Debugging with XCB By Oliver Marks

Debugging XCB and catching errors and exploring the library.

Using properties and atoms By Oliver Marks

This example shows how to get properties and use atoms.

Retrieving window details By Oliver Marks

Howto get a list of open windows and there titles.

Generating a debian package By Oliver Marks

Using launchpad we will create a ppa which will automatically generate your package from the standard debian packaging files.

Create a new login greeter entry By Oliver Marks

Populate a listbox with custom widgets, in this case an example file downloader.

Create your project repository By Oliver Marks

Howto create a new project and push it to launchpad.

Review of "From Mathematics to Generic Programming" By Oliver Marks

Short review of "From Mathematics to Generic Programming"

Rendering textured pixels with OpenGL Example By Oliver Marks

OpenGL program that does pixel shading, OpenGL pixels with size and textures often used for particle effects.

GTK3 custom signals example By Oliver Marks

Simple example on how to setup, connect to and trigger signals.

Draw two cubes using Kivy with different shaders. By Oliver Marks

Kivy example draw two cubes with different shaders and vertices so they can be moved seperately.

Review of OpenGL ES 3.0 programming guide By Oliver Marks

Short review of "OpenGL ES 3.0 programming guide"

Draw a cube mixing kivy widgets By Oliver Marks

Kivy example on setting up a display and drawing a basic triangle

Draw a textured square with Kivy By Oliver Marks

Kivy example drawing a square and loading an image and applying to the quad as a simple texture.

-
-
-
- -