docx.py 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. from flask import Flask, Blueprint, render_template, abort, redirect, url_for, flash, make_response
  2. from flask_wtf import FlaskForm
  3. from flask_pagedown import PageDown
  4. from flask_pagedown.fields import PageDownField
  5. from flask_login import login_required, current_user
  6. from wtforms import TextAreaField, StringField, SubmitField
  7. from wtforms.validators import DataRequired, Length
  8. from typing import Optional
  9. import bleach
  10. from markdown import markdown
  11. from view.base import App
  12. from sql.base import DBBit
  13. from core.blog import BlogArticle, load_blog_by_id
  14. from core.user import User
  15. from core.comment import Comment
  16. from core.archive import load_archive_by_name
  17. docx = Blueprint("docx", __name__)
  18. app: Optional[Flask] = None
  19. allow_tag = ['a', 'abbr', 'acronym', 'b', 'br', 'blockquote', 'code', 'em', 'i', 'li', 'ol', 'pre', 'strong', 'small',
  20. 'ul', 'h1', 'h2', 'h3', 'h4', 'h5' 'h6', 'p']
  21. class WriteBlogForm(FlaskForm):
  22. title = StringField("标题", validators=[DataRequired(), Length(1, 10)])
  23. subtitle = StringField("副标题", validators=[DataRequired(), Length(1, 10)])
  24. archive = StringField("归档", validators=[DataRequired(), Length(1, 10)])
  25. context = PageDownField("博客内容", validators=[DataRequired()])
  26. submit = SubmitField("提交博客")
  27. class WriteCommentForm(FlaskForm):
  28. context = TextAreaField(validators=[DataRequired()])
  29. submit = SubmitField("评论")
  30. @docx.route('/<int:page>')
  31. def docx_page(page: int = 1):
  32. if page < 1:
  33. abort(404)
  34. return
  35. blog_list = BlogArticle.get_blog_list(limit=20, offset=(page - 1) * 20)
  36. max_page = App.get_max_page(BlogArticle.get_blog_count(), 20)
  37. page_list = App.get_page("docx.docx_page", page, max_page)
  38. return render_template("docx/docx.html",
  39. blog_list=blog_list,
  40. is_top=DBBit.BIT_1,
  41. page_list=page_list,
  42. form=WriteBlogForm(),
  43. show_delete=current_user.check_role("DeleteBlog"))
  44. @docx.route('/<int:archive>/<int:page>')
  45. def archive_page(archive: int, page: int = 1):
  46. if page < 1:
  47. abort(404)
  48. return
  49. blog_list = BlogArticle.get_blog_list(archive_id=archive, limit=20, offset=(page - 1) * 20)
  50. max_page = App.get_max_page(BlogArticle.get_blog_count(archive_id=archive), 20)
  51. page_list = App.get_page("docx.archive_page", page, max_page)
  52. return render_template("docx/docx.html",
  53. blog_list=blog_list,
  54. is_top=DBBit.BIT_1,
  55. page_list=page_list,
  56. form=None)
  57. @docx.route('/article/<int:blog_id>')
  58. def article_page(blog_id: int):
  59. article = load_blog_by_id(blog_id)
  60. if article is None:
  61. abort(404)
  62. return
  63. return render_template("docx/article.html",
  64. article=article,
  65. archive_list=article.archive,
  66. form=WriteCommentForm(),
  67. show_delete=current_user.check_role("DeleteComment"),
  68. show_email=current_user.check_role("ReadUserInfo"))
  69. @docx.route('/down/<int:blog_id>')
  70. def article_down_page(blog_id: int):
  71. article = load_blog_by_id(blog_id)
  72. if article is None:
  73. abort(404)
  74. return
  75. response = make_response(article.context)
  76. response.headers["Content-Disposition"] = f"attachment; filename={article.title}.html"
  77. return response
  78. @docx.route('/comment/<int:blog>', methods=["POST"])
  79. @login_required
  80. def comment_page(blog: int):
  81. form = WriteCommentForm()
  82. if form.validate_on_submit():
  83. auth: User = current_user
  84. if not auth.check_role("WriteComment"): # 检查是否具有权限
  85. abort(403)
  86. return
  87. context = form.context.data
  88. if Comment(None, blog, auth, context).create():
  89. flash("评论成功")
  90. else:
  91. flash("评论失败")
  92. return redirect(url_for("docx.article_page", blog_id=blog))
  93. abort(404)
  94. @docx.route('/create-docx', methods=["POST"])
  95. @login_required
  96. def create_docx_page():
  97. form = WriteBlogForm()
  98. if form.validate_on_submit():
  99. auth: User = current_user
  100. if not auth.check_role("WriteBlog"): # 检查是否具有写入权限
  101. abort(403)
  102. return
  103. title = form.title.data
  104. subtitle = form.subtitle.data
  105. archive = set(str(form.archive.data).replace(" ", "").split(";"))
  106. archive_list = []
  107. for f in archive:
  108. f_ = load_archive_by_name(f)
  109. if f_ is not None:
  110. archive_list.append(f_)
  111. context = bleach.linkify(
  112. bleach.clean(
  113. markdown(form.context.data, output_format='html'), tags=allow_tag, strip=True))
  114. if BlogArticle(None, current_user, title, subtitle, context, archive=archive_list).create():
  115. flash(f"博客 {title} 发表成功")
  116. else:
  117. flash(f"博客 {title} 发表失败")
  118. return redirect(url_for("docx.docx_page", page=1))
  119. abort(404)
  120. @docx.route("delete/<int:blog_id>")
  121. @login_required
  122. def delete_blog_page(blog_id: int):
  123. if not current_user.check_role("DeleteBlog"):
  124. abort(403)
  125. return
  126. if BlogArticle(blog_id, None, None, None, None).delete():
  127. flash("博文删除成功")
  128. else:
  129. flash("博文删除失败")
  130. return redirect(url_for("docx.docx_page", page=1))
  131. @docx.route("delete_comment/<int:comment_id>")
  132. @login_required
  133. def delete_comment_page(comment_id: int):
  134. if not current_user.check_role("DeleteComment"):
  135. abort(403)
  136. return
  137. if Comment(comment_id, None, None, None).delete():
  138. flash("博文评论成功")
  139. else:
  140. flash("博文评论失败")
  141. return redirect(url_for("docx.docx_page", page=1))
  142. @docx.context_processor
  143. def inject_base():
  144. return {"top_nav": ["", "", "active", "", "", ""]}
  145. class DocxApp(App):
  146. def __init__(self, import_name):
  147. super(DocxApp, self).__init__(import_name)
  148. global app
  149. app = self._app
  150. self.pagedown = PageDown()
  151. self.pagedown.init_app(app)
  152. app.register_blueprint(docx, url_prefix="/docx")