Browse Source

add upgrade script,fix some javascript

honmaple 8 years ago
parent
commit
325ac74669
42 changed files with 477 additions and 471 deletions
  1. 2 2
      forums/api/follow/urls.py
  2. 1 1
      forums/api/forms.py
  3. 5 4
      forums/api/forums/models.py
  4. 1 1
      forums/api/forums/views.py
  5. 21 6
      forums/api/topic/models.py
  6. 10 1
      forums/api/topic/permissions.py
  7. 28 29
      forums/api/topic/views.py
  8. 10 7
      forums/api/upload/views.py
  9. 16 3
      forums/api/user/models.py
  10. 1 1
      forums/app.py
  11. 2 1
      forums/common/middleware.py
  12. 2 2
      forums/common/utils.py
  13. 10 1
      forums/count.py
  14. 1 29
      forums/filters.py
  15. 2 2
      manager.py
  16. 2 8
      static/assets/home.js
  17. 18 51
      static/styles/topic.js
  18. 6 2
      templates/base/link.html
  19. 1 0
      templates/board/board.html
  20. 0 17
      templates/common/col.html
  21. 0 0
      templates/mine/_collect_macro.html
  22. 0 0
      templates/mine/_follower_macro.html
  23. 0 0
      templates/mine/_following_macro.html
  24. 0 93
      templates/mine/_macro.html
  25. 0 43
      templates/mine/_reply_macro.html
  26. 0 42
      templates/mine/_topic_macro.html
  27. 0 21
      templates/mine/collect_list.html
  28. 0 21
      templates/mine/follower_list.html
  29. 0 21
      templates/mine/following_list.html
  30. 0 21
      templates/mine/reply_list.html
  31. 0 22
      templates/mine/topic_list.html
  32. 1 0
      templates/tag/tag.html
  33. 1 1
      templates/topic/_list_macro.html
  34. 1 5
      templates/topic/item/body.html
  35. 1 3
      templates/topic/item/heading.html
  36. 2 2
      templates/topic/reply/_macro.html
  37. 1 0
      templates/topic/topic_list.html
  38. 1 1
      templates/user/followers.html
  39. 1 1
      templates/user/info.html
  40. 3 3
      templates/user/replies.html
  41. 3 3
      templates/user/user.html
  42. 323 0
      upgrade.py

+ 2 - 2
forums/api/follow/urls.py

@@ -6,7 +6,7 @@
 # Author: jianglin
 # Email: xiyang0807@gmail.com
 # Created: 2016-12-22 21:49:08 (CST)
-# Last Update:星期四 2016-12-22 22:20:53 (CST)
+# Last Update:星期四 2017-3-30 15:26:49 (CST)
 #          By:
 # Description:
 # **************************************************************************
@@ -14,7 +14,7 @@ from flask import Blueprint
 from .views import (FollowingTagsView, FollowingUsersView, FollowingTopicsView,
                     FollowingCollectsView)
 
-site = Blueprint('follow', __name__, url_prefix='/user/following')
+site = Blueprint('follow', __name__, url_prefix='/following')
 
 topic_view = FollowingTopicsView.as_view('topic')
 tag_view = FollowingTagsView.as_view('tag')

+ 1 - 1
forums/api/forms.py

@@ -6,7 +6,7 @@
 # Author: jianglin
 # Email: xiyang0807@gmail.com
 # Created: 2017-03-28 12:53:02 (CST)
-# Last Update:星期三 2017-3-29 20:53:1 (CST)
+# Last Update:星期三 2017-3-29 22:51:17 (CST)
 #          By:
 # Description:
 # **************************************************************************

+ 5 - 4
forums/api/forums/models.py

@@ -6,7 +6,7 @@
 # Author: jianglin
 # Email: xiyang0807@gmail.com
 # Created: 2017-03-25 18:48:33 (CST)
-# Last Update:星期三 2017-3-29 22:14:49 (CST)
+# Last Update:星期四 2017-3-30 14:49:19 (CST)
 #          By:
 # Description:
 # **************************************************************************
@@ -40,11 +40,12 @@ class Board(db.Model, ModelMixin):
 
     @property
     def topic_count(self):
-        return self.topics.count()
+        # return self.topics.count()
+        return Count.board_topic_count(self.id)
 
     @topic_count.setter
     def topic_count(self, value):
-        Count.board_topic_count(self.id, value)
+        return Count.board_topic_count(self.id, value)
 
     @property
     def post_count(self):
@@ -52,7 +53,7 @@ class Board(db.Model, ModelMixin):
 
     @post_count.setter
     def post_count(self, value):
-        Count.board_post_count(self.id, value)
+        return Count.board_post_count(self.id, value)
 
     def __str__(self):
         return self.name

+ 1 - 1
forums/api/forums/views.py

@@ -6,7 +6,7 @@
 # Author: jianglin
 # Email: xiyang0807@gmail.com
 # Created: 2016-12-17 20:45:08 (CST)
-# Last Update:星期三 2017-3-29 21:9:52 (CST)
+# Last Update:星期四 2017-3-30 14:54:26 (CST)
 #          By:
 # Description:
 # **************************************************************************

+ 21 - 6
forums/api/topic/models.py

@@ -6,7 +6,7 @@
 # Author: jianglin
 # Email: xiyang0807@gmail.com
 # Created: 2016-12-15 20:52:07 (CST)
-# Last Update:星期三 2017-3-29 21:49:10 (CST)
+# Last Update:星期四 2017-3-30 15:5:44 (CST)
 #          By:
 # Description:
 # **************************************************************************
@@ -21,6 +21,7 @@ from forums.api.user.models import User
 from forums.common.models import CommonUserMixin
 from forums.extension import db
 from forums.count import Count
+from forums.filters import safe_markdown, safe_clean, markdown
 
 topic_follower = db.Table(
     'topic_follower',
@@ -85,12 +86,20 @@ class Topic(db.Model, ModelMixin):
         return self.collects.filter_by(author_id=user.id).exists()
 
     @property
+    def text(self):
+        if self.content_type == Topic.CONTENT_TYPE_TEXT:
+            return safe_clean(self.content)
+        elif self.content_type == Topic.CONTENT_TYPE_MARKDOWN:
+            return markdown(self.content)
+        return self.content
+
+    @property
     def newest_reply(self):
         return self.replies.order_by('-id').first()
 
     @property
     def reply_count(self):
-        return self.replies.count()
+        return Count.topic_reply_count(self.id)
 
     @reply_count.setter
     def reply_count(self, value):
@@ -104,10 +113,6 @@ class Topic(db.Model, ModelMixin):
     def read_count(self, value):
         return Count.topic_read_count(self.id, value)
 
-    @property
-    def read_count(self):
-        return self.replies.count()
-
     def __str__(self):
         return self.title
 
@@ -153,8 +158,18 @@ class Reply(db.Model, ModelMixin):
     def is_liked(self, user=None):
         if user is None:
             user = current_user
+            if not user.is_authenticated:
+                return False
         return self.likers.filter_by(id=user.id).exists()
 
+    @property
+    def liker_count(self):
+        return Count.reply_liker_count(self.id)
+
+    @liker_count.setter
+    def liker_count(self, value):
+        return Count.reply_liker_count(self.id, value)
+
     def __str__(self):
         return self.content[:10]
 

+ 10 - 1
forums/api/topic/permissions.py

@@ -6,7 +6,7 @@
 # Author: jianglin
 # Email: xiyang0807@gmail.com
 # Created: 2017-03-29 15:53:37 (CST)
-# Last Update:星期三 2017-3-29 18:38:9 (CST)
+# Last Update:星期四 2017-3-30 16:17:48 (CST)
 #          By:
 # Description:
 # **************************************************************************
@@ -17,6 +17,14 @@ from forums.permission import (ReplyPermission, RestfulView, TopicPermission,
                                is_confirmed)
 
 
+class Edit(RestfulView):
+    def get(self, topicId):
+        permission = TopicPermission(topicId)
+        if not permission.can():
+            return self.callback()
+        return True
+
+
 class TopicList(RestfulView):
     @is_confirmed
     def post(self):
@@ -70,3 +78,4 @@ topic_permission = Topic()
 reply_list_permission = ReplyList()
 reply_permission = Reply()
 like_permission = Like()
+edit_permission = Edit()

+ 28 - 29
forums/api/topic/views.py

@@ -6,7 +6,7 @@
 # Author: jianglin
 # Email: xiyang0807@gmail.com
 # Created: 2016-12-15 22:07:39 (CST)
-# Last Update:星期三 2017-3-29 22:17:13 (CST)
+# Last Update:星期四 2017-3-30 16:39:56 (CST)
 #          By:
 # Description:
 # **************************************************************************
@@ -30,7 +30,7 @@ from forums.filters import safe_markdown
 from .models import Reply, Topic
 from .permissions import (like_permission, reply_list_permission,
                           reply_permission, topic_list_permission,
-                          topic_permission)
+                          topic_permission, edit_permission)
 
 
 class TopicAskView(IsConfirmedMethodView):
