Просмотр исходного кода

PEP8 fixes + further work on the admin panel

sh4nks 11 лет назад
Родитель
Сommit
e96054c861

+ 183 - 0
flaskbb/admin/forms.py

@@ -1 +1,184 @@
 # -*- coding: utf-8 -*-
+"""
+    flaskbb.admin.forms
+    ~~~~~~~~~~~~~~~~~~~~
+
+    It provides the forms that are needed for the admin views.
+
+    :copyright: (c) 2013 by the FlaskBB Team.
+    :license: BSD, see LICENSE for more details.
+"""
+from datetime import datetime
+
+from flask.ext.wtf import Form
+from wtforms import (TextField, TextAreaField, PasswordField, IntegerField,
+                     BooleanField, SelectField, DateField)
+from wtforms.validators import (Required, Optional, Email, EqualTo, regexp,
+                                ValidationError, URL, Length)
+
+from wtforms.ext.sqlalchemy.fields import (QuerySelectField,
+                                           QuerySelectMultipleField)
+
+from flaskbb.helpers import SelectDateWidget
+from flaskbb.forum.models import Category, Forum
+from flaskbb.user.models import User, Group
+
+USERNAME_RE = r'^[\w.+-]+$'
+is_username = regexp(USERNAME_RE,
+                     message=("You can only use letters, numbers or dashes"))
+
+
+def selectable_categories():
+    return Category.query.order_by(Category.id)
+
+
+def select_primary_group():
+    return Group.query.order_by(Group.id)
+
+
+def select_secondary_groups():
+    return Group.query.order_by(Group.id)
+
+
+class UserForm(Form):
+    username = TextField("Username", validators=[
+        Required(message="Username required"),
+        is_username])
+
+    email = TextField("E-Mail", validators=[
+        Required(message="Email adress required"),
+        Email(message="This email is invalid")])
+
+    password = PasswordField("Password", validators=[
+        Required(message="Password required")])
+
+    birthday = DateField("Birthday", format="%d %m %Y",
+                         widget=SelectDateWidget(),
+                         validators=[Optional()])
+
+    gender = SelectField("Gender", default="None", choices=[
+        ("None", ""),
+        ("Male", "Male"),
+        ("Female", "Female")])
+
+    location = TextField("Location", validators=[
+        Optional()])
+
+    website = TextField("Website", validators=[
+        Optional(), URL()])
+
+    avatar = TextField("Avatar", validators=[
+        Optional(), URL()])
+
+    signature = TextAreaField("Forum Signature", validators=[
+        Optional(), Length(min=0, max=250)])
+
+    notes = TextAreaField("Notes", validators=[
+        Optional(), Length(min=0, max=5000)])
+
+    primary_group = QuerySelectField("Primary Group",
+                                     query_factory=select_primary_group,
+                                     get_label="name")
+
+    #secondary_groups = QuerySelectMultipleField(
+    #    "Secondary Groups", query_factory=select_secondary_groups,
+    #    allow_blank=True, get_label="name")
+
+    def validate_username(self, field):
+        user = User.query.filter_by(username=field.data).first()
+        if user:
+            raise ValidationError("This username is taken")
+
+    def validate_email(self, field):
+        email = User.query.filter_by(email=field.data).first()
+        if email:
+            raise ValidationError("This email is taken")
+
+    def save(self):
+        user = User(date_joined=datetime.utcnow(),
+                    **self.data)
+        return user.save()
+
+
+class GroupForm(Form):
+    name = TextField("Group Name", validators=[
+        Required(message="Group name required")])
+
+    description = TextAreaField("Description", validators=[
+        Optional()])
+
+    admin = BooleanField("Is Admin Group?",
+                         description="With this option the group has access \
+                                     to the admin panel.")
+    super_mod = BooleanField("Is Super Moderator Group?",
+                             description="Check this if the users in this \
+                                         group are allowed to moderate every \
+                                         forum")
+    mod = BooleanField("Is Moderator Group?",
+                       description="Check this if the users in this group are \
+                                   allowed to moderate specified forums")
+    banned = BooleanField("Is Banned Group?",
+                          description="Only one Banned group is allowed")
+    guest = BooleanField("Is Guest Group?",
+                         description="Only one Guest group is allowed")
+    editpost = BooleanField("Can edit posts",
+                            description="Check this if the users in this \
+                                        group can edit posts")
+    deletepost = BooleanField("Can delete posts",
+                              description="Check this is the users in this \
+                                          group can delete posts")
+    deletetopic = BooleanField("Can delete topics",
+                               description="Check this is the users in this \
+                                           group can delete topics")
+    posttopic = BooleanField("Can create topics",
+                             description="Check this is the users in this \
+                                         group can create topics")
+    postreply = BooleanField("Can post replies",
+                             description="Check this is the users in this \
+                                         group can post replies")
+
+    def save(self):
+        group = Group(**self.data)
+        return group.save()
+
+    def validate_banned(self, field):
+        if Group.query.filter_by(banned=True).count >= 1:
+            raise ValidationError("There is already a Banned group")
+
+    def validate_guest(self, field):
+        if Group.query.filter_by(guest=True).count() >= 1:
+            raise ValidationError("There is already a Guest group")
+
+
+class ForumForm(Form):
+    title = TextField("Forum Title", validators=[
+        Required(message="Forum title required")])
+
+    description = TextAreaField("Description", validators=[
+        Optional()])
+
+    position = IntegerField("Position", validators=[
+        Required(message="Forum position required")])
+
+    category = QuerySelectField("Category",
+                                query_factory=selectable_categories,
+                                get_label="title")
+
+    def save(self, category):
+        forum = Forum(**self.data)
+        return forum.save(category=category)
+
+
+class CategoryForm(Form):
+    title = TextField("Category Title", validators=[
+        Required(message="Category title required")])
+
+    description = TextAreaField("Description", validators=[
+        Optional()])
+
+    position = IntegerField("Position", validators=[
+        Required(message="Forum position required")])
+
+    def save(self):
+        category = Category(**self.data)
+        return category.save()

+ 0 - 1
flaskbb/admin/models.py

@@ -1 +0,0 @@
-# -*- coding: utf-8 -*-

+ 144 - 56
flaskbb/admin/views.py

