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.
|
# Don't check in site specific settings.
|
||||||
config.py
|
settings.ini
|
||||||
|
|
||||||
# Compiled Python
|
# Compiled Python
|
||||||
*.pyc
|
*.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 os
|
||||||
import json
|
import json
|
||||||
|
|
||||||
import config
|
from rophako.settings import Config
|
||||||
from rophako.app import app
|
from rophako.app import app
|
||||||
from rophako.utils import template, login_required
|
from rophako.utils import template, login_required
|
||||||
import rophako.model.blog as Blog
|
import rophako.model.blog as Blog
|
||||||
|
@ -109,6 +109,6 @@ def ssl_test():
|
||||||
},
|
},
|
||||||
"App Configuration": {
|
"App Configuration": {
|
||||||
"Session cookies secure": app.config["SESSION_COOKIE_SECURE"],
|
"Session cookies secure": app.config["SESSION_COOKIE_SECURE"],
|
||||||
"config.FORCE_SSL": config.FORCE_SSL,
|
"config.FORCE_SSL": Config.security.force_ssl,
|
||||||
},
|
},
|
||||||
}))
|
}))
|
||||||
|
|
|
@ -23,17 +23,20 @@ app = Flask(__name__,
|
||||||
# jinja2.ChoiceLoader.
|
# jinja2.ChoiceLoader.
|
||||||
BLUEPRINT_PATHS = []
|
BLUEPRINT_PATHS = []
|
||||||
|
|
||||||
import config
|
from rophako.settings import Config
|
||||||
|
Config.load_settings()
|
||||||
|
Config.load_plugins()
|
||||||
|
|
||||||
from rophako import __version__
|
from rophako import __version__
|
||||||
from rophako.plugin import load_plugin
|
from rophako.plugin import load_plugin
|
||||||
import rophako.model.tracking as Tracking
|
import rophako.model.tracking as Tracking
|
||||||
import rophako.utils
|
import rophako.utils
|
||||||
|
|
||||||
app.DEBUG = config.DEBUG
|
app.DEBUG = Config.site.debug == "true"
|
||||||
app.secret_key = config.SECRET_KEY
|
app.secret_key = Config.security.secret_key.decode("string_escape")
|
||||||
|
|
||||||
# Security?
|
# Security?
|
||||||
if config.FORCE_SSL:
|
if Config.security.force_ssl == "true":
|
||||||
app.config['SESSION_COOKIE_SECURE'] = True
|
app.config['SESSION_COOKIE_SECURE'] = True
|
||||||
sslify = SSLify(app)
|
sslify = SSLify(app)
|
||||||
|
|
||||||
|
@ -44,7 +47,7 @@ load_plugin("rophako.modules.account")
|
||||||
# Custom Jinja handler to support custom- and default-template folders for
|
# Custom Jinja handler to support custom- and default-template folders for
|
||||||
# rendering templates.
|
# rendering templates.
|
||||||
template_paths = [
|
template_paths = [
|
||||||
config.SITE_ROOT, # Site specific.
|
Config.site.site_root, # Site specific.
|
||||||
"rophako/www", # Default/fall-back
|
"rophako/www", # Default/fall-back
|
||||||
]
|
]
|
||||||
template_paths.extend(BLUEPRINT_PATHS)
|
template_paths.extend(BLUEPRINT_PATHS)
|
||||||
|
@ -70,7 +73,7 @@ def before_request():
|
||||||
"version": __version__,
|
"version": __version__,
|
||||||
"python_version": "{}.{}".format(sys.version_info.major, sys.version_info.minor),
|
"python_version": "{}.{}".format(sys.version_info.major, sys.version_info.minor),
|
||||||
"author": "Noah Petherbridge",
|
"author": "Noah Petherbridge",
|
||||||
"photo_url": config.PHOTO_ROOT_PUBLIC,
|
"photo_url": Config.photo.root_public,
|
||||||
},
|
},
|
||||||
"uri": request.path,
|
"uri": request.path,
|
||||||
"session": {
|
"session": {
|
||||||
|
@ -125,7 +128,7 @@ def catchall(path):
|
||||||
otherwise we give the 404 error page."""
|
otherwise we give the 404 error page."""
|
||||||
|
|
||||||
# Search for this file.
|
# 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))
|
abspath = os.path.abspath("{}/{}".format(root, path))
|
||||||
if os.path.isfile(abspath):
|
if os.path.isfile(abspath):
|
||||||
return send_file(abspath)
|
return send_file(abspath)
|
||||||
|
|
|
@ -12,7 +12,7 @@ import redis
|
||||||
import json
|
import json
|
||||||
import time
|
import time
|
||||||
|
|
||||||
import config
|
from rophako.settings import Config
|
||||||
from rophako.log import logger
|
from rophako.log import logger
|
||||||
|
|
||||||
redis_client = None
|
redis_client = None
|
||||||
|
@ -111,7 +111,7 @@ def mkpath(document):
|
||||||
if document.endswith(".json"):
|
if document.endswith(".json"):
|
||||||
# Let's not do that.
|
# Let's not do that.
|
||||||
raise Exception("mkpath: document path already includes .json extension!")
|
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):
|
def read_json(path):
|
||||||
|
@ -137,6 +137,7 @@ def read_json(path):
|
||||||
data = json.loads(text)
|
data = json.loads(text)
|
||||||
except:
|
except:
|
||||||
logger.error("Couldn't decode JSON data from {}".format(path))
|
logger.error("Couldn't decode JSON data from {}".format(path))
|
||||||
|
logger.error(text)
|
||||||
data = None
|
data = None
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
@ -179,16 +180,16 @@ def get_redis():
|
||||||
global redis_client
|
global redis_client
|
||||||
if not redis_client:
|
if not redis_client:
|
||||||
redis_client = redis.StrictRedis(
|
redis_client = redis.StrictRedis(
|
||||||
host = config.REDIS_HOST,
|
host = Config.db.redis_host,
|
||||||
port = config.REDIS_PORT,
|
port = Config.db.redis_port,
|
||||||
db = config.REDIS_DB,
|
db = Config.db.redis_db,
|
||||||
)
|
)
|
||||||
return redis_client
|
return redis_client
|
||||||
|
|
||||||
|
|
||||||
def set_cache(key, value, expires=None):
|
def set_cache(key, value, expires=None):
|
||||||
"""Set a key in the Redis cache."""
|
"""Set a key in the Redis cache."""
|
||||||
key = config.REDIS_PREFIX + key
|
key = Config.db.redis_prefix + key
|
||||||
try:
|
try:
|
||||||
client = get_redis()
|
client = get_redis()
|
||||||
client.set(key, json.dumps(value))
|
client.set(key, json.dumps(value))
|
||||||
|
@ -202,7 +203,7 @@ def set_cache(key, value, expires=None):
|
||||||
|
|
||||||
def get_cache(key):
|
def get_cache(key):
|
||||||
"""Get a cached item."""
|
"""Get a cached item."""
|
||||||
key = config.REDIS_PREFIX + key
|
key = Config.db.redis_prefix + key
|
||||||
value = None
|
value = None
|
||||||
try:
|
try:
|
||||||
client = get_redis()
|
client = get_redis()
|
||||||
|
@ -217,6 +218,6 @@ def get_cache(key):
|
||||||
|
|
||||||
def del_cache(key):
|
def del_cache(key):
|
||||||
"""Delete a cached item."""
|
"""Delete a cached item."""
|
||||||
key = config.REDIS_PREFIX + key
|
key = Config.db.redis_prefix + key
|
||||||
client = get_redis()
|
client = get_redis()
|
||||||
client.delete(key)
|
client.delete(key)
|
||||||
|
|
|
@ -7,7 +7,7 @@ from __future__ import print_function
|
||||||
from flask import g, request
|
from flask import g, request
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import config
|
from rophako.settings import Config
|
||||||
|
|
||||||
class LogHandler(logging.Handler):
|
class LogHandler(logging.Handler):
|
||||||
"""A custom logging handler."""
|
"""A custom logging handler."""
|
||||||
|
@ -29,7 +29,7 @@ handler.setFormatter(logging.Formatter("[%(asctime)s] [%(levelname)s] $prefix$%(
|
||||||
logger.addHandler(handler)
|
logger.addHandler(handler)
|
||||||
|
|
||||||
# Log level.
|
# Log level.
|
||||||
if config.DEBUG:
|
if Config.site.debug == "true":
|
||||||
logger.setLevel(logging.DEBUG)
|
logger.setLevel(logging.DEBUG)
|
||||||
else:
|
else:
|
||||||
logger.setLevel(logging.INFO)
|
logger.setLevel(logging.INFO)
|
|
@ -8,7 +8,7 @@ import re
|
||||||
import glob
|
import glob
|
||||||
import os
|
import os
|
||||||
|
|
||||||
import config
|
from rophako.settings import Config
|
||||||
import rophako.jsondb as JsonDB
|
import rophako.jsondb as JsonDB
|
||||||
from rophako.log import logger
|
from rophako.log import logger
|
||||||
|
|
||||||
|
@ -206,7 +206,7 @@ def list_avatars():
|
||||||
# Load avatars from both locations. We check the built-in set first,
|
# 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.
|
# so if you have matching names in your local site those will override.
|
||||||
"rophako/www/static/avatars/*.*",
|
"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 path in paths:
|
||||||
for filename in glob.glob(path):
|
for filename in glob.glob(path):
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
"""Commenting models."""
|
"""Commenting models."""
|
||||||
|
|
||||||
from flask import g, url_for
|
from flask import url_for
|
||||||
import time
|
import time
|
||||||
import hashlib
|
import hashlib
|
||||||
import urllib
|
import urllib
|
||||||
|
@ -10,7 +10,7 @@ import random
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
import config
|
from rophako.settings import Config
|
||||||
import rophako.jsondb as JsonDB
|
import rophako.jsondb as JsonDB
|
||||||
import rophako.model.user as User
|
import rophako.model.user as User
|
||||||
import rophako.model.emoticons as Emoticons
|
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 the e-mail to the site admins.
|
||||||
send_email(
|
send_email(
|
||||||
to=config.NOTIFY_ADDRESS,
|
to=Config.site.notify_address,
|
||||||
subject="New comment: {}".format(subject),
|
subject="New comment: {}".format(subject),
|
||||||
message="""{name} has left a comment on: {subject}
|
message="""{name} has left a comment on: {subject}
|
||||||
|
|
||||||
|
@ -230,7 +230,7 @@ def gravatar(email):
|
||||||
"""Generate a Gravatar link for an email address."""
|
"""Generate a Gravatar link for an email address."""
|
||||||
if "@" in email:
|
if "@" in email:
|
||||||
# Default avatar?
|
# Default avatar?
|
||||||
default = config.COMMENT_DEFAULT_AVATAR
|
default = Config.comment.default_avatar
|
||||||
|
|
||||||
# Construct the URL.
|
# Construct the URL.
|
||||||
params = {
|
params = {
|
||||||
|
|
|
@ -2,14 +2,12 @@
|
||||||
|
|
||||||
"""Emoticon models."""
|
"""Emoticon models."""
|
||||||
|
|
||||||
from flask import g, url_for
|
|
||||||
import os
|
import os
|
||||||
import codecs
|
import codecs
|
||||||
import json
|
import json
|
||||||
import re
|
import re
|
||||||
|
|
||||||
import config
|
from rophako.settings import Config
|
||||||
import rophako.jsondb as JsonDB
|
|
||||||
from rophako.log import logger
|
from rophako.log import logger
|
||||||
|
|
||||||
|
|
||||||
|
@ -18,7 +16,7 @@ _cache = {}
|
||||||
|
|
||||||
def load_theme():
|
def load_theme():
|
||||||
"""Pre-load and cache the emoticon theme. This happens on startup."""
|
"""Pre-load and cache the emoticon theme. This happens on startup."""
|
||||||
theme = config.EMOTICON_THEME
|
theme = Config.emoticons.theme
|
||||||
global _cache
|
global _cache
|
||||||
|
|
||||||
# Cached?
|
# Cached?
|
||||||
|
@ -26,13 +24,13 @@ def load_theme():
|
||||||
return _cache
|
return _cache
|
||||||
|
|
||||||
# Only if the theme file exists.
|
# 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):
|
if not os.path.isfile(settings):
|
||||||
logger.error("Failed to load smiley theme {}: not found!")
|
logger.error("Failed to load smiley theme {}: not found!")
|
||||||
|
|
||||||
# Try the default (tango).
|
# Try the default (tango).
|
||||||
theme = "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):
|
if os.path.isfile(settings):
|
||||||
logger.info("Falling back to default theme: tango")
|
logger.info("Falling back to default theme: tango")
|
||||||
else:
|
else:
|
||||||
|
@ -71,7 +69,7 @@ def render(message):
|
||||||
if trigger in message:
|
if trigger in message:
|
||||||
# Substitute it.
|
# Substitute it.
|
||||||
sub = """<img src="{url}" alt="{trigger}" title="{trigger}">""".format(
|
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,
|
trigger=trigger,
|
||||||
)
|
)
|
||||||
pattern = r'([^A-Za-z0-9:\-]|^){}([^A-Za-z0-9:\-]|$)'.format(re.escape(trigger))
|
pattern = r'([^A-Za-z0-9:\-]|^){}([^A-Za-z0-9:\-]|$)'.format(re.escape(trigger))
|
||||||
|
|
|
@ -10,16 +10,16 @@ from PIL import Image
|
||||||
import hashlib
|
import hashlib
|
||||||
import random
|
import random
|
||||||
|
|
||||||
import config
|
from rophako.settings import Config
|
||||||
import rophako.jsondb as JsonDB
|
import rophako.jsondb as JsonDB
|
||||||
from rophako.utils import sanitize_name, remote_addr
|
from rophako.utils import sanitize_name, remote_addr
|
||||||
from rophako.log import logger
|
from rophako.log import logger
|
||||||
|
|
||||||
# Maps the friendly names of photo sizes with their pixel values from config.
|
# Maps the friendly names of photo sizes with their pixel values from config.
|
||||||
PHOTO_SCALES = dict(
|
PHOTO_SCALES = dict(
|
||||||
large=config.PHOTO_WIDTH_LARGE,
|
large=int(Config.photo.width_large),
|
||||||
thumb=config.PHOTO_WIDTH_THUMB,
|
thumb=int(Config.photo.width_thumb),
|
||||||
avatar=config.PHOTO_WIDTH_AVATAR,
|
avatar=int(Config.photo.width_avatar),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -57,7 +57,6 @@ def list_albums():
|
||||||
def get_album(name):
|
def get_album(name):
|
||||||
"""Get details about an album."""
|
"""Get details about an album."""
|
||||||
index = get_index()
|
index = get_index()
|
||||||
result = []
|
|
||||||
|
|
||||||
if not name in index["albums"]:
|
if not name in index["albums"]:
|
||||||
return None
|
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
|
Returns True on success, False if the new name conflicts with another
|
||||||
album's name."""
|
album's name."""
|
||||||
old_name = sanitize_name(old_name)
|
old_name = sanitize_name(old_name)
|
||||||
newname = sanitize_name(new_name)
|
new_name = sanitize_name(new_name)
|
||||||
index = get_index()
|
index = get_index()
|
||||||
|
|
||||||
# New name is unique?
|
# New name is unique?
|
||||||
|
@ -196,7 +195,7 @@ def get_photo(key):
|
||||||
|
|
||||||
def get_image_dimensions(pic):
|
def get_image_dimensions(pic):
|
||||||
"""Use PIL to get the image's true dimensions."""
|
"""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)
|
img = Image.open(filename)
|
||||||
return img.size
|
return img.size
|
||||||
|
|
||||||
|
@ -233,11 +232,11 @@ def crop_photo(key, x, y, length):
|
||||||
for size in ["thumb", "avatar"]:
|
for size in ["thumb", "avatar"]:
|
||||||
pic = index["albums"][album][key][size]
|
pic = index["albums"][album][key][size]
|
||||||
logger.debug("Delete {} size: {}".format(size, pic))
|
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.
|
# Regenerate all the thumbnails.
|
||||||
large = index["albums"][album][key]["large"]
|
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"]:
|
for size in ["thumb", "avatar"]:
|
||||||
pic = resize_photo(source, size, crop=dict(
|
pic = resize_photo(source, size, crop=dict(
|
||||||
x=x,
|
x=x,
|
||||||
|
@ -305,7 +304,7 @@ def rotate_photo(key, rotate):
|
||||||
|
|
||||||
new_names = dict()
|
new_names = dict()
|
||||||
for size in ["large", "thumb", "avatar"]:
|
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))
|
logger.info("Rotating image {} by {} degrees.".format(fname, degrees))
|
||||||
|
|
||||||
# Give it a new name.
|
# Give it a new name.
|
||||||
|
@ -315,7 +314,7 @@ def rotate_photo(key, rotate):
|
||||||
|
|
||||||
img = Image.open(fname)
|
img = Image.open(fname)
|
||||||
img = img.rotate(degrees)
|
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.
|
# Delete the old name.
|
||||||
os.unlink(fname)
|
os.unlink(fname)
|
||||||
|
@ -339,7 +338,7 @@ def delete_photo(key):
|
||||||
# Delete all the images.
|
# Delete all the images.
|
||||||
for size in ["large", "thumb", "avatar"]:
|
for size in ["large", "thumb", "avatar"]:
|
||||||
logger.info("Delete: {}".format(photo[size]))
|
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):
|
if os.path.isfile(fname):
|
||||||
os.unlink(fname)
|
os.unlink(fname)
|
||||||
|
|
||||||
|
@ -428,7 +427,7 @@ def upload_from_pc(request):
|
||||||
if not allowed_filetype(upload.filename):
|
if not allowed_filetype(upload.filename):
|
||||||
return dict(success=False, error="Unsupported file extension.")
|
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))
|
logger.debug("Save incoming photo to: {}".format(tempfile))
|
||||||
upload.save(tempfile)
|
upload.save(tempfile)
|
||||||
|
|
||||||
|
@ -461,7 +460,7 @@ def upload_from_www(form):
|
||||||
|
|
||||||
# Make a temp filename for it.
|
# Make a temp filename for it.
|
||||||
filetype = url.rsplit(".", 1)[1]
|
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))
|
logger.debug("Save incoming photo to: {}".format(tempfile))
|
||||||
|
|
||||||
# Grab the file.
|
# Grab the file.
|
||||||
|
@ -500,7 +499,7 @@ def process_photo(form, filename):
|
||||||
album = sanitize_name(album)
|
album = sanitize_name(album)
|
||||||
if album == "":
|
if album == "":
|
||||||
logger.warning("Album name didn't pass sanitization! Fall back to default album name.")
|
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.
|
# Make up a unique public key for this set of photos.
|
||||||
key = random_hash()
|
key = random_hash()
|
||||||
|
@ -574,7 +573,7 @@ def resize_photo(filename, size, crop=None):
|
||||||
|
|
||||||
# Make up a unique filename.
|
# Make up a unique filename.
|
||||||
outfile = random_name(filetype)
|
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))
|
logger.debug("Output file for {} scale: {}".format(size, target))
|
||||||
|
|
||||||
# Get the image's dimensions.
|
# Get the image's dimensions.
|
||||||
|
@ -679,7 +678,7 @@ def random_name(filetype):
|
||||||
"""Get a random available file name to save a new photo."""
|
"""Get a random available file name to save a new photo."""
|
||||||
filetype = filetype.lower()
|
filetype = filetype.lower()
|
||||||
outfile = random_hash() + "." + filetype
|
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
|
outfile = random_hash() + "." + filetype
|
||||||
return outfile
|
return outfile
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
import bcrypt
|
import bcrypt
|
||||||
import time
|
import time
|
||||||
|
|
||||||
import config
|
from rophako.settings import Config
|
||||||
import rophako.jsondb as JsonDB
|
import rophako.jsondb as JsonDB
|
||||||
import rophako.model.photo as Photo
|
import rophako.model.photo as Photo
|
||||||
from rophako.log import logger
|
from rophako.log import logger
|
||||||
|
@ -147,7 +147,7 @@ def exists(uid=None, username=None):
|
||||||
|
|
||||||
|
|
||||||
def hash_password(password):
|
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):
|
def check_auth(username, password):
|
||||||
|
|
|
@ -14,8 +14,8 @@ import rophako.model.emoticons as Emoticons
|
||||||
from rophako.utils import (template, render_markdown, pretty_time,
|
from rophako.utils import (template, render_markdown, pretty_time,
|
||||||
login_required, remote_addr)
|
login_required, remote_addr)
|
||||||
from rophako.plugin import load_plugin
|
from rophako.plugin import load_plugin
|
||||||
|
from rophako.settings import Config
|
||||||
from rophako.log import logger
|
from rophako.log import logger
|
||||||
from config import *
|
|
||||||
|
|
||||||
mod = Blueprint("blog", __name__, url_prefix="/blog")
|
mod = Blueprint("blog", __name__, url_prefix="/blog")
|
||||||
load_plugin("rophako.modules.comment")
|
load_plugin("rophako.modules.comment")
|
||||||
|
@ -34,16 +34,16 @@ def archive():
|
||||||
groups = dict()
|
groups = dict()
|
||||||
friendly_months = dict()
|
friendly_months = dict()
|
||||||
for post_id, data in index.items():
|
for post_id, data in index.items():
|
||||||
time = datetime.datetime.fromtimestamp(data["time"])
|
ts = datetime.datetime.fromtimestamp(data["time"])
|
||||||
date = time.strftime("%Y-%m")
|
date = ts.strftime("%Y-%m")
|
||||||
if not date in groups:
|
if not date in groups:
|
||||||
groups[date] = dict()
|
groups[date] = dict()
|
||||||
friendly = time.strftime("%B %Y")
|
friendly = ts.strftime("%B %Y")
|
||||||
friendly_months[date] = friendly
|
friendly_months[date] = friendly
|
||||||
|
|
||||||
# Get author's profile && Pretty-print the time.
|
# Get author's profile && Pretty-print the time.
|
||||||
data["profile"] = User.get_user(uid=data["author"])
|
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
|
groups[date][post_id] = data
|
||||||
|
|
||||||
# Sort by calendar month.
|
# Sort by calendar month.
|
||||||
|
@ -101,10 +101,10 @@ def entry(fid):
|
||||||
# Get the author's information.
|
# Get the author's information.
|
||||||
post["profile"] = User.get_user(uid=post["author"])
|
post["profile"] = User.get_user(uid=post["author"])
|
||||||
post["photo"] = User.get_picture(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.
|
# 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
|
# Count the comments for this post
|
||||||
post["comment_count"] = Comment.count_comments("blog-{}".format(post_id))
|
post["comment_count"] = Comment.count_comments("blog-{}".format(post_id))
|
||||||
|
@ -153,9 +153,9 @@ def update():
|
||||||
format="markdown",
|
format="markdown",
|
||||||
avatar="",
|
avatar="",
|
||||||
categories="",
|
categories="",
|
||||||
privacy=BLOG_DEFAULT_PRIVACY,
|
privacy=Config.blog.default_privacy,
|
||||||
emoticons=True,
|
emoticons=True,
|
||||||
comments=BLOG_ALLOW_COMMENTS,
|
comments=Config.blog.allow_comments,
|
||||||
month="",
|
month="",
|
||||||
day="",
|
day="",
|
||||||
year="",
|
year="",
|
||||||
|
@ -328,14 +328,14 @@ def rss():
|
||||||
today = time.strftime(rss_time, time.gmtime())
|
today = time.strftime(rss_time, time.gmtime())
|
||||||
|
|
||||||
xml_add_text_tags(doc, channel, [
|
xml_add_text_tags(doc, channel, [
|
||||||
["title", RSS_TITLE],
|
["title", Config.blog.title],
|
||||||
["link", RSS_LINK],
|
["link", Config.blog.link],
|
||||||
["description", RSS_DESCRIPTION],
|
["description", Config.blog.description],
|
||||||
["language", RSS_LANGUAGE],
|
["language", Config.blog.language],
|
||||||
["copyright", RSS_COPYRIGHT],
|
["copyright", Config.blog.copyright],
|
||||||
["pubDate", today],
|
["pubDate", today],
|
||||||
["lastBuildDate", today],
|
["lastBuildDate", today],
|
||||||
["webmaster", RSS_WEBMASTER],
|
["webmaster", Config.blog.webmaster],
|
||||||
])
|
])
|
||||||
|
|
||||||
######
|
######
|
||||||
|
@ -345,12 +345,12 @@ def rss():
|
||||||
image = doc.createElement("image")
|
image = doc.createElement("image")
|
||||||
channel.appendChild(image)
|
channel.appendChild(image)
|
||||||
xml_add_text_tags(doc, image, [
|
xml_add_text_tags(doc, image, [
|
||||||
["title", RSS_IMAGE_TITLE],
|
["title", Config.blog.image_title],
|
||||||
["url", RSS_IMAGE_URL],
|
["url", Config.blog.image_url],
|
||||||
["link", RSS_LINK],
|
["link", Config.blog.link],
|
||||||
["width", RSS_IMAGE_WIDTH],
|
["width", Config.blog.image_width],
|
||||||
["height", RSS_IMAGE_HEIGHT],
|
["height", Config.blog.image_height],
|
||||||
["description", RSS_IMAGE_DESCRIPTION],
|
["description", Config.blog.image_description],
|
||||||
])
|
])
|
||||||
|
|
||||||
######
|
######
|
||||||
|
@ -359,7 +359,7 @@ def rss():
|
||||||
|
|
||||||
index = Blog.get_index()
|
index = Blog.get_index()
|
||||||
posts = get_index_posts(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)
|
post = Blog.get_entry(post_id)
|
||||||
item = doc.createElement("item")
|
item = doc.createElement("item")
|
||||||
channel.appendChild(item)
|
channel.appendChild(item)
|
||||||
|
@ -432,8 +432,8 @@ def partial_index():
|
||||||
# Handle the offsets, and get those for the "older" and "earlier" posts.
|
# Handle the offsets, and get those for the "older" and "earlier" posts.
|
||||||
# "earlier" posts count down (towards index 0), "older" counts up.
|
# "earlier" posts count down (towards index 0), "older" counts up.
|
||||||
g.info["offset"] = offset
|
g.info["offset"] = offset
|
||||||
g.info["earlier"] = offset - BLOG_ENTRIES_PER_PAGE if offset > 0 else 0
|
g.info["earlier"] = offset - int(Config.blog.entries_per_page) if offset > 0 else 0
|
||||||
g.info["older"] = offset + BLOG_ENTRIES_PER_PAGE
|
g.info["older"] = offset + int(Config.blog.entries_per_page)
|
||||||
if g.info["earlier"] < 0:
|
if g.info["earlier"] < 0:
|
||||||
g.info["earlier"] = 0
|
g.info["earlier"] = 0
|
||||||
if g.info["older"] < 0 or g.info["older"] > len(posts):
|
if g.info["older"] < 0 or g.info["older"] > len(posts):
|
||||||
|
@ -446,7 +446,7 @@ def partial_index():
|
||||||
|
|
||||||
# Load the selected posts.
|
# Load the selected posts.
|
||||||
selected = []
|
selected = []
|
||||||
stop = offset + BLOG_ENTRIES_PER_PAGE
|
stop = offset + int(Config.blog.entries_per_page)
|
||||||
if stop > len(posts): stop = len(posts)
|
if stop > len(posts): stop = len(posts)
|
||||||
index = 1 # Let each post know its position on-page.
|
index = 1 # Let each post know its position on-page.
|
||||||
for i in range(offset, stop):
|
for i in range(offset, stop):
|
||||||
|
@ -468,9 +468,9 @@ def partial_index():
|
||||||
# Get the author's information.
|
# Get the author's information.
|
||||||
post["profile"] = User.get_user(uid=post["author"])
|
post["profile"] = User.get_user(uid=post["author"])
|
||||||
post["photo"] = User.get_picture(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
|
# Count the comments for this post
|
||||||
post["comment_count"] = Comment.count_comments("blog-{}".format(post_id))
|
post["comment_count"] = Comment.count_comments("blog-{}".format(post_id))
|
||||||
|
|
|
@ -2,8 +2,7 @@
|
||||||
|
|
||||||
"""Endpoints for the commenting subsystem."""
|
"""Endpoints for the commenting subsystem."""
|
||||||
|
|
||||||
from flask import Blueprint, g, request, redirect, url_for, session, flash
|
from flask import Blueprint, g, request, redirect, url_for, flash
|
||||||
import re
|
|
||||||
import time
|
import time
|
||||||
|
|
||||||
import rophako.model.user as User
|
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,
|
from rophako.utils import (template, pretty_time, login_required, sanitize_name,
|
||||||
remote_addr)
|
remote_addr)
|
||||||
from rophako.plugin import load_plugin
|
from rophako.plugin import load_plugin
|
||||||
from rophako.log import logger
|
from rophako.settings import Config
|
||||||
from config import *
|
|
||||||
|
|
||||||
mod = Blueprint("comment", __name__, url_prefix="/comments")
|
mod = Blueprint("comment", __name__, url_prefix="/comments")
|
||||||
load_plugin("rophako.modules.emoticons")
|
load_plugin("rophako.modules.emoticons")
|
||||||
|
@ -71,7 +69,7 @@ def preview():
|
||||||
# Gravatar.
|
# Gravatar.
|
||||||
g.info["gravatar"] = gravatar
|
g.info["gravatar"] = gravatar
|
||||||
g.info["preview"] = Comment.format_message(form["message"])
|
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)
|
g.info.update(form)
|
||||||
return template("comment/preview.html")
|
return template("comment/preview.html")
|
||||||
|
@ -191,7 +189,7 @@ def partial_index(thread, subject, header=True):
|
||||||
comment["image"] = avatar
|
comment["image"] = avatar
|
||||||
|
|
||||||
# Add the pretty time.
|
# 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.
|
# Format the message for display.
|
||||||
comment["formatted_message"] = Comment.format_message(comment["message"])
|
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["subject"] = subject
|
||||||
g.info["url"] = request.url
|
g.info["url"] = request.url
|
||||||
g.info["comments"] = sorted_comments
|
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")
|
return template("comment/index.inc.html")
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
from flask import Blueprint, request, redirect, url_for, flash
|
from flask import Blueprint, request, redirect, url_for, flash
|
||||||
|
|
||||||
from rophako.utils import template, send_email, remote_addr
|
from rophako.utils import template, send_email, remote_addr
|
||||||
from config import *
|
from rophako.settings import Config
|
||||||
|
|
||||||
mod = Blueprint("contact", __name__, url_prefix="/contact")
|
mod = Blueprint("contact", __name__, url_prefix="/contact")
|
||||||
|
|
||||||
|
@ -42,9 +42,9 @@ def send():
|
||||||
|
|
||||||
# Send the e-mail.
|
# Send the e-mail.
|
||||||
send_email(
|
send_email(
|
||||||
to=NOTIFY_ADDRESS,
|
to=Config.site.notify_address,
|
||||||
reply_to=reply_to,
|
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!
|
message="""A visitor to {site_name} has sent you a message!
|
||||||
|
|
||||||
IP Address: {ip}
|
IP Address: {ip}
|
||||||
|
@ -55,7 +55,7 @@ E-mail: {email}
|
||||||
Subject: {subject}
|
Subject: {subject}
|
||||||
|
|
||||||
{message}""".format(
|
{message}""".format(
|
||||||
site_name=SITE_NAME,
|
site_name=Config.site.site_name,
|
||||||
ip=remote_addr(),
|
ip=remote_addr(),
|
||||||
ua=request.user_agent.string,
|
ua=request.user_agent.string,
|
||||||
referer=request.headers.get("Referer", ""),
|
referer=request.headers.get("Referer", ""),
|
||||||
|
|
|
@ -6,8 +6,7 @@ from flask import Blueprint, g
|
||||||
|
|
||||||
import rophako.model.emoticons as Emoticons
|
import rophako.model.emoticons as Emoticons
|
||||||
from rophako.utils import template
|
from rophako.utils import template
|
||||||
from rophako.log import logger
|
from rophako.settings import Config
|
||||||
from config import *
|
|
||||||
|
|
||||||
mod = Blueprint("emoticons", __name__, url_prefix="/emoticons")
|
mod = Blueprint("emoticons", __name__, url_prefix="/emoticons")
|
||||||
|
|
||||||
|
@ -24,7 +23,7 @@ def index():
|
||||||
"triggers": theme["map"][img],
|
"triggers": theme["map"][img],
|
||||||
})
|
})
|
||||||
|
|
||||||
g.info["theme"] = EMOTICON_THEME
|
g.info["theme"] = Config.emoticons.theme
|
||||||
g.info["theme_name"] = theme["name"]
|
g.info["theme_name"] = theme["name"]
|
||||||
g.info["smileys"] = smileys
|
g.info["smileys"] = smileys
|
||||||
return template("emoticons/index.html")
|
return template("emoticons/index.html")
|
|
@ -2,15 +2,14 @@
|
||||||
|
|
||||||
"""Endpoints for the photo albums."""
|
"""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.user as User
|
||||||
import rophako.model.photo as Photo
|
import rophako.model.photo as Photo
|
||||||
from rophako.utils import (template, pretty_time, render_markdown,
|
from rophako.utils import (template, pretty_time, render_markdown,
|
||||||
login_required, ajax_response)
|
login_required, ajax_response)
|
||||||
from rophako.plugin import load_plugin
|
from rophako.plugin import load_plugin
|
||||||
from rophako.log import logger
|
from rophako.settings import Config
|
||||||
from config import *
|
|
||||||
|
|
||||||
mod = Blueprint("photo", __name__, url_prefix="/photos")
|
mod = Blueprint("photo", __name__, url_prefix="/photos")
|
||||||
load_plugin("rophako.modules.comment")
|
load_plugin("rophako.modules.comment")
|
||||||
|
@ -68,7 +67,7 @@ def view_photo(key):
|
||||||
|
|
||||||
g.info["photo"] = photo
|
g.info["photo"] = photo
|
||||||
g.info["photo"]["key"] = key
|
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", ""))
|
g.info["photo"]["markdown"] = render_markdown(photo.get("description", ""))
|
||||||
return template("photos/view.html")
|
return template("photos/view.html")
|
||||||
|
|
||||||
|
@ -125,10 +124,10 @@ def upload():
|
||||||
g.info["album_list"] = [
|
g.info["album_list"] = [
|
||||||
"My Photos", # the default
|
"My Photos", # the default
|
||||||
]
|
]
|
||||||
g.info["selected"] = PHOTO_DEFAULT_ALBUM
|
g.info["selected"] = Config.photo.default_album
|
||||||
albums = Photo.list_albums()
|
albums = Photo.list_albums()
|
||||||
if len(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]
|
g.info["selected"] = albums[0]
|
||||||
|
|
||||||
return template("photos/upload.html")
|
return template("photos/upload.html")
|
||||||
|
|
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
|
import urlparse
|
||||||
|
|
||||||
from rophako.log import logger
|
from rophako.log import logger
|
||||||
from config import *
|
from rophako.settings import Config
|
||||||
|
|
||||||
|
|
||||||
def login_required(f):
|
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):
|
def send_email(to, subject, message, sender=None, reply_to=None):
|
||||||
"""Send an e-mail out."""
|
"""Send an e-mail out."""
|
||||||
if sender is None:
|
if sender is None:
|
||||||
sender = MAIL_SENDER
|
sender = Config.mail.sender
|
||||||
|
|
||||||
if type(to) != list:
|
if type(to) != list:
|
||||||
to = [to]
|
to = [to]
|
||||||
|
|
||||||
logger.info("Send email to {}".format(to))
|
logger.info("Send email to {}".format(to))
|
||||||
if MAIL_METHOD == "smtp":
|
if Config.mail.method == "smtp":
|
||||||
# Send mail with SMTP.
|
# Send mail with SMTP.
|
||||||
for email in to:
|
for email in to:
|
||||||
# Construct the mail headers.
|
# Construct the mail headers.
|
||||||
|
@ -186,7 +186,7 @@ def send_email(to, subject, message, sender=None, reply_to=None):
|
||||||
headers.append("Subject: {}".format(subject))
|
headers.append("Subject: {}".format(subject))
|
||||||
|
|
||||||
# Prepare the mail for transport.
|
# 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
|
msg = "\n".join(headers) + "\n\n" + message
|
||||||
server.sendmail(sender, email, msg)
|
server.sendmail(sender, email, msg)
|
||||||
server.quit()
|
server.quit()
|
||||||
|
|
Loading…
Reference in New Issue
Block a user