@@ -44,6 +44,8 @@ class TopicAskView(IsConfirmedMethodView):
 
 
 class TopicEditView(IsConfirmedMethodView):
+    decorators = (edit_permission, )
+
     def get(self, topicId):
         topic = Topic.query.filter_by(id=topicId).first_or_404()
         form = form_board()
@@ -101,6 +103,7 @@ class TopicListView(MethodView):
         tags = tags.split(',')
         topic_tags = []
         for tag in tags:
+            tag = tag.strip()
             topic_tag = Tags.query.filter_by(name=tag).first()
             if topic_tag is None:
                 topic_tag = Tags(name=tag, description=tag)
@@ -118,8 +121,6 @@ class TopicListView(MethodView):
 
 
 class TopicView(MethodView):
-    per_page = 10
-
     decorators = (topic_permission, )
 
     def get(self, topicId):
@@ -140,23 +141,25 @@ class TopicView(MethodView):
         }
         return render_template('topic/topic.html', **data)
 
-    # def put(self, topicId):
-    #     post_data = request.data
-    #     topic = Topic.query.filter_by(id=topicId).first_or_404()
-    #     title = post_data.pop('title', None)
-    #     content = post_data.pop('content', None)
-    #     content_type = post_data.pop('content_type', None)
-    #     board = post_data.pop('board', None)
-    #     if title is not None:
-    #         topic.title = title
-    #     if content is not None:
-    #         topic.content = content
-    #     if content_type is not None:
-    #         topic.content_type = content_type
-    #     if board is not None:
-    #         topic.board = int(board)
-    #     topic.save()
-    #     return redirect(url_for('topic.topic', topicId=topic.id))
+    @form_validate(form_board)
+    def put(self, topicId):
+        form = form_board()
+        post_data = form.data
+        topic = Topic.query.filter_by(id=topicId).first_or_404()
+        title = post_data.pop('title', None)
+        content = post_data.pop('content', None)
+        content_type = post_data.pop('content_type', None)
+        category = post_data.pop('category', None)
+        if title is not None:
+            topic.title = title
+        if content is not None:
+            topic.content = content
+        if content_type is not None:
+            topic.content_type = content_type
+        if category is not None:
+            topic.board_id = int(category)
+        topic.save()
+        return HTTPResponse(HTTPResponse.NORMAL_STATUS).to_response()
 
 
 class ReplyListView(MethodView):
@@ -183,21 +186,17 @@ class ReplyView(MethodView):
 
     def put(self, replyId):
         post_data = request.data
-        reply = Reply.query.filter_by(id=replyId).first()
+        reply = Reply.query.filter_by(id=replyId).first_or_404()
         content = post_data.pop('content', None)
         if content is not None:
             reply.content = content
         reply.save()
-        serializer = Serializer(reply, many=False)
-        return HTTPResponse(HTTPResponse.NORMAL_STATUS,
-                            **serializer.data).to_response()
+        return HTTPResponse(HTTPResponse.NORMAL_STATUS).to_response()
 
     def delete(self, replyId):
-        reply = Reply.query.filter_by(id=replyId).first()
+        reply = Reply.query.filter_by(id=replyId).first_or_404()
         reply.delete()
-        serializer = Serializer(reply, many=False)
-        return HTTPResponse(HTTPResponse.NORMAL_STATUS,
-                            **serializer.data).to_response()
+        return HTTPResponse(HTTPResponse.NORMAL_STATUS).to_response()
 
 
 class LikeView(MethodView):

+ 10 - 7
forums/api/upload/views.py

@@ -6,7 +6,7 @@
 # Author: jianglin
 # Email: xiyang0807@gmail.com
 # Created: 2016-12-21 21:56:41 (CST)
-# Last Update:星期三 2017-3-29 18:19:47 (CST)
+# Last Update:星期四 2017-3-30 21:50:30 (CST)
 #          By:
 # Description:
 # **************************************************************************
@@ -42,7 +42,7 @@ class AvatarView(MethodView):
         avatar_path = current_app.config['AVATAR_FOLDER']
         avatar = os.path.join(avatar_path, filename + '.png')
         if not os.path.exists(avatar_path):
-            os.mkdir(avatar_path)
+            os.makedirs(avatar_path)
         img.save(avatar)
         img.close()
         info = user.info
@@ -58,10 +58,13 @@ class AvatarView(MethodView):
 
 class AvatarFileView(MethodView):
     def get(self, filename):
-        avatar_path = os.path.join(current_app.static_folder,
-                                   current_app.config.get('AVATAR_FOLDER',
-                                                          'avatars/'))
+        current_app.config.setdefault('AVATAR_FOLDER', os.path.join(
+            current_app.static_folder, 'avatars/'))
+        avatar_path = current_app.config['AVATAR_FOLDER']
+        print(avatar_path)
         if not os.path.exists(os.path.join(avatar_path, filename)):
-            avatar_path = os.path.join(current_app.static_folder, 'images/')
-            filename = 'Moo.png'
+            filename = filename.split('-')[0]
+            return redirect(url_for('avatar', text=filename))
+            # avatar_path = os.path.join(current_app.static_folder, 'images/')
+            # filename = 'Moo.png'
         return send_from_directory(avatar_path, filename)

+ 16 - 3
forums/api/user/models.py

@@ -6,7 +6,7 @@
 # Author: jianglin
 # Email: xiyang0807@gmail.com
 # Created: 2016-12-15 21:09:08 (CST)
-# Last Update:星期三 2017-3-29 21:54:25 (CST)
+# Last Update:星期四 2017-3-30 15:10:19 (CST)
 #          By:
 # Description:
 # **************************************************************************
@@ -25,6 +25,7 @@ from werkzeug.security import check_password_hash, generate_password_hash
 from flask_maple.models import ModelMixin
 from forums.count import Count
 from forums.extension import db, mail
