Move to new settings system
This commit is contained in:
parent
023d5f91df
commit
ff75921129
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1,5 +1,5 @@
|
|||
# Don't check in site specific settings.
|
||||
config.py
|
||||
settings.ini
|
||||
|
||||
# Compiled Python
|
||||
*.pyc
|
||||
|
|
180
defaults.ini
Normal file
180
defaults.ini
Normal file
|
@ -0,0 +1,180 @@
|
|||
# Default configuration settings for Rophako - DO NOT EDIT THIS FILE!
|
||||
#
|
||||
# To configure your site, create a new file named "settings.yml" and override
|
||||
# settings defined in this file. Your settings.yml is masked on top of the
|
||||
# settings in defaults.yml.
|
||||
#
|
||||
# String values can substitute the following special variables:
|
||||
# %(_basedir): The absolute path to the root of this git repository, such that
|
||||
# ./rophako/app.py exists.
|
||||
# %(_year): inserts the current year (for the RSS feed copyright setting)
|
||||
|
||||
# Constants that may be useful in this file.
|
||||
[DEFAULT]
|
||||
_admin_email = root@localhost
|
||||
_date_format = %A, %B %d %Y @ %I:%M:%S %p
|
||||
# "Weekday, Month dd yyyy @ hh:mm:ss AM"
|
||||
|
||||
#------------------------------------------------------------------------------#
|
||||
# General Website Settings #
|
||||
#------------------------------------------------------------------------------#
|
||||
[site]
|
||||
|
||||
# Debug mode for development only!
|
||||
debug = false
|
||||
|
||||
# Unique name of your site, e.g. "kirsle.net"
|
||||
site_name = example.com
|
||||
|
||||
# Path to your site's HTML root. Whenever Rophako tries to render a
|
||||
# template, it will check in your site's root for the template first before
|
||||
# defaulting to the default fallback pages in the rophako/www folder. All
|
||||
# of the core Rophako pages, e.g. for account, blog, photo albums and so on,
|
||||
# have templates in the default site. You can override those templates by
|
||||
# creating files with the same paths in your site's HTML folder.
|
||||
site_root = %(_basedir)s/site/www
|
||||
|
||||
# E-mail address for site notifications (e.g. new comments and exceptions)
|
||||
notify_address = %(_admin_email)s
|
||||
|
||||
# Where to save temp files for photo uploads etc.
|
||||
tempdir = /tmp
|
||||
|
||||
#------------------------------------------------------------------------------#
|
||||
# Database settings #
|
||||
#------------------------------------------------------------------------------#
|
||||
[db]
|
||||
|
||||
# Rophako uses a flat file JSON database system, and a Redis server sits
|
||||
# between Rophako and the filesystem. The db_root is the path on the
|
||||
# filesystem to store documents in (can be relative, default "./db")
|
||||
db_root = db
|
||||
|
||||
redis_host = localhost
|
||||
redis_port = 6379
|
||||
redis_db = 0
|
||||
redis_prefix = rophako:
|
||||
|
||||
#------------------------------------------------------------------------------#
|
||||
# Security Settings #
|
||||
#------------------------------------------------------------------------------#
|
||||
[security]
|
||||
|
||||
# Set this value to true to force SSL/TLS use on your web app. Turning
|
||||
# this on will do the following:
|
||||
# - Send HTTP Strict-Transport-Security header
|
||||
# - Use secure session cookies
|
||||
force_ssl = false
|
||||
|
||||
# Secret key used for session cookie signing. Make this long and hard to
|
||||
# guess.
|
||||
#
|
||||
# Tips for creating a strong secret key:
|
||||
# $ python
|
||||
# >>> import os
|
||||
# >>> os.urandom(24)
|
||||
# '\xfd{H\xe5<\x95\xf9\xe3\x96.5\xd1\x01O<!\xd5\xa2\xa0\x9fR"\xa1\xa8'
|
||||
#
|
||||
# Then take that whole quoted string and paste it right in as the secret
|
||||
# key! Do NOT use that one. It was just an example! Make your own.
|
||||
secret_key = for the love of Arceus, change this key!
|
||||
|
||||
# Password strength: number of iterations for bcrypt password.
|
||||
bcrypt_iterations = 12
|
||||
|
||||
#------------------------------------------------------------------------------#
|
||||
# Mail Settings #
|
||||
#------------------------------------------------------------------------------#
|
||||
[mail]
|
||||
|
||||
# method = smtp or sendmail (not yet implemented)
|
||||
method = smtp
|
||||
server = localhost
|
||||
port = 25
|
||||
sender = Rophako CMS <no-reply@rophako.kirsle.net>
|
||||
|
||||
#------------------------------------------------------------------------------#
|
||||
# Plugin Configurations #
|
||||
#------------------------------------------------------------------------------#
|
||||
|
||||
###
|
||||
# Emoticons
|
||||
###
|
||||
# Emoticon theme used for blog posts and comments. Should exist at the URL
|
||||
# "/static/smileys" from your document root, and have a file named
|
||||
# "emoticons.json" inside. If you add a custom theme to your private site
|
||||
# folder, then also change EMOTICON_ROOT_PRIVATE to look there instead.
|
||||
[emoticons]
|
||||
theme = tango
|
||||
root_private = %(_basedir)s/rophako/www/static/smileys
|
||||
|
||||
###
|
||||
# Blog
|
||||
###
|
||||
[blog]
|
||||
default_category = Uncategorized
|
||||
default_privacy = public
|
||||
time_format = %(_date_format)s
|
||||
allow_comments = true
|
||||
entries_per_page = 5
|
||||
|
||||
# RSS feed settings.
|
||||
title = Rophako CMS Blog
|
||||
link = http://rophako.kirsle.net/
|
||||
language = en
|
||||
description = The web blog of the Rophako CMS.
|
||||
copyright = Copyright %(_year)s
|
||||
webmaster = %(_admin_email)s
|
||||
image_title = Rophako CMS Blog
|
||||
image_url = //www.kirsle.net/static/avatars/default.png
|
||||
image_width = 100
|
||||
image_height = 100
|
||||
image_description = Rophako CMS
|
||||
entries_per_feed = 5
|
||||
|
||||
###
|
||||
# Photo
|
||||
###
|
||||
[photo]
|
||||
# The path to where uploaded photos will be stored.
|
||||
# The PRIVATE path is from the perspective of the server file system.
|
||||
# The PUBLIC path is from the perspective of the web browser via HTTP.
|
||||
root_private = %(_basedir)s/site/www/static/photos
|
||||
root_public = /static/photos
|
||||
default_album = My Photos
|
||||
time_format = %(_date_format)s
|
||||
# Max widths for photo sizes
|
||||
width_large = 800
|
||||
width_thumb = 256
|
||||
width_avatar = 96
|
||||
|
||||
###
|
||||
# Comment
|
||||
###
|
||||
[comment]
|
||||
time_format = %(_date_format)s
|
||||
# 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.
|
||||
default_avatar =
|
||||
|
||||
#------------------------------------------------------------------------------#
|
||||
# List of Enabled Plugins #
|
||||
#------------------------------------------------------------------------------#
|
||||
[plugins]
|
||||
|
||||
# Which plugins to enable? List each plugin by module name. The plugins
|
||||
# will be assumed to be blueprints that can be attached to the main app
|
||||
# object. If you instead want to load an arbitrary Python module (i.e. to
|
||||
# define custom routes at the app layer, not in a blueprint) list those
|
||||
# under the "custom" section (remove the empty array [] and list them
|
||||
# like shown in the plugins section).
|
||||
blueprints =
|
||||
rophako.modules.blog
|
||||
rophako.modules.photo
|
||||
rophako.modules.comment
|
||||
rophako.modules.emoticons
|
||||
rophako.modules.contact
|
||||
rophako.modules.tracking
|
||||
|
||||
custom =
|
|
@ -7,7 +7,7 @@ import re
|
|||
import os
|
||||
import json
|
||||
|
||||
import config
|
||||
from rophako.settings import Config
|
||||
from rophako.app import app
|
||||
from rophako.utils import template, login_required
|
||||
import rophako.model.blog as Blog
|
||||
|
@ -109,6 +109,6 @@ def ssl_test():
|
|||
},
|
||||
"App Configuration": {
|
||||
"Session cookies secure": app.config["SESSION_COOKIE_SECURE"],
|
||||
"config.FORCE_SSL": config.FORCE_SSL,
|
||||
"config.FORCE_SSL": Config.security.force_ssl,
|
||||
},
|
||||
}))
|
||||
|
|
|
@ -5,4 +5,4 @@ bcrypt
|
|||
pillow
|
||||
requests
|
||||
Markdown
|
||||
Pygments
|
||||
Pygments
|
||||
|
|
|
@ -23,17 +23,20 @@ app = Flask(__name__,
|
|||
# jinja2.ChoiceLoader.
|
||||
BLUEPRINT_PATHS = []
|
||||
|
||||
import config
|
||||
from rophako.settings import Config
|
||||
Config.load_settings()
|
||||
Config.load_plugins()
|
||||
|
||||
from rophako import __version__
|
||||
from rophako.plugin import load_plugin
|
||||
import rophako.model.tracking as Tracking
|
||||
import rophako.utils
|
||||
|
||||
app.DEBUG = config.DEBUG
|
||||
app.secret_key = config.SECRET_KEY
|
||||
app.DEBUG = Config.site.debug == "true"
|
||||
app.secret_key = Config.security.secret_key.decode("string_escape")
|
||||
|
||||
# Security?
|
||||
if config.FORCE_SSL:
|
||||
if Config.security.force_ssl == "true":
|
||||
app.config['SESSION_COOKIE_SECURE'] = True
|
||||
sslify = SSLify(app)
|
||||
|
||||
|
@ -44,8 +47,8 @@ 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
|
||||
Config.site.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])
|
||||
|
@ -70,7 +73,7 @@ def before_request():
|
|||
"version": __version__,
|
||||
"python_version": "{}.{}".format(sys.version_info.major, sys.version_info.minor),
|
||||
"author": "Noah Petherbridge",
|
||||
"photo_url": config.PHOTO_ROOT_PUBLIC,
|
||||
"photo_url": Config.photo.root_public,
|
||||
},
|
||||
"uri": request.path,
|
||||
"session": {
|
||||
|
@ -125,7 +128,7 @@ def catchall(path):
|
|||
otherwise we give the 404 error page."""
|
||||
|
||||
# Search for this file.
|
||||
for root in [config.SITE_ROOT, "rophako/www"]:
|
||||
for root in [Config.site.site_root, "rophako/www"]:
|
||||
abspath = os.path.abspath("{}/{}".format(root, path))
|
||||
if os.path.isfile(abspath):
|
||||
return send_file(abspath)
|
||||
|
|
|
@ -12,7 +12,7 @@ import redis
|
|||
import json
|
||||
import time
|
||||
|
||||
import config
|
||||
from rophako.settings import Config
|
||||
from rophako.log import logger
|
||||
|
||||
redis_client = None
|
||||
|
@ -111,7 +111,7 @@ def mkpath(document):
|
|||
if document.endswith(".json"):
|
||||
# Let's not do that.
|
||||
raise Exception("mkpath: document path already includes .json extension!")
|
||||
return "{}/{}.json".format(config.DB_ROOT, str(document))
|
||||
return "{}/{}.json".format(Config.db.db_root, str(document))
|
||||
|
||||
|
||||
def read_json(path):
|
||||
|
@ -137,6 +137,7 @@ def read_json(path):
|
|||
data = json.loads(text)
|
||||
except:
|
||||
logger.error("Couldn't decode JSON data from {}".format(path))
|
||||
logger.error(text)
|
||||
data = None
|
||||
|
||||
return data
|
||||
|
@ -179,16 +180,16 @@ def get_redis():
|
|||
global redis_client
|
||||
if not redis_client:
|
||||
redis_client = redis.StrictRedis(
|
||||
host = config.REDIS_HOST,
|
||||
port = config.REDIS_PORT,
|
||||
db = config.REDIS_DB,
|
||||
host = Config.db.redis_host,
|
||||
port = Config.db.redis_port,
|
||||
db = Config.db.redis_db,
|
||||
)
|
||||
return redis_client
|
||||
|
||||
|
||||
def set_cache(key, value, expires=None):
|
||||
"""Set a key in the Redis cache."""
|
||||
key = config.REDIS_PREFIX + key
|
||||
key = Config.db.redis_prefix + key
|
||||
try:
|
||||
client = get_redis()
|
||||
client.set(key, json.dumps(value))
|
||||
|
@ -202,7 +203,7 @@ def set_cache(key, value, expires=None):
|
|||
|
||||
def get_cache(key):
|
||||
"""Get a cached item."""
|
||||
key = config.REDIS_PREFIX + key
|
||||
key = Config.db.redis_prefix + key
|
||||
value = None
|
||||
try:
|
||||
client = get_redis()
|
||||
|
@ -217,6 +218,6 @@ def get_cache(key):
|
|||
|
||||
def del_cache(key):
|
||||
"""Delete a cached item."""
|
||||
key = config.REDIS_PREFIX + key
|
||||
key = Config.db.redis_prefix + key
|
||||
client = get_redis()
|
||||
client.delete(key)
|
||||
|
|
|
@ -7,7 +7,7 @@ from __future__ import print_function
|
|||
from flask import g, request
|
||||
import logging
|
||||
|
||||
import config
|
||||
from rophako.settings import Config
|
||||
|
||||
class LogHandler(logging.Handler):
|
||||
"""A custom logging handler."""
|
||||
|
@ -29,7 +29,7 @@ handler.setFormatter(logging.Formatter("[%(asctime)s] [%(levelname)s] $prefix$%(
|
|||
logger.addHandler(handler)
|
||||
|
||||
# Log level.
|
||||
if config.DEBUG:
|
||||
if Config.site.debug == "true":
|
||||
logger.setLevel(logging.DEBUG)
|
||||
else:
|
||||
logger.setLevel(logging.INFO)
|
||||
logger.setLevel(logging.INFO)
|
||||
|
|
|
@ -8,7 +8,7 @@ import re
|
|||
import glob
|
||||
import os
|
||||
|
||||
import config
|
||||
from rophako.settings import Config
|
||||
import rophako.jsondb as JsonDB
|
||||
from rophako.log import logger
|
||||
|
||||
|
@ -206,7 +206,7 @@ def list_avatars():
|
|||
# Load avatars from both locations. We check the built-in set first,
|
||||
# so if you have matching names in your local site those will override.
|
||||
"rophako/www/static/avatars/*.*",
|
||||
os.path.join(config.SITE_ROOT, "static", "avatars", "*.*"),
|
||||
os.path.join(Config.site.site_root, "static", "avatars", "*.*"),
|
||||
]
|
||||
for path in paths:
|
||||
for filename in glob.glob(path):
|
||||
|
@ -228,4 +228,4 @@ def get_next_id(index):
|
|||
# Sanity check!
|
||||
if next_id in index:
|
||||
raise Exception("Failed to get_next_id for the blog. Chosen ID is still in the index!")
|
||||
return next_id
|
||||
return next_id
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
"""Commenting models."""
|
||||
|
||||
from flask import g, url_for
|
||||
from flask import url_for
|
||||
import time
|
||||
import hashlib
|
||||
import urllib
|
||||
|
@ -10,7 +10,7 @@ import random
|
|||
import re
|
||||
import sys
|
||||
|
||||
import config
|
||||
from rophako.settings import Config
|
||||
import rophako.jsondb as JsonDB
|
||||
import rophako.model.user as User
|
||||
import rophako.model.emoticons as Emoticons
|
||||
|
@ -58,7 +58,7 @@ def add_comment(thread, uid, name, subject, message, url, time, ip, image=None):
|
|||
|
||||
# Send the e-mail to the site admins.
|
||||
send_email(
|
||||
to=config.NOTIFY_ADDRESS,
|
||||
to=Config.site.notify_address,
|
||||
subject="New comment: {}".format(subject),
|
||||
message="""{name} has left a comment on: {subject}
|
||||
|
||||
|
@ -230,7 +230,7 @@ def gravatar(email):
|
|||
"""Generate a Gravatar link for an email address."""
|
||||
if "@" in email:
|
||||
# Default avatar?
|
||||
default = config.COMMENT_DEFAULT_AVATAR
|
||||
default = Config.comment.default_avatar
|
||||
|
||||
# Construct the URL.
|
||||
params = {
|
||||
|
|
|
@ -2,14 +2,12 @@
|
|||
|
||||
"""Emoticon models."""
|
||||
|
||||
from flask import g, url_for
|
||||
import os
|
||||
import codecs
|
||||
import json
|
||||
import re
|
||||
|
||||
import config
|
||||
import rophako.jsondb as JsonDB
|
||||
from rophako.settings import Config
|
||||
from rophako.log import logger
|
||||
|
||||
|
||||
|
@ -18,7 +16,7 @@ _cache = {}
|
|||
|
||||
def load_theme():
|
||||
"""Pre-load and cache the emoticon theme. This happens on startup."""
|
||||
theme = config.EMOTICON_THEME
|
||||
theme = Config.emoticons.theme
|
||||
global _cache
|
||||
|
||||
# Cached?
|
||||
|
@ -26,13 +24,13 @@ def load_theme():
|
|||
return _cache
|
||||
|
||||
# Only if the theme file exists.
|
||||
settings = os.path.join(config.EMOTICON_ROOT_PRIVATE, theme, "emoticons.json")
|
||||
settings = os.path.join(Config.emoticons.root_private, theme, "emoticons.json")
|
||||
if not os.path.isfile(settings):
|
||||
logger.error("Failed to load smiley theme {}: not found!")
|
||||
|
||||
# Try the default (tango).
|
||||
theme = "tango"
|
||||
settings = os.path.join(config.EMOTICON_ROOT_PRIVATE, theme, "emoticons.json")
|
||||
settings = os.path.join(Config.emoticons.root_private, theme, "emoticons.json")
|
||||
if os.path.isfile(settings):
|
||||
logger.info("Falling back to default theme: tango")
|
||||
else:
|
||||
|
@ -71,11 +69,11 @@ def render(message):
|
|||
if trigger in message:
|
||||
# Substitute it.
|
||||
sub = """<img src="{url}" alt="{trigger}" title="{trigger}">""".format(
|
||||
url="/static/smileys/{}/{}".format(config.EMOTICON_THEME, img),
|
||||
url="/static/smileys/{}/{}".format(Config.emoticons.theme, img),
|
||||
trigger=trigger,
|
||||
)
|
||||
pattern = r'([^A-Za-z0-9:\-]|^){}([^A-Za-z0-9:\-]|$)'.format(re.escape(trigger))
|
||||
result = r'\1{}\2'.format(sub)
|
||||
message = re.sub(pattern, result, message)
|
||||
|
||||
return message
|
||||
return message
|
||||
|
|
|
@ -10,16 +10,16 @@ from PIL import Image
|
|||
import hashlib
|
||||
import random
|
||||
|
||||
import config
|
||||
from rophako.settings import Config
|
||||
import rophako.jsondb as JsonDB
|
||||
from rophako.utils import sanitize_name, remote_addr
|
||||
from rophako.log import logger
|
||||
|
||||
# Maps the friendly names of photo sizes with their pixel values from config.
|
||||
PHOTO_SCALES = dict(
|
||||
large=config.PHOTO_WIDTH_LARGE,
|
||||
thumb=config.PHOTO_WIDTH_THUMB,
|
||||
avatar=config.PHOTO_WIDTH_AVATAR,
|
||||
large=int(Config.photo.width_large),
|
||||
thumb=int(Config.photo.width_thumb),
|
||||
avatar=int(Config.photo.width_avatar),
|
||||
)
|
||||
|
||||
|
||||
|
@ -57,7 +57,6 @@ def list_albums():
|
|||
def get_album(name):
|
||||
"""Get details about an album."""
|
||||
index = get_index()
|
||||
result = []
|
||||
|
||||
if not name in index["albums"]:
|
||||
return None
|
||||
|
@ -79,7 +78,7 @@ def rename_album(old_name, new_name):
|
|||
Returns True on success, False if the new name conflicts with another
|
||||
album's name."""
|
||||
old_name = sanitize_name(old_name)
|
||||
newname = sanitize_name(new_name)
|
||||
new_name = sanitize_name(new_name)
|
||||
index = get_index()
|
||||
|
||||
# New name is unique?
|
||||
|
@ -196,7 +195,7 @@ def get_photo(key):
|
|||
|
||||
def get_image_dimensions(pic):
|
||||
"""Use PIL to get the image's true dimensions."""
|
||||
filename = os.path.join(config.PHOTO_ROOT_PRIVATE, pic["large"])
|
||||
filename = os.path.join(Config.photo.root_private, pic["large"])
|
||||
img = Image.open(filename)
|
||||
return img.size
|
||||
|
||||
|
@ -233,11 +232,11 @@ def crop_photo(key, x, y, length):
|
|||
for size in ["thumb", "avatar"]:
|
||||
pic = index["albums"][album][key][size]
|
||||
logger.debug("Delete {} size: {}".format(size, pic))
|
||||
os.unlink(os.path.join(config.PHOTO_ROOT_PRIVATE, pic))
|
||||
os.unlink(os.path.join(Config.photo.root_private, pic))
|
||||
|
||||
# Regenerate all the thumbnails.
|
||||
large = index["albums"][album][key]["large"]
|
||||
source = os.path.join(config.PHOTO_ROOT_PRIVATE, large)
|
||||
source = os.path.join(Config.photo.root_private, large)
|
||||
for size in ["thumb", "avatar"]:
|
||||
pic = resize_photo(source, size, crop=dict(
|
||||
x=x,
|
||||
|
@ -305,7 +304,7 @@ def rotate_photo(key, rotate):
|
|||
|
||||
new_names = dict()
|
||||
for size in ["large", "thumb", "avatar"]:
|
||||
fname = os.path.join(config.PHOTO_ROOT_PRIVATE, photo[size])
|
||||
fname = os.path.join(Config.photo.root_private, photo[size])
|
||||
logger.info("Rotating image {} by {} degrees.".format(fname, degrees))
|
||||
|
||||
# Give it a new name.
|
||||
|
@ -315,7 +314,7 @@ def rotate_photo(key, rotate):
|
|||
|
||||
img = Image.open(fname)
|
||||
img = img.rotate(degrees)
|
||||
img.save(os.path.join(config.PHOTO_ROOT_PRIVATE, outfile))
|
||||
img.save(os.path.join(Config.photo.root_private, outfile))
|
||||
|
||||
# Delete the old name.
|
||||
os.unlink(fname)
|
||||
|
@ -339,7 +338,7 @@ def delete_photo(key):
|
|||
# Delete all the images.
|
||||
for size in ["large", "thumb", "avatar"]:
|
||||
logger.info("Delete: {}".format(photo[size]))
|
||||
fname = os.path.join(config.PHOTO_ROOT_PRIVATE, photo[size])
|
||||
fname = os.path.join(Config.photo.root_private, photo[size])
|
||||
if os.path.isfile(fname):
|
||||
os.unlink(fname)
|
||||
|
||||
|
@ -428,7 +427,7 @@ def upload_from_pc(request):
|
|||
if not allowed_filetype(upload.filename):
|
||||
return dict(success=False, error="Unsupported file extension.")
|
||||
|
||||
tempfile = "{}/rophako-photo-{}.{}".format(config.TEMPDIR, int(time.time()), filetype)
|
||||
tempfile = "{}/rophako-photo-{}.{}".format(Config.site.tempdir, int(time.time()), filetype)
|
||||
logger.debug("Save incoming photo to: {}".format(tempfile))
|
||||
upload.save(tempfile)
|
||||
|
||||
|
@ -461,7 +460,7 @@ def upload_from_www(form):
|
|||
|
||||
# Make a temp filename for it.
|
||||
filetype = url.rsplit(".", 1)[1]
|
||||
tempfile = "{}/rophako-photo-{}.{}".format(config.TEMPDIR, int(time.time()), filetype)
|
||||
tempfile = "{}/rophako-photo-{}.{}".format(Config.site.tempdir, int(time.time()), filetype)
|
||||
logger.debug("Save incoming photo to: {}".format(tempfile))
|
||||
|
||||
# Grab the file.
|
||||
|
@ -500,7 +499,7 @@ def process_photo(form, filename):
|
|||
album = sanitize_name(album)
|
||||
if album == "":
|
||||
logger.warning("Album name didn't pass sanitization! Fall back to default album name.")
|
||||
album = config.PHOTO_DEFAULT_ALBUM
|
||||
album = Config.photo.default_album
|
||||
|
||||
# Make up a unique public key for this set of photos.
|
||||
key = random_hash()
|
||||
|
@ -574,7 +573,7 @@ def resize_photo(filename, size, crop=None):
|
|||
|
||||
# Make up a unique filename.
|
||||
outfile = random_name(filetype)
|
||||
target = os.path.join(config.PHOTO_ROOT_PRIVATE, outfile)
|
||||
target = os.path.join(Config.photo.root_private, outfile)
|
||||
logger.debug("Output file for {} scale: {}".format(size, target))
|
||||
|
||||
# Get the image's dimensions.
|
||||
|
@ -679,7 +678,7 @@ def random_name(filetype):
|
|||
"""Get a random available file name to save a new photo."""
|
||||
filetype = filetype.lower()
|
||||
outfile = random_hash() + "." + filetype
|
||||
while os.path.isfile(os.path.join(config.PHOTO_ROOT_PRIVATE, outfile)):
|
||||
while os.path.isfile(os.path.join(Config.photo.root_private, outfile)):
|
||||
outfile = random_hash() + "." + filetype
|
||||
return outfile
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
import bcrypt
|
||||
import time
|
||||
|
||||
import config
|
||||
from rophako.settings import Config
|
||||
import rophako.jsondb as JsonDB
|
||||
import rophako.model.photo as Photo
|
||||
from rophako.log import logger
|
||||
|
@ -147,7 +147,7 @@ def exists(uid=None, username=None):
|
|||
|
||||
|
||||
def hash_password(password):
|
||||
return bcrypt.hashpw(str(password).encode("utf-8"), bcrypt.gensalt(config.BCRYPT_ITERATIONS)).decode("utf-8")
|
||||
return bcrypt.hashpw(str(password).encode("utf-8"), bcrypt.gensalt(int(Config.security.bcrypt_iterations))).decode("utf-8")
|
||||
|
||||
|
||||
def check_auth(username, password):
|
||||
|
@ -172,4 +172,4 @@ def get_next_uid():
|
|||
uid = 1
|
||||
while exists(uid=uid):
|
||||
uid += 1
|
||||
return uid
|
||||
return uid
|
||||
|
|
|
@ -14,8 +14,8 @@ import rophako.model.emoticons as Emoticons
|
|||
from rophako.utils import (template, render_markdown, pretty_time,
|
||||
login_required, remote_addr)
|
||||
from rophako.plugin import load_plugin
|
||||
from rophako.settings import Config
|
||||
from rophako.log import logger
|
||||
from config import *
|
||||
|
||||
mod = Blueprint("blog", __name__, url_prefix="/blog")
|
||||
load_plugin("rophako.modules.comment")
|
||||
|
@ -34,16 +34,16 @@ def archive():
|
|||
groups = dict()
|
||||
friendly_months = dict()
|
||||
for post_id, data in index.items():
|
||||
time = datetime.datetime.fromtimestamp(data["time"])
|
||||
date = time.strftime("%Y-%m")
|
||||
ts = datetime.datetime.fromtimestamp(data["time"])
|
||||
date = ts.strftime("%Y-%m")
|
||||
if not date in groups:
|
||||
groups[date] = dict()
|
||||
friendly = time.strftime("%B %Y")
|
||||
friendly = ts.strftime("%B %Y")
|
||||
friendly_months[date] = friendly
|
||||
|
||||
# Get author's profile && Pretty-print the time.
|
||||
data["profile"] = User.get_user(uid=data["author"])
|
||||
data["pretty_time"] = pretty_time(BLOG_TIME_FORMAT, data["time"])
|
||||
data["pretty_time"] = pretty_time(Config.blog.time_format, data["time"])
|
||||
groups[date][post_id] = data
|
||||
|
||||
# Sort by calendar month.
|
||||
|
@ -101,10 +101,10 @@ def entry(fid):
|
|||
# Get the author's information.
|
||||
post["profile"] = User.get_user(uid=post["author"])
|
||||
post["photo"] = User.get_picture(uid=post["author"])
|
||||
post["photo_url"] = PHOTO_ROOT_PUBLIC
|
||||
post["photo_url"] = Config.photo.root_public
|
||||
|
||||
# Pretty-print the time.
|
||||
post["pretty_time"] = pretty_time(BLOG_TIME_FORMAT, post["time"])
|
||||
post["pretty_time"] = pretty_time(Config.blog.time_format, post["time"])
|
||||
|
||||
# Count the comments for this post
|
||||
post["comment_count"] = Comment.count_comments("blog-{}".format(post_id))
|
||||
|
@ -153,9 +153,9 @@ def update():
|
|||
format="markdown",
|
||||
avatar="",
|
||||
categories="",
|
||||
privacy=BLOG_DEFAULT_PRIVACY,
|
||||
privacy=Config.blog.default_privacy,
|
||||
emoticons=True,
|
||||
comments=BLOG_ALLOW_COMMENTS,
|
||||
comments=Config.blog.allow_comments,
|
||||
month="",
|
||||
day="",
|
||||
year="",
|
||||
|
@ -328,14 +328,14 @@ def rss():
|
|||
today = time.strftime(rss_time, time.gmtime())
|
||||
|
||||
xml_add_text_tags(doc, channel, [
|
||||
["title", RSS_TITLE],
|
||||
["link", RSS_LINK],
|
||||
["description", RSS_DESCRIPTION],
|
||||
["language", RSS_LANGUAGE],
|
||||
["copyright", RSS_COPYRIGHT],
|
||||
["title", Config.blog.title],
|
||||
["link", Config.blog.link],
|
||||
["description", Config.blog.description],
|
||||
["language", Config.blog.language],
|
||||
["copyright", Config.blog.copyright],
|
||||
["pubDate", today],
|
||||
["lastBuildDate", today],
|
||||
["webmaster", RSS_WEBMASTER],
|
||||
["webmaster", Config.blog.webmaster],
|
||||
])
|
||||
|
||||
######
|
||||
|
@ -345,12 +345,12 @@ def rss():
|
|||
image = doc.createElement("image")
|
||||
channel.appendChild(image)
|
||||
xml_add_text_tags(doc, image, [
|
||||
["title", RSS_IMAGE_TITLE],
|
||||
["url", RSS_IMAGE_URL],
|
||||
["link", RSS_LINK],
|
||||
["width", RSS_IMAGE_WIDTH],
|
||||
["height", RSS_IMAGE_HEIGHT],
|
||||
["description", RSS_IMAGE_DESCRIPTION],
|
||||
["title", Config.blog.image_title],
|
||||
["url", Config.blog.image_url],
|
||||
["link", Config.blog.link],
|
||||
["width", Config.blog.image_width],
|
||||
["height", Config.blog.image_height],
|
||||
["description", Config.blog.image_description],
|
||||
])
|
||||
|
||||
######
|
||||
|
@ -359,7 +359,7 @@ def rss():
|
|||
|
||||
index = Blog.get_index()
|
||||
posts = get_index_posts(index)
|
||||
for post_id in posts[:BLOG_ENTRIES_PER_RSS]:
|
||||
for post_id in posts[:int(Config.blog.entries_per_feed)]:
|
||||
post = Blog.get_entry(post_id)
|
||||
item = doc.createElement("item")
|
||||
channel.appendChild(item)
|
||||
|
@ -432,8 +432,8 @@ def partial_index():
|
|||
# Handle the offsets, and get those for the "older" and "earlier" posts.
|
||||
# "earlier" posts count down (towards index 0), "older" counts up.
|
||||
g.info["offset"] = offset
|
||||
g.info["earlier"] = offset - BLOG_ENTRIES_PER_PAGE if offset > 0 else 0
|
||||
g.info["older"] = offset + BLOG_ENTRIES_PER_PAGE
|
||||
g.info["earlier"] = offset - int(Config.blog.entries_per_page) if offset > 0 else 0
|
||||
g.info["older"] = offset + int(Config.blog.entries_per_page)
|
||||
if g.info["earlier"] < 0:
|
||||
g.info["earlier"] = 0
|
||||
if g.info["older"] < 0 or g.info["older"] > len(posts):
|
||||
|
@ -446,7 +446,7 @@ def partial_index():
|
|||
|
||||
# Load the selected posts.
|
||||
selected = []
|
||||
stop = offset + BLOG_ENTRIES_PER_PAGE
|
||||
stop = offset + int(Config.blog.entries_per_page)
|
||||
if stop > len(posts): stop = len(posts)
|
||||
index = 1 # Let each post know its position on-page.
|
||||
for i in range(offset, stop):
|
||||
|
@ -468,9 +468,9 @@ def partial_index():
|
|||
# Get the author's information.
|
||||
post["profile"] = User.get_user(uid=post["author"])
|
||||
post["photo"] = User.get_picture(uid=post["author"])
|
||||
post["photo_url"] = PHOTO_ROOT_PUBLIC
|
||||
post["photo_url"] = Config.photo.root_public
|
||||
|
||||
post["pretty_time"] = pretty_time(BLOG_TIME_FORMAT, post["time"])
|
||||
post["pretty_time"] = pretty_time(Config.blog.time_format, post["time"])
|
||||
|
||||
# Count the comments for this post
|
||||
post["comment_count"] = Comment.count_comments("blog-{}".format(post_id))
|
||||
|
|
|
@ -2,8 +2,7 @@
|
|||
|
||||
"""Endpoints for the commenting subsystem."""
|
||||
|
||||
from flask import Blueprint, g, request, redirect, url_for, session, flash
|
||||
import re
|
||||
from flask import Blueprint, g, request, redirect, url_for, flash
|
||||
import time
|
||||
|
||||
import rophako.model.user as User
|
||||
|
@ -11,8 +10,7 @@ import rophako.model.comment as Comment
|
|||
from rophako.utils import (template, pretty_time, login_required, sanitize_name,
|
||||
remote_addr)
|
||||
from rophako.plugin import load_plugin
|
||||
from rophako.log import logger
|
||||
from config import *
|
||||
from rophako.settings import Config
|
||||
|
||||
mod = Blueprint("comment", __name__, url_prefix="/comments")
|
||||
load_plugin("rophako.modules.emoticons")
|
||||
|
@ -71,7 +69,7 @@ def preview():
|
|||
# Gravatar.
|
||||
g.info["gravatar"] = gravatar
|
||||
g.info["preview"] = Comment.format_message(form["message"])
|
||||
g.info["pretty_time"] = pretty_time(COMMENT_TIME_FORMAT, time.time())
|
||||
g.info["pretty_time"] = pretty_time(Config.comment.time_format, time.time())
|
||||
|
||||
g.info.update(form)
|
||||
return template("comment/preview.html")
|
||||
|
@ -191,7 +189,7 @@ def partial_index(thread, subject, header=True):
|
|||
comment["image"] = avatar
|
||||
|
||||
# Add the pretty time.
|
||||
comment["pretty_time"] = pretty_time(COMMENT_TIME_FORMAT, comment["time"])
|
||||
comment["pretty_time"] = pretty_time(Config.comment.time_format, comment["time"])
|
||||
|
||||
# Format the message for display.
|
||||
comment["formatted_message"] = Comment.format_message(comment["message"])
|
||||
|
@ -203,7 +201,7 @@ def partial_index(thread, subject, header=True):
|
|||
g.info["subject"] = subject
|
||||
g.info["url"] = request.url
|
||||
g.info["comments"] = sorted_comments
|
||||
g.info["photo_url"] = PHOTO_ROOT_PUBLIC
|
||||
g.info["photo_url"] = Config.photo.root_public
|
||||
return template("comment/index.inc.html")
|
||||
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
from flask import Blueprint, request, redirect, url_for, flash
|
||||
|
||||
from rophako.utils import template, send_email, remote_addr
|
||||
from config import *
|
||||
from rophako.settings import Config
|
||||
|
||||
mod = Blueprint("contact", __name__, url_prefix="/contact")
|
||||
|
||||
|
@ -42,9 +42,9 @@ def send():
|
|||
|
||||
# Send the e-mail.
|
||||
send_email(
|
||||
to=NOTIFY_ADDRESS,
|
||||
to=Config.site.notify_address,
|
||||
reply_to=reply_to,
|
||||
subject="Contact Form on {}: {}".format(SITE_NAME, subject),
|
||||
subject="Contact Form on {}: {}".format(Config.site.site_name, subject),
|
||||
message="""A visitor to {site_name} has sent you a message!
|
||||
|
||||
IP Address: {ip}
|
||||
|
@ -55,7 +55,7 @@ E-mail: {email}
|
|||
Subject: {subject}
|
||||
|
||||
{message}""".format(
|
||||
site_name=SITE_NAME,
|
||||
site_name=Config.site.site_name,
|
||||
ip=remote_addr(),
|
||||
ua=request.user_agent.string,
|
||||
referer=request.headers.get("Referer", ""),
|
||||
|
|
|
@ -6,8 +6,7 @@ from flask import Blueprint, g
|
|||
|
||||
import rophako.model.emoticons as Emoticons
|
||||
from rophako.utils import template
|
||||
from rophako.log import logger
|
||||
from config import *
|
||||
from rophako.settings import Config
|
||||
|
||||
mod = Blueprint("emoticons", __name__, url_prefix="/emoticons")
|
||||
|
||||
|
@ -24,7 +23,7 @@ def index():
|
|||
"triggers": theme["map"][img],
|
||||
})
|
||||
|
||||
g.info["theme"] = EMOTICON_THEME
|
||||
g.info["theme"] = Config.emoticons.theme
|
||||
g.info["theme_name"] = theme["name"]
|
||||
g.info["smileys"] = smileys
|
||||
return template("emoticons/index.html")
|
||||
return template("emoticons/index.html")
|
||||
|
|
|
@ -2,15 +2,14 @@
|
|||
|
||||
"""Endpoints for the photo albums."""
|
||||
|
||||
from flask import Blueprint, g, request, redirect, url_for, session, flash
|
||||
from flask import Blueprint, g, request, redirect, url_for, flash
|
||||
|
||||
import rophako.model.user as User
|
||||
import rophako.model.photo as Photo
|
||||
from rophako.utils import (template, pretty_time, render_markdown,
|
||||
login_required, ajax_response)
|
||||
from rophako.plugin import load_plugin
|
||||
from rophako.log import logger
|
||||
from config import *
|
||||
from rophako.settings import Config
|
||||
|
||||
mod = Blueprint("photo", __name__, url_prefix="/photos")
|
||||
load_plugin("rophako.modules.comment")
|
||||
|
@ -68,7 +67,7 @@ def view_photo(key):
|
|||
|
||||
g.info["photo"] = photo
|
||||
g.info["photo"]["key"] = key
|
||||
g.info["photo"]["pretty_time"] = pretty_time(PHOTO_TIME_FORMAT, photo["uploaded"])
|
||||
g.info["photo"]["pretty_time"] = pretty_time(Config.photo.time_format, photo["uploaded"])
|
||||
g.info["photo"]["markdown"] = render_markdown(photo.get("description", ""))
|
||||
return template("photos/view.html")
|
||||
|
||||
|
@ -125,10 +124,10 @@ def upload():
|
|||
g.info["album_list"] = [
|
||||
"My Photos", # the default
|
||||
]
|
||||
g.info["selected"] = PHOTO_DEFAULT_ALBUM
|
||||
g.info["selected"] = Config.photo.default_album
|
||||
albums = Photo.list_albums()
|
||||
if len(albums):
|
||||
g.info["album_list"] = [ album["name"] for album in albums ]
|
||||
g.info["album_list"] = [ x["name"] for x in albums ]
|
||||
g.info["selected"] = albums[0]
|
||||
|
||||
return template("photos/upload.html")
|
||||
|
|
|
@ -26,4 +26,4 @@ def load_plugin(name, as_blueprint=True, template_path=None):
|
|||
module_path = name.replace(".", "/")
|
||||
template_path = os.path.join(module_path, "templates")
|
||||
|
||||
BLUEPRINT_PATHS.append(template_path)
|
||||
BLUEPRINT_PATHS.append(template_path)
|
||||
|
|
60
rophako/settings.py
Normal file
60
rophako/settings.py
Normal file
|
@ -0,0 +1,60 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
import os
|
||||
import datetime
|
||||
from attrdict import AttrDict
|
||||
from ConfigParser import ConfigParser
|
||||
|
||||
from rophako.plugin import load_plugin
|
||||
|
||||
# Get the base directory of the git root.
|
||||
basedir = os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir))
|
||||
|
||||
# https://github.com/bcj/AttrDict/issues/20
|
||||
if not hasattr(AttrDict, "copy"):
|
||||
setattr(AttrDict, "copy", lambda self: self._mapping.copy())
|
||||
|
||||
class ConfigHandler(object):
|
||||
settings = None
|
||||
|
||||
def load_settings(self):
|
||||
"""Load the settings files and make them available in the global config."""
|
||||
self.settings = ConfigParser(dict_type=AttrDict)
|
||||
|
||||
# Set dynamic default variables.
|
||||
self.settings.set("DEFAULT", "_basedir", basedir)
|
||||
self.settings.set("DEFAULT", "_year", str(datetime.datetime.now().strftime("%Y")))
|
||||
|
||||
# Read the defaults and then apply the custom settings on top.
|
||||
self.settings.read(["defaults.ini", "settings.ini"])
|
||||
|
||||
def print_settings(self):
|
||||
"""Pretty-print the contents of the configuration as JSON."""
|
||||
for section in self.settings.sections():
|
||||
print "[{}]".format(section)
|
||||
for opt in self.settings.options(section):
|
||||
print "{} = {}".format(opt, repr(self.settings.get(section, opt)))
|
||||
print ""
|
||||
|
||||
def load_plugins(self):
|
||||
"""Load all the plugins specified by the config file."""
|
||||
for plugin in self.plugins.blueprints.split("\n"):
|
||||
plugin = plugin.strip()
|
||||
if not plugin:
|
||||
continue
|
||||
load_plugin(plugin)
|
||||
for custom in self.plugins.custom.split("\n"):
|
||||
custom = custom.strip()
|
||||
if not custom:
|
||||
continue
|
||||
load_plugin(custom, as_blueprint=False)
|
||||
|
||||
def __getattr__(self, section):
|
||||
"""Attribute access for the config object.
|
||||
|
||||
You can access config settings via Config.<section>.<name>, for example
|
||||
Config.site.notify_email and Config.blog.posts_per_page. All results are
|
||||
returned as strings per ConfigParser, so cast them if you need to."""
|
||||
return AttrDict(dict(self.settings.items(section)))
|
||||
|
||||
Config = ConfigHandler()
|
|
@ -14,7 +14,7 @@ import json
|
|||
import urlparse
|
||||
|
||||
from rophako.log import logger
|
||||
from config import *
|
||||
from rophako.settings import Config
|
||||
|
||||
|
||||
def login_required(f):
|
||||
|
@ -167,13 +167,13 @@ def render_markdown(body, html_escape=True, extensions=None, blacklist=None):
|
|||
def send_email(to, subject, message, sender=None, reply_to=None):
|
||||
"""Send an e-mail out."""
|
||||
if sender is None:
|
||||
sender = MAIL_SENDER
|
||||
sender = Config.mail.sender
|
||||
|
||||
if type(to) != list:
|
||||
to = [to]
|
||||
|
||||
logger.info("Send email to {}".format(to))
|
||||
if MAIL_METHOD == "smtp":
|
||||
if Config.mail.method == "smtp":
|
||||
# Send mail with SMTP.
|
||||
for email in to:
|
||||
# Construct the mail headers.
|
||||
|
@ -186,7 +186,7 @@ def send_email(to, subject, message, sender=None, reply_to=None):
|
|||
headers.append("Subject: {}".format(subject))
|
||||
|
||||
# Prepare the mail for transport.
|
||||
server = smtplib.SMTP(MAIL_SERVER, MAIL_PORT)
|
||||
server = smtplib.SMTP(Config.mail.server, Config.mail.port)
|
||||
msg = "\n".join(headers) + "\n\n" + message
|
||||
server.sendmail(sender, email, msg)
|
||||
server.quit()
|
||||
|
|
Loading…
Reference in New Issue
Block a user