@@ -1,16 +1,19 @@
 # -*- coding: utf-8 -*-
 import sys
 
-from flask import (Blueprint, render_template, current_app, request,
-    __version__ as flask_version)
+from flask import (Blueprint, render_template, current_app, request, redirect,
+                   url_for, flash, __version__ as flask_version)
 
+from flaskbb import __version__ as flaskbb_version
 from flaskbb.decorators import admin_required
 from flaskbb.user.models import User, Group
 from flaskbb.forum.models import Post, Topic, Forum, Category
-from flaskbb import __version__ as flaskbb_version
+from flaskbb.admin.forms import UserForm, GroupForm, ForumForm, CategoryForm
+
 
 admin = Blueprint("admin", __name__)
 
+
 @admin.route("/")
 @admin_required
 def overview():
@@ -19,12 +22,12 @@ def overview():
     topic_count = Topic.query.count()
     post_count = Post.query.count()
     return render_template("admin/overview.html",
-        python_version=python_version,
-        flask_version=flask_version,
-        flaskbb_version=flaskbb_version,
-        user_count=user_count,
-        topic_count=topic_count,
-        post_count=post_count)
+                           python_version=python_version,
+                           flask_version=flask_version,
+                           flaskbb_version=flaskbb_version,
+                           user_count=user_count,
+                           topic_count=topic_count,
+                           post_count=post_count)
 
 
 @admin.route("/users")
@@ -67,32 +70,33 @@ def forums():
     return render_template("admin/forums.html", forums=forums)
 
 
-@admin.route("/users/<int:user_id>/edit")
+@admin.route("/users/<int:user_id>/edit", methods=["GET", "POST"])
 @admin_required
 def edit_user(user_id):
     user = User.query.filter_by(id=user_id).first()
 
-    if user:
-        form = EditUserForm()
-        if form.validate_on_submit():
-            form.populate_obj(user)
-            user.save()
-        else:
-            form.username.data = user.username
-            form.email.data = user.email
-            form.password.data = user.password
-            form.birthday.data = user.birthday
-            form.gender.data = user.gender
-            form.website.data = user.website
-            form.location.data = user.location
-            form.signature.data= user.signature
-            form.avatar.data = user.avatar
-            form.notes.data = user.notes
-            form.primary_group.data = user.primary_group_id
-            form.secondary_groups.data = user.groups
-
-    flash("User successfully edited")
-    return redirect(url_for("admin.users"))
+    form = UserForm()
+    if form.validate_on_submit():
+        form.populate_obj(user)
+        user.save()
+
+        flash("User successfully edited", "success")
+        return redirect(url_for("admin.edit_user", user_id=user.id))
+    else:
+        form.username.data = user.username
+        form.email.data = user.email
+        form.password.data = user.password
+        form.birthday.data = user.birthday
+        form.gender.data = user.gender
+        form.website.data = user.website
+        form.location.data = user.location
+        form.signature.data = user.signature
+        form.avatar.data = user.avatar
+        form.notes.data = user.notes
+        form.primary_group.data = user.primary_group_id
+        #form.secondary_groups.data = user.groups
+
+    return render_template("admin/edit_user.html", form=form)
 
 
 @admin.route("/users/<int:user_id>/delete")
@@ -100,68 +104,152 @@ def edit_user(user_id):
 def delete_user(user_id):
     user = User.query.filter_by(id=user_id).first()
     user.delete()
-    flash("User successfully deleted")
+    flash("User successfully deleted", "success")
     return redirect(url_for("admin.users"))
 
 
-@admin.route("/users/add")
+@admin.route("/users/add", methods=["GET", "POST"])
 @admin_required
 def add_user():
-    form = RegisterForm()
+    form = UserForm()
     if form.validate_on_submit():
-        user = form.save()
-    return redirect(url_for("admin.users"))
+        form.save()
+        flash("User successfully added.", "success")
+        return redirect(url_for("admin.users"))
+
+    return render_template("admin/add_user.html", form=form)
 
 
-@admin.route("/groups/<int:group_id>/edit")
+@admin.route("/groups/<int:group_id>/edit", methods=["GET", "POST"])
 @admin_required
 def edit_group(group_id):
     group = Group.query.filter_by(id=group_id).first()
 
-    if group:
-        form = EditGroupForm()
-        if form.validate_on_submit():
-            pass
-        else:
-            pass
+    form = GroupForm()
+    if form.validate_on_submit():
+        form.populate_obj(group)
+        group.save()
+
+        flash("Group successfully edited.", "success")
+        return redirect(url_for("admin.edit_group", group_id=group.id))
+    else:
+        form.name.data = group.name
+        form.description.data = group.description
+        form.admin.data = group.admin
+        form.super_mod.data = group.super_mod
+        form.mod.data = group.mod
+        form.guest.data = group.guest
+        form.banned.data = group.banned
+        form.editpost.data = group.editpost
+        form.deletepost.data = group.deletepost
+        form.deletetopic.data = group.deletetopic
+        form.posttopic.data = group.posttopic
+        form.postreply.data = group.postreply
+
+    return render_template("admin/edit_group.html", form=form)
+
 
 @admin.route("/groups/<int:group_id>/delete")
 @admin_required
 def delete_group(group_id):
-    group = Group.query.filter.
+    group = Group.query.filter_by(id=group_id).first()
+    group.delete()
+    flash("Group successfully deleted.", "success")
+    return redirect(url_for("admin.groups"))
+
 
-@admin.route("/groups/add")
+@admin.route("/groups/add", methods=["GET", "POST"])
 @admin_required
 def add_group():
-    pass
+    form = GroupForm()
+    if form.validate_on_submit():
+        form.save()
+        flash("Group successfully added.", "success")
+        return redirect(url_for("admin.users"))
+
+    return render_template("admin/add_group.html", form=form)
 
 
-@admin.route("/forums/<int:forum_id>/edit")
+@admin.route("/forums/<int:forum_id>/edit", methods=["GET", "POST"])
 @admin_required
 def edit_forum(forum_id):
