Browse Source

Added theming functionality.

sh4nks 11 years ago
parent
commit
7e838e7f9a
53 changed files with 297 additions and 104 deletions
  1. 3 2
      flaskbb/admin/views.py
  2. 9 8
      flaskbb/app.py
  3. 3 2
      flaskbb/auth/views.py
  4. 3 0
      flaskbb/configs/default.py
  5. 4 0
      flaskbb/extensions.py
  6. 2 2
      flaskbb/forum/views.py
  7. 2 2
      flaskbb/templates/admin/admin_layout.html
  8. 2 2
      flaskbb/templates/admin/category_form.html
  9. 2 2
      flaskbb/templates/admin/forum_form.html
  10. 2 2
      flaskbb/templates/admin/forums.html
  11. 2 2
      flaskbb/templates/admin/group_form.html
  12. 2 2
      flaskbb/templates/admin/groups.html
  13. 1 1
      flaskbb/templates/admin/overview.html
  14. 2 2
      flaskbb/templates/admin/user_form.html
  15. 2 2
      flaskbb/templates/admin/users.html
  16. 3 3
      flaskbb/templates/auth/forgot_password.html
  17. 2 2
      flaskbb/templates/auth/login.html
  18. 3 3
      flaskbb/templates/auth/reauth.html
  19. 2 2
      flaskbb/templates/auth/register.html
  20. 5 5
      flaskbb/templates/auth/reset_password.html
  21. 1 1
      flaskbb/templates/errors/forbidden_page.html
  22. 1 1
      flaskbb/templates/errors/page_not_found.html
  23. 1 1
      flaskbb/templates/errors/server_error.html
  24. 1 1
      flaskbb/templates/forum/category.html
  25. 2 2
      flaskbb/templates/forum/forum.html
  26. 2 2
      flaskbb/templates/forum/index.html
  27. 2 2
      flaskbb/templates/forum/memberlist.html
  28. 2 2
      flaskbb/templates/forum/new_post.html
  29. 2 2
      flaskbb/templates/forum/new_topic.html
  30. 1 1
      flaskbb/templates/forum/online_users.html
  31. 2 2
      flaskbb/templates/forum/topic.html
  32. 2 2
      flaskbb/templates/forum/topictracker.html
  33. 2 2
      flaskbb/templates/layout.html
  34. 2 2
      flaskbb/templates/message/message_layout.html
  35. 6 5
      flaskbb/templates/message/new_message.html
  36. 1 1
      flaskbb/templates/message/sent.html
  37. 1 1
      flaskbb/templates/message/trash.html
  38. 1 1
      flaskbb/templates/message/view_message.html
  39. 2 2
      flaskbb/templates/user/all_posts.html
  40. 2 2
      flaskbb/templates/user/all_topics.html
  41. 2 2
      flaskbb/templates/user/change_email.html
  42. 2 2
      flaskbb/templates/user/change_password.html
  43. 2 2
      flaskbb/templates/user/change_user_details.html
  44. 15 0
      flaskbb/templates/user/general_settings.html
  45. 0 5
      flaskbb/templates/user/overview.html
  46. 1 1
      flaskbb/templates/user/profile.html
  47. 3 3
      flaskbb/templates/user/settings_layout.html
  48. 9 0
      flaskbb/themes/bootstrap3/info.json
  49. 123 0
      flaskbb/themes/bootstrap3/templates/layout.html
  50. 7 0
      flaskbb/user/forms.py
  51. 2 0
      flaskbb/user/models.py
  52. 24 7
      flaskbb/user/views.py
  53. 15 1
      flaskbb/utils/helpers.py

+ 3 - 2
flaskbb/admin/views.py

