diff --git a/config-sample.py b/config-sample.py index 6946b91..ae1803c 100644 --- a/config-sample.py +++ b/config-sample.py @@ -5,6 +5,7 @@ import os _basedir = os.path.abspath(os.path.dirname(__file__)) import datetime +from rophako.plugin import load_plugin DEBUG = True @@ -116,3 +117,21 @@ COMMENT_TIME_FORMAT = "%A, %B %d %Y @ %I:%M %p" # We use Gravatar for comments if the user provides an e-mail address. Specify # the URL to a fallback image to use in case they don't have a gravatar. COMMENT_DEFAULT_AVATAR = "" + +################################################################################ +## Enabled Plugins ## +################################################################################ + +# Place all the load_plugin calls down here. Some of the plugins need to refer +# to config params above so those need to get declared before the plugin begins +# to initialize itself. +# +# Some plugins will automatically load others as dependencies, i.e. the blog +# and photo will load comment, and comment will load emoticons. But it doesn't +# hurt to list them all out here to be explicit anyway. + +load_plugin("rophako.modules.blog") +load_plugin("rophako.modules.photo") +load_plugin("rophako.modules.comment") +load_plugin("rophako.modules.emoticons") +load_plugin("rophako.modules.contact") \ No newline at end of file diff --git a/rophako/modules/kirsle_legacy.py b/kirsle_legacy.py similarity index 99% rename from rophako/modules/kirsle_legacy.py rename to kirsle_legacy.py index 6af8e14..a5164b8 100644 --- a/rophako/modules/kirsle_legacy.py +++ b/kirsle_legacy.py @@ -8,7 +8,7 @@ import os import json import config -from rophako import app +from rophako.app import app from rophako.utils import template, login_required import rophako.model.blog as Blog import rophako.jsondb as JsonDB diff --git a/rophako/app.py b/rophako/app.py index 29c8714..e386f57 100644 --- a/rophako/app.py +++ b/rophako/app.py @@ -1,3 +1,7 @@ +#!/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 @@ -5,13 +9,25 @@ import os.path import time import sys -import config -from rophako import __version__ -import rophako.utils - +# 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 @@ -20,28 +36,32 @@ if config.FORCE_SSL: app.config['SESSION_COOKIE_SECURE'] = True sslify = SSLify(app) -# Load all the blueprints! -from rophako.modules.admin import mod as AdminModule -from rophako.modules.account import mod as AccountModule -from rophako.modules.blog import mod as BlogModule -from rophako.modules.photo import mod as PhotoModule -from rophako.modules.comment import mod as CommentModule -from rophako.modules.emoticons import mod as EmoticonsModule -from rophako.modules.contact import mod as ContactModule -app.register_blueprint(AdminModule) -app.register_blueprint(AccountModule) -app.register_blueprint(BlogModule) -app.register_blueprint(PhotoModule) -app.register_blueprint(CommentModule) -app.register_blueprint(EmoticonsModule) -app.register_blueprint(ContactModule) +# Load all the built-in essential plugins. +load_plugin("rophako.modules.admin") +load_plugin("rophako.modules.account") +# from rophako.modules.admin import mod as AdminModule +# from rophako.modules.account import mod as AccountModule +# from rophako.modules.blog import mod as BlogModule +# from rophako.modules.photo import mod as PhotoModule +# from rophako.modules.comment import mod as CommentModule +# from rophako.modules.emoticons import mod as EmoticonsModule +# from rophako.modules.contact import mod as ContactModule +# app.register_blueprint(AdminModule) +# app.register_blueprint(AccountModule) +# app.register_blueprint(BlogModule) +# app.register_blueprint(PhotoModule) +# app.register_blueprint(CommentModule) +# app.register_blueprint(EmoticonsModule) +# app.register_blueprint(ContactModule) # Custom Jinja handler to support custom- and default-template folders for # rendering templates. -app.jinja_loader = jinja2.ChoiceLoader([ - jinja2.FileSystemLoader(config.SITE_ROOT), # Site specific. - jinja2.FileSystemLoader("rophako/www"), # Default/fall-back -]) +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 @@ -152,9 +172,4 @@ def not_found(error): @app.errorhandler(403) def forbidden(error): - return render_template('errors/403.html', **g.info), 403 - - -# Domain specific endpoints. -if config.SITE_NAME == "kirsle.net": - import rophako.modules.kirsle_legacy \ No newline at end of file + return render_template('errors/403.html', **g.info), 403 \ No newline at end of file diff --git a/rophako/modules/account.py b/rophako/modules/account/__init__.py similarity index 100% rename from rophako/modules/account.py rename to rophako/modules/account/__init__.py diff --git a/rophako/www/account/login.html b/rophako/modules/account/templates/account/login.html similarity index 100% rename from rophako/www/account/login.html rename to rophako/modules/account/templates/account/login.html diff --git a/rophako/www/account/setup.html b/rophako/modules/account/templates/account/setup.html similarity index 100% rename from rophako/www/account/setup.html rename to rophako/modules/account/templates/account/setup.html diff --git a/rophako/modules/admin.py b/rophako/modules/admin/__init__.py similarity index 100% rename from rophako/modules/admin.py rename to rophako/modules/admin/__init__.py diff --git a/rophako/www/admin/edit_user.html b/rophako/modules/admin/templates/admin/edit_user.html similarity index 100% rename from rophako/www/admin/edit_user.html rename to rophako/modules/admin/templates/admin/edit_user.html diff --git a/rophako/www/admin/index.html b/rophako/modules/admin/templates/admin/index.html similarity index 100% rename from rophako/www/admin/index.html rename to rophako/modules/admin/templates/admin/index.html diff --git a/rophako/www/admin/users.html b/rophako/modules/admin/templates/admin/users.html similarity index 100% rename from rophako/www/admin/users.html rename to rophako/modules/admin/templates/admin/users.html diff --git a/rophako/modules/blog.py b/rophako/modules/blog/__init__.py similarity index 99% rename from rophako/modules/blog.py rename to rophako/modules/blog/__init__.py index 6e74ce3..061b6a9 100644 --- a/rophako/modules/blog.py +++ b/rophako/modules/blog/__init__.py @@ -14,10 +14,12 @@ import rophako.model.blog as Blog import rophako.model.comment as Comment import rophako.model.emoticons as Emoticons from rophako.utils import template, render_markdown, pretty_time, login_required +from rophako.plugin import load_plugin from rophako.log import logger from config import * mod = Blueprint("blog", __name__, url_prefix="/blog") +load_plugin("rophako.modules.comment") @mod.route("/") def index(): diff --git a/rophako/www/blog/categories.inc.html b/rophako/modules/blog/templates/blog/categories.inc.html similarity index 100% rename from rophako/www/blog/categories.inc.html rename to rophako/modules/blog/templates/blog/categories.inc.html diff --git a/rophako/www/blog/delete.html b/rophako/modules/blog/templates/blog/delete.html similarity index 100% rename from rophako/www/blog/delete.html rename to rophako/modules/blog/templates/blog/delete.html diff --git a/rophako/www/blog/entry.html b/rophako/modules/blog/templates/blog/entry.html similarity index 100% rename from rophako/www/blog/entry.html rename to rophako/modules/blog/templates/blog/entry.html diff --git a/rophako/www/blog/entry.inc.html b/rophako/modules/blog/templates/blog/entry.inc.html similarity index 100% rename from rophako/www/blog/entry.inc.html rename to rophako/modules/blog/templates/blog/entry.inc.html diff --git a/rophako/www/blog/index.html b/rophako/modules/blog/templates/blog/index.html similarity index 100% rename from rophako/www/blog/index.html rename to rophako/modules/blog/templates/blog/index.html diff --git a/rophako/www/blog/index.inc.html b/rophako/modules/blog/templates/blog/index.inc.html similarity index 100% rename from rophako/www/blog/index.inc.html rename to rophako/modules/blog/templates/blog/index.inc.html diff --git a/rophako/www/blog/nav-links.inc.html b/rophako/modules/blog/templates/blog/nav-links.inc.html similarity index 100% rename from rophako/www/blog/nav-links.inc.html rename to rophako/modules/blog/templates/blog/nav-links.inc.html diff --git a/rophako/www/blog/sibling-links.html b/rophako/modules/blog/templates/blog/sibling-links.html similarity index 100% rename from rophako/www/blog/sibling-links.html rename to rophako/modules/blog/templates/blog/sibling-links.html diff --git a/rophako/www/blog/update.html b/rophako/modules/blog/templates/blog/update.html similarity index 100% rename from rophako/www/blog/update.html rename to rophako/modules/blog/templates/blog/update.html diff --git a/rophako/modules/comment.py b/rophako/modules/comment/__init__.py similarity index 98% rename from rophako/modules/comment.py rename to rophako/modules/comment/__init__.py index fae494f..12f6a50 100644 --- a/rophako/modules/comment.py +++ b/rophako/modules/comment/__init__.py @@ -9,10 +9,12 @@ import time import rophako.model.user as User import rophako.model.comment as Comment from rophako.utils import template, pretty_time, login_required, sanitize_name +from rophako.plugin import load_plugin from rophako.log import logger from config import * mod = Blueprint("comment", __name__, url_prefix="/comments") +load_plugin("rophako.modules.emoticons") @mod.route("/") diff --git a/rophako/www/comment/form.inc.html b/rophako/modules/comment/templates/comment/form.inc.html similarity index 100% rename from rophako/www/comment/form.inc.html rename to rophako/modules/comment/templates/comment/form.inc.html diff --git a/rophako/www/comment/index.inc.html b/rophako/modules/comment/templates/comment/index.inc.html similarity index 100% rename from rophako/www/comment/index.inc.html rename to rophako/modules/comment/templates/comment/index.inc.html diff --git a/rophako/www/comment/preview.html b/rophako/modules/comment/templates/comment/preview.html similarity index 100% rename from rophako/www/comment/preview.html rename to rophako/modules/comment/templates/comment/preview.html diff --git a/rophako/www/comment/privacy.html b/rophako/modules/comment/templates/comment/privacy.html similarity index 100% rename from rophako/www/comment/privacy.html rename to rophako/modules/comment/templates/comment/privacy.html diff --git a/rophako/www/comment/unsubscribed.html b/rophako/modules/comment/templates/comment/unsubscribed.html similarity index 100% rename from rophako/www/comment/unsubscribed.html rename to rophako/modules/comment/templates/comment/unsubscribed.html diff --git a/rophako/modules/contact.py b/rophako/modules/contact/__init__.py similarity index 100% rename from rophako/modules/contact.py rename to rophako/modules/contact/__init__.py diff --git a/rophako/www/contact/index.html b/rophako/modules/contact/templates/contact/index.html similarity index 100% rename from rophako/www/contact/index.html rename to rophako/modules/contact/templates/contact/index.html diff --git a/rophako/modules/emoticons.py b/rophako/modules/emoticons/__init__.py similarity index 100% rename from rophako/modules/emoticons.py rename to rophako/modules/emoticons/__init__.py diff --git a/rophako/www/emoticons/index.html b/rophako/modules/emoticons/templates/emoticons/index.html similarity index 100% rename from rophako/www/emoticons/index.html rename to rophako/modules/emoticons/templates/emoticons/index.html diff --git a/rophako/modules/photo.py b/rophako/modules/photo/__init__.py similarity index 99% rename from rophako/modules/photo.py rename to rophako/modules/photo/__init__.py index edfb51a..b33c364 100644 --- a/rophako/modules/photo.py +++ b/rophako/modules/photo/__init__.py @@ -7,10 +7,12 @@ from flask import Blueprint, g, request, redirect, url_for, session, flash import rophako.model.user as User import rophako.model.photo as Photo from rophako.utils import template, pretty_time, login_required, ajax_response +from rophako.plugin import load_plugin from rophako.log import logger from config import * mod = Blueprint("photo", __name__, url_prefix="/photos") +load_plugin("rophako.modules.comment") @mod.route("/") def index(): diff --git a/rophako/www/photos/album.html b/rophako/modules/photo/templates/photos/album.html similarity index 100% rename from rophako/www/photos/album.html rename to rophako/modules/photo/templates/photos/album.html diff --git a/rophako/www/photos/albums.html b/rophako/modules/photo/templates/photos/albums.html similarity index 100% rename from rophako/www/photos/albums.html rename to rophako/modules/photo/templates/photos/albums.html diff --git a/rophako/www/photos/arrange_albums.html b/rophako/modules/photo/templates/photos/arrange_albums.html similarity index 100% rename from rophako/www/photos/arrange_albums.html rename to rophako/modules/photo/templates/photos/arrange_albums.html diff --git a/rophako/www/photos/arrange_photos.html b/rophako/modules/photo/templates/photos/arrange_photos.html similarity index 100% rename from rophako/www/photos/arrange_photos.html rename to rophako/modules/photo/templates/photos/arrange_photos.html diff --git a/rophako/www/photos/crop.html b/rophako/modules/photo/templates/photos/crop.html similarity index 100% rename from rophako/www/photos/crop.html rename to rophako/modules/photo/templates/photos/crop.html diff --git a/rophako/www/photos/delete.html b/rophako/modules/photo/templates/photos/delete.html similarity index 100% rename from rophako/www/photos/delete.html rename to rophako/modules/photo/templates/photos/delete.html diff --git a/rophako/www/photos/delete_album.html b/rophako/modules/photo/templates/photos/delete_album.html similarity index 100% rename from rophako/www/photos/delete_album.html rename to rophako/modules/photo/templates/photos/delete_album.html diff --git a/rophako/www/photos/edit.html b/rophako/modules/photo/templates/photos/edit.html similarity index 100% rename from rophako/www/photos/edit.html rename to rophako/modules/photo/templates/photos/edit.html diff --git a/rophako/www/photos/upload.html b/rophako/modules/photo/templates/photos/upload.html similarity index 100% rename from rophako/www/photos/upload.html rename to rophako/modules/photo/templates/photos/upload.html diff --git a/rophako/www/photos/view.html b/rophako/modules/photo/templates/photos/view.html similarity index 100% rename from rophako/www/photos/view.html rename to rophako/modules/photo/templates/photos/view.html diff --git a/rophako/plugin.py b/rophako/plugin.py new file mode 100644 index 0000000..a287606 --- /dev/null +++ b/rophako/plugin.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python + +"""Dynamic CMS plugin loader.""" + +import os +from importlib import import_module +from rophako.app import app, BLUEPRINT_PATHS + +def load_plugin(name, as_blueprint=True, template_path=None): + """Load a Rophako CMS plugin. + + * `name` is a Python module name, i.e. `rophako.modules.blog` + * `as_blueprint` is True if the module exports a blueprint object called + `mod` that can be attached to the Flask app. Set this value to False if + you simply need to include a Python module that isn't a blueprint. + * `template_path` is a filesystem path where the blueprint's templates + can be found. If not provided, the path is automatically determined + based on the module name, which is suitable for the built-in plugins.""" + module = import_module(name) + if as_blueprint: + mod = getattr(module, "mod") + app.register_blueprint(mod) + + # Get the template path to add to the BLUEPRINT_PATHS. + if template_path is None: + module_path = name.replace(".", "/") + template_path = os.path.join(module_path, "templates") + + BLUEPRINT_PATHS.append(template_path) \ No newline at end of file