+from forums.common.records import load_online_sign_users
 
 user_follower = db.Table(
     'user_follower',
@@ -41,7 +42,7 @@ class User(db.Model, UserMixin, ModelMixin):
     is_superuser = db.Column(db.Boolean, default=False)
     is_confirmed = db.Column(db.Boolean, default=False)
     register_time = db.Column(db.DateTime, default=datetime.now())
-    last_login = db.Column(db.DateTime, nullable=True)
+    last_login = db.Column(db.DateTime, default=datetime.now())
 
     followers = db.relationship(
         'User',
@@ -60,6 +61,18 @@ class User(db.Model, UserMixin, ModelMixin):
             user_follower.c.follower_id == user.id).exists()
 
     @property
+    def is_online(self):
+        setting = self.setting
+        if setting.online_status == UserSetting.STATUS_ALLOW_ALL:
+            return self.username in load_online_sign_users()
+        elif setting.online_status == UserSetting.STATUS_ALLOW_AUTHENTICATED:
+            return self.username in load_online_sign_users(
+            ) and current_user.is_authenticated
+        elif setting.online_status == UserSetting.STATUS_ALLOW_OWN:
+            return current_user.id == self.id
+        return False
+
+    @property
     def topic_count(self):
         return self.topics.count()
 
@@ -202,7 +215,7 @@ class UserSetting(db.Model, ModelMixin):
     __tablename__ = 'usersetting'
     id = db.Column(db.Integer, primary_key=True)
     online_status = db.Column(
-        db.Integer, nullable=False, default=STATUS_ALLOW_ALL)
+        db.String(10), nullable=False, default=STATUS_ALLOW_ALL)
     topic_list = db.Column(
         db.String(10), nullable=False, default=STATUS_ALLOW_ALL)
     rep_list = db.Column(

+ 1 - 1
forums/app.py

@@ -6,7 +6,7 @@
 # Author: jianglin
 # Email: xiyang0807@gmail.com
 # Created: 2017-03-28 16:11:07 (CST)
-# Last Update:星期三 2017-3-29 17:35:46 (CST)
+# Last Update:星期四 2017-3-30 15:7:20 (CST)
 #          By:
 # Description:
 # **************************************************************************

+ 2 - 1
forums/common/middleware.py

@@ -6,7 +6,7 @@
 # Author: jianglin
 # Email: xiyang0807@gmail.com
 # Created: 2016-11-12 13:29:17 (CST)
-# Last Update:星期三 2017-3-29 22:26:9 (CST)
+# Last Update:星期四 2017-3-30 14:46:35 (CST)
 #          By:
 # Description:
 # **************************************************************************
@@ -48,6 +48,7 @@ class OnlineMiddleware(object):
         else:
             mark_online(request.remote_addr)
         g.get_online = get_online()
+        # g.get_online = (1, 2, 3, 4, 5)
 
 
 def get_online():

+ 2 - 2
forums/common/utils.py

@@ -6,7 +6,7 @@
 # Author: jianglin
 # Email: xiyang0807@gmail.com
 # Created: 2017-03-13 13:40:38 (CST)
-# Last Update:星期三 2017-3-29 19:59:57 (CST)
+# Last Update:星期四 2017-3-30 13:55:7 (CST)
 #          By:
 # Description:
 # **************************************************************************
@@ -21,7 +21,7 @@ def gen_order_by(query_dict=dict(), keys=[], date_key=True):
     keys.append('id')
     if date_key:
         keys += ['created_at', 'updated_at']
-    order_by = ['-id']
+    order_by = ['id']
     descent = query_dict.pop('orderby', None)
     if descent is not None:
         descent = descent.split(',')

+ 10 - 1
forums/count.py

@@ -6,7 +6,7 @@
 # Author: jianglin
 # Email: xiyang0807@gmail.com
 # Created: 2017-03-29 21:28:52 (CST)
-# Last Update:星期三 2017-3-29 22:1:2 (CST)
+# Last Update:星期四 2017-3-30 14:59:51 (CST)
 #          By:
 # Description: 一些统计信息
 # **************************************************************************
@@ -51,6 +51,15 @@ class Count(object):
         return redis_data.hget(key, 'read') or 0
 
     @classmethod
+    def reply_liker_count(cls, replyId, value=None):
+        key = 'count:reply:%s' % str(replyId)
+        if value is not None:
+            pipe = redis_data.pipeline()
+            pipe.hincrby(key, 'likers', value)
+            pipe.execute()
+        return redis_data.hget(key, 'likers') or 0
+
+    @classmethod
     def user_topic_count(cls, userId, value=None):
         key = 'count:user:%s' % str(userId)
         if value is not None:

+ 1 - 29
forums/filters.py

@@ -6,7 +6,7 @@
 # Author: jianglin
 # Email: xiyang0807@gmail.com
 # Created: 2016-11-07 21:00:32 (CST)
-# Last Update:星期三 2017-3-29 20:42:23 (CST)
+# Last Update:星期四 2017-3-30 15:10:35 (CST)
 #          By:
 # Description:
 # **************************************************************************
@@ -68,17 +68,6 @@ def show_time():
     else:
         return 'UTC:' + format_datetime(datetime.utcnow())
 
-
-def notice_count():
-    from forums.api.forums.models import Notice
-    if g.user.is_authenticated:
-        count = Notice.query.filter_by(
-            rece_id=g.user.id, is_read=False).count()
-        if count > 0:
-            return count
-    return None
-
-
 def hot_tags():
     from forums.api.tag.models import Tags
     tags = Tags.query.limit(9).all()
@@ -91,20 +80,6 @@ def recent_tags():
     return tags
 
 
-def is_online(user):
-    from forums.api.user.models import UserSetting
-    from forums.common.records import load_online_sign_users
-    setting = user.setting
-    if setting.online_status == UserSetting.STATUS_ALLOW_ALL:
-        return user.username in load_online_sign_users()
-    elif setting.online_status == UserSetting.STATUS_ALLOW_AUTHENTICATED:
-        return user.username in load_online_sign_users(
-        ) and current_user.is_authenticated
-    elif setting.online_status == UserSetting.STATUS_ALLOW_OWN:
-        return current_user.id == user.id
-    return False
-
-
 def is_not_confirmed(user):
     return (not user.is_confirmed and user.id == current_user.id)
 
@@ -114,10 +89,7 @@ def register_jinja2(app):
     app.jinja_env.globals['SITE'] = SITE
     app.jinja_env.globals['hot_tags'] = hot_tags
     app.jinja_env.globals['recent_tags'] = recent_tags
-    app.jinja_env.globals['notice_count'] = notice_count
     app.jinja_env.globals['show_time'] = show_time
     app.jinja_env.filters['timesince'] = timesince
-    app.jinja_env.filters['markdown'] = safe_markdown
     app.jinja_env.filters['safe_clean'] = safe_clean
-    app.jinja_env.filters['is_online'] = is_online
     app.jinja_env.filters['is_not_confirmed'] = is_not_confirmed

+ 2 - 2
manager.py

@@ -6,7 +6,7 @@
 # Author: jianglin
 # Email: xiyang0807@gmail.com
 # Created: 2016-10-25 22:08:39 (CST)
-# Last Update:星期三 2017-3-29 20:22:32 (CST)
+# Last Update:星期三 2017-3-29 23:38:31 (CST)
 #          By:
 # Description:
 # **************************************************************************
@@ -35,7 +35,7 @@ def init_db():
     """
     Drops and re-creates the SQL schema
     """
-    # db.drop_all()
+    db.drop_all()
     db.configure_mappers()
     db.create_all()
     db.session.commit()

+ 2 - 8
static/assets/home.js

@@ -26,15 +26,9 @@ $(document).ready(function(){$('button.topic-following').click(function(){var _$
 {window.location.href=collect_url;}}});});$('#delete-from-collect').click(function(){var _$this=$(this);var topicId=_$this.attr('data-id');var data=JSON.stringify({topicId:topicId});$.ajax({type:"DELETE",url:'/topic/'+topicId+'/collect',data:data,contentType:'application/json;charset=UTF-8',success:function(response){if(response.status==='200')
 {_$this.parent().remove();}}});});});}
 $(document).ready(function(){$('.like-reply').click(function(){var _$this=$(this);var replyId=_$this.attr('data-id');var like_url="/replies/"+replyId+'/like';var data=JSON.stringify({});if(_$this.hasClass('like-active')){$.ajax({type:"DELETE",url:like_url,data:data,contentType:'application/json;charset=UTF-8',success:function(response){if(response.status==='200')
-{_$this.attr("title","赞");_$this.removeClass("like-active");_$this.addClass("like-no-active");}else
-{window.location.href=response.data.url;}}});}else{$.ajax({type:"POST",url:like_url,data:data,contentType:'application/json;charset=UTF-8',success:function(response){if(response.status==='200')
+{_$this.attr("title","赞");_$this.removeClass("like-active");_$this.addClass("like-no-active");}else{window.location.href=response.url;}}});}else{$.ajax({type:"POST",url:like_url,data:data,contentType:'application/json;charset=UTF-8',success:function(response){if(response.status==='200')
 {_$this.attr("title","取消赞");_$this.removeClass("like-no-active");_$this.addClass("like-active");}else
-{window.location.href=response.url;}}});}});$('.reply-author').click(function(){var _$this=$(this);var author=_$this.attr('data-id');$('#content').focus();$('#content').val('@'+author+' ');});});function DoVote(voteData){$(document).ready(function(){$('#topic-up-vote').click(function(){var data=JSON.stringify({});$.ajax({type:"POST",url:voteData.vote_url,data:data,contentType:'application/json;charset=UTF-8',success:function(response){if(response.status==='200')
-{$('.votes').html(result.html);}else
-{window.location.href=result.url;}}});});$('#topic-down-vote').click(function(){var data=JSON.stringify({});$.ajax({type:"DELETE",url:voteData.vote_url,data:data,contentType:'application/json;charset=UTF-8',success:function(response){if(response.status==='200')
-{$('.votes').html(result.html);}else
-{window.location.href=result.url;}}});});});}
-$(document).ready(function(){$('#topic-preview').click(function(){var content=$('#content').val();$.post('/topic/preview',{content:$("#content").val(),content_type:$("#content_type").val()},function(data){$("#show-preview").html(data);});});$('#tokenfield').tokenfield({limit:4});$('#topic-put-btn').click(function(){var _$this=$(this);var form_data=$("form#topic-put").serializeArray();var url='/topic/'+_$this.attr("data-id");var data={};$.each(form_data,function(){data[this.name]=this.value;});$.ajax({type:"PUT",url:url,data:JSON.stringify(data),contentType:'application/json;charset=UTF-8',success:function(response){if(response.status==='200'){window.location.href=url;}}});});});/*!
+{window.location.href=response.url;}}});}});$('.reply-author').click(function(){var _$this=$(this);var author=_$this.attr('data-id');$('#content').focus();$('#content').val('@'+author+' ');});$('#topic-preview').click(function(){var content=$('#content').val();$.post('/topic/preview',{content:$("#content").val(),content_type:$("#content_type").val()},function(data){$("#show-preview").html(data);});});$('#tokenfield').tokenfield({limit:4});$('#topic-put-btn').click(function(){var _$this=$(this);var url='/topic/'+_$this.attr("data-id");var data={csrf_token:$('input[name="csrf_token"]').val(),title:$('input[name="title"]').val(),tags:$('input[name="tags"]').val(),category:$('select[name="category"]').val(),content:$('textarea[name="content"]').val(),content_type:$('select[name="content_type"]').val()};$.ajax({type:"PUT",url:url,data:JSON.stringify(data),contentType:'application/json;charset=UTF-8',success:function(response){if(response.status==='200'){window.location.href=url;}else{if(response.description!==""){alert(response.description);}else{alert(response.message);}}}});});});/*!
  * bootstrap-tokenfield 0.12.1
  * https://github.com/sliptree/bootstrap-tokenfield
  * Copyright 2013-2014 Sliptree and other contributors; Licensed MIT

+ 18 - 51
static/styles/topic.js

@@ -17,9 +17,8 @@ $(document).ready(function(){
             _$this.attr("title","赞");
             _$this.removeClass("like-active");
             _$this.addClass("like-no-active");
-          } else
-          {
-            window.location.href = response.data.url;
+          } else {
+            window.location.href = response.url;
           }
         }});
     }else {
@@ -39,55 +38,14 @@ $(document).ready(function(){
             window.location.href = response.url;
           }
         }});
-    }});
+    }
+  });
   $('.reply-author').click(function() {
     var _$this = $(this);
     var author = _$this.attr('data-id');
     $('#content').focus();
     $('#content').val('@' + author + ' ');
   });
-});
-function DoVote(voteData) {
-  $(document).ready(function(){
-    $('#topic-up-vote').click(function() {
-      var data = JSON.stringify({
-      });
-      $.ajax ({
-        type : "POST",
-        url : voteData.vote_url,
-        data:data,
-        contentType: 'application/json;charset=UTF-8',
-        success: function(response) {
-          if (response.status === '200')
-          {
-            $('.votes').html(result.html);
-          } else
-          {
-            window.location.href = result.url;
-          }
-        }});
-    });
-    $('#topic-down-vote').click(function() {
-      var data = JSON.stringify({
-      });
-      $.ajax ({
-        type : "DELETE",
-        url : voteData.vote_url,
-        data:data,
-        contentType: 'application/json;charset=UTF-8',
-        success: function(response) {
-          if (response.status === '200')
-          {
-            $('.votes').html(result.html);
-          } else
-          {
-            window.location.href = result.url;
-          }
-        }});
-    });
-  });
-}
-$(document).ready(function(){
   $('#topic-preview').click(function() {
     var content = $('#content').val();
     $.post('/topic/preview', {
@@ -102,12 +60,15 @@ $(document).ready(function(){
   });
   $('#topic-put-btn').click(function() {
     var _$this = $(this);
-    var form_data = $("form#topic-put").serializeArray();
     var url = '/topic/' + _$this.attr("data-id");
-    var data = {};
-    $.each(form_data,function() {
-      data[this.name] = this.value;
-    });
+    var data = {
+      csrf_token:$('input[name="csrf_token"]').val(),
+      title:$('input[name="title"]').val(),
+      tags:$('input[name="tags"]').val(),
+      category:$('select[name="category"]').val(),
+      content:$('textarea[name="content"]').val(),
+      content_type:$('select[name="content_type"]').val()
+    };
     $.ajax ({
       type : "PUT",
       url : url,
@@ -116,6 +77,12 @@ $(document).ready(function(){
       success: function(response) {
         if (response.status === '200') {
           window.location.href= url;
+        }else {
+          if (response.description !==""){
+            alert(response.description);
+          }else {
+            alert(response.message);
+          }
         }
       }
     });

+ 6 - 2
templates/base/link.html

@@ -38,9 +38,13 @@
 {% macro user_avatar(user,width=64) -%}
 {% set info = user.info %}
 {% if not info.avatar -%}
-<img class="img-circle" src="{{ url_for('avatar',text=user.username) }}" style="width:{{ width }}px;height:{{ width }}px">
+<a href="{{ url_for('user.user',username=user.username) }}">
+  <img class="img-circle" src="{{ url_for('avatar',text=user.username) }}" style="width:{{ width }}px;height:{{ width }}px">
+</a>
 {% else %}
-<img class="img-circle" src="{{ url_for('upload.avatar_file',filename=info.avatar) }}" style="width:{{ width }}px;height:{{ width }}px">
+<a href="{{ url_for('user.user',username=user.username) }}">
+  <img class="img-circle" src="{{ url_for('upload.avatar_file',filename=info.avatar) }}" style="width:{{ width }}px;height:{{ width }}px">
+</a>
 {%- endif %}
 {%- endmacro %}
 

+ 1 - 0
templates/board/board.html

@@ -20,6 +20,7 @@
     </div>
     <div class="panel panel-default">
       {% include "topic/_topic.html" %}
+      {{ p_footer(topics,'forums.board',dict(boardId=board.id))}}
     </div>
   </div>
   <div class="col-md-3" style="padding-left:0">

+ 0 - 17
templates/common/col.html

@@ -1,17 +0,0 @@
-{% macro col_row(heading,body,other) -%}
-<div class="row">
-    <div class="col-md-9">
-        <div class="panel panel-primary">
-            <div class="panel-heading">
-                {{ heading }}
-            </div>
-            <div class="panel-body">
-                {{ body }}
-            </div>
-        </div>
-    </div>
-    <div class="col-md-3" style="padding-left:0">
-        {{ other }}
-    </div>
-</div>
-{%- endmacro %}

+ 0 - 0
templates/mine/_collect_macro.html


+ 0 - 0
templates/mine/_follower_macro.html


+ 0 - 0
templates/mine/_following_macro.html


+ 0 - 93
templates/mine/_macro.html

@@ -1,93 +0,0 @@
-{% import 'base/link.html' as link %}
-
-{% macro nav_list(type,name) -%}
-<ul class="nav nav-pills nav-stacked" >
-    <li role="presentation" {% if type =='topic' %}class="active"{% endif %}>
-        <a href="{{ url_for('mine.topiclist',username=name)}}">{{ _("topics of %(n)s",n = name) }}</a>
-    </li>
-    <li role="presentation" {% if type =='reply' %}class="active"{% endif %}>
-        <a href="{{ url_for('mine.replylist',username=name)}}">{{ _("replies of %(n)s",n = name) }}</a>
-    </li>
-    <li role="presentation" {% if type =='collect' %}class="active"{% endif %}>
-        <a href="{{ url_for('mine.collectlist',username=name)}}">{{ _("collects of %(n)s",n = name) }}</a>
-    </li>
-    <li role="presentation" {% if type =='follower' %}class="active"{% endif %}>
-        <a href="{{ url_for('mine.followerlist',username=name)}}">{{ _("followers of %(n)s",n = name) }}</a>
-    </li>
-    <li role="presentation" {% if type =='follower' %}class="active"{% endif %}>
-        <a href="{{ url_for('mine.followinglist',username=name)}}">{{ _("following of %(n)s",n = name) }}</a>
-    </li>
-</ul>
-{%- endmacro %}
-
-{% macro other(user) -%}
-{% if g.user.is_authenticated and g.user.username != user.username %}
-<div class="list-group-item">
-    <span class="text-right" style="display:block">
-        {% if user in g.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 %}
-{%- endmacro %}
-
-{% macro mine(user) -%}
-{% if g.user.is_authenticated and g.user.username == user.username and not user.is_confirmed %}
-<div class="alert alert-info" style="padding:6px;font-size:12px;margin-top:10px;">
-    <button type="button" class="close" data-dismiss="alert" aria-label="Close">
-        <span aria-hidden="true">&times;</span>
-    </button>
-    {{ _("You haven't confirm your account,Please confirmed") }}
-    <span id="email-confirm" class="btn btn-info btn-sm" style="padding:2px 5px;">{{ _('Activate  Account')}}</span>
-</div>
-{% endif %}
-{%- endmacro %}
-
-
-{% macro user_title(user) -%}
-{% set setting = user.setting %}
-{% set info = user.info %}
-<div class="list-group">
-    <div class="media list-group-item">
-        <div class="media-left">
-            {{ link.user_avatar(user)}}
-        </div>
-        <div class="media-body">
-            <h4 class="media-heading">
-                {{ user.username}}
-                {%- set _a = (setting.online_status == 1 and user.username | is_online) %}
-                {%- set _b = (setting.online_status == 2 and g.user.is_authenticated and user.username | is_online) %}
-                {%- if _a or _b %}
-                <span class="online">{{ _('ONLINE') }} </span>
-                {%- else %}
-                <span class="online">{{ _('OUTLINE') }}</span>
-                {% endif -%}
-            </h4>
-            <small style="color:#999">
-                <span>第{{ user.id }}号会员</span>/
-                <span>{{user.register_time | timesince }}</span>
-                <br/>
-                <span>{{ user.topics.count() }}篇主题</span> |
-                <span>{{ user.replies.count() }}条回复</span>
-            </small>
-        </div>
-        <blockquote style="font-size:14px;">
-            <p>{{ info.word }}</p>
-            <footer>Someone famous in <cite title="Source Title">{{ info.school }}</cite></footer>
-        </blockquote>
-        <table  style="font-size:12px;width:100%" align="center">
-            <tr>
-                <td align="center" style="border-right:1px solid #AACCEE;"><p>1</p>积分</td>
-                <td align="center" style="border-right:1px solid #AACCEE;"><p>{{ user.followers.count()}}</p>粉丝</td>
-                <td align="center"><p>{{ user.following_users.count() }}</p>关注</td>
-            </tr>
-        </table>
-    </div>
-    {{ other(user) }}
-    {{ mine(user) }}
-</div>
-{%- endmacro %}

+ 0 - 43
templates/mine/_reply_macro.html

@@ -1,43 +0,0 @@
-<div class="panel panel-default">
-    <div class="panel-heading clearfix">
-        <span style="float:right">
-            {{_("Sort:")}}
-            <div class="btn-group btn-group-xs" role="group">
-                {% if request.args.get('orderby') == 'like' %}
-                <a href="{{ url_for('mine.replylist',orderby='publish')}}" class="btn btn-default">{{_('time')}}</a>
-                <a href="{{ url_for('mine.replylist',orderby='like')}}" class="btn btn-default active">{{_('likers')}}</a>
-                {% else %}
-                <a href="" class="btn btn-default active">{{_('time')}}</a>
-                <a href="{{ url_for('mine.replylist',orderby='like')}}" class="btn btn-default">{{_('likers')}}</a>
-                {% endif %}
-            </div>
-        </span>
-    </div>
-    {% if replies.items %}
-    {% if setting.rep_list == 1 or (g.user.is_authenticated and setting.rep_list == 2) or (g.user.is_authenticated and g.user.username == g.user_url) %}
-    {% for reply in replies.items %}
-    <div class="panel-body" style="border-bottom:1px solid #eee">
-        <span style="font-size:12px;color:#999;">
-            {{_('replied %(title)s created by %(author)s',title=link_base.topic(reply.topic),author = link_base.user(reply.author))}}
-        </span>
-        <p>{{ reply.content | safe_clean }}</p>
-        <span style="font-size:12px;color:#999;">
-            {{_('replied time:')}}{{ reply.publish | timesince }}
-        </span>
-    </div>
-    {% endfor  %}
-    {{ p_footer(replies,'user.reply')}}
-    {% else %}
-    <div class="panel-body" style="border:1px dashed #337ab7;margin:5px;">
-        <span class="text-center" style="display:block;width:100%;color:#999">
-            <span class="glyphicon glyphicon-lock" aria-hidden="true" style="font-size:16px;"></span>
-            {{_("Due to user's setting,the list have been hidden.")}}
-        </span>
-    </div>
-    {% endif %}
-    {% else %}
-    <div class="panel-body text-center">
-        {{_('No Reply')}}
-    </div>
-    {% endif %}
-</div>

+ 0 - 42
templates/mine/_topic_macro.html

@@ -1,42 +0,0 @@
-<div class="panel panel-default">
-    <div class="panel-heading clearfix">
-        <span style="float:right">
-            {{_('Sort:')}}
-            <div class="btn-group btn-group-xs" role="group">
-                {% if request.args.get('orderby') == 'vote' %}
-                <a href="{{ url_for('mine.topiclist',orderby='publish')}}" class="btn btn-default">{{_('time')}}</a>
-                <a href="{{ url_for('mine.topiclist',orderby='vote')}}" class="btn btn-default active">{{_('vote')}}</a>
-                {% else %}
-                <a href="" class="btn btn-default active">{{_('time')}}</a>
-                <a href="{{ url_for('mine.topiclist',orderby='vote')}}" class="btn btn-default">{{_('vote')}}</a>
-                {% endif %}
-            </div>
-        </span>
-    </div>
-    {% if topics.items %}
-    {% if setting.topic_list == 1 or (setting.topic_list == 2 and g.user.is_authenticated) or (g.user.is_authenticated and g.user.username == g.user_url) %}
-    {% for topic in topics.items %}
-    <div class="panel-body" style="border-bottom:1px solid #eee;font-size:16px;">
-        <span style="color:#666">{{ topic.board.board }}</span>
-        {{ link_base.topic(topic) }}
-        <div style="font-size:12px;color:#999;">
-            {{_('create time:')}}{{ topic.publish | timesince }}
-            · {{ _('the last reply published by %(author)s',author=link_base.user(topic.author.username))}}
-        </div>
-    </div>
-    {% endfor  %}
-    {{ p_footer(topics,'user.topic')}}
-    {% else %}
-    <div class="panel-body" style="border:1px dashed #337ab7;margin:5px;">
-        <span class="text-center" style="display:block;width:100%;color:#999">
-            <span class="glyphicon glyphicon-lock" aria-hidden="true" style="font-size:16px;"></span>
-            {{_("Due to user's setting,the list have been hidden.")}}
-        </span>
-    </div>
-    {% endif %}
-    {% else %}
-    <div class="panel-body text-center">
-        {{_('No Topic')}}
-    </div>
-    {% endif %}
-</div>

+ 0 - 21
templates/mine/collect_list.html

@@ -1,21 +0,0 @@
-{% extends 'base/base.html' %}
-{% block content %}
-{% from 'mine/_macro.html' import user_title,nav_list %}
-{{ breadcrumb(hrefs={_('UserList'):url_for('user.list')},active=user.username) }}
-<div class="row">
-    <div class="col-md-3" style="padding-right:0;">
-        {% set setting = user.setting %}
-        {{ user_title(user) }}
-        <div  style="background:#fff;border:1px solid #ddd;border-radius:5px">
-            {% if g.user.is_authenticated and g.user.username == user.username %}
-            {{ nav_list('collect',user.username)}}
-            {% else %}
-            {{ nav_list('collect',user.username)}}
-            {% endif %}
-        </div>
-    </div>
-    <div class="col-md-9">
-        {% include "mine/_collect_macro.html" %}
-    </div>
-</div>
-{% endblock %}

+ 0 - 21
templates/mine/follower_list.html

@@ -1,21 +0,0 @@
-{% extends 'base/base.html' %}
-{% block content %}
-{% from 'mine/_macro.html' import user_title,nav_list %}
-{{ breadcrumb(hrefs={_('UserList'):url_for('user.list')},active=user.username) }}
-<div class="row">
-    <div class="col-md-3" style="padding-right:0;">
-        {% set setting = user.setting %}
-        {{ user_title(user) }}
-        <div  style="background:#fff;border:1px solid #ddd;border-radius:5px">
-            {% if g.user.is_authenticated and g.user.username == user.username %}
-            {{ nav_list('collect',user.username)}}
-            {% else %}
-            {{ nav_list('collect',user.username)}}
-            {% endif %}
-        </div>
-    </div>
-    <div class="col-md-9">
-        {% include "mine/_collect_macro.html" %}
-    </div>
-</div>
-{% endblock %}

+ 0 - 21
templates/mine/following_list.html

@@ -1,21 +0,0 @@
-{% extends 'base/base.html' %}
-{% block content %}
-{% from 'mine/_macro.html' import user_title,nav_list %}
-{{ breadcrumb(hrefs={_('UserList'):url_for('user.list')},active=user.username) }}
-<div class="row">
-    <div class="col-md-3" style="padding-right:0;">
-        {% set setting = user.setting %}
-        {{ user_title(user) }}
-        <div  style="background:#fff;border:1px solid #ddd;border-radius:5px">
-            {% if g.user.is_authenticated and g.user.username == user.username %}
-            {{ nav_list('collect',user.username)}}
-            {% else %}
-            {{ nav_list('collect',user.username)}}
-            {% endif %}
-        </div>
-    </div>
-    <div class="col-md-9">
-        {% include "mine/_collect_macro.html" %}
-    </div>
-</div>
-{% endblock %}

+ 0 - 21
templates/mine/reply_list.html

@@ -1,21 +0,0 @@
-{% extends 'base/base.html' %}
-{% block content %}
-{% from 'mine/_macro.html' import user_title,nav_list %}
-{{ breadcrumb(hrefs={_('UserList'):url_for('user.list')},active=user.username) }}
-<div class="row">
-    <div class="col-md-3" style="padding-right:0;">
-        {% set setting = user.setting %}
-        {{ user_title(user) }}
-        <div  style="background:#fff;border:1px solid #ddd;border-radius:5px">
-            {% if g.user.is_authenticated and g.user.username == user.username %}
-            {{ nav_list('reply',user.username)}}
-            {% else %}
-            {{ nav_list('reply',user.username)}}
-            {% endif %}
-        </div>
-    </div>
-    <div class="col-md-9">
-        {% include "mine/_reply_macro.html" %}
-    </div>
-</div>
-{% endblock %}

+ 0 - 22
templates/mine/topic_list.html

@@ -1,22 +0,0 @@
-{% extends 'base/base.html' %}
-{% block content %}
-{% from 'mine/_macro.html' import user_title,nav_list %}
-{{ breadcrumb(hrefs={_('UserList'):url_for('user.list')},active=user.username) }}
-<div class="row">
-    <div class="col-md-3" style="padding-right:0;">
-        {% set setting = user.setting %}
-        {{ user_title(user) }}
-        <div  style="background:#fff;border:1px solid #ddd;border-radius:5px">
-            {% if g.user.is_authenticated and g.user.username == user.username %}
-            {{ nav_list('topic',user.username)}}
-            {% else %}
-            {{ nav_list('topic',user.username)}}
-            {% endif %}
-        </div>
-    </div>
-    <div class="col-md-9">
-        {% include "mine/_topic_macro.html" %}
-    </div>
-</div>
-{% endblock %}
-

+ 1 - 0
templates/tag/tag.html

@@ -9,6 +9,7 @@
         {{ title(tag) }}
       </div>
       {% include "topic/_topic.html" %}
+      {{ p_footer(topics,'tag.tag',dict(name=tag.name))}}
     </div>
   </div>
   <div class="col-md-3" style="padding-left:0">

+ 1 - 1
templates/topic/_list_macro.html

@@ -43,7 +43,7 @@
   <div class="media-body">
     <div class="media-heading">
       {% if topic.is_top %}
-      <span><i class="fa fa-pushpin text-danger">&nbsp</i></span>
+      <span><i class="fa fa-thumb-tack text-danger">&nbsp</i></span>
       {% endif %}
       <a href="{{url_for('topic.topic',topicId=topic.id)}}" style="color:#555">{{ topic.title }}</a>
     </div>

+ 1 - 5
templates/topic/item/body.html

@@ -1,7 +1,3 @@
 <div class="panel-body topic-content">
-  {% if topic.is_markdown %}
-  {{ topic.content | markdown }}
-  {% else %}
-  {{ topic.content | safe_clean }}
-  {% endif %}
+  {{ topic.text }}
 </div>

+ 1 - 3
templates/topic/item/heading.html

@@ -8,8 +8,6 @@
     </small>
   </div>
   <div class="media-right">
-    <a href="{{ url_for('user.user',username=topic.author.username)}}">
-      <img class="media-object img-circle" src="{{ url_for('avatar',text=author.username)}}" style="width:56px;height:56px">
-    </a>
+    {{ link.user_avatar(topic.author) }}
   </div>
 </div>

+ 2 - 2
templates/topic/reply/_macro.html

@@ -2,12 +2,12 @@
 {% if reply.is_liked() %}
 <a href="javascript:void(0);" style="padding:0" class="like-reply btn btn-sm like-active" data-id="{{ reply.id}}" title="取消赞">
   <i class="fa fa-thumbs-up"></i>
-  <span class="reply-count">{{ reply.likers.count() }}</span>
+  <span class="reply-count">{{ reply.liker_count }}</span>
 </a>
 {% else %}
 <a href="javascript:void(0);" style="padding:0" class="like-reply btn btn-sm like-no-active" data-id="{{ reply.id}}" title="赞">
   <i class="fa fa-thumbs-up"></i>
-  <span class="reply-count">{{ reply.likers.count() }}</span>
+  <span class="reply-count">{{ reply.liker_count }}</span>
 </a>
 {% endif %}
 {%- endmacro %}

+ 1 - 0
templates/topic/topic_list.html

@@ -5,6 +5,7 @@
   <div class="col-md-9">
     <div class="panel panel-default">
       {% include "topic/_topic.html" %}
+      {{ p_footer(topics,'topic.list')}}
     </div>
   </div>
   <div class="col-md-3" style="padding-left:0">

+ 1 - 1
templates/user/followers.html

@@ -23,6 +23,6 @@
     {{_('No Follower')}}
   </div>
   {% endfor  %}
-  {{ p_footer(followers,'user.follower')}}
+  {{ p_footer(followers,'user.follower',dict(username=user.username)) }}
 </div>
 {%- endblock mmm %}

+ 1 - 1
templates/user/info.html

@@ -25,7 +25,7 @@
 {%- endmacro %}
 
 {% macro online(user) -%}
-{% if user | is_online %}
+{% if user.is_online %}
 <span class="online">{{ _('ONLINE') }} </span>
 {%- else %}
 <span class="online">{{ _('OUTLINE') }}</span>

+ 3 - 3
templates/user/replies.html

@@ -15,14 +15,14 @@
     </span>
   </div>
   {% if replies_is_allowed %}
-  {{ itemlist(replies) }}
+  {{ itemlist(replies,user) }}
   {% else %}
   {{ not_allowed() }}
   {% endif %}
 </div>
 {%- endblock mmm %}
 
-{% macro itemlist(replies) -%}
+{% macro itemlist(replies,user) -%}
 {% for reply in replies.items %}
 <div class="panel-body" style="border-bottom:1px solid #eee">
   <span style="font-size:12px;color:#999;">
@@ -38,5 +38,5 @@
   {{_('No Reply')}}
 </div>
 {% endfor  %}
-{{ p_footer(replies,'user.reply')}}
+{{ p_footer(replies,'user.reply',dict(username=user.username))}}
 {%- endmacro %}

+ 3 - 3
templates/user/user.html

@@ -15,14 +15,14 @@
     </span>
   </div>
   {% if topic_is_allowed %}
-  {{ itemlist(topics) }}
+  {{ itemlist(topics,user) }}
   {% else %}
   {{ not_allowed() }}
   {% endif %}
 </div>
 {%- endblock mmm %}
 
-{% macro itemlist(topics) -%}
+{% macro itemlist(topics,user) -%}
 {% for topic in topics.items %}
 <div class="panel-body" style="border-bottom:1px solid #eee;font-size:16px;">
   <span style="color:#666">{{ topic.board.board }}</span>
@@ -37,5 +37,5 @@
   {{_('No Topic')}}
 </div>
 {% endfor  %}
-{{ p_footer(topics,'user.topic')}}
+{{ p_footer(topics,'user.topic',dict(username=user.username))}}
 {%- endmacro %}

+ 323 - 0
upgrade.py

@@ -0,0 +1,323 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# **************************************************************************
+# Copyright © 2017 jianglin
+# File Name: upgrade.py
+# Author: jianglin
+# Email: xiyang0807@gmail.com
+# Created: 2017-03-29 23:28:43 (CST)
+# Last Update:星期四 2017-3-30 14:42:34 (CST)
+#          By:
+# Description:
+# **************************************************************************
+from datetime import datetime
+from sqlalchemy import create_engine
+from sqlalchemy.orm import sessionmaker
+
+engine1 = create_engine('postgresql://postgres:password@localhost/forums1')
+session1 = sessionmaker(bind=engine1)()
+
+engine2 = create_engine('postgresql://postgres:password@localhost/forums')
+session2 = sessionmaker(bind=engine2)()
+
+
+def upgrade_board():
+    _boards = session1.execute('select * from boards;')
+    for i in _boards:
+        print(i)
+        board = session2.execute(
+            "insert into boards (id,name,description) values (:1,:2,:3) RETURNING *",
+            {
+                '1': i.id,
+                '2': i.board,
+                '3': i.description
+            })
+    session2.commit()
+    _parent_boards = session1.execute(
+        'select parent_board from boards group by parent_board;')
+    n = 9
+    for i in _parent_boards:
+        p = session2.execute(
+            "insert into boards (id,name,description) values (:1,:2,:3) RETURNING *",
+            {
+                '1': n,
+                '2': i.parent_board,
+                '3': i.parent_board
+            })
+        session2.commit()
+        p = p.fetchone()
+        _boards = session1.execute(
+            "select * from boards where parent_board = :parent_board", {
+                "parent_board": p.name
+            })
+        for j in _boards:
+            q = session2.execute(
+                "update boards set parent_id = :1 where id = :2", {
+                    '1': p.id,
+                    '2': j.id
+                })
+            session2.commit()
+        n += 1
+
+
+def upgrade_reply():
+    _replies = session1.execute('select * from replies')
+    for i in _replies:
+        print(i)
+        reply = session2.execute(
+            'insert into replies (id,content,created_at,updated_at,author_id,topic_id) values (:1,:2,:3,:4,:5,:6)',
+            {
+                '1': i.id,
+                '2': i.content,
+                '3': i.publish,
+                '4': i.publish,
+                '5': i.author_id,
+                '6': i.topic_id
+            })
+    session2.commit()
+
+
+def upgrade_topic():
+    _topics = session1.execute('select * from topics')
+    for t in _topics:
+        print(t)
+        content_type = '1' if t.is_markdown else '0'
+        topic = session2.execute(
+            "insert into topics (id,title,content,content_type,created_at,updated_at,\
+            is_good,is_top,author_id,board_id) values (:1,:2,:3,:4,:5,:6,:7,:8,:9,:10) RETURNING *",
+            {
+                '1': t.id,
+                '2': t.title,
+                '3': t.content,
+                '4': content_type,
+                '5': t.publish,
+                '6': t.updated,
+                '7': t.is_good,
+                '8': t.is_top,
+                '9': t.author_id,
+                '10': t.board_id
+            })
+    session2.commit()
+
+
+def upgrade_user():
+    results = session1.execute('select * from users;')
+    for i in results:
+        print(i)
+        p = session2.execute(
+            "insert into users (id,username,password,email,is_confirmed,\
+            is_superuser,register_time) values (:1,:2,:3,:4,:5,:6,:7) RETURNING *",
+            {
+                '1': i.id,
+                '2': i.username,
+                '3': i.password,
+                '4': i.email,
+                '5': i.is_confirmed,
+                '6': i.is_superuser,
+                '7': i.register_time
+            })
+        session2.commit()
+        p = p.fetchone()
+        _info = session1.execute('select * from userinfor where id = :id', {
+            'id': i.infor_id
+        })
+        _info = _info.fetchone()
+        info = session2.execute(
+            "insert into userinfo (id,avatar,school,word,introduce,user_id) values (:1,:2,:3,:4,:5,:6) RETURNING *",
+            {
+                '1': _info.id,
+                '2': _info.avatar or '',
+                '3': _info.school or '',
+                '4': _info.word or '',
+                '5': _info.introduce or '',
+                '6': p.id
+            })
+        session2.commit()
+        _setting = session1.execute('select * from usersetting where id = :id',
+                                    {
+                                        'id': i.setting_id
+                                    })
+        _setting = _setting.fetchone()
+        setting = session2.execute(
+            "insert into usersetting (id,online_status,topic_list,rep_list,\
+             ntb_list,collect_list,locale,timezone,user_id) values (:1,:2,:3,:4,:5,:6,:7,:8,:9) RETURNING *",
+            {
+                '1': _setting.id,
+                '2': _setting.online_status,
+                '3': _setting.topic_list,
+                '4': _setting.rep_list,
+                '5': _setting.ntb_list,
+                '6': _setting.collect_list,
+                '7': _setting.locale,
+                '8': _setting.timezone,
+                '9': p.id
+            })
+        session2.commit()
+
+
+def upgrade_tags():
+    _tags = session1.execute('select * from tags;')
+    for i in _tags:
+        p = session2.execute(
+            "insert into tags (id,name,description) values (:1,:2,:3) RETURNING *",
+            {
+                '1': i.id,
+                '2': i.tagname,
+                '3': i.summary or i.tagname
+            })
+    session2.commit()
+    _tag_parents = session1.execute('select * from tags_parents;')
+    for i in _tag_parents:
+        session2.execute('update tags set parent_id = :1 where id = :2', {
+            '1': i.parent_id,
+            '2': i.tag_id
+        })
+    session2.commit()
+    _tag_topics = session1.execute('select * from tag_topic;')
+    for i in _tag_topics:
+        p = session2.execute(
+            "insert into tag_topic (tag_id,topic_id) values (:1,:2) RETURNING *",
+            {
+                '1': i.tags_id,
+                '2': i.topics_id,
+            })
+    session2.commit()
+
+
+def upgrade_collect():
+    _collects = session1.execute('select * from collects;')
+    for i in _collects:
+        print(i)
+        p = session2.execute(
+            "insert into collects (id,name,description,is_hidden,author_id,created_at,updated_at) values (:1,:2,:3,:4,:5,:6,:7) RETURNING *",
+            {
+                '1': i.id,
+                '2': i.name,
+                '3': i.description,
+                '4': i.is_privacy,
+                '5': i.author_id,
+                '6': datetime.now(),
+                '7': datetime.now()
+            })
+    session2.commit()
+    _collect_topics = session1.execute('select * from collect_topic;')
+    for i in _collect_topics:
+        p = session2.execute(
+            "insert into topic_collect (collect_id,topic_id) values (:1,:2) RETURNING *",
+            {
+                '1': i.collect_id,
+                '2': i.topic_id
+            })
+    session2.commit()
+
+
+def upgrade_like():
+    _likes = session1.execute('select * from likes;')
+    for i in _likes:
+        p = session2.execute(
+            "insert into reply_liker (reply_id,liker_id) values (:1,:2) RETURNING *",
+            {
+                '1': i.reply_id,
+                '2': i.author_id
+            })
+    session2.commit()
+
+
+def upgrade_follow():
+    _followers = session1.execute('select * from follows;')
+    for i in _followers:
+        print(i)
+        if i.following_user_id:
+            p = session2.execute(
+                "insert into user_follower (follower_id,user_id) values (:1,:2) RETURNING *",
+                {
+                    '1': i.follower_id,
+                    '2': i.following_user_id
+                })
+        elif i.following_tag_id:
+            p = session2.execute(
+                "insert into tag_follower (follower_id,tag_id) values (:1,:2) RETURNING *",
+                {
+                    '1': i.follower_id,
+                    '2': i.following_tag_id
+                })
+        elif i.followinf_topic_id:
+            p = session2.execute(
+                "insert into topic_follower (follower_id,topic_id) values (:1,:2) RETURNING *",
+                {
+                    '1': i.follower_id,
+                    '2': i.followinf_topic_id
+                })
+        elif i.following_collect_id:
+            p = session2.execute(
+                "insert into collect_follower (follower_id,collect_id) values (:1,:2) RETURNING *",
+                {
+                    '1': i.follower_id,
+                    '2': i.following_collect_id
+                })
+    session2.commit()
+
+
+def upgrade_setval():
+    '''
+    psql (9.6.1)
+    输入 "help" 来获取帮助信息.
+
+    forums=# \d
+                        关联列表
+    架构模式 |        名称        |  类型  |  拥有者
+    ----------+--------------------+--------+----------
+    public   | boards             | 数据表 | postgres
+    public   | boards_id_seq      | 序列数 | postgres
+    public   | collect_follower   | 数据表 | postgres
+    public   | collects           | 数据表 | postgres
+    public   | collects_id_seq    | 序列数 | postgres
+    public   | replies            | 数据表 | postgres
+    public   | replies_id_seq     | 序列数 | postgres
+    public   | reply_liker        | 数据表 | postgres
+    public   | tag_follower       | 数据表 | postgres
+    public   | tag_topic          | 数据表 | postgres
+    public   | tags               | 数据表 | postgres
+    public   | tags_id_seq        | 序列数 | postgres
+    public   | topic_collect      | 数据表 | postgres
+    public   | topic_follower     | 数据表 | postgres
+    public   | topics             | 数据表 | postgres
+    public   | topics_id_seq      | 序列数 | postgres
+    public   | user_follower      | 数据表 | postgres
+    public   | userinfo           | 数据表 | postgres
+    public   | userinfo_id_seq    | 序列数 | postgres
+    public   | users              | 数据表 | postgres
+    public   | users_id_seq       | 序列数 | postgres
+    public   | usersetting        | 数据表 | postgres
+    public   | usersetting_id_seq | 序列数 | postgres
+    (23 行记录)
+
+    forums=#
+    '''
+    session2.execute(
+        "select setval('boards_id_seq',(select max(id) from boards))")
+    session2.execute(
+        "select setval('collects_id_seq',(select max(id) from collects))")
+    session2.execute("select setval('tags_id_seq',(select max(id) from tags))")
+    session2.execute(
+        "select setval('topics_id_seq',(select max(id) from topics))")
+    session2.execute(
+        "select setval('replies_id_seq',(select max(id) from replies))")
+    session2.execute(
+        "select setval('users_id_seq',(select max(id) from users))")
+    session2.execute(
+        "select setval('userinfo_id_seq',(select max(id) from userinfo))")
+    session2.execute(
+        "select setval('usersetting_id_seq',(select max(id) from usersetting))")
+
+
+if __name__ == '__main__':
+    upgrade_board()
+    upgrade_user()
+    upgrade_topic()
+    upgrade_collect()
+    upgrade_tags()
+    upgrade_reply()
+    upgrade_like()
+    upgrade_follow()
+    upgrade_setval()