@@ -10,10 +10,11 @@
 """
 import sys
 
-from flask import (Blueprint, render_template, current_app, request, redirect,
-                   url_for, flash, __version__ as flask_version)
+from flask import (Blueprint, current_app, request, redirect, url_for, flash,
+                   __version__ as flask_version)
 
 from flaskbb import __version__ as flaskbb_version
+from flaskbb.utils.helpers import render_template
 from flaskbb.utils.decorators import admin_required
 from flaskbb.extensions import db
 from flaskbb.user.models import User, Group

+ 9 - 8
flaskbb/app.py

@@ -12,7 +12,7 @@ import os
 import logging
 import datetime
 
-from flask import Flask, render_template, request
+from flask import Flask, request
 from flask.ext.login import current_user
 
 # Import the user blueprint
@@ -26,13 +26,11 @@ from flaskbb.admin.views import admin
 from flaskbb.forum.views import forum
 
 from flaskbb.extensions import (db, login_manager, mail, cache, redis,
-                                debugtoolbar, migrate)
-from flaskbb.utils.helpers import (format_date, time_since, crop_title,
-                                   can_post_reply, can_post_topic,
-                                   can_delete_topic, can_delete_post, is_online,
-                                   can_edit_post, can_lock_topic,
-                                   can_move_topic, render_markup, mark_online,
-                                   forum_is_unread, topic_is_unread)
+                                debugtoolbar, migrate, themes)
+from flaskbb.utils.helpers import format_date, time_since, crop_title, \
+    can_post_reply, can_post_topic, can_delete_topic, can_delete_post, \
+    is_online, can_edit_post, can_lock_topic, can_move_topic, render_markup, \
+    mark_online, forum_is_unread, topic_is_unread, render_template
 
 
 def create_app(config=None):
@@ -86,6 +84,9 @@ def configure_extensions(app):
     # Flask-Debugtoolbar
     debugtoolbar.init_app(app)
 
+    # Flask-Themes
+    themes.init_themes(app, app_identifier="flaskbb")
+
     # Flask-And-Redis
     redis.init_app(app)
 

+ 3 - 2
flaskbb/auth/views.py

@@ -9,10 +9,11 @@
     :copyright: (c) 2014 by the FlaskBB Team.
     :license: BSD, see LICENSE for more details.
 """
-from flask import (Blueprint, flash, redirect, render_template,
-                   url_for, request, current_app)
+from flask import Blueprint, flash, redirect, url_for, request, current_app
 from flask.ext.login import (current_user, login_user, login_required,
                              logout_user, confirm_login, login_fresh)
+
+from flaskbb.utils.helpers import render_template
 from flaskbb.email import send_reset_token
 from flaskbb.auth.forms import (LoginForm, ReauthForm, ForgotPasswordForm,
                                 ResetPasswordForm)

+ 3 - 0
flaskbb/configs/default.py

@@ -104,3 +104,6 @@ class DefaultConfig(object):
     USER_URL_PREFIX = "/user"
     AUTH_URL_PREFIX = "/auth"
     ADMIN_URL_PREFIX = "/admin"
+
+    # Default style
+    DEFAULT_THEME = "bootstrap3"

+ 4 - 0
flaskbb/extensions.py

@@ -15,6 +15,7 @@ from flask.ext.cache import Cache
 from flask.ext.debugtoolbar import DebugToolbarExtension
 from flask.ext.redis import Redis
 from flask.ext.migrate import Migrate
+from flask.ext.themes2 import Themes
 
 # Database
 db = SQLAlchemy()
@@ -36,3 +37,6 @@ debugtoolbar = DebugToolbarExtension()
 
 # Migrations
 migrate = Migrate()
+
+# Themes
+themes = Themes()

+ 2 - 2
flaskbb/forum/views.py

@@ -12,7 +12,7 @@
 import datetime
 import math
 
-from flask import (Blueprint, render_template, redirect, url_for, current_app,
+from flask import (Blueprint, redirect, url_for, current_app,
                    request, flash)
 from flask.ext.login import login_required, current_user
 
@@ -20,7 +20,7 @@ from flaskbb.extensions import db
 from flaskbb.utils.helpers import (can_post_reply, can_delete_topic,
                                    can_edit_post, can_post_topic,
                                    can_delete_post, can_lock_topic,
-                                   get_online_users, time_diff)
+                                   get_online_users, time_diff, render_template)
 from flaskbb.forum.models import (Category, Forum, Topic, Post, ForumsRead,
                                   TopicsRead)
 from flaskbb.forum.forms import QuickreplyForm, ReplyForm, NewTopicForm

+ 2 - 2
flaskbb/templates/admin/admin_layout.html

@@ -1,6 +1,6 @@
-{% extends "layout.html" %}
+{% extends theme("layout.html") %}
 {% block content %}
-{%- from 'macros.html' import navlink with context-%}
+{%- from theme('macros.html') import navlink with context-%}
 
 <div class="row">
     <div class="col-sm-3">

+ 2 - 2
flaskbb/templates/admin/category_form.html

@@ -1,9 +1,9 @@
 {% set page_title = title %}
 {% set active_forum_nav=True %}
 
-{% extends "admin/admin_layout.html" %}
+{% extends theme("admin/admin_layout.html") %}
 {% block admin_content %}
-{% from "macros.html" import horizontal_field, render_boolean_field %}
+{% from theme("macros.html") import horizontal_field, render_boolean_field %}
 
 <form class="form-horizontal" role="form" method="post">
     {{ form.hidden_tag() }}

+ 2 - 2
flaskbb/templates/admin/forum_form.html

@@ -1,9 +1,9 @@
 {% set page_title = title %}
 {% set active_forum_nav=True %}
 
-{% extends "admin/admin_layout.html" %}
+{% extends theme("admin/admin_layout.html") %}
 {% block admin_content %}
