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

用户关注用户,增加私信功能,实现部分消息通知

消息通知 content::json,增加is_read字段

notice: reply,user,collect,like
honmaple 9 лет назад
Родитель
Сommit
c70b6276be

+ 5 - 3
maple/__init__.py

@@ -6,7 +6,7 @@
 # Author: jianglin
 # Author: jianglin
 # Email: xiyang0807@gmail.com
 # Email: xiyang0807@gmail.com
 # Created: 2016-05-20 12:35:52 (CST)
 # Created: 2016-05-20 12:35:52 (CST)
-# Last Update:星期一 2016-6-27 15:0:21 (CST)
+# Last Update:星期三 2016-6-29 15:22:56 (CST)
 #          By:jianglin
 #          By:jianglin
 # Description:
 # Description:
 # **************************************************************************
 # **************************************************************************
@@ -61,8 +61,10 @@ def register_routes(app):
     app.register_blueprint(site, url_prefix='/t')
     app.register_blueprint(site, url_prefix='/t')
     from maple.docs.views import site
     from maple.docs.views import site
     app.register_blueprint(site, subdomain='docs')
     app.register_blueprint(site, subdomain='docs')
-    import maple.auth.views
-    import maple.admin.admin
+    # import maple.auth.views
+    from maple.auth import views
+    from maple.admin import admin
+    # import maple.admin.admin
 
 
 
 
 app = create_app()
 app = create_app()

+ 3 - 2
maple/extensions.py

@@ -6,7 +6,7 @@
 # Author: jianglin
 # Author: jianglin
 # Email: xiyang0807@gmail.com
 # Email: xiyang0807@gmail.com
 # Created: 2016-05-20 13:02:50 (CST)
 # Created: 2016-05-20 13:02:50 (CST)
-# Last Update:星期一 2016-6-27 17:17:11 (CST)
+# Last Update:星期四 2016-6-30 20:54:16 (CST)
 #          By:
 #          By:
 # Description:
 # Description:
 # **************************************************************************
 # **************************************************************************
@@ -67,7 +67,7 @@ def register_babel(app):
 def register_maple(app):
 def register_maple(app):
     Bootstrap(app,
     Bootstrap(app,
               css=('styles/monokai.css', 'styles/mine.css'),
               css=('styles/monokai.css', 'styles/mine.css'),
-              js=('styles/upload.js', 'styles/order.js', 'styles/mine.js',
+              js=('styles/upload.js', 'styles/forums.js', 'styles/mine.js',
                   'styles/topic.js'),
                   'styles/topic.js'),
               use_auth=True)
               use_auth=True)
     Captcha(app)
     Captcha(app)
@@ -130,3 +130,4 @@ def register_jinja2(app):
     app.jinja_env.filters['markdown'] = Filters.safe_markdown
     app.jinja_env.filters['markdown'] = Filters.safe_markdown
     app.jinja_env.filters['safe_clean'] = safe_clean
     app.jinja_env.filters['safe_clean'] = safe_clean
     app.jinja_env.filters['is_collected'] = Filters.is_collected
     app.jinja_env.filters['is_collected'] = Filters.is_collected
+    app.jinja_env.filters['notice_count'] = Filters.notice_count

+ 6 - 1
maple/filters.py

@@ -6,7 +6,7 @@
 # Author: jianglin
 # Author: jianglin
 # Email: xiyang0807@gmail.com
 # Email: xiyang0807@gmail.com
 # Created: 2016-06-15 00:39:29 (CST)
 # Created: 2016-06-15 00:39:29 (CST)
-# Last Update:星期一 2016-6-27 12:33:39 (CST)
+# Last Update:星期四 2016-6-30 20:53:41 (CST)
 #          By:
 #          By:
 # Description:
 # Description:
 # **************************************************************************
 # **************************************************************************
@@ -100,6 +100,11 @@ class Filters(object):
                 return True
                 return True
         return False
         return False
 
 
+    def notice_count(uid):
+        from maple.forums.models import Notice
+        count = Notice.query.filter_by(rece_id=uid, is_read=False).count()
+        return count
+
     class Title(object):
     class Title(object):
         title = setting['title']
         title = setting['title']
         picture = setting['picture']
         picture = setting['picture']

+ 67 - 0
maple/forums/controls.py

@@ -0,0 +1,67 @@
+#!/usr/bin/env python
+# -*- coding=UTF-8 -*-
+# **************************************************************************
+# Copyright © 2016 jianglin
+# File Name: controls.py
+# Author: jianglin
+# Email: xiyang0807@gmail.com
+# Created: 2016-06-30 19:39:13 (CST)
+# Last Update:星期四 2016-6-30 20:34:48 (CST)
+#          By:
+# Description:
+# **************************************************************************
+from flask import url_for
+from flask_login import current_user
+from .models import db
+from .models import Notice
+
+
+def reply(topic, reply):
+    url = url_for('topic.topic',
+                  uid=topic.uid,
+                  _anchor='reply-' + str(reply.id))
+    notice = Notice()
+    notice.category = 'reply'
+    notice.content = {'url': url,
+                      'content': reply.content[:100],
+                      'title': topic.title}
+    notice.rece_id = topic.author_id
+    notice.send_user = current_user
+    db.session.add(notice)
+    db.session.commit()
+
+
+def collect(topic):
+    url = url_for('topic.topic', uid=topic.uid)
+    notice = Notice()
+    notice.category = 'collect'
+    notice.content = {'url': url, 'title': topic.title}
+    notice.rece_id = topic.author_id
+    notice.send_user = current_user
+    db.session.add(notice)
+    db.session.commit()
+
+
+def like(reply):
+    topic = reply.topic
+    url = url_for('topic.topic',
+                  uid=topic.uid,
+                  _anchor='reply-' + str(reply.id))
+    notice = Notice()
+    notice.category = 'like'
+    notice.content = {'url': url,
+                      'title': topic.title,
+                      'content': reply.content[:100]}
+    notice.rece_id = reply.author_id
+    notice.send_user = current_user
+    db.session.add(notice)
+    db.session.commit()
+
+
+def user(userId):
+    notice = Notice()
+    notice.category = 'user'
+    notice.rece_id = userId
+    notice.send_user = current_user
+    db.session.add(notice)
+    db.session.commit()

