views.py 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875
  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 logging
  11. import math
  12. from flask import (Blueprint, abort, current_app, flash, redirect, request,
  13. url_for)
  14. from flask.views import MethodView
  15. from flask_allows import And, Permission
  16. from flask_babelplus import gettext as _
  17. from flask_login import current_user, login_required
  18. from sqlalchemy import asc, desc
  19. from flaskbb.extensions import allows, db
  20. from flaskbb.forum.forms import (NewTopicForm, QuickreplyForm, ReplyForm,
  21. ReportForm, SearchPageForm, UserSearchForm)
  22. from flaskbb.forum.models import (Category, Forum, ForumsRead, Post, Topic,
  23. TopicsRead)
  24. from flaskbb.user.models import User
  25. from flaskbb.utils.helpers import (do_topic_action, format_quote,
  26. get_online_users, real, register_view,
  27. render_template, time_diff, time_utcnow)
  28. from flaskbb.utils.requirements import (CanAccessForum, CanAccessTopic,
  29. CanDeletePost, CanDeleteTopic,
  30. CanEditPost, CanPostReply,
  31. CanPostTopic, Has,
  32. IsAtleastModeratorInForum)
  33. from flaskbb.utils.settings import flaskbb_config
  34. logger = logging.getLogger(__name__)
  35. forum = Blueprint("forum", __name__)
  36. class ForumIndex(MethodView):
  37. def get(self):
  38. categories = Category.get_all(user=real(current_user))
  39. # Fetch a few stats about the forum
  40. user_count = User.query.count()
  41. topic_count = Topic.query.count()
  42. post_count = Post.query.count()
  43. newest_user = User.query.order_by(User.id.desc()).first()
  44. # Check if we use redis or not
  45. if not current_app.config['REDIS_ENABLED']:
  46. online_users = User.query.filter(User.lastseen >= time_diff()).count()
  47. # Because we do not have server side sessions, we cannot check if there
  48. # are online guests
  49. online_guests = None
  50. else:
  51. online_users = len(get_online_users())
  52. online_guests = len(get_online_users(guest=True))
  53. return render_template(
  54. 'forum/index.html',
  55. categories=categories,
  56. user_count=user_count,
  57. topic_count=topic_count,
  58. post_count=post_count,
  59. newest_user=newest_user,
  60. online_users=online_users,
  61. online_guests=online_guests
  62. )
  63. class ViewCategory(MethodView):
  64. def get(self, category_id, slug=None):
  65. category, forums = Category.get_forums(category_id=category_id, user=real(current_user))
  66. return render_template('forum/category.html', forums=forums, category=category)
  67. class ViewForum(MethodView):
  68. decorators = [allows.requires(CanAccessForum())]
  69. def get(self, forum_id, slug=None):
  70. page = request.args.get('page', 1, type=int)
  71. forum_instance, forumsread = Forum.get_forum(forum_id=forum_id, user=real(current_user))
  72. if forum_instance.external:
  73. return redirect(forum_instance.external)
  74. topics = Forum.get_topics(
  75. forum_id=forum_instance.id,
  76. user=real(current_user),
  77. page=page,
  78. per_page=flaskbb_config['TOPICS_PER_PAGE']
  79. )
  80. return render_template(
  81. 'forum/forum.html',
  82. forum=forum_instance,
  83. topics=topics,
  84. forumsread=forumsread,
  85. )
  86. class ViewPost(MethodView):
  87. def get(self, post_id):
  88. '''Redirects to a post in a topic.'''
  89. post = Post.query.filter_by(id=post_id).first_or_404()
  90. post_in_topic = Post.query.filter(Post.topic_id == post.topic_id,
  91. Post.id <= post_id).order_by(Post.id.asc()).count()
  92. page = math.ceil(post_in_topic / float(flaskbb_config['POSTS_PER_PAGE']))
  93. return redirect(
  94. url_for(
  95. 'forum.view_topic',
  96. topic_id=post.topic.id,
  97. slug=post.topic.slug,
  98. page=page,
  99. _anchor='pid{}'.format(post.id)
  100. )
  101. )
  102. class ViewTopic(MethodView):
  103. decorators = [allows.requires(CanAccessTopic())]
  104. def get(self, topic_id, slug=None):
  105. page = request.args.get('page', 1, type=int)
  106. # Fetch some information about the topic
  107. topic = Topic.get_topic(topic_id=topic_id, user=real(current_user))
  108. # Count the topic views
  109. topic.views += 1
  110. topic.save()
  111. # fetch the posts in the topic
  112. posts = Post.query.\
  113. outerjoin(User, Post.user_id == User.id).\
  114. filter(Post.topic_id == topic.id).\
  115. add_entity(User).\
  116. order_by(Post.id.asc()).\
  117. paginate(page, flaskbb_config['POSTS_PER_PAGE'], False)
  118. # Abort if there are no posts on this page
  119. if len(posts.items) == 0:
  120. abort(404)
  121. # Update the topicsread status if the user hasn't read it
  122. forumsread = None
  123. if current_user.is_authenticated:
  124. forumsread = ForumsRead.query.\
  125. filter_by(user_id=real(current_user).id,
  126. forum_id=topic.forum.id).first()
  127. topic.update_read(real(current_user), topic.forum, forumsread)
  128. return render_template(
  129. 'forum/topic.html', topic=topic, posts=posts, last_seen=time_diff(), form=self.form()
  130. )
  131. @allows.requires(CanPostReply)
  132. def post(self, topic_id, slug=None):
  133. topic = Topic.get_topic(topic_id=topic_id, user=real(current_user))
  134. form = self.form()
  135. if not form:
  136. flash(_('Cannot post reply'), 'warning')
  137. return redirect('forum.view_topic', topic_id=topic_id, slug=slug)
  138. elif form.validate_on_submit():
  139. post = form.save(real(current_user), topic)
  140. return redirect(url_for('forum.view_post', post_id=post.id))
  141. else:
  142. for e in form.errors.get('content', []):
  143. flash(e, 'danger')
  144. return redirect(url_for('forum.view_topic', topic_id=topic_id, slug=slug))
  145. def form(self):
  146. if Permission(CanPostReply):
  147. return QuickreplyForm()
  148. return None
  149. class NewTopic(MethodView):
  150. decorators = [login_required]
  151. form = NewTopicForm
  152. def get(self, forum_id, slug=None):
  153. forum_instance = Forum.query.filter_by(id=forum_id).first_or_404()
  154. return render_template('forum/new_topic.html', forum=forum_instance, form=self.form())
  155. @allows.requires(CanPostTopic)
  156. def post(self, forum_id, slug=None):
  157. forum_instance = Forum.query.filter_by(id=forum_id).first_or_404()
  158. form = self.form()
  159. if 'preview' in request.form and form.validate():
  160. return render_template(
  161. 'forum/new_topic.html', forum=forum_instance, form=form, preview=form.content.data
  162. )
  163. elif 'submit' in request.form and form.validate():
  164. topic = form.save(real(current_user), forum_instance)
  165. # redirect to the new topic
  166. return redirect(url_for('forum.view_topic', topic_id=topic.id))
  167. else:
  168. return render_template('forum/new_topic.html', forum=forum_instance, form=form)
  169. class ManageForum(MethodView):
  170. decorators = [allows.requires(IsAtleastModeratorInForum()), login_required]
  171. def get(self, forum_id, slug=None):
  172. forum_instance, forumsread = Forum.get_forum(forum_id=forum_id, user=real(current_user))
  173. if forum_instance.external:
  174. return redirect(forum_instance.external)
  175. # remove the current forum from the select field (move).
  176. available_forums = Forum.query.order_by(Forum.position).all()
  177. available_forums.remove(forum_instance)
  178. page = request.args.get('page', 1, type=int)
  179. topics = Forum.get_topics(
  180. forum_id=forum_instance.id,
  181. user=real(current_user),
  182. page=page,
  183. per_page=flaskbb_config['TOPICS_PER_PAGE']
  184. )
  185. return render_template(
  186. 'forum/edit_forum.html',
  187. forum=forum_instance,
  188. topics=topics,
  189. available_forums=available_forums,
  190. forumsread=forumsread,
  191. )
  192. def post(self, forum_id, slug=None):
  193. forum_instance, __ = Forum.get_forum(forum_id=forum_id, user=real(current_user))
  194. mod_forum_url = url_for(
  195. 'forum.manage_forum', forum_id=forum_instance.id, slug=forum_instance.slug
  196. )
  197. ids = request.form.getlist('rowid')
  198. tmp_topics = Topic.query.filter(Topic.id.in_(ids)).all()
  199. if not len(tmp_topics) > 0:
  200. flash(
  201. _('In order to perform this action you have to select at '
  202. 'least one topic.'), 'danger'
  203. )
  204. return redirect(mod_forum_url)
  205. # locking/unlocking
  206. if 'lock' in request.form:
  207. changed = do_topic_action(
  208. topics=tmp_topics, user=real(current_user), action='locked', reverse=False
  209. )
  210. flash(_('%(count)s topics locked.', count=changed), 'success')
  211. return redirect(mod_forum_url)
  212. elif 'unlock' in request.form:
  213. changed = do_topic_action(
  214. topics=tmp_topics, user=real(current_user), action='locked', reverse=True
  215. )
  216. flash(_('%(count)s topics unlocked.', count=changed), 'success')
  217. return redirect(mod_forum_url)
  218. # highlighting/trivializing
  219. elif 'highlight' in request.form:
  220. changed = do_topic_action(
  221. topics=tmp_topics, user=real(current_user), action='important', reverse=False
  222. )
  223. flash(_('%(count)s topics highlighted.', count=changed), 'success')
  224. return redirect(mod_forum_url)
  225. elif 'trivialize' in request.form:
  226. changed = do_topic_action(
  227. topics=tmp_topics, user=real(current_user), action='important', reverse=True
  228. )
  229. flash(_('%(count)s topics trivialized.', count=changed), 'success')
  230. return redirect(mod_forum_url)
  231. # deleting
  232. elif 'delete' in request.form:
  233. changed = do_topic_action(
  234. topics=tmp_topics, user=real(current_user), action='delete', reverse=False
  235. )
  236. flash(_('%(count)s topics deleted.', count=changed), 'success')
  237. return redirect(mod_forum_url)
  238. # moving
  239. elif 'move' in request.form:
  240. new_forum_id = request.form.get('forum')
  241. if not new_forum_id:
  242. flash(_('Please choose a new forum for the topics.'), 'info')
  243. return redirect(mod_forum_url)
  244. new_forum = Forum.query.filter_by(id=new_forum_id).first_or_404()
  245. # check the permission in the current forum and in the new forum
  246. if not Permission(And(IsAtleastModeratorInForum(forum_id=new_forum_id),
  247. IsAtleastModeratorInForum(forum=forum_instance))):
  248. flash(_('You do not have the permissions to move this topic.'), 'danger')
  249. return redirect(mod_forum_url)
  250. if new_forum.move_topics_to(tmp_topics):
  251. flash(_('Topics moved.'), 'success')
  252. else:
  253. flash(_('Failed to move topics.'), 'danger')
  254. return redirect(mod_forum_url)
  255. # hiding/unhiding
  256. elif "hide" in request.form:
  257. changed = do_topic_action(
  258. topics=tmp_topics, user=real(current_user), action="hide", reverse=False
  259. )
  260. flash(_("%(count)s topics hidden.", count=changed), "success")
  261. return redirect(mod_forum_url)
  262. elif "unhide" in request.form:
  263. changed = do_topic_action(
  264. topics=tmp_topics, user=real(current_user), action="unhide", reverse=False
  265. )
  266. flash(_("%(count)s topics unhidden.", count=changed), "success")
  267. return redirect(mod_forum_url)
  268. else:
  269. flash(_('Unknown action requested'), 'danger')
  270. return redirect(mod_forum_url)
  271. class NewPost(MethodView):
  272. decorators = [allows.requires(CanPostReply), login_required]
  273. form = ReplyForm
  274. def get(self, topic_id, slug=None):
  275. topic = Topic.query.filter_by(id=topic_id).first_or_404()
  276. return render_template('forum/new_post.html', topic=topic, form=self.form())
  277. def post(self, topic_id, slug=None):
  278. topic = Topic.query.filter_by(id=topic_id).first_or_404()
  279. form = self.form()
  280. if form.validate_on_submit():
  281. if 'preview' in request.form:
  282. return render_template(
  283. 'forum/new_post.html', topic=topic, form=form, preview=form.content.data
  284. )
  285. else:
  286. post = form.save(real(current_user), topic)
  287. return redirect(url_for('forum.view_post', post_id=post.id))
  288. return render_template('forum/new_post.html', topic=topic, form=form)
  289. class ReplyPost(MethodView):
  290. decorators = [allows.requires(CanPostReply), login_required]
  291. form = ReplyForm
  292. def get(self, topic_id, post_id):
  293. topic = Topic.query.filter_by(id=topic_id).first_or_404()
  294. return render_template('forum/new_post.html', topic=topic, form=self.form())
  295. def post(self, topic_id, post_id):
  296. form = self.form()
  297. topic = Topic.query.filter_by(id=topic_id).first_or_404()
  298. if form.validate_on_submit():
  299. if 'preview' in request.form:
  300. return render_template(
  301. 'forum/new_post.html', topic=topic, form=form, preview=form.content.data
  302. )
  303. else:
  304. post = form.save(real(current_user), topic)
  305. return redirect(url_for('forum.view_post', post_id=post.id))
  306. else:
  307. form.content.data = format_quote(post.username, post.content)
  308. return render_template('forum/new_post.html', topic=post.topic, form=form)
  309. class EditPost(MethodView):
  310. decorators = [allows.requires(CanEditPost), login_required]
  311. form = ReplyForm
  312. def get(self, post_id):
  313. post = Post.query.filter_by(id=post_id).first_or_404()
  314. form = self.form(obj=post)
  315. return render_template('forum/new_post.html', topic=post.topic, form=form, edit_mode=True)
  316. def post(self, post_id):
  317. post = Post.query.filter_by(id=post_id).first_or_404()
  318. form = self.form(obj=post)
  319. if form.validate_on_submit():
  320. if 'preview' in request.form:
  321. return render_template(
  322. 'forum/new_post.html',
  323. topic=post.topic,
  324. form=form,
  325. preview=form.content.data,
  326. edit_mode=True
  327. )
  328. else:
  329. form.populate_obj(post)
  330. post.date_modified = time_utcnow()
  331. post.modified_by = real(current_user).username
  332. post.save()
  333. return redirect(url_for('forum.view_post', post_id=post.id))
  334. return render_template('forum/new_post.html', topic=post.topic, form=form, edit_mode=True)
  335. class ReportView(MethodView):
  336. decorators = [login_required]
  337. form = ReportForm
  338. def get(self, post_id):
  339. return render_template('forum/report_post.html', form=self.form())
  340. def post(self, post_id):
  341. form = self.form()
  342. if form.validate_on_submit():
  343. post = Post.query.filter_by(id=post_id).first_or_404()
  344. form.save(real(current_user), post)
  345. flash(_('Thanks for reporting.'), 'success')
  346. return render_template('forum/report_post.html', form=form)
  347. class MemberList(MethodView):
  348. form = UserSearchForm
  349. def get(self):
  350. page = request.args.get('page', 1, type=int)
  351. sort_by = request.args.get('sort_by', 'reg_date')
  352. order_by = request.args.get('order_by', 'asc')
  353. if order_by == 'asc':
  354. order_func = asc
  355. else:
  356. order_func = desc
  357. if sort_by == 'reg_date':
  358. sort_obj = User.id
  359. elif sort_by == 'post_count':
  360. sort_obj = User.post_count
  361. else:
  362. sort_obj = User.username
  363. users = User.query.order_by(order_func(sort_obj)).paginate(
  364. page, flaskbb_config['USERS_PER_PAGE'], False
  365. )
  366. return render_template('forum/memberlist.html', users=users, search_form=self.form())
  367. def post(self):
  368. page = request.args.get('page', 1, type=int)
  369. sort_by = request.args.get('sort_by', 'reg_date')
  370. order_by = request.args.get('order_by', 'asc')
  371. if order_by == 'asc':
  372. order_func = asc
  373. else:
  374. order_func = desc
  375. if sort_by == 'reg_date':
  376. sort_obj = User.id
  377. elif sort_by == 'post_count':
  378. sort_obj = User.post_count
  379. else:
  380. sort_obj = User.username
  381. form = self.form()
  382. if form.validate():
  383. users = form.get_results().paginate(page, flaskbb_config['USERS_PER_PAGE'], False)
  384. return render_template('forum/memberlist.html', users=users, search_form=form)
  385. users = User.query.order_by(order_func(sort_obj)).paginate(
  386. page, flaskbb_config['USERS_PER_PAGE'], False
  387. )
  388. return render_template('forum/memberlist.html', users=users, search_form=form)
  389. class TopicTracker(MethodView):
  390. decorators = [login_required]
  391. def get(self):
  392. page = request.args.get('page', 1, type=int)
  393. topics = real(current_user).tracked_topics.outerjoin(
  394. TopicsRead,
  395. db.and_(TopicsRead.topic_id == Topic.id, TopicsRead.user_id == real(current_user).id)
  396. ).add_entity(TopicsRead).order_by(Topic.last_updated.desc()).paginate(
  397. page, flaskbb_config['TOPICS_PER_PAGE'], True
  398. )
  399. return render_template('forum/topictracker.html', topics=topics)
  400. def post(self):
  401. topic_ids = request.form.getlist('rowid')
  402. tmp_topics = Topic.query.filter(Topic.id.in_(topic_ids)).all()
  403. for topic in tmp_topics:
  404. real(current_user).untrack_topic(topic)
  405. real(current_user).save()
  406. flash(_('%(topic_count)s topics untracked.', topic_count=len(tmp_topics)), 'success')
  407. return redirect(url_for('forum.topictracker'))
  408. class Search(MethodView):
  409. form = SearchPageForm
  410. def get(self):
  411. return render_template('forum/search_form.html', form=self.form())
  412. def post(self):
  413. form = self.form()
  414. if form.validate_on_submit():
  415. result = form.get_results()
  416. return render_template('forum/search_result.html', form=form, result=result)
  417. return render_template('forum/search_form.html', form=form)
  418. class DeleteTopic(MethodView):
  419. decorators = [allows.requires(CanDeleteTopic), login_required]
  420. def post(self, topic_id, slug=None):
  421. topic = Topic.query.filter_by(id=topic_id).first_or_404()
  422. involved_users = User.query.filter(Post.topic_id == topic.id,
  423. User.id == Post.user_id).all()
  424. topic.delete(users=involved_users)
  425. return redirect(url_for('forum.view_forum', forum_id=topic.forum_id))
  426. class LockTopic(MethodView):
  427. decorators = [allows.requires(IsAtleastModeratorInForum()), login_required]
  428. def post(self, topic_id, slug=None):
  429. topic = Topic.query.filter_by(id=topic_id).first_or_404()
  430. topic.locked = True
  431. topic.save()
  432. return redirect(topic.url)
  433. class UnlockTopic(MethodView):
  434. decorators = [allows.requires(IsAtleastModeratorInForum()), login_required]
  435. def post(self, topic_id, slug=None):
  436. topic = Topic.query.filter_by(id=topic_id).first_or_404()
  437. topic.locked = False
  438. topic.save()
  439. return redirect(topic.url)
  440. class HighlightTopic(MethodView):
  441. decorators = [allows.requires(IsAtleastModeratorInForum()), login_required]
  442. def post(self, topic_id, slug=None):
  443. topic = Topic.query.filter_by(id=topic_id).first_or_404()
  444. topic.important = True
  445. topic.save()
  446. return redirect(topic.url)
  447. class TrivializeTopic(MethodView):
  448. decorators = [allows.requires(IsAtleastModeratorInForum()), login_required]
  449. def post(topic_id=None, slug=None):
  450. topic = Topic.query.filter_by(id=topic_id).first_or_404()
  451. topic.important = False
  452. topic.save()
  453. return redirect(topic.url)
  454. class DeletePost(MethodView):
  455. decorators = [allows.requires(CanDeletePost), login_required]
  456. def post(self, post_id):
  457. post = Post.query.filter_by(id=post_id).first_or_404()
  458. first_post = post.first_post
  459. topic_url = post.topic.url
  460. forum_url = post.topic.forum.url
  461. post.delete()
  462. # If the post was the first post in the topic, redirect to the forums
  463. if first_post:
  464. return redirect(forum_url)
  465. return redirect(topic_url)
  466. class RawPost(MethodView):
  467. decorators = [login_required]
  468. def get(self, post_id):
  469. post = Post.query.filter_by(id=post_id).first_or_404()
  470. return format_quote(username=post.username, content=post.content)
  471. class MarkRead(MethodView):
  472. decorators = [login_required]
  473. def post(self, forum_id=None, slug=None):
  474. # Mark a single forum as read
  475. if forum_id is not None:
  476. forum_instance = Forum.query.filter_by(id=forum_id).first_or_404()
  477. forumsread = ForumsRead.query.filter_by(
  478. user_id=real(current_user).id, forum_id=forum_instance.id
  479. ).first()
  480. TopicsRead.query.filter_by(
  481. user_id=real(current_user).id, forum_id=forum_instance.id
  482. ).delete()
  483. if not forumsread:
  484. forumsread = ForumsRead()
  485. forumsread.user = real(current_user)
  486. forumsread.forum = forum_instance
  487. forumsread.last_read = time_utcnow()
  488. forumsread.cleared = time_utcnow()
  489. db.session.add(forumsread)
  490. db.session.commit()
  491. flash(_('Forum %(forum)s marked as read.', forum=forum_instance.title), 'success')
  492. return redirect(forum_instance.url)
  493. # Mark all forums as read
  494. ForumsRead.query.filter_by(user_id=real(current_user).id).delete()
  495. TopicsRead.query.filter_by(user_id=real(current_user).id).delete()
  496. forums = Forum.query.all()
  497. forumsread_list = []
  498. for forum_instance in forums:
  499. forumsread = ForumsRead()
  500. forumsread.user = real(current_user)
  501. forumsread.forum = forum_instance
  502. forumsread.last_read = time_utcnow()
  503. forumsread.cleared = time_utcnow()
  504. forumsread_list.append(forumsread)
  505. db.session.add_all(forumsread_list)
  506. db.session.commit()
  507. flash(_('All forums marked as read.'), 'success')
  508. return redirect(url_for('forum.index'))
  509. class WhoIsOnline(MethodView):
  510. def get(self):
  511. if current_app.config['REDIS_ENABLED']:
  512. online_users = get_online_users()
  513. else:
  514. online_users = User.query.filter(User.lastseen >= time_diff()).all()
  515. return render_template('forum/online_users.html', online_users=online_users)
  516. class TrackTopic(MethodView):
  517. decorators = [login_required]
  518. def post(self, topic_id, slug=None):
  519. topic = Topic.query.filter_by(id=topic_id).first_or_404()
  520. real(current_user).track_topic(topic)
  521. real(current_user).save()
  522. return redirect(topic.url)
  523. class UntrackTopic(MethodView):
  524. decorators = [login_required]
  525. def post(self, topic_id, slug=None):
  526. topic = Topic.query.filter_by(id=topic_id).first_or_404()
  527. real(current_user).untrack_topic(topic)
  528. real(current_user).save()
  529. return redirect(topic.url)
  530. class HideTopic(MethodView):
  531. decorators = [login_required]
  532. def post(self, topic_id, slug=None):
  533. topic = Topic.query.with_hidden().filter_by(id=topic_id).first_or_404()
  534. if not Permission(Has('makehidden'), IsAtleastModeratorInForum(forum=topic.forum)):
  535. flash(_("You do not have permission to hide this topic"), "danger")
  536. return redirect(topic.url)
  537. topic.hide(user=current_user)
  538. topic.save()
  539. if Permission(Has('viewhidden')):
  540. return redirect(topic.url)
  541. return redirect(topic.forum.url)
  542. class UnhideTopic(MethodView):
  543. decorators = [login_required]
  544. def post(self, topic_id, slug=None):
  545. topic = Topic.query.filter_by(id=topic_id).first_or_404()
  546. if not Permission(Has('makehidden'), IsAtleastModeratorInForum(forum=topic.forum)):
  547. flash(_("You do not have permission to unhide this topic"), "danger")
  548. return redirect(topic.url)
  549. topic.unhide()
  550. topic.save()
  551. return redirect(topic.url)
  552. class HidePost(MethodView):
  553. decorators = [login_required]
  554. def post(self, post_id):
  555. post = Post.query.filter(Post.id == post_id).first_or_404()
  556. if not Permission(Has('makehidden'), IsAtleastModeratorInForum(forum=post.topic.forum)):
  557. flash(_("You do not have permission to hide this post"), "danger")
  558. return redirect(post.topic.url)
  559. if post.hidden:
  560. flash(_("Post is already hidden"), "warning")
  561. return redirect(post.topic.url)
  562. first_post = post.first_post
  563. post.hide(current_user)
  564. post.save()
  565. if first_post:
  566. flash(_("Topic hidden"), "success")
  567. else:
  568. flash(_("Post hidden"), "success")
  569. if post.first_post and not Permission(Has("viewhidden")):
  570. return redirect(post.topic.forum.url)
  571. return redirect(post.topic.url)
  572. class UnhidePost(MethodView):
  573. decorators = [login_required]
  574. def post(self, post_id):
  575. post = Post.query.filter(Post.id == post_id).first_or_404()
  576. if not Permission(Has('makehidden'), IsAtleastModeratorInForum(forum=post.topic.forum)):
  577. flash(_("You do not have permission to unhide this post"), "danger")
  578. return redirect(post.topic.url)
  579. if not post.hidden:
  580. flash(_("Post is already unhidden"), "warning")
  581. redirect(post.topic.url)
  582. post.unhide()
  583. post.save()
  584. flash(_("Post unhidden"), "success")
  585. return redirect(post.topic.url)
  586. register_view(
  587. forum,
  588. routes=['/category/<int:category_id>', '/category/<int:category_id>-<slug>'],
  589. view_func=ViewCategory.as_view('view_category')
  590. )
  591. register_view(
  592. forum,
  593. routes=['/forum/<int:forum_id>/edit', '/forum/<int:forum_id>-<slug>/edit'],
  594. view_func=ManageForum.as_view('manage_forum')
  595. )
  596. register_view(
  597. forum,
  598. routes=['/forum/<int:forum_id>', '/forum/<int:forum_id>-<slug>'],
  599. view_func=ViewForum.as_view('view_forum')
  600. )
  601. register_view(
  602. forum,
  603. routes=['/<int:forum_id>/markread', '/<int:forum_id>-<slug>/markread'],
  604. view_func=MarkRead.as_view('markread')
  605. )
  606. register_view(
  607. forum,
  608. routes=['/<int:forum_id>/topic/new', '/<int:forum_id>-<slug>/topic/new'],
  609. view_func=NewTopic.as_view('new_topic')
  610. )
  611. register_view(forum, routes=['/memberlist'], view_func=MemberList.as_view('memberlist'))
  612. register_view(
  613. forum, routes=['/post/<int:post_id>/delete'], view_func=DeletePost.as_view('delete_post')
  614. )
  615. register_view(forum, routes=['/post/<int:post_id>/edit'], view_func=EditPost.as_view('edit_post'))
  616. register_view(forum, routes=['/post/<int:post_id>/raw'], view_func=RawPost.as_view('raw_post'))
  617. register_view(
  618. forum, routes=['/post/<int:post_id>/report'], view_func=ReportView.as_view('report_post')
  619. )
  620. register_view(forum, routes=['/post/<int:post_id>'], view_func=ViewPost.as_view('view_post'))
  621. register_view(forum, routes=['/search'], view_func=Search.as_view('search'))
  622. register_view(
  623. forum,
  624. routes=['/topic/<int:topic_id>/delete', '/topic/<int:topic_id>-<slug>/delete'],
  625. view_func=DeleteTopic.as_view('delete_topic')
  626. )
  627. register_view(
  628. forum,
  629. routes=['/topic/<int:topic_id>/highlight', '/topic/<int:topic_id>-<slug>/highlight'],
  630. view_func=HighlightTopic.as_view('highlight_topic')
  631. )
  632. register_view(
  633. forum,
  634. routes=['/topic/<int:topic_id>/lock', '/topic/<int:topic_id>-<slug>/lock'],
  635. view_func=LockTopic.as_view('lock_topic')
  636. )
  637. register_view(
  638. forum,
  639. routes=['/topic/<int:topic_id>/post/<int:post_id>/reply'],
  640. view_func=ReplyPost.as_view('reply_post')
  641. )
  642. register_view(
  643. forum,
  644. routes=['/topic/<int:topic_id>/post/new', '/topic/<int:topic_id>-<slug>/post/new'],
  645. view_func=NewPost.as_view('new_post')
  646. )
  647. register_view(
  648. forum,
  649. routes=['/topic/<int:topic_id>', '/topic/<int:topic_id>-<slug>'],
  650. view_func=ViewTopic.as_view('view_topic')
  651. )
  652. register_view(
  653. forum,
  654. routes=['/topic/<int:topic_id>/trivialize', '/topic/<int:topic_id>-<slug>/trivialize'],
  655. view_func=TrivializeTopic.as_view('trivialize_topic')
  656. )
  657. register_view(
  658. forum,
  659. routes=['/topic/<int:topic_id>/unlock', '/topic/<int:topic_id>-<slug>/unlock'],
  660. view_func=UnlockTopic.as_view('unlock_topic')
  661. )
  662. register_view(
  663. forum,
  664. routes=['/topictracker/<int:topic_id>/add', '/topictracker/<int:topic_id>-<slug>/add'],
  665. view_func=TrackTopic.as_view('track_topic')
  666. )
  667. register_view(
  668. forum,
  669. routes=['/topictracker/<int:topic_id>/delete', '/topictracker/<int:topic_id>-<slug>/delete'],
  670. view_func=UntrackTopic.as_view('untrack_topic')
  671. )
  672. register_view(forum, routes=['/topictracker'], view_func=TopicTracker.as_view('topictracker'))
  673. register_view(forum, routes=['/'], view_func=ForumIndex.as_view('index'))
  674. register_view(forum, routes=['/who-is-online'], view_func=WhoIsOnline.as_view('who_is_online'))
  675. register_view(
  676. forum,
  677. routes=["/topic/<int:topic_id>/hide", "/topic/<int:topic_id>-<slug>/hide"],
  678. view_func=HideTopic.as_view('hide_topic')
  679. )
  680. register_view(
  681. forum,
  682. routes=["/topic/<int:topic_id>/unhide", "/topic/<int:topic_id>-<slug>/unhide"],
  683. view_func=UnhideTopic.as_view('unhide_topic')
  684. )
  685. register_view(forum, routes=["/post/<int:post_id>/hide"], view_func=HidePost.as_view('hide_post'))
  686. register_view(
  687. forum, routes=["/post/<int:post_id>/unhide"], view_func=UnhidePost.as_view('unhide_post')
  688. )