views.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423
  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. import datetime
  11. import math
  12. from flask import (Blueprint, render_template, redirect, url_for, current_app,
  13. request, flash)
  14. from flask.ext.login import login_required, current_user
  15. from flaskbb.extensions import db
  16. from flaskbb.utils.helpers import (can_post_reply, can_delete_topic,
  17. can_edit_post, can_post_topic,
  18. can_delete_post, can_lock_topic,
  19. get_online_users, time_diff)
  20. from flaskbb.forum.models import Forum, Topic, Post, ForumsRead, TopicsRead
  21. from flaskbb.forum.forms import QuickreplyForm, ReplyForm, NewTopicForm
  22. from flaskbb.forum.helpers import get_forums
  23. from flaskbb.user.models import User
  24. forum = Blueprint("forum", __name__)
  25. @forum.route("/")
  26. def index():
  27. # Get the categories and forums
  28. if current_user.is_authenticated():
  29. categories_query = Forum.query.\
  30. outerjoin(ForumsRead,
  31. db.and_(ForumsRead.forum_id == Forum.id,
  32. ForumsRead.user_id == current_user.id)).\
  33. add_entity(ForumsRead).\
  34. order_by(Forum.position.asc()).\
  35. all()
  36. categories = get_forums(categories_query, current_user=True)
  37. else:
  38. categories_query = Forum.query.order_by(Forum.position.asc()).all()
  39. categories = get_forums(categories_query, current_user=False)
  40. # Fetch a few stats about the forum
  41. user_count = User.query.count()
  42. topic_count = Topic.query.count()
  43. post_count = Post.query.count()
  44. newest_user = User.query.order_by(User.id.desc()).first()
  45. # Check if we use redis or not
  46. if not current_app.config["REDIS_ENABLED"]:
  47. online_users = User.query.filter(User.lastseen >= time_diff()).count()
  48. online_guests = None
  49. else:
  50. online_users = len(get_online_users())
  51. online_guests = len(get_online_users(guest=True))
  52. return render_template("forum/index.html",
  53. categories=categories,
  54. user_count=user_count,
  55. topic_count=topic_count,
  56. post_count=post_count,
  57. newest_user=newest_user,
  58. online_users=online_users,
  59. online_guests=online_guests)
  60. @forum.route("/<int:forum_id>")
  61. def view_forum(forum_id):
  62. page = request.args.get('page', 1, type=int)
  63. if current_user.is_authenticated():
  64. forum = Forum.query.\
  65. filter(Forum.id == forum_id).\
  66. outerjoin(ForumsRead,
  67. db.and_(ForumsRead.forum_id == Forum.id,
  68. ForumsRead.user_id == current_user.id)).\
  69. add_entity(ForumsRead).\
  70. first_or_404()
  71. subforums = Forum.query.\
  72. filter(Forum.parent_id == forum[0].id).\
  73. outerjoin(ForumsRead,
  74. db.and_(ForumsRead.forum_id == Forum.id,
  75. ForumsRead.user_id == current_user.id)).\
  76. add_entity(ForumsRead).\
  77. all()
  78. topics = Topic.query.filter_by(forum_id=forum[0].id).\
  79. filter(Post.topic_id == Topic.id).\
  80. outerjoin(TopicsRead,
  81. db.and_(TopicsRead.topic_id == Topic.id,
  82. TopicsRead.user_id == current_user.id)).\
  83. add_entity(TopicsRead).\
  84. order_by(Post.id.desc()).\
  85. paginate(page, current_app.config['TOPICS_PER_PAGE'], True)
  86. else:
  87. forum = Forum.query.filter(Forum.id == forum_id).first_or_404()
  88. forum = (forum, None)
  89. subforums = Forum.query.filter(Forum.parent_id == forum[0].id).all()
  90. # This isn't really nice imho, but "add_entity" (see above)
  91. # makes a list with tuples
  92. subforums = [(item, None) for item in subforums]
  93. topics = Topic.query.filter_by(forum_id=forum[0].id).\
  94. filter(Post.topic_id == Topic.id).\
  95. order_by(Post.id.desc()).\
  96. paginate(page, current_app.config['TOPICS_PER_PAGE'], True, True)
  97. return render_template("forum/forum.html",
  98. forum=forum, topics=topics, subforums=subforums)
  99. @forum.route("/markread")
  100. @forum.route("/<int:forum_id>/markread")
  101. def markread(forum_id=None):
  102. if not current_user.is_authenticated():
  103. flash("You need to be logged in for that feature.", "danger")
  104. return redirect(url_for("forum.index"))
  105. # Mark a single forum as read
  106. if forum_id:
  107. forum = Forum.query.filter_by(id=forum_id).first_or_404()
  108. forumsread = ForumsRead.query.filter_by(forum_id=forum.id).first()
  109. TopicsRead.query.filter_by(user_id=current_user.id,
  110. forum_id=forum.id).delete()
  111. if not forumsread:
  112. forumsread = ForumsRead()
  113. forumsread.user_id = current_user.id
  114. forumsread.forum_id = forum.id
  115. forumsread.last_read = datetime.datetime.utcnow()
  116. forumsread.cleared = datetime.datetime.utcnow()
  117. db.session.add(forumsread)
  118. db.session.commit()
  119. return redirect(url_for("forum.view_forum", forum_id=forum.id))
  120. # Mark all forums as read
  121. ForumsRead.query.filter_by(user_id=current_user.id).delete()
  122. TopicsRead.query.filter_by(user_id=current_user.id).delete()
  123. forums = Forum.query.all()
  124. for forum in forums:
  125. if forum.is_category:
  126. continue
  127. forumsread = ForumsRead()
  128. forumsread.user_id = current_user.id
  129. forumsread.forum_id = forum.id
  130. forumsread.last_read = datetime.datetime.utcnow()
  131. forumsread.cleared = datetime.datetime.utcnow()
  132. db.session.add(forumsread)
  133. db.session.commit()
  134. return redirect(url_for("forum.index"))
  135. @forum.route("/topic/<int:topic_id>", methods=["POST", "GET"])
  136. def view_topic(topic_id):
  137. page = request.args.get('page', 1, type=int)
  138. topic = Topic.query.filter_by(id=topic_id).first()
  139. posts = Post.query.filter_by(topic_id=topic.id).\
  140. paginate(page, current_app.config['POSTS_PER_PAGE'], False)
  141. # Count the topic views
  142. topic.views += 1
  143. # Update the topicsread status if the user hasn't read it
  144. forumsread = None
  145. if current_user.is_authenticated():
  146. forumsread = ForumsRead.query.\
  147. filter_by(user_id=current_user.id,
  148. forum_id=topic.forum.id).first()
  149. topic.update_read(current_user, topic.forum, forumsread)
  150. topic.save()
  151. form = None
  152. if not topic.locked \
  153. and not topic.forum.locked \
  154. and can_post_reply(user=current_user,
  155. forum=topic.forum):
  156. form = QuickreplyForm()
  157. if form.validate_on_submit():
  158. post = form.save(current_user, topic)
  159. return view_post(post.id)
  160. return render_template("forum/topic.html", topic=topic, posts=posts,
  161. last_seen=time_diff(), form=form)
  162. @forum.route("/post/<int:post_id>")
  163. def view_post(post_id):
  164. post = Post.query.filter_by(id=post_id).first_or_404()
  165. count = post.topic.post_count
  166. page = math.ceil(count / current_app.config["POSTS_PER_PAGE"])
  167. if count > 10:
  168. page += 1
  169. else:
  170. page = 1
  171. return redirect(url_for("forum.view_topic", topic_id=post.topic.id) +
  172. "?page=%d#pid%s" % (page, post.id))
  173. @forum.route("/<int:forum_id>/topic/new", methods=["POST", "GET"])
  174. @login_required
  175. def new_topic(forum_id):
  176. forum = Forum.query.filter_by(id=forum_id).first_or_404()
  177. if forum.locked:
  178. flash("This forum is locked; you cannot submit new topics or posts.",
  179. "danger")
  180. return redirect(url_for('forum.view_forum', forum_id=forum.id))
  181. if not can_post_topic(user=current_user, forum=forum):
  182. flash("You do not have the permissions to create a new topic.",
  183. "danger")
  184. return redirect(url_for('forum.view_forum', forum_id=forum.id))
  185. form = NewTopicForm()
  186. if form.validate_on_submit():
  187. topic = form.save(current_user, forum)
  188. # redirect to the new topic
  189. return redirect(url_for('forum.view_topic', topic_id=topic.id))
  190. return render_template("forum/new_topic.html", forum=forum, form=form)
  191. @forum.route("/topic/<int:topic_id>/delete")
  192. @login_required
  193. def delete_topic(topic_id):
  194. topic = Topic.query.filter_by(id=topic_id).first_or_404()
  195. if not can_delete_topic(user=current_user, forum=topic.forum,
  196. post_user_id=topic.first_post.user_id):
  197. flash("You do not have the permissions to delete the topic", "danger")
  198. return redirect(url_for("forum.view_forum", forum_id=topic.forum_id))
  199. involved_users = User.query.filter(Post.topic_id == topic.id,
  200. User.id == Post.user_id).all()
  201. topic.delete(users=involved_users)
  202. return redirect(url_for("forum.view_forum", forum_id=topic.forum_id))
  203. @forum.route("/topic/<int:topic_id>/lock")
  204. @login_required
  205. def lock_topic(topic_id):
  206. topic = Topic.query.filter_by(id=topic_id).first_or_404()
  207. if not can_lock_topic(user=current_user, forum=topic.forum):
  208. flash("Yo do not have the permissions to lock this topic", "danger")
  209. return redirect(url_for("forum.view_topic", topic_id=topic.id))
  210. topic.locked = True
  211. topic.save()
  212. return redirect(url_for("forum.view_topic", topic_id=topic.id))
  213. @forum.route("/topic/<int:topic_id>/unlock")
  214. @login_required
  215. def unlock_topic(topic_id):
  216. topic = Topic.query.filter_by(id=topic_id).first_or_404()
  217. # Unlock is basically the same as lock
  218. if not can_lock_topic(user=current_user, forum=topic.forum):
  219. flash("Yo do not have the permissions to unlock this topic", "danger")
  220. return redirect(url_for("forum.view_topic", topic_id=topic.id))
  221. topic.locked = False
  222. topic.save()
  223. return redirect(url_for("forum.view_topic", topic_id=topic.id))
  224. @forum.route("/topic/<int:topic_id>/move")
  225. @login_required
  226. def move_topic(topic_id):
  227. pass
  228. @forum.route("/topic/<int:topic_id>/post/new", methods=["POST", "GET"])
  229. @login_required
  230. def new_post(topic_id):
  231. topic = Topic.query.filter_by(id=topic_id).first_or_404()
  232. if topic.forum.locked:
  233. flash("This forum is locked; you cannot submit new topics or posts.",
  234. "danger")
  235. return redirect(url_for('forum.view_forum', forum_id=topic.forum.id))
  236. if topic.locked:
  237. flash("The topic is locked.", "danger")
  238. return redirect(url_for("forum.view_forum", forum_id=topic.forum_id))
  239. if not can_post_reply(user=current_user, forum=topic.forum):
  240. flash("You do not have the permissions to delete the topic", "danger")
  241. return redirect(url_for("forum.view_forum", forum_id=topic.forum_id))
  242. form = ReplyForm()
  243. if form.validate_on_submit():
  244. post = form.save(current_user, topic)
  245. return view_post(post.id)
  246. return render_template("forum/new_post.html", topic=topic, form=form)
  247. @forum.route("/post/<int:post_id>/edit", methods=["POST", "GET"])
  248. @login_required
  249. def edit_post(post_id):
  250. post = Post.query.filter_by(id=post_id).first_or_404()
  251. if post.topic.forum.locked:
  252. flash("This forum is locked; you cannot submit new topics or posts.",
  253. "danger")
  254. return redirect(url_for("forum.view_forum",
  255. forum_id=post.topic.forum.id))
  256. if post.topic.locked:
  257. flash("The topic is locked.", "danger")
  258. return redirect(url_for("forum.view_forum",
  259. forum_id=post.topic.forum_id))
  260. if not can_edit_post(user=current_user, forum=post.topic.forum,
  261. post_user_id=post.user_id):
  262. flash("You do not have the permissions to edit this post", "danger")
  263. return redirect(url_for('forum.view_topic', topic_id=post.topic_id))
  264. form = ReplyForm()
  265. if form.validate_on_submit():
  266. form.populate_obj(post)
  267. post.date_modified = datetime.datetime.utcnow()
  268. post.save()
  269. return redirect(url_for("forum.view_topic", topic_id=post.topic.id))
  270. else:
  271. form.content.data = post.content
  272. return render_template("forum/new_post.html", topic=post.topic, form=form)
  273. @forum.route("/post/<int:post_id>/delete")
  274. @login_required
  275. def delete_post(post_id):
  276. post = Post.query.filter_by(id=post_id).first_or_404()
  277. if not can_delete_post(user=current_user, forum=post.topic.forum,
  278. post_user_id=post.user_id):
  279. flash("You do not have the permissions to edit this post", "danger")
  280. return redirect(url_for('forum.view_topic', topic_id=post.topic_id))
  281. topic_id = post.topic_id
  282. post.delete()
  283. # If the post was the first post in the topic, redirect to the forums
  284. if post.first_post:
  285. return redirect(url_for('forum.view_forum',
  286. forum_id=post.topic.forum_id))
  287. return redirect(url_for('forum.view_topic', topic_id=topic_id))
  288. @forum.route("/who_is_online")
  289. def who_is_online():
  290. if current_app.config['USE_REDIS']:
  291. online_users = get_online_users()
  292. else:
  293. online_users = User.query.filter(User.lastseen >= time_diff()).all()
  294. return render_template("forum/online_users.html",
  295. online_users=online_users)
  296. @forum.route("/memberlist")
  297. def memberlist():
  298. page = request.args.get('page', 1, type=int)
  299. users = User.query.order_by(User.id).\
  300. paginate(page, current_app.config['POSTS_PER_PAGE'], False)
  301. return render_template("forum/memberlist.html",
  302. users=users)
  303. @forum.route("/topictracker")
  304. def topictracker():
  305. page = request.args.get("page", 1, type=int)
  306. topics = current_user.tracked_topics.\
  307. outerjoin(TopicsRead,
  308. db.and_(TopicsRead.topic_id == Topic.id,
  309. TopicsRead.user_id == current_user.id)).\
  310. add_entity(TopicsRead).\
  311. order_by(Post.id.desc()).\
  312. paginate(page, current_app.config['TOPICS_PER_PAGE'], True)
  313. return render_template("forum/topictracker.html", topics=topics)
  314. @forum.route("/topictracker/<topic_id>/add")
  315. def track_topic(topic_id):
  316. topic = Topic.query.filter_by(id=topic_id).first_or_404()
  317. current_user.track_topic(topic)
  318. current_user.save()
  319. return redirect(url_for("forum.view_topic", topic_id=topic.id))
  320. @forum.route("/topictracker/<topic_id>/delete")
  321. def untrack_topic(topic_id):
  322. topic = Topic.query.filter_by(id=topic_id).first_or_404()
  323. current_user.untrack_topic(topic)
  324. current_user.save()
  325. return redirect(url_for("forum.view_topic", topic_id=topic.id))