+ 6 - 2
maple/forums/forms.py

@@ -6,12 +6,12 @@
 # Author: jianglin
 # Author: jianglin
 # Email: xiyang0807@gmail.com
 # Email: xiyang0807@gmail.com
 # Created: 2016-06-03 19:27:58 (CST)
 # Created: 2016-06-03 19:27:58 (CST)
-# Last Update:星期一 2016-6-27 14:59:44 (CST)
+# Last Update:星期二 2016-6-28 21:52:3 (CST)
 #          By:
 #          By:
 # Description:
 # Description:
 # **************************************************************************
 # **************************************************************************
 from flask_wtf import Form
 from flask_wtf import Form
-from wtforms import SelectField, StringField
+from wtforms import SelectField, StringField, TextAreaField
 from wtforms.validators import DataRequired
 from wtforms.validators import DataRequired
 from flask_babel import lazy_gettext as _
 from flask_babel import lazy_gettext as _
 
 
@@ -32,3 +32,7 @@ class SortForm(Form):
 
 
 class SearchForm(Form):
 class SearchForm(Form):
     search = StringField(_('search'), validators=[DataRequired()])
     search = StringField(_('search'), validators=[DataRequired()])
+
+
+class MessageForm(Form):
+    message = TextAreaField(_('message'), validators=[DataRequired()])

+ 8 - 12
maple/forums/models.py

@@ -6,12 +6,15 @@
 # Author: jianglin
 # Author: jianglin
 # Email: xiyang0807@gmail.com
 # Email: xiyang0807@gmail.com
 # Created: 2016-05-20 13:24:19 (CST)
 # Created: 2016-05-20 13:24:19 (CST)
-# Last Update:星期二 2016-6-28 1:4:23 (CST)
+# Last Update:星期四 2016-6-30 21:28:54 (CST)
 #          By:
 #          By:
 # Description:
 # Description:
 # **************************************************************************
 # **************************************************************************
 from maple import db
 from maple import db
 from datetime import datetime
 from datetime import datetime
+from sqlalchemy.dialects.postgresql import JSON
+
+# from sqlalchemy.types import JSON
 
 
 
 
 class Board(db.Model):
 class Board(db.Model):
@@ -76,26 +79,19 @@ class Notice(db.Model):
     id = db.Column(db.Integer, primary_key=True)
     id = db.Column(db.Integer, primary_key=True)
     publish = db.Column(db.DateTime, default=datetime.now())
     publish = db.Column(db.DateTime, default=datetime.now())
     category = db.Column(db.String(81), nullable=False)
     category = db.Column(db.String(81), nullable=False)
-    content = db.Column(db.Text)
+    content = db.Column(JSON)
+    is_read = db.Column(db.Boolean, default=False)
 
 
-    rece_id = db.Column(db.Integer,
-                        db.ForeignKey('users.id',
-                                      ondelete="CASCADE"))
+    rece_id = db.Column(db.Integer, db.ForeignKey('users.id'))
     rece_user = db.relationship("User",
     rece_user = db.relationship("User",
                                 backref="rece_user",
                                 backref="rece_user",
                                 foreign_keys='Notice.rece_id',
                                 foreign_keys='Notice.rece_id',
-                                cascade='all,delete-orphan',
-                                single_parent=True,
                                 uselist=False)
                                 uselist=False)
 
 
-    send_id = db.Column(db.Integer,
-                        db.ForeignKey('users.id',
-                                      ondelete="CASCADE"))
+    send_id = db.Column(db.Integer, db.ForeignKey('users.id'))
     send_user = db.relationship("User",
     send_user = db.relationship("User",
                                 backref="send_user",
                                 backref="send_user",
                                 foreign_keys='Notice.send_id',
                                 foreign_keys='Notice.send_id',
-                                cascade='all,delete-orphan',
-                                single_parent=True,
                                 uselist=False)
                                 uselist=False)
 
 
     def __repr__(self):
     def __repr__(self):

+ 33 - 8
maple/forums/views.py

@@ -6,17 +6,20 @@
 # Author: jianglin
 # Author: jianglin
 # Email: xiyang0807@gmail.com
 # Email: xiyang0807@gmail.com
 # Created: 2016-05-20 13:18:19 (CST)
 # Created: 2016-05-20 13:18:19 (CST)
-# Last Update:星期六 2016-6-25 18:4:14 (CST)
+# Last Update:星期四 2016-6-30 20:32:38 (CST)
 #          By:
 #          By:
 # Description:
 # Description:
 # **************************************************************************
 # **************************************************************************
-from flask import Blueprint, render_template, g, request, abort
+from flask import (Blueprint, render_template, g, request, abort, redirect,
+                   flash, url_for)
 from flask_login import current_user, login_required
 from flask_login import current_user, login_required
+from flask_maple.forms import flash_errors
 from maple import app, db
 from maple import app, db
 from maple.helpers import is_num
 from maple.helpers import is_num
 from maple.user.models import User
 from maple.user.models import User
 from maple.forums.models import Notice, Board
 from maple.forums.models import Notice, Board
 from maple.topic.models import Topic
 from maple.topic.models import Topic
+from .forms import MessageForm
 
 
 site = Blueprint('forums', __name__)
 site = Blueprint('forums', __name__)
 
 
@@ -43,15 +46,16 @@ def forums():
     return render_template('forums/forums.html', **data)
     return render_template('forums/forums.html', **data)
 
 
 
 
-@site.route('/notices', defaults={'page': 1})
-@site.route('/notices/?page=<int:page>')
+@site.route('/notices')
 @login_required
 @login_required
