Browse Source

Merge pull request #275 from sh4nks/makeconfig

Generate config file via the command line
Peter Justin 8 years ago
parent
commit
6f5b4698d7

+ 2 - 2
.gitignore

@@ -44,6 +44,8 @@ __pycache__
 *.iml
 flaskbb/configs/production.py
 flaskbb/configs/development.py
+flaskbb/configs/*.cfg
+*.cfg
 .tox
 *.rdb
 .cache
@@ -57,5 +59,3 @@ node_modules
 
 # Ignore plugins
 flaskbb/plugins/*
-!flaskbb/plugins/__init__.py
-!flaskbb/plugins/portal/

+ 1 - 1
Makefile

@@ -20,7 +20,7 @@ test:
 	py.test
 
 run:
-	flaskbb --config flaskbb.configs.development.DevelopmentConfig run
+	flaskbb --config ./flaskbb.cfg run
 
 install:dependencies
 	clear

+ 45 - 36
docs/installation.rst

@@ -117,69 +117,77 @@ this is `pacman` and for Debian/Ubuntu based systems this is `apt-get`.
 Configuration
 -------------
 
-FlaskBB will no longer assume which config to use. By default, it will load
-a config with some sane defaults (i.e. debug off) but thats it.
-You can either pass the import string to a config object or
-the path to the config file, which is in turn a valid python file.
+FlaskBB comes with the ability to generate the configuration file for you.
+Just run::
 
-A valid import string, for example, is::
+    flaskbb makeconfig
 
-    flaskbb.configs.development.DevelopmentConfig
+and answer its questions. By default it will try to save the configuration
+file with the name ``flaskbb.cfg`` in FlaskBB's root folder.
 
-and if you wish to use a configuration file, you are free to place it anywhere
-your app user has read access. Please note, that if you decide to use a
-relativ path, it will start looking for the file in the 'root' directory
-of FlaskBB (this is, where the README.md, LICENSE, etc. files are in).
-Absolut paths are also supported. Use whatever you like.
-::
+You can also omit the questions, which will generate a **developemnt**
+configuration by passing the ``-d/--development`` option to it::
+
+    flaskbb makeconfig -d
+
+In previous versions, FlaskBB tried to assume which configuration file to use,
+which it will no longer do. Now, by default, it will load a config with
+some sane defaults (i.e. debug off) but thats it. You can either pass an
+import string to a config object or the path to the (python) config file.
 
-    flaskbb --config dev_config.cfg run
-    [+] Using config from: /path/to/flaskbb/dev_config.cfg
+For example, if you are using a generated config file it looks something
+like this::
+
+    flaskbb --config flaskbb.cfg run
+    [+] Using config from: /path/to/flaskbb/flaskbb.cfg
      * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
 
+and this is how you do it by using an import string. Be sure that it is
+importable from within FlaskBB::
+
+    flaskbb --config flaskbb.configs.default.DefaultConfig run
+
 
 Development
 ~~~~~~~~~~~
 
-For development, you need to copy ``flaskbb/configs/development.py.example`` to
-``flaskbb/configs/development.py``.
-::
-
-    cp flaskbb/configs/development.py.example flaskbb/configs/development.py
+To get started with development you have to generate a development
+configuration first. You can use the CLI for this,
+as explained in `Configuration <#configuration>`_::
 
-And you should be ready to go!
+    flaskbb makeconfig --development
 
-You can either run::
+Now you can either use ``make`` to run the development server::
 
     make run
 
-or::
-
-    flaskbb --config flaskbb.configs.development.DevelopmentConfig run
+or if you like to type a little bit more, the CLI::
 
-to start the development server using the development config.
+    flaskbb --config flaskbb.cfg run
 
 
 Production
 ~~~~~~~~~~
 
 FlaskBB already sets some sane defaults, so you shouldn't have to change much.
-There are only a few things you have to do. Here we will use the provided
-`production.py.example` configuration file as a template.
+To make this whole process a little bit easier for you, we have created
+a little wizard which will ask you some questions and with the answers
+you provide it will generate a configuration for you. You can of course
+further adjust the generated configuration.
+
+The setup wizard can be started with::
 
-Let's copy the example config (production.py file is in .gitignore)::
+    flaskbb makeconfig
 
-    cp flaskbb/configs/production.py.example flaskbb/configs/production.py
 
-and now you are ready to start adjusting the config.
-Open `production.py` with your favorite editor and search for the following
-configuration variables and change them accordingly to your needs:
+These are the only settings you have to make sure to setup accordingly if
+you want to run FlaskBB in production:
 
 - ``SERVER_NAME = "example.org"``
 - ``PREFERRED_URL_SCHEME = "https"``
 - ``SQLALCHEMY_DATABASE_URI = 'sqlite:///path/to/flaskbb.sqlite'``
 - ``SECRET_KEY = "secret key"``
-- ``WTF_CSRF_SECRET_KEY = "reallyhardtoguess"``
+- ``WTF_CSRF_SECRET_KEY = "secret key"``
 
 
 Redis
@@ -190,13 +198,14 @@ the following services and features can be enabled and configured to use redis.
 
 Before you can start using redis, you have to enable and configure it.
 This is quite easy just set ``REDIS_ENABLE`` to ``True`` and adjust the
-``REDIS_URL`` if needed.::
+``REDIS_URL`` if needed.
+::
 
     REDIS_ENABLED = True
     REDIS_URL = "redis://localhost:6379"  # or with a password: "redis://:password@localhost:6379"
     REDIS_DATABASE = 0
 
-The other services are already configured to use the REDIS_URL configuration
+The other services are already configured to use the ``REDIS_URL`` configuration
 variable.
 
 **Celery**
@@ -257,7 +266,7 @@ or::
 
 During the installation process you are asked about your username,
 your email address and the password for your administrator user. Using the
-`make install` command is recommended as it checks that the dependencies
+``make install`` command is recommended as it checks that the dependencies
 are also installed.
 
 

+ 171 - 3
flaskbb/cli/main.py

@@ -12,18 +12,23 @@ import sys
 import os
 import time
 import requests
+import binascii
+from datetime import datetime
 
 import click
 from werkzeug.utils import import_string, ImportStringError
+from jinja2 import Environment, FileSystemLoader
 from flask import current_app
 from flask.cli import FlaskGroup, ScriptInfo, with_appcontext
-from sqlalchemy_utils.functions import database_exists, create_database, drop_database
+from sqlalchemy_utils.functions import (database_exists, create_database,
+                                        drop_database)
 from flask_migrate import upgrade as upgrade_database
 
 from flaskbb import create_app
 from flaskbb._compat import iteritems
 from flaskbb.extensions import db, whooshee, celery
-from flaskbb.cli.utils import (get_version, save_user_prompt, FlaskBBCLIError,
+from flaskbb.cli.utils import (prompt_save_user, prompt_config_path,
+                               write_config, get_version, FlaskBBCLIError,
                                EmailType)
 from flaskbb.utils.populate import (create_test_data, create_welcome_forum,
                                     create_default_groups,
@@ -105,7 +110,7 @@ def install(welcome, force, username, email, password, group):
     create_default_settings()
 
     click.secho("[+] Creating admin user...", fg="cyan")
-    save_user_prompt(username, email, password, group)
+    prompt_save_user(username, email, password, group)
 
     if welcome:
         click.secho("[+] Creating welcome forum...", fg="cyan")
@@ -403,3 +408,166 @@ def list_urls(order_by):
     for rule in rules:
         methods = ", ".join(rule.methods)
         click.echo(column_template.format(rule.rule, rule.endpoint, methods))
+
+
+@flaskbb.command("makeconfig")
+@click.option("--development", "-d", default=False, is_flag=True,
+              help="Creates a development config with DEBUG set to True.")
+@click.option("--output", "-o", required=False,
+              help="The path where the config file will be saved at. "
+                   "Defaults to the flaskbb's root folder.")
+@click.option("--force", "-f", default=False, is_flag=True,
+              help="Overwrite any existing config file if one exists.")
+def generate_config(development, output, force):
+    """Generates a FlaskBB configuration file."""
+    config_env = Environment(
+        loader=FileSystemLoader(os.path.join(current_app.root_path, "configs"))
+    )
+    config_template = config_env.get_template('config.cfg.template')
+
+    if output:
+        config_path = os.path.abspath(output)
+    else:
+        config_path = os.path.dirname(current_app.root_path)
+
+    if os.path.exists(config_path) and not os.path.isfile(config_path):
+        config_path = os.path.join(config_path, "flaskbb.cfg")
+
+    default_conf = {
+        "is_debug": True,
+        "server_name": "localhost",
+        "url_scheme": "http",
+        "database_uri": "sqlite:///" + os.path.join(
+            os.path.dirname(current_app.root_path), "flaskbb.sqlite"),
+        "redis_enabled": False,
+        "redis_uri": "redis://localhost:6379",
+        "mail_server": "localhost",
+        "mail_port": 25,
+        "mail_use_tls": False,
+        "mail_use_ssl": False,
+        "mail_username": "",
+        "mail_password": "",
+        "mail_sender_name": "FlaskBB Mailer",
+        "mail_sender_address": "noreply@yourdomain",
+        "mail_admin_address": "admin@yourdomain",
+        "secret_key": binascii.hexlify(os.urandom(24)).decode(),
+        "csrf_secret_key": binascii.hexlify(os.urandom(24)).decode(),
+        "timestamp": datetime.utcnow().strftime("%A, %d. %B %Y at %H:%M")
+    }
+
+    if not force:
+        config_path = prompt_config_path(config_path)
+
+    if force and os.path.exists(config_path):
+        click.secho("Overwriting existing config file: {}".format(config_path),
+                    fg="yellow")
+
+    if development:
+        write_config(default_conf, config_template, config_path)
+        sys.exit(0)
+
+    # SERVER_NAME
+    click.secho("The name and port number of the server.\n"
+                "This is needed to correctly generate URLs when no request "
+                "context is available.", fg="cyan")
+    default_conf["server_name"] = click.prompt(
+        click.style("Server Name", fg="magenta"), type=str,
+        default=default_conf.get("server_name"))
+
+    # PREFERRED_URL_SCHEME
+    click.secho("The URL Scheme is also needed in order to generate correct "
+                "URLs when no request context is available.\n"
+                "Choose either 'https' or 'http'.", fg="cyan")
+    default_conf["url_scheme"] = click.prompt(
+        click.style("URL Scheme", fg="magenta"),
+        type=click.Choice(["https", "http"]),
+        default=default_conf.get("url_scheme"))
+
+    # SQLALCHEMY_DATABASE_URI
+    click.secho("For Postgres use:\n"
+                "    postgresql://flaskbb@localhost:5432/flaskbb\n"
+                "For more options see the SQLAlchemy docs:\n"
+                "    http://docs.sqlalchemy.org/en/latest/core/engines.html",
+                fg="cyan")
+    default_conf["database_url"] = click.prompt(
+        click.style("Database URI", fg="magenta"),
+        default=default_conf.get("database_uri"))
+
+    # REDIS_ENABLED
+    click.secho("Redis will be used for things such as the task queue, "
+                "caching and rate limiting.", fg="cyan")
+    default_conf["redis_enabled"] = click.confirm(
+        click.style("Would you like to use redis?", fg="magenta"),
+        default=True)  # default_conf.get("redis_enabled") is False
+
+    # REDIS_URI
+    if default_conf.get("redis_enabled", False):
+        default_conf["redis_uri"] = click.prompt(
+            click.style("Redis URI", fg="magenta"),
+            default=default_conf.get("redis_uri"))
+    else:
+        default_conf["redis_uri"] = ""
+
+    # MAIL_SERVER
+    click.secho("To use 'localhost' make sure that you have sendmail or\n"
+                "something similar installed. Gmail is also supprted.",
+                fg="cyan")
+    default_conf["mail_server"] = click.prompt(
+        click.style("Mail Server", fg="magenta"),
+        default=default_conf.get("mail_server"))
+    # MAIL_PORT
+    click.secho("The port on which the SMTP server is listening on.",
+                fg="cyan")
+    default_conf["mail_port"] = click.prompt(
+        click.style("Mail Server SMTP Port", fg="magenta"),
+        default=default_conf.get("mail_port"))
+    # MAIL_USE_TLS
+    click.secho("If you are using a local SMTP server like sendmail this is "
+                "not needed. For external servers this is hopefully required.",
+                fg="cyan")
+    default_conf["mail_use_tls"] = click.confirm(
+        click.style("Use TLS for sending mails?", fg="magenta"),
+        default=default_conf.get("mail_use_tls"))
+    # MAIL_USE_SSL
+    click.secho("Same as above. TLS is the successor to SSL.", fg="cyan")
+    default_conf["mail_use_ssl"] = click.confirm(
+        click.style("Use SSL for sending mails?", fg="magenta"),
+        default=default_conf.get("mail_use_ssl"))
+    # MAIL_USERNAME
+    click.secho("Not needed if using a local smtp server. For gmail you have "
+                "put in your email address here.", fg="cyan")
+    default_conf["mail_username"] = click.prompt(
+        click.style("Mail Username", fg="magenta"),
+        default=default_conf.get("mail_username"))
+    # MAIL_PASSWORD
+    click.secho("Not needed if using a local smtp server. For gmail you have "
+                "put in your gmail password here.", fg="cyan")
+    default_conf["mail_password"] = click.prompt(
+        click.style("Mail Password", fg="magenta"),
+        default=default_conf.get("mail_password"))
+    # MAIL_DEFAULT_SENDER
+    click.secho("The name of the sender. You probably want to change it to "
+                "something like '<your_community> Mailer'.", fg="cyan")
+    default_conf["mail_sender_name"] = click.prompt(
+        click.style("Mail Sender Name", fg="magenta"),
+        default=default_conf.get("mail_sender_name"))
+    click.secho("On localhost you want to use a noreply address here. "
+                "Use your email address for gmail here.", fg="cyan")
+    default_conf["mail_sender_address"] = click.prompt(
+        click.style("Mail Sender Address", fg="magenta"),
+        default=default_conf.get("mail_sender_address"))
+    # ADMINS
+    click.secho("Logs and important system messages are sent to this address."
+                "Use your email address for gmail here.", fg="cyan")
+    default_conf["mail_admin_address"] = click.prompt(
+        click.style("Mail Admin Email", fg="magenta"),
+        default=default_conf.get("mail_admin_address"))
+
+    write_config(default_conf, config_template, config_path)
+
+    # Finished
+    click.secho("The configuration file has been saved to:\n{cfg}"
+                .format(cfg=config_path), fg="blue")
+    click.secho("Usage: flaskbb --config {cfg} run\n"
+                "Feel free to adjust it as needed."
+                .format(cfg=config_path), fg="green")

+ 3 - 3
flaskbb/cli/users.py

@@ -15,7 +15,7 @@ import click
 from sqlalchemy.exc import IntegrityError
 
 from flaskbb.cli.main import flaskbb
-from flaskbb.cli.utils import FlaskBBCLIError, EmailType, save_user_prompt
+from flaskbb.cli.utils import FlaskBBCLIError, EmailType, prompt_save_user
 from flaskbb.user.models import User
 
 
@@ -35,7 +35,7 @@ def users():
 def new_user(username, email, password, group):
     """Creates a new user. Omit any options to use the interactive mode."""
     try:
-        user = save_user_prompt(username, email, password, group)
+        user = prompt_save_user(username, email, password, group)
 
         click.secho("[+] User {} with Email {} in Group {} created.".format(
             user.username, user.email, user.primary_group.name), fg="cyan"
@@ -56,7 +56,7 @@ def new_user(username, email, password, group):
 def change_user(username, password, email, group):
     """Updates an user. Omit any options to use the interactive mode."""
 
-    user = save_user_prompt(username, password, email, group)
+    user = prompt_save_user(username, password, email, group)
     if user is None:
         raise FlaskBBCLIError("The user with username {} does not exist."
                               .format(username), fg="red")

+ 65 - 28
flaskbb/cli/utils.py

@@ -13,7 +13,6 @@ import sys
 import os
 import re
 
-
 import click
 
 from flask import __version__ as flask_version
@@ -70,33 +69,6 @@ class EmailType(click.ParamType):
         return "email"
 
 
-def save_user_prompt(username, email, password, group, only_update=False):
-    if not username:
-        username = click.prompt(
-            click.style("Username", fg="magenta"), type=str,
-            default=os.environ.get("USER", "")
-        )
-    if not email:
-        email = click.prompt(
-            click.style("Email address", fg="magenta"), type=EmailType()
-        )
-    if not password:
-        password = click.prompt(
-            click.style("Password", fg="magenta"), hide_input=True,
-            confirmation_prompt=True
-        )
-    if not group:
-        group = click.prompt(
-            click.style("Group", fg="magenta"),
-            type=click.Choice(["admin", "super_mod", "mod", "member"]),
-            default="admin"
-        )
-
-    if only_update:
-        return update_user(username, password, email, group)
-    return create_user(username, password, email, group)
-
-
 def validate_plugin(plugin):
     """Checks if a plugin is installed.
     TODO: Figure out how to use this in a callback. Doesn't work because
@@ -137,3 +109,68 @@ def get_version(ctx, param, value):
         'python_version': sys.version.split("\n")[0]
     }, color=ctx.color)
     ctx.exit()
+
+
+def prompt_save_user(username, email, password, group, only_update=False):
+    if not username:
+        username = click.prompt(
+            click.style("Username", fg="magenta"), type=str,
+            default=os.environ.get("USER", "")
+        )
+    if not email:
+        email = click.prompt(
+            click.style("Email address", fg="magenta"), type=EmailType()
+        )
+    if not password:
+        password = click.prompt(
+            click.style("Password", fg="magenta"), hide_input=True,
+            confirmation_prompt=True
+        )
+    if not group:
+        group = click.prompt(
+            click.style("Group", fg="magenta"),
+            type=click.Choice(["admin", "super_mod", "mod", "member"]),
+            default="admin"
+        )
+
+    if only_update:
+        return update_user(username, password, email, group)
+    return create_user(username, password, email, group)
+
+
+def prompt_config_path(config_path):
+    """Asks for a config path. If the path exists it will ask the user
+    for a new path until a he enters a path that doesn't exist.
+
+    :param config_path: The path to the configuration.
+    """
+    click.secho("The path to save this configuration file.", fg="cyan")
+    while True:
+        if os.path.exists(config_path) and click.confirm(click.style(
+            "Config {cfg} exists. Do you want to overwrite it?"
+            .format(cfg=config_path), fg="magenta")
+        ):
+            break
+
+        config_path = click.prompt(
+            click.style("Save to", fg="magenta"),
+            default=config_path)
+
+        if not os.path.exists(config_path):
+            break
+
+    return config_path
+
+
+def write_config(config, config_template, config_path):
+    """Writes a new config file based upon the config template.
+
+    :param config: A dict containing all the key/value pairs which should be
+                   used for the new configuration file.
+    :param config_template: The config (jinja2-)template.
+    :param config_path: The place to write the new config file.
+    """
+    with open(config_path, 'w') as cfg_file:
+        cfg_file.write(
+            config_template.render(**config).encode("utf-8")
+        )

+ 166 - 0
flaskbb/configs/config.cfg.template

@@ -0,0 +1,166 @@
+# This file has been automatically generated on {{ timestamp }}.
+# Feel free to adjust it as needed.
+import os
+import datetime
+from flaskbb.configs.default import DefaultConfig
+
+
+# Flask Settings
+# ------------------------------
+# There is a whole bunch of more settings available here:
+# http://flask.pocoo.org/docs/0.11/config/#builtin-configuration-values
+DEBUG = {{ is_debug }}
+TESTING = False
+
+# Server Name - REQUIRED for Celery/Mailing
+# The name and port number of the server.
+# Required for subdomain support (e.g.: 'myapp.dev:5000') and
+# URL generation without a request context but with an application context
+# which we need in order to generate URLs (with the celery application)
+# Note that localhost does not support subdomains so setting this to
+# “localhost” does not help.
+# Example for the FlaskBB forums: SERVER_NAME = "forums.flaskbb.org"
+SERVER_NAME = "{{ server_name }}"
+
+# Prefer HTTPS over HTTP
+PREFERRED_URL_SCHEME = "{{ url_scheme }}"
+
+
+# Database
+# ------------------------------
+# For PostgresSQL:
+#SQLALCHEMY_DATABASE_URI = "postgresql://flaskbb@localhost:5432/flaskbb"
+# For SQLite:
+SQLALCHEMY_DATABASE_URI = "{{ database_uri }}"
+
+# This option will be removed as soon as Flask-SQLAlchemy removes it.
+# At the moment it is just used to suppress the super annoying warning
+SQLALCHEMY_TRACK_MODIFICATIONS = False
+# This will print all SQL statements
+SQLALCHEMY_ECHO = False
+
+
+# Security - IMPORTANT
+# ------------------------------
+# This is the secret key that is used for session signing.
+# You can generate a secure key with os.urandom(24)
+SECRET_KEY = "{{ secret_key }}"
+
+# You can generate the WTF_CSRF_SECRET_KEY the same way as you have
+# generated the SECRET_KEY. If no WTF_CSRF_SECRET_KEY is provided, it will
+# use the SECRET_KEY.
+WTF_CSRF_ENABLED = True
+WTF_CSRF_SECRET_KEY = "{{ csrf_secret_key }}"
+
+
+# Auth
+# ------------------------------
+LOGIN_VIEW = "auth.login"
+REAUTH_VIEW = "auth.reauth"
+LOGIN_MESSAGE_CATEGORY = "info"
+REFRESH_MESSAGE_CATEGORY = "info"
+
+# The name of the cookie to store the “remember me” information in.
+REMEMBER_COOKIE_NAME = "remember_token"
+# The amount of time before the cookie expires, as a datetime.timedelta object.
+# Default: 365 days (1 non-leap Gregorian year)
+REMEMBER_COOKIE_DURATION = datetime.timedelta(days=365)
+# If the “Remember Me” cookie should cross domains,
+# set the domain value here (i.e. .example.com would allow the cookie
+# to be used on all subdomains of example.com).
+REMEMBER_COOKIE_DOMAIN = None
+# Limits the “Remember Me” cookie to a certain path.
+REMEMBER_COOKIE_PATH = "/"
+# Restricts the “Remember Me” cookie’s scope to secure channels (typically HTTPS).
+REMEMBER_COOKIE_SECURE = None
+# Prevents the “Remember Me” cookie from being accessed by client-side scripts.
+REMEMBER_COOKIE_HTTPONLY = False
+
+
+# Full-Text-Search
+# ------------------------------
+# This will use the "whoosh_index" directory to store the search indexes
+WHOOSHEE_DIR = os.path.join(DefaultConfig.basedir, "whoosh_index", DefaultConfig.py_version)
+# How long should whooshee try to acquire write lock? (defaults to 2)
+WHOOSHEE_WRITER_TIMEOUT = 2
+# Minimum number of characters for the search (defaults to 3)
+WHOOSHEE_MIN_STRING_LEN = 3
+
+
+# Redis
+# ------------------------------
+# If redis is enabled, it can be used for:
+#   - Sending non blocking emails via Celery (Task Queue)
+#   - Caching
+#   - Rate Limiting
+REDIS_ENABLED = {{ redis_enabled }}
+REDIS_URL = "{{ redis_uri }}"
+REDIS_DATABASE = 0
+
+
+# Celery
+# ------------------------------
+CELERY_BROKER_URL = "{{ redis_uri }}"
+CELERY_RESULT_BACKEND = "{{ redis_uri }}"
+
+
+# Rate Limiting
+# -------------------------------
+# A full list with configuration values is available at the flask-limiter
+# docs, but you actually just need those settings below.
+# You can disabled the Rate Limiter here as well - it will overwrite
+# the setting from the admin panel!
+# RATELIMIT_ENABLED = True
+# You can choose from:
+#   memory:// (default)
+#   redis://host:port
+#   memcached://host:port
+# Using the redis storage requires the installation of the redis package,
+# which will be installed if you enable REDIS_ENABLE while memcached
+# relies on the pymemcache package.
+RATELIMIT_STORAGE_URL = "{% if redis_enabled %}{{ redis_uri }}{% else %}memory://{% endif %}"
+
+
+# Caching
+# ------------------------------
+# For all available caching types, have a look at the Flask-Cache docs
+# https://pythonhosted.org/Flask-Caching/#configuring-flask-caching
+CACHE_TYPE = "{% if redis_enabled %}redis{% else %}simple{% endif %}"
+CACHE_DEFAULT_TIMEOUT = 60
+
+
+# Mail
+# ------------------------------
+# Google Mail Example
+# https://support.google.com/mail/answer/7126229?hl=en
+#MAIL_SERVER = "smtp.gmail.com"
+#MAIL_PORT = 587
+#MAIL_USE_TLS = True
+#MAIL_USE_SSL = True
+#MAIL_USERNAME = "your_username@gmail.com"
+#MAIL_PASSWORD = "your_password"
+#MAIL_DEFAULT_SENDER = ("Your Name", "your_username@gmail.com")
+
+# Local SMTP Server
+MAIL_SERVER = "{{ mail_server }}"
+MAIL_PORT = {{ mail_port }}
+MAIL_USE_SSL = {{ mail_use_ssl }}
+MAIL_USE_TLS = {{ mail_use_tls }}
+MAIL_USERNAME = "{{ mail_username }}"
+MAIL_PASSWORD = "{{ mail_password }}"
+MAIL_DEFAULT_SENDER = ("{{ mail_sender_name }}", "{{ mail_sender_address }}")
+# Where to logger should send the emails to
+ADMINS = ["{{ mail_admin_address }}"]
+# If SEND_LOGS is set to True, the admins (see the mail configuration) will
+# recieve the error logs per email.
+SEND_LOGS = False
+
+
+# FlaskBB Settings
+# ------------------------------ #
+# URL Prefixes
+FORUM_URL_PREFIX = ""
+USER_URL_PREFIX = "/user"
+MESSAGE_URL_PREFIX = "/message"
+AUTH_URL_PREFIX = "/auth"
+ADMIN_URL_PREFIX = "/admin"

+ 7 - 7
flaskbb/configs/default.py

@@ -19,11 +19,11 @@ class DefaultConfig(object):
     # Get the app root path
     #            <_basedir>
     # ../../ -->  flaskbb/flaskbb/configs/base.py
-    _basedir = os.path.join(os.path.abspath(os.path.dirname(os.path.dirname(
-                            os.path.dirname(__file__)))))
+    basedir = os.path.join(os.path.abspath(os.path.dirname(os.path.dirname(
+                           os.path.dirname(__file__)))))
 
     # Python version
-    _version_str = '{0.major}{0.minor}'.format(sys.version_info)
+    py_version = '{0.major}{0.minor}'.format(sys.version_info)
 
     # Flask Settings
     # ------------------------------
@@ -61,7 +61,7 @@ class DefaultConfig(object):
     # For PostgresSQL:
     #SQLALCHEMY_DATABASE_URI = "postgresql://flaskbb@localhost:5432/flaskbb"
     # For SQLite:
-    SQLALCHEMY_DATABASE_URI = 'sqlite:///' + _basedir + '/' + \
+    SQLALCHEMY_DATABASE_URI = 'sqlite:///' + basedir + '/' + \
                               'flaskbb.sqlite'
 
     # This option will be removed as soon as Flask-SQLAlchemy removes it.
@@ -85,7 +85,7 @@ class DefaultConfig(object):
     # Full-Text-Search
     # ------------------------------
     # This will use the "whoosh_index" directory to store the search indexes
-    WHOOSHEE_DIR = os.path.join(_basedir, "whoosh_index", _version_str)
+    WHOOSHEE_DIR = os.path.join(basedir, "whoosh_index", py_version)
     # How long should whooshee try to acquire write lock? (defaults to 2)
     WHOOSHEE_WRITER_TIMEOUT = 2
     # Minimum number of characters for the search (defaults to 3)
@@ -172,7 +172,7 @@ class DefaultConfig(object):
     # Celery
     CELERY_BROKER_URL = 'redis://localhost:6379'
     CELERY_RESULT_BACKEND = 'redis://localhost:6379'
-    if not REDIS_ENABLED: CELERY_ALWAYS_EAGER=True
+    if not REDIS_ENABLED: CELERY_ALWAYS_EAGER = True
 
     # FlaskBB Settings
     # ------------------------------ #
@@ -183,4 +183,4 @@ class DefaultConfig(object):
     AUTH_URL_PREFIX = "/auth"
     ADMIN_URL_PREFIX = "/admin"
     # Plugin Folder
-    PLUGINS_FOLDER = os.path.join(_basedir, "flaskbb", "plugins")
+    PLUGINS_FOLDER = os.path.join(basedir, "flaskbb", "plugins")

+ 0 - 47
flaskbb/configs/development.py.example

@@ -1,47 +0,0 @@
-"""
-    flaskbb.configs.development
-    ~~~~~~~~~~~~~~~~~~~~
-
-    This is the FlaskBB's development config.
-    An extensive description for all those settings values
-    is available in ``default.py``.
-
-    :copyright: (c) 2014 by the FlaskBB Team.
-    :license: BSD, see LICENSE for more details.
-"""
-from flaskbb.configs.default import DefaultConfig
-
-
-class DevelopmentConfig(DefaultConfig):
-
-    # Indicates that it is a dev environment
-    DEBUG = True
-
-    # Prefer HTTP for development
-    PREFERRED_URL_SCHEME = "http"
-
-    # Database
-    SQLALCHEMY_DATABASE_URI = 'sqlite:///' + DefaultConfig._basedir + '/' + \
-                              'flaskbb.sqlite'
-
-    # This will print all SQL statements
-    SQLALCHEMY_ECHO = True
-
-    # Security
-    SECRET_KEY = "SecretKeyForSessionSigning"
-    WTF_CSRF_ENABLED = True
-    WTF_CSRF_SECRET_KEY = "reallyhardtoguess"
-
-    # Redis
-    REDIS_ENABLED = False
-    REDIS_URL = "redis://localhost:6379"
-
-    # Local SMTP Server
-    MAIL_SERVER = "localhost"
-    MAIL_PORT = 25
-    MAIL_USE_SSL = False
-    MAIL_USERNAME = ""
-    MAIL_PASSWORD = ""
-    MAIL_DEFAULT_SENDER = ("FlaskBB Mailer", "noreply@example.org")
-
-    ADMINS = ["your_admin_user@gmail.com"]

+ 0 - 171
flaskbb/configs/production.py.example

@@ -1,171 +0,0 @@
-"""
-    flaskbb.configs.production
-    ~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-    This is how a production configuration can look like.
-
-    :copyright: (c) 2014 by the FlaskBB Team.
-    :license: BSD, see LICENSE for more details.
-"""
-import os
-import datetime
-from flaskbb.configs.default import DefaultConfig
-
-
-class ProductionConfig(DefaultConfig):
-    # Flask Settings
-    # ------------------------------
-    # There is a whole bunch of more settings available here:
-    # http://flask.pocoo.org/docs/0.11/config/#builtin-configuration-values
-    DEBUG = False
-    TESTING = False
-
-    # Server Name - REQUIRED for Celery/Mailing
-    # The name and port number of the server.
-    # Required for subdomain support (e.g.: 'myapp.dev:5000') and
-    # URL generation without a request context but with an application context
-    # which we need in order to generate URLs (with the celery application)
-    # Note that localhost does not support subdomains so setting this to
-    # “localhost” does not help.
-    # Example for the FlaskBB forums: SERVER_NAME = "forums.flaskbb.org"
-    SERVER_NAME =
-
-    # Prefer HTTPS over HTTP
-    PREFERRED_URL_SCHEME = "https"
-
-    # If SEND_LOGS is set to True, the admins (see the mail configuration) will
-    # recieve the error logs per email.
-    SEND_LOGS = False
-
-    # The filename for the info and error logs. The logfiles are stored at
-    # flaskbb/logs
-    INFO_LOG = "info.log"
-    ERROR_LOG = "error.log"
-
-    # Database
-    # ------------------------------
-    # For PostgresSQL:
-    #SQLALCHEMY_DATABASE_URI = "postgresql://flaskbb@localhost:5432/flaskbb"
-    # For SQLite:
-    SQLALCHEMY_DATABASE_URI = 'sqlite:///' + DefaultConfig._basedir + '/' + \
-                              'flaskbb.sqlite'
-
-    # This option will be removed as soon as Flask-SQLAlchemy removes it.
-    # At the moment it is just used to suppress the super annoying warning
-    SQLALCHEMY_TRACK_MODIFICATIONS = False
-    # This will print all SQL statements
-    SQLALCHEMY_ECHO = False
-
-    # Security - IMPORTANT
-    # ------------------------------
-    # This is the secret key that is used for session signing.
-    # You can generate a secure key with os.urandom(24)
-    SECRET_KEY = 'secret key'
-
-    # You can generate the WTF_CSRF_SECRET_KEY the same way as you have
-    # generated the SECRET_KEY. If no WTF_CSRF_SECRET_KEY is provided, it will
-    # use the SECRET_KEY.
-    WTF_CSRF_ENABLED = True
-    WTF_CSRF_SECRET_KEY = "reallyhardtoguess"
-
-
-    # Full-Text-Search
-    # ------------------------------
-    # This will use the "whoosh_index" directory to store the search indexes
-    WHOOSHEE_DIR = os.path.join(DefaultConfig._basedir, "whoosh_index", DefaultConfig._version_str)
-    # How long should whooshee try to acquire write lock? (defaults to 2)
-    WHOOSHEE_WRITER_TIMEOUT = 2
-    # Minimum number of characters for the search (defaults to 3)
-    WHOOSHEE_MIN_STRING_LEN = 3
-
-    # Auth
-    # ------------------------------
-    LOGIN_VIEW = "auth.login"
-    REAUTH_VIEW = "auth.reauth"
-    LOGIN_MESSAGE_CATEGORY = "info"
-    REFRESH_MESSAGE_CATEGORY = "info"
-
-    # The name of the cookie to store the “remember me” information in.
-    REMEMBER_COOKIE_NAME = "remember_token"
-    # The amount of time before the cookie expires, as a datetime.timedelta object.
-    # Default: 365 days (1 non-leap Gregorian year)
-    REMEMBER_COOKIE_DURATION = datetime.timedelta(days=365)
-    # If the “Remember Me” cookie should cross domains,
-    # set the domain value here (i.e. .example.com would allow the cookie
-    # to be used on all subdomains of example.com).
-    REMEMBER_COOKIE_DOMAIN = None
-    # Limits the “Remember Me” cookie to a certain path.
-    REMEMBER_COOKIE_PATH = "/"
-    # Restricts the “Remember Me” cookie’s scope to secure channels (typically HTTPS).
-    REMEMBER_COOKIE_SECURE = None
-    # Prevents the “Remember Me” cookie from being accessed by client-side scripts.
-    REMEMBER_COOKIE_HTTPONLY = False
-
-    # Rate Limiting via Flask-Limiter
-    # -------------------------------
-    # A full list with configuration values is available at the flask-limiter
-    # docs, but you actually just need those settings below.
-    # You can disabled the Rate Limiter here as well - it will overwrite
-    # the setting from the admin panel!
-    # RATELIMIT_ENABLED = True
-    # You can choose from:
-    #   memory:// (default)
-    #   redis://host:port
-    #   memcached://host:port
-    # Using the redis storage requires the installation of the redis package,
-    # which will be installed if you enable REDIS_ENABLE while memcached
-    # relies on the pymemcache package.
-    #RATELIMIT_STORAGE_URL = "redis://localhost:6379"
-
-    # Caching
-    # ------------------------------
-    # For all available caching types, have a look at the Flask-Cache docs
-    # https://pythonhosted.org/Flask-Caching/#configuring-flask-caching
-    CACHE_TYPE = "simple"
-    # For redis:
-    #CACHE_TYPE = "redis"
-    CACHE_DEFAULT_TIMEOUT = 60
-
-    # Mail
-    # ------------------------------
-    # Google Mail Example
-    #MAIL_SERVER = "smtp.gmail.com"
-    #MAIL_PORT = 465
-    #MAIL_USE_SSL = True
-    #MAIL_USERNAME = "your_username@gmail.com"
-    #MAIL_PASSWORD = "your_password"
-    #MAIL_DEFAULT_SENDER = ("Your Name", "your_username@gmail.com")
-
-    # Local SMTP Server
-    MAIL_SERVER = "localhost"
-    MAIL_PORT = 25
-    MAIL_USE_SSL = False
-    MAIL_USE_TLS = False
-    MAIL_USERNAME = "noreply@example.org"
-    MAIL_PASSWORD = ""
-    MAIL_DEFAULT_SENDER = ("Default Sender", "noreply@example.org")
-    # Where to logger should send the emails to
-    ADMINS = ["admin@example.org"]
-
-    # Redis
-    # ------------------------------ #
-    # If redis is enabled, it can be used for:
-    #   - Sending non blocking emails via Celery (Task Queue)
-    #   - Caching
-    #   - Rate Limiting
-    REDIS_ENABLED = False
-    REDIS_URL = "redis://localhost:6379"  # or with a password: "redis://:password@localhost:6379"
-    REDIS_DATABASE = 0
-
-    # Celery
-    CELERY_BROKER_URL = 'redis://localhost:6379'
-    CELERY_RESULT_BACKEND = 'redis://localhost:6379'
-
-    # FlaskBB Settings
-    # ------------------------------ #
-    # URL Prefixes
-    FORUM_URL_PREFIX = ""
-    USER_URL_PREFIX = "/user"
-    MESSAGE_URL_PREFIX = "/message"
-    AUTH_URL_PREFIX = "/auth"
-    ADMIN_URL_PREFIX = "/admin"

+ 2 - 37
flaskbb/configs/testing.py

@@ -16,9 +16,6 @@ class TestingConfig(DefaultConfig):
     DEBUG = False
     TESTING = True
 
-    # SQLAlchemy connection options
-    # This will create in the applications folder (where manage.py is)
-    # a database named flaskbb.sqlite.
     SQLALCHEMY_DATABASE_URI = (
         'sqlite://'
     )
@@ -28,42 +25,10 @@ class TestingConfig(DefaultConfig):
     # This will print all SQL statements
     SQLALCHEMY_ECHO = False
 
-    # Security
-    SECRET_KEY = "SecretKeyForSessionSigning"
-    WTF_CSRF_ENABLED = True
-    WTF_CSRF_SECRET_KEY = "reallyhardtoguess"
-
-    # Recaptcha
-    # To get recaptcha, visit the link below:
-    # https://www.google.com/recaptcha/admin/create
-    # Those keys are only going to work on localhost!
-    RECAPTCHA_ENABLED = True
-    RECAPTCHA_USE_SSL = False
-    RECAPTCHA_PUBLIC_KEY = "6LcZB-0SAAAAAGIddBuSFU9aBpHKDa16p5gSqnxK"
-    RECAPTCHA_PRIVATE_KEY = "6LcZB-0SAAAAAPuPHhazscMJYa2mBe7MJSoWXrUu"
-    RECAPTCHA_OPTIONS = {"theme": "white"}
-
-    # Mail
-    # Local SMTP Server
-    #MAIL_SERVER = "localhost"
-    #MAIL_PORT = 25
-    #MAIL_USE_SSL = False
-    #MAIL_USERNAME = ""
-    #MAIL_PASSWORD = ""
-    #MAIL_DEFAULT_SENDER = "noreply@example.org"
-
-    # Google Mail Example
-    MAIL_SERVER = "smtp.gmail.com"
-    MAIL_PORT = 465
-    MAIL_USE_SSL = True
-    MAIL_USERNAME = "your_username@gmail.com"
-    MAIL_PASSWORD = "your_password"
-    MAIL_DEFAULT_SENDER = ("Your Name", "your_username@gmail.com")
+    # Use the in-memory storage
+    WHOOSHEE_MEMORY_STORAGE = True
 
     CELERY_ALWAYS_EAGER = True
     CELERY_RESULT_BACKEND = "cache"
     CELERY_CACHE_BACKEND = "memory"
     CELERY_EAGER_PROPAGATES_EXCEPTIONS = True
-
-    # The user who should recieve the error logs
-    ADMINS = ["your_admin_user@gmail.com"]

+ 2 - 2
wsgi.py

@@ -1,4 +1,4 @@
 from flaskbb import create_app
-from flaskbb.configs.production import ProductionConfig
 
-flaskbb = create_app(config=ProductionConfig())
+# will throw an error if the config doesn't exist
+flaskbb = create_app(config="flaskbb.cfg")