views.py 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654
  1. # -*- coding: utf-8 -*-
  2. """
  3. flaskbb.forum.views
  4. ~~~~~~~~~~~~~~~~~~~
  5. This module handles the forum logic like creating and viewing
  6. topics and posts.
  7. :copyright: (c) 2014 by the FlaskBB Team.
  8. :license: BSD, see LICENSE for more details.
  9. """
  10. from sqlalchemy import asc, desc
  11. from flask import (Blueprint, redirect, url_for, current_app, request, flash,
  12. abort)
  13. from flask_login import login_required, current_user
  14. from flask_babelplus import gettext as _
  15. from flask_allows import Permission, And
  16. from flaskbb.extensions import db, allows
  17. from flaskbb.utils.settings import flaskbb_config
  18. from flaskbb.utils.helpers import (get_online_users, time_diff, time_utcnow,
  19. format_quote, render_template,
  20. do_topic_action)
  21. from flaskbb.utils.requirements import (CanAccessForum, CanAccessTopic,
  22. CanDeletePost, CanDeleteTopic,
  23. CanEditPost, CanPostReply,
  24. CanPostTopic,
  25. IsAtleastModeratorInForum)
  26. from flaskbb.forum.models import (Category, Forum, Topic, Post, ForumsRead,
  27. TopicsRead)
  28. from flaskbb.forum.forms import (NewTopicForm, QuickreplyForm, ReplyForm,
  29. ReportForm, SearchPageForm, UserSearchForm)
  30. from flaskbb.user.models import User
  31. forum = Blueprint("forum", __name__)
  32. @forum.route("/")
  33. def index():
  34. categories = Category.get_all(user=current_user)
  35. # Fetch a few stats about the forum
  36. user_count = User.query.count()
  37. topic_count = Topic.query.count()
  38. post_count = Post.query.count()
  39. newest_user = User.query.order_by(User.id.desc()).first()
  40. # Check if we use redis or not
  41. if not current_app.config["REDIS_ENABLED"]:
  42. online_users = User.query.filter(User.lastseen >= time_diff()).count()
  43. # Because we do not have server side sessions, we cannot check if there
  44. # are online guests
  45. online_guests = None
  46. else:
  47. online_users = len(get_online_users())
  48. online_guests = len(get_online_users(guest=True))
  49. return render_template("forum/index.html",
  50. categories=categories,
  51. user_count=user_count,
  52. topic_count=topic_count,
  53. post_count=post_count,
  54. newest_user=newest_user,
  55. online_users=online_users,
  56. online_guests=online_guests)
  57. @forum.route("/category/<int:category_id>")
  58. @forum.route("/category/<int:category_id>-<slug>")
  59. def view_category(category_id, slug=None):
  60. category, forums = Category.\
  61. get_forums(category_id=category_id, user=current_user)
  62. return render_template("forum/category.html", forums=forums,
  63. category=category)
  64. @forum.route("/forum/<int:forum_id>")
  65. @forum.route("/forum/<int:forum_id>-<slug>")
  66. @allows.requires(CanAccessForum())
  67. def view_forum(forum_id, slug=None):
  68. page = request.args.get('page', 1, type=int)
  69. forum_instance, forumsread = Forum.get_forum(
  70. forum_id=forum_id, user=current_user
  71. )
  72. if forum_instance.external:
  73. return redirect(forum_instance.external)
  74. topics = Forum.get_topics(
  75. forum_id=forum_instance.id, user=current_user, page=page,
  76. per_page=flaskbb_config["TOPICS_PER_PAGE"]
  77. )
  78. return render_template(
  79. "forum/forum.html", forum=forum_instance,
  80. topics=topics, forumsread=forumsread,
  81. )
  82. @forum.route("/topic/<int:topic_id>", methods=["POST", "GET"])
  83. @forum.route("/topic/<int:topic_id>-<slug>", methods=["POST", "GET"])
  84. @allows.requires(CanAccessTopic())
  85. def view_topic(topic_id, slug=None):
  86. page = request.args.get('page', 1, type=int)
  87. # Fetch some information about the topic
  88. topic = Topic.get_topic(topic_id=topic_id, user=current_user)
  89. # Count the topic views
  90. topic.views += 1
  91. topic.save()
  92. # fetch the posts in the topic
  93. posts = Post.query.\
  94. join(User, Post.user_id == User.id).\
  95. filter(Post.topic_id == topic.id).\
  96. add_entity(User).\
  97. order_by(Post.id.asc()).\
  98. paginate(page, flaskbb_config['POSTS_PER_PAGE'], False)
  99. # Abort if there are no posts on this page
  100. if len(posts.items) == 0:
  101. abort(404)
  102. # Update the topicsread status if the user hasn't read it
  103. forumsread = None
  104. if current_user.is_authenticated:
  105. forumsread = ForumsRead.query.\
  106. filter_by(user_id=current_user.id,
  107. forum_id=topic.forum.id).first()
  108. topic.update_read(current_user, topic.forum, forumsread)
  109. form = None
  110. if Permission(CanPostReply):
  111. form = QuickreplyForm()
  112. if form.validate_on_submit():
  113. post = form.save(current_user, topic)
  114. return view_post(post.id)
  115. return render_template("forum/topic.html", topic=topic, posts=posts,
  116. last_seen=time_diff(), form=form)
  117. @forum.route("/post/<int:post_id>")
  118. def view_post(post_id):
  119. post = Post.query.filter_by(id=post_id).first_or_404()
  120. count = post.topic.post_count
  121. page = count / flaskbb_config["POSTS_PER_PAGE"]
  122. if count > flaskbb_config["POSTS_PER_PAGE"]:
  123. page += 1
  124. else:
  125. page = 1
  126. return redirect(post.topic.url + "?page=%d#pid%s" % (page, post.id))
  127. @forum.route("/<int:forum_id>/topic/new", methods=["POST", "GET"])
  128. @forum.route("/<int:forum_id>-<slug>/topic/new", methods=["POST", "GET"])
  129. @login_required
  130. def new_topic(forum_id, slug=None):
  131. forum_instance = Forum.query.filter_by(id=forum_id).first_or_404()
  132. if not Permission(CanPostTopic):
  133. flash(_("You do not have the permissions to create a new topic."),
  134. "danger")
  135. return redirect(forum_instance.url)
  136. form = NewTopicForm()
  137. if request.method == "POST":
  138. if "preview" in request.form and form.validate():
  139. return render_template(
  140. "forum/new_topic.html", forum=forum_instance,
  141. form=form, preview=form.content.data
  142. )
  143. if "submit" in request.form and form.validate():
  144. topic = form.save(current_user, forum_instance)
  145. # redirect to the new topic
  146. return redirect(url_for('forum.view_topic', topic_id=topic.id))
  147. return render_template(
  148. "forum/new_topic.html", forum=forum_instance, form=form
  149. )
  150. @forum.route("/topic/<int:topic_id>/delete", methods=["POST"])
  151. @forum.route("/topic/<int:topic_id>-<slug>/delete", methods=["POST"])
  152. @login_required
  153. def delete_topic(topic_id=None, slug=None):
  154. topic = Topic.query.filter_by(id=topic_id).first_or_404()
  155. if not Permission(CanDeleteTopic):
  156. flash(_("You do not have the permissions to delete this topic."),
  157. "danger")
  158. return redirect(topic.forum.url)
  159. involved_users = User.query.filter(Post.topic_id == topic.id,
  160. User.id == Post.user_id).all()
  161. topic.delete(users=involved_users)
  162. return redirect(url_for("forum.view_forum", forum_id=topic.forum_id))
  163. @forum.route("/topic/<int:topic_id>/lock", methods=["POST"])
  164. @forum.route("/topic/<int:topic_id>-<slug>/lock", methods=["POST"])
  165. @login_required
  166. def lock_topic(topic_id=None, slug=None):
  167. topic = Topic.query.filter_by(id=topic_id).first_or_404()
  168. if not Permission(IsAtleastModeratorInForum(forum=topic.forum)):
  169. flash(_("You do not have the permissions to lock this topic."),
  170. "danger")
  171. return redirect(topic.url)
  172. topic.locked = True
  173. topic.save()
  174. return redirect(topic.url)
  175. @forum.route("/topic/<int:topic_id>/unlock", methods=["POST"])
  176. @forum.route("/topic/<int:topic_id>-<slug>/unlock", methods=["POST"])
  177. @login_required
  178. def unlock_topic(topic_id=None, slug=None):
  179. topic = Topic.query.filter_by(id=topic_id).first_or_404()
  180. if not Permission(IsAtleastModeratorInForum(forum=topic.forum)):
  181. flash(_("You do not have the permissions to unlock this topic."),
  182. "danger")
  183. return redirect(topic.url)
  184. topic.locked = False
  185. topic.save()
  186. return redirect(topic.url)
  187. @forum.route("/topic/<int:topic_id>/highlight", methods=["POST"])
  188. @forum.route("/topic/<int:topic_id>-<slug>/highlight", methods=["POST"])
  189. @login_required
  190. def highlight_topic(topic_id=None, slug=None):
  191. topic = Topic.query.filter_by(id=topic_id).first_or_404()
  192. if not Permission(IsAtleastModeratorInForum(forum=topic.forum)):
  193. flash(_("You do not have the permissions to highlight this topic."),
  194. "danger")
  195. return redirect(topic.url)
  196. topic.important = True
  197. topic.save()
  198. return redirect(topic.url)
  199. @forum.route("/topic/<int:topic_id>/trivialize", methods=["POST"])
  200. @forum.route("/topic/<int:topic_id>-<slug>/trivialize", methods=["POST"])
  201. @login_required
  202. def trivialize_topic(topic_id=None, slug=None):
  203. topic = Topic.query.filter_by(id=topic_id).first_or_404()
  204. # Unlock is basically the same as lock
  205. if not Permission(IsAtleastModeratorInForum(forum=topic.forum)):
  206. flash(_("You do not have the permissions to trivialize this topic."),
  207. "danger")
  208. return redirect(topic.url)
  209. topic.important = False
  210. topic.save()
  211. return redirect(topic.url)
  212. @forum.route("/forum/<int:forum_id>/edit", methods=["POST", "GET"])
  213. @forum.route("/forum/<int:forum_id>-<slug>/edit", methods=["POST", "GET"])
  214. @login_required
  215. def manage_forum(forum_id, slug=None):
  216. page = request.args.get('page', 1, type=int)
  217. forum_instance, forumsread = Forum.get_forum(forum_id=forum_id,
  218. user=current_user)
  219. # remove the current forum from the select field (move).
  220. available_forums = Forum.query.order_by(Forum.position).all()
  221. available_forums.remove(forum_instance)
  222. if not Permission(IsAtleastModeratorInForum(forum=forum_instance)):
  223. flash(_("You do not have the permissions to moderate this forum."),
  224. "danger")
  225. return redirect(forum_instance.url)
  226. if forum_instance.external:
  227. return redirect(forum_instance.external)
  228. topics = Forum.get_topics(
  229. forum_id=forum_instance.id, user=current_user, page=page,
  230. per_page=flaskbb_config["TOPICS_PER_PAGE"]
  231. )
  232. mod_forum_url = url_for("forum.manage_forum", forum_id=forum_instance.id,
  233. slug=forum_instance.slug)
  234. # the code is kind of the same here but it somehow still looks cleaner than
  235. # doin some magic
  236. if request.method == "POST":
  237. ids = request.form.getlist("rowid")
  238. tmp_topics = Topic.query.filter(Topic.id.in_(ids)).all()
  239. if not len(tmp_topics) > 0:
  240. flash(_("In order to perform this action you have to select at "
  241. "least one topic."), "danger")
  242. return redirect(mod_forum_url)
  243. # locking/unlocking
  244. if "lock" in request.form:
  245. changed = do_topic_action(topics=tmp_topics, user=current_user,
  246. action="locked", reverse=False)
  247. flash(_("%(count)s topics locked.", count=changed), "success")
  248. return redirect(mod_forum_url)
  249. elif "unlock" in request.form:
  250. changed = do_topic_action(topics=tmp_topics, user=current_user,
  251. action="locked", reverse=True)
  252. flash(_("%(count)s topics unlocked.", count=changed), "success")
  253. return redirect(mod_forum_url)
  254. # highlighting/trivializing
  255. elif "highlight" in request.form:
  256. changed = do_topic_action(topics=tmp_topics, user=current_user,
  257. action="important", reverse=False)
  258. flash(_("%(count)s topics highlighted.", count=changed), "success")
  259. return redirect(mod_forum_url)
  260. elif "trivialize" in request.form:
  261. changed = do_topic_action(topics=tmp_topics, user=current_user,
  262. action="important", reverse=True)
  263. flash(_("%(count)s topics trivialized.", count=changed), "success")
  264. return redirect(mod_forum_url)
  265. # deleting
  266. elif "delete" in request.form:
  267. changed = do_topic_action(topics=tmp_topics, user=current_user,
  268. action="delete", reverse=False)
  269. flash(_("%(count)s topics deleted.", count=changed), "success")
  270. return redirect(mod_forum_url)
  271. # moving
  272. elif "move" in request.form:
  273. new_forum_id = request.form.get("forum")
  274. if not new_forum_id:
  275. flash(_("Please choose a new forum for the topics."), "info")
  276. return redirect(mod_forum_url)
  277. new_forum = Forum.query.filter_by(id=new_forum_id).first_or_404()
  278. # check the permission in the current forum and in the new forum
  279. if not Permission(
  280. And(
  281. IsAtleastModeratorInForum(forum_id=new_forum_id),
  282. IsAtleastModeratorInForum(forum=forum_instance)
  283. )
  284. ):
  285. flash(_("You do not have the permissions to move this topic."),
  286. "danger")
  287. return redirect(mod_forum_url)
  288. new_forum.move_topics_to(tmp_topics)
  289. return redirect(mod_forum_url)
  290. return render_template(
  291. "forum/edit_forum.html", forum=forum_instance, topics=topics,
  292. available_forums=available_forums, forumsread=forumsread,
  293. )
  294. @forum.route("/topic/<int:topic_id>/post/new", methods=["POST", "GET"])
  295. @forum.route("/topic/<int:topic_id>-<slug>/post/new", methods=["POST", "GET"])
  296. @login_required
  297. def new_post(topic_id, slug=None):
  298. topic = Topic.query.filter_by(id=topic_id).first_or_404()
  299. if not Permission(CanPostReply):
  300. flash(_("You do not have the permissions to post in this topic."),
  301. "danger")
  302. return redirect(topic.forum.url)
  303. form = ReplyForm()
  304. if form.validate_on_submit():
  305. if "preview" in request.form:
  306. return render_template(
  307. "forum/new_post.html", topic=topic,
  308. form=form, preview=form.content.data
  309. )
  310. else:
  311. post = form.save(current_user, topic)
  312. return view_post(post.id)
  313. return render_template("forum/new_post.html", topic=topic, form=form)
  314. @forum.route(
  315. "/topic/<int:topic_id>/post/<int:post_id>/reply", methods=["POST", "GET"]
  316. )
  317. @login_required
  318. def reply_post(topic_id, post_id):
  319. topic = Topic.query.filter_by(id=topic_id).first_or_404()
  320. post = Post.query.filter_by(id=post_id).first_or_404()
  321. if not Permission(CanPostReply):
  322. flash(_("You do not have the permissions to post in this topic."),
  323. "danger")
  324. return redirect(topic.forum.url)
  325. form = ReplyForm()
  326. if form.validate_on_submit():
  327. if "preview" in request.form:
  328. return render_template(
  329. "forum/new_post.html", topic=topic,
  330. form=form, preview=form.content.data
  331. )
  332. else:
  333. form.save(current_user, topic)
  334. return redirect(post.topic.url)
  335. else:
  336. form.content.data = format_quote(post.username, post.content)
  337. return render_template("forum/new_post.html", topic=post.topic, form=form)
  338. @forum.route("/post/<int:post_id>/edit", methods=["POST", "GET"])
  339. @login_required
  340. def edit_post(post_id):
  341. post = Post.query.filter_by(id=post_id).first_or_404()
  342. if not Permission(CanEditPost):
  343. flash(_("You do not have the permissions to edit this post."),
  344. "danger")
  345. return redirect(post.topic.url)
  346. form = ReplyForm()
  347. if form.validate_on_submit():
  348. if "preview" in request.form:
  349. return render_template(
  350. "forum/new_post.html", topic=post.topic,
  351. form=form, preview=form.content.data
  352. )
  353. else:
  354. form.populate_obj(post)
  355. post.date_modified = time_utcnow()
  356. post.modified_by = current_user.username
  357. post.save()
  358. return redirect(post.topic.url)
  359. else:
  360. form.content.data = post.content
  361. return render_template("forum/new_post.html", topic=post.topic, form=form)
  362. @forum.route("/post/<int:post_id>/delete", methods=["POST"])
  363. @login_required
  364. def delete_post(post_id):
  365. post = Post.query.filter_by(id=post_id).first_or_404()
  366. # TODO: Bulk delete
  367. if not Permission(CanDeletePost):
  368. flash(_("You do not have the permissions to delete this post."),
  369. "danger")
  370. return redirect(post.topic.url)
  371. first_post = post.first_post
  372. topic_url = post.topic.url
  373. forum_url = post.topic.forum.url
  374. post.delete()
  375. # If the post was the first post in the topic, redirect to the forums
  376. if first_post:
  377. return redirect(forum_url)
  378. return redirect(topic_url)
  379. @forum.route("/post/<int:post_id>/report", methods=["GET", "POST"])
  380. @login_required
  381. def report_post(post_id):
  382. post = Post.query.filter_by(id=post_id).first_or_404()
  383. form = ReportForm()
  384. if form.validate_on_submit():
  385. form.save(current_user, post)
  386. flash(_("Thanks for reporting."), "success")
  387. return render_template("forum/report_post.html", form=form)
  388. @forum.route("/post/<int:post_id>/raw", methods=["POST", "GET"])
  389. @login_required
  390. def raw_post(post_id):
  391. post = Post.query.filter_by(id=post_id).first_or_404()
  392. return format_quote(username=post.username, content=post.content)
  393. @forum.route("/<int:forum_id>/markread", methods=["POST"])
  394. @forum.route("/<int:forum_id>-<slug>/markread", methods=["POST"])
  395. @login_required
  396. def markread(forum_id=None, slug=None):
  397. # Mark a single forum as read
  398. if forum_id:
  399. forum_instance = Forum.query.filter_by(id=forum_id).first_or_404()
  400. forumsread = ForumsRead.query.filter_by(
  401. user_id=current_user.id, forum_id=forum_instance.id
  402. ).first()
  403. TopicsRead.query.filter_by(user_id=current_user.id,
  404. forum_id=forum_instance.id).delete()
  405. if not forumsread:
  406. forumsread = ForumsRead()
  407. forumsread.user_id = current_user.id
  408. forumsread.forum_id = forum_instance.id
  409. forumsread.last_read = time_utcnow()
  410. forumsread.cleared = time_utcnow()
  411. db.session.add(forumsread)
  412. db.session.commit()
  413. flash(_("Forum %(forum)s marked as read.", forum=forum_instance.title),
  414. "success")
  415. return redirect(forum_instance.url)
  416. # Mark all forums as read
  417. ForumsRead.query.filter_by(user_id=current_user.id).delete()
  418. TopicsRead.query.filter_by(user_id=current_user.id).delete()
  419. forums = Forum.query.all()
  420. forumsread_list = []
  421. for forum_instance in forums:
  422. forumsread = ForumsRead()
  423. forumsread.user_id = current_user.id
  424. forumsread.forum_id = forum_instance.id
  425. forumsread.last_read = time_utcnow()
  426. forumsread.cleared = time_utcnow()
  427. forumsread_list.append(forumsread)
  428. db.session.add_all(forumsread_list)
  429. db.session.commit()
  430. flash(_("All forums marked as read."), "success")
  431. return redirect(url_for("forum.index"))
  432. @forum.route("/who-is-online")
  433. def who_is_online():
  434. if current_app.config['REDIS_ENABLED']:
  435. online_users = get_online_users()
  436. else:
  437. online_users = User.query.filter(User.lastseen >= time_diff()).all()
  438. return render_template("forum/online_users.html",
  439. online_users=online_users)
  440. @forum.route("/memberlist", methods=['GET', 'POST'])
  441. def memberlist():
  442. page = request.args.get('page', 1, type=int)
  443. sort_by = request.args.get('sort_by', 'reg_date')
  444. order_by = request.args.get('order_by', 'asc')
  445. sort_obj = None
  446. order_func = None
  447. if order_by == 'asc':
  448. order_func = asc
  449. else:
  450. order_func = desc
  451. if sort_by == 'reg_date':
  452. sort_obj = User.id
  453. elif sort_by == 'post_count':
  454. sort_obj = User.post_count
  455. else:
  456. sort_obj = User.username
  457. search_form = UserSearchForm()
  458. if search_form.validate():
  459. users = search_form.get_results().\
  460. paginate(page, flaskbb_config['USERS_PER_PAGE'], False)
  461. return render_template("forum/memberlist.html", users=users,
  462. search_form=search_form)
  463. else:
  464. users = User.query.order_by(order_func(sort_obj)).\
  465. paginate(page, flaskbb_config['USERS_PER_PAGE'], False)
  466. return render_template("forum/memberlist.html", users=users,
  467. search_form=search_form)
  468. @forum.route("/topictracker", methods=["GET", "POST"])
  469. @login_required
  470. def topictracker():
  471. page = request.args.get("page", 1, type=int)
  472. topics = current_user.tracked_topics.\
  473. outerjoin(TopicsRead,
  474. db.and_(TopicsRead.topic_id == Topic.id,
  475. TopicsRead.user_id == current_user.id)).\
  476. add_entity(TopicsRead).\
  477. order_by(Topic.last_updated.desc()).\
  478. paginate(page, flaskbb_config['TOPICS_PER_PAGE'], True)
  479. # bulk untracking
  480. if request.method == "POST":
  481. topic_ids = request.form.getlist("rowid")
  482. tmp_topics = Topic.query.filter(Topic.id.in_(topic_ids)).all()
  483. for topic in tmp_topics:
  484. current_user.untrack_topic(topic)
  485. current_user.save()
  486. flash(_("%(topic_count)s topics untracked.",
  487. topic_count=len(tmp_topics)), "success")
  488. return redirect(url_for("forum.topictracker"))
  489. return render_template("forum/topictracker.html", topics=topics)
  490. @forum.route("/topictracker/<int:topic_id>/add", methods=["POST"])
  491. @forum.route("/topictracker/<int:topic_id>-<slug>/add", methods=["POST"])
  492. @login_required
  493. def track_topic(topic_id, slug=None):
  494. topic = Topic.query.filter_by(id=topic_id).first_or_404()
  495. current_user.track_topic(topic)
  496. current_user.save()
  497. return redirect(topic.url)
  498. @forum.route("/topictracker/<int:topic_id>/delete", methods=["POST"])
  499. @forum.route("/topictracker/<int:topic_id>-<slug>/delete", methods=["POST"])
  500. @login_required
  501. def untrack_topic(topic_id, slug=None):
  502. topic = Topic.query.filter_by(id=topic_id).first_or_404()
  503. current_user.untrack_topic(topic)
  504. current_user.save()
  505. return redirect(topic.url)
  506. @forum.route("/search", methods=['GET', 'POST'])
  507. def search():
  508. form = SearchPageForm()
  509. if form.validate_on_submit():
  510. result = form.get_results()
  511. return render_template('forum/search_result.html', form=form,
  512. result=result)
  513. return render_template('forum/search_form.html', form=form)