-def notice(page):
-    notices = Notice.query.join(Notice.rece_user).filter(
-        User.username == current_user.username).paginate(
+def notice():
+    page = is_num(request.args.get('page'))
+    notices = Notice.query.filter_by(
+        rece_id=current_user.id).order_by(Notice.publish.desc()).paginate(
             page, app.config['PER_PAGE'],
             page, app.config['PER_PAGE'],
             error_out=True)
             error_out=True)
-    return render_template('forums/notice.html', notices=notices)
+    data = {'notices': notices}
+    return render_template('forums/notice.html', **data)
 
 
 
 
 @site.route('/userlist')
 @site.route('/userlist')
@@ -63,6 +67,27 @@ def userlist():
     return render_template('forums/userlist.html', **data)
     return render_template('forums/userlist.html', **data)
 
 
 
 
+@site.route('/messages/<int:receId>', methods=['POST'])
+@login_required
+def message(receId):
+    form = MessageForm()
+    rece_user = User.query.filter_by(id=receId).first_or_404()
+    if form.validate_on_submit() and request.method == "POST":
+        message = Notice()
+        message.category = 'privacy'
+        message.content = form.message.data
+        message.rece_user = rece_user
+        message.send_id = current_user.id
+        db.session.add(message)
+        db.session.commit()
+        flash('成功发送', category='success')
+        return redirect(url_for('user.user', user_url=rece_user.username))
+    else:
+        if form.errors:
+            flash_errors(form)
+    return redirect(url_for('user.user', user_url=rece_user.username))
+
+
 @site.route('/about')
 @site.route('/about')
 def about():
 def about():
     return render_template('forums/about.html')
     return render_template('forums/about.html')

+ 33 - 2
maple/mine/controls.py

@@ -6,14 +6,40 @@
 # Author: jianglin
 # Author: jianglin
 # Email: xiyang0807@gmail.com
 # Email: xiyang0807@gmail.com
 # Created: 2016-06-15 09:44:01 (CST)
 # Created: 2016-06-15 09:44:01 (CST)
-# Last Update:星期三 2016-6-15 13:11:34 (CST)
+# Last Update:星期四 2016-6-30 20:43:59 (CST)
 #          By:
 #          By:
 # Description:
 # Description:
 # **************************************************************************
 # **************************************************************************
+from flask import flash
 from flask_login import current_user
 from flask_login import current_user
 from maple import db
 from maple import db
 from maple.topic.models import Collect, Topic, Tags, Reply
 from maple.topic.models import Collect, Topic, Tags, Reply
 from maple.user.models import User
 from maple.user.models import User
+from maple.forums.controls import collect as notice_collect
+from maple.forums.controls import like as notice_like
+from maple.forums.controls import user as notice_user
+
+
+class CollectDetail(object):
+    def post(form, topicId):
+        topic = Topic.query.filter_by(uid=topicId).first_or_404()
+        for id in form:
+            collect = Collect.query.filter_by(id=id).first_or_404()
+            if topic in collect.topics:
+                flash('This topic has been collected in %s' % collect.name,
+                      'warning')
+            else:
+                collect.topics.append(topic)
+                db.session.commit()
+                if topic.author_id != current_user.id:
+                    notice_collect(topic)
+        return topic
+
+    def delete(topicId, collectId):
+        topic = Topic.query.filter_by(uid=topicId).first_or_404()
+        collect = Collect.query.filter_by(id=collectId).first_or_404()
+        collect.topics.remove(topic)
+        db.session.commit()
 
 
 
 
 class CollectModel(object):
 class CollectModel(object):
@@ -54,6 +80,7 @@ class FollowModel(object):
             user = User.query.filter_by(id=id).first()
             user = User.query.filter_by(id=id).first()
             current_user.following_users.append(user)
             current_user.following_users.append(user)
             db.session.commit()
             db.session.commit()
+            notice_user(user.id)
         elif type == 'collect':
         elif type == 'collect':
             collect = Collect.query.filter_by(id=id).first()
             collect = Collect.query.filter_by(id=id).first()
             current_user.following_collects.append(collect)
             current_user.following_collects.append(collect)
@@ -69,7 +96,9 @@ class FollowModel(object):
             current_user.following_topics.remove(topic)
             current_user.following_topics.remove(topic)
             db.session.commit()
             db.session.commit()
         elif type == 'user':
         elif type == 'user':
-            pass
+            user = User.query.filter_by(id=id).first()
+            current_user.following_users.remove(user)
+            db.session.commit()
         elif type == 'collect':
         elif type == 'collect':
             collect = Collect.query.filter_by(id=id).first()
             collect = Collect.query.filter_by(id=id).first()
             current_user.following_collects.remove(collect)
             current_user.following_collects.remove(collect)
@@ -81,6 +110,8 @@ class LikeModel(object):
         reply = Reply.query.filter_by(id=uid).first_or_404()
         reply = Reply.query.filter_by(id=uid).first_or_404()
         current_user.likes.append(reply)
         current_user.likes.append(reply)
         db.session.commit()
         db.session.commit()
+        if reply.author_id != current_user.id:
+            notice_like(reply)
 
 
     def delete_data(uid):
     def delete_data(uid):
         reply = Reply.query.filter_by(id=uid).first_or_404()
         reply = Reply.query.filter_by(id=uid).first_or_404()

+ 4 - 15
maple/mine/views.py

@@ -6,7 +6,7 @@
 # Author: jianglin
 # Author: jianglin
 # Email: xiyang0807@gmail.com
 # Email: xiyang0807@gmail.com
 # Created: 2016-05-20 18:04:43 (CST)
 # Created: 2016-05-20 18:04:43 (CST)
-# Last Update:星期一 2016-6-27 12:52:4 (CST)
+# Last Update:星期四 2016-6-30 20:0:46 (CST)
 #          By:
 #          By:
 # Description:
 # Description:
 # **************************************************************************
 # **************************************************************************
