views.py 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541
  1. # -*- coding: utf-8 -*-
  2. """
  3. flaskbb.management.views
  4. ~~~~~~~~~~~~~~~~~~~~~~~~
  5. This module handles the management views.
  6. :copyright: (c) 2014 by the FlaskBB Team.
  7. :license: BSD, see LICENSE for more details.
  8. """
  9. import sys
  10. import os
  11. from datetime import datetime
  12. from flask import (Blueprint, current_app, request, redirect, url_for, flash,
  13. __version__ as flask_version)
  14. from flask.ext.login import current_user
  15. from flask.ext.plugins import get_all_plugins, get_plugin, get_plugin_from_all
  16. from flaskbb import __version__ as flaskbb_version
  17. from flaskbb._compat import iteritems
  18. from flaskbb.forum.forms import UserSearchForm
  19. from flaskbb.utils.settings import flaskbb_config
  20. from flaskbb.utils.helpers import render_template
  21. from flaskbb.utils.decorators import admin_required, moderator_required
  22. from flaskbb.utils.permissions import can_ban_user, can_edit_user
  23. from flaskbb.extensions import db
  24. from flaskbb.user.models import User, Group
  25. from flaskbb.forum.models import Post, Topic, Forum, Category, Report
  26. from flaskbb.management.models import Setting, SettingsGroup
  27. from flaskbb.management.forms import (AddUserForm, EditUserForm, AddGroupForm,
  28. EditGroupForm, EditForumForm,
  29. AddForumForm, CategoryForm)
  30. management = Blueprint("management", __name__)
  31. @management.route("/")
  32. @moderator_required
  33. def overview():
  34. python_version = "%s.%s" % (sys.version_info[0], sys.version_info[1])
  35. user_count = User.query.count()
  36. topic_count = Topic.query.count()
  37. post_count = Post.query.count()
  38. return render_template("management/overview.html",
  39. python_version=python_version,
  40. flask_version=flask_version,
  41. flaskbb_version=flaskbb_version,
  42. user_count=user_count,
  43. topic_count=topic_count,
  44. post_count=post_count)
  45. @management.route("/settings", methods=["GET", "POST"])
  46. @management.route("/settings/<path:slug>", methods=["GET", "POST"])
  47. @admin_required
  48. def settings(slug=None):
  49. slug = slug if slug else "general"
  50. # get the currently active group
  51. active_group = SettingsGroup.query.filter_by(key=slug).first_or_404()
  52. # get all groups - used to build the navigation
  53. all_groups = SettingsGroup.query.all()
  54. SettingsForm = Setting.get_form(active_group)
  55. old_settings = Setting.get_settings(active_group)
  56. new_settings = {}
  57. form = SettingsForm()
  58. if form.validate_on_submit():
  59. for key, values in iteritems(old_settings):
  60. try:
  61. # check if the value has changed
  62. if values['value'] == form[key].data:
  63. continue
  64. else:
  65. new_settings[key] = form[key].data
  66. except KeyError:
  67. pass
  68. Setting.update(settings=new_settings, app=current_app)
  69. else:
  70. for key, values in iteritems(old_settings):
  71. try:
  72. form[key].data = values['value']
  73. except (KeyError, ValueError):
  74. pass
  75. return render_template("management/settings.html", form=form,
  76. all_groups=all_groups, active_group=active_group)
  77. # Users
  78. @management.route("/users", methods=['GET', 'POST'])
  79. @moderator_required
  80. def users():
  81. page = request.args.get("page", 1, type=int)
  82. search_form = UserSearchForm()
  83. if search_form.validate():
  84. users = search_form.get_results().\
  85. paginate(page, flaskbb_config['USERS_PER_PAGE'], False)
  86. return render_template("management/users.html", users=users,
  87. search_form=search_form)
  88. users = User.query. \
  89. order_by(User.id.asc()).\
  90. paginate(page, flaskbb_config['USERS_PER_PAGE'], False)
  91. return render_template("management/users.html", users=users,
  92. search_form=search_form)
  93. @management.route("/users/<int:user_id>/edit", methods=["GET", "POST"])
  94. @moderator_required
  95. def edit_user(user_id):
  96. user = User.query.filter_by(id=user_id).first_or_404()
  97. if not can_edit_user(current_user):
  98. flash("You are not allowed to edit this user.", "danger")
  99. return redirect(url_for("management.users"))
  100. secondary_group_query = Group.query.filter(
  101. db.not_(Group.id == user.primary_group_id),
  102. db.not_(Group.banned == True),
  103. db.not_(Group.guest == True))
  104. form = EditUserForm(user)
  105. form.secondary_groups.query = secondary_group_query
  106. if form.validate_on_submit():
  107. form.populate_obj(user)
  108. user.primary_group_id = form.primary_group.data.id
  109. # Don't override the password
  110. if form.password.data:
  111. user.password = form.password.data
  112. user.save(groups=form.secondary_groups.data)
  113. flash("User successfully edited", "success")
  114. return redirect(url_for("management.edit_user", user_id=user.id))
  115. return render_template("management/user_form.html", form=form,
  116. title="Edit User")
  117. @management.route("/users/<int:user_id>/delete")
  118. @admin_required
  119. def delete_user(user_id):
  120. user = User.query.filter_by(id=user_id).first_or_404()
  121. user.delete()
  122. flash("User successfully deleted", "success")
  123. return redirect(url_for("management.users"))
  124. @management.route("/users/add", methods=["GET", "POST"])
  125. @admin_required
  126. def add_user():
  127. form = AddUserForm()
  128. if form.validate_on_submit():
  129. form.save()
  130. flash("User successfully added.", "success")
  131. return redirect(url_for("management.users"))
  132. return render_template("management/user_form.html", form=form,
  133. title="Add User")
  134. @management.route("/users/banned")
  135. @moderator_required
  136. def banned_users():
  137. page = request.args.get("page", 1, type=int)
  138. search_form = UserSearchForm()
  139. users = User.query.filter(
  140. Group.banned == True,
  141. Group.id == User.primary_group_id
  142. ).paginate(page, flaskbb_config['USERS_PER_PAGE'], False)
  143. if search_form.validate():
  144. users = search_form.get_results().\
  145. paginate(page, flaskbb_config['USERS_PER_PAGE'], False)
  146. return render_template("management/banned_users.html", users=users,
  147. search_form=search_form)
  148. return render_template("management/banned_users.html", users=users,
  149. search_form=search_form)
  150. @management.route("/users/<int:user_id>/ban", methods=["GET", "POST"])
  151. @moderator_required
  152. def ban_user(user_id):
  153. if not can_ban_user(current_user):
  154. flash("You do not have the permissions to ban this user.")
  155. return redirect(url_for("management.overview"))
  156. user = User.query.filter_by(id=user_id).first_or_404()
  157. # Do not allow moderators to ban admins
  158. if user.get_permissions()['admin'] and \
  159. (current_user.permissions['mod'] or
  160. current_user.permissions['super_mod']):
  161. flash("A moderator cannot ban an admin user.", "danger")
  162. return redirect(url_for("management.overview"))
  163. if user.ban():
  164. flash("User was banned successfully.", "success")
  165. else:
  166. flash("Could not ban user.", "danger")
  167. return redirect(url_for("management.banned_users"))
  168. @management.route("/users/<int:user_id>/unban", methods=["GET", "POST"])
  169. @moderator_required
  170. def unban_user(user_id):
  171. if not can_ban_user(current_user):
  172. flash("You do not have the permissions to unban this user.")
  173. return redirect(url_for("management.overview"))
  174. user = User.query.filter_by(id=user_id).first_or_404()
  175. if user.unban():
  176. flash("User is now unbanned.", "success")
  177. else:
  178. flash("Could not unban user.", "danger")
  179. return redirect(url_for("management.banned_users"))
  180. # Reports
  181. @management.route("/reports")
  182. @moderator_required
  183. def reports():
  184. page = request.args.get("page", 1, type=int)
  185. reports = Report.query.\
  186. order_by(Report.id.asc()).\
  187. paginate(page, flaskbb_config['USERS_PER_PAGE'], False)
  188. return render_template("management/reports.html", reports=reports)
  189. @management.route("/reports/unread")
  190. @moderator_required
  191. def unread_reports():
  192. page = request.args.get("page", 1, type=int)
  193. reports = Report.query.\
  194. filter(Report.zapped == None).\
  195. order_by(Report.id.desc()).\
  196. paginate(page, flaskbb_config['USERS_PER_PAGE'], False)
  197. return render_template("management/unread_reports.html", reports=reports)
  198. @management.route("/reports/<int:report_id>/markread")
  199. @management.route("/reports/markread")
  200. @moderator_required
  201. def report_markread(report_id=None):
  202. # mark single report as read
  203. if report_id:
  204. report = Report.query.filter_by(id=report_id).first_or_404()
  205. if report.zapped:
  206. flash("Report %s is already marked as read" % report.id, "success")
  207. return redirect(url_for("management.reports"))
  208. report.zapped_by = current_user.id
  209. report.zapped = datetime.utcnow()
  210. report.save()
  211. flash("Report %s marked as read" % report.id, "success")
  212. return redirect(url_for("management.reports"))
  213. # mark all as read
  214. reports = Report.query.filter(Report.zapped == None).all()
  215. report_list = []
  216. for report in reports:
  217. report.zapped_by = current_user.id
  218. report.zapped = datetime.utcnow()
  219. report_list.append(report)
  220. db.session.add_all(report_list)
  221. db.session.commit()
  222. flash("All reports were marked as read", "success")
  223. return redirect(url_for("management.reports"))
  224. # Groups
  225. @management.route("/groups")
  226. @admin_required
  227. def groups():
  228. page = request.args.get("page", 1, type=int)
  229. groups = Group.query.\
  230. order_by(Group.id.asc()).\
  231. paginate(page, flaskbb_config['USERS_PER_PAGE'], False)
  232. return render_template("management/groups.html", groups=groups)
  233. @management.route("/groups/<int:group_id>/edit", methods=["GET", "POST"])
  234. @admin_required
  235. def edit_group(group_id):
  236. group = Group.query.filter_by(id=group_id).first_or_404()
  237. form = EditGroupForm(group)
  238. if form.validate_on_submit():
  239. form.populate_obj(group)
  240. group.save()
  241. flash("Group successfully edited.", "success")
  242. return redirect(url_for("management.groups", group_id=group.id))
  243. return render_template("management/group_form.html", form=form,
  244. title="Edit Group")
  245. @management.route("/groups/<int:group_id>/delete")
  246. @admin_required
  247. def delete_group(group_id):
  248. group = Group.query.filter_by(id=group_id).first_or_404()
  249. group.delete()
  250. flash("Group successfully deleted.", "success")
  251. return redirect(url_for("management.groups"))
  252. @management.route("/groups/add", methods=["GET", "POST"])
  253. @admin_required
  254. def add_group():
  255. form = AddGroupForm()
  256. if form.validate_on_submit():
  257. form.save()
  258. flash("Group successfully added.", "success")
  259. return redirect(url_for("management.groups"))
  260. return render_template("management/group_form.html", form=form,
  261. title="Add Group")
  262. # Forums and Categories
  263. @management.route("/forums")
  264. @admin_required
  265. def forums():
  266. categories = Category.query.order_by(Category.position.asc()).all()
  267. return render_template("management/forums.html", categories=categories)
  268. @management.route("/forums/<int:forum_id>/edit", methods=["GET", "POST"])
  269. @admin_required
  270. def edit_forum(forum_id):
  271. forum = Forum.query.filter_by(id=forum_id).first_or_404()
  272. form = EditForumForm(forum)
  273. if form.validate_on_submit():
  274. form.populate_obj(forum)
  275. forum.save(moderators=form.moderators.data)
  276. flash("Forum successfully edited.", "success")
  277. return redirect(url_for("management.edit_forum", forum_id=forum.id))
  278. else:
  279. if forum.moderators:
  280. form.moderators.data = ",".join([user.username
  281. for user in forum.moderators])
  282. else:
  283. form.moderators.data = None
  284. return render_template("management/forum_form.html", form=form,
  285. title="Edit Forum")
  286. @management.route("/forums/<int:forum_id>/delete")
  287. @admin_required
  288. def delete_forum(forum_id):
  289. forum = Forum.query.filter_by(id=forum_id).first_or_404()
  290. involved_users = User.query.filter(Topic.forum_id == forum.id,
  291. Post.user_id == User.id).all()
  292. forum.delete(involved_users)
  293. flash("Forum successfully deleted.", "success")
  294. return redirect(url_for("management.forums"))
  295. @management.route("/forums/add", methods=["GET", "POST"])
  296. @management.route("/forums/<int:category_id>/add", methods=["GET", "POST"])
  297. @admin_required
  298. def add_forum(category_id=None):
  299. form = AddForumForm()
  300. if form.validate_on_submit():
  301. form.save()
  302. flash("Forum successfully added.", "success")
  303. return redirect(url_for("management.forums"))
  304. else:
  305. if category_id:
  306. category = Category.query.filter_by(id=category_id).first()
  307. form.category.data = category
  308. return render_template("management/forum_form.html", form=form,
  309. title="Add Forum")
  310. @management.route("/category/add", methods=["GET", "POST"])
  311. @admin_required
  312. def add_category():
  313. form = CategoryForm()
  314. if form.validate_on_submit():
  315. form.save()
  316. flash("Category successfully created.", "success")
  317. return redirect(url_for("management.forums"))
  318. return render_template("management/category_form.html", form=form,
  319. title="Add Category")
  320. @management.route("/category/<int:category_id>/edit", methods=["GET", "POST"])
  321. @admin_required
  322. def edit_category(category_id):
  323. category = Category.query.filter_by(id=category_id).first_or_404()
  324. form = CategoryForm(obj=category)
  325. if form.validate_on_submit():
  326. form.populate_obj(category)
  327. category.save()
  328. return render_template("management/category_form.html", form=form,
  329. title="Edit Category")
  330. @management.route("/category/<int:category_id>/delete", methods=["GET", "POST"])
  331. @admin_required
  332. def delete_category(category_id):
  333. category = Category.query.filter_by(id=category_id).first_or_404()
  334. involved_users = User.query.filter(Forum.category_id == category.id,
  335. Topic.forum_id == Forum.id,
  336. Post.user_id == User.id).all()
  337. category.delete(involved_users)
  338. flash("Category with all associated forums deleted.", "success")
  339. return redirect(url_for("management.forums"))
  340. # Plugins
  341. @management.route("/plugins")
  342. @admin_required
  343. def plugins():
  344. plugins = get_all_plugins()
  345. return render_template("management/plugins.html", plugins=plugins)
  346. @management.route("/plugins/enable/<plugin>")
  347. @admin_required
  348. def enable_plugin(plugin):
  349. plugin = get_plugin_from_all(plugin)
  350. if not plugin.enabled:
  351. plugin_dir = os.path.join(
  352. os.path.abspath(os.path.dirname(os.path.dirname(__file__))),
  353. "plugins", plugin.identifier
  354. )
  355. disabled_file = os.path.join(plugin_dir, "DISABLED")
  356. os.remove(disabled_file)
  357. flash("Plugin should be enabled. Please reload your app.", "success")
  358. flash("If you are using a host which doesn't support writting on the "
  359. "disk, this won't work - than you need to delete the "
  360. "'DISABLED' file by yourself.", "info")
  361. else:
  362. flash("Plugin is not enabled", "danger")
  363. return redirect(url_for("management.plugins"))
  364. @management.route("/plugins/disable/<plugin>")
  365. @admin_required
  366. def disable_plugin(plugin):
  367. try:
  368. plugin = get_plugin(plugin)
  369. except KeyError:
  370. flash("Plugin {} not found".format(plugin), "danger")
  371. return redirect(url_for("management.plugins"))
  372. plugin_dir = os.path.join(
  373. os.path.abspath(os.path.dirname(os.path.dirname(__file__))),
  374. "plugins", plugin.identifier
  375. )
  376. disabled_file = os.path.join(plugin_dir, "DISABLED")
  377. open(disabled_file, "a").close()
  378. flash("Plugin should be disabled. Please reload your app.", "success")
  379. flash("If you are using a host which doesn't "
  380. "support writting on the disk, this won't work - than you need to "
  381. "create a 'DISABLED' file by yourself.", "info")
  382. return redirect(url_for("management.plugins"))
  383. @management.route("/plugins/uninstall/<plugin>")
  384. @admin_required
  385. def uninstall_plugin(plugin):
  386. plugin = get_plugin_from_all(plugin)
  387. if plugin.uninstallable:
  388. plugin.uninstall()
  389. Setting.invalidate_cache()
  390. flash("Plugin {} has been uninstalled.".format(plugin.name), "success")
  391. else:
  392. flash("Cannot uninstall Plugin {}".format(plugin.name), "danger")
  393. return redirect(url_for("management.plugins"))
  394. @management.route("/plugins/install/<plugin>")
  395. @admin_required
  396. def install_plugin(plugin):
  397. plugin = get_plugin_from_all(plugin)
  398. if plugin.installable and not plugin.uninstallable:
  399. plugin.install()
  400. Setting.invalidate_cache()
  401. flash("Plugin {} has been installed.".format(plugin.name), "success")
  402. else:
  403. flash("Cannot install Plugin {}".format(plugin.name), "danger")
  404. return redirect(url_for("management.plugins"))