-{% from "macros.html" import horizontal_field, render_boolean_field %}
+{% from theme("macros.html") import horizontal_field, render_boolean_field %}
 
 <form class="form-horizontal" role="form" method="post">
     {{ form.hidden_tag() }}

+ 2 - 2
flaskbb/templates/admin/forums.html

@@ -1,6 +1,6 @@
-{% extends "admin/admin_layout.html" %}
+{% extends theme("admin/admin_layout.html") %}
 {% block admin_content %}
-{% from 'macros.html' import render_pagination %}
+{% from theme('macros.html') import render_pagination %}
 
 <legend>Manage Forums | <a href="{{ url_for('admin.add_forum') }}">Add Forum</a> | <a href="{{ url_for('admin.add_category') }}">Add Category</a></legend>
 

+ 2 - 2
flaskbb/templates/admin/group_form.html

@@ -1,9 +1,9 @@
 {% set page_title = title %}
 {% set active_forum_nav=True %}
 
-{% extends "admin/admin_layout.html" %}
+{% extends theme("admin/admin_layout.html") %}
 {% block admin_content %}
-{% from "macros.html" import horizontal_field, render_boolean_field %}
+{% from theme("macros.html") import horizontal_field, render_boolean_field %}
 
 <form class="form-horizontal" role="form" method="post">
     {{ form.hidden_tag() }}

+ 2 - 2
flaskbb/templates/admin/groups.html

@@ -1,6 +1,6 @@
-{% extends "admin/admin_layout.html" %}
+{% extends theme("admin/admin_layout.html") %}
 {% block admin_content %}
-{% from 'macros.html' import render_pagination %}
+{% from theme('macros.html') import render_pagination %}
 
 <legend>Manage Groups | <a href="{{ url_for('admin.add_group') }}">Add Group</a></legend>
 

+ 1 - 1
flaskbb/templates/admin/overview.html

@@ -1,4 +1,4 @@
-{% extends "admin/admin_layout.html" %}
+{% extends theme("admin/admin_layout.html") %}
 {% block admin_content %}
 <table class="table table-bordered">
     <thead>

+ 2 - 2
flaskbb/templates/admin/user_form.html

@@ -1,9 +1,9 @@
 {% set page_title = title %}
 {% set active_forum_nav=True %}
 
-{% extends "admin/admin_layout.html" %}
+{% extends theme("admin/admin_layout.html") %}
 {% block admin_content %}
-{% from "macros.html" import horizontal_field %}
+{% from theme("macros.html") import horizontal_field %}
 
 <form class="form-horizontal" role="form" method="post">
     {{ form.hidden_tag() }}

+ 2 - 2
flaskbb/templates/admin/users.html

@@ -1,6 +1,6 @@
-{% extends "admin/admin_layout.html" %}
+{% extends theme("admin/admin_layout.html") %}
 {% block admin_content %}
-{% from 'macros.html' import render_pagination %}
+{% from theme('macros.html') import render_pagination %}
 
 <legend>Manage Users | <a href="{{ url_for('admin.add_user') }}">Add User</a></legend>
 

+ 3 - 3
flaskbb/templates/auth/forgot_password.html

@@ -1,14 +1,14 @@
 {% set page_title = "Reset Password" %}
 
-{% extends "layout.html" %}
+{% extends theme("layout.html") %}
 {% block content %}
-{% import "macros.html" as wtf %}
+{% from theme("macros.html") import horizontal_field %}
 
 <form class="form-horizontal" role="form" method="POST">
     <h2>Reset Password</h2>
     <hr>
         {{ form.hidden_tag() }}
-        {{ wtf.horizontal_field(form.email) }}
+        {{ horizontal_field(form.email) }}
 
     <div class="form-group">
         <div class="col-sm-offset-3 col-sm-9">

+ 2 - 2
flaskbb/templates/auth/login.html

@@ -1,7 +1,7 @@
 {% set page_title = "Login" %}
 
-{% extends "layout.html" %}
-{% from "macros.html" import horizontal_field %}
+{% extends theme("layout.html") %}
+{% from theme("macros.html") import horizontal_field %}
 {% block content %}
 
 <form class="form-horizontal" role="form" method="POST">

+ 3 - 3
flaskbb/templates/auth/reauth.html

@@ -1,14 +1,14 @@
 {% set page_title = "Refresh Login" %}
 {% set active_forum_nav=True %}
 
-{% extends "layout.html" %}
+{% extends theme("layout.html") %}
 {% block content %}
-{% import "macros.html" as wtf %}
+{% from theme("macros.html") import form_input_field %}
 
 <form class="form-signin" action="" method="POST">
     <h2 class="form-signin-heading">Refresh Login</h2>
     {{ form.hidden_tag() }}
-    {{ wtf.form_input_field(form.password)}}
+    {{ form_input_field(form.password)}}
     <button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button>
 </form>
 