@@ -22,7 +22,7 @@ from maple.main.permission import (follow_permission, collect_permission,
 from maple.helpers import is_num
 from maple.helpers import is_num
 from maple.topic.models import Topic, Collect
 from maple.topic.models import Topic, Collect
 from maple.mine.forms import CollectForm
 from maple.mine.forms import CollectForm
-from .controls import CollectModel, FollowModel, LikeModel
+from .controls import CollectModel, FollowModel, LikeModel, CollectDetail
 
 
 site = Blueprint('mine', __name__)
 site = Blueprint('mine', __name__)
 
 
@@ -95,15 +95,7 @@ def collect_following():
 def add_collect():
 def add_collect():
     form = request.form.getlist('add-to-collect')
     form = request.form.getlist('add-to-collect')
     topicId = request.args.get('topicId')
     topicId = request.args.get('topicId')
-    topic = Topic.query.filter_by(uid=topicId).first_or_404()
-    for id in form:
-        collect = Collect.query.filter_by(id=id).first_or_404()
-        if topic in collect.topics:
-            flash('This topic has been collected in %s' % collect.name,
-                  'warning')
-        else:
-            collect.topics.append(topic)
-            db.session.commit()
+    topic = CollectDetail.post(form, topicId)
     return redirect(url_for('topic.topic', uid=topic.uid))
     return redirect(url_for('topic.topic', uid=topic.uid))
 
 
 
 
@@ -113,10 +105,7 @@ def delete_collect():
     data = request.get_json()
     data = request.get_json()
     topicId = data['topicId']
     topicId = data['topicId']
     collectId = data['collectId']
     collectId = data['collectId']
-    topic = Topic.query.filter_by(uid=topicId).first_or_404()
-    collect = Collect.query.filter_by(id=collectId).first_or_404()
-    collect.topics.remove(topic)
-    db.session.commit()
+    CollectDetail.delete(topicId, collectId)
     return jsonify(judge=True)
     return jsonify(judge=True)
 
 
 
 

+ 1 - 1
maple/static/assets/home.js

@@ -25,7 +25,7 @@ else
 {$.ajax({type:"POST",url:"/user/follow",data:data,contentType:'application/json;charset=UTF-8',success:function(result){if(result.judge===true)
 {$.ajax({type:"POST",url:"/user/follow",data:data,contentType:'application/json;charset=UTF-8',success:function(result){if(result.judge===true)
 {obj.text('取消关注').addClass('active');}else
 {obj.text('取消关注').addClass('active');}else
 {alert('asd');}}});}}
 {alert('asd');}}});}}
-$(document).ready(function(){$('button.tagfollow').click(function(){var _$this=$(this);var data=JSON.stringify({id:_$this.attr("id"),type:'tag'});Follow(_$this,data);});$('button.topicfollow').click(function(){var _$this=$(this);var data=JSON.stringify({id:_$this.attr("id"),type:'topic'});Follow(_$this,data);});$('button.collectfollow').click(function(){var _$this=$(this);var data=JSON.stringify({id:_$this.attr("id"),type:'collect'});Follow(_$this,data);});});function DoCollect(collectData){$(document).ready(function(){$('button#edit-collect-form').click(function(){var data=JSON.stringify({name:$('#name').val(),description:$('#description').val(),is_privacy:$("input[name='is_privacy']:checked").val()});$.ajax({type:"PUT",url:collectData.edit_url,data:data,contentType:'application/json;charset=UTF-8',success:function(result){if(result.judge==true)
+$(document).ready(function(){$('button.tagfollow').click(function(){var _$this=$(this);var data=JSON.stringify({id:_$this.attr("id"),type:'tag'});Follow(_$this,data);});$('button.topicfollow').click(function(){var _$this=$(this);var data=JSON.stringify({id:_$this.attr("id"),type:'topic'});Follow(_$this,data);});$('button.collectfollow').click(function(){var _$this=$(this);var data=JSON.stringify({id:_$this.attr("id"),type:'collect'});Follow(_$this,data);});$('button.userfollow').click(function(){var _$this=$(this);var data=JSON.stringify({id:_$this.attr("id"),type:'user'});Follow(_$this,data);});});function DoCollect(collectData){$(document).ready(function(){$('button#edit-collect-form').click(function(){var data=JSON.stringify({name:$('#name').val(),description:$('#description').val(),is_privacy:$("input[name='is_privacy']:checked").val()});$.ajax({type:"PUT",url:collectData.edit_url,data:data,contentType:'application/json;charset=UTF-8',success:function(result){if(result.judge==true)
 {window.location=collectData.edit_url;}}});});$('button#delete-collect-form').click(function(){$.ajax({type:"DELETE",url:collectData.delete_url,data:JSON.stringify(),contentType:'application/json;charset=UTF-8',success:function(result){if(result.judge==true)
 {window.location=collectData.edit_url;}}});});$('button#delete-collect-form').click(function(){$.ajax({type:"DELETE",url:collectData.delete_url,data:JSON.stringify(),contentType:'application/json;charset=UTF-8',success:function(result){if(result.judge==true)
 {window.location=collectData.url;}}});});$('#delete-from-collect').click(function(){var _$this=$(this);var topicId=_$this.attr('data-id');var collectId=collectData.collectId;var data=JSON.stringify({collectId:collectId,topicId:topicId});$.ajax({type:"DELETE",url:collectData.delete,data:data,contentType:'application/json;charset=UTF-8',success:function(result){if(result.judge==true)
 {window.location=collectData.url;}}});});$('#delete-from-collect').click(function(){var _$this=$(this);var topicId=_$this.attr('data-id');var collectId=collectData.collectId;var data=JSON.stringify({collectId:collectId,topicId:topicId});$.ajax({type:"DELETE",url:collectData.delete,data:data,contentType:'application/json;charset=UTF-8',success:function(result){if(result.judge==true)
 {_$this.parent().remove();}}});});});}
 {_$this.parent().remove();}}});});});}

BIN
maple/static/avatars/honmaple-14670891852944.png


+ 8 - 0
maple/static/styles/mine.js

@@ -52,6 +52,14 @@ $(document).ready(function(){
     });
     });
     Follow(_$this,data);
     Follow(_$this,data);
   });
   });
