Browse Source

Merged the "pms" module with the user module and named it "messages"

sh4nks 11 years ago
parent
commit
7603e1cca3

+ 3 - 0
.gitignore

@@ -36,5 +36,8 @@ __pycache__
 *.pyc
 *.pyc
 *.db
 *.db
 .venv
 .venv
+*.sublime-workspace
+*.sublime-project
+*.egg-info
 flaskbb/configs/production.py
 flaskbb/configs/production.py
 flaskbb/configs/development.py
 flaskbb/configs/development.py

+ 0 - 0
flaskbb/pms/__init__.py


+ 0 - 44
flaskbb/pms/forms.py

@@ -1,44 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
-    flaskbb.pms.forms
-    ~~~~~~~~~~~~~~~~~~~~
-
-    It provides the forms that are needed for the pm views.
-
-    :copyright: (c) 2013 by the FlaskBB Team.
-    :license: BSD, see LICENSE for more details.
-"""
-from flask.ext.wtf import Form
-from wtforms import TextField, TextAreaField, ValidationError
-from wtforms.validators import Required
-
-from flask.ext.login import current_user
-
-from flaskbb.user.models import User
-from flaskbb.pms.models import PrivateMessage
-
-
-class NewMessage(Form):
-    to_user = TextField("To User", validators=[
-        Required(message="A username is required.")])
-    subject = TextField("Subject", validators=[
-        Required(message="A subject is required.")])
-    message = TextAreaField("Message", validators=[
-        Required(message="A message is required.")])
-
-    def validate_to_user(self, field):
-        user = User.query.filter_by(username=field.data).first()
-        if not user:
-            raise ValidationError("The username you have entered doesn't exist")
-        if user.id == current_user.id:
-            raise ValidationError("You cannot send a PM to yourself.")
-
-    def save(self, from_user, to_user, user_id, unread, as_draft=False):
-        message = PrivateMessage(
-            subject=self.subject.data,
-            message=self.message.data,
-            unread=unread)
-
-        if as_draft:
-            return message.save(from_user, to_user, user_id, draft=True)
-        return message.save(from_user, to_user, user_id)

+ 0 - 57
flaskbb/pms/models.py

@@ -1,57 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
-    flaskbb.pms.models
-    ~~~~~~~~~~~~~~~~~~~~
-
-    This module provides the appropriate models for the pm views.
-
-    :copyright: (c) 2013 by the FlaskBB Team.
-    :license: BSD, see LICENSE for more details.
-"""
-from datetime import datetime
-
-from flaskbb.extensions import db
-
-
-class PrivateMessage(db.Model):
-    __tablename__ = "privatemessages"
-
-    id = db.Column(db.Integer, primary_key=True)
-    user_id = db.Column(db.Integer, db.ForeignKey("users.id"), nullable=False)
-    from_user_id = db.Column(db.Integer, db.ForeignKey("users.id"))
-    to_user_id = db.Column(db.Integer, db.ForeignKey("users.id"))
-    subject = db.Column(db.String)
-    message = db.Column(db.Text)
-    date_created = db.Column(db.DateTime, default=datetime.utcnow())
-    trash = db.Column(db.Boolean, nullable=False, default=False)
-    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])
-    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):
-        if self.id:
-            db.session.add(self)
-            db.session.commit()
-            return self
-
-        if draft:
-            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
-
-        db.session.add(self)
-        db.session.commit()
-        return self
-
-    def delete(self):
-        db.session.delete(self)
-        db.session.commit()
-        return self

+ 0 - 133
flaskbb/pms/views.py

@@ -1,133 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
-    flaskbb.pms.views
-    ~~~~~~~~~~~~~~~~~~~~
-
-    This module contains the views that provides the functionality
-    for creating and viewing private messages.
-
-    :copyright: (c) 2013 by the FlaskBB Team.
-    :license: BSD, see LICENSE for more details.
-"""
-from flask import Blueprint, render_template, flash, redirect, url_for, request
-from flask.ext.login import login_required, current_user
-
-from flaskbb.extensions import db
-from flaskbb.user.models import User
-from flaskbb.pms.forms import NewMessage
-from flaskbb.pms.models import PrivateMessage
-
-pms = Blueprint("pms", __name__)
-
-
-@pms.route("/")
-@pms.route("/inbox")
-@login_required
-def inbox():
-    messages = PrivateMessage.query.filter(
-        PrivateMessage.user_id == current_user.id,
-        PrivateMessage.draft == False,
-        PrivateMessage.trash == False,
-        db.not_(PrivateMessage.from_user_id == current_user.id)).all()
-    return render_template("pms/inbox.html", messages=messages)
-
-
-@pms.route("/message/<int:id>")
-@login_required
-def view_message(id):
-    message = PrivateMessage.query.filter_by(id=id).first()
-    if message.unread:
-        message.unread=False
-        db.session.commit()
-    return render_template("pms/view_message.html", message=message)
-
-
-@pms.route("/sent")
-@login_required
-def sent():
-    messages = PrivateMessage.query.filter(
-        PrivateMessage.user_id == current_user.id,
-        PrivateMessage.draft == False,
-        PrivateMessage.trash == False,
-        db.not_(PrivateMessage.to_user_id == current_user.id)).all()
-    return render_template("pms/sent.html", messages=messages)
-
-
-@pms.route("/trash")
-@login_required
-def trash():
-    messages = PrivateMessage.query.filter(
-        PrivateMessage.user_id == current_user.id,
-        PrivateMessage.trash == True).all()
-    return render_template("pms/trash.html", messages=messages)
-
-
-@pms.route("/draft")
-@login_required
-def drafts():
-    messages = PrivateMessage.query.filter(
-        PrivateMessage.user_id == current_user.id,
-        PrivateMessage.draft == True,
-        PrivateMessage.trash == False).all()
-    return render_template("pms/drafts.html", messages=messages)
-
-
-@pms.route("/new", methods=["POST", "GET"])
-@login_required
-def new_message():
-    form = NewMessage()
-    to_user = request.args.get("to_user")
-
-    if request.method == "POST":
-        if "save_message" in request.form:
-            to_user = User.query.filter_by(username=form.to_user.data).first()
-
-            form.save(from_user=current_user.id,
-                      to_user=to_user.id,
-                      user_id=current_user.id,
-                      unread=False,
-                      as_draft=True)
-
-            flash("Message saved!", "success")
-            return redirect(url_for("pms.drafts"))
-
-        if "send_message" in request.form and form.validate():
-            to_user = User.query.filter_by(username=form.to_user.data).first()
-
-            # 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,
-                      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,
-                      unread=True)
-
-            flash("Message sent!", "success")
-            return redirect(url_for("pms.sent"))
-    else:
-        form.to_user.data = to_user
-
-    return render_template("pms/new_message.html", form=form)
-
-
-@pms.route("/<int:id>/move")
-@login_required
-def move_message(id):
-    message = PrivateMessage.query.filter_by(id=id).first()
-    message.trash = True
-    message.save()
-    flash("Message moved to Trash!", "success")
-    return redirect(url_for("pms.inbox"))
-
-
-@pms.route("/<int:id>/delete")
-@login_required
-def delete_message(id):
-    message = PrivateMessage.query.filter_by(id=id).first()
-    message.delete()
-    flash("Message deleted!", "success")
-    return redirect(url_for("pms.inbox"))

+ 2 - 2
flaskbb/templates/layout.html

@@ -66,8 +66,8 @@
                                 <span class="glyphicon glyphicon-envelope"></span> <span class="badge">{{ current_user.pm_unread }}</span>
                                 <span class="glyphicon glyphicon-envelope"></span> <span class="badge">{{ current_user.pm_unread }}</span>
                             </button>
                             </button>
                             <ul class="dropdown-menu" role="menu">
                             <ul class="dropdown-menu" role="menu">
-                                <li><a href="{{ url_for('pms.inbox') }}"><span class="glyphicon glyphicon-envelope"></span> Inbox</a></li>
-                                <li><a href="{{ url_for('pms.new_message') }}"><span class="glyphicon glyphicon-pencil"></span> New Message</a></li>
+                                <li><a href="{{ url_for('user.inbox') }}"><span class="glyphicon glyphicon-envelope"></span> Inbox</a></li>
+                                <li><a href="{{ url_for('user.new_message') }}"><span class="glyphicon glyphicon-pencil"></span> New Message</a></li>
                             </ul>
                             </ul>
                         </div>
                         </div>
                     {% else %}
                     {% else %}

+ 5 - 5
flaskbb/templates/pms/drafts.html → flaskbb/templates/message/drafts.html

@@ -1,5 +1,5 @@
-{% extends "pms/pm_layout.html" %}
-{% block pm_content %}
+{% extends "message/message_layout.html" %}
+{% block message_content %}
 <table class="table table-bordered">
 <table class="table table-bordered">
     <thead>
     <thead>
         <tr>
         <tr>
@@ -13,10 +13,10 @@
         {% for message in messages %}
         {% for message in messages %}
         <tr>
         <tr>
             <td><a href="{{ url_for('user.profile', username=message.to_user.username) }}">{{ message.to_user.username }}</a></td>
             <td><a href="{{ url_for('user.profile', username=message.to_user.username) }}">{{ message.to_user.username }}</a></td>
-            <td><a href="{{ url_for('pms.view_message', id=message.id) }}">{% if message.subject %}{{ message.subject }}{% else %}(No Subject){% endif %}</a></td>
+            <td><a href="{{ url_for('user.view_message', id=message.id) }}">{% if message.subject %}{{ message.subject }}{% else %}(No Subject){% endif %}</a></td>
             <td>{{ message.date_created|format_date('%d %B %Y') }}</td>
             <td>{{ message.date_created|format_date('%d %B %Y') }}</td>
-            <td><a href="{{ url_for('pms.delete_message', id=message.id) }}">Delete</a> |
-                <a href="{{ url_for('pms.move_message', id=message.id) }}">Move</a></td>
+            <td><a href="{{ url_for('user.delete_message', id=message.id) }}">Delete</a> |
+                <a href="{{ url_for('user.move_message', id=message.id) }}">Move</a></td>
         </tr>
         </tr>
         {% endfor %}
         {% endfor %}
     </tbody>
     </tbody>

+ 5 - 5
flaskbb/templates/pms/inbox.html → flaskbb/templates/message/inbox.html

@@ -1,5 +1,5 @@
-{% extends "pms/pm_layout.html" %}
-{% block pm_content %}
+{% extends "message/message_layout.html" %}
+{% block message_content %}
 <table class="table table-bordered">
 <table class="table table-bordered">
     <thead>
     <thead>
         <tr>
         <tr>
@@ -13,10 +13,10 @@
         {% for message in messages %}
         {% for message in messages %}
         <tr>
         <tr>
             <td><a href="{{ url_for('user.profile', username=message.from_user.username) }}">{{ message.from_user.username }}</a></td>
             <td><a href="{{ url_for('user.profile', username=message.from_user.username) }}">{{ message.from_user.username }}</a></td>
-            <td><a href="{{ url_for('pms.view_message', id=message.id) }}">{{ message.subject }}</a></td>
+            <td><a href="{{ url_for('user.view_message', id=message.id) }}">{{ message.subject }}</a></td>
             <td>{{ message.date_created|time_since }}</td>
             <td>{{ message.date_created|time_since }}</td>
-            <td><a href="{{ url_for('pms.delete_message', id=message.id) }}">Delete</a> |
-                <a href="{{ url_for('pms.move_message', id=message.id) }}">Move</a></td>
+            <td><a href="{{ url_for('user.delete_message', id=message.id) }}">Delete</a> |
+                <a href="{{ url_for('user.move_message', id=message.id) }}">Move</a></td>
         </tr>
         </tr>
         {% endfor %}
         {% endfor %}
     </tbody>
     </tbody>

+ 6 - 6
flaskbb/templates/pms/pm_layout.html → flaskbb/templates/message/message_layout.html

@@ -12,16 +12,16 @@
     <div class="col-sm-3">
     <div class="col-sm-3">
         <div class="sidebar">
         <div class="sidebar">
             <ul class="nav sidenav">
             <ul class="nav sidenav">
-                <li class="nav-header">Options | <small><a href="{{ url_for('pms.new_message') }}">New PM</a></small></li>
-                {{ navlink('pms.inbox', 'Inbox') }}
-                {{ navlink('pms.sent', 'Sent') }}
-                {{ navlink('pms.drafts', 'Drafts') }}
-                {{ navlink('pms.trash', 'Trash') }}
+                <li class="nav-header">Options | <small><a href="{{ url_for('user.new_message') }}">New PM</a></small></li>
+                {{ navlink('user.inbox', 'Inbox') }}
+                {{ navlink('user.sent', 'Sent') }}
+                {{ navlink('user.drafts', 'Drafts') }}
+                {{ navlink('user.trash', 'Trash') }}
             </ul>
             </ul>
         </div><!--/.sidebar -->
         </div><!--/.sidebar -->
     </div><!--/.col-sm-3 -->
     </div><!--/.col-sm-3 -->
     <div class="col-sm-9">
     <div class="col-sm-9">
-        {% block pm_content %}{% endblock %}
+        {% block message_content %}{% endblock %}
     </div><!--/.col-sm-9 -->
     </div><!--/.col-sm-9 -->
 </div><!--/.row -->
 </div><!--/.row -->
 {% endblock %}
 {% endblock %}

+ 2 - 2
flaskbb/templates/pms/new_message.html → flaskbb/templates/message/new_message.html

@@ -1,5 +1,5 @@
-{% extends "pms/pm_layout.html" %}
-{% block pm_content %}
+{% extends "message/message_layout.html" %}
+{% block message_content %}
 {% import "macros.html" as wtf %}
 {% import "macros.html" as wtf %}
 <form class="form-horizontal" role="form" method="post" name="new">
 <form class="form-horizontal" role="form" method="post" name="new">
     <legend>Compose Message</legend>
     <legend>Compose Message</legend>

+ 5 - 5
flaskbb/templates/pms/sent.html → flaskbb/templates/message/sent.html

@@ -1,5 +1,5 @@
-{% extends "pms/pm_layout.html" %}
-{% block pm_content %}
+{% extends "message/message_layout.html" %}
+{% block message_content %}
 <table class="table table-bordered">
 <table class="table table-bordered">
     <thead>
     <thead>
         <tr>
         <tr>
@@ -13,10 +13,10 @@
         {% for message in messages %}
         {% for message in messages %}
         <tr>
         <tr>
             <td><a href="{{ url_for('user.profile', username=message.to_user.username) }}">{{ message.to_user.username }}</a></td>
             <td><a href="{{ url_for('user.profile', username=message.to_user.username) }}">{{ message.to_user.username }}</a></td>
-            <td><a href="{{ url_for('pms.view_message', id=message.id) }}">{{ message.subject }}</a></td>
+            <td><a href="{{ url_for('user.view_message', id=message.id) }}">{{ message.subject }}</a></td>
             <td>{{ message.date_created|time_since }}</td>
             <td>{{ message.date_created|time_since }}</td>
-            <td><a href="{{ url_for('pms.delete_message', id=message.id) }}">Delete</a> |
-                <a href="{{ url_for('pms.move_message', id=message.id) }}">Move</a></td>
+            <td><a href="{{ url_for('user.delete_message', id=message.id) }}">Delete</a> |
+                <a href="{{ url_for('user.move_message', id=message.id) }}">Move</a></td>
         </tr>
         </tr>
         {% endfor %}
         {% endfor %}
     </tbody>
     </tbody>

+ 4 - 4
flaskbb/templates/pms/trash.html → flaskbb/templates/message/trash.html

@@ -1,5 +1,5 @@
-{% extends "pms/pm_layout.html" %}
-{% block pm_content %}
+{% extends "message/message_layout.html" %}
+{% block message_content %}
 <table class="table table-bordered">
 <table class="table table-bordered">
     <thead>
     <thead>
         <tr>
         <tr>
@@ -13,9 +13,9 @@
         {% for message in messages %}
         {% for message in messages %}
         <tr>
         <tr>
             <td>{% if message.to_user %}<a href="{{ url_for('user.profile', username=message.to_user.username) }}">{{ message.to_user.username }}</a>{% else %} (No User) {% endif %}</td>
             <td>{% if message.to_user %}<a href="{{ url_for('user.profile', username=message.to_user.username) }}">{{ message.to_user.username }}</a>{% else %} (No User) {% endif %}</td>
-            <td><a href="{{ url_for('pms.view_message', id=message.id) }}">{% if message.subject %}{{ message.subject }}{% else %}(No Subject){% endif %}</a></td>
+            <td><a href="{{ url_for('user.view_message', id=message.id) }}">{% if message.subject %}{{ message.subject }}{% else %}(No Subject){% endif %}</a></td>
             <td>{{ message.date_created|format_date }}</td>
             <td>{{ message.date_created|format_date }}</td>
-            <td><a href="{{ url_for('pms.delete_message', id=message.id) }}">Delete</a></td>
+            <td><a href="{{ url_for('user.delete_message', id=message.id) }}">Delete</a></td>
         </tr>
         </tr>
         {% endfor %}
         {% endfor %}
     </tbody>
     </tbody>

+ 2 - 2
flaskbb/templates/pms/view_message.html → flaskbb/templates/message/view_message.html

@@ -1,5 +1,5 @@
-{% extends "pms/pm_layout.html" %}
-{% block pm_content %}
+{% extends "message/message_layout.html" %}
+{% block message_content %}
 <table class="table table-bordered">
 <table class="table table-bordered">
     <tbody>
     <tbody>
         <tr>
         <tr>

+ 29 - 1
flaskbb/user/forms.py

@@ -8,16 +8,18 @@
     :copyright: (c) 2013 by the FlaskBB Team.
     :copyright: (c) 2013 by the FlaskBB Team.
     :license: BSD, see LICENSE for more details.
     :license: BSD, see LICENSE for more details.
 """
 """
+from flask.ext.login import current_user
 from flask.ext.wtf import Form
 from flask.ext.wtf import Form
 from wtforms import (TextField, PasswordField, DateField, TextAreaField,
 from wtforms import (TextField, PasswordField, DateField, TextAreaField,
                      SelectField, ValidationError)
                      SelectField, ValidationError)
 from wtforms.validators import (Length, Required, Email, EqualTo, regexp,
 from wtforms.validators import (Length, Required, Email, EqualTo, regexp,
                                 Optional, URL)
                                 Optional, URL)
 
 
-from flaskbb.user.models import User
+from flaskbb.user.models import User, PrivateMessage
 from flaskbb.extensions import db
 from flaskbb.extensions import db
 from flaskbb.helpers import SelectDateWidget
 from flaskbb.helpers import SelectDateWidget
 
 
+
 IMG_RE = r'^[^/\\]\.(?:jpg|gif|png)'
 IMG_RE = r'^[^/\\]\.(?:jpg|gif|png)'
 
 
 is_image = regexp(IMG_RE,
 is_image = regexp(IMG_RE,
@@ -86,3 +88,29 @@ class ChangeUserDetailsForm(Form):
 
 
     notes = TextAreaField("Notes", validators=[
     notes = TextAreaField("Notes", validators=[
         Optional(), Length(min=0, max=5000)])
         Optional(), Length(min=0, max=5000)])
+
+
+class NewMessage(Form):
+    to_user = TextField("To User", validators=[
+        Required(message="A username is required.")])
+    subject = TextField("Subject", validators=[
+        Required(message="A subject is required.")])
+    message = TextAreaField("Message", validators=[
+        Required(message="A message is required.")])
+
+    def validate_to_user(self, field):
+        user = User.query.filter_by(username=field.data).first()
+        if not user:
+            raise ValidationError("The username you have entered doesn't exist")
+        if user.id == current_user.id:
+            raise ValidationError("You cannot send a PM to yourself.")
+
+    def save(self, from_user, to_user, user_id, unread, as_draft=False):
+        message = PrivateMessage(
+            subject=self.subject.data,
+            message=self.message.data,
+            unread=unread)
+
+        if as_draft:
+            return message.save(from_user, to_user, user_id, draft=True)
+        return message.save(from_user, to_user, user_id)

+ 49 - 21
flaskbb/user/models.py

@@ -20,7 +20,8 @@ from flaskbb.extensions import db, cache
 from flaskbb.forum.models import Post, Topic, topictracker
 from flaskbb.forum.models import Post, Topic, topictracker
 
 
 
 
-groups_users = db.Table('groups_users',
+groups_users = db.Table(
+    'groups_users',
     db.Column('user_id', db.Integer(), db.ForeignKey('users.id')),
     db.Column('user_id', db.Integer(), db.ForeignKey('users.id')),
     db.Column('group_id', db.Integer(), db.ForeignKey('groups.id')))
     db.Column('group_id', db.Integer(), db.ForeignKey('groups.id')))
 
 
@@ -84,6 +85,8 @@ class User(db.Model, UserMixin):
     posts = db.relationship("Post", backref="user", lazy="dynamic")
     posts = db.relationship("Post", backref="user", lazy="dynamic")
     topics = db.relationship("Topic", backref="user", lazy="dynamic")
     topics = db.relationship("Topic", backref="user", lazy="dynamic")
 
 
+    post_count = db.Column(db.Integer, default=0)
+
     primary_group_id = db.Column(db.Integer, db.ForeignKey('groups.id'))
     primary_group_id = db.Column(db.Integer, db.ForeignKey('groups.id'))
 
 
     primary_group = db.relationship('Group', lazy="joined",
     primary_group = db.relationship('Group', lazy="joined",
@@ -103,25 +106,6 @@ class User(db.Model, UserMixin):
                         backref=db.backref("topicstracked", lazy="dynamic"),
                         backref=db.backref("topicstracked", lazy="dynamic"),
                         lazy="dynamic")
                         lazy="dynamic")
 
 
-    # Properties
-    @property
-    def post_count(self):
-        """
-        Property interface for get_post_count method.
-
-        Method seperate for easy invalidation of cache.
-        """
-        return self.get_post_count()
-
-    @property
-    def last_post(self):
-        """
-        Property interface for get_last_post method.
-
-        Method seperate for easy invalidation of cache.
-        """
-        return self.get_last_post()
-
     # Methods
     # Methods
     def __repr__(self):
     def __repr__(self):
         """
         """
@@ -253,7 +237,7 @@ class User(db.Model, UserMixin):
         return self.secondary_groups.filter(
         return self.secondary_groups.filter(
             groups_users.c.group_id == group.id).count() > 0
             groups_users.c.group_id == group.id).count() > 0
 
 
-    @cache.memoize(60*5)
+    @cache.memoize(timeout=sys.maxint)
     def get_permissions(self, exclude=None):
     def get_permissions(self, exclude=None):
         """
         """
         Returns a dictionary with all the permissions the user has.
         Returns a dictionary with all the permissions the user has.
@@ -346,3 +330,47 @@ class Guest(AnonymousUserMixin):
                 continue
                 continue
             perms[c.name] = getattr(group, c.name)
             perms[c.name] = getattr(group, c.name)
         return perms
         return perms
+
+
+class PrivateMessage(db.Model):
+    __tablename__ = "privatemessages"
+
+    id = db.Column(db.Integer, primary_key=True)
+    user_id = db.Column(db.Integer, db.ForeignKey("users.id"), nullable=False)
+    from_user_id = db.Column(db.Integer, db.ForeignKey("users.id"))
+    to_user_id = db.Column(db.Integer, db.ForeignKey("users.id"))
+    subject = db.Column(db.String)
+    message = db.Column(db.Text)
+    date_created = db.Column(db.DateTime, default=datetime.utcnow())
+    trash = db.Column(db.Boolean, nullable=False, default=False)
+    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])
+    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):
+        if self.id:
+            db.session.add(self)
+            db.session.commit()
+            return self
+
+        if draft:
+            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
+
+        db.session.add(self)
+        db.session.commit()
+        return self
+
+    def delete(self):
+        db.session.delete(self)
+        db.session.commit()
+        return self

+ 120 - 5
flaskbb/user/views.py

@@ -10,12 +10,14 @@
     :license: BSD, see LICENSE for more details.
     :license: BSD, see LICENSE for more details.
 """
 """
 from datetime import datetime
 from datetime import datetime
-from flask import Blueprint, render_template, flash, request
+from flask import Blueprint, render_template, flash, request, redirect, url_for
 from flask.ext.login import login_required, current_user
 from flask.ext.login import login_required, current_user
 
 
-from flaskbb.user.models import User
+from flaskbb.extensions import db
+from flaskbb.user.models import User, PrivateMessage
 from flaskbb.user.forms import (ChangePasswordForm, ChangeEmailForm,
 from flaskbb.user.forms import (ChangePasswordForm, ChangeEmailForm,
-                                ChangeUserDetailsForm)
+                                ChangeUserDetailsForm, NewMessage)
+
 
 
 user = Blueprint("user", __name__)
 user = Blueprint("user", __name__)
 
 
@@ -34,7 +36,7 @@ def profile(username):
                            posts_per_day=posts_per_day)
                            posts_per_day=posts_per_day)
 
 
 
 
-@user.route("/<username>/all_topics")
+@user.route("/<username>/topics")
 def view_all_topics(username):
 def view_all_topics(username):
     page = request.args.get("page", 1, type=int)
     page = request.args.get("page", 1, type=int)
     user = User.query.filter_by(username=username).first_or_404()
     user = User.query.filter_by(username=username).first_or_404()
@@ -42,7 +44,7 @@ def view_all_topics(username):
     return render_template("user/all_topics.html", user=user, topics=topics)
     return render_template("user/all_topics.html", user=user, topics=topics)
 
 
 
 
-@user.route("/<username>/all_posts")
+@user.route("/<username>/posts")
 def view_all_posts(username):
 def view_all_posts(username):
     page = request.args.get("page", 1, type=int)
     page = request.args.get("page", 1, type=int)
     user = User.query.filter_by(username=username).first_or_404()
     user = User.query.filter_by(username=username).first_or_404()
@@ -99,3 +101,116 @@ def change_user_details():
         form.notes.data = current_user.notes
         form.notes.data = current_user.notes
 
 
     return render_template("user/change_user_details.html", form=form)
     return render_template("user/change_user_details.html", form=form)
+
+
+@user.route("/messages")
+@user.route("/messages/inbox")
+@login_required
+def inbox():
+    messages = PrivateMessage.query.filter(
+        PrivateMessage.user_id == current_user.id,
+        PrivateMessage.draft == False,
+        PrivateMessage.trash == False,
+        db.not_(PrivateMessage.from_user_id == current_user.id)).all()
+    return render_template("message/inbox.html", messages=messages)
+
+
+@user.route("/messages/<int:id>/view")
+@login_required
+def view_message(id):
+    message = PrivateMessage.query.filter_by(id=id).first()
+    if message.unread:
+        message.unread=False
+        db.session.commit()
+    return render_template("message/view_message.html", message=message)
+
+
+@user.route("/messages/sent")
+@login_required
+def sent():
+    messages = PrivateMessage.query.filter(
+        PrivateMessage.user_id == current_user.id,
+        PrivateMessage.draft == False,
+        PrivateMessage.trash == False,
+        db.not_(PrivateMessage.to_user_id == current_user.id)).all()
+    return render_template("message/sent.html", messages=messages)
+
+
+@user.route("/messages/trash")
+@login_required
+def trash():
+    messages = PrivateMessage.query.filter(
+        PrivateMessage.user_id == current_user.id,
+        PrivateMessage.trash == True).all()
+    return render_template("message/trash.html", messages=messages)
+
+
+@user.route("/messages/draft")
+@login_required
+def drafts():
+    messages = PrivateMessage.query.filter(
+        PrivateMessage.user_id == current_user.id,
+        PrivateMessage.draft == True,
+        PrivateMessage.trash == False).all()
+    return render_template("message/drafts.html", messages=messages)
+
+
+@user.route("/messages/new", methods=["POST", "GET"])
+@login_required
+def new_message():
+    form = NewMessage()
+    to_user = request.args.get("to_user")
+
+    if request.method == "POST":
+        if "save_message" in request.form:
+            to_user = User.query.filter_by(username=form.to_user.data).first()
+
+            form.save(from_user=current_user.id,
+                      to_user=to_user.id,
+                      user_id=current_user.id,
+                      unread=False,
+                      as_draft=True)
+
+            flash("Message saved!", "success")
+            return redirect(url_for("user.drafts"))
+
+        if "send_message" in request.form and form.validate():
+            to_user = User.query.filter_by(username=form.to_user.data).first()
+
+            # 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,
+                      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,
+                      unread=True)
+
+            flash("Message sent!", "success")
+            return redirect(url_for("user.sent"))
+    else:
+        form.to_user.data = to_user
+
+    return render_template("message/new_message.html", form=form)
+
+
+@user.route("/messages/<int:id>/move")
+@login_required
+def move_message(id):
+    message = PrivateMessage.query.filter_by(id=id).first()
+    message.trash = True
+    message.save()
+    flash("Message moved to Trash!", "success")
+    return redirect(url_for("user.inbox"))
+
+
+@user.route("/messages/<int:id>/delete")
+@login_required
+def delete_message(id):
+    message = PrivateMessage.query.filter_by(id=id).first()
+    message.delete()
+    flash("Message deleted!", "success")
+    return redirect(url_for("user.inbox"))