+ 2 - 2
flaskbb/templates/auth/register.html

@@ -1,8 +1,8 @@
 {% set page_title = "Register" %}
 
-{% extends "layout.html" %}
+{% extends theme("layout.html") %}
 {% block content %}
-{% from "macros.html" import horizontal_field %}
+{% from theme("macros.html") import horizontal_field %}
 
 <form class="form-horizontal" role="form" method="POST">
     <h2>Register</h2>

+ 5 - 5
flaskbb/templates/auth/reset_password.html

@@ -1,17 +1,17 @@
 {% set page_title = "Reset Password" %}
 
-{% extends "layout.html" %}
+{% extends theme("layout.html") %}
 {% block content %}
-{% import "macros.html" as wtf %}
+{% from theme("macros.html") import horizontal_field %}
 
 <form class="form-horizontal" role="form" method="POST">
     <h2>Reset Password</h2>
     <hr>
         {{ form.hidden_tag() }}
         {{ form.token }}
-        {{ wtf.horizontal_field(form.email) }}
-        {{ wtf.horizontal_field(form.password) }}
-        {{ wtf.horizontal_field(form.confirm_password)}}
+        {{ horizontal_field(form.email) }}
+        {{ horizontal_field(form.password) }}
+        {{ horizontal_field(form.confirm_password)}}
 
     <div class="form-group">
         <div class="col-lg-offset-2 col-lg-10">

+ 1 - 1
flaskbb/templates/errors/forbidden_page.html

@@ -1,6 +1,6 @@
 {% set page_title = "No Access - 403" %}
 
-{% extends "layout.html" %}
+{% extends theme("layout.html") %}
 {% block content %}
 
 <div class="well">

+ 1 - 1
flaskbb/templates/errors/page_not_found.html

@@ -1,6 +1,6 @@
 {% set page_title = "Oh noes! 404" %}
 
-{% extends "layout.html" %}
+{% extends theme("layout.html") %}
 {% block content %}
 
 <div class="well">

+ 1 - 1
flaskbb/templates/errors/server_error.html

@@ -1,6 +1,6 @@
 {% set page_title = "Server Error - 500" %}
 
-{% extends "layout.html" %}
+{% extends theme("layout.html") %}
 {% block content %}
 
 <div class="well">

+ 1 - 1
flaskbb/templates/forum/category.html

@@ -1,7 +1,7 @@
 {% set page_title = categories.keys()[0].title %}
 {% set active_forum_nav=True %}
 
-{% extends "layout.html" %}
+{% extends theme("layout.html") %}
 {% block content %}
 
 <ol class="breadcrumb">

+ 2 - 2
flaskbb/templates/forum/forum.html

@@ -1,9 +1,9 @@
 {% set page_title = forum[0].title %}
 {% set active_forum_nav=True %}
 
-{% extends "layout.html" %}
+{% extends theme("layout.html") %}
 {% block content %}
-{% from 'macros.html' import render_pagination, topic_pages %}
+{% from theme('macros.html') import render_pagination, topic_pages %}
 
 <ol class="breadcrumb">
     <li><a href="{{ url_for('forum.index') }}">Forum</a></li>

+ 2 - 2
flaskbb/templates/forum/index.html

@@ -1,11 +1,11 @@
-{% extends "layout.html" %}
+{% extends theme("layout.html") %}
 {% block content %}
 
 <ol class="breadcrumb">
     <li><a href="{{ url_for('forum.index') }}">Forum</a></li>
 </ol>
 
-{% include "forum/category_layout.html" %}
+{% include theme("forum/category_layout.html") %}
 
 <!-- Forum Stats -->
 <table class="table table-bordered">

+ 2 - 2
flaskbb/templates/forum/memberlist.html

@@ -1,8 +1,8 @@
 {% set page_title = "Memberlist" %}
 
-{% extends "layout.html" %}
+{% extends theme("layout.html") %}
 {% block content %}
-{% from 'macros.html' import render_pagination %}
+{% from theme('macros.html') import render_pagination %}
 
 <ul class="breadcrumb">
     <li><a href="{{ url_for('forum.index') }}">Forum</a></li>

+ 2 - 2
flaskbb/templates/forum/new_post.html

@@ -1,9 +1,9 @@
 {% set page_title = "New Post" %}
 {% set active_forum_nav=True %}
 
-{% extends "layout.html" %}
+{% extends theme("layout.html") %}
 {% block content %}
-{% from "macros.html" import render_field %}
+{% from theme("macros.html") import render_field %}
 
 <ul class="breadcrumb">
     <li><a href="{{ url_for('forum.index') }}">Forum</a></li>

+ 2 - 2
flaskbb/templates/forum/new_topic.html

@@ -1,9 +1,9 @@
 {% set page_title = "New Topic" %}
 {% set active_forum_nav=True %}
 