+  $('button.userfollow').click(function(){
+    var _$this = $(this);
+    var data = JSON.stringify({
+      id:_$this.attr("id"),
+      type:'user'
+    });
+    Follow(_$this,data);
+  });
 });
 });
 function DoCollect(collectData) {
 function DoCollect(collectData) {
   $(document).ready(function(){
   $(document).ready(function(){

+ 11 - 1
maple/templates/base/base.html

@@ -72,7 +72,17 @@
     {% endif %}
     {% endif %}
     <a href="{{ url_for('tag.tag')}}" ><span class="btn btn-sm btn-primary pull-right">所有标签</span></a>
     <a href="{{ url_for('tag.tag')}}" ><span class="btn btn-sm btn-primary pull-right">所有标签</span></a>
     <a href="{{ url_for('forums.userlist')}}"><span class="btn btn-sm btn-primary pull-right">用户列表</span></a>
     <a href="{{ url_for('forums.userlist')}}"><span class="btn btn-sm btn-primary pull-right">用户列表</span></a>
-    <a href="{{ url_for('forums.notice')}}"><span class="btn btn-sm btn-primary pull-right">消息通知</span></a>
+    <a href="{{ url_for('forums.notice')}}"><span class="btn btn-sm btn-primary pull-right">
+        消息通知
+        {% if g.user.is_authenticated %}
+        {% set n = current_user.id | notice_count %}
+        {% if n != 0 -%}
+        <span class="badge">
+            {{ n }}
+        </span>
+        {% endif %}
+        {% endif %}
+    </span></a>
     {% block content %} {% endblock %}
     {% block content %} {% endblock %}
 </div>
 </div>
 {% endblock %}
 {% endblock %}

+ 34 - 0
maple/templates/base/notice.html

@@ -0,0 +1,34 @@
+{% import 'base/link.html' as link_base %}
+{% macro privacy(notice) -%}
+<small style="color:#999;">由{{ link_base.user(notice.send_user) }} 发来的私信:</small>
+<small class="pull-right">{{ notice.publish | timesince }}</small>
+<p style="margin-bottom:0px">{{ notice.content['content'] }}</p>
+{%- endmacro %}
+{% macro reply(notice) -%}
+{% set content = notice.content %}
+<small style="color:#999;"> {{ link_base.user(notice.send_user)}} 回复了你创建的主题:
+    <a href="{{ content['url'] }}">{{ content['title'] }}</a>
+</small>
+<small class="pull-right"> {{ notice.publish | timesince }} </small>
+<p style="margin-bottom:0px">{{ content["content"] | safe_clean }}</p>
+{%- endmacro %}
+{% macro collect(notice) -%}
+{% set content = notice.content %}
+<small style="color:#999;"> {{ link_base.user(notice.send_user)}} 收藏了你创建的主题:
+    <a href="{{ content['url'] }}">{{ content['title'] }}</a>
+</small>
+<small class="pull-right"> {{ notice.publish | timesince }} </small>
+{%- endmacro %}
+{% macro like(notice) -%}
+{% set content = notice.content %}
+<small style="color:#999;"> {{ link_base.user(notice.send_user)}} 在
+    <a href="{{ content['url'] }}">{{ content['title'] }}</a> 赞了你回复
+</small>
+<small class="pull-right"> {{ notice.publish | timesince }} </small>
+<p style="margin-bottom:0px">{{ content["content"] | safe_clean }}</p>
+{%- endmacro %}
+{% macro user(notice) -%}
+{% set content = notice.content %}
+<small style="color:#999;"> {{ link_base.user(notice.send_user)}} 关注了你 </small>
+<small class="pull-right"> {{ notice.publish | timesince }} </small>
+{%- endmacro %}

+ 6 - 4
maple/templates/forums/notice.html

@@ -6,14 +6,16 @@
     <div class="col-md-9">
     <div class="col-md-9">
         <div class="panel panel-default">
         <div class="panel panel-default">
             <div class="panel-heading">
             <div class="panel-heading">
+                <button class="pull-right btn btn-sm btn-default ingore-all" style="padding:0 5px">全部标记为已读</button>
                 消息通知
                 消息通知
             </div>
             </div>
             {% if notices.items %}
             {% if notices.items %}
-            <div class="panel-body">
-                {% for notice in notices.items %}
-                {{ notice }}
-                {% endfor %}
+            {% for notice in notices.items %}
+            <div class="panel-body" style="border-bottom:1px solid #eee;">
+                {% import 'base/notice.html' as notice_base %}
+                {{ notice_base[notice.category](notice) }}
             </div>
             </div>
+            {% endfor %}
             {{ p_footer(notices, 'forums.notice')}}
             {{ p_footer(notices, 'forums.notice')}}
             {% else %}
             {% else %}
             <div class="panel-body">
             <div class="panel-body">

+ 1 - 1
maple/templates/topic/replies.html

@@ -19,7 +19,7 @@
     {% if replies.items %}
     {% if replies.items %}
     {% set num = 1 %}
     {% set num = 1 %}
     {% for reply in replies.items %}
     {% for reply in replies.items %}
-    <div class="panel-body media" style="border-bottom:1px solid #eee;margin:0">
+    <div class="panel-body media" id="reply-{{ reply.id }}" style="border-bottom:1px solid #eee;margin:0">
         <div class="media-left">
         <div class="media-left">
             <a href="{{ url_for('user.user',user_url=reply.author.username) }}">
             <a href="{{ url_for('user.user',user_url=reply.author.username) }}">
                 <img class="media-object img-circle" src="{{ link_base.avatar(reply.author.infor)}}" alt="avatar" style="width:48px;height:48px">
                 <img class="media-object img-circle" src="{{ link_base.avatar(reply.author.infor)}}" alt="avatar" style="width:48px;height:48px">

+ 1 - 1
maple/templates/user/following_user.html

@@ -12,7 +12,7 @@
         <div class="panel-panel-default">
         <div class="panel-panel-default">
             {% for user in current_user.following_users %}
             {% for user in current_user.following_users %}
             <div class="panel-body" style="border-bottom:1px solid #eee">
             <div class="panel-body" style="border-bottom:1px solid #eee">
-                {{ user.username}}
+                {{ link_base.user(user)}}
                 <span style="float:right">
                 <span style="float:right">
                     <button class="btn btn-sm btn-default userfollow active" id="{{ user.id}}" style="padding:0 5px">取消关注</button>
                     <button class="btn btn-sm btn-default userfollow active" id="{{ user.id}}" style="padding:0 5px">取消关注</button>
                 </span>
                 </span>

+ 32 - 0
maple/templates/user/infor.html

@@ -25,4 +25,36 @@
             </tr>
             </tr>
         </table>
         </table>
     </div>
     </div>
+    {% if g.user.is_authenticated and current_user.username != g.user_url %}
+    <div class="list-group-item">
+        <span class="text-right" style="display:block">
+            {% if user in current_user.following_users %}
+            <button class="btn btn-sm btn-default userfollow active" id="{{ user.id}}">取消关注</button>
+            {% else %}
+            <button class="btn btn-sm btn-default userfollow" id="{{ user.id}}">关注他</button>
+            {% endif %}
+            <button class="btn btn-sm btn-default" id="{{ user.id}}" title="私信" data-toggle="modal" data-target="#send-message"><i class="icon-comments-alt" style="font-size:16px;"></i></button>
+        </span>
+    </div>
+    {% endif %}
+</div>
+<div class="modal fade" id="send-message" tabindex="-1" role="dialog" aria-labelledby="send-messageLabel">
+    <div class="modal-dialog" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
+                <h5 class="modal-title" id="send-messageLabel">发送私信</h5>
+            </div>
+            <form action="{{ url_for('forums.message',receId=user.id)}}" method="POST">
+                <div class="modal-body">
+                    {{ g.message_form.hidden_tag() }}
+                    {{ g.message_form.message(class="form-control",placeholder="仅支持纯文本,不超过1024字") }}
+                </div>
+                <div class="modal-footer" style="padding-top:5px;padding-bottom:5px;">
+                    <button type="button" class="btn btn-default" data-dismiss="modal">取消</button>
+                    <button type="submit" class="btn btn-primary">确认</button>
+                </div>
+            </form>
+        </div>
+    </div>
 </div>
 </div>

+ 12 - 21
maple/templates/user/user.html

@@ -30,22 +30,16 @@
         <div  style="background:#fff;border:1px solid #ddd;border-radius:5px">
         <div  style="background:#fff;border:1px solid #ddd;border-radius:5px">
             {% if g.user.is_authenticated and current_user.username == g.user_url %}
             {% if g.user.is_authenticated and current_user.username == g.user_url %}
             <ul class="nav nav-pills nav-stacked ">
             <ul class="nav nav-pills nav-stacked ">
-                <li role="presentation" class="topics"><a href="{{ url_for('user.topic')}}">我的主题</a></li>
-                <li role="presentation" class="replies"><a href="{{ url_for('user.reply')}}">我的回复</a></li>
-                <li role="presentation" class="collects"><a href="{{ url_for('user.collect')}}">我的收藏</a></li>
-            </ul>
-            <ul class="nav nav-pills nav-stacked" >
-                <li role="presentation" class="followings"><a href="{{ url_for('user.following')}}">我的关注</a></li>
-                <li role="presentation" class="followers"><a href="{{ url_for('user.follower')}}">我的粉丝</a></li>
+                {% set class_list = ['topics','replies','collects','followings','followers'] %}
+                {% set href_list = ['user.topic','user.reply','user.collect','user.following','user.follower'] %}
+                {% set show_list = ['我的主题','我的回复','我的收藏','我的关注','我的粉丝'] %}
+                {{ nav_list(class_list,href_list,show_list)}}
             </ul>
             </ul>
             {% else %}
             {% else %}
             <ul class="nav nav-pills nav-stacked" >
             <ul class="nav nav-pills nav-stacked" >
                 <li role="presentation" class="topics"><a href="{{ url_for('user.topic')}}">{{ g.user_url}}的主题</a></li>
                 <li role="presentation" class="topics"><a href="{{ url_for('user.topic')}}">{{ g.user_url}}的主题</a></li>
                 <li role="presentation" class="replies"><a href="{{ url_for('user.reply')}}">{{ g.user_url}}的回复</a></li>
                 <li role="presentation" class="replies"><a href="{{ url_for('user.reply')}}">{{ g.user_url}}的回复</a></li>
                 <li role="presentation" class="collects"><a href="{{ url_for('user.collect')}}">{{ g.user_url}}的收藏</a></li>
                 <li role="presentation" class="collects"><a href="{{ url_for('user.collect')}}">{{ g.user_url}}的收藏</a></li>
-            </ul>
-            <ul class="nav nav-pills nav-stacked" >
-                <!-- <li role="presentation" class="followings"><a href="{{ url_for('user.following')}}">{{ g.user_url}}的关注</a></li> -->
                 <li role="presentation" class="followers"><a href="{{ url_for('user.follower')}}">{{ g.user_url}}的粉丝</a></li>
                 <li role="presentation" class="followers"><a href="{{ url_for('user.follower')}}">{{ g.user_url}}的粉丝</a></li>
             </ul>
             </ul>
             {% endif %}
             {% endif %}
@@ -53,17 +47,14 @@
     </div>
     </div>
     <div class="col-md-9">
     <div class="col-md-9">
         {% set setting = user.setting %}
         {% set setting = user.setting %}
-        {% if type == 'topic' %}
-        {% include 'user/topic.html' %}
-        {% elif type == 'reply' %}
-        {% include 'user/reply.html' %}
-        {% elif type == 'notebook' %}
-        {% include 'user/notebook.html' %}
-        {% elif type == 'follower' %}
-        {% include 'user/follower.html' %}
-        {% elif type == 'collect' %}
-        {% include 'user/collect.html' %}
-        {% endif %}
+        {% include "user/" + type + ".html" %}
     </div>
     </div>
 </div>
 </div>
 {% endblock %}
 {% endblock %}
+
+{% macro nav_list(class,href,show) -%}
+{% set len = class | length %}
+{% for i in range(len) %}
+<li role="presentation" class="{{ class[i] }}"><a href="{{ url_for(href[i])}}">{{show[i]}}</a></li>
+{% endfor %}
+{%- endmacro %}

+ 6 - 2
maple/topic/controls.py

@@ -6,7 +6,7 @@
 # Author: jianglin
 # Author: jianglin
 # Email: xiyang0807@gmail.com
 # Email: xiyang0807@gmail.com
 # Created: 2016-06-15 10:22:42 (CST)
 # Created: 2016-06-15 10:22:42 (CST)
-# Last Update:星期一 2016-6-27 14:36:20 (CST)
+# Last Update:星期四 2016-6-30 19:49:45 (CST)
 #          By:
 #          By:
 # Description:
 # Description:
 # **************************************************************************
 # **************************************************************************
@@ -14,6 +14,7 @@ from flask_login import current_user
 from maple import db
 from maple import db
 from maple.helpers import make_uid
 from maple.helpers import make_uid
 from maple.main.models import RedisData
 from maple.main.models import RedisData
+from maple.forums.controls import reply as notice_reply
 from .models import Topic, Tags, Reply
 from .models import Topic, Tags, Reply
 from re import split as sp
 from re import split as sp
 
 
@@ -92,6 +93,9 @@ class ReplyModel(object):
         reply.topic_id = uid
         reply.topic_id = uid
         db.session.add(reply)
         db.session.add(reply)
         db.session.commit()
         db.session.commit()
-        reply.topic.board.count.all_topics += 1
+        topic = reply.topic
+        topic.board.count.all_topics += 1
+        if topic.author_id != current_user.id:
+            notice_reply(topic, reply)
         db.session.commit()
         db.session.commit()
         RedisData.set_replies(uid)
         RedisData.set_replies(uid)

+ 6 - 1
maple/topic/models.py

@@ -6,7 +6,7 @@
 # Author: jianglin
 # Author: jianglin
 # Email: xiyang0807@gmail.com
 # Email: xiyang0807@gmail.com
 # Created: 2016-05-20 13:32:12 (CST)
 # Created: 2016-05-20 13:32:12 (CST)
-# Last Update:星期一 2016-6-27 23:1:42 (CST)
+# Last Update:星期二 2016-6-28 11:53:41 (CST)
 #          By:
 #          By:
 # Description:
 # Description:
 # **************************************************************************
 # **************************************************************************
@@ -26,6 +26,9 @@ class Tags(db.Model):
     tagname = db.Column(db.String(64), nullable=False)
     tagname = db.Column(db.String(64), nullable=False)
     summary = db.Column(db.Text)
     summary = db.Column(db.Text)
 
 
+    def __str__(self):
+        return self.tagname
+
     def __repr__(self):
     def __repr__(self):
         return '<Tags %r>' % self.tagname
         return '<Tags %r>' % self.tagname
 
 
@@ -131,6 +134,8 @@ class Collect(db.Model):
                              secondary='collect_topic',
                              secondary='collect_topic',
                              lazy='dynamic',
                              lazy='dynamic',
                              backref="collects")
                              backref="collects")
+    def __str__(self):
+        return self.name
 
 
     def __repr__(self):
     def __repr__(self):
         return "<Collect %r>" % self.name
         return "<Collect %r>" % self.name

+ 10 - 17
maple/user/forms.py

@@ -6,7 +6,7 @@
 # Author: jianglin
 # Author: jianglin
 # Email: xiyang0807@gmail.com
 # Email: xiyang0807@gmail.com
 # Created: 2016-05-20 18:08:44 (CST)
 # Created: 2016-05-20 18:08:44 (CST)
-# Last Update:星期五 2016-6-24 23:36:35 (CST)
+# Last Update:星期二 2016-6-28 21:51:0 (CST)
 #          By:
 #          By:
 # Description:
 # Description:
 # **************************************************************************
 # **************************************************************************
@@ -30,20 +30,13 @@ class PasswordForm(Form):
     password_nn = PasswordField('重复新密码:', [DataRequired()])
     password_nn = PasswordField('重复新密码:', [DataRequired()])
 
 
 
 
+choices = [(1, '所有人'), (2, '已登陆用户'), (3, '仅自己')]
+
+
 class PrivacyForm(Form):
 class PrivacyForm(Form):
-    online_status = SelectField('登录状态',
-                                coerce=int,
-                                choices=[(1, '所有人'), (2, '已登陆用户'), (3, '仅自己')])
-    topic_list = SelectField('主题列表',
-                             coerce=int,
-                             choices=[(1, '所有人'), (2, '已登陆用户'), (3, '仅自己')])
-
-    rep_list = SelectField('回复列表',
-                           coerce=int,
-                           choices=[(1, '所有人'), (2, '已登陆用户'), (3, '仅自己')])
-    ntb_list = SelectField('笔记列表',
-                           coerce=int,
-                           choices=[(1, '所有人'), (2, '已登陆用户'), (3, '仅自己')])
-    collect_list = SelectField('收藏列表',
-                               coerce=int,
-                               choices=[(1, '所有人'), (2, '已登陆用户'), (3, '仅自己')])
+    online_status = SelectField('登录状态', coerce=int, choices=choices)
+    topic_list = SelectField('主题列表', coerce=int, choices=choices)
+
+    rep_list = SelectField('回复列表', coerce=int, choices=choices)
+    ntb_list = SelectField('笔记列表', coerce=int, choices=choices)
+    collect_list = SelectField('收藏列表', coerce=int, choices=choices)

+ 14 - 14
maple/user/models.py

@@ -6,7 +6,7 @@
 # Author: jianglin
 # Author: jianglin
 # Email: xiyang0807@gmail.com
 # Email: xiyang0807@gmail.com
 # Created: 2016-05-20 13:24:19 (CST)
 # Created: 2016-05-20 13:24:19 (CST)
-# Last Update:星期一 2016-6-27 22:46:38 (CST)
+# Last Update:星期四 2016-6-30 21:14:5 (CST)
 #          By:
 #          By:
 # Description:
 # Description:
 # **************************************************************************
 # **************************************************************************
@@ -32,14 +32,11 @@ class Follow(db.Model):
     following_user_id = db.Column(db.Integer,
     following_user_id = db.Column(db.Integer,
                                   db.ForeignKey('users.id'))
                                   db.ForeignKey('users.id'))
     following_tag_id = db.Column(db.Integer,
     following_tag_id = db.Column(db.Integer,
-                                 db.ForeignKey('tags.id',
-                                               ondelete="CASCADE"))
+                                 db.ForeignKey('tags.id'))
     following_collect_id = db.Column(db.Integer,
     following_collect_id = db.Column(db.Integer,
-                                     db.ForeignKey('collects.id',
-                                                   ondelete="CASCADE"))
+                                     db.ForeignKey('collects.id'))
     followinf_topic_id = db.Column(db.Integer,
     followinf_topic_id = db.Column(db.Integer,
-                                   db.ForeignKey('topics.id',
-                                                 ondelete="CASCADE"))
+                                   db.ForeignKey('topics.id'))
 
 
 
 
 class User(db.Model, UserMixin):
 class User(db.Model, UserMixin):
@@ -60,24 +57,27 @@ class User(db.Model, UserMixin):
     following_tags = db.relationship('Tags',
     following_tags = db.relationship('Tags',
                                      secondary='follows',
                                      secondary='follows',
                                      primaryjoin="User.id==follows.c.follower_id",
                                      primaryjoin="User.id==follows.c.follower_id",
-                                     lazy='dynamic',
-                                     backref="followers",
+                                     # lazy='dynamic',
+                                     backref=db.backref(
+                                         'followers', lazy='dynamic'),
                                      # cascade='all,delete-orphan',
                                      # cascade='all,delete-orphan',
                                      # single_parent=True,
                                      # single_parent=True,
                                      )
                                      )
     following_topics = db.relationship('Topic',
     following_topics = db.relationship('Topic',
                                        secondary='follows',
                                        secondary='follows',
                                        primaryjoin="User.id==follows.c.follower_id",
                                        primaryjoin="User.id==follows.c.follower_id",
-                                       lazy='dynamic',
-                                       backref="followers",
+                                       # lazy='dynamic',
+                                       backref=db.backref(
+                                           'followers', lazy='dynamic'),
                                        # cascade='all,delete-orphan',
                                        # cascade='all,delete-orphan',
                                        # single_parent=True,
                                        # single_parent=True,
                                        )
                                        )
     following_collects = db.relationship('Collect',
     following_collects = db.relationship('Collect',
                                          secondary='follows',
                                          secondary='follows',
                                          primaryjoin="User.id==follows.c.follower_id",
                                          primaryjoin="User.id==follows.c.follower_id",
-                                         lazy='dynamic',
-                                         backref="followers",
+                                         backref=db.backref(
+                                             'followers', lazy='dynamic'),
+                                         # lazy='dynamic',
                                          # cascade='all,delete-orphan',
                                          # cascade='all,delete-orphan',
                                          # single_parent=True,
                                          # single_parent=True,
                                          )
                                          )
@@ -87,7 +87,7 @@ class User(db.Model, UserMixin):
                                       secondaryjoin="User.id==follows.c.following_user_id",
                                       secondaryjoin="User.id==follows.c.following_user_id",
                                       backref=db.backref(
                                       backref=db.backref(
                                           'followers', lazy='dynamic'),
                                           'followers', lazy='dynamic'),
-                                      lazy='dynamic'
+                                      # lazy='dynamic'
                                       )
                                       )
 
 
     setting_id = db.Column(db.Integer,
     setting_id = db.Column(db.Integer,

+ 8 - 2
maple/user/views.py

@@ -6,7 +6,7 @@
 # Author: jianglin
 # Author: jianglin
 # Email: xiyang0807@gmail.com
 # Email: xiyang0807@gmail.com
 # Created: 2016-05-20 18:04:43 (CST)
 # Created: 2016-05-20 18:04:43 (CST)
-# Last Update:星期六 2016-6-25 13:30:4 (CST)
+# Last Update:星期二 2016-6-28 22:1:34 (CST)
 #          By:
 #          By:
 # Description:
 # Description:
 # **************************************************************************
 # **************************************************************************
@@ -16,10 +16,16 @@ from maple import app
 from maple.helpers import is_num
 from maple.helpers import is_num
 from maple.topic.models import Topic, Reply, Collect
 from maple.topic.models import Topic, Reply, Collect
 from maple.user.models import User
 from maple.user.models import User
+from maple.forums.forms import MessageForm
 
 
 site = Blueprint('user', __name__)
 site = Blueprint('user', __name__)
 
 
 
 
+@site.before_request
+def before():
+    g.message_form = MessageForm()
+
+
 @site.url_value_preprocessor
 @site.url_value_preprocessor
 def pull_user_url(endpoint, values):
 def pull_user_url(endpoint, values):
     g.user_url = values.pop('user_url')
     g.user_url = values.pop('user_url')
@@ -54,7 +60,7 @@ def topic():
         abort(404)
         abort(404)
     if orderby == 'vote':
     if orderby == 'vote':
         topics = Topic.query.join(Topic.author).filter(
         topics = Topic.query.join(Topic.author).filter(
-            User.username == g.user_url).order_by(Topic.vote).paginate(
+            User.username == g.user_url).order_by(Topic.vote.desc()).paginate(
                 page, app.config['PER_PAGE'],
                 page, app.config['PER_PAGE'],
                 error_out=True)
                 error_out=True)
     else:
     else: