rophako/rophako/app.py

161 lines
4.8 KiB
Python

#!/usr/bin/env python
"""Flask app for Rophako."""
from flask import Flask, g, request, session, render_template, send_file, abort
from flask_sslify import SSLify
import jinja2
import os.path
import time
import sys
# Get the Flask app object ready right away so other modules can import it
# without getting a circular import error.
app = Flask(__name__,
static_url_path="/.static",
)
# We use a custom Jinja loader to support multiple template paths for custom
# and default templates. The base list of template paths to check includes
# your custom path (from config.SITE_ROOT), the "rophako/www" path for normal
# pages, and then the blueprint paths for all imported plugins. This list will
# be extended while blueprints are being loaded and passed in below to the
# jinja2.ChoiceLoader.
BLUEPRINT_PATHS = []
import config
from rophako import __version__
from rophako.plugin import load_plugin
import rophako.utils
app.DEBUG = config.DEBUG
app.secret_key = config.SECRET_KEY
# Security?
if config.FORCE_SSL:
app.config['SESSION_COOKIE_SECURE'] = True
sslify = SSLify(app)
# Load all the built-in essential plugins.
load_plugin("rophako.modules.admin")
load_plugin("rophako.modules.account")
# Custom Jinja handler to support custom- and default-template folders for
# rendering templates.
template_paths = [
config.SITE_ROOT, # Site specific.
"rophako/www", # Default/fall-back
]
template_paths.extend(BLUEPRINT_PATHS)
app.jinja_loader = jinja2.ChoiceLoader([ jinja2.FileSystemLoader(x) for x in template_paths])
app.jinja_env.globals["csrf_token"] = rophako.utils.generate_csrf_token
app.jinja_env.globals["include_page"] = rophako.utils.include
# Preload the emoticon data.
import rophako.model.emoticons as Emoticons
Emoticons.load_theme()
@app.before_request
def before_request():
"""Called before all requests. Initialize global template variables."""
# Default template vars.
g.info = {
"time": time.time(),
"app": {
"name": "Rophako",
"version": __version__,
"python_version": "{}.{}".format(sys.version_info.major, sys.version_info.minor),
"author": "Noah Petherbridge",
"photo_url": config.PHOTO_ROOT_PUBLIC,
},
"uri": request.path,
"session": {
"login": False, # Not logged in, until proven otherwise.
"username": "guest",
"uid": 0,
"name": "Guest",
"role": "user",
}
}
# Default session vars.
if not "login" in session:
session.update(g.info["session"])
# CSRF protection.
if request.method == "POST":
token = session.pop("_csrf", None)
if not token or str(token) != str(request.form.get("token")):
abort(403)
# Refresh their login status from the DB.
if session["login"]:
import rophako.model.user as User
if not User.exists(uid=session["uid"]):
# Weird! Log them out.
from rophako.modules.account import logout
logout()
return
db = User.get_user(uid=session["uid"])
session["username"] = db["username"]
session["name"] = db["name"]
session["role"] = db["role"]
# Copy session params into g.info. The only people who should touch the
# session are the login/out pages.
for key in session:
g.info["session"][key] = session[key]
@app.context_processor
def after_request():
"""Called just before render_template. Inject g.info into the template vars."""
return g.info
@app.route("/<path:path>")
def catchall(path):
"""The catch-all path handler. If it exists in the www folders, it's sent,
otherwise we give the 404 error page."""
# Search for this file.
for root in [config.SITE_ROOT, "rophako/www"]:
abspath = os.path.abspath("{}/{}".format(root, path))
if os.path.isfile(abspath):
return send_file(abspath)
# The exact file wasn't found, look for some extensions and index pages.
suffixes = [
".html",
"/index.html",
".md", # Markdown formatted pages.
"/index.md",
]
for suffix in suffixes:
if not "." in path and os.path.isfile(abspath + suffix):
# HTML, or Markdown?
if suffix.endswith(".html"):
return rophako.utils.template(path + suffix)
else:
return rophako.utils.markdown_template(abspath + suffix)
return not_found("404")
@app.route("/")
def index():
return catchall("index")
@app.errorhandler(404)
def not_found(error):
return render_template('errors/404.html', **g.info), 404
@app.errorhandler(403)
def forbidden(error):
return render_template('errors/403.html', **g.info), 403