-    pass
+    forum = Forum.query.filter_by(id=forum_id).first()
+
+    form = ForumForm()
+    if form.validate_on_submit():
+        form.populate_obj(forum)
+        forum.save()
+
+        flash("Forum successfully edited.", "success")
+        return redirect(url_for("admin.edit_forum", forum_id=forum.id))
+    else:
+        form.title.data = forum.title
+        form.description.data = forum.description
+        form.position.data = forum.position
+        form.category.data = forum.category_id
+        #form.moderators.data = forum.moderators
+
+    return render_template("admin/edit_forum.html", form=form)
+
 
 @admin.route("/forums/<int:forum_id>/delete")
 @admin_required
 def delete_forum(forum_id):
-    pass
+    forum = Forum.query.filter_by(id=forum_id).first()
+    forum.delete()
+    flash("Forum successfully deleted.", "success")
+    return redirect(url_for("admin.forums"))
+
 
-@admin.route("/forums/add")
+@admin.route("/forums/add", methods=["GET", "POST"])
 @admin_required
 def add_forum():
-    pass
+    form = ForumForm()
+
+    if form.validate_on_submit():
+        form.save()
+        flash("Forum successfully added.", "success")
+        return redirect(url_for("admin.forums"))
+
+    return render_template("admin/add_forum.html", form=form)
+
 
-@admin.route("/categories/<int:category_id>/edit")
+@admin.route("/categories/<int:category_id>/edit", methods=["GET", "POST"])
 @admin_required
 def edit_category(category_id):
-    pass
+    category = Category.query.filter_by(id=category_id).first()
+
+    form = CategoryForm()
+    if form.validate_on_submit():
+        form.populate_obj(category)
+        category.save()
+        flash("Category successfully edited.", "success")
+        return redirect(url_for("admin.edit_category", category_id=category.id))
+    else:
+        form.title.data = category.title
+        form.description.data = category.description
+        form.position.data = category.position
+
+    return render_template("admin/edit_category.html", form=form)
+
 
 @admin.route("/categories/<int:category_id>/delete")
 @admin_required
 def delete_category(category_id):
-    pass
+    category = Category.query.filter_by(id=category_id).first()
+    category.delete()
+    flash("Category successfully deleted.", "success")
+    return redirect(url_for("admin.categories"))
 
-@admin.route("/categories/add")
+
+@admin.route("/categories/add", methods=["GET", "POST"])
 @admin_required
 def add_category():
-    pass
+    form = CategoryForm()
+
+    if form.validate_on_submit():
+        form.save()
+        flash("Category successfully added.", "success")
+        return redirect(url_for("admin.categories"))
+
+    return render_template("admin/add_category.html", form=form)

+ 4 - 2
flaskbb/app.py

@@ -28,10 +28,11 @@ from flaskbb.pms.views import pms
 from flaskbb.forum.views import forum
 from flaskbb.forum.models import *
 
