contact form, newer styles and various other tweaks

This commit is contained in:
Oliver Marks 2016-08-20 19:45:28 +01:00
parent 7470d0f364
commit c213dd0811
132 changed files with 1683 additions and 1404 deletions

View File

@ -1,34 +1,52 @@
# fabricad FROM olymk2/uwsgi
#
# VERSION 0.0.1
FROM ubuntu:16.04 ENV SERVER_ENVIRONMENT DEVELOPMENT
MAINTAINER Oliver Marks "olymk2@gmail.com" 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 # make sure the package repository is up to date
RUN \ #RUN \
apt-get update && \ # apt-get update && \
apt-get upgrade -y && \ # apt-get upgrade -y && \
apt-get install -y libssl-dev libffi-dev nano && \ # 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-software-properties && \
apt-get install -y software-properties-common python-pip python-dev python-nose2 && \ # 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 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 && \ # apt-get install -y cssmin slimit && \
add-apt-repository -y ppa:oly/ppa && \ # add-apt-repository -y ppa:oly/ppa && \
apt-get update && \ # apt-get update && \
apt-get install -y python-scaffold # apt-get install -y python-scaffold
RUN pip install gocardless paypalrestsdk pytz
#allow access to flask #allow access to flask
EXPOSE 5000 5002 #EXPOSE 5000 5002
WORKDIR /var/www/website/ #WORKDIR /var/www/website/
#RUN /bin/sh -c 'cd /var/www/site; python index.py' #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 build -t mhackspace .
#docker run -d --name=mhackspace_container --restart=always 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 #accesss on dockerip 172.17.0.?:5000
#https://hub.docker.com/r/olymk2/mhackspace/ #https://hub.docker.com/r/olymk2/mhackspace/

View File

@ -15,6 +15,9 @@ and make sure the quick start guide works https://docs.docker.com/machine/get-st
docker-compose up 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. If you plan on making large changes consider discussing it first so you dont waste your own time.
Generating static content Generating static content

View File

@ -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

View File

@ -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

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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

View File

@ -1,25 +1,50 @@
version: '2'
nginx: services:
image: olymk2/mhackspace mhackspace_uwsgi:
#name: mhackspace image: olymk2/mhackspace
links: restart: unless-stopped
- db:database network_mode: bridge
dns: links:
- 8.8.8.8 - mariadb:mariadb
- 8.8.4.4 - mhackspace_web:nginx
ports: - mhackspace_mail:mail_server
- "5000:5000" volumes:
volumes: - sockets:/data/sockets
- .:/var/www - ./website/:/var/www
restart: always - ./config/uwsgi/maidstone-hackspace.org.uk.ini:/etc/uwsgi/apps-enabled/maidstone-hackspace.org.uk.ini
db: mhackspace_web:
image: mariadb image: olymk2/nginx
ports: restart: unless-stopped
- "3300:3306" network_mode: bridge
environment: dns:
MYSQL_DATABASE: maidstone_hackspace - 8.8.8.8
MYSQL_USER: mhackspace - 8.8.4.4
MYSQL_PASSWORD: mhackspace volumes:
MYSQL_ROOT_PASSWORD: mhackspace - ./website/:/var/www
restart: always - 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

View File

@ -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/<provider>/<start_oauth_login>/", methods=['GET'])
@authorize_pages.route("/oauth/<provider>/<start_oauth_login>", methods=['GET'])
@authorize_pages.route("/oauth/<provider>/", methods=['GET'])
@authorize_pages.route("/oauth/<provider>", 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/<code>", 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('/')

10
website/config/logger.py Normal file
View File

@ -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)

View File

@ -1,6 +1,10 @@
import os import os
import constants
import socket
from scaffold.core.data.database import db from scaffold.core.data.database import db
from scaffold import web from scaffold import web
from libs import mail
schema = 'https:' schema = 'https:'
domain = '127.0.0.1' domain = '127.0.0.1'
@ -35,27 +39,27 @@ google_calendar_api_key = ''
if os.environ.get('SERVER_ENVIRONMENT') =='DOCKER': if os.environ.get('SERVER_ENVIRONMENT') =='DOCKER':
if os.path.exists('config/settings_docker.py'): if os.path.exists('config/settings_docker.py'):
print 'Using settings for docker enviroment' print('Using settings for docker enviroment')
from settings_docker import * from config.settings_docker import *
else: else:
if os.path.exists('config/settings_dev.py'): if os.path.exists('config/settings_dev.py'):
print 'Using settings for dev enviroment' print('Using settings for dev enviroment')
from settings_dev import * from config.settings_dev import *
if os.path.exists('config/settings_testing.py'): if os.path.exists('config/settings_testing.py'):
print('Using settings for test enviroment') print('Using settings for test enviroment')
from settings_testing import * from config.settings_testing import *
if os.path.exists('config/settings_live.py'): if os.path.exists('config/settings_live.py'):
print('Using settings for live enviroment') print('Using settings for live enviroment')
from settings_live import * from config.settings_live import *
with web.template as setup: with web.template as setup:
#css for jquery, material sprite sheet and custom css #css for jquery, material sprite sheet and custom css
setup.persistent_header('<link rel="stylesheet" id="navigationCss" href="/static/css/default.css" media="" type="text/css" />')
setup.persistent_header('<link rel="stylesheet" id="navigationCss" href="/static/css/materialize.css" media="" type="text/css" />') setup.persistent_header('<link rel="stylesheet" id="navigationCss" href="/static/css/materialize.css" media="" type="text/css" />')
setup.persistent_header('<link rel="stylesheet" id="navigationCss" href="/static/css/default.css" media="" type="text/css" />')
setup.persistent_header('<link rel="stylesheet" id="navigationCss" href="/static/js/jquery-ui/themes/base/jquery-ui.css" media="" type="text/css" />') setup.persistent_header('<link rel="stylesheet" id="navigationCss" href="/static/js/jquery-ui/themes/base/jquery-ui.css" media="" type="text/css" />')
setup.persistent_header('<link rel="stylesheet" id="navigationCss" href="/static/css/sprite-navigation-white.css" media="" type="text/css" />') setup.persistent_header('<link rel="stylesheet" id="navigationCss" href="/static/css/sprite-navigation-white.css" media="" type="text/css" />')
setup.persistent_header('<link rel="stylesheet" id="navigationCss" href="/static/css/sprite-action-white.css" media="" type="text/css" />') setup.persistent_header('<link rel="stylesheet" id="navigationCss" href="/static/css/sprite-action-white.css" media="" type="text/css" />')
@ -65,6 +69,7 @@ with web.template as setup:
setup.persistent_header('<script type="text/javascript" src="/static/js/jquery-2.2.3.min.js"></script>') setup.persistent_header('<script type="text/javascript" src="/static/js/jquery-2.2.3.min.js"></script>')
setup.persistent_header('<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.0/angular.js"></script>') setup.persistent_header('<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.0/angular.js"></script>')
setup.persistent_header('<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.0/angular-animate.js"></script>') setup.persistent_header('<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.0/angular-animate.js"></script>')
setup.persistent_header('<script type="text/javascript" src="/static/js/materialize.js"></script>')
setup.persistent_header('<script type="text/javascript" src="/static/js/default.js"></script>') setup.persistent_header('<script type="text/javascript" src="/static/js/default.js"></script>')
#other favicon etc #other favicon etc
@ -75,5 +80,5 @@ with web.template as setup:
domain=domain, domain=domain,
port=port) port=port)
db.config(database) db.config(database)
mail.sendmail.set_server(email_server)

View File

