subdomain dispatcher
This commit is contained in:
parent
2906b79dfc
commit
1a08aade00
30
app.py
30
app.py
|
@ -1,35 +1,9 @@
|
||||||
from gevent import monkey, pywsgi
|
from gevent import monkey, pywsgi
|
||||||
monkey.patch_all()
|
monkey.patch_all()
|
||||||
from realms import create_app, config
|
from realms import config, init_db, make_app, SubdomainDispatcher
|
||||||
from threading import Lock
|
|
||||||
|
|
||||||
class SubdomainDispatcher(object):
|
|
||||||
|
|
||||||
def __init__(self, domain, create_app):
|
|
||||||
self.domain = domain
|
|
||||||
self.create_app = create_app
|
|
||||||
self.lock = Lock()
|
|
||||||
self.instances = {}
|
|
||||||
|
|
||||||
def get_application(self, host):
|
|
||||||
host = host.split(':')[0]
|
|
||||||
assert host.endswith(self.domain), 'Configuration error'
|
|
||||||
subdomain = host[:-len(self.domain)].rstrip('.')
|
|
||||||
with self.lock:
|
|
||||||
app = self.instances.get(subdomain)
|
|
||||||
if app is None:
|
|
||||||
app = self.create_app(subdomain)
|
|
||||||
self.instances[subdomain] = app
|
|
||||||
return app
|
|
||||||
|
|
||||||
def __call__(self, environ, start_response):
|
|
||||||
app = self.get_application(environ['HTTP_HOST'])
|
|
||||||
return app(environ, start_response)
|
|
||||||
|
|
||||||
|
|
||||||
def make_app(subdomain):
|
|
||||||
return create_app(subdomain)
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
init_db(config.db['dbname'])
|
||||||
app = SubdomainDispatcher(config.domain, make_app)
|
app = SubdomainDispatcher(config.domain, make_app)
|
||||||
pywsgi.WSGIServer(('', config.port), app).serve_forever()
|
pywsgi.WSGIServer(('', config.port), app).serve_forever()
|
||||||
|
|
|
@ -1,18 +1,60 @@
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import time
|
import time
|
||||||
|
from threading import Lock
|
||||||
|
|
||||||
from flask import Flask, request, render_template, url_for, redirect, flash, session
|
import rethinkdb as rdb
|
||||||
from flask.ext.bcrypt import Bcrypt
|
from flask import Flask, request, render_template, url_for, redirect, flash
|
||||||
from flask.ext.login import LoginManager, login_user, logout_user
|
from flask.ext.login import LoginManager, login_required
|
||||||
from flask.ext.assets import Environment
|
from flask.ext.assets import Environment
|
||||||
from recaptcha.client import captcha
|
from recaptcha.client import captcha
|
||||||
from werkzeug.routing import BaseConverter
|
from werkzeug.routing import BaseConverter
|
||||||
|
|
||||||
from session import RedisSessionInterface
|
from session import RedisSessionInterface
|
||||||
import config
|
import config
|
||||||
from wiki import Wiki
|
from wiki import Wiki
|
||||||
from util import to_canonical, remove_ext, mkdir_safe, gravatar_url
|
from util import to_canonical, remove_ext, mkdir_safe, gravatar_url
|
||||||
from models import Site, User, CurrentUser
|
from models import Site, User, CurrentUser
|
||||||
|
from ratelimit import get_view_rate_limit, ratelimiter
|
||||||
|
from services import db
|
||||||
|
|
||||||
|
|
||||||
|
instances = {}
|
||||||
|
|
||||||
|
class SubdomainDispatcher(object):
|
||||||
|
def __init__(self, domain, create_app):
|
||||||
|
self.domain = domain
|
||||||
|
self.create_app = create_app
|
||||||
|
self.lock = Lock()
|
||||||
|
|
||||||
|
def get_application(self, host):
|
||||||
|
host = host.split(':')[0]
|
||||||
|
assert host.endswith(self.domain), 'Configuration error'
|
||||||
|
subdomain = host[:-len(self.domain)].rstrip('.')
|
||||||
|
with self.lock:
|
||||||
|
app = instances.get(subdomain)
|
||||||
|
if app is None:
|
||||||
|
app = self.create_app(subdomain)
|
||||||
|
instances[subdomain] = app
|
||||||
|
return app
|
||||||
|
|
||||||
|
def __call__(self, environ, start_response):
|
||||||
|
app = self.get_application(environ['HTTP_HOST'])
|
||||||
|
return app(environ, start_response)
|
||||||
|
|
||||||
|
|
||||||
|
def init_db(dbname):
|
||||||
|
if not dbname in rdb.db_list().run(db):
|
||||||
|
print "Creating DB %s" % dbname
|
||||||
|
rdb.db_create(dbname).run(db)
|
||||||
|
|
||||||
|
for tbl in ['sites', 'users', 'pages']:
|
||||||
|
if not tbl in rdb.table_list().run(db):
|
||||||
|
rdb.table_create(tbl).run(db)
|
||||||
|
|
||||||
|
s = Site()
|
||||||
|
if not s.get_by_name('_'):
|
||||||
|
s.create(name='_', repo='_')
|
||||||
|
|
||||||
|
|
||||||
class RegexConverter(BaseConverter):
|
class RegexConverter(BaseConverter):
|
||||||
|
@ -21,8 +63,10 @@ class RegexConverter(BaseConverter):
|
||||||
self.regex = items[0]
|
self.regex = items[0]
|
||||||
|
|
||||||
|
|
||||||
def redirect_url():
|
def redirect_url(referrer=None):
|
||||||
return request.args.get('next') or request.referrer or url_for('index')
|
if not referrer:
|
||||||
|
referrer = request.referrer
|
||||||
|
return request.args.get('next') or referrer or url_for('index')
|
||||||
|
|
||||||
|
|
||||||
def validate_captcha():
|
def validate_captcha():
|
||||||
|
@ -34,6 +78,12 @@ def validate_captcha():
|
||||||
return response.is_valid
|
return response.is_valid
|
||||||
|
|
||||||
|
|
||||||
|
def make_app(subdomain):
|
||||||
|
if subdomain and not Wiki.is_registered(subdomain):
|
||||||
|
return redirect("http://%s/_new/?site=%s" % (config.hostname, subdomain))
|
||||||
|
return create_app(subdomain)
|
||||||
|
|
||||||
|
|
||||||
def create_app(subdomain=None):
|
def create_app(subdomain=None):
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
app.config.update(config.flask)
|
app.config.update(config.flask)
|
||||||
|
@ -43,10 +93,9 @@ def create_app(subdomain=None):
|
||||||
app.session_interface = RedisSessionInterface()
|
app.session_interface = RedisSessionInterface()
|
||||||
app.url_map.converters['regex'] = RegexConverter
|
app.url_map.converters['regex'] = RegexConverter
|
||||||
|
|
||||||
bcrypt = Bcrypt(app)
|
|
||||||
|
|
||||||
login_manager = LoginManager()
|
login_manager = LoginManager()
|
||||||
login_manager.init_app(app)
|
login_manager.init_app(app)
|
||||||
|
login_manager.login_view = 'login'
|
||||||
|
|
||||||
@login_manager.user_loader
|
@login_manager.user_loader
|
||||||
def load_user(user_id):
|
def load_user(user_id):
|
||||||
|
@ -56,60 +105,65 @@ def create_app(subdomain=None):
|
||||||
assets.url = app.static_url_path
|
assets.url = app.static_url_path
|
||||||
assets.directory = app.static_folder
|
assets.directory = app.static_folder
|
||||||
|
|
||||||
|
|
||||||
main_repo_dir = config.repos['main']
|
|
||||||
repo_dir = config.repos['dir']
|
repo_dir = config.repos['dir']
|
||||||
|
repo_name = subdomain if subdomain else "_"
|
||||||
|
|
||||||
w = Wiki(main_repo_dir) if not subdomain else Wiki(repo_dir + "/" + subdomain)
|
w = Wiki(repo_dir + "/" + repo_name)
|
||||||
|
|
||||||
|
@app.after_request
|
||||||
|
def inject_x_rate_headers(response):
|
||||||
|
limit = get_view_rate_limit()
|
||||||
|
if limit and limit.send_x_headers:
|
||||||
|
h = response.headers
|
||||||
|
h.add('X-RateLimit-Remaining', str(limit.remaining))
|
||||||
|
h.add('X-RateLimit-Limit', str(limit.limit))
|
||||||
|
h.add('X-RateLimit-Reset', str(limit.reset))
|
||||||
|
return response
|
||||||
|
|
||||||
@app.template_filter('datetime')
|
@app.template_filter('datetime')
|
||||||
def _jinja2_filter_datetime(ts):
|
def _jinja2_filter_datetime(ts):
|
||||||
return time.strftime('%b %d, %Y %I:%M %p', time.localtime(ts))
|
return time.strftime('%b %d, %Y %I:%M %p', time.localtime(ts))
|
||||||
|
|
||||||
|
|
||||||
@app.errorhandler(404)
|
@app.errorhandler(404)
|
||||||
def page_not_found(e):
|
def page_not_found(e):
|
||||||
return render_template('errors/404.html'), 404
|
return render_template('errors/404.html'), 404
|
||||||
|
|
||||||
|
|
||||||
@app.errorhandler(500)
|
@app.errorhandler(500)
|
||||||
def page_error(e):
|
def page_error(e):
|
||||||
logging.exception(e)
|
logging.exception(e)
|
||||||
return render_template('errors/500.html'), 500
|
return render_template('errors/500.html'), 500
|
||||||
|
|
||||||
|
|
||||||
@app.route("/")
|
@app.route("/")
|
||||||
|
@ratelimiter(limit=50, per=60)
|
||||||
def root():
|
def root():
|
||||||
return render('home')
|
return render('home')
|
||||||
#return redirect('/home')
|
#return redirect('/home')
|
||||||
|
|
||||||
|
|
||||||
@app.route("/account/")
|
@app.route("/account/")
|
||||||
|
@login_required
|
||||||
def account():
|
def account():
|
||||||
return render_template('account/index.html')
|
return render_template('account/index.html')
|
||||||
|
|
||||||
|
|
||||||
@app.route("/_new/", methods=['GET', 'POST'])
|
@app.route("/_new/", methods=['GET', 'POST'])
|
||||||
|
@login_required
|
||||||
def new_wiki():
|
def new_wiki():
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
# TODO validate wiki name
|
wiki_name = to_canonical(request.form['name'])
|
||||||
wiki_name = request.form['name']
|
|
||||||
s = Site()
|
if Wiki.is_registered(wiki_name):
|
||||||
if s.get_by_name(wiki_name):
|
|
||||||
flash("Site already exists")
|
flash("Site already exists")
|
||||||
return redirect(redirect_url())
|
return redirect(redirect_url())
|
||||||
else:
|
else:
|
||||||
Wiki(repo_dir + "/" + wiki_name)
|
s = Site()
|
||||||
|
s.create(name=wiki_name, repo=wiki_name)
|
||||||
|
instances.pop(wiki_name, None)
|
||||||
return redirect('http://%s.%s' % (wiki_name, config.hostname))
|
return redirect('http://%s.%s' % (wiki_name, config.hostname))
|
||||||
else:
|
else:
|
||||||
return render_template('_new/index.html')
|
return render_template('_new/index.html')
|
||||||
|
|
||||||
|
|
||||||
@app.route("/logout/")
|
@app.route("/logout/")
|
||||||
def logout():
|
def logout():
|
||||||
logout_user()
|
User.logout()
|
||||||
del session['user']
|
|
||||||
return redirect(url_for('root'))
|
return redirect(url_for('root'))
|
||||||
|
|
||||||
@app.route("/commit/<sha>/<name>")
|
@app.route("/commit/<sha>/<name>")
|
||||||
|
@ -122,55 +176,40 @@ def create_app(subdomain=None):
|
||||||
else:
|
else:
|
||||||
return redirect('/create/'+cname)
|
return redirect('/create/'+cname)
|
||||||
|
|
||||||
|
|
||||||
@app.route("/compare/<name>/<regex('[^.]+'):fsha><regex('\.{2,3}'):dots><regex('.+'):lsha>")
|
@app.route("/compare/<name>/<regex('[^.]+'):fsha><regex('\.{2,3}'):dots><regex('.+'):lsha>")
|
||||||
def compare(name, fsha, dots, lsha):
|
def compare(name, fsha, dots, lsha):
|
||||||
diff = w.compare(name, fsha, lsha)
|
diff = w.compare(name, fsha, lsha)
|
||||||
return render_template('page/compare.html', name=name, diff=diff)
|
return render_template('page/compare.html', name=name, diff=diff)
|
||||||
|
|
||||||
|
|
||||||
@app.route("/register", methods=['GET', 'POST'])
|
@app.route("/register", methods=['GET', 'POST'])
|
||||||
def register():
|
def register():
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
user = User()
|
if User.register(request.form.get('username'), request.form.get('email'), request.form.get('password')):
|
||||||
if user.get_by_email(request.form['email']):
|
return redirect(url_for('root'))
|
||||||
flash('Email is already taken')
|
else:
|
||||||
return redirect('/register')
|
# Login failed
|
||||||
if user.get_by_username(request.form['username']):
|
return redirect(url_for('register'))
|
||||||
flash('Username is already taken')
|
|
||||||
return redirect('/register')
|
|
||||||
|
|
||||||
email = request.form['email'].lower()
|
|
||||||
# Create user and login
|
|
||||||
u = User.create(email=email,
|
|
||||||
username=request.form['username'],
|
|
||||||
password=bcrypt.generate_password_hash(request.form['password']),
|
|
||||||
avatar=gravatar_url(email))
|
|
||||||
login_user(CurrentUser(u.id))
|
|
||||||
return redirect("/")
|
|
||||||
else:
|
else:
|
||||||
return render_template('account/register.html')
|
return render_template('account/register.html')
|
||||||
|
|
||||||
|
|
||||||
@app.route("/login", methods=['GET', 'POST'])
|
@app.route("/login", methods=['GET', 'POST'])
|
||||||
def login():
|
def login():
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
if User.auth(request.form['email'], request.form['password']):
|
if User.auth(request.form['email'], request.form['password']):
|
||||||
return redirect("/")
|
return redirect(redirect_url(referrer=url_for('root')))
|
||||||
else:
|
else:
|
||||||
flash("Email or Password invalid")
|
flash("Email or Password invalid")
|
||||||
return redirect("/login")
|
return redirect("/login")
|
||||||
else:
|
else:
|
||||||
return render_template('account/login.html')
|
return render_template('account/login.html')
|
||||||
|
|
||||||
|
|
||||||
@app.route("/history/<name>")
|
@app.route("/history/<name>")
|
||||||
def history(name):
|
def history(name):
|
||||||
history = w.get_history(name)
|
history = w.get_history(name)
|
||||||
return render_template('page/history.html', name=name, history=history)
|
return render_template('page/history.html', name=name, history=history)
|
||||||
|
|
||||||
|
|
||||||
@app.route("/edit/<name>", methods=['GET', 'POST'])
|
@app.route("/edit/<name>", methods=['GET', 'POST'])
|
||||||
|
@login_required
|
||||||
def edit(name):
|
def edit(name):
|
||||||
data = w.get_page(name)
|
data = w.get_page(name)
|
||||||
cname = to_canonical(name)
|
cname = to_canonical(name)
|
||||||
|
@ -188,14 +227,14 @@ def create_app(subdomain=None):
|
||||||
else:
|
else:
|
||||||
return redirect('/create/'+cname)
|
return redirect('/create/'+cname)
|
||||||
|
|
||||||
|
|
||||||
@app.route("/delete/<name>", methods=['POST'])
|
@app.route("/delete/<name>", methods=['POST'])
|
||||||
|
@login_required
|
||||||
def delete(name):
|
def delete(name):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@app.route("/create/", methods=['GET', 'POST'])
|
@app.route("/create/", methods=['GET', 'POST'])
|
||||||
@app.route("/create/<name>", methods=['GET', 'POST'])
|
@app.route("/create/<name>", methods=['GET', 'POST'])
|
||||||
|
@login_required
|
||||||
def create(name=None):
|
def create(name=None):
|
||||||
cname = ""
|
cname = ""
|
||||||
if name:
|
if name:
|
||||||
|
|
|
@ -1,27 +1,13 @@
|
||||||
import rethinkdb as rdb
|
import rethinkdb as rdb
|
||||||
import bcrypt
|
import bcrypt
|
||||||
import redis
|
from flask import session, flash
|
||||||
from flask import session
|
from flask.ext.login import login_user, logout_user
|
||||||
from flask.ext.login import login_user
|
|
||||||
from rethinkORM import RethinkModel
|
from rethinkORM import RethinkModel
|
||||||
from realms import config
|
from util import gravatar_url
|
||||||
|
from services import db
|
||||||
# Default DB connection
|
|
||||||
conn = rdb.connect(config.db['host'], config.db['port'], db=config.db['dbname'])
|
|
||||||
|
|
||||||
# Default Cache connection
|
|
||||||
cache = redis.StrictRedis(host=config.cache['host'], port=config.cache['port'])
|
|
||||||
|
|
||||||
|
|
||||||
def init_db():
|
def to_dict(cur, first):
|
||||||
if not config.db['dbname'] in rdb.db_list().run(conn) and config.ENV is not 'PROD':
|
|
||||||
# Create default db and repo
|
|
||||||
print "Creating DB %s" % config.db['dbname']
|
|
||||||
rdb.db_create(config.db['dbname']).run(conn)
|
|
||||||
for tbl in ['sites', 'users', 'pages']:
|
|
||||||
rdb.table_create(tbl).run(conn)
|
|
||||||
|
|
||||||
def to_dict(cur, first=False):
|
|
||||||
ret = []
|
ret = []
|
||||||
for row in cur:
|
for row in cur:
|
||||||
ret.append(row)
|
ret.append(row)
|
||||||
|
@ -33,9 +19,11 @@ def to_dict(cur, first=False):
|
||||||
|
|
||||||
class BaseModel(RethinkModel):
|
class BaseModel(RethinkModel):
|
||||||
|
|
||||||
|
_conn = db
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
if not kwargs.get('conn'):
|
if not kwargs.get('conn'):
|
||||||
kwargs['conn'] = conn
|
kwargs['conn'] = db
|
||||||
super(BaseModel, self).__init__(**kwargs)
|
super(BaseModel, self).__init__(**kwargs)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -66,7 +54,7 @@ class CurrentUser():
|
||||||
return self.id
|
return self.id
|
||||||
|
|
||||||
def is_active(self):
|
def is_active(self):
|
||||||
return True
|
return True if self.id else False
|
||||||
|
|
||||||
def is_anonymous(self):
|
def is_anonymous(self):
|
||||||
return False if self.id else True
|
return False if self.id else True
|
||||||
|
@ -100,8 +88,36 @@ class User(BaseModel):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if bcrypt.checkpw(password, data['password']):
|
if bcrypt.checkpw(password, data['password']):
|
||||||
login_user(CurrentUser(data['id']))
|
cls.login(data['id'], data)
|
||||||
session['user'] = data
|
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def register(cls, username, email, password):
|
||||||
|
user = User()
|
||||||
|
email = email.lower()
|
||||||
|
if user.get_by_email(email):
|
||||||
|
flash('Email is already taken')
|
||||||
|
return False
|
||||||
|
if user.get_by_username(username):
|
||||||
|
flash('Username is already taken')
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Create user and login
|
||||||
|
u = User.create(email=email,
|
||||||
|
username=username,
|
||||||
|
password=bcrypt.hashpw(password, bcrypt.gensalt(10)),
|
||||||
|
avatar=gravatar_url(email))
|
||||||
|
|
||||||
|
User.login(u.id, user.get_one(u.id, 'id'))
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def login(cls, id, data=None):
|
||||||
|
login_user(CurrentUser(id), True)
|
||||||
|
session['user'] = data
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def logout(cls):
|
||||||
|
logout_user()
|
||||||
|
session.pop('user', None)
|
|
@ -1,8 +1,7 @@
|
||||||
import time
|
import time
|
||||||
from functools import update_wrapper
|
from functools import update_wrapper
|
||||||
from flask import request, g
|
from flask import request, g
|
||||||
from realms import app
|
from services import cache
|
||||||
from models import cache
|
|
||||||
|
|
||||||
|
|
||||||
class RateLimit(object):
|
class RateLimit(object):
|
||||||
|
@ -28,10 +27,10 @@ def get_view_rate_limit():
|
||||||
|
|
||||||
|
|
||||||
def on_over_limit(limit):
|
def on_over_limit(limit):
|
||||||
return 'You hit the rate limit', 400
|
return 'Slow it down', 400
|
||||||
|
|
||||||
|
|
||||||
def ratelimit(limit, per=300, send_x_headers=True,
|
def ratelimiter(limit, per=300, send_x_headers=True,
|
||||||
over_limit=on_over_limit,
|
over_limit=on_over_limit,
|
||||||
scope_func=lambda: request.remote_addr,
|
scope_func=lambda: request.remote_addr,
|
||||||
key_func=lambda: request.endpoint):
|
key_func=lambda: request.endpoint):
|
||||||
|
@ -44,15 +43,4 @@ def ratelimit(limit, per=300, send_x_headers=True,
|
||||||
return over_limit(rlimit)
|
return over_limit(rlimit)
|
||||||
return f(*args, **kwargs)
|
return f(*args, **kwargs)
|
||||||
return update_wrapper(rate_limited, f)
|
return update_wrapper(rate_limited, f)
|
||||||
return decorator
|
return decorator
|
||||||
|
|
||||||
|
|
||||||
@app.after_request
|
|
||||||
def inject_x_rate_headers(response):
|
|
||||||
limit = get_view_rate_limit()
|
|
||||||
if limit and limit.send_x_headers:
|
|
||||||
h = response.headers
|
|
||||||
h.add('X-RateLimit-Remaining', str(limit.remaining))
|
|
||||||
h.add('X-RateLimit-Limit', str(limit.limit))
|
|
||||||
h.add('X-RateLimit-Reset', str(limit.reset))
|
|
||||||
return response
|
|
11
realms/services.py
Normal file
11
realms/services.py
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import rethinkdb as rdb
|
||||||
|
import redis
|
||||||
|
|
||||||
|
import config
|
||||||
|
|
||||||
|
|
||||||
|
# Default DB connection
|
||||||
|
db = rdb.connect(config.db['host'], config.db['port'], db=config.db['dbname'])
|
||||||
|
|
||||||
|
# Default Cache connection
|
||||||
|
cache = redis.StrictRedis(host=config.cache['host'], port=config.cache['port'])
|
|
@ -7,7 +7,7 @@
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="wiki" class="control-label">Site Name</label>
|
<label for="wiki" class="control-label">Site Name</label>
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<input id="wiki" name="name" type="text" class="form-control" />
|
<input id="wiki" name="name" type="text" class="form-control" value="{{ request.args.get('site') }}" />
|
||||||
<span class="input-group-addon">.realms.io</span>
|
<span class="input-group-addon">.realms.io</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -3,18 +3,12 @@
|
||||||
|
|
||||||
<h2>Account</h2>
|
<h2>Account</h2>
|
||||||
|
|
||||||
<form method="POST" role="form" class="form-horizontal">
|
<form method="POST" role="form">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="email" class="col-md-2 control-label">Email</label>
|
<label for="email" class="control-label">Email</label>
|
||||||
<div class="col-md-10">
|
<input id="email" type="text" class="form-control" value="{{ session['user']['email'] }}" />
|
||||||
<input id="email" type="text" class="form-control" value="{{ session['user']['email'] }}" />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<input type="submit" class="btn btn-primary" value="Save">
|
||||||
<div class="col-md-offset-2 col-md-10">
|
|
||||||
<input type="submit" class="btn btn-primary" value="Save">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
</form>
|
||||||
{% endblock %}
|
{% endblock %}
|
|
@ -4,8 +4,10 @@ from lxml.html.clean import clean_html
|
||||||
import ghdiff
|
import ghdiff
|
||||||
|
|
||||||
from gittle import Gittle
|
from gittle import Gittle
|
||||||
|
from dulwich.repo import NotGitRepository
|
||||||
|
|
||||||
from util import to_canonical
|
from util import to_canonical
|
||||||
|
from models import Site
|
||||||
|
|
||||||
|
|
||||||
class MyGittle(Gittle):
|
class MyGittle(Gittle):
|
||||||
|
@ -54,13 +56,17 @@ class Wiki():
|
||||||
|
|
||||||
def __init__(self, path):
|
def __init__(self, path):
|
||||||
try:
|
try:
|
||||||
self.repo = MyGittle.init(path)
|
|
||||||
except OSError:
|
|
||||||
# Repo already exists
|
|
||||||
self.repo = MyGittle(path)
|
self.repo = MyGittle(path)
|
||||||
|
except NotGitRepository:
|
||||||
|
self.repo = MyGittle.init(path)
|
||||||
|
|
||||||
self.path = path
|
self.path = path
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def is_registered(name):
|
||||||
|
s = Site()
|
||||||
|
return True if s.get_by_name(name) else False
|
||||||
|
|
||||||
def write_page(self, name, content, message=None, create=False, username=None, email=None):
|
def write_page(self, name, content, message=None, create=False, username=None, email=None):
|
||||||
content = clean_html(content)
|
content = clean_html(content)
|
||||||
filename = self.cname_to_filename(to_canonical(name))
|
filename = self.cname_to_filename(to_canonical(name))
|
||||||
|
|
Loading…
Reference in a new issue