-{% extends "layout.html" %}
+{% extends theme("layout.html") %}
 {% block content %}
-{% from "macros.html" import render_field %}
+{% from theme("macros.html") import render_field %}
 
 <ul class="breadcrumb">
     <li><a href="{{ url_for('forum.index') }}">Forum</a></li>

+ 1 - 1
flaskbb/templates/forum/online_users.html

@@ -1,6 +1,6 @@
 {% set page_title = "Online Users" %}
 
-{% extends "layout.html" %}
+{% extends theme("layout.html") %}
 {% block content %}
 
 <legend>Online Users</legend>

+ 2 - 2
flaskbb/templates/forum/topic.html

@@ -1,9 +1,9 @@
 {% set page_title = topic.title ~ " - Topic" %}
 {% set active_forum_nav=True %}
 
-{% extends "layout.html" %}
+{% extends theme("layout.html") %}
 {% block content %}
-{% from 'macros.html' import render_pagination, form_field %}
+{% from theme('macros.html') import render_pagination, form_field %}
 
 <ol class="breadcrumb">
     <li><a href="{{ url_for('forum.index') }}">Forum</a></li>

+ 2 - 2
flaskbb/templates/forum/topictracker.html

@@ -1,8 +1,8 @@
 {% set page_title = "Topic Tracker" %}
 {% set active_forum_nav=True %}
 
-{% extends 'layout.html' %}
-{% from 'macros.html' import render_pagination %}
+{% extends theme("layout.html") %}
+{% from theme('macros.html') import render_pagination %}
 
 {% block content %}
 <ul class="breadcrumb">

+ 2 - 2
flaskbb/templates/layout.html

@@ -20,7 +20,7 @@
     <body>
         <div id="wrap">
         {% block navigation %}
-        {%- from "macros.html" import topnav with context -%}
+        {%- from themes("macros.html") import topnav with context -%}
         <!-- Navigation -->
             <nav class="navbar navbar-default navbar-static-top">
                 <div class="container">
@@ -98,7 +98,7 @@
 
             <div class="container">
                 {% block messages %}
-                    {% include 'flashed_messages.html' %}
+                    {% include theme('flashed_messages.html') %}
                 {% endblock %}
 
                 {% block content %}

+ 2 - 2
flaskbb/templates/message/message_layout.html

@@ -1,6 +1,6 @@
-{% extends "layout.html" %}
+{% extends theme("layout.html") %}
 {% block content %}
-{%- from 'macros.html' import navlink with context -%}
+{%- from theme('macros.html') import navlink with context -%}
 
 <ul class="breadcrumb">
     <li><a href="{{ url_for('forum.index') }}">Forum</a></li>

+ 6 - 5
flaskbb/templates/message/new_message.html

@@ -1,12 +1,13 @@
-{% extends "message/message_layout.html" %}
+{% extends theme("message/message_layout.html") %}
+
 {% block message_content %}
-{% import "macros.html" as wtf %}
+{% from theme("macros.html") import horizontal_field %}
 <form class="form-horizontal" role="form" method="post" name="new">
     <legend>Compose Message</legend>
     {{ form.hidden_tag() }}
-    {{ wtf.horizontal_field(form.to_user)}}
-    {{ wtf.horizontal_field(form.subject)}}
-    {{ wtf.horizontal_field(form.message, rows=7)}}
+    {{ horizontal_field(form.to_user)}}
+    {{ horizontal_field(form.subject)}}
+    {{ horizontal_field(form.message, rows=7)}}
 
     <div class="form-group row">
         <div class="col-sm-offset-3 col-lg-9">

+ 1 - 1
flaskbb/templates/message/sent.html

@@ -1,4 +1,4 @@
-{% extends "message/message_layout.html" %}
+{% extends theme("message/message_layout.html") %}
 {% block message_content %}
 <table class="table table-bordered">
     <thead>

+ 1 - 1
flaskbb/templates/message/trash.html

@@ -1,4 +1,4 @@
-{% extends "message/message_layout.html" %}
+{% extends theme("message/message_layout.html") %}
 {% block message_content %}
 <table class="table table-bordered">
     <thead>

+ 1 - 1
flaskbb/templates/message/view_message.html

@@ -1,4 +1,4 @@
-{% extends "message/message_layout.html" %}
+{% extends theme("message/message_layout.html") %}
 {% block message_content %}
 <table class="table table-bordered">
     <tbody>

+ 2 - 2
flaskbb/templates/user/all_posts.html

@@ -1,5 +1,5 @@
-{% extends 'layout.html' %}
-{% from 'macros.html' import render_pagination %}
+{% extends theme('layout.html') %}
+{% from theme('macros.html') import render_pagination %}
 
 {% block content %}
 <ul class="breadcrumb">