@ -1,3 +1,5 @@
import os
import socket
database = { database = {
'charset': 'utf8', 'charset': 'utf8',
@ -43,8 +45,28 @@ payment_providers = {
google_calendar_id = 'contact@maidstone-hackspace.org.uk' google_calendar_id = 'contact@maidstone-hackspace.org.uk'
google_calendar_api_key = 'AIzaSyA98JvRDmplA9lVLZeKwrs1f2k17resLy0' 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/' 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' 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'}

View File

@ -3,7 +3,7 @@ page_menu = [
('Home', '/'), ('Home', '/'),
('Chat', '/chat'), ('Chat', '/chat'),
('Donate', '/donate'), ('Donate', '/donate'),
('Contact', '#mailing-list-signup')] ('Contact', '/contact-us')]
nav_for_authenticated_user = ( nav_for_authenticated_user = (
('Profile', '/profile'), ('Profile', '/profile'),
@ -14,7 +14,8 @@ nav_for_authenticated_user = (
) )
banner_images = [ 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/audio_board.jpg', 'Audio board', 'Audio board', ''),
('/static/images/banners/microscope.jpg', '', 'Microscope', ''), ('/static/images/banners/microscope.jpg', '', 'Microscope', ''),
('/static/images/banners/object_avoiding_robot.jpg', '', 'Object avoiding robot', ''), ('/static/images/banners/object_avoiding_robot.jpg', '', 'Object avoiding robot', ''),
@ -42,6 +43,9 @@ rss_feeds = [{
}, { }, {
'author':'Mike McRoberts', 'author':'Mike McRoberts',
'url': 'http://thearduinoguy.org/?feed=rss2' 'url': 'http://thearduinoguy.org/?feed=rss2'
}, {
'author': 'James',
'url': 'https://feeds.feedburner.com/projects-jl'
}] }]
kent_hackspace = [ kent_hackspace = [
@ -58,14 +62,6 @@ maker_events = [
email = 'support@maidstone-hackspace.org.uk' 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 = { badge_lookup = {
1: 'member', 1: 'member',
2: 'backer', 2: 'backer',

View File

@ -23,7 +23,6 @@ class assign_badge(insert_data):
class fetch_badges(select_data): class fetch_badges(select_data):
#~ debug = True #~ debug = True
table = 'badges' table = 'badges'
required = {}
columns = {'id', 'name'} columns = {'id', 'name'}
class fetch_badge(select_data): class fetch_badge(select_data):

View File

@ -28,7 +28,6 @@ class get_requests(select_data):
debug = True debug = True
#~ limit_rows = False #~ limit_rows = False
pagination_rows = 100 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, 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' query_str = 'select id, user_id, name, description, url, price as quantity from maidstone_hackspace.requests order by name'
columns = {} columns = {}

View File

@ -9,7 +9,6 @@ query_builder.query_path = os.path.abspath('./data/sql/')
class get_members(select_data): class get_members(select_data):
required = {}
query_file = 'member_list.sql' query_file = 'member_list.sql'
columns = {} columns = {}

View File

@ -76,7 +76,6 @@ class delete_password_reset(delete_data):
"""clean up expired password resets""" """clean up expired password resets"""
table = 'user_password_reset' table = 'user_password_reset'
sql_where = 'where DATE_ADD(created, INTERVAL 1 HOUR) < now()' sql_where = 'where DATE_ADD(created, INTERVAL 1 HOUR) < now()'
required = {}
class create_password_reset(insert_data): class create_password_reset(insert_data):
@ -89,14 +88,15 @@ class get_user_by_reset_code(select_data):
columns_where = ['reset_code'] columns_where = ['reset_code']
class change_password(update_data): class change_password(update_data):
debug=True
table = 'users' table = 'users'
required = {'id', 'password'} required = {'id', 'password'}
columns_where = ['password'] columns = ['password']
#~ columns_where = ['password']
sql_where = 'id=%(id)s' sql_where = 'id=%(id)s'
class get_users(select_data): class get_users(select_data):
required = {}
query_file = 'get_users.sql' query_file = 'get_users.sql'
@ -119,9 +119,9 @@ class get_by_email(select_data):
class get_by_username(select_data): class get_by_username(select_data):
required = {'email'} required = {'username'}
query_file = 'get_user_credentials.sql' query_file = 'get_user_credentials.sql'
columns_where = {'email'} columns_where = {'username'}
class authorize(select_data): class authorize(select_data):
required = {'id'} required = {'id'}
@ -138,6 +138,18 @@ class create_oauth_login(insert_data):
def calculated_data(self): def calculated_data(self):
return {'registered': time.strftime('%Y-%m-%d %H:%M:%S')} 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): class update_oauth_login(update_data):
table = 'user_oauth' table = 'user_oauth'
columns = {'username', 'provider', 'last_login'} columns = {'username', 'provider', 'last_login'}

0
website/data/sql/__init__.py Executable file
View File

54
website/data/sql/badges.py Executable file
View File

@ -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'}

37
website/data/sql/donate.py Executable file
View File

@ -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'}

43
website/data/sql/equipment.py Executable file
View File

@ -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 = {}

0
website/data/sql/fetch_user_badges.sql Normal file → Executable file
View File

0
website/data/sql/get_user_bio.sql Normal file → Executable file
View File

0
website/data/sql/get_user_by_oauth_username.sql Normal file → Executable file
View File

0
website/data/sql/get_user_credentials.sql Normal file → Executable file
View File

0
website/data/sql/get_user_detail.sql Normal file → Executable file
View File

0
website/data/sql/get_user_password_reset.sql Normal file → Executable file
View File

0
website/data/sql/get_users.sql Normal file → Executable file
View File

0
website/data/sql/member_list.sql Normal file → Executable file
View File

35
website/data/sql/members.py Executable file
View File

@ -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'}

0
website/data/sql/pledge_totals.sql Normal file → Executable file
View File

28
website/data/sql/profile.py Executable file
View File

@ -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'}

154
website/data/sql/site_user.py Executable file
View File

@ -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'}

View File

@ -3,9 +3,6 @@ import sys
import codecs import codecs
import argparse import argparse
sys.path.append(os.path.abspath('../../../scaffold/'))
sys.path.insert(0,os.path.abspath('../../../scaffold/'))
from scaffold import web from scaffold import web
web.load_widgets('widgets') web.load_widgets('widgets')
@ -33,7 +30,7 @@ def generate_rss():
if __name__ == "__main__": if __name__ == "__main__":
parser = argparse.ArgumentParser(description='Generate static pages') 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 #module, function, output file
pages_list = ( pages_list = (
@ -44,7 +41,6 @@ if __name__ == "__main__":
('pages.competition', 'index', 'competition.htm')) ('pages.competition', 'index', 'competition.htm'))
args = parser.parse_args() args = parser.parse_args()
print args.folder
for module, page, filename in pages_list: for module, page, filename in pages_list:
page_module = __import__(module, globals(), locals(), page) page_module = __import__(module, globals(), locals(), page)
@ -56,5 +52,5 @@ if __name__ == "__main__":
print('Failed to Generate %s%s' % (args.folder, filename)) print('Failed to Generate %s%s' % (args.folder, filename))
import traceback import traceback
exc_type, exc_value, exc_traceback = sys.exc_info() 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)

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -12,6 +12,7 @@ from pages import homepage
from pages import chat from pages import chat
from pages import blog from pages import blog
from pages import members from pages import members
from pages.contact import contact_page, submit_contact_page
from pages.core.login_pages import login_pages from pages.core.login_pages import login_pages
from pages.core.authorize import authorize_pages, login_manager from pages.core.authorize import authorize_pages, login_manager
@ -21,7 +22,6 @@ from pages.equipment import equipment_pages
from pages.profile import profile_pages from pages.profile import profile_pages
web_app = Flask(__name__, static_folder='static') web_app = Flask(__name__, static_folder='static')
web_app.config['PROPAGATE_EXCEPTIONS'] = True web_app.config['PROPAGATE_EXCEPTIONS'] = True
web_app.secret_key = settings.flask_secret_key web_app.secret_key = settings.flask_secret_key
@ -73,5 +73,15 @@ def chat_index():
"""competition page""" """competition page"""
return make_response(chat.index()) return make_response(chat.index())
@web_app.route("/contact-us/", methods=['GET'])
def contact_us():
"""Contact page"""
return make_response(contact_page())
@web_app.route("/contact-us/", methods=['POST'])
def submit_contact_us():
"""Contact page"""
return make_response(submit_contact_page())
if __name__ == '__main__': if __name__ == '__main__':
web_app.run(host='0.0.0.0', port=5000, debug=True) web_app.run(host='0.0.0.0', port=5000, debug=True)

View File

@ -0,0 +1,28 @@
from uuid import uuid4
import os
import hashlib
import requests
def save_remote_image(url, path='', domain='', skip_exists=True):
filename = '%s.%s' % (hashlib.md5(url.encode()).hexdigest(), url[-3:])
filepath = os.path.abspath('./static/images/blogs/%s' %(filename))
urlpath = 'static/images/blogs/' + filename
if not url:
return None
if url[-3:] not in ('jpg', 'png'):
return None
print(domain + urlpath)
if skip_exists is True and os.path.exists(filepath):
return domain + urlpath
r = requests.get(url, stream=True)
if r.status_code != 200:
return None
with open(filepath, 'wb') as f:
for chunk in r.iter_content(1024):
f.write(chunk)
return domain + urlpath

View File

@ -1,15 +1,15 @@
from mailer import Mailer from mailer import Mailer
from mailer import Message from mailer import Message
from config.logger import log
class sendmail: class sendmail:
host = 'localhost' config = 'localhost'
charset = 'utf-8' charset = 'utf-8'
subject_prefix = '' subject_prefix = ''
@classmethod @classmethod
def set_server(cls, host='localhost', charset='utf-8'): def set_server(cls, config, charset='utf-8'):
cls.host = host cls.config = config
def __call__(self, **args): def __call__(self, **args):
return self return self
@ -19,20 +19,35 @@ class sendmail:
self.body = fp.read() self.body = fp.read()
self.body.format(**params) self.body.format(**params)
def send(self, from_address, to_address, subject, body=None, html=True): def send(self, from_address, to_address, subject, body=None, html=True):
message = Message( try:
From=from_address, message = Message(
To=to_address, From=from_address,
charset=self.charset To=to_address,
) charset=self.charset
)
if body: if body is None:
body = ''
self.body = body self.body = body
message.Subject = "%sAn HTML Email" % self.subject_prefix message.Subject = "%s - %s" % (self.subject_prefix, subject)
message.Html = self.body message.Html = self.body
message.Body = self.body message.Body = self.body
except Exception as e:
log.exception('[scaffold_mailer] - Failed to create message object for mailer')
return False
sender = Mailer(self.host) try:
sender.send(message) sender = Mailer(
host=self.config.get('host'),
port=self.config.get('port'),
use_tls=self.config.get('use_tls', False),
use_ssl=True,
usr=self.config.get('username'),
pwd=self.config.get('password'))
sender.send(message)
except Exception as e:
log.exception('[scaffold_mailer] - Failed to connect to smtp sever and send message with subject: %s' % message.Subject)
return False
return True

View File

@ -24,8 +24,8 @@ class payment:
self.provider_id = PROVIDER_ID.get(provider) self.provider_id = PROVIDER_ID.get(provider)
if provider == 'paypal': if provider == 'paypal':
print settings.payment_providers[provider]['credentials']
paypal.configure(**settings.payment_providers[provider]['credentials']) paypal.configure(**settings.payment_providers[provider]['credentials'])
print(settings.payment_providers[provider]['credentials'])
return return
#~ environment = int('production' = settings.payment_providers[provider]['environment']) #~ environment = int('production' = settings.payment_providers[provider]['environment'])
@ -52,13 +52,13 @@ class payment:
"description": reference}]}) "description": reference}]})
payment_response = payment.create() payment_response = payment.create()
print 'payment create' print('payment create')
if payment_response: if payment_response:
print payment_response print(payment_response)
for link in payment.links: for link in payment.links:
if link.method == "REDIRECT": if link.method == "REDIRECT":
redirect_url = str(link.href) redirect_url = str(link.href)
print redirect_url print(redirect_url)
return str(redirect_url) return str(redirect_url)
else: else:
print("Error while creating payment:") print("Error while creating payment:")
@ -84,17 +84,18 @@ class payment:
'amount': paying_member.amount} 'amount': paying_member.amount}
if self.provider == 'paypal': if self.provider == 'paypal':
#~ I-S39170DK26AF
#~ start_date, end_date = "2014-07-01", "2014-07-20" #~ start_date, end_date = "2014-07-01", "2014-07-20"
billing_agreement = paypal.BillingAgreement.find('') billing_agreement = paypal.BillingAgreement.find('')
print billing_agreement print(billing_agreement)
print dir(billing_agreement) print(dir(billing_agreement))
#~ print billing_agreement.search_transactions(start_date, end_date) #~ print billing_agreement.search_transactions(start_date, end_date)
#~ transactions = billing_agreement.search_transactions(start_date, end_date) #~ transactions = billing_agreement.search_transactions(start_date, end_date)
payment_history = paypal.Payment.all({"count": 2}) payment_history = paypal.Payment.all({"count": 2})
# List Payments # List Payments
print("List Payment:") print("List Payment:")
print payment_history print(payment_history)
for payment in payment_history.payments: for payment in payment_history.payments:
print(" -> Payment[%s]" % (payment.id)) print(" -> Payment[%s]" % (payment.id))
#~ print paypal.BillingAgreement.all() #~ print paypal.BillingAgreement.all()
@ -104,8 +105,8 @@ class payment:
print("List BillingPlan:") print("List BillingPlan:")
for plan in history.plans: for plan in history.plans:
print dir(plan) print(dir(plan))
print plan.to_dict() print(plan.to_dict())
print(" -> BillingPlan[%s]" % (plan.id)) print(" -> BillingPlan[%s]" % (plan.id))
#~ merchant = gocardless.client.merchant() #~ merchant = gocardless.client.merchant()
@ -128,12 +129,12 @@ class payment:
} }
if self.provider == 'paypal': if self.provider == 'paypal':
print 'subscribe_confirm' print('subscribe_confirm')
payment_token = args.get('token', '') payment_token = args.get('token', '')
billing_agreement_response = paypal.BillingAgreement.execute(payment_token) billing_agreement_response = paypal.BillingAgreement.execute(payment_token)
amount = 0 amount = 0
print billing_agreement_response print(billing_agreement_response)
print billing_agreement_response.id print(billing_agreement_response.id)
for row in billing_agreement_response.plan.payment_definitions: for row in billing_agreement_response.plan.payment_definitions:
amount = row.amount.value amount = row.amount.value
@ -147,16 +148,16 @@ class payment:
def unsubscribe(self, reference): def unsubscribe(self, reference):
if self.provider == 'gocardless': if self.provider == 'gocardless':
print 'unsubscribe gocardless' print('unsubscribe gocardless')
subscription = gocardless.client.subscription(reference) subscription = gocardless.client.subscription(reference)
print subscription.cancel() print(subscription.cancel())
if self.provider == 'paypal': if self.provider == 'paypal':
# this may be wrong # this may be wrong
# ManageRecurringPaymentsProfileStatus # ManageRecurringPaymentsProfileStatus
print reference print(reference)
billing_plan = paypal.BillingAgreement.find(reference) billing_plan = paypal.BillingAgreement.find(reference)
print billing_plan print(billing_plan)
print(billing_plan.error) print(billing_plan.error)
#~ billing_plan.replace([{"op": "replace","path": "/","value": {"state":"DELETED"}}]) #~ billing_plan.replace([{"op": "replace","path": "/","value": {"state":"DELETED"}}])
print(billing_plan.error) print(billing_plan.error)
@ -212,7 +213,7 @@ class payment:
], ],
"type": "INFINITE" "type": "INFINITE"
}) })
print 'create bill' print('create bill')
response = billing_plan.create() response = billing_plan.create()
@ -229,8 +230,8 @@ class payment:
}) })
if billing_agreement.create(): if billing_agreement.create():
print 'billing agreement id' print('billing agreement id')
print billing_agreement.id print(billing_agreement.id)
for link in billing_agreement.links: for link in billing_agreement.links:
if link.rel == "approval_url": if link.rel == "approval_url":
@ -238,24 +239,24 @@ class payment:
return approval_url return approval_url
else: else:
print(billing_agreement.error) print(billing_agreement.error)
print 'failed' print('failed')
def confirm(self, args): def confirm(self, args):
confirm_details = {} confirm_details = {}
confirm_details['successfull'] = False confirm_details['successfull'] = False
print '---------------------' print('---------------------')
print args print(args)
from pprint import pprint from pprint import pprint
if self.provider == 'paypal': if self.provider == 'paypal':
print args.get('paymentId') print(args.get('paymentId'))
print args.get('PayerID') print(args.get('PayerID'))
payment = paypal.Payment.find(args.get('paymentId')) payment = paypal.Payment.find(args.get('paymentId'))
pprint(payment) pprint(payment)
print pprint(payment) print(pprint(payment))
print payment print(payment)
confirm_details['name'] = payment['payer']['payer_info'].first_name + ' ' + payment['payer']['payer_info'].last_name confirm_details['name'] = payment['payer']['payer_info'].first_name + ' ' + payment['payer']['payer_info'].last_name
confirm_details['user'] = payment['payer']['payer_info'].email confirm_details['user'] = payment['payer']['payer_info'].email