-from flaskbb.extensions import db, login_manager, mail, cache #toolbar
+from flaskbb.extensions import db, login_manager, mail, cache
 
 from flaskbb.template_filters import (format_date, time_since, is_online,
-    edit_post, delete_post, delete_topic, post_reply, crop_title)
+                                      edit_post, delete_post, delete_topic,
+                                      post_reply, crop_title)
 
 DEFAULT_BLUEPRINTS = (
     (forum, ""),
@@ -135,6 +136,7 @@ def configure_template_filters(app):
     app.jinja_env.filters['post_reply'] = post_reply
     app.jinja_env.filters['crop_title'] = crop_title
 
+
 def configure_before_handlers(app):
     """
     Configures the before request handlers

+ 6 - 6
flaskbb/auth/forms.py

@@ -43,7 +43,7 @@ class RegisterForm(Form):
     password = PasswordField("Password", validators=[
         Required(message="Password required")])
 
-    confirm_password = PasswordField("Confirm Password", [
+    confirm_password = PasswordField("Confirm Password", validators=[
         Required(message="Confirm Password required"),
         EqualTo("password", message="Passwords do not match")])
 
@@ -72,7 +72,7 @@ class ReauthForm(Form):
 
 
 class ForgotPasswordForm(Form):
-    email = TextField('Email', validators = [
+    email = TextField('Email', validators=[
         Required(message="Email reguired"),
         Email()])
 
@@ -80,16 +80,16 @@ class ForgotPasswordForm(Form):
 class ResetPasswordForm(Form):
     token = HiddenField('Token')
 
-    email = TextField('Email', validators = [
+    email = TextField('Email', validators=[
         Required(),
         Email()])
 
-    password = PasswordField('Password', validators = [
+    password = PasswordField('Password', validators=[
         Required()])
 
-    confirm_password = PasswordField('Confirm password', validators = [
+    confirm_password = PasswordField('Confirm password', validators=[
         Required(),
-        EqualTo('password', message = 'Passwords must match')])
+        EqualTo('password', message='Passwords must match')])
 
     def validate_email(self, field):
         email = User.query.filter_by(email=field.data).first()

+ 55 - 13
flaskbb/forum/models.py

@@ -13,11 +13,14 @@ from datetime import datetime
 from flaskbb.extensions import db
 from flaskbb.helpers import DenormalizedText
 
+
 class Post(db.Model):
     __tablename__ = "posts"
 
     id = db.Column(db.Integer, primary_key=True)
-    topic_id = db.Column(db.Integer, db.ForeignKey("topics.id", use_alter=True, name="fk_topic_id", ondelete="CASCADE"))
+    topic_id = db.Column(db.Integer, db.ForeignKey("topics.id", use_alter=True,
+                                                   name="fk_topic_id",
+                                                   ondelete="CASCADE"))
     user_id = db.Column(db.Integer, db.ForeignKey("users.id"))
     content = db.Column(db.Text)
     date_created = db.Column(db.DateTime, default=datetime.utcnow())
@@ -82,7 +85,8 @@ class Topic(db.Model):
     __tablename__ = "topics"
 
     id = db.Column(db.Integer, primary_key=True)
-    forum_id = db.Column(db.Integer, db.ForeignKey("forums.id", use_alter=True, name="fk_forum_id"))
+    forum_id = db.Column(db.Integer, db.ForeignKey("forums.id", use_alter=True,
+                                                   name="fk_forum_id"))
     title = db.Column(db.String)
     user_id = db.Column(db.Integer, db.ForeignKey("users.id"))
     date_created = db.Column(db.DateTime, default=datetime.utcnow())
@@ -92,15 +96,22 @@ class Topic(db.Model):
     post_count = db.Column(db.Integer, default=0)
 
     # One-to-one (uselist=False) relationship between first_post and topic
-    first_post_id = db.Column(db.Integer, db.ForeignKey("posts.id", ondelete="CASCADE"))
-    first_post = db.relationship("Post", backref="first_post", uselist=False, foreign_keys=[first_post_id])
+    first_post_id = db.Column(db.Integer, db.ForeignKey("posts.id",
+                                                        ondelete="CASCADE"))
+    first_post = db.relationship("Post", backref="first_post", uselist=False,
+                                 foreign_keys=[first_post_id])
 
     # One-to-one
-    last_post_id = db.Column(db.Integer, db.ForeignKey("posts.id", ondelete="CASCADE", onupdate="CASCADE"))
-    last_post = db.relationship("Post", backref="last_post", uselist=False, foreign_keys=[last_post_id])
+    last_post_id = db.Column(db.Integer, db.ForeignKey("posts.id",
+                                                       ondelete="CASCADE",
+                                                       onupdate="CASCADE"))
+    last_post = db.relationship("Post", backref="last_post", uselist=False,
+                                foreign_keys=[last_post_id])
 
     # One-to-many
-    posts = db.relationship("Post", backref="topic", lazy="joined", primaryjoin="Post.topic_id == Topic.id", cascade="all, delete-orphan", post_update=True)
+    posts = db.relationship("Post", backref="topic", lazy="joined",
+                            primaryjoin="Post.topic_id == Topic.id",
+                            cascade="all, delete-orphan", post_update=True)
 
     def __init__(self, title=None):
         if title:
@@ -161,12 +172,18 @@ class Topic(db.Model):
 
         # Update the post counts
         if users:
-            # If someone knows a better method for this, feel free to improve it :)
+            # If someone knows a better method for this,
+            # feel free to improve it :)
             for user in users:
                 user.post_count = Post.query.filter_by(user_id=user.id).count()
                 db.session.commit()
-        forum.topic_count = Topic.query.filter_by(forum_id=self.forum_id).count()
-        forum.post_count = Post.query.filter(Post.topic_id == Topic.id, Topic.forum_id == self.forum_id).count()
+        forum.topic_count = Topic.query.filter_by(
+            forum_id=self.forum_id).count()
+
+        forum.post_count = Post.query.filter(
+            Post.topic_id == Topic.id,
+            Topic.forum_id == self.forum_id).count()
+
         db.session.commit()
 
         return self
@@ -179,13 +196,16 @@ class Forum(db.Model):
     title = db.Column(db.String)
     description = db.Column(db.String)
     position = db.Column(db.Integer, default=0)
-    category_id = db.Column(db.Integer, db.ForeignKey("categories.id", use_alter=True, name="fk_category_id"))
+    category_id = db.Column(db.Integer, db.ForeignKey("categories.id",
+                                                      use_alter=True,
+                                                      name="fk_category_id"))
     post_count = db.Column(db.Integer, default=0)
     topic_count = db.Column(db.Integer, default=0)
 
     # One-to-one
     last_post_id = db.Column(db.Integer, db.ForeignKey("posts.id"))
-    last_post = db.relationship("Post", backref="last_post_forum", uselist=False, foreign_keys=[last_post_id])
+    last_post = db.relationship("Post", backref="last_post_forum",
+                                uselist=False, foreign_keys=[last_post_id])
 
     # One-to-many
     topics = db.relationship("Topic", backref="forum", lazy="joined")
@@ -198,6 +218,17 @@ class Forum(db.Model):
     def remove_moderator(self, user_id):
         self.moderators.remove(user_id)
 
+    def save(self, category):
+        self.category_id = category.id
+        db.session.add(self)
+        db.session.commit()
+        return self
+
+    def delete(self):
+        db.session.delete(self)
+        db.session.commit()
+        return self
+
 
 class Category(db.Model):
     __tablename__ = "categories"
@@ -208,4 +239,15 @@ class Category(db.Model):
     position = db.Column(db.Integer, default=0)
 
     # One-to-many
-    forums = db.relationship("Forum", backref="category", lazy="joined", primaryjoin="Forum.category_id == Category.id")
+    forums = db.relationship("Forum", backref="category", lazy="joined",
+                             primaryjoin="Forum.category_id == Category.id")
+
+    def save(self):
+        db.session.add(self)
+        db.session.commit()
+        return self
+
+    def delete(self):
+        db.session.delete(self)
+        db.session.commit()
+        return self

+ 6 - 6
flaskbb/forum/views.py

@@ -111,7 +111,7 @@ def new_topic(forum_id):
     forum = Forum.query.filter_by(id=forum_id).first()
 
     if not check_perm(current_user, 'posttopic', forum):
-        flash("You do not have the permissions to create a new topic.")
+        flash("You do not have the permissions to create a new topic.", "error")
         return redirect(url_for('forum.view_forum', forum_id=forum.id))
 
     form = NewTopicForm()
@@ -129,7 +129,7 @@ def delete_topic(topic_id):
     topic = Topic.query.filter_by(id=topic_id).first()
 
     if not check_perm(current_user, 'deletetopic', topic.forum):
-        flash("You do not have the permissions to delete the topic")
+        flash("You do not have the permissions to delete the topic", "error")
         return redirect(url_for("forum.view_forum", forum_id=topic.forum_id))
 
     involved_users = User.query.filter(Post.topic_id == topic.id,
@@ -144,11 +144,11 @@ def new_post(topic_id):
     topic = Topic.query.filter_by(id=topic_id).first()
 
     if topic.locked:
-        flash("The topic is locked.")
+        flash("The topic is locked.", "error")
         return redirect(url_for("forum.view_forum", forum_id=topic.forum_id))
 
     if not check_perm(current_user, 'postreply', topic.forum):
-        flash("You do not have the permissions to delete the topic")
+        flash("You do not have the permissions to delete the topic", "error")
         return redirect(url_for("forum.view_forum", forum_id=topic.forum_id))
 
     form = ReplyForm()
@@ -166,7 +166,7 @@ def edit_post(post_id):
 
     if not check_perm(current_user, 'editpost', post.topic.forum,
         post.user_id):
-        flash("You do not have the permissions to edit this post")
+        flash("You do not have the permissions to edit this post", "error")
         return redirect(url_for('forum.view_topic', topic_id=post.topic_id))
 
     form = ReplyForm(obj=post)
@@ -188,7 +188,7 @@ def delete_post(post_id):
 
     if not check_perm(current_user, 'deletepost', post.topic.forum,
         post.user_id):
-        flash("You do not have the permissions to edit this post")
+        flash("You do not have the permissions to edit this post", "error")
         return redirect(url_for('forum.view_topic', topic_id=post.topic_id))
 
     topic_id = post.topic_id

+ 3 - 2
flaskbb/pms/forms.py

@@ -33,10 +33,11 @@ class NewMessage(Form):
         if user.id == current_user.id:
             raise ValidationError("You cannot send a PM to yourself.")
 
-    def save(self, from_user, to_user, user_id, as_draft=False):
+    def save(self, from_user, to_user, user_id, unread, as_draft=False):
         message = PrivateMessage(
             subject=self.subject.data,
-            message=self.message.data)
+            message=self.message.data,
+            unread=unread)
 
         if as_draft:
             return message.save(from_user, to_user, user_id, draft=True)

+ 7 - 5
flaskbb/pms/models.py

@@ -27,8 +27,10 @@ class PrivateMessage(db.Model):
     draft = db.Column(db.Boolean, nullable=False, default=False)
     unread = db.Column(db.Boolean, nullable=False, default=True)
 
-    user = db.relationship("User", backref="pms", lazy="joined", foreign_keys=[user_id])
-    from_user = db.relationship("User", lazy="joined", foreign_keys=[from_user_id])
+    user = db.relationship("User", backref="pms", lazy="joined",
+                           foreign_keys=[user_id])
+    from_user = db.relationship("User", lazy="joined",
+                                foreign_keys=[from_user_id])
     to_user = db.relationship("User", lazy="joined", foreign_keys=[to_user_id])
 
     def save(self, from_user=None, to_user=None, user_id=None, draft=False):
@@ -41,9 +43,9 @@ class PrivateMessage(db.Model):
             self.draft = True
 
         # Add the message to the user's pm box
-        self.user_id=user_id
-        self.from_user_id=from_user
-        self.to_user_id=to_user
+        self.user_id = user_id
+        self.from_user_id = from_user
+        self.to_user_id = to_user
 
         db.session.add(self)
         db.session.commit()

+ 5 - 2
flaskbb/pms/views.py

@@ -85,6 +85,7 @@ def new_message():
             form.save(from_user=current_user.id,
                       to_user=to_user.id,
                       user_id=current_user.id,
+                      unread=True,
                       as_draft=True)
 
             flash("Message saved!", "success")
@@ -96,12 +97,14 @@ def new_message():
             # Save the message in the current users inbox
             form.save(from_user=current_user.id,
                       to_user=to_user.id,
-                      user_id=current_user.id)
+                      user_id=current_user.id,
+                      unread=False)
 
             # Save the message in the recievers inbox
             form.save(from_user=current_user.id,
                       to_user=to_user.id,
-                      user_id=to_user.id)
+                      user_id=to_user.id,
+                      unread=True)
 
             flash("Message sent!", "success")
             return redirect(url_for("pms.sent"))

+ 6 - 0
flaskbb/template_filters.py

@@ -1,30 +1,36 @@
 from flask import current_app
 from flaskbb.helpers import time_diff, time_delta_format, check_perm
 
+
 def format_date(value, format='%Y-%m-%d'):
     """
     Returns a formatted time string
     """
     return value.strftime(format)
 
+
 def time_since(value):
     return time_delta_format(value)
 
+
 def is_online(user):
     return user.lastseen >= time_diff()
 
+
 def is_current_user(user, post):
     """
     Check if the post is written by the user
     """
     return post.user_id == user.id
 
+
 def edit_post(user, post, forum):
     """
     Check if the post can be edited by the user
     """
     return check_perm(user, 'deletepost', forum, post.user_id)
 
+
 def delete_post(user, post, forum):
     """
     Check if the post can be edited by the user

+ 22 - 0
flaskbb/templates/admin/add_category.html

@@ -0,0 +1,22 @@
+{% set page_title = "Add Category" %}
+{% set active_forum_nav=True %}
+
+{% extends "admin/admin_layout.html" %}
+{% block admin_content %}
+{% from "macros.html" import render_field %}
+
+<form class="form-horizontal" role="form" method="post">
+    {{ form.hidden_tag() }}
+    <legend class="">Add Forum</legend>
+        {{ render_field(form.title) }}
+        {{ render_field(form.position) }}
+        {{ render_field(form.description, rows=5, div_class="col-lg-9") }}
+
+        <div class="form-group">
+            <div class="col-lg-offset-0 col-lg-9">
+                <button type="submit" class="btn btn-success">Save</button>
+            </div>
+        </div>
+</form>
+
+{% endblock %}

+ 23 - 0
flaskbb/templates/admin/add_forum.html

@@ -0,0 +1,23 @@
+{% set page_title = "Add Forum" %}
+{% set active_forum_nav=True %}
+
+{% extends "admin/admin_layout.html" %}
+{% block admin_content %}
+{% from "macros.html" import render_field, render_select_field %}
+
+<form class="form-horizontal" role="form" method="post">
+    {{ form.hidden_tag() }}
+    <legend class="">Add Forum</legend>
+        {{ render_field(form.title) }}
+        {{ render_field(form.position) }}
+        {{ render_select_field(form.category) }}
+        {{ render_field(form.description, rows=5, div_class="col-lg-9") }}
+
+        <div class="form-group">
+            <div class="col-lg-offset-0 col-lg-9">
+                <button type="submit" class="btn btn-success">Save</button>
+            </div>
+        </div>
+</form>
+
+{% endblock %}

+ 35 - 0
flaskbb/templates/admin/add_group.html

@@ -0,0 +1,35 @@
+{% set page_title = "Add Group" %}
+{% set active_forum_nav=True %}
+
+{% extends "admin/admin_layout.html" %}
+{% block admin_content %}
+{% from "macros.html" import render_field, render_boolean_field %}
+
+<form class="form-horizontal" role="form" method="post">
+    {{ form.hidden_tag() }}
+    <legend class="">Add Group</legend>
+        {{ render_field(form.name) }}
+        {{ render_field(form.description) }}
+
+        {{ render_boolean_field(form.admin) }}
+        {{ render_boolean_field(form.super_mod) }}
+
+
+        {{ render_boolean_field(form.mod) }}
+        {{ render_boolean_field(form.banned) }}
+        {{ render_boolean_field(form.guest) }}
+
+        {{ render_boolean_field(form.editpost) }}
+        {{ render_boolean_field(form.deletepost) }}
+        {{ render_boolean_field(form.deletetopic) }}
+        {{ render_boolean_field(form.posttopic) }}
+        {{ render_boolean_field(form.postreply) }}
+
+        <div class="form-group">
+            <div class="col-lg-offset-0 col-lg-9">
+                <button type="submit" class="btn btn-success">Save</button>
+            </div>
+        </div>
+</form>
+
+{% endblock %}

+ 31 - 0
flaskbb/templates/admin/add_user.html

@@ -0,0 +1,31 @@
+{% set page_title = "Add User" %}
+{% set active_forum_nav=True %}
+
+{% extends "admin/admin_layout.html" %}
+{% block admin_content %}
+{% from "macros.html" import render_field, render_select_field %}
+
+<form class="form-horizontal" role="form" method="post">
+    {{ form.hidden_tag() }}
+    <legend class="">Add User</legend>
+        {{ render_field(form.username) }}
+        {{ render_field(form.email) }}
+        {{ render_field(form.password) }}
+        {{ render_field(form.birthday) }}
+        {{ render_select_field(form.gender) }}
+        {{ render_field(form.location) }}
+        {{ render_field(form.website) }}
+        {{ render_field(form.avatar) }}
+        {{ render_select_field(form.primary_group) }}
+        {{ render_field(form.signature, rows=5, div_class="col-lg-9") }}
+        {{ render_field(form.notes, rows=12, div_class="col-lg-9") }}
+
+
+        <div class="form-group">
+            <div class="col-lg-offset-0 col-lg-9">
+                <button type="submit" class="btn btn-success">Save</button>
+            </div>
+        </div>
+</form>
+
+{% endblock %}

+ 22 - 0
flaskbb/templates/admin/edit_category.html

@@ -0,0 +1,22 @@
+{% set page_title = "Add Category" %}
+{% set active_forum_nav=True %}
+
+{% extends "admin/admin_layout.html" %}
+{% block admin_content %}
+{% from "macros.html" import render_field %}
+
+<form class="form-horizontal" role="form" method="post">
+    {{ form.hidden_tag() }}
+    <legend class="">Add Forum</legend>
+        {{ render_field(form.title) }}
+        {{ render_field(form.position) }}
+        {{ render_field(form.description, rows=5, div_class="col-lg-9") }}
+
+        <div class="form-group">
+            <div class="col-lg-offset-0 col-lg-9">
+                <button type="submit" class="btn btn-success">Save</button>
+            </div>
+        </div>
+</form>
+
+{% endblock %}

+ 23 - 0
flaskbb/templates/admin/edit_forum.html

@@ -0,0 +1,23 @@
+{% set page_title = "Add Forum" %}
+{% set active_forum_nav=True %}
+
+{% extends "admin/admin_layout.html" %}
+{% block admin_content %}
+{% from "macros.html" import render_field, render_select_field %}
+
+<form class="form-horizontal" role="form" method="post">
+    {{ form.hidden_tag() }}
+    <legend class="">Add Forum</legend>
+        {{ render_field(form.title) }}
+        {{ render_field(form.position) }}
+        {{ render_select_field(form.category) }}
+        {{ render_field(form.description, rows=5, div_class="col-lg-9") }}
+
+        <div class="form-group">
+            <div class="col-lg-offset-0 col-lg-9">
+                <button type="submit" class="btn btn-success">Save</button>
+            </div>
+        </div>
+</form>
+
+{% endblock %}

+ 35 - 0
flaskbb/templates/admin/edit_group.html

@@ -0,0 +1,35 @@
+{% set page_title = "Add Group" %}
+{% set active_forum_nav=True %}
+
+{% extends "admin/admin_layout.html" %}
+{% block admin_content %}
+{% from "macros.html" import render_field, render_boolean_field %}
+
+<form class="form-horizontal" role="form" method="post">
+    {{ form.hidden_tag() }}
+    <legend class="">Add Group</legend>
+        {{ render_field(form.name) }}
+        {{ render_field(form.description) }}
+
+        {{ render_boolean_field(form.admin) }}
+        {{ render_boolean_field(form.super_mod) }}
+
+
+        {{ render_boolean_field(form.mod) }}
+        {{ render_boolean_field(form.banned) }}
+        {{ render_boolean_field(form.guest) }}
+
+        {{ render_boolean_field(form.editpost) }}
+        {{ render_boolean_field(form.deletepost) }}
+        {{ render_boolean_field(form.deletetopic) }}
+        {{ render_boolean_field(form.posttopic) }}
+        {{ render_boolean_field(form.postreply) }}
+
+        <div class="form-group">
+            <div class="col-lg-offset-0 col-lg-9">
+                <button type="submit" class="btn btn-success">Save</button>
+            </div>
+        </div>
+</form>
+
+{% endblock %}

+ 31 - 0
flaskbb/templates/admin/edit_user.html

@@ -0,0 +1,31 @@
+{% set page_title = "Add User" %}
+{% set active_forum_nav=True %}
+
+{% extends "admin/admin_layout.html" %}
+{% block admin_content %}
+{% from "macros.html" import render_field, render_select_field %}
+
+<form class="form-horizontal" role="form" method="post">
+    {{ form.hidden_tag() }}
+    <legend class="">Add User</legend>
+        {{ render_field(form.username) }}
+        {{ render_field(form.email) }}
+        {{ render_field(form.password) }}
+        {{ render_field(form.birthday) }}
+        {{ render_select_field(form.gender) }}
+        {{ render_field(form.location) }}
+        {{ render_field(form.website) }}
+        {{ render_field(form.avatar) }}
+        {{ render_select_field(form.primary_group) }}
+        {{ render_field(form.signature, rows=5, div_class="col-lg-9") }}
+        {{ render_field(form.notes, rows=12, div_class="col-lg-9") }}
+
+
+        <div class="form-group">
+            <div class="col-lg-offset-0 col-lg-9">
+                <button type="submit" class="btn btn-success">Save</button>
+            </div>
+        </div>
+</form>
+
+{% endblock %}

+ 76 - 55
flaskbb/templates/macros.html

@@ -1,7 +1,81 @@
-{%- macro inline_field(field, label_text='') -%}
+{%- macro field_label(field) -%}
+    <label for="{{ field.id }}">{{ field.label.text }}</label>
+{% endmacro %}
+
+
+{%- macro field_description(field) -%}
+    {% if field.description %}
+        <p class="help-block">{{ field.description }}</p>
+    {% endif %}
+{%- endmacro -%}
+
+
+{%- macro field_errors(field) -%}
+    {% if field.errors %}
+        {%- for error in field.errors -%}
+        <span class="help-block">{{error}}</span>
+        {%- endfor -%}
+    {% endif %}
+{%- endmacro -%}
+
+
+{%- macro render_boolean_field(field, inline=False) -%}
+<label class="checkbox {%- if inline -%}inline{%- endif -%}">
+    {{ field(**kwargs) }}
+    {{ field_label(field) }}
+    {{ field_description(field) }}
+    {{ field_errors(field) }}
+</label>
+{%- endmacro -%}
+
+{%- macro render_select_field(field, div_class='') -%}
+<div class="form-group">
+    {% if div_class %}
+    <div class="{{ div_class }}">
+    {% else %}
+    <div class="col-lg-5">
+    {% endif %}
+        <label>{{ field.label.text }}</label>
+        {% if field.type == 'QuerySelectMultipleField' or field.type == 'SelectMultipleField' %}
+            {{ field(multiple=True, class="form-control") }}
+        {% else %}
+            {{ field(class="form-control") }}
+        {%- endif -%}
+    </div>
+</div>
+{%- endmacro -%}
+
+{%- macro render_field(field, div_class='', rows='') -%}
+<div class="form-group">
+    {% if div_class %}
+    <div class="{{ div_class }}">
+    {% else %}
+    <div class="col-lg-5">
+    {% endif %}
+        <label>{{ field.label.text }}</label>
+        {%- if kwargs['required'] or field.flags.required -%}
+            {% if rows %}
+                {{ field(class="form-control", required="required", rows=rows, placeholder=field.label.text) }}
+            {% else %}
+                {{ field(class="form-control", required="required", placeholder=field.label.text) }}
+            {% endif %}
+        {%- else -%}
+            {% if rows %}
+                {{ field(class="form-control", rows=rows, placeholder=field.label.text) }}
+            {% else %}
+                {{ field(class="form-control", placeholder=field.label.text) }}
+            {% endif %}
+        {%- endif -%}
+    </div>
+</div>
+{%- endmacro -%}
+
+
+{%- macro inline_field(field, label_text='', label_class='') -%}
 <div class="form-group {%- if field.errors %} has-error{%- endif %}">
     {{field.label(class="sr-only")}}
 
+    <div class="col-lg-4">
     {%- if kwargs['required'] or field.flags.required -%}
         {% if label_text %}
             {{field(class='form-control', placeholder=label_text, required="required", **kwargs)}}
@@ -23,6 +97,7 @@
     {%- elif field.description -%}
         <span class="help-block">{{field.description|safe}}</span>
     {%- endif %}
+    </div>
 </div>
 {%- endmacro -%}
 
@@ -64,60 +139,6 @@
 {%- endmacro -%}
 
 
-{# See https://gist.github.com/ligthyear/1284631 #}
-{%- macro form_field_label(field) -%}
-    <label for="{{ field.id }}">{{ field.label.text }}</label>
-{% endmacro %}
-
-
-{%- macro form_field_description(field) -%}
-    {% if field.description %}
-        <span class="help-block">{{ field.description }}</span>
-    {% endif %}
-{%- endmacro -%}
-
-
-{%- macro form_field_errors(field) -%}
-    {% if field.errors %}
-        {%- for error in field.errors -%}
-        <span class="label label-important">{{ error }}</span>
-        {%- endfor -%}
-    {% endif %}
-{%- endmacro -%}
-
-
-{%- macro form_field_boolean(field, inline=False) -%}
-    <label class="checkbox {%- if inline -%}inline{%- endif -%}">
-            {{ field(**kwargs) }}
-            {{ field.label.text }}
-            {{ form_field_description(field) }}
-            {{ form_field_errors(field) }}
-    </label>
-{%- endmacro -%}
-
-
-{%- macro form_field(field) -%}
-    {% if field.type == 'HiddenField' %}
-        {{ field() }}
-    {% else %}
-        {% if field.type == 'BooleanField' %}
-            {{ form_field_boolean(field, **kwargs) }}
-        {% else%}
-            {{ form_field_label(field) }}
-            <div class="input" id="{{field.id}}">
-            {% if field.type == 'RadioField' %}
-                {{ field(class='radio', **kwargs) }}
-            {% else %}
-                {{ field(**kwargs) }}
-            {% endif %}
-            {{ form_field_description(field) }}
-            {{ form_field_errors(field) }}
-            </div>
-        {% endif %}
-    {% endif %}
-{%- endmacro -%}
-
-
 {% macro topnav(endpoint, name, icon='', active=False) %}
 <li {% if endpoint == request.endpoint or active == True %}class="active"{% endif %}>
     <a href={{ url_for(endpoint) }}>

+ 1 - 2
flaskbb/user/forms.py

@@ -65,8 +65,7 @@ class ChangePasswordForm(Form):
 
 class ChangeUserDetailsForm(Form):
     birthday = DateField("Your Birthday", format="%d %m %Y",
-        widget=SelectDateWidget(), validators=[
-        Optional()])
+                         widget=SelectDateWidget(), validators=[Optional()])
 
     gender = SelectField("Gender", default="None", choices=[
         ("None", ""),

+ 20 - 2
flaskbb/user/models.py

@@ -21,8 +21,8 @@ from flaskbb.pms.models import PrivateMessage
 
 
 groups_users = db.Table('groups_users',
-        db.Column('user_id', db.Integer(), db.ForeignKey('users.id')),
-        db.Column('group_id', db.Integer(), db.ForeignKey('groups.id')))
+    db.Column('user_id', db.Integer(), db.ForeignKey('users.id')),
+    db.Column('group_id', db.Integer(), db.ForeignKey('groups.id')))
 
 
 class Group(db.Model):
@@ -44,6 +44,16 @@ class Group(db.Model):
     posttopic = db.Column(db.Boolean)
     postreply = db.Column(db.Boolean)
 
+    def save(self):
+        db.session.add(self)
+        db.session.commit()
+        return self
+
+    def delete(self):
+        db.session.delete(self)
+        db.session.commit()
+        return self
+
 
 class User(db.Model, UserMixin):
     __tablename__ = "users"
@@ -181,6 +191,14 @@ class User(db.Model, UserMixin):
             self.groups.append(group)
             return self
 
+    def remove_from_group(self, group):
+        """
+        Removes the user from the `group` if he is in it.
+        """
+        if self.in_group(group):
+            self.groups.pop(group)
+            return self
+
     def in_group(self, group):
         """
         Returns True if the user is in the specified group

+ 87 - 86
manage.py

@@ -57,7 +57,7 @@ def createall():
     """
 
     # Just for testing purposes
-    dbfile = os.path.join(BaseConfig._basedir, "flaskbb.sqlite")
+    dbfile = os.path.join(Config._basedir, "flaskbb.sqlite")
     if os.path.exists(dbfile):
         print "Removing old database file..."
         os.remove(dbfile)
@@ -66,95 +66,95 @@ def createall():
 
     groups = OrderedDict((
         ('Administrator', {
-             'description': 'The Administrator Group',
-             'admin': True,
-             'super_mod': False,
-             'mod': False,
-             'banned': False,
-             'guest': False,
-             'editpost': True,
-             'deletepost': True,
-             'deletetopic': True,
-             'posttopic': True,
-             'postreply': True,
-             'viewtopic': True,
-             'viewprofile': True
-             }),
+            'description': 'The Administrator Group',
+            'admin': True,
+            'super_mod': False,
+            'mod': False,
+            'banned': False,
+            'guest': False,
+            'editpost': True,
+            'deletepost': True,
+            'deletetopic': True,
+            'posttopic': True,
+            'postreply': True,
+            'viewtopic': True,
+            'viewprofile': True
+        }),
         ('Super Moderator', {
-             'description': 'The Super Moderator Group',
-             'admin': False,
-             'super_mod': True,
-             'mod': False,
-             'banned': False,
-             'guest': False,
-             'editpost': True,
-             'deletepost': True,
-             'deletetopic': True,
-             'posttopic': True,
-             'postreply': True,
-             'viewtopic': True,
-             'viewprofiles': True
-             }),
+            'description': 'The Super Moderator Group',
+            'admin': False,
+            'super_mod': True,
+            'mod': False,
+            'banned': False,
+            'guest': False,
+            'editpost': True,
+            'deletepost': True,
+            'deletetopic': True,
+            'posttopic': True,
+            'postreply': True,
+            'viewtopic': True,
+            'viewprofiles': True
+        }),
         ('Moderator', {
-             'description': 'The Moderator Group',
-             'admin': False,
-             'super_mod': False,
-             'mod': True,
-             'banned': False,
-             'guest': False,
-             'editpost': True,
-             'deletepost': True,
-             'deletetopic': True,
-             'posttopic': True,
-             'postreply': True,
-             'viewtopic': True,
-             'viewprofile': True
-             }),
+            'description': 'The Moderator Group',
+            'admin': False,
+            'super_mod': False,
+            'mod': True,
+            'banned': False,
+            'guest': False,
+            'editpost': True,
+            'deletepost': True,
+            'deletetopic': True,
+            'posttopic': True,
+            'postreply': True,
+            'viewtopic': True,
+            'viewprofile': True
+        }),
         ('Member', {
-             'description': 'The Member Group',
-             'admin': False,
-             'super_mod': False,
-             'mod': False,
-             'banned': False,
-             'guest': False,
-             'editpost': True,
-             'deletepost': False,
-             'deletetopic': False,
-             'posttopic': True,
-             'postreply': True,
-             'viewtopic': True,
-             'viewprofile': True
-             }),
+            'description': 'The Member Group',
+            'admin': False,
+            'super_mod': False,
+            'mod': False,
+            'banned': False,
+            'guest': False,
+            'editpost': True,
+            'deletepost': False,
+            'deletetopic': False,
+            'posttopic': True,
+            'postreply': True,
+            'viewtopic': True,
+            'viewprofile': True
+        }),
         ('Banned', {
-             'description': 'The Banned Group',
-             'admin': False,
-             'super_mod': False,
-             'mod': False,
-             'banned': True,
-             'guest': False,
-             'editpost': False,
-             'deletepost': False,
-             'deletetopic': False,
-             'posttopic': False,
-             'postreply': False,
-             'viewtopic': False,
-             'viewprofile': False
-             }),
+            'description': 'The Banned Group',
+            'admin': False,
+            'super_mod': False,
+            'mod': False,
+            'banned': True,
+            'guest': False,
+            'editpost': False,
+            'deletepost': False,
+            'deletetopic': False,
+            'posttopic': False,
+            'postreply': False,
+            'viewtopic': False,
+            'viewprofile': False
+        }),
         ('Guest', {
-             'description': 'The Guest Group',
-             'admin': False,
-             'super_mod': False,
-             'mod': False,
-             'banned': False,
-             'guest': True,
-             'editpost': False,
-             'deletepost': False,
-             'deletetopic': False,
-             'posttopic': False,
-             'postreply': False,
-             'viewtopic': False,
-             'viewprofile': False
-             })
+            'description': 'The Guest Group',
+            'admin': False,
+            'super_mod': False,
+            'mod': False,
+            'banned': False,
+            'guest': True,
+            'editpost': False,
+            'deletepost': False,
+            'deletetopic': False,
+            'posttopic': False,
+            'postreply': False,
+            'viewtopic': False,
+            'viewprofile': False
+        })
     ))
 
     # create 5 groups
@@ -180,7 +180,8 @@ def createall():
     # create 2 categories
     for i in range(1, 3):
         category_title = "Test Category %s" % i
-        category = Category(title=category_title, description="Test Description")
+        category = Category(title=category_title,
+                            description="Test Description")
         db.session.add(category)
 
         # create 2 forums in each category