+ 2 - 2
flaskbb/templates/user/all_topics.html

@@ -1,5 +1,5 @@
-{% extends 'layout.html' %}
-{% from 'macros.html' import render_pagination %}
+{% extends theme('layout.html') %}
+{% from theme('macros.html') import render_pagination %}
 
 {% block content %}
 <ul class="breadcrumb">

+ 2 - 2
flaskbb/templates/user/change_email.html

@@ -1,5 +1,5 @@
-{% extends "user/settings_layout.html" %}
-{% from "macros.html" import horizontal_field %}
+{% extends theme("user/settings_layout.html") %}
+{% from theme("macros.html") import horizontal_field %}
 
 {% block settings_content %}
 <form class="form-horizontal" role="form" method="POST">

+ 2 - 2
flaskbb/templates/user/change_password.html

@@ -1,5 +1,5 @@
-{% extends "user/settings_layout.html" %}
-{% from "macros.html" import horizontal_field %}
+{% extends theme("user/settings_layout.html") %}
+{% from theme("macros.html") import horizontal_field %}
 
 {% block settings_content %}
 <form class="form-horizontal" role="form" method="POST">

+ 2 - 2
flaskbb/templates/user/change_user_details.html

@@ -1,5 +1,5 @@
-{% extends "user/settings_layout.html" %}
-{% from "macros.html" import horizontal_field %}
+{% extends theme("user/settings_layout.html") %}
+{% from theme("macros.html") import horizontal_field %}
 
 {% block settings_content %}
 <form class="form-horizontal" role="form" method="POST">

+ 15 - 0
flaskbb/templates/user/general_settings.html

@@ -0,0 +1,15 @@
+{% extends theme("user/settings_layout.html") %}
+{% from theme("macros.html") import horizontal_field %}
+
+{% block settings_content %}
+<form class="form-horizontal" role="form" method="POST">
+    <legend class="">General Settings</legend>
+    {{ form.hidden_tag() }}
+    {{ horizontal_field(form.theme) }}
+    <div class="form-group row">
+        <div class="col-sm-offset-3 col-sm-9">
+            <button type="submit" class="btn btn-success">Save</button>
+        </div>
+    </div>
+</form>
+{% endblock %}

+ 0 - 5
flaskbb/templates/user/overview.html

@@ -1,5 +0,0 @@
-{% extends "user/settings_layout.html" %}
-{% block settings_content %}
-<legend>Overview</legend>
-To be continued...
-{% endblock %}

+ 1 - 1
flaskbb/templates/user/profile.html

@@ -1,4 +1,4 @@
-{% extends "layout.html" %}
+{% extends theme("layout.html") %}
 {% block content %}
 <ul class="breadcrumb">
     <li><a href="{{ url_for('forum.index') }}">Forum</a></li>

+ 3 - 3
flaskbb/templates/user/settings_layout.html

@@ -1,6 +1,6 @@
-{% extends "layout.html" %}
+{% extends theme("layout.html") %}
 {% block content %}
-{%- from 'macros.html' import navlink with context -%}
+{%- from theme('macros.html') import navlink with context -%}
 
 <ul class="breadcrumb">
     <li><a href="{{ url_for('forum.index') }}">Forum</a></li>
@@ -13,7 +13,7 @@
         <div class="sidebar">
             <ul class="nav sidenav">
                 <li class="nav-header">Account Settings</li>
-                {{ navlink('user.settings', 'Overview') }}
+                {{ navlink('user.settings', 'General Settings') }}
                 {{ navlink('user.change_user_details', 'Change User Details') }}
                 {{ navlink('user.change_email', 'Change E-Mail Address') }}
                 {{ navlink('user.change_password', 'Change Password') }}

+ 9 - 0
flaskbb/themes/bootstrap3/info.json

@@ -0,0 +1,9 @@
+{
+    "application": "flaskbb",
+    "identifier": "bootstrap3",
+    "name": "Bootstrap3",
+    "author": "sh4nks",
+    "license": "BSD/Apache 2.0 (Bootstrap)",
+    "description": "The default Bootstrap3 theme",
+    "version": "1.0.0"
+}

+ 123 - 0
flaskbb/themes/bootstrap3/templates/layout.html

