docx.py 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. from flask import Blueprint, render_template, abort, redirect, url_for, flash, make_response, g, request
  2. from flask_wtf import FlaskForm
  3. from flask_login import login_required, current_user
  4. from wtforms import HiddenField, TextAreaField, StringField, SelectMultipleField, SubmitField, ValidationError
  5. from wtforms.validators import DataRequired, Length
  6. from typing import Optional
  7. import app
  8. from sql.base import DBBit
  9. from object.blog import BlogArticle, load_blog_by_id
  10. from object.comment import Comment
  11. from object.archive import load_archive_by_id, Archive
  12. docx = Blueprint("docx", __name__)
  13. class EditorMD(FlaskForm):
  14. context = TextAreaField("博客内容", validators=[DataRequired(message="必须输入博客文章")])
  15. class WriteBlogForm(EditorMD):
  16. title = StringField("标题", description="博文主标题",
  17. validators=[
  18. DataRequired(message="必须填写标题"),
  19. Length(1, 20, message="标题长度1-20个字符")])
  20. subtitle = StringField("副标题", description="博文副标题",
  21. validators=[Length(-1, 20, message="副标题长度20个字符以内")])
  22. archive = SelectMultipleField("归档", coerce=int)
  23. submit = SubmitField("提交博客")
  24. def __init__(self, default: bool = False, **kwargs):
  25. super().__init__(**kwargs)
  26. if default:
  27. self.context.data = "# Blog Title\n## Blog subtitle\nHello, World"
  28. archive = Archive.get_archive_list()
  29. self.archive_res = []
  30. self.archive_choices = [(-1, "None")]
  31. for i in archive:
  32. self.archive_res.append(i[0])
  33. self.archive_choices.append((i[0], f"{i[1]} ({i[3]})"))
  34. self.archive.choices = self.archive_choices
  35. def validate_archive(self, field):
  36. if -1 in field.data:
  37. if len(field.data) != 1:
  38. raise ValidationError("归档指定错误(none归档不能和其他归档同时被指定)")
  39. else:
  40. for i in field.data:
  41. if i not in self.archive_res:
  42. raise ValidationError("错误的归档被指定")
  43. class UpdateBlogForm(EditorMD):
  44. blog_id = HiddenField("ID", validators=[DataRequired()])
  45. submit = SubmitField("更新博客")
  46. def __init__(self, blog: Optional[BlogArticle] = None, **kwargs):
  47. super().__init__(**kwargs)
  48. if blog is not None:
  49. self.blog_id.data = blog.blog_id
  50. self.context.data = blog.context
  51. class WriteCommentForm(FlaskForm):
  52. context = TextAreaField("", description="评论正文",
  53. validators=[DataRequired(message="请输入评论的内容"),
  54. Length(1, 100, message="请输入1-100个字的评论")])
  55. submit = SubmitField("评论")
  56. def __load_docx_page(page: int, form: WriteBlogForm):
  57. if page < 1:
  58. app.HBlogFlask.print_user_opt_fail_log(f"Load docx list with error page({page})")
  59. abort(404)
  60. return
  61. blog_list = BlogArticle.get_blog_list(limit=20, offset=(page - 1) * 20)
  62. max_page = app.HBlogFlask.get_max_page(BlogArticle.get_blog_count(), 20)
  63. page_list = app.HBlogFlask.get_page("docx.docx_page", page, max_page)
  64. app.HBlogFlask.print_load_page_log(f"docx list (page: {page})")
  65. return render_template("docx/docx.html",
  66. page=page,
  67. blog_list=blog_list,
  68. is_top=DBBit.BIT_1,
  69. page_list=page_list,
  70. form=form,
  71. show_delete=current_user.check_role("DeleteBlog"))
  72. @docx.route('/')
  73. def docx_page():
  74. page = int(request.args.get("page", 1))
  75. return __load_docx_page(page, WriteBlogForm(True))
  76. @docx.route('/archive')
  77. def archive_page():
  78. page = int(request.args.get("page", 1))
  79. archive = int(request.args.get("archive", 1))
  80. if page < 1:
  81. app.HBlogFlask.print_user_opt_fail_log(f"Load archive-docx list with error page({page}) archive: {archive}")
  82. abort(404)
  83. return
  84. blog_list = BlogArticle.get_blog_list(archive_id=archive, limit=20, offset=(page - 1) * 20)
  85. max_page = app.HBlogFlask.get_max_page(BlogArticle.get_blog_count(archive_id=archive), 20)
  86. page_list = app.HBlogFlask.get_page("docx.archive_page", page, max_page)
  87. app.HBlogFlask.print_load_page_log(f"archive-docx list (archive-id: {archive} page: {page})")
  88. return render_template("docx/docx.html",
  89. blog_list=blog_list,
  90. is_top=DBBit.BIT_1,
  91. page_list=page_list,
  92. form=None)
  93. def __load_article_page(blog_id: int, form: WriteCommentForm, view: Optional[UpdateBlogForm] = None):
  94. article = load_blog_by_id(blog_id)
  95. if article is None:
  96. app.HBlogFlask.print_user_opt_fail_log(f"Load article with error id({blog_id})")
  97. abort(404)
  98. return
  99. app.HBlogFlask.print_load_page_log(f"article (id: {blog_id})")
  100. if view is None:
  101. view = UpdateBlogForm(article)
  102. return render_template("docx/article.html",
  103. article=article,
  104. archive_list=article.archive,
  105. form=form,
  106. view=view,
  107. can_update=current_user.check_role("WriteBlog"),
  108. show_delete=current_user.check_role("DeleteComment"),
  109. show_email=current_user.check_role("ReadUserInfo"))
  110. @docx.route('/article')
  111. def article_page():
  112. blog_id = int(request.args.get("blog", 1))
  113. return __load_article_page(blog_id, WriteCommentForm())
  114. @docx.route('/article/download')
  115. def article_down_page():
  116. blog_id = int(request.args.get("blog", 1))
  117. article = load_blog_by_id(blog_id)
  118. if article is None:
  119. app.HBlogFlask.print_user_opt_fail_log(f"Download article with error id({blog_id})")
  120. abort(404)
  121. return
  122. response = make_response(article.context)
  123. response.headers["Content-Disposition"] = f"attachment;filename={article.title.encode().decode('latin-1')}.md"
  124. app.HBlogFlask.print_load_page_log(f"download article (id: {blog_id})")
  125. return response
  126. @docx.route('/article/create', methods=["POST"])
  127. @login_required
  128. @app.form_required(WriteBlogForm, "write blog", lambda form: __load_docx_page(int(request.args.get("page", 1)), form))
  129. @app.role_required("WriteBlog", "write blog")
  130. def create_docx_page():
  131. form: WriteBlogForm = g.form
  132. title = form.title.data
  133. subtitle = form.subtitle.data
  134. archive = []
  135. if -1 not in form.archive.data:
  136. for i in form.archive.data:
  137. i = load_archive_by_id(i)
  138. if i is not None:
  139. archive.append(i)
  140. if BlogArticle(None, current_user, title, subtitle, form.context.data, archive=archive).create():
  141. app.HBlogFlask.print_sys_opt_success_log("write blog")
  142. flash(f"博客 {title} 发表成功")
  143. else:
  144. app.HBlogFlask.print_sys_opt_fail_log("write blog")
  145. flash(f"博客 {title} 发表失败")
  146. return redirect(url_for("docx.docx_page", page=1))
  147. @docx.route('/article/update', methods=["POST"])
  148. @login_required
  149. @app.form_required(UpdateBlogForm, "update blog",
  150. lambda form: __load_article_page(int(request.args.get("blog", 1)), WriteCommentForm(), form))
  151. @app.role_required("WriteBlog", "write blog")
  152. def update_docx_page():
  153. form: UpdateBlogForm = g.form
  154. if BlogArticle(form.blog_id.data, None, None, None, None).update(form.context.data):
  155. app.HBlogFlask.print_sys_opt_success_log("update blog")
  156. flash("博文更新成功")
  157. else:
  158. app.HBlogFlask.print_sys_opt_fail_log("update blog")
  159. flash("博文更新失败")
  160. return redirect(url_for("docx.article_page", blog=form.blog_id.data))
  161. @docx.route("/article/delete")
  162. @login_required
  163. @app.role_required("DeleteBlog", "delete blog")
  164. def delete_blog_page():
  165. blog_id = int(request.args.get("blog", -1))
  166. if blog_id == -1:
  167. return abort(400)
  168. if BlogArticle(blog_id, None, None, None, None).delete():
  169. app.HBlogFlask.print_sys_opt_success_log("delete blog")
  170. flash("博文删除成功")
  171. else:
  172. app.HBlogFlask.print_sys_opt_fail_log("delete blog")
  173. flash("博文删除失败")
  174. return redirect(url_for("docx.docx_page", page=1))
  175. @docx.route('/comment/create', methods=["POST"])
  176. @login_required
  177. @app.form_required(WriteCommentForm, "write comment",
  178. lambda form: __load_article_page(int(request.args.get("blog", 1)), form))
  179. @app.role_required("WriteComment", "write comment")
  180. def comment_page():
  181. blog_id = int(request.args.get("blog", 1))
  182. form: WriteCommentForm = g.form
  183. context = form.context.data
  184. if Comment(None, blog_id, current_user, context).create():
  185. app.HBlogFlask.print_user_opt_success_log("comment")
  186. flash("评论成功")
  187. else:
  188. app.HBlogFlask.print_user_opt_error_log("comment")
  189. flash("评论失败")
  190. return redirect(url_for("docx.article_page", blog=blog_id))
  191. @docx.route("/comment/delete")
  192. @login_required
  193. @app.role_required("DeleteComment", "delete comment")
  194. def delete_comment_page():
  195. comment_id = int(request.args.get("comment", 1))
  196. if Comment(comment_id, None, None, None).delete():
  197. app.HBlogFlask.print_sys_opt_success_log("delete comment")
  198. flash("博文评论成功")
  199. else:
  200. app.HBlogFlask.print_sys_opt_fail_log("delete comment")
  201. flash("博文评论失败")
  202. return redirect(url_for("docx.docx_page", page=1))
  203. @docx.context_processor
  204. def inject_base():
  205. return {"top_nav": ["", "", "active", "", "", ""]}