views.py 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734
  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. jsonify, __version__ as flask_version)
  14. from flask_login import current_user
  15. from flask_plugins import get_all_plugins, get_plugin, get_plugin_from_all
  16. from flask_babelex import gettext as _
  17. from flask_allows import Permission, Not
  18. from flaskbb import __version__ as flaskbb_version
  19. from flaskbb._compat import iteritems
  20. from flaskbb.forum.forms import UserSearchForm
  21. from flaskbb.utils.settings import flaskbb_config
  22. from flaskbb.utils.requirements import (
  23. IsAtleastModerator, IsAdmin, CanBanUser, CanEditUser, IsAtleastSuperModerator
  24. )
  25. from flaskbb.extensions import db, allows
  26. from flaskbb.utils.helpers import render_template, time_diff, get_online_users
  27. from flaskbb.user.models import Guest, User, Group
  28. from flaskbb.forum.models import Post, Topic, Forum, Category, Report
  29. from flaskbb.management.models import Setting, SettingsGroup
  30. from flaskbb.management.forms import (AddUserForm, EditUserForm, AddGroupForm,
  31. EditGroupForm, EditForumForm,
  32. AddForumForm, CategoryForm)
  33. management = Blueprint("management", __name__)
  34. @management.route("/")
  35. @allows.requires(IsAtleastModerator)
  36. def overview():
  37. # user and group stats
  38. banned_users = User.query.filter(
  39. Group.banned == True,
  40. Group.id == User.primary_group_id
  41. ).count()
  42. if not current_app.config["REDIS_ENABLED"]:
  43. online_users = User.query.filter(User.lastseen >= time_diff()).count()
  44. else:
  45. online_users = len(get_online_users())
  46. stats = {
  47. # user stats
  48. "all_users": User.query.count(),
  49. "banned_users": banned_users,
  50. "online_users": online_users,
  51. "all_groups": Group.query.count(),
  52. # forum stats
  53. "report_count": Report.query.count(),
  54. "topic_count": Topic.query.count(),
  55. "post_count": Post.query.count(),
  56. # misc stats
  57. "plugins": get_all_plugins(),
  58. "python_version": "%s.%s" % (sys.version_info[0], sys.version_info[1]),
  59. "flask_version": flask_version,
  60. "flaskbb_version": flaskbb_version
  61. }
  62. return render_template("management/overview.html", **stats)
  63. @management.route("/settings", methods=["GET", "POST"])
  64. @management.route("/settings/<path:slug>", methods=["GET", "POST"])
  65. @allows.requires(IsAdmin)
  66. def settings(slug=None):
  67. slug = slug if slug else "general"
  68. # get the currently active group
  69. active_group = SettingsGroup.query.filter_by(key=slug).first_or_404()
  70. # get all groups - used to build the navigation
  71. all_groups = SettingsGroup.query.all()
  72. SettingsForm = Setting.get_form(active_group)
  73. old_settings = Setting.get_settings(active_group)
  74. new_settings = {}
  75. form = SettingsForm()
  76. if form.validate_on_submit():
  77. for key, values in iteritems(old_settings):
  78. try:
  79. # check if the value has changed
  80. if values['value'] == form[key].data:
  81. continue
  82. else:
  83. new_settings[key] = form[key].data
  84. except KeyError:
  85. pass
  86. Setting.update(settings=new_settings, app=current_app)
  87. flash(_("Settings saved."), "success")
  88. else:
  89. for key, values in iteritems(old_settings):
  90. try:
  91. form[key].data = values['value']
  92. except (KeyError, ValueError):
  93. pass
  94. return render_template("management/settings.html", form=form,
  95. all_groups=all_groups, active_group=active_group)
  96. # Users
  97. @management.route("/users", methods=['GET', 'POST'])
  98. @allows.requires(IsAtleastModerator)
  99. def users():
  100. page = request.args.get("page", 1, type=int)
  101. search_form = UserSearchForm()
  102. if search_form.validate():
  103. users = search_form.get_results().\
  104. paginate(page, flaskbb_config['USERS_PER_PAGE'], False)
  105. return render_template("management/users.html", users=users,
  106. search_form=search_form)
  107. users = User.query. \
  108. order_by(User.id.asc()).\
  109. paginate(page, flaskbb_config['USERS_PER_PAGE'], False)
  110. return render_template("management/users.html", users=users,
  111. search_form=search_form)
  112. @management.route("/users/<int:user_id>/edit", methods=["GET", "POST"])
  113. @allows.requires(IsAtleastModerator)
  114. def edit_user(user_id):
  115. user = User.query.filter_by(id=user_id).first_or_404()
  116. if not Permission(CanEditUser, identity=current_user):
  117. flash(_("You are not allowed to edit this user."), "danger")
  118. return redirect(url_for("management.users"))
  119. member_group = db.and_(*[db.not_(getattr(Group, p)) for p in
  120. ['admin', 'mod', 'super_mod', 'banned', 'guest']])
  121. filt = db.or_(Group.id.in_(g.id for g in current_user.groups), member_group)
  122. if Permission(IsAtleastSuperModerator, identity=current_user):
  123. filt = db.or_(filt, Group.mod)
  124. if Permission(IsAdmin, identity=current_user):
  125. filt = db.or_(filt, Group.admin, Group.super_mod)
  126. if Permission(CanBanUser, identity=current_user):
  127. filt = db.or_(filt, Group.banned)
  128. group_query = Group.query.filter(filt)
  129. form = EditUserForm(user)
  130. form.primary_group.query = group_query
  131. form.secondary_groups.query = group_query
  132. if form.validate_on_submit():
  133. form.populate_obj(user)
  134. user.primary_group_id = form.primary_group.data.id
  135. # Don't override the password
  136. if form.password.data:
  137. user.password = form.password.data
  138. user.save(groups=form.secondary_groups.data)
  139. flash(_("User successfully updated."), "success")
  140. return redirect(url_for("management.edit_user", user_id=user.id))
  141. return render_template("management/user_form.html", form=form,
  142. title=_("Edit User"))
  143. @management.route("/users/delete", methods=["POST"])
  144. @management.route("/users/<int:user_id>/delete", methods=["POST"])
  145. @allows.requires(IsAdmin)
  146. def delete_user(user_id=None):
  147. # ajax request
  148. if request.is_xhr:
  149. ids = request.get_json()["ids"]
  150. data = []
  151. for user in User.query.filter(User.id.in_(ids)).all():
  152. # do not delete current user
  153. if current_user.id == user.id:
  154. continue
  155. if user.delete():
  156. data.append({
  157. "id": user.id,
  158. "type": "delete",
  159. "reverse": False,
  160. "reverse_name": None,
  161. "reverse_url": None
  162. })
  163. return jsonify(
  164. message="{} Users deleted.".format(len(data)),
  165. category="success",
  166. data=data,
  167. status=200
  168. )
  169. user = User.query.filter_by(id=user_id).first_or_404()
  170. if current_user.id == user.id:
  171. flash(_("You cannot delete yourself.", "danger"))
  172. return redirect(url_for("management.users"))
  173. user.delete()
  174. flash(_("User successfully deleted."), "success")
  175. return redirect(url_for("management.users"))
  176. @management.route("/users/add", methods=["GET", "POST"])
  177. @allows.requires(IsAdmin)
  178. def add_user():
  179. form = AddUserForm()
  180. if form.validate_on_submit():
  181. form.save()
  182. flash(_("User successfully added."), "success")
  183. return redirect(url_for("management.users"))
  184. return render_template("management/user_form.html", form=form,
  185. title=_("Add User"))
  186. @management.route("/users/banned", methods=["GET", "POST"])
  187. @allows.requires(IsAtleastModerator)
  188. def banned_users():
  189. page = request.args.get("page", 1, type=int)
  190. search_form = UserSearchForm()
  191. users = User.query.filter(
  192. Group.banned == True,
  193. Group.id == User.primary_group_id
  194. ).paginate(page, flaskbb_config['USERS_PER_PAGE'], False)
  195. if search_form.validate():
  196. users = search_form.get_results().\
  197. paginate(page, flaskbb_config['USERS_PER_PAGE'], False)
  198. return render_template("management/banned_users.html", users=users,
  199. search_form=search_form)
  200. return render_template("management/banned_users.html", users=users,
  201. search_form=search_form)
  202. @management.route("/users/ban", methods=["POST"])
  203. @management.route("/users/<int:user_id>/ban", methods=["POST"])
  204. @allows.requires(IsAtleastModerator)
  205. def ban_user(user_id=None):
  206. if not Permission(CanBanUser, identity=current_user):
  207. flash(_("You do not have the permissions to ban this user."), "danger")
  208. return redirect(url_for("management.overview"))
  209. # ajax request
  210. if request.is_xhr:
  211. ids = request.get_json()["ids"]
  212. data = []
  213. users = User.query.filter(User.id.in_(ids)).all()
  214. for user in users:
  215. # don't let a user ban himself and do not allow a moderator to ban
  216. # a admin user
  217. if (
  218. current_user.id == user.id or
  219. Permission(IsAdmin, identity=user)
  220. and Permission(Not(IsAdmin), current_user)
  221. ):
  222. continue
  223. elif user.ban():
  224. data.append({
  225. "id": user.id,
  226. "type": "ban",
  227. "reverse": "unban",
  228. "reverse_name": _("Unban"),
  229. "reverse_url": url_for("management.unban_user",
  230. user_id=user.id)
  231. })
  232. return jsonify(
  233. message="{} Users banned.".format(len(data)),
  234. category="success",
  235. data=data,
  236. status=200
  237. )
  238. user = User.query.filter_by(id=user_id).first_or_404()
  239. # Do not allow moderators to ban admins
  240. if Permission(IsAdmin, identity=user) and \
  241. Permission(Not(IsAdmin), identity=current_user):
  242. flash(_("A moderator cannot ban an admin user."), "danger")
  243. return redirect(url_for("management.overview"))
  244. if not current_user.id == user.id and user.ban():
  245. flash(_("User is now banned."), "success")
  246. else:
  247. flash(_("Could not ban user."), "danger")
  248. return redirect(url_for("management.banned_users"))
  249. @management.route("/users/unban", methods=["POST"])
  250. @management.route("/users/<int:user_id>/unban", methods=["POST"])
  251. @allows.requires(IsAtleastModerator)
  252. def unban_user(user_id=None):
  253. if not Permission(CanBanUser, identity=current_user):
  254. flash(_("You do not have the permissions to unban this user."),
  255. "danger")
  256. return redirect(url_for("management.overview"))
  257. # ajax request
  258. if request.is_xhr:
  259. ids = request.get_json()["ids"]
  260. data = []
  261. for user in User.query.filter(User.id.in_(ids)).all():
  262. if user.unban():
  263. data.append({
  264. "id": user.id,
  265. "type": "unban",
  266. "reverse": "ban",
  267. "reverse_name": _("Ban"),
  268. "reverse_url": url_for("management.ban_user",
  269. user_id=user.id)
  270. })
  271. return jsonify(
  272. message="{} Users unbanned.".format(len(data)),
  273. category="success",
  274. data=data,
  275. status=200
  276. )
  277. user = User.query.filter_by(id=user_id).first_or_404()
  278. if user.unban():
  279. flash(_("User is now unbanned."), "success")
  280. else:
  281. flash(_("Could not unban user."), "danger")
  282. return redirect(url_for("management.banned_users"))
  283. # Reports
  284. @management.route("/reports")
  285. @allows.requires(IsAtleastModerator)
  286. def reports():
  287. page = request.args.get("page", 1, type=int)
  288. reports = Report.query.\
  289. order_by(Report.id.asc()).\
  290. paginate(page, flaskbb_config['USERS_PER_PAGE'], False)
  291. return render_template("management/reports.html", reports=reports)
  292. @management.route("/reports/unread")
  293. @allows.requires(IsAtleastModerator)
  294. def unread_reports():
  295. page = request.args.get("page", 1, type=int)
  296. reports = Report.query.\
  297. filter(Report.zapped == None).\
  298. order_by(Report.id.desc()).\
  299. paginate(page, flaskbb_config['USERS_PER_PAGE'], False)
  300. return render_template("management/unread_reports.html", reports=reports)
  301. @management.route("/reports/<int:report_id>/markread", methods=["POST"])
  302. @management.route("/reports/markread", methods=["POST"])
  303. @allows.requires(IsAtleastModerator)
  304. def report_markread(report_id=None):
  305. # AJAX request
  306. if request.is_xhr:
  307. ids = request.get_json()["ids"]
  308. data = []
  309. for report in Report.query.filter(Report.id.in_(ids)).all():
  310. report.zapped_by = current_user.id
  311. report.zapped = datetime.utcnow()
  312. report.save()
  313. data.append({
  314. "id": report.id,
  315. "type": "read",
  316. "reverse": False,
  317. "reverse_name": None,
  318. "reverse_url": None
  319. })
  320. return jsonify(
  321. message="{} Reports marked as read.".format(len(data)),
  322. category="success",
  323. data=data,
  324. status=200
  325. )
  326. # mark single report as read
  327. if report_id:
  328. report = Report.query.filter_by(id=report_id).first_or_404()
  329. if report.zapped:
  330. flash(_("Report %(id)s is already marked as read.", id=report.id),
  331. "success")
  332. return redirect(url_for("management.reports"))
  333. report.zapped_by = current_user.id
  334. report.zapped = datetime.utcnow()
  335. report.save()
  336. flash(_("Report %(id)s marked as read.", id=report.id), "success")
  337. return redirect(url_for("management.reports"))
  338. # mark all as read
  339. reports = Report.query.filter(Report.zapped == None).all()
  340. report_list = []
  341. for report in reports:
  342. report.zapped_by = current_user.id
  343. report.zapped = datetime.utcnow()
  344. report_list.append(report)
  345. db.session.add_all(report_list)
  346. db.session.commit()
  347. flash(_("All reports were marked as read."), "success")
  348. return redirect(url_for("management.reports"))
  349. # Groups
  350. @management.route("/groups")
  351. @allows.requires(IsAdmin)
  352. def groups():
  353. page = request.args.get("page", 1, type=int)
  354. groups = Group.query.\
  355. order_by(Group.id.asc()).\
  356. paginate(page, flaskbb_config['USERS_PER_PAGE'], False)
  357. return render_template("management/groups.html", groups=groups)
  358. @management.route("/groups/<int:group_id>/edit", methods=["GET", "POST"])
  359. @allows.requires(IsAdmin)
  360. def edit_group(group_id):
  361. group = Group.query.filter_by(id=group_id).first_or_404()
  362. form = EditGroupForm(group)
  363. if form.validate_on_submit():
  364. form.populate_obj(group)
  365. group.save()
  366. if group.guest:
  367. Guest.invalidate_cache()
  368. flash(_("Group successfully updated."), "success")
  369. return redirect(url_for("management.groups", group_id=group.id))
  370. return render_template("management/group_form.html", form=form,
  371. title=_("Edit Group"))
  372. @management.route("/groups/<int:group_id>/delete", methods=["POST"])
  373. @management.route("/groups/delete", methods=["POST"])
  374. @allows.requires(IsAdmin)
  375. def delete_group(group_id=None):
  376. if request.is_xhr:
  377. ids = request.get_json()["ids"]
  378. if not (set(ids) & set(["1", "2", "3", "4", "5"])):
  379. data = []
  380. for group in Group.query.filter(Group.id.in_(ids)).all():
  381. group.delete()
  382. data.append({
  383. "id": group.id,
  384. "type": "delete",
  385. "reverse": False,
  386. "reverse_name": None,
  387. "reverse_url": None
  388. })
  389. return jsonify(
  390. message="{} Groups deleted.".format(len(data)),
  391. category="success",
  392. data=data,
  393. status=200
  394. )
  395. return jsonify(
  396. message=_("You cannot delete one of the standard groups."),
  397. category="danger",
  398. data=None,
  399. status=404
  400. )
  401. if group_id is not None:
  402. if group_id <= 5: # there are 5 standard groups
  403. flash(_("You cannot delete the standard groups. "
  404. "Try renaming them instead.", "danger"))
  405. return redirect(url_for("management.groups"))
  406. group = Group.query.filter_by(id=group_id).first_or_404()
  407. group.delete()
  408. flash(_("Group successfully deleted."), "success")
  409. return redirect(url_for("management.groups"))
  410. flash(_("No group choosen.."), "danger")
  411. return redirect(url_for("management.groups"))
  412. @management.route("/groups/add", methods=["GET", "POST"])
  413. @allows.requires(IsAdmin)
  414. def add_group():
  415. form = AddGroupForm()
  416. if form.validate_on_submit():
  417. form.save()
  418. flash(_("Group successfully added."), "success")
  419. return redirect(url_for("management.groups"))
  420. return render_template("management/group_form.html", form=form,
  421. title=_("Add Group"))
  422. # Forums and Categories
  423. @management.route("/forums")
  424. @allows.requires(IsAdmin)
  425. def forums():
  426. categories = Category.query.order_by(Category.position.asc()).all()
  427. return render_template("management/forums.html", categories=categories)
  428. @management.route("/forums/<int:forum_id>/edit", methods=["GET", "POST"])
  429. @allows.requires(IsAdmin)
  430. def edit_forum(forum_id):
  431. forum = Forum.query.filter_by(id=forum_id).first_or_404()
  432. form = EditForumForm(forum)
  433. if form.validate_on_submit():
  434. form.save()
  435. flash(_("Forum successfully updated."), "success")
  436. return redirect(url_for("management.edit_forum", forum_id=forum.id))
  437. else:
  438. if forum.moderators:
  439. form.moderators.data = ",".join([
  440. user.username for user in forum.moderators
  441. ])
  442. else:
  443. form.moderators.data = None
  444. return render_template("management/forum_form.html", form=form,
  445. title=_("Edit Forum"))
  446. @management.route("/forums/<int:forum_id>/delete", methods=["POST"])
  447. @allows.requires(IsAdmin)
  448. def delete_forum(forum_id):
  449. forum = Forum.query.filter_by(id=forum_id).first_or_404()
  450. involved_users = User.query.filter(Topic.forum_id == forum.id,
  451. Post.user_id == User.id).all()
  452. forum.delete(involved_users)
  453. flash(_("Forum successfully deleted."), "success")
  454. return redirect(url_for("management.forums"))
  455. @management.route("/forums/add", methods=["GET", "POST"])
  456. @management.route("/forums/<int:category_id>/add", methods=["GET", "POST"])
  457. @allows.requires(IsAdmin)
  458. def add_forum(category_id=None):
  459. form = AddForumForm()
  460. if form.validate_on_submit():
  461. form.save()
  462. flash(_("Forum successfully added."), "success")
  463. return redirect(url_for("management.forums"))
  464. else:
  465. form.groups.data = Group.query.order_by(Group.id.asc()).all()
  466. if category_id:
  467. category = Category.query.filter_by(id=category_id).first()
  468. form.category.data = category
  469. return render_template("management/forum_form.html", form=form,
  470. title=_("Add Forum"))
  471. @management.route("/category/add", methods=["GET", "POST"])
  472. @allows.requires(IsAdmin)
  473. def add_category():
  474. form = CategoryForm()
  475. if form.validate_on_submit():
  476. form.save()
  477. flash(_("Category successfully added."), "success")
  478. return redirect(url_for("management.forums"))
  479. return render_template("management/category_form.html", form=form,
  480. title=_("Add Category"))
  481. @management.route("/category/<int:category_id>/edit", methods=["GET", "POST"])
  482. @allows.requires(IsAdmin)
  483. def edit_category(category_id):
  484. category = Category.query.filter_by(id=category_id).first_or_404()
  485. form = CategoryForm(obj=category)
  486. if form.validate_on_submit():
  487. form.populate_obj(category)
  488. flash(_("Category successfully updated."), "success")
  489. category.save()
  490. return render_template("management/category_form.html", form=form,
  491. title=_("Edit Category"))
  492. @management.route("/category/<int:category_id>/delete", methods=["POST"])
  493. @allows.requires(IsAdmin)
  494. def delete_category(category_id):
  495. category = Category.query.filter_by(id=category_id).first_or_404()
  496. involved_users = User.query.filter(Forum.category_id == category.id,
  497. Topic.forum_id == Forum.id,
  498. Post.user_id == User.id).all()
  499. category.delete(involved_users)
  500. flash(_("Category with all associated forums deleted."), "success")
  501. return redirect(url_for("management.forums"))
  502. # Plugins
  503. @management.route("/plugins")
  504. @allows.requires(IsAdmin)
  505. def plugins():
  506. plugins = get_all_plugins()
  507. return render_template("management/plugins.html", plugins=plugins)
  508. @management.route("/plugins/<path:plugin>/enable", methods=["POST"])
  509. @allows.requires(IsAdmin)
  510. def enable_plugin(plugin):
  511. plugin = get_plugin_from_all(plugin)
  512. if not plugin.enabled:
  513. plugin_dir = os.path.join(
  514. os.path.abspath(os.path.dirname(os.path.dirname(__file__))),
  515. "plugins", plugin.identifier
  516. )
  517. disabled_file = os.path.join(plugin_dir, "DISABLED")
  518. try:
  519. if os.path.exists(disabled_file):
  520. os.remove(disabled_file)
  521. flash(_("Plugin is enabled. Please reload your app."),
  522. "success")
  523. else:
  524. flash(_("Plugin is already enabled. Please reload your app."),
  525. "warning")
  526. except OSError:
  527. flash(_("If you are using a host which doesn't support writting "
  528. "on the disk, this won't work - than you need to delete "
  529. "the 'DISABLED' file by yourself."), "danger")
  530. else:
  531. flash(_("Couldn't enable Plugin."), "danger")
  532. return redirect(url_for("management.plugins"))
  533. @management.route("/plugins/<path:plugin>/disable", methods=["POST"])
  534. @allows.requires(IsAdmin)
  535. def disable_plugin(plugin):
  536. try:
  537. plugin = get_plugin(plugin)
  538. except KeyError:
  539. flash(_("Plugin %(plugin)s not found.", plugin=plugin.name), "danger")
  540. return redirect(url_for("management.plugins"))
  541. plugin_dir = os.path.join(
  542. os.path.abspath(os.path.dirname(os.path.dirname(__file__))),
  543. "plugins", plugin.identifier
  544. )
  545. disabled_file = os.path.join(plugin_dir, "DISABLED")
  546. try:
  547. open(disabled_file, "a").close()
  548. flash(_("Plugin is disabled. Please reload your app."), "success")
  549. except OSError:
  550. flash(_("If you are using a host which doesn't "
  551. "support writting on the disk, this won't work - than you "
  552. "need to create a 'DISABLED' file by yourself."), "info")
  553. return redirect(url_for("management.plugins"))
  554. @management.route("/plugins/<path:plugin>/uninstall", methods=["POST"])
  555. @allows.requires(IsAdmin)
  556. def uninstall_plugin(plugin):
  557. plugin = get_plugin_from_all(plugin)
  558. if plugin.uninstallable:
  559. plugin.uninstall()
  560. Setting.invalidate_cache()
  561. flash(_("Plugin has been uninstalled."), "success")
  562. else:
  563. flash(_("Cannot uninstall Plugin."), "danger")
  564. return redirect(url_for("management.plugins"))
  565. @management.route("/plugins/<path:plugin>/install", methods=["POST"])
  566. @allows.requires(IsAdmin)
  567. def install_plugin(plugin):
  568. plugin = get_plugin_from_all(plugin)
  569. if plugin.installable and not plugin.uninstallable:
  570. plugin.install()
  571. Setting.invalidate_cache()
  572. flash(_("Plugin has been installed."), "success")
  573. else:
  574. flash(_("Cannot install Plugin."), "danger")
  575. return redirect(url_for("management.plugins"))