class DisablePlugin(MethodView): decorators = [ allows.requires(IsAdmin, on_fail=FlashAndRedirect( message=_("You are not allowed to modify plugins"), level="danger", endpoint="management.overview")) ] def post(self, name): validate_plugin(name) plugin = PluginRegistry.query.filter_by(name=name).first_or_404() if not plugin.enabled: flash( _("Plugin %(plugin)s is already disabled.", plugin=plugin.name), "info") return redirect(url_for("management.plugins")) plugin.enabled = False plugin.save() flash( _("Plugin %(plugin)s disabled. Please restart FlaskPet now.", plugin=plugin.name), "success") return redirect(url_for("management.plugins"))
class ViewForum(MethodView): decorators = [allows.requires( CanAccessForum(), on_fail=FlashAndRedirect( message=_("You are not allowed to access that forum"), level="warning", endpoint=lambda *a, **k: current_category.url ) )] def get(self, forum_id, slug=None): page = request.args.get("page", 1, type=int) forum_instance, forumsread = Forum.get_forum( forum_id=forum_id, user=real(current_user) ) if forum_instance.external: return redirect(forum_instance.external) topics = Forum.get_topics( forum_id=forum_instance.id, user=real(current_user), page=page, per_page=flaskpet_config["TOPICS_PER_PAGE"] ) return render_template( "forum/forum.html", forum=forum_instance, topics=topics, forumsread=forumsread, )
class EditGroup(MethodView): decorators = [ allows.requires(IsAdmin, on_fail=FlashAndRedirect( message=_("You are not allowed to modify groups."), level="danger", endpoint="management.overview")) ] form = EditGroupForm def get(self, group_id): group = Group.query.filter_by(id=group_id).first_or_404() form = self.form(group) return render_template('management/group_form.html', form=form, title=_('Edit Group')) def post(self, group_id): group = Group.query.filter_by(id=group_id).first_or_404() form = EditGroupForm(group) if form.validate_on_submit(): form.populate_obj(group) group.save() if group.guest: Guest.invalidate_cache() flash(_('Group updated.'), 'success') return redirect(url_for('management.groups', group_id=group.id)) return render_template('management/group_form.html', form=form, title=_('Edit Group'))
class BanUser(MethodView): decorators = [ allows.requires(IsAtleastModerator, on_fail=FlashAndRedirect( message=_("You are not allowed to manage users"), level="danger", endpoint="management.overview")) ] def post(self, user_id=None): if not Permission(CanBanUser, identity=current_user): flash(_("You do not have the permissions to ban this user."), "danger") return redirect(url_for("management.overview")) # ajax request if request.is_xhr: ids = request.get_json()["ids"] data = [] users = User.query.filter(User.id.in_(ids)).all() for user in users: # don't let a user ban himself and do not allow a moderator # to ban a admin user if (current_user.id == user.id or Permission(IsAdmin, identity=user) and Permission(Not(IsAdmin), current_user)): continue elif user.ban(): data.append({ "id": user.id, "type": "ban", "reverse": "unban", "reverse_name": _("Unban"), "reverse_url": url_for("management.unban_user", user_id=user.id) }) return jsonify(message="{} users banned.".format(len(data)), category="success", data=data, status=200) user = User.query.filter_by(id=user_id).first_or_404() # Do not allow moderators to ban admins if Permission(IsAdmin, identity=user) and Permission( Not(IsAdmin), identity=current_user): flash(_("A moderator cannot ban an admin user."), "danger") return redirect(url_for("management.overview")) if not current_user.id == user.id and user.ban(): flash(_("User is now banned."), "success") else: flash(_("Could not ban user."), "danger") return redirect(url_for("management.banned_users"))
class AddCategory(MethodView): decorators = [ allows.requires( IsAdmin, on_fail=FlashAndRedirect( message=_("You are not allowed to modify categories"), level="danger", endpoint="management.overview")) ] form = CategoryForm def get(self): return render_template('management/category_form.html', form=self.form(), title=_('Add Category')) def post(self): form = self.form() if form.validate_on_submit(): form.save() flash(_('Category added.'), 'success') return redirect(url_for('management.forums')) return render_template('management/category_form.html', form=form, title=_('Add Category'))
class DeletePost(MethodView): decorators = [ login_required, allows.requires( CanDeletePost, on_fail=FlashAndRedirect( message=_("You are not allowed to delete this post"), level="danger", endpoint=lambda *a, **k: current_topic.url ) ), ] def post(self, post_id): post = Post.query.filter_by(id=post_id).first_or_404() first_post = post.first_post topic_url = post.topic.url forum_url = post.topic.forum.url post.delete() # If the post was the first post in the topic, redirect to the forums if first_post: return redirect(forum_url) return redirect(topic_url)
class EditCategory(MethodView): decorators = [ allows.requires( IsAdmin, on_fail=FlashAndRedirect( message=_("You are not allowed to modify categories"), level="danger", endpoint="management.overview")) ] form = CategoryForm def get(self, category_id): category = Category.query.filter_by(id=category_id).first_or_404() form = self.form(obj=category) return render_template('management/category_form.html', form=form, title=_('Edit Category')) def post(self, category_id): category = Category.query.filter_by(id=category_id).first_or_404() form = self.form(obj=category) if form.validate_on_submit(): form.populate_obj(category) flash(_('Category updated.'), 'success') category.save() return render_template('management/category_form.html', form=form, title=_('Edit Category'))
class DeleteReport(MethodView): decorators = [ allows.requires(IsAtleastModerator, on_fail=FlashAndRedirect( message=_("You are not allowed to view reports."), level="danger", endpoint="management.overview")) ] def post(self, report_id=None): if request.is_xhr: ids = request.get_json()["ids"] data = [] for report in Report.query.filter(Report.id.in_(ids)).all(): if report.delete(): data.append({ "id": report.id, "type": "delete", "reverse": False, "reverse_name": None, "reverse_url": None }) return jsonify(message="{} reports deleted.".format(len(data)), category="success", data=data, status=200) report = Report.query.filter_by(id=report_id).first_or_404() report.delete() flash(_("Report deleted."), "success") return redirect(url_for("management.reports"))
class NewTopic(MethodView): decorators = [ login_required, allows.requires( CanAccessForum(), CanPostTopic, on_fail=FlashAndRedirect( message=_("You are not allowed to post a topic here"), level="warning", endpoint=lambda *a, **k: current_forum.url ) ), ] def get(self, forum_id, slug=None): forum_instance = Forum.query.filter_by(id=forum_id).first_or_404() return render_template( "forum/new_topic.html", forum=forum_instance, form=self.form() ) def post(self, forum_id, slug=None): forum_instance = Forum.query.filter_by(id=forum_id).first_or_404() form = self.form() if form.validate_on_submit(): topic = form.save(real(current_user), forum_instance) return redirect(url_for("forum.view_topic", topic_id=topic.id)) return render_template( "forum/new_topic.html", forum=forum_instance, form=form ) def form(self): current_app.pluggy.hook.flaskpet_form_new_topic(form=NewTopicForm) return NewTopicForm()
class ViewPost(MethodView): decorators = [allows.requires( CanAccessForum(), on_fail=FlashAndRedirect( message=_("You are not allowed to access that topic"), level="warning", endpoint=lambda *a, **k: current_category.url ) )] def get(self, post_id): """Redirects to a post in a topic.""" post = Post.query.filter_by(id=post_id).first_or_404() post_in_topic = Post.query.filter( Post.topic_id == post.topic_id, Post.id <= post_id ).order_by(Post.id.asc()).count() page = int( math.ceil(post_in_topic / float(flaskpet_config["POSTS_PER_PAGE"])) ) return redirect( url_for( "forum.view_topic", topic_id=post.topic.id, slug=post.topic.slug, page=page, _anchor="pid{}".format(post.id) ) )
class PluginsView(MethodView): decorators = [ allows.requires(IsAdmin, on_fail=FlashAndRedirect( message=_("You are not allowed to modify plugins"), level="danger", endpoint="management.overview")) ] def get(self): plugins = PluginRegistry.query.all() return render_template("management/plugins.html", plugins=plugins)
class Forums(MethodView): decorators = [ allows.requires(IsAdmin, on_fail=FlashAndRedirect( message=_("You are not allowed to modify forums."), level="danger", endpoint="management.overview")) ] def get(self): categories = Category.query.order_by(Category.position.asc()).all() return render_template("management/forums.html", categories=categories)
class ManagementOverview(MethodView): decorators = [ allows.requires( IsAtleastModerator, on_fail=FlashAndRedirect(message=_( "You are not allowed to access the management panel"), level="danger", endpoint="forum.index")) ] def get(self): # user and group stats banned_users = User.query.filter( Group.banned == True, Group.id == User.primary_group_id).count() if not current_app.config["REDIS_ENABLED"]: online_users = User.query.filter( User.lastseen >= time_diff()).count() else: online_users = len(get_online_users()) unread_reports = Report.query.\ filter(Report.zapped == None).\ order_by(Report.id.desc()).\ count() python_version = "{}.{}.{}".format(sys.version_info[0], sys.version_info[1], sys.version_info[2]) stats = { "current_app": current_app, "unread_reports": unread_reports, # stats stats "all_users": User.query.count(), "banned_users": banned_users, "online_users": online_users, "all_groups": Group.query.count(), "report_count": Report.query.count(), "topic_count": Topic.query.count(), "post_count": Post.query.count(), # components "python_version": python_version, "celery_version": celery_version, "flask_version": flask_version, "flaskpet_version": flaskpet_version, # plugins "plugins": PluginRegistry.query.all() } return render_template("management/overview.html", **stats)
class DeleteGroup(MethodView): decorators = [ allows.requires(IsAdmin, on_fail=FlashAndRedirect( message=_("You are not allowed to modify groups."), level="danger", endpoint="management.overview")) ] def post(self, group_id=None): if request.is_xhr: ids = request.get_json()["ids"] # TODO: Get rid of magic numbers if not (set(ids) & set(["1", "2", "3", "4", "5", "6"])): data = [] for group in Group.query.filter(Group.id.in_(ids)).all(): group.delete() data.append({ "id": group.id, "type": "delete", "reverse": False, "reverse_name": None, "reverse_url": None }) return jsonify(message="{} groups deleted.".format(len(data)), category="success", data=data, status=200) return jsonify( message=_("You cannot delete one of the standard groups."), category="danger", data=None, status=404) if group_id is not None: if group_id <= 6: # there are 6 standard groups flash( _( "You cannot delete the standard groups. " "Try renaming it instead.", "danger")) return redirect(url_for("management.groups")) group = Group.query.filter_by(id=group_id).first_or_404() group.delete() flash(_("Group deleted."), "success") return redirect(url_for("management.groups")) flash(_("No group chosen."), "danger") return redirect(url_for("management.groups"))
class RawPost(MethodView): decorators = [ login_required, allows.requires( CanAccessForum(), on_fail=FlashAndRedirect( message=_("You are not allowed to access that forum"), level="warning", endpoint=lambda *a, **k: current_category.url ) ), ] def get(self, post_id): post = Post.query.filter_by(id=post_id).first_or_404() return format_quote(username=post.username, content=post.content)
class UninstallPlugin(MethodView): decorators = [ allows.requires(IsAdmin, on_fail=FlashAndRedirect( message=_("You are not allowed to modify plugins"), level="danger", endpoint="management.overview")) ] def post(self, name): validate_plugin(name) plugin = PluginRegistry.query.filter_by(name=name).first_or_404() PluginStore.query.filter_by(plugin_id=plugin.id).delete() db.session.commit() flash(_("Plugin has been uninstalled."), "success") return redirect(url_for("management.plugins"))
class Reports(MethodView): decorators = [ allows.requires(IsAtleastModerator, on_fail=FlashAndRedirect( message=_("You are not allowed to view reports."), level="danger", endpoint="management.overview")) ] def get(self): page = request.args.get("page", 1, type=int) reports = Report.query.\ order_by(Report.id.asc()).\ paginate(page, flaskpet_config['USERS_PER_PAGE'], False) return render_template("management/reports.html", reports=reports)
class NewPost(MethodView): decorators = [ login_required, allows.requires( CanAccessForum(), CanPostReply, on_fail=FlashAndRedirect( message=_("You are not allowed to post a reply"), level="warning", endpoint=lambda *a, **k: url_for( "forum.view_topic", topic_id=k["topic_id"], ) ) ), ] def get(self, topic_id, slug=None, post_id=None): topic = Topic.query.filter_by(id=topic_id).first_or_404() form = self.form() if post_id is not None: post = Post.query.filter_by(id=post_id).first_or_404() form.content.data = format_quote(post.username, post.content) return render_template( "forum/new_post.html", topic=topic, form=form ) def post(self, topic_id, slug=None, post_id=None): topic = Topic.query.filter_by(id=topic_id).first_or_404() form = self.form() # check if topic exists if post_id is not None: post = Post.query.filter_by(id=post_id).first_or_404() if form.validate_on_submit(): post = form.save(real(current_user), topic) return redirect(url_for("forum.view_post", post_id=post.id)) return render_template("forum/new_post.html", topic=topic, form=form) def form(self): current_app.pluggy.hook.flaskpet_form_new_post(form=ReplyForm) return ReplyForm()
class Groups(MethodView): decorators = [ allows.requires(IsAdmin, on_fail=FlashAndRedirect( message=_("You are not allowed to modify groups."), level="danger", endpoint="management.overview")) ] def get(self): page = request.args.get("page", 1, type=int) groups = Group.query.\ order_by(Group.id.asc()).\ paginate(page, flaskpet_config['USERS_PER_PAGE'], False) return render_template("management/groups.html", groups=groups)
class UntrackTopic(MethodView): decorators = [ login_required, allows.requires( CanAccessForum(), on_fail=FlashAndRedirect( message=_("You are not allowed to access that forum"), level="warning", endpoint=lambda *a, **k: current_category.url ) ), ] def post(self, topic_id, slug=None): topic = Topic.query.filter_by(id=topic_id).first_or_404() real(current_user).untrack_topic(topic) real(current_user).save() return redirect(topic.url)
class EditForum(MethodView): decorators = [ allows.requires(IsAdmin, on_fail=FlashAndRedirect( message=_("You are not allowed to modify forums."), level="danger", endpoint="management.overview")) ] form = EditForumForm def get(self, forum_id): forum = Forum.query.filter_by(id=forum_id).first_or_404() form = self.form(forum) if forum.moderators: form.moderators.data = ','.join( [user.username for user in forum.moderators]) else: form.moderators.data = None return render_template('management/forum_form.html', form=form, title=_('Edit Forum')) def post(self, forum_id): forum = Forum.query.filter_by(id=forum_id).first_or_404() form = self.form(forum) if form.validate_on_submit(): form.save() flash(_('Forum updated.'), 'success') return redirect(url_for('management.edit_forum', forum_id=forum.id)) else: if forum.moderators: form.moderators.data = ','.join( [user.username for user in forum.moderators]) else: form.moderators.data = None return render_template('management/forum_form.html', form=form, title=_('Edit Forum'))
class DeleteForum(MethodView): decorators = [ allows.requires(IsAdmin, on_fail=FlashAndRedirect( message=_("You are not allowed to modify forums"), level="danger", endpoint="management.overview")) ] def post(self, forum_id): forum = Forum.query.filter_by(id=forum_id).first_or_404() involved_users = User.query.filter(Topic.forum_id == forum.id, Post.user_id == User.id).all() forum.delete(involved_users) flash(_("Forum deleted."), "success") return redirect(url_for("management.forums"))
class DeleteUser(MethodView): decorators = [ allows.requires(IsAdmin, on_fail=FlashAndRedirect( message=_("You are not allowed to manage users"), level="danger", endpoint="management.overview")) ] def post(self, user_id=None): # ajax request if request.is_xhr: ids = request.get_json()["ids"] data = [] for user in User.query.filter(User.id.in_(ids)).all(): # do not delete current user if current_user.id == user.id: continue if user.delete(): data.append({ "id": user.id, "type": "delete", "reverse": False, "reverse_name": None, "reverse_url": None }) return jsonify(message="{} users deleted.".format(len(data)), category="success", data=data, status=200) user = User.query.filter_by(id=user_id).first_or_404() if current_user.id == user.id: flash(_("You cannot delete yourself.", "danger")) return redirect(url_for("management.users")) user.delete() flash(_("User deleted."), "success") return redirect(url_for("management.users"))
class TrivializeTopic(MethodView): decorators = [ login_required, allows.requires( IsAtleastModeratorInForum(), on_fail=FlashAndRedirect( message=_("You are not allowed to trivialize this topic"), level="danger", # TODO(anr): consider the referrer -- for now, back to topic endpoint=lambda *a, **k: current_topic.url ) ), ] def post(self, topic_id=None, slug=None): topic = Topic.query.filter_by(id=topic_id).first_or_404() topic.important = False topic.save() return redirect(topic.url)
class BannedUsers(MethodView): decorators = [ allows.requires(IsAtleastModerator, on_fail=FlashAndRedirect( message=_("You are not allowed to manage users"), level="danger", endpoint="management.overview")) ] form = UserSearchForm def get(self): page = request.args.get('page', 1, type=int) search_form = self.form() users = User.query.filter(Group.banned == True, Group.id == User.primary_group_id).paginate( page, flaskpet_config['USERS_PER_PAGE'], False) return render_template('management/banned_users.html', users=users, search_form=search_form) def post(self): page = request.args.get('page', 1, type=int) search_form = self.form() users = User.query.filter(Group.banned == True, Group.id == User.primary_group_id).paginate( page, flaskpet_config['USERS_PER_PAGE'], False) if search_form.validate(): users = search_form.get_results().\ paginate(page, flaskpet_config['USERS_PER_PAGE'], False) return render_template('management/banned_users.html', users=users, search_form=search_form) return render_template('management/banned_users.html', users=users, search_form=search_form)
class DeleteCategory(MethodView): decorators = [ allows.requires( IsAdmin, on_fail=FlashAndRedirect( message=_("You are not allowed to modify categories"), level="danger", endpoint="management.overview")) ] def post(self, category_id): category = Category.query.filter_by(id=category_id).first_or_404() involved_users = User.query.filter(Forum.category_id == category.id, Topic.forum_id == Forum.id, Post.user_id == User.id).all() category.delete(involved_users) flash(_("Category with all associated forums deleted."), "success") return redirect(url_for("management.forums"))
class DeleteTopic(MethodView): decorators = [ login_required, allows.requires( CanDeleteTopic, on_fail=FlashAndRedirect( message=_("You are not allowed to delete this topic"), level="danger", # TODO(anr): consider the referrer -- for now, back to topic endpoint=lambda *a, **k: current_topic.url ) ), ] def post(self, topic_id, slug=None): topic = Topic.query.filter_by(id=topic_id).first_or_404() involved_users = User.query.filter( Post.topic_id == topic.id, User.id == Post.user_id ).all() topic.delete(users=involved_users) return redirect(url_for("forum.view_forum", forum_id=topic.forum_id))
class InstallPlugin(MethodView): decorators = [ allows.requires(IsAdmin, on_fail=FlashAndRedirect( message=_("You are not allowed to modify plugins"), level="danger", endpoint="management.overview")) ] def post(self, name): plugin_module = validate_plugin(name) plugin = PluginRegistry.query.filter_by(name=name).first_or_404() if not plugin.enabled: flash( _("Can't install plugin. Enable '%(plugin)s' plugin first.", plugin=plugin.name), "danger") return redirect(url_for("management.plugins")) plugin.add_settings(plugin_module.SETTINGS) flash(_("Plugin has been installed."), "success") return redirect(url_for("management.plugins"))
class AddForum(MethodView): decorators = [ allows.requires(IsAdmin, on_fail=FlashAndRedirect( message=_("You are not allowed to modify forums."), level="danger", endpoint="management.overview")) ] form = AddForumForm def get(self, category_id=None): form = self.form() form.groups.data = Group.query.order_by(Group.id.asc()).all() if category_id: category = Category.query.filter_by(id=category_id).first() form.category.data = category return render_template('management/forum_form.html', form=form, title=_('Add Forum')) def post(self, category_id=None): form = self.form() if form.validate_on_submit(): form.save() flash(_('Forum added.'), 'success') return redirect(url_for('management.forums')) else: form.groups.data = Group.query.order_by(Group.id.asc()).all() if category_id: category = Category.query.filter_by(id=category_id).first() form.category.data = category return render_template('management/forum_form.html', form=form, title=_('Add Forum'))
class EditPost(MethodView): decorators = [ allows.requires( CanEditPost, on_fail=FlashAndRedirect( message=_("You are not allowed to edit that post"), level="danger", endpoint=lambda *a, **k: current_topic.url ) ), login_required ] def get(self, post_id): post = Post.query.filter_by(id=post_id).first_or_404() form = self.form(obj=post) return render_template( "forum/new_post.html", topic=post.topic, form=form, edit_mode=True ) def post(self, post_id): post = Post.query.filter_by(id=post_id).first_or_404() form = self.form(obj=post) if form.validate_on_submit(): form.populate_obj(post) post.date_modified = time_utcnow() post.modified_by = real(current_user).username post.save() return redirect(url_for("forum.view_post", post_id=post.id)) return render_template( "forum/new_post.html", topic=post.topic, form=form, edit_mode=True ) def form(self, **kwargs): current_app.pluggy.hook.flaskpet_form_new_post(form=ReplyForm) return ReplyForm(**kwargs)