reorg
This commit is contained in:
parent
ba1ec10a34
commit
36cf728862
24 changed files with 181 additions and 488 deletions
|
@ -4,19 +4,21 @@ import time
|
|||
from threading import Lock
|
||||
|
||||
import rethinkdb as rdb
|
||||
from flask import Flask, request, render_template, url_for, redirect, flash
|
||||
from flask import Flask, g, request, render_template, url_for, redirect, flash, session
|
||||
from flask.ctx import _AppCtxGlobals
|
||||
from flask.ext.login import LoginManager, login_required
|
||||
from flask.ext.assets import Environment, Bundle
|
||||
from recaptcha.client import captcha
|
||||
from werkzeug.routing import BaseConverter
|
||||
from werkzeug.utils import cached_property
|
||||
|
||||
from session import RedisSessionInterface
|
||||
import config
|
||||
from wiki import Wiki
|
||||
from util import to_canonical, remove_ext, mkdir_safe, gravatar_url
|
||||
from realms.lib.ratelimit import get_view_rate_limit, ratelimiter
|
||||
from realms.lib.session import RedisSessionInterface
|
||||
from realms.lib.wiki import Wiki
|
||||
from realms.lib.util import to_canonical, remove_ext, mkdir_safe, gravatar_url
|
||||
from realms.lib.services import db
|
||||
from models import Site, User, CurrentUser
|
||||
from ratelimit import get_view_rate_limit, ratelimiter
|
||||
from services import db
|
||||
|
||||
|
||||
# Flask instance container
|
||||
instances = {}
|
||||
|
@ -26,6 +28,17 @@ login_manager = LoginManager()
|
|||
assets = Environment()
|
||||
|
||||
|
||||
class AppCtxGlobals(_AppCtxGlobals):
|
||||
|
||||
@cached_property
|
||||
def current_user(self):
|
||||
return session.get('user') if session.get('user') else {'username': 'Anon'}
|
||||
|
||||
|
||||
class Application(Flask):
|
||||
app_ctx_globals_class = AppCtxGlobals
|
||||
|
||||
|
||||
class SubdomainDispatcher(object):
|
||||
"""
|
||||
Application factory
|
||||
|
@ -90,19 +103,12 @@ def redirect_url(referrer=None):
|
|||
return request.args.get('next') or referrer or url_for('index')
|
||||
|
||||
|
||||
def validate_captcha():
|
||||
response = captcha.submit(
|
||||
request.form['recaptcha_challenge_field'],
|
||||
request.form['recaptcha_response_field'],
|
||||
config.flask['RECAPTCHA_PRIVATE_KEY'],
|
||||
request.remote_addr)
|
||||
return response.is_valid
|
||||
|
||||
|
||||
def format_subdomain(s):
|
||||
if not config.repos['enable_subrepos']:
|
||||
return ""
|
||||
s = s.lower()
|
||||
s = to_canonical(s)
|
||||
if s in ['www', 'api']:
|
||||
if s in config.repos['forbidden_subrepos']:
|
||||
# Not allowed
|
||||
s = ""
|
||||
return s
|
||||
|
@ -116,7 +122,7 @@ def make_app(subdomain):
|
|||
|
||||
|
||||
def create_app(subdomain=None):
|
||||
app = Flask(__name__)
|
||||
app = Application(__name__)
|
||||
app.config.update(config.flask)
|
||||
app.debug = (config.ENV is not 'PROD')
|
||||
app.secret_key = config.secret_key
|
||||
|
@ -199,7 +205,7 @@ def create_app(subdomain=None):
|
|||
def home():
|
||||
return redirect(url_for('root'))
|
||||
|
||||
@app.route("/account/")
|
||||
@app.route("/_account/")
|
||||
@login_required
|
||||
def account():
|
||||
return render_template('account/index.html')
|
||||
|
@ -215,13 +221,13 @@ def create_app(subdomain=None):
|
|||
return redirect(redirect_url())
|
||||
else:
|
||||
s = Site()
|
||||
s.create(name=wiki_name, repo=wiki_name, founder=CurrentUser.get('id'))
|
||||
s.create(name=wiki_name, repo=wiki_name, founder=g.current_user.get('id'))
|
||||
instances.pop(wiki_name, None)
|
||||
return redirect('http://%s.%s' % (wiki_name, config.hostname))
|
||||
else:
|
||||
return render_template('_new/index.html')
|
||||
|
||||
@app.route("/logout/")
|
||||
@app.route("/_logout/")
|
||||
def logout():
|
||||
User.logout()
|
||||
return redirect(url_for('root'))
|
||||
|
@ -234,7 +240,7 @@ def create_app(subdomain=None):
|
|||
if data:
|
||||
return render_template('page/page.html', name=name, page=data, commit=sha)
|
||||
else:
|
||||
return redirect('/create/'+cname)
|
||||
return redirect('/_create/'+cname)
|
||||
|
||||
@app.route("/_compare/<name>/<regex('[^.]+'):fsha><regex('\.{2,3}'):dots><regex('.+'):lsha>")
|
||||
def compare(name, fsha, dots, lsha):
|
||||
|
@ -247,11 +253,11 @@ def create_app(subdomain=None):
|
|||
name = request.form.get('name')
|
||||
commit = request.form.get('commit')
|
||||
cname = to_canonical(name)
|
||||
w.revert_page(name, commit, message="Reverting %s" % cname, username=CurrentUser.get('username'))
|
||||
w.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'])
|
||||
@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')):
|
||||
|
@ -262,14 +268,14 @@ def create_app(subdomain=None):
|
|||
else:
|
||||
return render_template('account/register.html')
|
||||
|
||||
@app.route("/login", methods=['GET', 'POST'])
|
||||
@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")
|
||||
return redirect("/_login")
|
||||
else:
|
||||
return render_template('account/login.html')
|
||||
|
||||
|
@ -288,7 +294,7 @@ def create_app(subdomain=None):
|
|||
w.rename_page(cname, edit_cname)
|
||||
w.write_page(edit_cname, request.form['content'],
|
||||
message=request.form['message'],
|
||||
username=CurrentUser.get('username'))
|
||||
username=g.current_user.get('username'))
|
||||
return redirect("/" + edit_cname)
|
||||
else:
|
||||
if data:
|
||||
|
@ -296,7 +302,7 @@ def create_app(subdomain=None):
|
|||
content = data['data']
|
||||
return render_template('page/edit.html', name=name, content=content)
|
||||
else:
|
||||
return redirect('/create/'+cname)
|
||||
return redirect('/_create/'+cname)
|
||||
|
||||
@app.route("/_delete/<name>", methods=['POST'])
|
||||
@login_required
|
||||
|
@ -316,7 +322,7 @@ def create_app(subdomain=None):
|
|||
w.write_page(request.form['name'], request.form['content'],
|
||||
message=request.form['message'],
|
||||
create=True,
|
||||
username=CurrentUser.get('username'))
|
||||
username=g.current_user.get('username'))
|
||||
return redirect("/" + cname)
|
||||
else:
|
||||
return render_template('page/edit.html', name=cname, content="")
|
||||
|
@ -331,6 +337,6 @@ def create_app(subdomain=None):
|
|||
if data:
|
||||
return render_template('page/page.html', name=cname, page=data)
|
||||
else:
|
||||
return redirect('/create/'+cname)
|
||||
return redirect('/_create/'+cname)
|
||||
|
||||
return app
|
0
realms/lib/__init__.py
Normal file
0
realms/lib/__init__.py
Normal file
|
@ -1,7 +1,6 @@
|
|||
import rethinkdb as rdb
|
||||
import redis
|
||||
|
||||
import config
|
||||
from realms import config
|
||||
|
||||
|
||||
# Default DB connection
|
128
realms/lib/util.py
Normal file
128
realms/lib/util.py
Normal file
|
@ -0,0 +1,128 @@
|
|||
import re
|
||||
import os
|
||||
import hashlib
|
||||
import json
|
||||
|
||||
from flask import request
|
||||
from recaptcha.client import captcha
|
||||
|
||||
from realms import config
|
||||
from realms.lib.services import cache
|
||||
|
||||
|
||||
def cache_it(fn):
|
||||
def wrap(*args, **kw):
|
||||
key = "%s:%s" % (args[0].table, args[1])
|
||||
data = cache.get(key)
|
||||
# Assume strings are JSON encoded
|
||||
try:
|
||||
data = json.loads(data)
|
||||
except TypeError:
|
||||
pass
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
if data is not None:
|
||||
return data
|
||||
else:
|
||||
data = fn(*args)
|
||||
print data
|
||||
ret = data
|
||||
if data is None:
|
||||
data = ''
|
||||
if not isinstance(data, basestring):
|
||||
try:
|
||||
data = json.dumps(data, separators=(',', ':'))
|
||||
except TypeError:
|
||||
pass
|
||||
cache.set(key, data)
|
||||
return ret
|
||||
return wrap
|
||||
|
||||
|
||||
def to_json(res, first=False):
|
||||
"""
|
||||
Jsonify query result.
|
||||
"""
|
||||
res = to_dict(res, first)
|
||||
return json.dumps(res, separators=(',', ':'))
|
||||
|
||||
|
||||
def to_dict(cur, first=False):
|
||||
if not cur:
|
||||
return None
|
||||
ret = []
|
||||
for row in cur:
|
||||
ret.append(row)
|
||||
if ret and first:
|
||||
return ret[0]
|
||||
else:
|
||||
return ret
|
||||
|
||||
def validate_captcha():
|
||||
response = captcha.submit(
|
||||
request.form['recaptcha_challenge_field'],
|
||||
request.form['recaptcha_response_field'],
|
||||
config.flask['RECAPTCHA_PRIVATE_KEY'],
|
||||
request.remote_addr)
|
||||
return response.is_valid
|
||||
|
||||
|
||||
def mkdir_safe(path):
|
||||
if path and not(os.path.exists(path)):
|
||||
os.makedirs(path)
|
||||
return path
|
||||
|
||||
|
||||
def extract_path(file_path):
|
||||
if not file_path:
|
||||
return None
|
||||
last_slash = file_path.rindex("/")
|
||||
if last_slash:
|
||||
return file_path[0, last_slash]
|
||||
|
||||
|
||||
def clean_path(path):
|
||||
if path:
|
||||
if path[0] != '/':
|
||||
path.insert(0, '/')
|
||||
return re.sub(r"//+", '/', path)
|
||||
|
||||
|
||||
def extract_name(file_path):
|
||||
if file_path[-1] == "/":
|
||||
return None
|
||||
return os.path.basename(file_path)
|
||||
|
||||
|
||||
def remove_ext(path):
|
||||
return os.path.splitext(path)[0]
|
||||
|
||||
|
||||
def clean_url(url):
|
||||
if not url:
|
||||
return url
|
||||
|
||||
url = url.replace('%2F', '/')
|
||||
url = re.sub(r"^/+", "", url)
|
||||
return re.sub(r"//+", '/', url)
|
||||
|
||||
|
||||
def to_canonical(s):
|
||||
"""
|
||||
Double space -> single dash
|
||||
Double dash -> single dash
|
||||
Remove all non alphanumeric and dash
|
||||
Limit to first 64 chars
|
||||
"""
|
||||
s = s.encode('ascii', 'ignore')
|
||||
s = str(s)
|
||||
s = re.sub(r"\s\s*", "-", s)
|
||||
s = re.sub(r"\-\-+", "-", s)
|
||||
s = re.sub(r"[^a-zA-Z0-9\-]", "", s)
|
||||
s = s[:64]
|
||||
return s
|
||||
|
||||
|
||||
def gravatar_url(email):
|
||||
return "https://www.gravatar.com/avatar/" + hashlib.md5(email).hexdigest()
|
|
@ -7,8 +7,8 @@ import ghdiff
|
|||
import gittle.utils
|
||||
from gittle import Gittle
|
||||
from dulwich.repo import NotGitRepository
|
||||
|
||||
from util import to_canonical, escape_repl, unescape_repl
|
||||
from werkzeug.utils import escape, unescape
|
||||
from util import to_canonical
|
||||
from models import Site
|
||||
|
||||
|
||||
|
@ -80,6 +80,14 @@ class Wiki():
|
|||
|
||||
def write_page(self, name, content, message=None, create=False, username=None, email=None):
|
||||
|
||||
def escape_repl(m):
|
||||
if m.group(1):
|
||||
return "```" + escape(m.group(1)) + "```"
|
||||
|
||||
def unescape_repl(m):
|
||||
if m.group(1):
|
||||
return "```" + unescape(m.group(1)) + "```"
|
||||
|
||||
# prevents p tag from being added, we remove this later
|
||||
content = '<div>' + content + '</div>'
|
||||
content = re.sub(r"```(.*?)```", escape_repl, content, flags=re.DOTALL)
|
||||
|
@ -93,9 +101,11 @@ class Wiki():
|
|||
|
||||
# post processing to fix errors
|
||||
content = content[5:-6]
|
||||
|
||||
# FIXME this is for block quotes, doesn't work for double ">"
|
||||
content = re.sub(r"(\n>)", "\n>", content)
|
||||
content = re.sub(r"(^>)", ">", content)
|
||||
|
||||
content = re.sub(r"```(.*?)```", unescape_repl, content, flags=re.DOTALL)
|
||||
|
||||
filename = self.cname_to_filename(to_canonical(name))
|
||||
|
@ -129,7 +139,7 @@ class Wiki():
|
|||
files=[old_name])
|
||||
|
||||
def get_page(self, name, sha='HEAD'):
|
||||
commit = gittle.utils.git.commit_info(self.repo[sha])
|
||||
# commit = gittle.utils.git.commit_info(self.repo[sha])
|
||||
name = self.cname_to_filename(name)
|
||||
try:
|
||||
return self.repo.get_commit_files(sha, paths=[name]).get(name)
|
|
@ -1,60 +1,9 @@
|
|||
import json
|
||||
import rethinkdb as rdb
|
||||
import bcrypt
|
||||
from flask import session, flash
|
||||
from flask.ext.login import login_user, logout_user
|
||||
from util import gravatar_url
|
||||
from services import db, cache
|
||||
|
||||
|
||||
def to_json(res, first=False):
|
||||
"""
|
||||
Jsonify query result.
|
||||
"""
|
||||
res = to_dict(res, first)
|
||||
return json.dumps(res, separators=(',',':'))
|
||||
|
||||
|
||||
def to_dict(cur, first=False):
|
||||
if not cur:
|
||||
return None
|
||||
ret = []
|
||||
for row in cur:
|
||||
ret.append(row)
|
||||
if ret and first:
|
||||
return ret[0]
|
||||
else:
|
||||
return ret
|
||||
|
||||
|
||||
def cache_it(fn):
|
||||
def wrap(*args, **kw):
|
||||
key = "%s:%s" % (args[0].table, args[1])
|
||||
data = cache.get(key)
|
||||
# Assume strings are JSON encoded
|
||||
try:
|
||||
data = json.loads(data)
|
||||
except TypeError:
|
||||
pass
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
if data is not None:
|
||||
return data
|
||||
else:
|
||||
data = fn(*args)
|
||||
print data
|
||||
ret = data
|
||||
if data is None:
|
||||
data = ''
|
||||
if not isinstance(data, basestring):
|
||||
try:
|
||||
data = json.dumps(data, separators=(',', ':'))
|
||||
except TypeError:
|
||||
pass
|
||||
cache.set(key, data)
|
||||
return ret
|
||||
return wrap
|
||||
from realms.lib.util import gravatar_url, to_dict, cache_it
|
||||
from realms.lib.services import db
|
||||
|
||||
|
||||
class BaseModel():
|
||||
|
@ -162,7 +111,7 @@ class User(BaseModel):
|
|||
User.login(u.id)
|
||||
|
||||
@classmethod
|
||||
def login(cls, id, data=None):
|
||||
def login(cls, id):
|
||||
login_user(CurrentUser(id), True)
|
||||
|
||||
@classmethod
|
||||
|
|
|
@ -1,95 +0,0 @@
|
|||
import re
|
||||
import os
|
||||
import hashlib
|
||||
|
||||
|
||||
def escape_repl(m):
|
||||
print "group 0"
|
||||
print m.group(0)
|
||||
print "group 1"
|
||||
print m.group(1)
|
||||
if m.group(1):
|
||||
return "```" + escape_html(m.group(1)) + "```"
|
||||
|
||||
|
||||
def unescape_repl(m):
|
||||
if m.group(1):
|
||||
return "```" + unescape_html(m.group(1)) + "```"
|
||||
|
||||
|
||||
def escape_html(s):
|
||||
s = s.replace("&", '&')
|
||||
s = s.replace("<", '<')
|
||||
s = s.replace(">", '>')
|
||||
s = s.replace('"', '"')
|
||||
s = s.replace("'", ''')
|
||||
return s
|
||||
|
||||
|
||||
def unescape_html(s):
|
||||
s = s.replace('&', "&")
|
||||
s = s.replace('<', "<")
|
||||
s = s.replace('>', ">")
|
||||
s = s.replace('"', '"')
|
||||
s = s.replace(''', "'")
|
||||
return s
|
||||
|
||||
|
||||
def mkdir_safe(path):
|
||||
if path and not(os.path.exists(path)):
|
||||
os.makedirs(path)
|
||||
return path
|
||||
|
||||
|
||||
def extract_path(file_path):
|
||||
if not file_path:
|
||||
return None
|
||||
last_slash = file_path.rindex("/")
|
||||
if last_slash:
|
||||
return file_path[0, last_slash]
|
||||
|
||||
|
||||
def clean_path(path):
|
||||
if path:
|
||||
if path[0] != '/':
|
||||
path.insert(0, '/')
|
||||
return re.sub(r"//+", '/', path)
|
||||
|
||||
|
||||
def extract_name(file_path):
|
||||
if file_path[-1] == "/":
|
||||
return None
|
||||
return os.path.basename(file_path)
|
||||
|
||||
|
||||
def remove_ext(path):
|
||||
return os.path.splitext(path)[0]
|
||||
|
||||
|
||||
def clean_url(url):
|
||||
if not url:
|
||||
return url
|
||||
|
||||
url = url.replace('%2F', '/')
|
||||
url = re.sub(r"^/+", "", url)
|
||||
return re.sub(r"//+", '/', url)
|
||||
|
||||
|
||||
def to_canonical(s):
|
||||
"""
|
||||
Double space -> single dash
|
||||
Double dash -> single dash
|
||||
Remove all non alphanumeric and dash
|
||||
Limit to first 64 chars
|
||||
"""
|
||||
s = s.encode('ascii', 'ignore')
|
||||
s = str(s)
|
||||
s = re.sub(r"\s\s*", "-", s)
|
||||
s = re.sub(r"\-\-+", "-", s)
|
||||
s = re.sub(r"[^a-zA-Z0-9\-]", "", s)
|
||||
s = s[:64]
|
||||
return s
|
||||
|
||||
|
||||
def gravatar_url(email):
|
||||
return "https://www.gravatar.com/avatar/" + hashlib.md5(email).hexdigest()
|
Loading…
Add table
Add a link
Reference in a new issue