18
website/libs/recapture.py Normal file
View File

@ -0,0 +1,18 @@
"""https://developers.google.com/recaptcha/docs/verify"""
from requests import post
from config.logger import log
def verify_captcha(secret, response, remoteip=''):
try:
response = post(
'https://www.google.com/recaptcha/api/siteverify',
{'secret': secret,
'response': response,
'remoteip': remoteip})
json = response.json()
except:
log.error('Failed to get capture json response from google')
return False
return json.get('success')

View File

@ -7,8 +7,8 @@ from scaffold.core.data.database import db
db.config(settings.database) db.config(settings.database)
from scaffold.core.data.migrations import export_schema, import_schema from scaffold.core.data.migrations import export_schema, import_schema
export_schema(os.path.abspath('./data/migrate/')) #export_schema(os.path.abspath('./data/migrate/'))
#~ import_schema(os.path.abspath('./data/migrate/')) import_schema(os.path.abspath('./data/migrate/'))

View File

@ -15,15 +15,15 @@ web.template.append('<link rel="icon" type="image/png" href="/static/images/icon
#paths #paths
web.document_root = os.path.abspath('./') web.document_root = os.path.abspath('./')
web.template.domain = 'http://maidstone-hackspace.org.uk/' web.template.domain = 'https://maidstone-hackspace.org.uk/'
web.template.theme_full_path = os.path.abspath('./static') + os.sep web.template.theme_full_path = os.path.abspath('./static') + os.sep
domain = 'http://192.168.21.41:5000/' #~ domain = 'http://192.168.21.41:5000/'
image_path = domain + os.sep + 'images' + os.sep #~ image_path = domain + os.sep + 'images' + os.sep
with web.template as setup: with web.template as setup:
setup.persistent_header('<link rel="stylesheet" id="navigationCss" href="/static/css/default.css" media="" type="text/css" />')
setup.persistent_header('<link rel="stylesheet" id="navigationCss" href="/static/css/materialize.css" media="" type="text/css" />') setup.persistent_header('<link rel="stylesheet" id="navigationCss" href="/static/css/materialize.css" media="" type="text/css" />')
setup.persistent_header('<link rel="stylesheet" id="navigationCss" href="/static/css/default.css" media="" type="text/css" />')
#~ setup.persistent_header('<link rel="stylesheet" id="navigationCss" href="/static/js/jquery-ui/themes/base/jquery-ui.css" media="" type="text/css" />') #~ setup.persistent_header('<link rel="stylesheet" id="navigationCss" href="/static/js/jquery-ui/themes/base/jquery-ui.css" media="" type="text/css" />')
#setup.persistent_header('<link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/angular_material/0.9.4/angular-material.min.css">') #setup.persistent_header('<link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/angular_material/0.9.4/angular-material.min.css">')
#setup.persistent_header('<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=RobotoDraft:300,400,500,700,400italic">') #setup.persistent_header('<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=RobotoDraft:300,400,500,700,400italic">')
@ -31,10 +31,10 @@ with web.template as setup:
setup.persistent_header('<link rel="stylesheet" id="navigationCss" href="/static/css/sprite-action-white.css" media="" type="text/css" />') setup.persistent_header('<link rel="stylesheet" id="navigationCss" href="/static/css/sprite-action-white.css" media="" type="text/css" />')
setup.persistent_header('<link rel="stylesheet" id="navigationCss" href="/static/css/sprite-content-white.css" media="" type="text/css" />') setup.persistent_header('<link rel="stylesheet" id="navigationCss" href="/static/css/sprite-content-white.css" media="" type="text/css" />')
setup.persistent_header('<script type="text/javascript" src="/static/js/jquery-2.2.3.min.js"></script>') setup.persistent_header('<script type="text/javascript" src="/static/js/jquery-2.2.3.min.js"></script>')
setup.persistent_header('<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.0/angular.js"></script>') setup.persistent_header('<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.0/angular.js"></script>')
setup.persistent_header('<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.0/angular-animate.js"></script>') setup.persistent_header('<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.0/angular-animate.js"></script>')
setup.persistent_header('<script type="text/javascript" src="/static/js/materialize.js"></script>')
setup.persistent_header('<script type="text/javascript" src="/static/js/default.js"></script>') setup.persistent_header('<script type="text/javascript" src="/static/js/default.js"></script>')
#setup.persistent_header('<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular-aria.min.js"></script>') #setup.persistent_header('<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular-aria.min.js"></script>')
@ -50,18 +50,19 @@ def header(title, description='Maidstone Hackspace is a shared space where artis
web.template.body.append(web.header_strip.render()) web.template.body.append(web.header_strip.render())
# top menu bar navigation # top menu bar navigation
web.menu.create('/' + url).set_id('leftNav')
web.menu.create('/' + url).set_classes('col s10 offset-s1')
web.menu * site.page_menu web.menu * site.page_menu
web.menu.append('Group', '/mailing-list') web.menu.append('Group', '/mailing-list')
if current_user and current_user.is_authenticated(): if current_user and current_user.is_authenticated:
web.menu.append('Logout', '/logout') web.menu.append('Logout', '/logout')
web.navigation_bar.create(hide=(False if url=='/profile' else True)) web.navigation_bar.create(hide=(False if url=='/profile' else True))
web.navigation_bar * site.nav_for_authenticated_user web.navigation_bar * site.nav_for_authenticated_user
web.template.body.append(web.navigation_bar.render()) web.template.body.append(web.navigation_bar.render())
else: else:
web.menu.append('Login', '/login') web.menu.append('Login', '/login')
web.template.body.append(web.menu.render()) web.template.body.append(web.container.create(web.menu.render()).set_id('menubar').set_classes('row').render())
# lets create the footer # lets create the footer
web.footer_content.create().append( web.footer_content.create().append(

View File

@ -12,5 +12,5 @@ def index():
web.page.create(web.title.create('IRC Chat Room').render()) web.page.create(web.title.create('IRC Chat Room').render())
web.page.create(web.paragraph.create('Pop in and say hi, please be patient users tend to idle and will respond when they get a chance.').render()) web.page.create(web.paragraph.create('Pop in and say hi, please be patient users tend to idle and will respond when they get a chance.').render())
web.page.section(web.chat.create('maidstone-hackspace').render()) web.page.section(web.chat.create('maidstone-hackspace').render())
web.template.body.append(web.page.render()) web.template.body.append(web.page.set_classes('page col s10 offset-s1').render())
return footer() return footer()

View File

@ -8,5 +8,5 @@ def index():
web.page.create(web.title.create('IRC Chat Room').render()) web.page.create(web.title.create('IRC Chat Room').render())
web.page.create(web.paragraph.create('Pop in and say hi, please be patient users tend to idle, but will likely respond given a chance.').render()) web.page.create(web.paragraph.create('Pop in and say hi, please be patient users tend to idle, but will likely respond given a chance.').render())
web.page.section(web.chat.create('maidstone-hackspace').render()) web.page.section(web.chat.create('maidstone-hackspace').render())
web.template.body.append(web.page.render()) web.template.body.append(web.page.set_classes('page col s10 offset-s1').render())
return footer() return footer()

56
website/pages/contact.py Normal file
View File

@ -0,0 +1,56 @@
from pages import web
from pages import header, footer
from config.settings import google_captcha, email_server
from libs.mail import sendmail
from flask import get_flashed_messages, flash, request
from libs.recapture import verify_captcha
def contact_page():
web.template.create('Maidstone Hackspace - Chat room')
header('Maidstone Hackspace Chat')
web.contact_form.capture_settings = google_captcha
web.contact_form.create('Contact Us')
web.contact_form.enable_capture()
web.contact_form.render()
web.simple_form.create()
web.simple_form.append(input_type='text', input_name='test', label='my label')
web.page.create(web.title.create('Contact Form').render())
web.page.section(web.contact_form.render())
web.template.body.append(web.page.set_classes('page col s10 offset-s1').render())
return footer()
def submit_contact_page():
subject = '[%s] - %s' % (
request.form.get('form_query', 'MHS'),
request.form.get('form_subject', 'No Subject ?')
)
# Check user password the robot check
print(request.form.get('g-recaptcha-response', ''))
print(google_captcha.get('secret'))
print(request.remote_addr)
success = verify_captcha(
secret=google_captcha.get('secret'),
response=request.form.get('g-recaptcha-response', ''),
remoteip=request.remote_addr)
if success is False:
flash('Message failed to send, capture failed to validate are you a robot ?')
return contact_page()
# Send Email and let user know if it worked or not
success = sendmail().send(
from_address=request.form.get('email'),
to_address='contact@maidstone-hackspace.org.uk',
subject=subject,
body=request.form.get('form_message'))
if success is False:
flash('Sorry system was unable to send your message, someone has been notified.')
return contact_page()
flash('Your message has been sent, we will get back to you shortly.')
return contact_page()

View File

@ -27,7 +27,8 @@ authorize_pages = Blueprint('authorize_pages', __name__, template_folder='templa
login_manager = LoginManager() login_manager = LoginManager()
login_manager.login_view = '/login' login_manager.login_view = '/login'
oauth_lookup = {'google':1, 'github':2, 'facebook':3} oauth_lookup_id = {'google':1, 'github':2, 'facebook':3}
oauth_lookup_name = dict((v, k) for k, v in oauth_lookup_id.items())
def is_weak_password(password1, password2): def is_weak_password(password1, password2):
@ -36,32 +37,28 @@ def is_weak_password(password1, password2):
return True return True
# TODO check length and chars # TODO check length and chars
password1 = password2 = None password1 = password2 = None
return False return False
def todict(data): #~ def todict(data):
new_dict = {} #~ new_dict = {}
for key, value in data.items(): #~ for key, value in data.items():
new_dict[key] = value #~ new_dict[key] = value
return new_dict #~ return new_dict
class User(UserMixin): class User(UserMixin):
def __init__(self, user_id, active=True): def __init__(self, user_id, active=True):
print user_id
self.id = None self.id = None
user_details = site_user.get_user_details({'id': user_id}).get() user_details = site_user.get_user_details({'id': user_id}).get()
self.active = False self.active = False
print 'user'
print user_details
if user_details: if user_details:
self.active = True
#~ self.check_password(user_details.get('password')) #~ self.check_password(user_details.get('password'))
self.id = user_id self.id = user_id
self.name = user_details.get('username') self.name = user_details.get('username')
print self.name #~ self.is_authenticated = self.active
self.active = active
def get_id(self): def get_id(self):
return self.id return self.id
@ -90,8 +87,6 @@ def load_token(request):
if token is not None: if token is not None:
username, password = token.split(":") # naive token username, password = token.split(":") # naive token
print username
print password
user_entry = User.get(username) user_entry = User.get(username)
if (user_entry is not None): if (user_entry is not None):
user = User(user_entry[0], user_entry[1]) user = User(user_entry[0], user_entry[1])
@ -144,7 +139,7 @@ def register_submit():
new_user.execute(data) new_user.execute(data)
flash('Your account has now been created') flash('Your account has now been created')
web.template.body.append(web.page.render()) web.template.body.append(web.page.set_classes('page col s10 offset-s1').render())
return make_response(footer()) return make_response(footer())
@authorize_pages.route("/oauth/<provider>/<start_oauth_login>/", methods=['GET']) @authorize_pages.route("/oauth/<provider>/<start_oauth_login>/", methods=['GET'])
@ -157,17 +152,17 @@ def oauth(provider, start_oauth_login=False):
oauth_access_type = '' oauth_access_type = ''
oauth_approval_prompt = '' oauth_approval_prompt = ''
if oauth_live is False: if oauth_live is False:
print('offline testing')
oauth_verify = False oauth_verify = False
oauth_access_type = 'offline' oauth_access_type = 'offline'
oauth_approval_prompt = "force" oauth_approval_prompt = "force"
os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = '1' os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = '1'
if start_oauth_login: if start_oauth_login:
print oauth_provider.get('redirect_uri')
oauth_session = OAuth2Session( oauth_session = OAuth2Session(
oauth_provider.get('client_id'), oauth_provider.get('client_id'),
scope=oauth_provider.get('scope'), scope=oauth_provider.get('scope'),
redirect_uri=oauth_provider.get('redirect_uri')) redirect_uri=request.url_root + oauth_provider.get('redirect_uri'))
if provider == 'facebook': if provider == 'facebook':
oauth_session = facebook_compliance_fix(oauth_session) oauth_session = facebook_compliance_fix(oauth_session)
@ -183,15 +178,15 @@ def oauth(provider, start_oauth_login=False):
session.modified = True session.modified = True
return redirect(authorization_url) return redirect(authorization_url)
if session.get('oauth_state', None) is None:
flash('Something went wrong, oauth session not started')
return redirect('/login')
# allready authorised so lets handle the callback # allready authorised so lets handle the callback
oauth_session = OAuth2Session( oauth_session = OAuth2Session(
oauth_provider.get('client_id'), oauth_provider.get('client_id'),
state=session['oauth_state'], state=session['oauth_state'],
redirect_uri=oauth_provider.get('redirect_uri')) redirect_uri=request.url_root + oauth_provider.get('redirect_uri'))
print '----------'
print oauth_provider.get('redirect_uri')
print request.url
if provider == 'facebook': if provider == 'facebook':
oauth_session = facebook_compliance_fix(oauth_session) oauth_session = facebook_compliance_fix(oauth_session)
@ -208,11 +203,8 @@ def oauth(provider, start_oauth_login=False):
response = oauth_session.get(oauth_provider.get('user_uri')) response = oauth_session.get(oauth_provider.get('user_uri'))
oauth_response = response.json() oauth_response = response.json()
print 'oauth response'
print oauth_response
oauth_id = oauth_response.get('login') or oauth_response.get('id') oauth_id = oauth_response.get('login') or oauth_response.get('id')
provider_id = oauth_lookup.get(provider) provider_id = oauth_lookup_id.get(provider)
oauth_user = site_user.fetch_oauth_login({ oauth_user = site_user.fetch_oauth_login({
'username': oauth_id or '', 'username': oauth_id or '',
'provider': provider_id 'provider': provider_id
@ -233,8 +225,6 @@ def oauth(provider, start_oauth_login=False):
flash('Your new profile has been created, and your now logged in') 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(): if current_user.get_id():
# link oauth to users account # link oauth to users account
site_user.create_oauth_login().execute({ site_user.create_oauth_login().execute({
@ -243,9 +233,6 @@ def oauth(provider, start_oauth_login=False):
'provider': provider_id}) 'provider': provider_id})
return redirect('/profile') return redirect('/profile')
print oauth_response
print '-----'
print oauth_response.get('email') or ''
# create new user from oauth information # create new user from oauth information
new_user_details = { new_user_details = {
@ -278,13 +265,11 @@ def oauth(provider, start_oauth_login=False):
@authorize_pages.route("/change-password", methods=['GET']) @authorize_pages.route("/change-password", methods=['GET'])
def change_password(code=None): def change_password(code=None):
#if we have a code this is a password reset, so try and login the user first #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({}) site_user.delete_password_reset().execute({})
if code: if code:
user_details = site_user.get_user_by_reset_code({'reset_code': code}).get() user_details = site_user.get_user_by_reset_code({'reset_code': code}).get()
print user_details
if not user_details: if not user_details:
#invalid code so pretend the page does not even exist #invalid code so pretend the page does not even exist
return abort(404) return abort(404)
@ -292,7 +277,6 @@ def change_password(code=None):
#datetime.datetime.now() + datetime.timedelta(minutes=15) #datetime.datetime.now() + datetime.timedelta(minutes=15)
has_date_expired = user_details.get('created') + datetime.timedelta(minutes=60) has_date_expired = user_details.get('created') + datetime.timedelta(minutes=60)
if has_date_expired < datetime.datetime.now(): if has_date_expired < datetime.datetime.now():
print 'date expired'
#date expired so clean up and pretend the page does not exist #date expired so clean up and pretend the page does not exist
return abort(404) return abort(404)
#challenge passed so login the user so they can change there password #challenge passed so login the user so they can change there password
@ -307,7 +291,7 @@ def change_password(code=None):
web.page.section( web.page.section(
web.change_password_box.create().render() web.change_password_box.create().render()
) )
web.template.body.append(web.page.render()) web.template.body.append(web.page.set_classes('page col s10 offset-s1').render())
return make_response(footer()) return make_response(footer())
@login_required @login_required
@ -315,16 +299,16 @@ def change_password(code=None):
def change_password_submit(code=None): def change_password_submit(code=None):
if not session.get('user_id'): if not session.get('user_id'):
abort(404) 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')): if is_weak_password(request.form.get('password'), request.form.get('password_confirm')):
print 'password not strong enough'
redirect('/login') redirect('/login')
pw_hash = generate_password_hash(request.form.get('password')) pw_hash = generate_password_hash(request.form.get('password'))
user_details = site_user.authorize({
site_user.change_password().execute({'id': user_details.get('user_id'), 'password': pw_hash}) 'id': session.get('user_id')}).get()
site_user.change_password().execute({
'id': user_details.get('user_id'),
'password': pw_hash})
web.template.create('Maidstone Hackspace - Profile') web.template.create('Maidstone Hackspace - Profile')
header('User Profile') header('User Profile')
@ -381,18 +365,6 @@ def reset_password_submit():
web.template.body.append(web.page.render()) web.template.body.append(web.page.render())
return make_response(footer()) 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']) @authorize_pages.route("/login/failure", methods=['GET'])
def login_Failure(): def login_Failure():
web.template.create('%s - Login' % site_name) web.template.create('%s - Login' % site_name)

View File

@ -1,5 +1,6 @@
import os import os
from werkzeug.security import generate_password_hash, check_password_hash
from flask import session, flash from flask import session, flash
from flask import redirect, abort from flask import redirect, abort
from flask import make_response from flask import make_response
@ -12,6 +13,7 @@ from requests_oauthlib.compliance_fixes import facebook_compliance_fix
from scaffold import web from scaffold import web
from libs.mail import sendmail from libs.mail import sendmail
from pages import header, footer from pages import header, footer
from pages.core.authorize import User
from data import site_user from data import site_user
from config.settings import * from config.settings import *
from constants import * from constants import *
@ -26,9 +28,9 @@ def login_screen():
header('Members Login') header('Members Login')
web.page.create('Member Login') web.page.create('Member Login')
web.page.section( web.page.section(
web.login_box.create().enable_oauth('google').enable_oauth('facebook').enable_oauth('github').render() web.loginBox.create().enable_oauth('google').enable_oauth('facebook').enable_oauth('github').render()
) )
web.template.body.append(web.page.render()) web.template.body.append(web.page.set_classes('page col s10 offset-s1').render())
return make_response(footer()) return make_response(footer())
@ -37,7 +39,7 @@ def login_screen_submit():
"""handle the login form submit""" """handle the login form submit"""
# try to find user by username # try to find user by username
user_details = site_user.get_by_username({ user_details = site_user.get_by_username({
'email': request.form.get('username')}).get() 'username': request.form.get('username')}).get()
# not found so lets bail to the login screen # not found so lets bail to the login screen
if not user_details: if not user_details:
@ -56,7 +58,7 @@ def login_screen_submit():
) )
flash('You have successfully logged in !') flash('You have successfully logged in !')
site_user.update_last_login().execute(user_details) site_user.update_last_login().execute({'id': user_details.get('user_id')})
# logged in but no E-Mail so lets ask the user for there email. # logged in but no E-Mail so lets ask the user for there email.
if not user_details.get('email'): if not user_details.get('email'):

View File

@ -40,7 +40,7 @@ def index():
web.form.append(name='amount', label='Donation Amount', placeholder='50.00', value='50.00') web.form.append(name='amount', label='Donation Amount', placeholder='50.00', value='50.00')
web.page.append(web.form.render()) web.page.append(web.form.render())
web.template.body.append(web.page.render()) web.template.body.append(web.page.set_classes('page col s10 offset-s1').render())
return web.render() return web.render()
@ -67,10 +67,8 @@ def populate_by_name():
web.template.body.append('Populating users') web.template.body.append('Populating users')
user_list = {} user_list = {}
#make sure we have all users in the system #make sure we have all users in the system
print '--------------'
#~ users_emails = [] #~ users_emails = []
for user in merchant.users(): for user in merchant.users():
#~ print dir(user)
user_list[user.id] = user.email user_list[user.id] = user.email
#~ users_emails.append(user.email) #~ users_emails.append(user.email)
site_user.create_basic_user().execute({ site_user.create_basic_user().execute({

View File

@ -11,3 +11,29 @@ def twitter():
web.twitter_feed.render()) web.twitter_feed.render())
web.template.body.append(web.page.render()) web.template.body.append(web.page.render())
return footer() return footer()
# normally you will abstract the html into the widgets folder so its reusable
# to keep things as simple as possibler for everyone you can bypass and render you html direct
# either to tha page web.page.section or to web.template.body
# This is the simplest way to make a page
def simple_page():
web.template.create('Maidstone Hackspace')
header('Maidstone Hackspace Homepage')
# render to a page in this way
web.page.create('Page title ')
web.page.section('<p>Page Body<p>')
web.template.body.append(web.page.render())
# render direct to the template body in this way
data = {
'first_value': 'value from where ever',
'second_value': 'value from where ever'}
web.template.body.append("""
<div>some content here</div>
<p>paragrph or any other html you want to place in here</p>
<span>{first_value}</span>
<span>{second_value}</span>
""".format(*data))
return footer()

View File

@ -19,6 +19,6 @@ def index(request_id=None):
web.container.create(web.google_groups.render()).set_classes('margin_default') web.container.create(web.google_groups.render()).set_classes('margin_default')
web.page.append(web.container.render()) web.page.append(web.container.render())
web.template.body.append(web.page.render()) web.template.body.append(web.page.set_classes('page col s10 offset-s1').render())
return footer() return footer()

View File

@ -1,19 +1,16 @@
import constants as site import constants as site
from libs.image_fetcher import save_remote_image
#~ from config.settings import * from config.settings import google_calendar_id, google_calendar_api_key, app_domain
from config.settings import google_calendar_id, google_calendar_api_key
from scaffold.readers.rss_reader import feed_reader from scaffold.readers.rss_reader import feed_reader
#~ from libs.rss_fetcher import feed_reader
from scaffold import web from scaffold import web
#~ from pages import web
from pages import header, footer from pages import header, footer
def index(): def index():
web.template.create('Maidstone Hackspace') web.template.create('Maidstone Hackspace')
header('Maidstone Hackspace Homepage') header('Maidstone Hackspace Homepage')
web.page.create('') web.page.create('').set_classes('page col s10 offset-s1')
web.page.section( web.page.section(
web.div.create( web.div.create(
web.google_calendar.create( web.google_calendar.create(
@ -53,18 +50,22 @@ def index():
# fetch the rss feeds from the various blogs for the homepage # fetch the rss feeds from the various blogs for the homepage
web.columns.create() web.columns.create()
feed = feed_reader(site.rss_feeds) feed = feed_reader(site.rss_feeds)
web.tiles.create()
for row in feed: for row in feed:
web.tiles.create() row['image'] = save_remote_image(row.get('image'), domain=app_domain + '/')
web.columns.append(
web.tiles.append( web.tiles.append(
title = row.get('title'), title = row.get('title'),
author = row.get('author'), author = row.get('author'),
link = row.get('url'), link = row.get('url'),
image = row.get('image'), image = row.get('image'),
date = row.get('date'), date = row.get('date'),
description = row.get('description')).render()) description = row.get('description')).render()
web.page.append(web.columns.render()) #~ web.columns.append(
#~ )
web.page.append(web.div.create(web.tiles.render()).set_classes('row').render())
web.template.body.append(web.page.render()) web.template.body.append(web.page.render())
return footer() return footer()

View File

@ -35,9 +35,9 @@ def index():
if item.get('status') is 1: if item.get('status') is 1:
count_members += 1 count_members += 1
web.info_box.create('Current Users') web.info_box.create('<div class="col s4 dark-blue">Current Users</div>').set_classes('row')
web.info_box.append('Members %d<br />' % count_members) web.info_box.append('<div class="col s4 dark-blue">Members %d</div>' % count_members)
web.info_box.append('Users %d<br />' % count_users) web.info_box.append('<div class="col s4 dark-blue">Users %d</div>' % count_users)
web.page.section(web.info_box.render()) web.page.section(web.info_box.render())
web.container.create(web.member_tiles.render()).set_classes('members') web.container.create(web.member_tiles.render()).set_classes('members')

View File

@ -5,9 +5,10 @@ from flask.ext.login import current_user, login_required
from constants import badge_lookup from constants import badge_lookup
from pages.core.authorize import oauth_lookup_name
from pages import web from pages import web
from pages import header, footer from pages import header, footer
from data.site_user import get_user_details, update_membership, update_membership_status, get_user_bio, create_membership from data.site_user import get_user_details, get_registered_oauth_providers, update_membership, update_membership_status, get_user_bio, create_membership
from data.profile import update_description, create_description, fetch_users from data.profile import update_description, create_description, fetch_users
from data import badges from data import badges
from data import members from data import members
@ -23,32 +24,24 @@ profile_pages = Blueprint('profile_pages', __name__, template_folder='templates'
def index(): def index():
web.template.create('Maidstone Hackspace - User profile') web.template.create('Maidstone Hackspace - User profile')
header('User Profile', url='/profile') header('User Profile', url='/profile')
print current_user
user = get_user_details({'id': current_user.get_id()}).get() user = get_user_details({'id': current_user.get_id()}).get()
user_oauth_providers = [provider for provider in get_registered_oauth_providers({'user_id': current_user.get_id()})]
name = '%s %s' % (user.get('first_name', '').capitalize(), user.get('last_name', '').capitalize()) name = '%s %s' % (
user.get('first_name', '').capitalize(),
user.get('last_name', '').capitalize())
web.page.create('%s - Profile' % name) web.page.create('%s - Profile' % name)
web.columns.create()
web.paragraph.create( web.paragraph.create(
web.images.create(user.get('profile_image', '/static/images/hackspace.png'), name).add_attributes('width', '200').render() web.images.create(user.get('profile_image', '/static/images/hackspace.png'), name).add_attributes('width', '200').render()
) )
web.paragraph.add(name) web.paragraph.add(name)
web.paragraph.add('%s' % (user.get('email'))) web.paragraph.add('%s' % (user.get('email', '') if user.get('email', '') else ''))
web.paragraph.add('Last Login %s' % (user.get('last_login', ''))) web.paragraph.add('Last Login %s' % (user.get('last_login', '')))
web.paragraph.add('Member since %s' % (user.get('created', ''))) web.paragraph.add('Member since %s' % (user.get('created', '')))
web.paragraph.add('Description %s' % (user.get('description', ''))) web.paragraph.add('Description %s' % (user.get('description', '')))
web.paragraph.add('Skills %s' % (user.get('skills', ''))) web.paragraph.add('Skills %s' % (user.get('skills', '')))
web.columns.append(web.paragraph.render()) web.div.create(web.paragraph.render())
# membership form
web.columns.append(
web.member_card.create(
reference=str(user.get('user_id')).zfill(5),
name=name,
active=user.get('status')==1
).render()
)
web.paragraph.create( web.paragraph.create(
web.link.create( web.link.create(
@ -64,10 +57,32 @@ def index():
'/login' '/login'
).render()) ).render())
web.table.create('', ('Login providers', 'Last login'))
for provider in user_oauth_providers:
web.table.append((
str(oauth_lookup_name.get(int(provider.get('provider')))),
provider.get('last_login') if provider.get('last_login') else 'Unknown'))
web.paragraph.append(
web.table.render())
web.page.section(
web.div.append(
web.paragraph.render()
).set_classes('col s6').render()
)
# membership form
web.page.append(
web.div.create(
web.member_card.create(
reference=str(user.get('user_id')).zfill(5),
name=name,
active=user.get('status')==1
).render()
).set_classes('col s6').render()
)
web.columns.append(web.paragraph.render())
web.page.section(web.columns.render())
web.template.body.append(web.page.render()) web.template.body.append(web.page.render())
web.template.body.append(web.popup.create('').render()) web.template.body.append(web.popup.create('').render())
@ -88,21 +103,19 @@ def setup():
provider = payment(provider='paypal', style='payment') provider = payment(provider='paypal', style='payment')
for item in provider.fetch_subscriptions(): for item in provider.fetch_subscriptions():
print item print(item)
print user_lookup
merchant = gocardless.client.merchant() merchant = gocardless.client.merchant()
#https://jsfiddle.net/api/post/library/pure/ #https://jsfiddle.net/api/post/library/pure/
for paying_member in merchant.subscriptions(): for paying_member in merchant.subscriptions():
print dir(paying_member) print(dir(paying_member))
print paying_member.user() print(paying_member.user())
print paying_member.amount print(paying_member.amount)
user=paying_member.user() user=paying_member.user()
print '---------------' print(user.email)
print user.email
user_id = user_lookup.get(user.email) user_id = user_lookup.get(user.email)
print user_id print(user_id)
update_membership_status().execute({'user_id': user_id, 'status': '1'}) update_membership_status().execute({'user_id': user_id, 'status': '1'})
create_membership().execute({'user_id': user_id, 'status': '1', 'join_date': paying_member.created_at, 'amount': paying_member.amount, 'subscription_id': paying_member.id}) create_membership().execute({'user_id': user_id, 'status': '1', 'join_date': paying_member.created_at, 'amount': paying_member.amount, 'subscription_id': paying_member.id})
@ -138,8 +151,8 @@ def cancel_membership():
user_code = str(user.get('user_id')).zfill(5) user_code = str(user.get('user_id')).zfill(5)
subscription = members.fetch_member_subscription({'user_id': current_user.get_id()}).get() subscription = members.fetch_member_subscription({'user_id': current_user.get_id()}).get()
print subscription.get('provider_id') print(subscription.get('provider_id'))
print subscription.get('subscription_reference') print(subscription.get('subscription_reference'))
provider = payment(provider='paypal', style='payment') provider = payment(provider='paypal', style='payment')
@ -194,10 +207,10 @@ def membership_signup(provider):
def update_profiles(): def update_profiles():
"""this is used to sync up older accounts""" """this is used to sync up older accounts"""
for user in get_users(): for user in get_users():
print user print(user)
for payment in get_users(): for payment in get_users():
print user print(user)
return web.form.render() return web.form.render()
@ -205,9 +218,8 @@ def update_profiles():
@login_required @login_required
def edit_profile(): def edit_profile():
user_details = get_user_details({'id': current_user.get_id()}).get() or {} user_details = get_user_details({'id': current_user.get_id()}).get() or {}
print user_details
if not user_details: if not user_details:
print 'create' print('create')
create_description().execute({'user_id': current_user.get_id()}) create_description().execute({'user_id': current_user.get_id()})
web.form.create('Update your details', '/profile/update') web.form.create('Update your details', '/profile/update')
web.form.append(name='description', label='Description', placeholder='This is me i am great', value=user_details.get('description') or '') web.form.append(name='description', label='Description', placeholder='This is me i am great', value=user_details.get('description') or '')

0
website/setup.py Executable file → Normal file
View File

275
website/static/css/default.css Normal file → Executable file

File diff suppressed because one or more lines are too long

0
website/static/css/materialize.css vendored Normal file → Executable file
View File

0
website/static/css/sprite-action-white.css Normal file → Executable file
View File

0
website/static/css/sprite-content-white.css Normal file → Executable file
View File

0
website/static/css/sprite-navigation-white.css Normal file → Executable file
View File

0
website/html/404.htm → website/static/html/404.htm Normal file → Executable file
View File

123
website/static/html/blog.htm Executable file

File diff suppressed because one or more lines are too long

76
website/html/chat.htm → website/static/html/chat.htm Normal file → Executable file
View File

@ -2,45 +2,47 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" xmlns:sa="/"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" xmlns:sa="/">
<head> <head>
<link rel="stylesheet" id="navigationCss" href="/static/css/materialize.css" media="" type="text/css" />
<link rel="stylesheet" id="navigationCss" href="/static/css/default.css" media="" type="text/css" /> <link rel="stylesheet" id="navigationCss" href="/static/css/default.css" media="" type="text/css" />
<link rel="stylesheet" id="navigationCss" href="/static/js/jquery-ui/themes/base/jquery-ui.css" media="" type="text/css" /> <link rel="stylesheet" id="navigationCss" href="/static/js/jquery-ui/themes/base/jquery-ui.css" media="" type="text/css" />
<link rel="stylesheet" id="navigationCss" href="/static/css/sprite-navigation-white.css" media="" type="text/css" /> <link rel="stylesheet" id="navigationCss" href="/static/css/sprite-navigation-white.css" media="" type="text/css" />
<link rel="stylesheet" id="navigationCss" href="/static/css/sprite-action-white.css" media="" type="text/css" /> <link rel="stylesheet" id="navigationCss" href="/static/css/sprite-action-white.css" media="" type="text/css" />
<link rel="stylesheet" id="navigationCss" href="/static/css/sprite-content-white.css" media="" type="text/css" /> <link rel="stylesheet" id="navigationCss" href="/static/css/sprite-content-white.css" media="" type="text/css" />
<script type="text/javascript" src="/static/js/jquery-2.1.4.min.js"></script> <script type="text/javascript" src="/static/js/jquery-2.2.3.min.js"></script>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.0/angular.js"></script> <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.0/angular.js"></script>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.0/angular-animate.js"></script> <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.0/angular-animate.js"></script>
<script type="text/javascript" src="/static/js/materialize.js"></script>
<script type="text/javascript" src="/static/js/default.js"></script> <script type="text/javascript" src="/static/js/default.js"></script>
<link rel="icon" type="image/png" href="/static/images/favicon.png"> <link rel="icon" type="image/png" href="/static/images/favicon.png">
<title>Maidstone Hackspace - Chat room</title> <title>Maidstone Hackspace - Chat room</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head> </head>
<body> <body>
<div id="headerstrip"><nav class="navstrip"><div class="left"><a id="mini_logo" href="/login"><img src="//127.0.0.1:5000/static/images/hackspace.png" class="mini-logo"></a><span class="mini-logo-text">Maidstone Hackspace</span></div><div class="social"><div class="btn"><a href="https://twitter.com/share" class="twitter-share-button" data-via="http://maidstone-hackspace.org.uk/">Tweet</a></div><div class="btn"><div class="fb-share-button" data-href="http://maidstone-hackspace.org.uk/" data-layout="button_count"></div></div><div class="btn"><script type="IN/Share" data-url="http://maidstone-hackspace.org.uk/" data-counter="right"></script></div><div class="btn"><div size="standard" class="g-plusone" data-href="http://maidstone-hackspace.org.uk/" data-size="medium" data-annotation="bubble" count="true"></div></div></div></nav></div> <div id="headerstrip" class="row"><nav class="navstrip"><div class="navstripleft col s6"><a id="mini_logo" href="/login"><img src="//http://127.0.0.1/static/images/hackspace.png" class="mini-logo"></a><span class="mini-logo-text">Maidstone Hackspace</span></div><div class="col s6 offset-s8 social"><div class="socbtn"><a href="https://twitter.com/share" class="twitter-share-button" data-via="https://maidstone-hackspace.org.uk/" data-hashtags="mhackspace">Tweet</a></div><div class="socbtn"><div class="fb-share-button" data-href="https://maidstone-hackspace.org.uk/" data-layout="button_count"></div></div><div class="socbtn"><script type="IN/Share" data-url="https://maidstone-hackspace.org.uk/" data-counter="right"></script></div><div class="socbtn"><div size="standard" class="g-plusone" data-href="https://maidstone-hackspace.org.uk/" data-size="medium" data-annotation="bubble" count="true"></div></div></div></nav></div>
<nav id="leftNav" class="menu" > <div id="menubar" class="row" ><nav class="col s10 offset-s1" >
<ul> <ul>
<li class="active mi0"><a href="/" >Home</a></li> <li class="active mi0"><a href="/" >Home</a></li>
<li class="mi1"><a href="/chat" >Chat</a></li> <li class="mi1"><a href="/chat" >Chat</a></li>
<li class="mi2"><a href="/donate" >Donate</a></li> <li class="mi2"><a href="/donate" >Donate</a></li>
<li class="mi3"><a href="#mailing-list-signup" >Contact</a></li> <li class="mi3"><a href="/contact-us" >Contact</a></li>
<li class="mi4"><a href="/login" >login</a></li> <li class="mi4"><a href="/mailing-list" >Group</a></li>
<li class="mi5"><a href="/login" >Login</a></li>
</ul> </ul>
<div style="clear:both;"></div> <div style="clear:both;"></div>
</nav> </nav></div>
<div class="page" > <div class="row"><div class="page col s10 offset-s1" >
<header class="pageHeader"> <header class="pageHeader">
<p>Pop in and say hi, please be patient users tend to idle, but will likely respond given a chance.</p></header> <p>Pop in and say hi, please be patient users tend to idle, but will likely respond given a chance.</p></header>
<section class="pageSection"> <section class="pageSection col s12">
<div class="social-chat"><div class="contain"><iframe src="https://webchat.freenode.net?channels=%23maidstone-hackspace&uio=MTE9MjU207"></iframe></div></div></section> <div class="social-chat"><div class="contain"><iframe src="https://webchat.freenode.net?channels=%23maidstone-hackspace&uio=MTE9MjU207"></iframe></div></div></section>
<footer class="pageFooter"> <footer class="pageFooter col s12">
</footer> </footer>
</div> </div></div>
<div id="footer"><div id="footertop"></div><div id="footerbottom"><div class="container"><div class="copyright">&copy;2016 Maidstone Hackspace</div><div mailing-list-signup class="google-groups-signup"><h3>Signup and make yourself known</h3><form class="block" name="signup" method="get" action="https://groups.google.com/group/maidstone-hackspace/boxsubscribe"><label for="groups-email">Email Address</label><input id="groups-email" name="email" class="required"/><button type="submit" />Subscribe</button><a href="http://groups.google.com/group/maidstone-hackspace">View Group</a></form><div style="clear:both;"></div><div></div></div><div>
<div id="footer"><div id="footertop"></div><div id="footerbottom"><div class="container"><div class="copyright">&copy;2016 Maidstone Hackspace</div><div mailing-list-signup class="google-groups-signup"><h3>Signup and make yourself known</h3><form class="block" name="signup" method="get" action="http://groups.google.com/group/maidstone-hackspace/boxsubscribe"><label for="groups-email">Email Address</label><input id="groups-email" name="email" class="required"/><button type="submit" />Subscribe</button><a href="http://groups.google.com/group/maidstone-hackspace">Browse Archives</a></form><div style="clear:both;"></div><div></div></div><div> <!--google analytics-->
<script type="text/javascript" src="https://apis.google.com/js/plusone.js"></script> <script type="text/javascript" src="https://apis.google.com/js/plusone.js"></script>
<script><!--//--><![CDATA[//><!--!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+'http://platform.twitter.com/widgets.js';fjs.parentNode.insertBefore(js,fjs);}}(document, 'script', 'twitter-wjs'); <script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+'://platform.twitter.com/widgets.js';fjs.parentNode.insertBefore(js,fjs);}}(document, 'script', 'twitter-wjs');</script>
//]]></script>
<script src="//platform.linkedin.com/in.js" type="text/javascript"> lang: en_US</script> <script src="//platform.linkedin.com/in.js" type="text/javascript"> lang: en_US</script>
@ -48,29 +50,6 @@
//facebook share //facebook share
(function(d, s, id) {var js, fjs = d.getElementsByTagName(s)[0];if (d.getElementById(id)) return;js = d.createElement(s); js.id = id;js.src = "//connect.facebook.net/en_GB/sdk.js#xfbml=1&version=v2.3";fjs.parentNode.insertBefore(js, fjs);}(document, 'script', 'facebook-jssdk')); (function(d, s, id) {var js, fjs = d.getElementsByTagName(s)[0];if (d.getElementById(id)) return;js = d.createElement(s); js.id = id;js.src = "//connect.facebook.net/en_GB/sdk.js#xfbml=1&version=v2.3";fjs.parentNode.insertBefore(js, fjs);}(document, 'script', 'facebook-jssdk'));
//]]></script><script type="text/javascript" ><!--//--><![CDATA[//><!-- //]]></script><script type="text/javascript" ><!--//--><![CDATA[//><!--
$(document).ready(function(){
$('#mini_logo').on("click", function(e){
e.preventDefault();
$('#member_navigation').toggle();
});
});
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-63373181-1', 'maidstone-hackspace.org.uk');
ga('send', 'pageview');
var app = angular.module('myApp', ['ngAnimate']); var app = angular.module('myApp', ['ngAnimate']);
app.controller('sliderController', function($scope, $interval) { app.controller('sliderController', function($scope, $interval) {
@ -113,6 +92,29 @@ app.controller('sliderController', function($scope, $interval) {
}); });
$(document).ready(function(){
$('#mini_logo').on("click", function(e){
e.preventDefault();
$('#member_navigation').toggle();
});
});
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-63373181-1', 'maidstone-hackspace.org.uk');
ga('send', 'pageview');
//]]> //]]>
</script> </script>

View File

@ -2,49 +2,52 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" xmlns:sa="/"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" xmlns:sa="/">
<head> <head>
<link rel="stylesheet" id="navigationCss" href="/static/css/materialize.css" media="" type="text/css" />
<link rel="stylesheet" id="navigationCss" href="/static/css/default.css" media="" type="text/css" /> <link rel="stylesheet" id="navigationCss" href="/static/css/default.css" media="" type="text/css" />
<link rel="stylesheet" id="navigationCss" href="/static/js/jquery-ui/themes/base/jquery-ui.css" media="" type="text/css" /> <link rel="stylesheet" id="navigationCss" href="/static/js/jquery-ui/themes/base/jquery-ui.css" media="" type="text/css" />
<link rel="stylesheet" id="navigationCss" href="/static/css/sprite-navigation-white.css" media="" type="text/css" /> <link rel="stylesheet" id="navigationCss" href="/static/css/sprite-navigation-white.css" media="" type="text/css" />
<link rel="stylesheet" id="navigationCss" href="/static/css/sprite-action-white.css" media="" type="text/css" /> <link rel="stylesheet" id="navigationCss" href="/static/css/sprite-action-white.css" media="" type="text/css" />
<link rel="stylesheet" id="navigationCss" href="/static/css/sprite-content-white.css" media="" type="text/css" /> <link rel="stylesheet" id="navigationCss" href="/static/css/sprite-content-white.css" media="" type="text/css" />
<script type="text/javascript" src="/static/js/jquery-2.1.4.min.js"></script> <script type="text/javascript" src="/static/js/jquery-2.2.3.min.js"></script>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.0/angular.js"></script> <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.0/angular.js"></script>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.0/angular-animate.js"></script> <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.0/angular-animate.js"></script>
<script type="text/javascript" src="/static/js/materialize.js"></script>
<script type="text/javascript" src="/static/js/default.js"></script> <script type="text/javascript" src="/static/js/default.js"></script>
<link rel="icon" type="image/png" href="/static/images/favicon.png"> <link rel="icon" type="image/png" href="/static/images/favicon.png">
<title>Maidstone Hackspace - Screw sorting competition</title> <title>Maidstone Hackspace - Screw sorting competition</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head> </head>
<body> <body>
<div id="headerstrip"><nav class="navstrip"><div class="left"><a id="mini_logo" href="/login"><img src="//127.0.0.1:5000/static/images/hackspace.png" class="mini-logo"></a><span class="mini-logo-text">Maidstone Hackspace</span></div><div class="social"><div class="btn"><a href="https://twitter.com/share" class="twitter-share-button" data-via="http://maidstone-hackspace.org.uk/">Tweet</a></div><div class="btn"><div class="fb-share-button" data-href="http://maidstone-hackspace.org.uk/" data-layout="button_count"></div></div><div class="btn"><script type="IN/Share" data-url="http://maidstone-hackspace.org.uk/" data-counter="right"></script></div><div class="btn"><div size="standard" class="g-plusone" data-href="http://maidstone-hackspace.org.uk/" data-size="medium" data-annotation="bubble" count="true"></div></div></div></nav></div> <div id="headerstrip" class="row"><nav class="navstrip"><div class="navstripleft col s6"><a id="mini_logo" href="/login"><img src="//http://127.0.0.1/static/images/hackspace.png" class="mini-logo"></a><span class="mini-logo-text">Maidstone Hackspace</span></div><div class="col s6 offset-s8 social"><div class="socbtn"><a href="https://twitter.com/share" class="twitter-share-button" data-via="https://maidstone-hackspace.org.uk/" data-hashtags="mhackspace">Tweet</a></div><div class="socbtn"><div class="fb-share-button" data-href="https://maidstone-hackspace.org.uk/" data-layout="button_count"></div></div><div class="socbtn"><script type="IN/Share" data-url="https://maidstone-hackspace.org.uk/" data-counter="right"></script></div><div class="socbtn"><div size="standard" class="g-plusone" data-href="https://maidstone-hackspace.org.uk/" data-size="medium" data-annotation="bubble" count="true"></div></div></div></nav></div>
<nav id="leftNav" class="menu" > <div id="menubar" class="row" ><nav class="col s10 offset-s1" >
<ul> <ul>
<li class="active mi0"><a href="/" >Home</a></li> <li class="active mi0"><a href="/" >Home</a></li>
<li class="mi1"><a href="/chat" >Chat</a></li> <li class="mi1"><a href="/chat" >Chat</a></li>
<li class="mi2"><a href="/donate" >Donate</a></li> <li class="mi2"><a href="/donate" >Donate</a></li>
<li class="mi3"><a href="#mailing-list-signup" >Contact</a></li> <li class="mi3"><a href="/contact-us" >Contact</a></li>
<li class="mi4"><a href="/login" >login</a></li> <li class="mi4"><a href="/mailing-list" >Group</a></li>
<li class="mi5"><a href="/login" >Login</a></li>
</ul> </ul>
<div style="clear:both;"></div> <div style="clear:both;"></div>
</nav> </nav></div>
<div class="page" > <div class="row"><div class="page col s10 offset-s1" >
<header class="pageHeader"> <header class="pageHeader">
<img src="/static/images/competitions/screw_sorting_competition_banner.jpg" alt="Screw sorting competition banner" align="middle" style="margin:auto;display:block;width:500px;" /> <img src="/static/images/competitions/screw_sorting_competition_banner.jpg" alt="Screw sorting competition banner" align="middle" style="margin:auto;display:block;width:500px;" />
</header> </header>
<section class="pageSection"> <section class="pageSection col s12">
<p>Welcome to the first Maidstone Hackspace challenge! A great opportunity for all to show off their creative flair and to join our community of makers, tinkerers, artists and more.</p><h2>The Challenge:</h2></section> <p>Welcome to the first Maidstone Hackspace challenge! A great opportunity for all to show off their creative flair and to join our community of makers, tinkerers, artists and more.</p><h2>The Challenge:</h2></section>
<section class="pageSection"> <section class="pageSection col s12">
<p>Design a device which can sort a jar of screws by size, the winning entry will be built by Maidstone Hackspace.</p></section> <p>Design a device which can sort a jar of screws by size, the winning entry will be built by Maidstone Hackspace.</p></section>
<section class="pageSection"> <section class="pageSection col s12">
<p>Concepts can be designed in any software as long as the finished product is viewable without any specialist software e.g.JPG images. If you prefer to paint or draw we accept that too.</p></section> <p>Concepts can be designed in any software as long as the finished product is viewable without any specialist software e.g.JPG images. If you prefer to paint or draw we accept that too.</p></section>
<section class="pageSection"> <section class="pageSection col s12">
<p>Submissions must be via our mailing list. The closing date is the 31st of July, submissions after this date will not be entered.</p></section> <p>Submissions must be via our mailing list. The closing date is the 31st of July, submissions after this date will not be entered.</p></section>
<section class="pageSection"> <section class="pageSection col s12">
<p><a title="Submit your image here." href="https://groups.google.com/forum/#!forum/maidstone-hackspace" >Submit your image here.</a> <p><a title="Submit your image here." href="https://groups.google.com/forum/#!forum/maidstone-hackspace" >Submit your image here.</a>
</p></section> </p></section>
<section class="pageSection"> <section class="pageSection col s12">
<h2>Win a UNO Basic Starter Kit</h2></section> <h2>Win a UNO Basic Starter Kit</h2></section>
<section class="pageSection"> <section class="pageSection col s12">
<p><img src="http://imgapp.banggood.com/thumb/large/2014/xiemeijuan/03/SKU208787/SKU208787a.jpg" alt="Arduino starter kit" align="middle" style="margin:auto;display:block;width:500px;" /> <p><img src="http://imgapp.banggood.com/thumb/large/2014/xiemeijuan/03/SKU208787/SKU208787a.jpg" alt="Arduino starter kit" align="middle" style="margin:auto;display:block;width:500px;" />
This kit comes with an arduino board and various sensors and components, list below of every thing in the kit.<ul class="bullet-list" > This kit comes with an arduino board and various sensors and components, list below of every thing in the kit.<ul class="bullet-list" >
<li>1 x Arduino UNO R3 development board</li> <li>1 x Arduino UNO R3 development board</li>
@ -85,15 +88,14 @@ This kit comes with an arduino board and various sensors and components, list be
<li>1 x 9V battery</li> <li>1 x 9V battery</li>
<li>1 x 2.54mm 40pin pin header</li> <li>1 x 2.54mm 40pin pin header</li>
</ul></p></section> </ul></p></section>
<footer class="pageFooter"> <footer class="pageFooter col s12">
</footer> </footer>
</div> </div></div>
<div id="footer"><div id="footertop"></div><div id="footerbottom"><div class="container"><div class="copyright">&copy;2016 Maidstone Hackspace</div><div mailing-list-signup class="google-groups-signup"><h3>Signup and make yourself known</h3><form class="block" name="signup" method="get" action="https://groups.google.com/group/maidstone-hackspace/boxsubscribe"><label for="groups-email">Email Address</label><input id="groups-email" name="email" class="required"/><button type="submit" />Subscribe</button><a href="http://groups.google.com/group/maidstone-hackspace">View Group</a></form><div style="clear:both;"></div><div></div></div><div>
<div id="footer"><div id="footertop"></div><div id="footerbottom"><div class="container"><div class="copyright">&copy;2016 Maidstone Hackspace</div><div mailing-list-signup class="google-groups-signup"><h3>Signup and make yourself known</h3><form class="block" name="signup" method="get" action="http://groups.google.com/group/maidstone-hackspace/boxsubscribe"><label for="groups-email">Email Address</label><input id="groups-email" name="email" class="required"/><button type="submit" />Subscribe</button><a href="http://groups.google.com/group/maidstone-hackspace">Browse Archives</a></form><div style="clear:both;"></div><div></div></div><div> <!--google analytics-->
<script type="text/javascript" src="https://apis.google.com/js/plusone.js"></script> <script type="text/javascript" src="https://apis.google.com/js/plusone.js"></script>
<script><!--//--><![CDATA[//><!--!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+'http://platform.twitter.com/widgets.js';fjs.parentNode.insertBefore(js,fjs);}}(document, 'script', 'twitter-wjs'); <script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+'://platform.twitter.com/widgets.js';fjs.parentNode.insertBefore(js,fjs);}}(document, 'script', 'twitter-wjs');</script>
//]]></script>
<script src="//platform.linkedin.com/in.js" type="text/javascript"> lang: en_US</script> <script src="//platform.linkedin.com/in.js" type="text/javascript"> lang: en_US</script>
@ -101,29 +103,6 @@ This kit comes with an arduino board and various sensors and components, list be
//facebook share //facebook share
(function(d, s, id) {var js, fjs = d.getElementsByTagName(s)[0];if (d.getElementById(id)) return;js = d.createElement(s); js.id = id;js.src = "//connect.facebook.net/en_GB/sdk.js#xfbml=1&version=v2.3";fjs.parentNode.insertBefore(js, fjs);}(document, 'script', 'facebook-jssdk')); (function(d, s, id) {var js, fjs = d.getElementsByTagName(s)[0];if (d.getElementById(id)) return;js = d.createElement(s); js.id = id;js.src = "//connect.facebook.net/en_GB/sdk.js#xfbml=1&version=v2.3";fjs.parentNode.insertBefore(js, fjs);}(document, 'script', 'facebook-jssdk'));
//]]></script><script type="text/javascript" ><!--//--><![CDATA[//><!-- //]]></script><script type="text/javascript" ><!--//--><![CDATA[//><!--
$(document).ready(function(){
$('#mini_logo').on("click", function(e){
e.preventDefault();
$('#member_navigation').toggle();
});
});
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-63373181-1', 'maidstone-hackspace.org.uk');
ga('send', 'pageview');
var app = angular.module('myApp', ['ngAnimate']); var app = angular.module('myApp', ['ngAnimate']);
app.controller('sliderController', function($scope, $interval) { app.controller('sliderController', function($scope, $interval) {
@ -166,6 +145,29 @@ app.controller('sliderController', function($scope, $interval) {
}); });
$(document).ready(function(){
$('#mini_logo').on("click", function(e){
e.preventDefault();
$('#member_navigation').toggle();
});
});
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-63373181-1', 'maidstone-hackspace.org.uk');
ga('send', 'pageview');
//]]> //]]>
</script> </script>

View File

@ -2,48 +2,48 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" xmlns:sa="/"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" xmlns:sa="/">
<head> <head>
<link rel="stylesheet" id="navigationCss" href="/static/css/materialize.css" media="" type="text/css" />
<link rel="stylesheet" id="navigationCss" href="/static/css/default.css" media="" type="text/css" /> <link rel="stylesheet" id="navigationCss" href="/static/css/default.css" media="" type="text/css" />
<link rel="stylesheet" id="navigationCss" href="/static/js/jquery-ui/themes/base/jquery-ui.css" media="" type="text/css" /> <link rel="stylesheet" id="navigationCss" href="/static/js/jquery-ui/themes/base/jquery-ui.css" media="" type="text/css" />
<link rel="stylesheet" id="navigationCss" href="/static/css/sprite-navigation-white.css" media="" type="text/css" /> <link rel="stylesheet" id="navigationCss" href="/static/css/sprite-navigation-white.css" media="" type="text/css" />
<link rel="stylesheet" id="navigationCss" href="/static/css/sprite-action-white.css" media="" type="text/css" /> <link rel="stylesheet" id="navigationCss" href="/static/css/sprite-action-white.css" media="" type="text/css" />
<link rel="stylesheet" id="navigationCss" href="/static/css/sprite-content-white.css" media="" type="text/css" /> <link rel="stylesheet" id="navigationCss" href="/static/css/sprite-content-white.css" media="" type="text/css" />
<script type="text/javascript" src="/static/js/jquery-2.1.4.min.js"></script> <script type="text/javascript" src="/static/js/jquery-2.2.3.min.js"></script>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.0/angular.js"></script> <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.0/angular.js"></script>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.0/angular-animate.js"></script> <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.0/angular-animate.js"></script>
<script type="text/javascript" src="/static/js/materialize.js"></script>
<script type="text/javascript" src="/static/js/default.js"></script> <script type="text/javascript" src="/static/js/default.js"></script>
<link rel="icon" type="image/png" href="/static/images/favicon.png"> <link rel="icon" type="image/png" href="/static/images/favicon.png">
<title>Maidstone Hackspace</title> <title>Maidstone Hackspace</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head> </head>
<body> <body>
<div id="headerstrip"><nav class="navstrip"><div class="left"><a id="mini_logo" href="/login"><img src="//127.0.0.1:5000/static/images/hackspace.png" class="mini-logo"></a><span class="mini-logo-text">Maidstone Hackspace</span></div><div class="social"><div class="btn"><a href="https://twitter.com/share" class="twitter-share-button" data-via="http://maidstone-hackspace.org.uk/">Tweet</a></div><div class="btn"><div class="fb-share-button" data-href="http://maidstone-hackspace.org.uk/" data-layout="button_count"></div></div><div class="btn"><script type="IN/Share" data-url="http://maidstone-hackspace.org.uk/" data-counter="right"></script></div><div class="btn"><div size="standard" class="g-plusone" data-href="http://maidstone-hackspace.org.uk/" data-size="medium" data-annotation="bubble" count="true"></div></div></div></nav></div> <div id="headerstrip" class="row"><nav class="navstrip"><div class="navstripleft col s6"><a id="mini_logo" href="/login"><img src="//http://127.0.0.1/static/images/hackspace.png" class="mini-logo"></a><span class="mini-logo-text">Maidstone Hackspace</span></div><div class="col s6 offset-s8 social"><div class="socbtn"><a href="https://twitter.com/share" class="twitter-share-button" data-via="https://maidstone-hackspace.org.uk/" data-hashtags="mhackspace">Tweet</a></div><div class="socbtn"><div class="fb-share-button" data-href="https://maidstone-hackspace.org.uk/" data-layout="button_count"></div></div><div class="socbtn"><script type="IN/Share" data-url="https://maidstone-hackspace.org.uk/" data-counter="right"></script></div><div class="socbtn"><div size="standard" class="g-plusone" data-href="https://maidstone-hackspace.org.uk/" data-size="medium" data-annotation="bubble" count="true"></div></div></div></nav></div>
<nav id="leftNav" class="menu" > <div id="menubar" class="row" ><nav class="col s10 offset-s1" >
<ul> <ul>
<li class="active mi0"><a href="/" >Home</a></li> <li class="active mi0"><a href="/" >Home</a></li>
<li class="mi1"><a href="/chat" >Chat</a></li> <li class="mi1"><a href="/chat" >Chat</a></li>
<li class="mi2"><a href="/donate" >Donate</a></li> <li class="mi2"><a href="/donate" >Donate</a></li>
<li class="mi3"><a href="#mailing-list-signup" >Contact</a></li> <li class="mi3"><a href="/contact-us" >Contact</a></li>
<li class="mi4"><a href="/login" >login</a></li> <li class="mi4"><a href="/mailing-list" >Group</a></li>
<li class="mi5"><a href="/login" >Login</a></li>
</ul> </ul>
<div style="clear:both;"></div> <div style="clear:both;"></div>
</nav> </nav></div>
<div class="page" > <div class="row"><div class="page col s10 offset-s1" >
<header class="pageHeader"> <header class="pageHeader">
Make a donation</header> Make a donation</header>
<section class="pageSection"> <section class="pageSection col s12">
<p>If you would like to donate to the space please type an amount and use the reference code for what ever your donating for, for example use #lair to donate to getting a space. <p>If you would like to donate to the space please type an amount and use the reference code for what ever your donating for, for example use #lair to donate to getting a space.
We may run pledges in the future for equipment in which case use the reference for the equipment your pledging towards.</p></section> We may run pledges in the future for equipment in which case use the reference for the equipment your pledging towards.</p><form action="/donate/submit" method="post" ><fieldset><legend>Donate to Maidstone Hackspace</legend><p><label for="provider">GoCardless<input type="radio" name="provider" placeholder="gocardless" value="gocardless" checked="checked"></label></p><p><label for="provider">PayPal<input type="radio" name="provider" placeholder="" value="paypal"></label></p><p><label for="reference">Reference<select name="reference"><option value="#lair" selected="selected">#lair</option></select></label></p><p><label for="amount">Donation Amount<input type="text" name="amount" placeholder="50.00" value="50.00"></label></p><p class="bottom full_width"><button type="submit">submit</button></p></fieldset></form></section>
<section class="pageSection"> <footer class="pageFooter col s12">
<p>Currently raised &pound;5.00 towards #lair target is &pound;1000.00.</p><form action="/donate/submit" method="post" ><fieldset><legend>Donate to Maidstone Hackspace</legend><p><label for="reference">Reference<select name="reference"><option value="#lair" selected="selected">#lair</option></select></label></p><p><label for="amount">Donation Amount<input type="text" name="amount" placeholder="50.00" value="50.00"></label></p><p class="bottom full_width"><button type="submit">submit</button></p></fieldset></form></section>
<footer class="pageFooter">
</footer> </footer>
</div> </div></div>
<div id="footer"><div id="footertop"></div><div id="footerbottom"><div class="container"><div class="copyright">&copy;2016 Maidstone Hackspace</div><div mailing-list-signup class="google-groups-signup"><h3>Signup and make yourself known</h3><form class="block" name="signup" method="get" action="https://groups.google.com/group/maidstone-hackspace/boxsubscribe"><label for="groups-email">Email Address</label><input id="groups-email" name="email" class="required"/><button type="submit" />Subscribe</button><a href="http://groups.google.com/group/maidstone-hackspace">View Group</a></form><div style="clear:both;"></div><div></div></div><div>
<div id="footer"><div id="footertop"></div><div id="footerbottom"><div class="container"><div class="copyright">&copy;2016 Maidstone Hackspace</div><div mailing-list-signup class="google-groups-signup"><h3>Signup and make yourself known</h3><form class="block" name="signup" method="get" action="http://groups.google.com/group/maidstone-hackspace/boxsubscribe"><label for="groups-email">Email Address</label><input id="groups-email" name="email" class="required"/><button type="submit" />Subscribe</button><a href="http://groups.google.com/group/maidstone-hackspace">Browse Archives</a></form><div style="clear:both;"></div><div></div></div><div> <!--google analytics-->
<script type="text/javascript" src="https://apis.google.com/js/plusone.js"></script> <script type="text/javascript" src="https://apis.google.com/js/plusone.js"></script>
<script><!--//--><![CDATA[//><!--!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+'http://platform.twitter.com/widgets.js';fjs.parentNode.insertBefore(js,fjs);}}(document, 'script', 'twitter-wjs'); <script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+'://platform.twitter.com/widgets.js';fjs.parentNode.insertBefore(js,fjs);}}(document, 'script', 'twitter-wjs');</script>
//]]></script>
<script src="//platform.linkedin.com/in.js" type="text/javascript"> lang: en_US</script> <script src="//platform.linkedin.com/in.js" type="text/javascript"> lang: en_US</script>
@ -51,29 +51,6 @@
//facebook share //facebook share
(function(d, s, id) {var js, fjs = d.getElementsByTagName(s)[0];if (d.getElementById(id)) return;js = d.createElement(s); js.id = id;js.src = "//connect.facebook.net/en_GB/sdk.js#xfbml=1&version=v2.3";fjs.parentNode.insertBefore(js, fjs);}(document, 'script', 'facebook-jssdk')); (function(d, s, id) {var js, fjs = d.getElementsByTagName(s)[0];if (d.getElementById(id)) return;js = d.createElement(s); js.id = id;js.src = "//connect.facebook.net/en_GB/sdk.js#xfbml=1&version=v2.3";fjs.parentNode.insertBefore(js, fjs);}(document, 'script', 'facebook-jssdk'));
//]]></script><script type="text/javascript" ><!--//--><![CDATA[//><!-- //]]></script><script type="text/javascript" ><!--//--><![CDATA[//><!--
$(document).ready(function(){
$('#mini_logo').on("click", function(e){
e.preventDefault();
$('#member_navigation').toggle();
});
});
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-63373181-1', 'maidstone-hackspace.org.uk');
ga('send', 'pageview');
var app = angular.module('myApp', ['ngAnimate']); var app = angular.module('myApp', ['ngAnimate']);
app.controller('sliderController', function($scope, $interval) { app.controller('sliderController', function($scope, $interval) {
@ -116,6 +93,29 @@ app.controller('sliderController', function($scope, $interval) {
}); });
$(document).ready(function(){
$('#mini_logo').on("click", function(e){
e.preventDefault();
$('#member_navigation').toggle();
});
});
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-63373181-1', 'maidstone-hackspace.org.uk');
ga('send', 'pageview');
//]]> //]]>
</script> </script>

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

144
website/static/html/index.htm Executable file

File diff suppressed because one or more lines are too long

View File

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

0
website/static/images/background.jpg Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

0
website/static/images/background.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 6.3 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

0
website/static/images/badges/art_hacker.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

0
website/static/images/badges/circuit_hacker.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 945 B

After

Width:  |  Height:  |  Size: 945 B

0
website/static/images/badges/code_hacker.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 1003 B

After

Width:  |  Height:  |  Size: 1003 B

0
website/static/images/badges/craft_hacker.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

0
website/static/images/badges/founder.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

0
website/static/images/badges/handyman.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

0
website/static/images/badges/member.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

0
website/static/images/badges/pledged.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 983 B

0
website/static/images/badges/trainer.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 983 B

After

Width:  |  Height:  |  Size: 983 B

0
website/static/images/banners/audio_board.jpg Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 236 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 559 KiB

0
website/static/images/banners/hackspace-banner.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

0
website/static/images/banners/indiegogo.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

0
website/static/images/banners/microscope.jpg Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

View File

Before

Width:  |  Height:  |  Size: 53 KiB

After

Width:  |  Height:  |  Size: 53 KiB

0
website/static/images/banners/rocket_camera.jpg Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 33 KiB

View File

Before

Width:  |  Height:  |  Size: 256 KiB

After

Width:  |  Height:  |  Size: 256 KiB

0
website/static/images/css/sprite-action-white.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

0
website/static/images/css/sprite-content-white.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

0
website/static/images/css/sprite-navigation-white.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

0
website/static/images/example-01.jpg Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 76 KiB

After

Width:  |  Height:  |  Size: 76 KiB

0
website/static/images/example-02.jpg Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 65 KiB

After

Width:  |  Height:  |  Size: 65 KiB

0
website/static/images/example-03.jpg Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 38 KiB

0
website/static/images/favicon.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

0
website/static/images/hackspace-banner.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

0
website/static/images/hackspace.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Some files were not shown because too many files have changed in this diff Show More