many things have occured
This commit is contained in:
parent
02b6b7d592
commit
db70df22a2
3
app.py
3
app.py
|
@ -1,7 +1,8 @@
|
|||
from gevent import monkey, pywsgi
|
||||
from realms import config, app
|
||||
|
||||
monkey.patch_all()
|
||||
import logging
|
||||
from realms import app, config
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
@ -1,16 +1,17 @@
|
|||
import logging
|
||||
import os
|
||||
import time
|
||||
from tldextract import tldextract
|
||||
import sys
|
||||
import os
|
||||
|
||||
from flask import Flask, g, request, render_template, url_for, redirect, flash, session, current_app
|
||||
from flask import Flask, request, render_template, url_for, redirect, session
|
||||
from flask.ctx import _AppCtxGlobals
|
||||
from flask.ext.script import Manager
|
||||
from flask.ext.login import LoginManager, login_required
|
||||
from flask.ext.assets import Environment, Bundle
|
||||
from werkzeug.routing import BaseConverter
|
||||
from werkzeug.utils import cached_property
|
||||
|
||||
import config
|
||||
from realms import config
|
||||
from realms.lib.ratelimit import get_view_rate_limit, ratelimiter
|
||||
from realms.lib.session import RedisSessionInterface
|
||||
from realms.lib.wiki import Wiki
|
||||
|
@ -19,13 +20,26 @@ from realms.lib.services import db
|
|||
from models import Site, User, CurrentUser
|
||||
|
||||
|
||||
wikis = {}
|
||||
|
||||
|
||||
class AppCtxGlobals(_AppCtxGlobals):
|
||||
|
||||
@cached_property
|
||||
def current_wiki(self):
|
||||
subdomain = format_subdomain(self.current_site)
|
||||
if not subdomain:
|
||||
subdomain = "_"
|
||||
|
||||
if not wikis.get(subdomain):
|
||||
wikis[subdomain] = Wiki("%s/%s" % (config.REPO_DIR, subdomain))
|
||||
|
||||
return wikis[subdomain]
|
||||
|
||||
@cached_property
|
||||
def current_site(self):
|
||||
ext = tldextract.extract(request.host)
|
||||
print ext
|
||||
return ext.subdomain
|
||||
host = request.host.split(':')[0]
|
||||
return host[:-len(config.DOMAIN)].rstrip('.')
|
||||
|
||||
@cached_property
|
||||
def current_user(self):
|
||||
|
@ -53,6 +67,41 @@ class Application(Flask):
|
|||
|
||||
return super(Application, self).__call__(environ, start_response)
|
||||
|
||||
def discover(self):
|
||||
"""
|
||||
Pattern taken from guildwork.com
|
||||
"""
|
||||
IMPORT_NAME = 'realms.modules'
|
||||
FROMLIST = (
|
||||
'assets',
|
||||
'models',
|
||||
'search',
|
||||
'perms',
|
||||
'broadcasts',
|
||||
'commands',
|
||||
'notifications',
|
||||
'requests',
|
||||
'tasks',
|
||||
'views',
|
||||
)
|
||||
|
||||
start_time = time.time()
|
||||
|
||||
__import__(IMPORT_NAME, fromlist=FROMLIST)
|
||||
|
||||
for module_name in self.config['MODULES']:
|
||||
sources = __import__('%s.%s' % (IMPORT_NAME, module_name), fromlist=FROMLIST)
|
||||
|
||||
# Blueprint
|
||||
if hasattr(sources, 'views'):
|
||||
self.register_blueprint(sources.views.blueprint)
|
||||
|
||||
# Flask-Script
|
||||
if hasattr(sources, 'commands'):
|
||||
manager.add_command(module_name, sources.commands.manager)
|
||||
|
||||
print >> sys.stderr, ' * Ready in %.2fms' % (1000.0 * (time.time() - start_time))
|
||||
|
||||
|
||||
def init_db(dbname):
|
||||
"""
|
||||
|
@ -77,23 +126,24 @@ def redirect_url(referrer=None):
|
|||
|
||||
|
||||
def format_subdomain(s):
|
||||
if not config.repos['enable_subrepos']:
|
||||
if not config.REPO_ENABLE_SUBDOMAIN:
|
||||
return ""
|
||||
s = s.lower()
|
||||
s = to_canonical(s)
|
||||
if s in config.repos['forbidden_subrepos']:
|
||||
if s in config.REPO_FORBIDDEN_NAMES:
|
||||
# Not allowed
|
||||
s = ""
|
||||
return s
|
||||
|
||||
|
||||
app = Application(__name__)
|
||||
app.config.update(config.FLASK)
|
||||
app.debug = (config.ENV is not 'PROD')
|
||||
app.secret_key = config.SECRET_KEY
|
||||
app.static_path = os.sep + 'static'
|
||||
app.config.from_object('realms.config')
|
||||
app.session_interface = RedisSessionInterface()
|
||||
app.url_map.converters['regex'] = RegexConverter
|
||||
app.url_map.strict_slashes = False
|
||||
app.debug = True
|
||||
|
||||
manager = Manager(app)
|
||||
|
||||
# Flask extension objects
|
||||
login_manager = LoginManager()
|
||||
|
@ -137,8 +187,6 @@ else:
|
|||
filters='closure_js', output='packed-editor.js')
|
||||
assets.register('js_editor', js)
|
||||
|
||||
repo_dir = config.REPO_DIR
|
||||
|
||||
|
||||
@app.after_request
|
||||
def inject_x_rate_headers(response):
|
||||
|
@ -155,6 +203,7 @@ def inject_x_rate_headers(response):
|
|||
def _jinja2_filter_datetime(ts):
|
||||
return time.strftime('%b %d, %Y %I:%M %p', time.localtime(ts))
|
||||
|
||||
|
||||
@app.errorhandler(404)
|
||||
def page_not_found(e):
|
||||
return render_template('errors/404.html'), 404
|
||||
|
@ -167,160 +216,18 @@ def page_error(e):
|
|||
|
||||
|
||||
@app.route("/")
|
||||
@ratelimiter(limit=50, per=60)
|
||||
def root():
|
||||
return g.current_site
|
||||
return render('home')
|
||||
return redirect(url_for(config.ROOT_ENDPOINT))
|
||||
|
||||
|
||||
@app.route("/home")
|
||||
def home():
|
||||
return redirect(url_for('root'))
|
||||
|
||||
|
||||
@app.route("/_account/")
|
||||
@login_required
|
||||
def account():
|
||||
return render_template('account/index.html')
|
||||
|
||||
if 'devserver' not in sys.argv or os.environ.get('WERKZEUG_RUN_MAIN'):
|
||||
app.discover()
|
||||
|
||||
@app.route("/_new/", methods=['GET', 'POST'])
|
||||
@login_required
|
||||
def new_wiki():
|
||||
if request.method == 'POST':
|
||||
wiki_name = to_canonical(request.form['name'])
|
||||
print app.url_map
|
||||
|
||||
if Wiki.is_registered(wiki_name):
|
||||
flash("Site already exists")
|
||||
return redirect(redirect_url())
|
||||
else:
|
||||
s = Site()
|
||||
s.create(name=wiki_name, repo=wiki_name, founder=g.current_user.get('id'))
|
||||
return redirect('http://%s.%s' % (wiki_name, config.hostname))
|
||||
else:
|
||||
return render_template('_new/index.html')
|
||||
|
||||
|
||||
@app.route("/_logout/")
|
||||
def logout():
|
||||
User.logout()
|
||||
return redirect(url_for('root'))
|
||||
|
||||
|
||||
@app.route("/_commit/<sha>/<name>")
|
||||
def commit_sha(name, sha):
|
||||
cname = to_canonical(name)
|
||||
|
||||
data = Wiki.get_page(cname, sha=sha)
|
||||
if data:
|
||||
return render_template('page/page.html', name=name, page=data, commit=sha)
|
||||
else:
|
||||
return redirect('/_create/'+cname)
|
||||
|
||||
|
||||
@app.route("/_compare/<name>/<regex('[^.]+'):fsha><regex('\.{2,3}'):dots><regex('.+'):lsha>")
|
||||
def compare(name, fsha, dots, lsha):
|
||||
diff = Wiki.compare(name, fsha, lsha)
|
||||
return render_template('page/compare.html', name=name, diff=diff, old=fsha, new=lsha)
|
||||
|
||||
|
||||
@app.route("/_revert", methods=['POST'])
|
||||
def revert():
|
||||
if request.method == 'POST':
|
||||
name = request.form.get('name')
|
||||
commit = request.form.get('commit')
|
||||
cname = to_canonical(name)
|
||||
Wiki.revert_page(name, commit, message="Reverting %s" % cname, username=g.current_user.get('username'))
|
||||
flash('Page reverted', 'success')
|
||||
return redirect("/" + cname)
|
||||
|
||||
|
||||
@app.route("/_register", methods=['GET', 'POST'])
|
||||
def register():
|
||||
if request.method == 'POST':
|
||||
if User.register(request.form.get('username'), request.form.get('email'), request.form.get('password')):
|
||||
return redirect(url_for('root'))
|
||||
else:
|
||||
# Login failed
|
||||
return redirect(url_for('register'))
|
||||
else:
|
||||
return render_template('account/register.html')
|
||||
|
||||
|
||||
@app.route("/_login", methods=['GET', 'POST'])
|
||||
def login():
|
||||
if request.method == 'POST':
|
||||
if User.auth(request.form['email'], request.form['password']):
|
||||
return redirect(redirect_url(referrer=url_for('root')))
|
||||
else:
|
||||
flash("Email or Password invalid")
|
||||
return redirect("/_login")
|
||||
else:
|
||||
return render_template('account/login.html')
|
||||
|
||||
|
||||
@app.route("/_history/<name>")
|
||||
def history(name):
|
||||
history = Wiki.get_history(name)
|
||||
return render_template('page/history.html', name=name, history=history)
|
||||
|
||||
|
||||
@app.route("/_edit/<name>", methods=['GET', 'POST'])
|
||||
def edit(name):
|
||||
data = Wiki.get_page(name)
|
||||
cname = to_canonical(name)
|
||||
if request.method == 'POST':
|
||||
edit_cname = to_canonical(request.form['name'])
|
||||
if edit_cname.lower() != cname.lower():
|
||||
Wiki.rename_page(cname, edit_cname)
|
||||
Wiki.write_page(edit_cname,
|
||||
request.form['content'],
|
||||
message=request.form['message'],
|
||||
username=g.current_user.get('username'))
|
||||
return redirect("/" + edit_cname)
|
||||
else:
|
||||
if data:
|
||||
name = remove_ext(data['name'])
|
||||
content = data['data']
|
||||
return render_template('page/edit.html', name=name, content=content)
|
||||
else:
|
||||
return redirect('/_create/'+cname)
|
||||
|
||||
|
||||
@app.route("/_delete/<name>", methods=['POST'])
|
||||
@login_required
|
||||
def delete(name):
|
||||
pass
|
||||
|
||||
|
||||
@app.route("/_create/", methods=['GET', 'POST'])
|
||||
@app.route("/_create/<name>", methods=['GET', 'POST'])
|
||||
def create(name=None):
|
||||
cname = ""
|
||||
if name:
|
||||
cname = to_canonical(name)
|
||||
if Wiki.get_page(cname):
|
||||
# Page exists, edit instead
|
||||
return redirect("/edit/" + cname)
|
||||
if request.method == 'POST':
|
||||
Wiki.write_page(request.form['name'],
|
||||
request.form['content'],
|
||||
message=request.form['message'],
|
||||
create=True,
|
||||
username=g.current_user.get('username'))
|
||||
return redirect("/" + cname)
|
||||
else:
|
||||
return render_template('page/edit.html', name=cname, content="")
|
||||
|
||||
|
||||
@app.route("/<name>")
|
||||
def render(name):
|
||||
cname = to_canonical(name)
|
||||
if cname != name:
|
||||
return redirect('/' + cname)
|
||||
|
||||
data = Wiki.get_page(cname)
|
||||
if data:
|
||||
return render_template('page/page.html', name=cname, page=data)
|
||||
else:
|
||||
return redirect('/_create/'+cname)
|
||||
|
|
38
realms/config/__init__.py
Normal file
38
realms/config/__init__.py
Normal file
|
@ -0,0 +1,38 @@
|
|||
import socket
|
||||
|
||||
HOSTNAME = socket.gethostname()
|
||||
|
||||
DOMAIN = 'realms.dev'
|
||||
ENV = 'DEV'
|
||||
PORT = 10000
|
||||
|
||||
DB_URI = 'postgresql://realms:dbpassword@localhost:5432/realms'
|
||||
|
||||
REDIS_HOST = '127.0.0.1'
|
||||
REDIS_PORT = 6379
|
||||
|
||||
SECRET_KEY = 'K3dRq1q9eN72GJDkgvyshFVwlqHHCyPI'
|
||||
|
||||
REPO_DIR = '/home/deploy/repos'
|
||||
REPO_MAIN_NAME = '_'
|
||||
REPO_FORBIDDEN_NAMES = ['api', 'www']
|
||||
REPO_ENABLE_SUBDOMAIN = True
|
||||
|
||||
|
||||
RECAPTCHA_PUBLIC_KEY = '6LfoxeESAAAAAGNaeWnISh0GTgDk0fBnr6Bo2Tfk'
|
||||
RECAPTCHA_PRIVATE_KEY = '6LfoxeESAAAAABFzdCs0hNIIyeb42mofV-Ndd2_2'
|
||||
RECAPTCHA_OPTIONS = {'theme': 'clean'}
|
||||
|
||||
ROOT_ENDPOINT = 'wiki.page'
|
||||
WIKI_HOME = 'home'
|
||||
|
||||
MODULES = [
|
||||
'wiki',
|
||||
'auth'
|
||||
]
|
||||
|
||||
if ENV is 'PROD':
|
||||
pass
|
||||
else:
|
||||
DEBUG = True
|
||||
ASSETS_DEBUG = True
|
|
@ -1,8 +1,9 @@
|
|||
import redis
|
||||
from realms import config
|
||||
from sqlalchemy import create_engine
|
||||
|
||||
# Default DB connection
|
||||
from realms import config
|
||||
|
||||
db = create_engine(config.DB_URI, encoding='utf8', echo=True)
|
||||
|
||||
# Default Cache connection
|
||||
|
|
|
@ -59,11 +59,12 @@ def to_dict(cur, first=False):
|
|||
else:
|
||||
return ret
|
||||
|
||||
|
||||
def validate_captcha():
|
||||
response = captcha.submit(
|
||||
request.form['recaptcha_challenge_field'],
|
||||
request.form['recaptcha_response_field'],
|
||||
config.flask['RECAPTCHA_PRIVATE_KEY'],
|
||||
config.RECAPTCHA_PRIVATE_KEY,
|
||||
request.remote_addr)
|
||||
return response.is_valid
|
||||
|
||||
|
@ -125,4 +126,4 @@ def to_canonical(s):
|
|||
|
||||
|
||||
def gravatar_url(email):
|
||||
return "https://www.gravatar.com/avatar/" + hashlib.md5(email).hexdigest()
|
||||
return "//www.gravatar.com/avatar/" + hashlib.md5(email).hexdigest()
|
|
@ -1,6 +1,7 @@
|
|||
import os
|
||||
import re
|
||||
import lxml.html
|
||||
from lxml.html.clean import Cleaner
|
||||
import ghdiff
|
||||
import gittle.utils
|
||||
from gittle import Gittle
|
||||
|
@ -92,7 +93,9 @@ class Wiki():
|
|||
|
||||
tree = lxml.html.fromstring(content)
|
||||
|
||||
cleaner = lxml.html.Cleaner(remove_unknown_tags=False, kill_tags=set(['style']), safe_attrs_only=False)
|
||||
cleaner = Cleaner(remove_unknown_tags=False,
|
||||
kill_tags=set(['style']),
|
||||
safe_attrs_only=False)
|
||||
tree = cleaner.clean_html(tree)
|
||||
|
||||
content = lxml.html.tostring(tree, encoding='utf-8', method='html')
|
||||
|
|
0
realms/modules/__init__.py
Normal file
0
realms/modules/__init__.py
Normal file
0
realms/modules/auth/__init__.py
Normal file
0
realms/modules/auth/__init__.py
Normal file
35
realms/modules/auth/views.py
Normal file
35
realms/modules/auth/views.py
Normal file
|
@ -0,0 +1,35 @@
|
|||
from flask import render_template, redirect, request, url_for, flash, Blueprint
|
||||
from realms import redirect_url
|
||||
from realms.models import User
|
||||
|
||||
blueprint = Blueprint('auth', __name__)
|
||||
|
||||
|
||||
@blueprint.route("/logout/")
|
||||
def logout():
|
||||
User.logout()
|
||||
return redirect(url_for('root'))
|
||||
|
||||
|
||||
@blueprint.route("/register/", methods=['GET', 'POST'])
|
||||
def register():
|
||||
if request.method == 'POST':
|
||||
if User.register(request.form.get('username'), request.form.get('email'), request.form.get('password')):
|
||||
return redirect(url_for('root'))
|
||||
else:
|
||||
# Login failed
|
||||
return redirect(url_for('.register'))
|
||||
else:
|
||||
return render_template('auth/register.html')
|
||||
|
||||
|
||||
@blueprint.route("/login/", methods=['GET', 'POST'])
|
||||
def login():
|
||||
if request.method == 'POST':
|
||||
if User.auth(request.form['email'], request.form['password']):
|
||||
return redirect(redirect_url(referrer=url_for('root')))
|
||||
else:
|
||||
flash("Email or Password invalid")
|
||||
return redirect(url_for(".login"))
|
||||
else:
|
||||
return render_template('auth/login.html')
|
0
realms/modules/wiki/__init__.py
Normal file
0
realms/modules/wiki/__init__.py
Normal file
5
realms/modules/wiki/tests.py
Normal file
5
realms/modules/wiki/tests.py
Normal file
|
@ -0,0 +1,5 @@
|
|||
import realms
|
||||
|
||||
c = realms.app.test_client()
|
||||
print c.get('/wiki/_create')
|
||||
print c.get('/wiki/_create/blah')
|
122
realms/modules/wiki/views.py
Normal file
122
realms/modules/wiki/views.py
Normal file
|
@ -0,0 +1,122 @@
|
|||
from flask import g, render_template, request, redirect, Blueprint, flash, url_for
|
||||
from flask.ext.login import login_required
|
||||
from realms import app, redirect_url, config
|
||||
from realms.lib.util import to_canonical, remove_ext
|
||||
from realms.lib.wiki import Wiki
|
||||
from realms.models import Site
|
||||
|
||||
blueprint = Blueprint('wiki', __name__)
|
||||
|
||||
|
||||
@blueprint.route("/wiki/_new/", methods=['GET', 'POST'])
|
||||
@login_required
|
||||
def new_wiki():
|
||||
if request.method == 'POST':
|
||||
wiki_name = to_canonical(request.form['name'])
|
||||
|
||||
if Wiki.is_registered(wiki_name):
|
||||
flash("Site already exists")
|
||||
return redirect(redirect_url())
|
||||
else:
|
||||
s = Site()
|
||||
s.create(name=wiki_name, repo=wiki_name, founder=g.current_user.get('id'))
|
||||
return redirect('http://%s.%s' % (wiki_name, config.HOSTNAME))
|
||||
else:
|
||||
return render_template('wiki/new.html')
|
||||
|
||||
|
||||
@blueprint.route("/wiki/_commit/<sha>/<name>")
|
||||
def commit_sha(name, sha):
|
||||
cname = to_canonical(name)
|
||||
|
||||
data = g.current_wiki.get_page(cname, sha=sha)
|
||||
if data:
|
||||
return render_template('wiki/page.html', name=name, page=data, commit=sha)
|
||||
else:
|
||||
return redirect(url_for('.create', name=cname))
|
||||
|
||||
|
||||
@blueprint.route("/wiki/_compare/<name>/<regex('[^.]+'):fsha><regex('\.{2,3}'):dots><regex('.+'):lsha>")
|
||||
def compare(name, fsha, dots, lsha):
|
||||
diff = g.current_wiki.compare(name, fsha, lsha)
|
||||
return render_template('wiki/compare.html', name=name, diff=diff, old=fsha, new=lsha)
|
||||
|
||||
|
||||
@blueprint.route("/wiki/_revert", methods=['POST'])
|
||||
def revert():
|
||||
if request.method == 'POST':
|
||||
name = request.form.get('name')
|
||||
commit = request.form.get('commit')
|
||||
cname = to_canonical(name)
|
||||
g.current_wiki.revert_page(name, commit, message="Reverting %s" % cname, username=g.current_user.get('username'))
|
||||
flash('Page reverted', 'success')
|
||||
return redirect(url_for('.page', name=cname))
|
||||
|
||||
@blueprint.route("/wiki/_history/<name>")
|
||||
def history(name):
|
||||
history = g.current_wiki.get_history(name)
|
||||
return render_template('wiki/history.html', name=name, history=history)
|
||||
|
||||
|
||||
@blueprint.route("/wiki/_edit/<name>", methods=['GET', 'POST'])
|
||||
def edit(name):
|
||||
data = g.current_wiki.get_page(name)
|
||||
cname = to_canonical(name)
|
||||
if request.method == 'POST':
|
||||
edit_cname = to_canonical(request.form['name'])
|
||||
if edit_cname.lower() != cname.lower():
|
||||
g.current_wiki.rename_page(cname, edit_cname)
|
||||
g.current_wiki.write_page(edit_cname,
|
||||
request.form['content'],
|
||||
message=request.form['message'],
|
||||
username=g.current_user.get('username'))
|
||||
return redirect(url_for('.page', name=edit_cname))
|
||||
else:
|
||||
if data:
|
||||
name = remove_ext(data['name'])
|
||||
content = data['data']
|
||||
return render_template('wiki/edit.html', name=name, content=content)
|
||||
else:
|
||||
return redirect(url_for('.create', name=cname))
|
||||
|
||||
|
||||
@blueprint.route("/wiki/_delete/<name>", methods=['POST'])
|
||||
@login_required
|
||||
def delete(name):
|
||||
pass
|
||||
|
||||
|
||||
@blueprint.route("/wiki/_create/", defaults={'name': None}, methods=['GET', 'POST'])
|
||||
@blueprint.route("/wiki/_create/<name>", methods=['GET', 'POST'])
|
||||
def create(name):
|
||||
cname = ""
|
||||
if name:
|
||||
cname = to_canonical(name)
|
||||
if g.current_wiki.get_page(cname):
|
||||
# Page exists, edit instead
|
||||
return redirect(url_for('.edit', name=cname))
|
||||
|
||||
if request.method == 'POST':
|
||||
g.current_wiki.write_page(request.form['name'],
|
||||
request.form['content'],
|
||||
message=request.form['message'],
|
||||
create=True,
|
||||
username=g.current_user.get('username'))
|
||||
return redirect(url_for('.page', name=cname))
|
||||
else:
|
||||
return render_template('wiki/edit.html', name=cname, content="")
|
||||
|
||||
|
||||
@blueprint.route("/wiki/", defaults={'name': 'home'})
|
||||
@blueprint.route("/wiki/<name>")
|
||||
def page(name):
|
||||
cname = to_canonical(name)
|
||||
if cname != name:
|
||||
return redirect(url_for('.page', name=cname))
|
||||
|
||||
data = g.current_wiki.get_page(cname)
|
||||
|
||||
if data:
|
||||
return render_template('wiki/page.html', name=cname, page=data)
|
||||
else:
|
||||
return redirect(url_for('.create', name=cname))
|
|
@ -7,7 +7,7 @@
|
|||
<meta name="author" content="">
|
||||
|
||||
<title>Realms</title>
|
||||
|
||||
<link rel="shortcut icon" href="{{ url_for('static', filename='img/favicon.ico') }}">
|
||||
<link href="/static/css/bootstrap/spacelab.css" rel="stylesheet">
|
||||
<link href="/static/css/font-awesome.min.css" rel="stylesheet">
|
||||
<link href="/static/vendor/highlightjs/styles/github.css" rel="stylesheet">
|
||||
|
|
|
@ -2,6 +2,7 @@ Flask==0.10.1
|
|||
Flask-Assets==0.8
|
||||
Flask-Bcrypt==0.5.2
|
||||
Flask-Login==0.2.7
|
||||
Flask-Script==0.6.3
|
||||
beautifulsoup4==4.3.2
|
||||
boto==2.17.0
|
||||
closure==20121212
|
||||
|
|
Loading…
Reference in a new issue