@@ -0,0 +1,123 @@
+<!DOCTYPE html>
+<html lang="en">
+    <head>
+        <meta charset="utf-8">
+        <title>{% block title %}{% if not page_title %}FlaskBB{% else %}{{ page_title }} - FlaskBB{% endif %}{% endblock %}</title>
+        <meta name="viewport" content="width=device-width, initial-scale=1.0">
+        <meta name="description" content="FlaskBB is a forum software written in Flask">
+        <meta name="author" content="FlaskBB Team">
+
+        <link rel="shortcut icon" href="{{ url_for('static', filename='favicon.ico') }}">
+
+        {% block css %}
+        <link rel="stylesheet" href="{{ url_for('static', filename='css/bootstrap.min.css') }}">
+        <link rel="stylesheet" href="{{ url_for('static', filename='css/font-awesome.min.css') }}" >
+        <link rel="stylesheet" href="{{ url_for('static', filename='css/code.css') }}">
+        <link rel="stylesheet" href="{{ url_for('static', filename='css/flaskbb.css') }}">
+        {% endblock %}
+    </head>
+
+    <body>
+        <div id="wrap">
+        {% block navigation %}
+        {%- from "macros.html" import topnav with context -%}
+        <!-- Navigation -->
+            <nav class="navbar navbar-default navbar-static-top">
+                <div class="container">
+                    <div class="navbar-header">
+                        <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-ex1-collapse">
+                            <span class="sr-only">Toggle navigation</span>
+                            <span class="icon-bar"></span>
+                            <span class="icon-bar"></span>
+                            <span class="icon-bar"></span>
+                        </button>
+                        <a class="navbar-brand" href="/">FlaskBB</a>
+                    </div>
+                    <div class="collapse navbar-collapse navbar-ex1-collapse">
+                        <ul class="nav navbar-nav">
+                            {# active_forum_nav is set in {forum, category, topic}.html and new_{topic, post}.html #}
+                            {{ topnav(endpoint='forum.index', name='Forum', icon='fa fa-comment', active=active_forum_nav) }}
+                            {{ topnav(endpoint='forum.memberlist', name='Memberlist', icon='fa fa-user') }}
+                        </ul>
+
+                    {% if current_user and current_user.is_authenticated() %}
+                        <div class="btn-group navbar-btn navbar-right" style="padding-left: 15px; margin-right: -10px">
+                            <a class="btn btn-primary" href="{{ url_for('user.profile', username=current_user.username) }}">
+                                <span class="fa fa-user"></span> {{ current_user.username }}
+                            </a>
+                            <button type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown">
+                                <span class="caret"></span>
+                            </button>
+                            <ul class="dropdown-menu" role="menu">
+                                <li><a href="{{ url_for('forum.topictracker') }}"><span class="fa fa-book"></span> Topic Tracker</a></li>
+                                <li class="divider"></li>
+
+                                <li><a href="{{ url_for('user.settings') }}"><span class="fa fa-cogs"></span> Settings</a></li>
+                                {% if current_user.permissions['admin'] %}
+                                <li><a href="{{ url_for('admin.overview') }}"><span class="fa fa-cog"></span> Admin Panel</a></li>
+                                <li class="divider"></li>
+                                {% endif %}
+
+                                <li><a href="{{ url_for('auth.logout') }}"><span class="fa fa-power-off"></span> Logout</a></li>
+                            </ul>
+                        </div>
+
+                        <div class="btn-group navbar-btn navbar-right">
+                            <button type="button" class="btn btn-success dropdown-toggle" data-toggle="dropdown">
+                                <span class="fa fa-envelope"></span> <span class="badge">{{ current_user.pm_unread }}</span>
+                            </button>
+                            <ul class="dropdown-menu" role="menu">
+                                <li><a href="{{ url_for('user.inbox') }}"><span class="fa fa-envelope"></span> Inbox</a></li>
+                                <li><a href="{{ url_for('user.new_message') }}"><span class="fa fa-pencil"></span> New Message</a></li>
+                            </ul>
+                        </div>
+                    {% else %}
+                        <div class="btn-group navbar-btn navbar-right">
+                            <a class="btn btn-primary" href="{{ url_for('auth.login') }}">
+                                <span class="fa fa-user"></span> Login
+                            </a>
+                            <button type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown">
+                                <span class="caret"></span>
+                            </button>
+                            <ul class="dropdown-menu" role="menu">
+                                <li><a href="{{ url_for('auth.register') }}">Register</a></li>
+                                <li><a href="{{ url_for('auth.forgot_password') }}">Reset Password</a></li>
+                            </ul>
+                        </div>
+                    {% endif %}
+                        <form class="navbar-form navbar-right" role="search">
+                            <div class="form-group">
+                                <input type="text" class="form-control" placeholder="Search">
+                            </div>
+                        </form>
+                    </div><!-- nav-collapse -->
+                </div><!-- container -->
+            </nav> <!-- navbar navbar-inverse -->
+            {% endblock %}
+
+
+            <div class="container">
+                {% block messages %}
+                    {% include 'flashed_messages.html' %}
+                {% endblock %}
+
+                {% block content %}
+                {% endblock %}
+            </div> <!-- /container -->
+        </div> <!-- End wrap -->
+
+            {% block footer %}
+            <div id="footer">
+                <div class="container">
+                    <p class="text-muted credit pull-left">powered by <a href="http://flask.pocoo.org">Flask</a></p>
+                    <p class="text-muted credit pull-right">&copy; 2013 - <a href="http://flaskbb.org">FlaskBB.org</a></p>
+                </div>
+            </div>
+            {% endblock %}
+
+        {% block javascript %}
+        <script src="{{ url_for('static', filename='js/jquery.min.js') }}"></script>
+        <script src="{{ url_for('static', filename='js/bootstrap.min.js') }}"></script>
+        {% endblock %}
+    </body>
+</html>

+ 7 - 0
flaskbb/user/forms.py

@@ -26,6 +26,13 @@ is_image = regexp(IMG_RE,
                   message=("Only jpg, jpeg, png and gifs are allowed!"))
 
 
+class GeneralSettingsForm(Form):
+    # The choices for those fields will be generated in the user view
+    # because we cannot access the current_app outside of the context
+    #language = SelectField("Language")
+    theme = SelectField("Theme")
+
+
 class ChangeEmailForm(Form):
     old_email = TextField("Old E-Mail Address", validators=[
         Required(message="Email adress required"),

+ 2 - 0
flaskbb/user/models.py

@@ -86,6 +86,8 @@ class User(db.Model, UserMixin):
     avatar = db.Column(db.String)
     notes = db.Column(db.Text(5000))
 
+    theme = db.Column(db.String)
+
     posts = db.relationship("Post", backref="user", lazy="dynamic")
     topics = db.relationship("Topic", backref="user", lazy="dynamic")
 

+ 24 - 7
flaskbb/user/views.py

@@ -10,13 +10,17 @@
     :license: BSD, see LICENSE for more details.
 """
 from datetime import datetime
-from flask import Blueprint, render_template, flash, request, redirect, url_for
+
+from flask import Blueprint, flash, request, redirect, url_for
 from flask.ext.login import login_required, current_user
+from flask.ext.themes2 import get_themes_list
 
 from flaskbb.extensions import db
+from flaskbb.utils.helpers import render_template
 from flaskbb.user.models import User, PrivateMessage
 from flaskbb.user.forms import (ChangePasswordForm, ChangeEmailForm,
-                                ChangeUserDetailsForm, NewMessage)
+                                ChangeUserDetailsForm, GeneralSettingsForm,
+                                NewMessage)
 
 
 user = Blueprint("user", __name__)
@@ -52,13 +56,26 @@ def view_all_posts(username):
     return render_template("user/all_posts.html", user=user, posts=posts)
 
 
-@user.route("/settings")
+@user.route("/settings/general")
 @login_required
 def settings():
-    return render_template("user/overview.html")
+    form = GeneralSettingsForm()
+
+    form.theme.choices = [(theme.identifier, theme.name)
+                          for theme in get_themes_list()]
+
+    if form.validate_on_submit():
+        current_user.theme = form.theme.data
+        current_user.save()
+
+        flash("Your settings have been updated!", "success")
+    else:
+        form.theme.data = current_user.theme
+
+    return render_template("user/general_settings.html", form=form)
 
 
-@user.route("/settings/change_password", methods=["POST", "GET"])
+@user.route("/settings/password", methods=["POST", "GET"])
 @login_required
 def change_password():
     form = ChangePasswordForm()
@@ -70,7 +87,7 @@ def change_password():
     return render_template("user/change_password.html", form=form)
 
 
-@user.route("/settings/change_email", methods=["POST", "GET"])
+@user.route("/settings/email", methods=["POST", "GET"])
 @login_required
 def change_email():
     form = ChangeEmailForm(current_user)
@@ -82,7 +99,7 @@ def change_email():
     return render_template("user/change_email.html", form=form)
 
 
-@user.route("/settings/change_user_details", methods=["POST", "GET"])
+@user.route("/settings/user-details", methods=["POST", "GET"])
 @login_required
 def change_user_details():
     form = ChangeUserDetailsForm()

+ 15 - 1
flaskbb/utils/helpers.py

@@ -12,12 +12,26 @@ import time
 from datetime import datetime, timedelta
 from collections import OrderedDict
 
-from flask import current_app
+from flask import current_app, session
+from flask.ext.themes2 import render_theme_template
+from flask.ext.login import current_user
+
 from postmarkup import render_bbcode
 
 from flaskbb.extensions import redis
 
 
+def render_template(template, **context):
+    """A helper function that uses the `render_theme_template` function
+    without needing to edit all the views
+    """
+    if current_user.is_authenticated() and current_user.theme:
+        theme = current_user.theme
+    else:
+        theme = session.get('theme', current_app.config['DEFAULT_THEME'])
+    return render_theme_template(theme, template, **context)
+
+
 def get_forums(forum_query):
     """Returns a dictionary where the key is the category and the values
     are the forums with their forumsread status