realms-wiki/realms/__init__.py

230 lines
7.1 KiB
Python
Raw Normal View History

2014-10-07 00:07:20 +03:00
import sys
2014-01-14 01:07:13 +02:00
# Set default encoding to UTF-8
reload(sys)
# noinspection PyUnresolvedReferences
sys.setdefaultencoding('utf-8')
2013-10-02 04:50:48 +03:00
import time
2014-01-17 01:46:22 +02:00
import json
2014-01-14 01:07:13 +02:00
import httplib
import traceback
import click
2014-08-30 18:06:12 +03:00
from flask import Flask, request, render_template, url_for, redirect, g
2014-11-10 18:54:46 +02:00
from flask.ext.elastic import Elastic
2014-08-30 18:06:12 +03:00
from flask.ext.cache import Cache
from flask.ext.login import LoginManager, current_user
2014-10-09 06:42:29 +03:00
from flask.ext.sqlalchemy import SQLAlchemy, declarative_base, Model, _QueryProperty
2014-08-30 18:06:12 +03:00
from flask.ext.assets import Environment, Bundle
2013-10-03 17:58:07 +03:00
from werkzeug.routing import BaseConverter
2014-01-14 01:07:13 +02:00
from werkzeug.exceptions import HTTPException
2013-10-05 00:42:45 +03:00
from .lib.util import to_canonical, remove_ext, mkdir_safe, gravatar_url, to_dict
from .lib.hook import HookModelMeta
from .lib.util import is_su, in_virtualenv
from .version import __version__
2013-12-09 22:24:22 +02:00
2013-11-08 20:20:40 +02:00
class Application(Flask):
2013-12-03 01:50:19 +02:00
def __call__(self, environ, start_response):
path_info = environ.get('PATH_INFO')
2013-11-08 20:20:40 +02:00
2013-12-03 01:50:19 +02:00
if path_info and len(path_info) > 1 and path_info.endswith('/'):
environ['PATH_INFO'] = path_info[:-1]
2013-10-05 00:42:45 +03:00
2013-12-03 01:50:19 +02:00
scheme = environ.get('HTTP_X_SCHEME')
2013-10-05 00:42:45 +03:00
2013-12-03 01:50:19 +02:00
if scheme:
environ['wsgi.url_scheme'] = scheme
2013-10-05 00:42:45 +03:00
2013-12-03 01:50:19 +02:00
real_ip = environ.get('HTTP_X_REAL_IP')
2013-10-05 00:42:45 +03:00
2013-12-03 01:50:19 +02:00
if real_ip:
environ['REMOTE_ADDR'] = real_ip
2013-10-05 00:42:45 +03:00
2013-12-03 01:50:19 +02:00
return super(Application, self).__call__(environ, start_response)
2013-10-05 00:52:41 +03:00
2013-12-03 22:09:57 +02:00
def discover(self):
2014-08-30 18:06:12 +03:00
import_name = 'realms.modules'
fromlist = (
2013-12-03 22:09:57 +02:00
'assets',
'commands',
2014-08-20 18:28:25 +03:00
'models',
2013-12-03 22:09:57 +02:00
'views',
2014-10-09 06:42:29 +03:00
'hooks'
2013-12-03 22:09:57 +02:00
)
start_time = time.time()
2014-08-30 18:06:12 +03:00
__import__(import_name, fromlist=fromlist)
2013-12-03 22:09:57 +02:00
for module_name in self.config['MODULES']:
2014-08-30 18:06:12 +03:00
sources = __import__('%s.%s' % (import_name, module_name), fromlist=fromlist)
2013-12-03 22:09:57 +02:00
2014-10-22 00:06:27 +03:00
if hasattr(sources, 'init'):
sources.init(self)
2013-12-03 22:09:57 +02:00
# Blueprint
if hasattr(sources, 'views'):
2014-10-22 00:06:27 +03:00
self.register_blueprint(sources.views.blueprint, url_prefix=self.config['RELATIVE_PATH'])
2013-12-03 22:09:57 +02:00
# Click
2013-12-03 22:09:57 +02:00
if hasattr(sources, 'commands'):
cli.add_command(sources.commands.cli, name=module_name)
2013-12-03 22:09:57 +02:00
2014-10-22 00:06:27 +03:00
# Hooks
if hasattr(sources, 'hooks'):
if hasattr(sources.hooks, 'before_request'):
self.before_request(sources.hooks.before_request)
if hasattr(sources.hooks, 'before_first_request'):
self.before_first_request(sources.hooks.before_first_request)
# print >> sys.stderr, ' * Ready in %.2fms' % (1000.0 * (time.time() - start_time))
2013-12-03 22:09:57 +02:00
2014-01-17 01:46:22 +02:00
def make_response(self, rv):
if rv is None:
rv = '', httplib.NO_CONTENT
elif not isinstance(rv, tuple):
rv = rv,
rv = list(rv)
if isinstance(rv[0], (list, dict)):
rv[0] = self.response_class(json.dumps(rv[0]), mimetype='application/json')
return super(Application, self).make_response(tuple(rv))
2013-10-05 00:52:41 +03:00
2014-08-30 18:06:12 +03:00
class Assets(Environment):
default_filters = {'js': 'rjsmin', 'css': 'cleancss'}
2014-08-30 18:06:12 +03:00
default_output = {'js': 'assets/%(version)s.js', 'css': 'assets/%(version)s.css'}
def register(self, name, *args, **kwargs):
ext = args[0].split('.')[-1]
filters = kwargs.get('filters', self.default_filters[ext])
output = kwargs.get('output', self.default_output[ext])
return super(Assets, self).register(name, Bundle(*args, filters=filters, output=output))
2014-08-30 18:06:12 +03:00
2013-10-03 17:58:07 +03:00
class RegexConverter(BaseConverter):
""" Enables Regex matching on endpoints
2013-10-10 00:35:06 +03:00
"""
2013-10-03 17:58:07 +03:00
def __init__(self, url_map, *items):
super(RegexConverter, self).__init__(url_map)
self.regex = items[0]
2013-09-26 16:51:15 +03:00
2013-10-05 00:42:45 +03:00
def redirect_url(referrer=None):
if not referrer:
referrer = request.referrer
return request.args.get('next') or referrer or url_for('index')
2013-10-04 05:57:19 +03:00
2013-09-26 16:51:15 +03:00
2014-01-14 01:07:13 +02:00
def error_handler(e):
try:
if isinstance(e, HTTPException):
status_code = e.code
message = e.description if e.description != type(e).description else None
tb = None
else:
status_code = httplib.INTERNAL_SERVER_ERROR
message = None
tb = traceback.format_exc() if current_user.admin else None
2014-01-14 01:07:13 +02:00
if request.is_xhr or request.accept_mimetypes.best in ['application/json', 'text/javascript']:
response = {
'message': message,
2014-08-30 18:06:12 +03:00
'traceback': tb
2014-01-14 01:07:13 +02:00
}
else:
response = render_template('errors/error.html',
title=httplib.responses[status_code],
status_code=status_code,
message=message,
traceback=tb)
except HTTPException as e2:
return error_handler(e2)
return response, status_code
2014-10-22 00:06:27 +03:00
def create_app(config=None):
app = Application(__name__)
app.config.from_object('realms.config')
app.url_map.converters['regex'] = RegexConverter
app.url_map.strict_slashes = False
2014-08-20 18:28:25 +03:00
2014-10-22 00:06:27 +03:00
login_manager.init_app(app)
db.init_app(app)
cache.init_app(app)
assets.init_app(app)
2014-11-10 18:54:46 +02:00
elastic.init_app(app)
2014-02-07 05:40:58 +02:00
2014-10-22 00:06:27 +03:00
for status_code in httplib.responses:
if status_code >= 400:
app.register_error_handler(status_code, error_handler)
2013-12-03 01:50:19 +02:00
2014-10-22 00:06:27 +03:00
@app.before_request
def init_g():
g.assets = dict(css=['main.css'], js=['main.js'])
2014-09-27 01:17:58 +03:00
2014-10-22 00:06:27 +03:00
@app.template_filter('datetime')
def _jinja2_filter_datetime(ts):
return time.strftime('%b %d, %Y %I:%M %p', time.localtime(ts))
2013-12-09 22:24:22 +02:00
2014-10-22 00:06:27 +03:00
@app.errorhandler(404)
def page_not_found(e):
return render_template('errors/404.html'), 404
2014-09-27 01:17:58 +03:00
2014-10-22 00:06:27 +03:00
if app.config['RELATIVE_PATH']:
@app.route("/")
def root():
return redirect(url_for(app.config['ROOT_ENDPOINT']))
2013-12-09 22:24:22 +02:00
2014-10-22 00:06:27 +03:00
app.discover()
2014-09-27 01:17:58 +03:00
2014-10-22 00:06:27 +03:00
# This will be removed at some point
with app.app_context():
db.create_all()
2013-12-03 01:50:19 +02:00
2014-10-22 00:06:27 +03:00
return app
2013-12-03 01:50:19 +02:00
# Init plugins here if possible
2014-10-22 00:06:27 +03:00
login_manager = LoginManager()
2014-08-30 18:06:12 +03:00
2014-10-31 00:59:19 +02:00
db = SQLAlchemy()
2014-10-22 00:06:27 +03:00
cache = Cache()
assets = Assets()
2014-11-10 18:54:46 +02:00
elastic = Elastic()
2014-08-30 18:06:12 +03:00
assets.register('main.js',
'vendor/jquery/dist/jquery.js',
2014-08-30 18:06:12 +03:00
'vendor/components-bootstrap/js/bootstrap.js',
'vendor/handlebars/handlebars.js',
'vendor/js-yaml/dist/js-yaml.js',
'vendor/marked/lib/marked.js',
2014-08-30 18:06:12 +03:00
'js/html-sanitizer-minified.js', # don't minify?
'vendor/highlightjs/highlight.pack.js',
'vendor/parsleyjs/dist/parsley.js',
2014-10-09 23:47:12 +03:00
'vendor/datatables/media/js/jquery.dataTables.js',
'vendor/datatables-plugins/integration/bootstrap/3/dataTables.bootstrap.js',
'js/hbs-helpers.js',
'js/mdr.js',
'js/main.js')
2014-08-30 18:06:12 +03:00
assets.register('main.css',
'vendor/bootswatch-dist/css/bootstrap.css',
'vendor/components-font-awesome/css/font-awesome.css',
'vendor/highlightjs/styles/github.css',
2014-10-09 23:47:12 +03:00
'vendor/datatables-plugins/integration/bootstrap/3/dataTables.bootstrap.css',
'css/style.css')
2014-08-30 18:06:12 +03:00
@click.group()
def cli():
pass