From 6bba33d7e6fbb0cedc39f9a11f816fe5bd372ae7 Mon Sep 17 00:00:00 2001 From: tilly-Q Date: Wed, 17 Jul 2013 16:16:07 -0400 Subject: [PATCH] Whew. This is a big update. I did some significant keeping work. I moved all of the folders and enpoints labeled 'admin' to the more accurate term of 'moderat- ion.' I also created the ability for admins and moderators to add or remove pr- ivileges or to ban a user in response to a report. This also meant implementing the UserBan class in various places. I also had to add a column called result to the ReportBase table. This allows the moderator/admin to leave comments when they respond to a report, allowing for archiving of what responses they do/n't take. --\ mediagoblin/db/migrations.py --| Added result column to ReportBase --\ mediagoblin/db/models.py --| Added result column to ReportBase --| Added documentation to tables I had made previously --\ mediagoblin/decorators.py --| Editted the user_has_privilege decorator to check whether a user has been | banned or not --| Created a seperate user_not_banned decorator to prevent banned users from | accessing any pages --| Changed require_admin_login into require_admin_or_moderator login --\ mediagoblin/gmg_commands/users.py --| Made the gmg command `adduser` create a user w/ the appropriate privileges --\ mediagoblin/moderation/routing.py << formerly mediagoblin/admin/routing.py --| Renamed all of the routes from admin -> moderation --\ mediagoblin/routing.py --| Renamed all of the routes from admin -> moderation --\ mediagoblin/moderation/views.py << formerly mediagoblin/admin/views.py --| Renamed all of the routes & functions from admin -> moderation --| Expanded greatly on the moderation_reports_detail view and functionality --| Added in the give_or_take_away_privilege form, however this might be a use- | -less function which I could remove (because privilege changes should happe- | n in response to a report so they can be archived and visible) --\ mediagoblin/static/css/base.css --| Added in a style for the reports_detail page --\ mediagoblin/templates/mediagoblin/base.html --| Renamed all of the routes from admin -> moderation --\ mediagoblin/templates/mediagoblin/moderation/report.html --| Added form to allow moderators and admins to respond to reports. --\ mediagoblin/templates/mediagoblin/moderation/reports_panel.html --| Fixed the table for closed reports --\ mediagoblin/templates/mediagoblin/moderation/user.html --| Added in a table w/ all of the user's privileges and the option to add or | remove them. Again, this is probably vestigial --| Renamed all of the routes from admin -> moderation --\ mediagoblin/templates/mediagoblin/moderation/user_panel.html --| Renamed all of the routes from admin -> moderation --\ mediagoblin/tools/response.py --| Added function render_user_banned, this is the view function for the redir- | -ect that happens when a user tries to access the site whilst banned --\ mediagoblin/user_pages/forms.py --| Added important translate function where I had text --\ mediagoblin/user_pages/lib.py --| Renamed functiion for clarity --\ mediagoblin/user_pages/views.py --| Added the user_not_banned decorator to every view --\ mediagoblin/views.py --| Added the user_not_banned decorator --\ mediagoblin/moderation/forms.py --| Created this new file --\ mediagoblin/templates/mediagoblin/banned.html --| Created this new file --| This is the page which people are redirected to when they access the site | while banned --- mediagoblin/admin/__init__.py | 16 -- mediagoblin/admin/routing.py | 32 ---- mediagoblin/admin/views.py | 115 ------------ mediagoblin/db/migrations.py | 1 + mediagoblin/db/models.py | 48 ++++- mediagoblin/decorators.py | 35 +++- mediagoblin/gmg_commands/users.py | 7 + mediagoblin/moderation/__init__.py | 16 ++ mediagoblin/moderation/forms.py | 40 +++++ mediagoblin/moderation/routing.py | 35 ++++ mediagoblin/moderation/views.py | 200 +++++++++++++++++++++ mediagoblin/routing.py | 4 +- mediagoblin/static/css/base.css | 2 + .../templates/mediagoblin/admin/media_panel.html | 114 ------------ .../templates/mediagoblin/admin/report.html | 91 ---------- .../templates/mediagoblin/admin/report_panel.html | 95 ---------- mediagoblin/templates/mediagoblin/admin/user.html | 137 -------------- .../templates/mediagoblin/admin/user_panel.html | 55 ------ mediagoblin/templates/mediagoblin/banned.html | 28 +++ mediagoblin/templates/mediagoblin/base.html | 5 +- .../mediagoblin/moderation/media_panel.html | 114 ++++++++++++ .../templates/mediagoblin/moderation/report.html | 137 ++++++++++++++ .../mediagoblin/moderation/report_panel.html | 106 +++++++++++ .../templates/mediagoblin/moderation/user.html | 155 ++++++++++++++++ .../mediagoblin/moderation/user_panel.html | 55 ++++++ mediagoblin/tools/response.py | 10 ++ mediagoblin/user_pages/forms.py | 8 +- mediagoblin/user_pages/lib.py | 2 +- mediagoblin/user_pages/views.py | 25 +-- mediagoblin/views.py | 4 +- 30 files changed, 1006 insertions(+), 686 deletions(-) delete mode 100644 mediagoblin/admin/__init__.py delete mode 100644 mediagoblin/admin/routing.py delete mode 100644 mediagoblin/admin/views.py create mode 100644 mediagoblin/moderation/__init__.py create mode 100644 mediagoblin/moderation/forms.py create mode 100644 mediagoblin/moderation/routing.py create mode 100644 mediagoblin/moderation/views.py delete mode 100644 mediagoblin/templates/mediagoblin/admin/media_panel.html delete mode 100644 mediagoblin/templates/mediagoblin/admin/report.html delete mode 100644 mediagoblin/templates/mediagoblin/admin/report_panel.html delete mode 100644 mediagoblin/templates/mediagoblin/admin/user.html delete mode 100644 mediagoblin/templates/mediagoblin/admin/user_panel.html create mode 100644 mediagoblin/templates/mediagoblin/banned.html create mode 100644 mediagoblin/templates/mediagoblin/moderation/media_panel.html create mode 100644 mediagoblin/templates/mediagoblin/moderation/report.html create mode 100644 mediagoblin/templates/mediagoblin/moderation/report_panel.html create mode 100644 mediagoblin/templates/mediagoblin/moderation/user.html create mode 100644 mediagoblin/templates/mediagoblin/moderation/user_panel.html diff --git a/mediagoblin/admin/__init__.py b/mediagoblin/admin/__init__.py deleted file mode 100644 index 719b56e..0000000 --- a/mediagoblin/admin/__init__.py +++ /dev/null @@ -1,16 +0,0 @@ -# GNU MediaGoblin -- federated, autonomous media hosting -# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS. -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - diff --git a/mediagoblin/admin/routing.py b/mediagoblin/admin/routing.py deleted file mode 100644 index c7ca5b9..0000000 --- a/mediagoblin/admin/routing.py +++ /dev/null @@ -1,32 +0,0 @@ -# GNU MediaGoblin -- federated, autonomous media hosting -# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS. -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -admin_routes = [ - ('mediagoblin.admin.media_panel', - '/media', - 'mediagoblin.admin.views:admin_media_processing_panel'), - ('mediagoblin.admin.users', - '/users', - 'mediagoblin.admin.views:admin_users_panel'), - ('mediagoblin.admin.reports', - '/reports', - 'mediagoblin.admin.views:admin_reports_panel'), - ('mediagoblin.admin.users_detail', - '/users/', - 'mediagoblin.admin.views:admin_users_detail'), - ('mediagoblin.admin.reports_detail', - '/reports/', - 'mediagoblin.admin.views:admin_reports_detail')] diff --git a/mediagoblin/admin/views.py b/mediagoblin/admin/views.py deleted file mode 100644 index 9797057..0000000 --- a/mediagoblin/admin/views.py +++ /dev/null @@ -1,115 +0,0 @@ -# GNU MediaGoblin -- federated, autonomous media hosting -# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS. -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -from werkzeug.exceptions import Forbidden - -from mediagoblin.db.models import (MediaEntry, User, MediaComment, \ - CommentReport, ReportBase, Privilege) -from mediagoblin.decorators import require_admin_login -from mediagoblin.tools.response import render_to_response - -@require_admin_login -def admin_media_processing_panel(request): - ''' - Show the global media processing panel for this instance - ''' - processing_entries = MediaEntry.query.filter_by(state = u'processing').\ - order_by(MediaEntry.created.desc()) - - # Get media entries which have failed to process - failed_entries = MediaEntry.query.filter_by(state = u'failed').\ - order_by(MediaEntry.created.desc()) - - processed_entries = MediaEntry.query.filter_by(state = u'processed').\ - order_by(MediaEntry.created.desc()).limit(10) - - # Render to response - return render_to_response( - request, - 'mediagoblin/admin/media_panel.html', - {'processing_entries': processing_entries, - 'failed_entries': failed_entries, - 'processed_entries': processed_entries}) - -@require_admin_login -def admin_users_panel(request): - ''' - Show the global panel for monitoring users in this instance - ''' - user_list = User.query - - return render_to_response( - request, - 'mediagoblin/admin/user_panel.html', - {'user_list': user_list}) - -@require_admin_login -def admin_users_detail(request): - ''' - Shows details about a particular user. - ''' - user = User.query.filter_by(username=request.matchdict['user']).first() - privileges = Privilege.query - active_reports = user.reports_filed_on.filter( - ReportBase.resolved==None).limit(5) - closed_reports = user.reports_filed_on.filter( - ReportBase.resolved!=None).all() - - return render_to_response( - request, - 'mediagoblin/admin/user.html', - {'user':user, - 'privileges':privileges, - 'reports':active_reports}) - -@require_admin_login -def admin_reports_panel(request): - ''' - Show the global panel for monitoring reports filed against comments or - media entries for this instance. - ''' - report_list = ReportBase.query.filter( - ReportBase.resolved==None).order_by( - ReportBase.created.desc()).limit(10) - closed_report_list = ReportBase.query.filter( - ReportBase.resolved!=None).order_by( - ReportBase.created.desc()).limit(10) - - # Render to response - return render_to_response( - request, - 'mediagoblin/admin/report_panel.html', - {'report_list':report_list, - 'closed_report_list':closed_report_list}) - -@require_admin_login -def admin_reports_detail(request): - report = ReportBase.query.get(request.matchdict['report_id']) - if report.discriminator == 'comment_report': - comment = MediaComment.query.get(report.comment_id) - media_entry = None - elif report.discriminator == 'media_report': - media_entry = MediaEntry.query.get(report.media_entry_id) - comment = None - - return render_to_response( - request, - 'mediagoblin/admin/report.html', - {'report':report, - 'media_entry':media_entry, - 'comment':comment}) - - diff --git a/mediagoblin/db/migrations.py b/mediagoblin/db/migrations.py index a32f593..3e6791c 100644 --- a/mediagoblin/db/migrations.py +++ b/mediagoblin/db/migrations.py @@ -300,6 +300,7 @@ class ReportBase_v0(declarative_base()): reported_user_id = Column(Integer, ForeignKey(User.id), nullable=False) created = Column(DateTime, nullable=False, default=datetime.datetime.now) resolved = Column(DateTime) + result = Column(UnicodeText) discriminator = Column('type', Unicode(50)) __mapper_args__ = {'polymorphic_on': discriminator} diff --git a/mediagoblin/db/models.py b/mediagoblin/db/models.py index e4c97a2..01078db 100644 --- a/mediagoblin/db/models.py +++ b/mediagoblin/db/models.py @@ -239,8 +239,8 @@ class MediaEntry(Base, MediaEntryMixin): This will *not* automatically delete unused collections, which can remain empty... - :param del_orphan_tags: True/false if we delete unused Tags too - :param commit: True/False if this should end the db transaction""" + :keyword del_orphan_tags: True/false if we delete unused Tags too + :keyword commit: True/False if this should end the db transaction""" # User's CollectionItems are automatically deleted via "cascade". # Comments on this Media are deleted by cascade, hopefully. @@ -487,6 +487,14 @@ class ProcessingMetaData(Base): class ReportBase(Base): """ + This is the basic report table which the other reports are based off of. + :keyword reporter_id + :keyword report_content + :keyword reported_user_id + :keyword created + :keyword resolved + :keyword result + :keyword discriminator """ __tablename__ = 'core__reports' @@ -508,6 +516,7 @@ class ReportBase(Base): primaryjoin="User.id==ReportBase.reported_user_id") created = Column(DateTime, nullable=False, default=datetime.datetime.now()) resolved = Column(DateTime) + result = Column(UnicodeText) discriminator = Column('type', Unicode(50)) __mapper_args__ = {'polymorphic_on': discriminator} @@ -551,13 +560,13 @@ class UserBan(Base): the reason why they are banned and when (if ever) the ban will be lifted - :param user_id Holds the id of the user this object is + :keyword user_id Holds the id of the user this object is attached to. This is a one-to-one relationship. - :param expiration_date Holds the date that the ban will be lifted. + :keyword expiration_date Holds the date that the ban will be lifted. If this is null, the ban is permanent unless a moderator manually lifts it. - :param reason Holds the reason why the user was banned. + :keyword reason Holds the reason why the user was banned. """ __tablename__ = 'core__user_bans' @@ -568,6 +577,17 @@ class UserBan(Base): class Privilege(Base): + """ + The Privilege table holds all of the different privileges a user can hold. + If a user 'has' a privilege, the User object is in a relationship with the + privilege object. + + :keyword privilege_name Holds a unicode object that is the recognizable + name of this privilege. This is the column + used for identifying whether or not a user + has a necessary privilege or not. + + """ __tablename__ = 'core__privileges' id = Column(Integer, nullable=False, primary_key=True) @@ -578,12 +598,30 @@ class Privilege(Base): secondary="core__privileges_users") def __init__(self, privilege_name): + ''' + Currently consructors are required for tables that are initialized thru + the FOUNDATIONS system. This is because they need to be able to be con- + -structed by a list object holding their arg*s + ''' self.privilege_name = privilege_name def __repr__(self): return "" % (self.privilege_name) + def is_admin_or_moderator(self): + ''' + This method is necessary to check if a user is able to take moderation + actions. + ''' + + return (self.privilege_name==u'admin' or + self.privilege_name==u'moderator') + class PrivilegeUserAssociation(Base): + ''' + This table holds the many-to-many relationship between User and Privilege + ''' + __tablename__ = 'core__privileges_users' privilege_id = Column( diff --git a/mediagoblin/decorators.py b/mediagoblin/decorators.py index fefbcce..b39b36f 100644 --- a/mediagoblin/decorators.py +++ b/mediagoblin/decorators.py @@ -21,8 +21,9 @@ from werkzeug.exceptions import Forbidden, NotFound from werkzeug.urls import url_quote from mediagoblin import mg_globals as mgg -from mediagoblin.db.models import MediaEntry, User, MediaComment, Privilege -from mediagoblin.tools.response import redirect, render_404 +from mediagoblin.db.models import MediaEntry, User, MediaComment, Privilege, \ + UserBan +from mediagoblin.tools.response import redirect, render_404, render_user_banned def require_active_login(controller): @@ -64,6 +65,7 @@ def active_user_from_url(controller): return wrapper def user_has_privilege(privilege_name): + def user_has_privilege_decorator(controller): @wraps(controller) def wrapper(request, *args, **kwargs): @@ -71,7 +73,9 @@ def user_has_privilege(privilege_name): privileges_of_user = Privilege.query.filter( Privilege.all_users.any( User.id==user_id)) - if not privileges_of_user.filter( + if UserBan.query.filter(UserBan.user_id==user_id).count(): + return render_user_banned(request) + elif not privileges_of_user.filter( Privilege.privilege_name==privilege_name).count(): raise Forbidden() @@ -271,14 +275,18 @@ def get_workbench(func): return new_func -def require_admin_login(controller): +def require_admin_or_moderator_login(controller): """ - Require an login from an administrator. + Require an login from an administrator or a moderator. """ @wraps(controller) def new_controller_func(request, *args, **kwargs): + admin_privilege = Privilege.one({'privilege_name':u'admin'}) + moderator_privilege = Privilege.one({'privilege_name':u'moderator'}) if request.user and \ - not request.user.is_admin: + not admin_privilege in request.user.all_privileges and \ + not moderator_privilege in request.user.all_privileges: + raise Forbidden() elif not request.user: next_url = urljoin( @@ -293,3 +301,18 @@ def require_admin_login(controller): return new_controller_func +def user_not_banned(controller): + """ + Requires that the user has not been banned. Otherwise redirects to the page + explaining why they have been banned + """ + @wraps(controller) + def wrapper(request, *args, **kwargs): + if request.user: + user_banned = UserBan.query.get(request.user.id) + if user_banned: + return render_user_banned(request) + return controller(request, *args, **kwargs) + + return wrapper + diff --git a/mediagoblin/gmg_commands/users.py b/mediagoblin/gmg_commands/users.py index ccc4da7..5c19d3a 100644 --- a/mediagoblin/gmg_commands/users.py +++ b/mediagoblin/gmg_commands/users.py @@ -55,6 +55,13 @@ def adduser(args): entry.pw_hash = auth_lib.bcrypt_gen_password_hash(args.password) entry.status = u'active' entry.email_verified = True + default_privileges = [ + db.Privilege.one({'privilege_name':u'commenter'}), + db.Privilege.one({'privilege_name':u'uploader'}), + db.Privilege.one({'privilege_name':u'reporter'}), + db.Privilege.one({'privilege_name':u'active'}) +] + entry.all_privileges = default_privileges entry.save() print "User created (and email marked as verified)" diff --git a/mediagoblin/moderation/__init__.py b/mediagoblin/moderation/__init__.py new file mode 100644 index 0000000..719b56e --- /dev/null +++ b/mediagoblin/moderation/__init__.py @@ -0,0 +1,16 @@ +# GNU MediaGoblin -- federated, autonomous media hosting +# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + diff --git a/mediagoblin/moderation/forms.py b/mediagoblin/moderation/forms.py new file mode 100644 index 0000000..0a91b9b --- /dev/null +++ b/mediagoblin/moderation/forms.py @@ -0,0 +1,40 @@ +# GNU MediaGoblin -- federated, autonomous media hosting +# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +import wtforms +from mediagoblin.tools.translate import lazy_pass_to_ugettext as _ + +ACTION_CHOICES = [(_(u'takeaway'),_('Take away privilege')), + (_(u'userban'),_('Ban the user')), + (_(u'closereport'),_('Close the report without taking an action'))] + +class PrivilegeAddRemoveForm(wtforms.Form): + giving_privilege = wtforms.HiddenField('',[wtforms.validators.required()]) + privilege_name = wtforms.HiddenField('',[wtforms.validators.required()]) + +class ReportResolutionForm(wtforms.Form): + action_to_resolve = wtforms.RadioField( + _('What action will you take to resolve this report'), + validators=[wtforms.validators.required()], + choices=ACTION_CHOICES) + targeted_user = wtforms.HiddenField('', + validators=[wtforms.validators.required()]) + user_banned_until = wtforms.DateField( + _('User will be banned until:'), + format='%Y-%m-%d', + validators=[wtforms.validators.optional()]) + resolution_content = wtforms.TextAreaField() + diff --git a/mediagoblin/moderation/routing.py b/mediagoblin/moderation/routing.py new file mode 100644 index 0000000..f177c32 --- /dev/null +++ b/mediagoblin/moderation/routing.py @@ -0,0 +1,35 @@ +# GNU MediaGoblin -- federated, autonomous media hosting +# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +moderation_routes = [ + ('mediagoblin.moderation.media_panel', + '/media/', + 'mediagoblin.moderation.views:moderation_media_processing_panel'), + ('mediagoblin.moderation.users', + '/users/', + 'mediagoblin.moderation.views:moderation_users_panel'), + ('mediagoblin.moderation.reports', + '/reports/', + 'mediagoblin.moderation.views:moderation_reports_panel'), + ('mediagoblin.moderation.users_detail', + '/users//', + 'mediagoblin.moderation.views:moderation_users_detail'), + ('mediagoblin.moderation.give_or_take_away_privilege', + '/users//privilege/', + 'mediagoblin.moderation.views:give_or_take_away_privilege'), + ('mediagoblin.moderation.reports_detail', + '/reports//', + 'mediagoblin.moderation.views:moderation_reports_detail')] diff --git a/mediagoblin/moderation/views.py b/mediagoblin/moderation/views.py new file mode 100644 index 0000000..6f6318b --- /dev/null +++ b/mediagoblin/moderation/views.py @@ -0,0 +1,200 @@ +# GNU MediaGoblin -- federated, autonomous media hosting +# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +from werkzeug.exceptions import Forbidden + +from mediagoblin.db.models import (MediaEntry, User, MediaComment, \ + CommentReport, ReportBase, Privilege, \ + UserBan) +from mediagoblin.decorators import (require_admin_or_moderator_login, \ + active_user_from_url) +from mediagoblin.tools.response import render_to_response, redirect +from mediagoblin.moderation import forms as moderation_forms +from datetime import datetime + +@require_admin_or_moderator_login +def moderation_media_processing_panel(request): + ''' + Show the global media processing panel for this instance + ''' + processing_entries = MediaEntry.query.filter_by(state = u'processing').\ + order_by(MediaEntry.created.desc()) + + # Get media entries which have failed to process + failed_entries = MediaEntry.query.filter_by(state = u'failed').\ + order_by(MediaEntry.created.desc()) + + processed_entries = MediaEntry.query.filter_by(state = u'processed').\ + order_by(MediaEntry.created.desc()).limit(10) + + # Render to response + return render_to_response( + request, + 'mediagoblin/moderation/media_panel.html', + {'processing_entries': processing_entries, + 'failed_entries': failed_entries, + 'processed_entries': processed_entries}) + +@require_admin_or_moderator_login +def moderation_users_panel(request): + ''' + Show the global panel for monitoring users in this instance + ''' + user_list = User.query + + return render_to_response( + request, + 'mediagoblin/moderation/user_panel.html', + {'user_list': user_list}) + +@require_admin_or_moderator_login +def moderation_users_detail(request): + ''' + Shows details about a particular user. + ''' + user = User.query.filter_by(username=request.matchdict['user']).first() + active_reports = user.reports_filed_on.filter( + ReportBase.resolved==None).limit(5) + closed_reports = user.reports_filed_on.filter( + ReportBase.resolved!=None).all() + privileges = Privilege.query + + return render_to_response( + request, + 'mediagoblin/moderation/user.html', + {'user':user, + 'privileges':privileges, + 'reports':active_reports}) + +@require_admin_or_moderator_login +def moderation_reports_panel(request): + ''' + Show the global panel for monitoring reports filed against comments or + media entries for this instance. + ''' + report_list = ReportBase.query.filter( + ReportBase.resolved==None).order_by( + ReportBase.created.desc()).limit(10) + closed_report_list = ReportBase.query.filter( + ReportBase.resolved!=None).order_by( + ReportBase.created.desc()).limit(10) + + # Render to response + return render_to_response( + request, + 'mediagoblin/moderation/report_panel.html', + {'report_list':report_list, + 'closed_report_list':closed_report_list}) + +@require_admin_or_moderator_login +def moderation_reports_detail(request): + """ + This is the page an admin or moderator goes to see the details of a report. + The report can be resolved or unresolved. This is also the page that a mod- + erator would go to to take an action to resolve a report. + """ + form = moderation_forms.ReportResolutionForm(request.form) + report = ReportBase.query.get(request.matchdict['report_id']) + + if request.method == "POST" and form.validate(): + user = User.query.get(form.targeted_user.data) + if form.action_to_resolve.data == u'takeaway': + if report.discriminator == u'comment_report': + privilege = Privilege.one({'privilege_name':u'commenter'}) + form.resolution_content.data += \ + u"
%s took away %s\'s commenting privileges" % ( + request.user.username, + user.username) + else: + privilege = Privilege.one({'privilege_name':u'uploader'}) + form.resolution_content.data += \ + u"
%s took away %s\'s media uploading privileges" % ( + request.user.username, + user.username) + user.all_privileges.remove(privilege) + user.save() + report.result = form.resolution_content.data + report.resolved = datetime.now() + report.save() + + elif form.action_to_resolve.data == u'userban': + reason = form.resolution_content.data + \ + "
"+request.user.username + user_ban = UserBan( + user_id=form.targeted_user.data, + expiration_date=form.user_banned_until.data, + reason= form.resolution_content.data) + user_ban.save() + if not form.user_banned_until == "": + form.resolution_content.data += \ + u"
%s banned user %s until %s." % ( + request.user.username, + user.username, + form.user_banned_until.data) + else: + form.resolution_content.data += \ + u"
%s banned user %s indefinitely." % ( + request.user.username, + user.username, + form.user_banned_until.data) + + report.result = form.resolution_content.data + report.resolved = datetime.now() + report.save() + + else: + pass + + return redirect( + request, + 'mediagoblin.moderation.users_detail', + user=user.username) + + if report.discriminator == 'comment_report': + comment = MediaComment.query.get(report.comment_id) + media_entry = None + elif report.discriminator == 'media_report': + media_entry = MediaEntry.query.get(report.media_entry_id) + comment = None + + form.targeted_user.data = report.reported_user_id + + return render_to_response( + request, + 'mediagoblin/moderation/report.html', + {'report':report, + 'media_entry':media_entry, + 'comment':comment, + 'form':form}) + +@require_admin_or_moderator_login +@active_user_from_url +def give_or_take_away_privilege(request, url_user): + ''' + A form action to give or take away a particular privilege from a user + ''' + form = moderation_forms.PrivilegeAddRemoveForm(request.form) + if request.method == "POST" and form.validate(): + privilege = Privilege.one({'privilege_name':form.privilege_name.data}) + if privilege in url_user.all_privileges is True: + url_user.all_privileges.remove(privilege) + else: + url_user.all_privileges.append(privilege) + url_user.save() + return redirect( + request, + 'mediagoblin.moderation.users_detail', + user=url_user.username) diff --git a/mediagoblin/routing.py b/mediagoblin/routing.py index a650f22..0642ad8 100644 --- a/mediagoblin/routing.py +++ b/mediagoblin/routing.py @@ -18,7 +18,7 @@ import logging from mediagoblin.tools.routing import add_route, mount, url_map from mediagoblin.tools.pluginapi import PluginManager -from mediagoblin.admin.routing import admin_routes +from mediagoblin.moderation.routing import moderation_routes from mediagoblin.auth.routing import auth_routes @@ -28,7 +28,7 @@ _log = logging.getLogger(__name__) def get_url_map(): add_route('index', '/', 'mediagoblin.views:root_view') mount('/auth', auth_routes) - mount('/a', admin_routes) + mount('/mod', moderation_routes) import mediagoblin.submit.routing import mediagoblin.user_pages.routing diff --git a/mediagoblin/static/css/base.css b/mediagoblin/static/css/base.css index 1cded53..343648d 100644 --- a/mediagoblin/static/css/base.css +++ b/mediagoblin/static/css/base.css @@ -402,6 +402,8 @@ a.report_authorlink, a.report_whenlink { color: #D486B1; } +ul#action_to_resolve {list-style:none; margin-left:10px;} + /* media galleries */ .media_thumbnail { diff --git a/mediagoblin/templates/mediagoblin/admin/media_panel.html b/mediagoblin/templates/mediagoblin/admin/media_panel.html deleted file mode 100644 index 1c3c866..0000000 --- a/mediagoblin/templates/mediagoblin/admin/media_panel.html +++ /dev/null @@ -1,114 +0,0 @@ -{# -# GNU MediaGoblin -- federated, autonomous media hosting -# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS. -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -#} -{% extends "mediagoblin/base.html" %} - -{% block title -%} - {% trans %}Media processing panel{% endtrans %} — {{ super() }} -{%- endblock %} - -{% block mediagoblin_content %} - -

{% trans %}Media processing panel{% endtrans %}

- -

- {% trans %}Here you can track the state of media being processed on this instance.{% endtrans %} -

- -

{% trans %}Media in-processing{% endtrans %}

- -{% if processing_entries.count() %} - - - - - - - - - {% for media_entry in processing_entries %} - - - - - - {% if media_entry.transcoding_progress %} - - {% else %} - - {% endif %} - - {% endfor %} -
IDUserTitleWhen submittedTranscoding progress
{{ media_entry.id }}{{ media_entry.get_uploader.username }}{{ media_entry.title }}{{ media_entry.created.strftime("%F %R") }}{{ media_entry.transcoding_progress }}%Unknown
-{% else %} -

{% trans %}No media in-processing{% endtrans %}

-{% endif %} - -

{% trans %}These uploads failed to process:{% endtrans %}

-{% if failed_entries.count() %} - - - - - - - - - - - {% for media_entry in failed_entries %} - - - - - - {% if media_entry.get_fail_exception() %} - - - {% else %} - - - {% endif %} - - {% endfor %} -
IDUserTitleWhen submittedReason for failureFailure metadata
{{ media_entry.id }}{{ media_entry.get_uploader.username }}{{ media_entry.title }}{{ media_entry.created.strftime("%F %R") }}{{ media_entry.get_fail_exception().general_message }}{{ media_entry.fail_metadata }}  
-{% else %} -

{% trans %}No failed entries!{% endtrans %}

-{% endif %} -

{% trans %}Last 10 successful uploads{% endtrans %}

-{% if processed_entries.count() %} - - - - - - - - - {% for media_entry in processed_entries %} - - - - - - - {% endfor %} -
IDUserTitleSubmitted
{{ media_entry.id }}{{ media_entry.get_uploader.username }}{{ media_entry.title }}{{ timesince(media_entry.created) }}
-{% else %} -

{% trans %}No processed entries, yet!{% endtrans %}

-{% endif %} -{% endblock %} diff --git a/mediagoblin/templates/mediagoblin/admin/report.html b/mediagoblin/templates/mediagoblin/admin/report.html deleted file mode 100644 index ef90df4..0000000 --- a/mediagoblin/templates/mediagoblin/admin/report.html +++ /dev/null @@ -1,91 +0,0 @@ -{# -# GNU MediaGoblin -- federated, autonomous media hosting -# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS. -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -#} -{%- extends "mediagoblin/base.html" %} - -{%- block mediagoblin_content %} -{% if not report %} - Sorry, no such report found. -{% else %} -

Report #{{ report.id }}

- {% if comment %} - Reported comment: - {% set reported_user = comment.get_author %} - - {% elif media_entry %} - -
- {% endif %} - Reason for report: - -{% endif %} -{% endblock %} diff --git a/mediagoblin/templates/mediagoblin/admin/report_panel.html b/mediagoblin/templates/mediagoblin/admin/report_panel.html deleted file mode 100644 index 3019457..0000000 --- a/mediagoblin/templates/mediagoblin/admin/report_panel.html +++ /dev/null @@ -1,95 +0,0 @@ -{# -# GNU MediaGoblin -- federated, autonomous media hosting -# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS. -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -#} -{% extends "mediagoblin/base.html" %} - -{% block title -%} - {% trans %}Report panel{% endtrans %} — {{ super() }} -{%- endblock %} - -{% block mediagoblin_content %} - -

{% trans %}Report panel{% endtrans %}

- -

- {% trans %}Here you can look up users in order to take punitive actions on them.{% endtrans %} -

- -

{% trans %}Reports Filed on Comments{% endtrans %}

- -{% if report_list.count() %} - - - - - - - - - - - {% for report in report_list %} - - {% if report.discriminator == "comment_report" %} - - - - - - - - {% elif report.discriminator == "media_report" %} - - - - - - - - {% endif %} - - {% endfor %} -
IDReport TypeOffenderWhen ReportedReported ByReasonReported Comment or Media Entry
{{ report.id }}Comment Report{{ report.comment.get_author.username }}{{ report.created.strftime("%F %R") }}{{ report.reporter.username }}{{ report.report_content }}{{ report.comment.get_media_entry.title }}{{ report.id }}Media Report{{ report.media_entry.get_uploader.username }}{{ report.created.strftime("%F %R") }}{{ report.reporter.username }}{{ report.report_content[0:20] }}...{{ report.media_entry.title }}
-{% else %} -

{% trans %}No open reports found.{% endtrans %}

-{% endif %} -

{% trans %}Closed Reports on Comments{% endtrans %}

-{% if closed_report_list.count() %} - - - - - - - - - - {% for report in closed_report_list %} - - - - - - - - - {% endfor %} -
IDOffenderWhen ReportedReported ByReasonComment Posted On
{{ report.id }}{{ report.comment.get_author.username }}{{ report.created.strftime("%F %R") }}{{ report.reporter.username }}{{ report.report_content }}{{ report.comment.get_media_entry.title }}
-{% else %} -

{% trans %}No closed reports found.{% endtrans %}

-{% endif %} -{% endblock %} diff --git a/mediagoblin/templates/mediagoblin/admin/user.html b/mediagoblin/templates/mediagoblin/admin/user.html deleted file mode 100644 index 90b3f58..0000000 --- a/mediagoblin/templates/mediagoblin/admin/user.html +++ /dev/null @@ -1,137 +0,0 @@ -{# -# GNU MediaGoblin -- federated, autonomous media hosting -# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS. -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -#} -{% extends "mediagoblin/base.html" %} - - -{% block title %} - {%- if user -%} - {%- trans username=user.username -%} - User: {{ username }} - {%- endtrans %} — {{ super() }} - {%- else -%} - {{ super() }} - {%- endif -%} -{% endblock %} - - -{% block mediagoblin_content -%} - {# If no user... #} - {% if not user %} -

{% trans %}Sorry, no such user found.{% endtrans %}

- - {# User exists, but needs verification #} - {% elif user.status == "needs_email_verification" %} -
-

{% trans %}Email verification needed{% endtrans %}

- -

- {% trans -%} - Someone has registered an account with this username, but it still has to be activated. - {%- endtrans %} -

- -

- {% trans login_url=request.urlgen('mediagoblin.auth.login') -%} - If you are that person but you've lost your verification email, you can log in and resend it. - {%- endtrans %} -

-
- - {# Active(?) (or at least verified at some point) user, horray! #} - {% else %} -

- {%- trans username=user.username %}{{ username }}'s profile{% endtrans -%} -

- - {% if not user.url and not user.bio %} -
-

- {% trans -%} - This user hasn't filled in their profile (yet). - {%- endtrans %} -

- {% else %} -
- {% include "mediagoblin/utils/profile.html" %} - {% if request.user and - (request.user.id == user.id or request.user.is_admin) %} - - {%- trans %}Edit profile{% endtrans -%} - - {% endif %} - {% endif %} -

- - {%- trans %}Browse collections{% endtrans -%} - -

-
- {% endif %} - {% if user %} -

{%- trans %}Active Reports on{% endtrans -%} {{ user.username }}

- {% if reports.count() %} - - - - - - - {% for report in reports %} - - - - - - - {% endfor %} - -
{%- trans %}Report ID{% endtrans -%}{%- trans %}Reported Content{% endtrans -%}{%- trans %}Description of Report{% endtrans -%}
- - - {%- trans %}Report #{% endtrans -%}{{ report.id }} - - - {% if report.discriminator == "comment_report" %} - {%- trans %}Reported Comment{% endtrans -%} - {% elif report.discriminator == "media_report" %} - {%- trans %}Reported Media Entry{% endtrans -%} - {% endif %} - {{ report.report_content[:21] }}{% if report.report_content|count >20 %}...{% endif %}{%- trans %}Resolve{% endtrans -%}
- {% else %} - {%- trans %}No active reports filed on{% endtrans -%} {{ user.username }} - {% endif %} - {{ user.username }}'s report history - -

{{ user.username }}'s Privileges

- - - - - {% for privilege in privileges %} - - - - - - {% endfor %} -
{% trans %}Privilege{% endtrans %}{% trans %}User Has Privilege{% endtrans %}
{{ privilege.privilege_name }}{% if privilege in user.all_privileges %}Yes{% else %}No{% endif %}{% if privilege in user.all_privileges and privilege.id < request.user.get_highest_privilege().id %}{% trans %}Take Away{% endtrans %}{% else %}{% trans %}Give Privilege{% endtrans %}{% endif %}
- {% endif %} -{% endblock %} diff --git a/mediagoblin/templates/mediagoblin/admin/user_panel.html b/mediagoblin/templates/mediagoblin/admin/user_panel.html deleted file mode 100644 index cc965b7..0000000 --- a/mediagoblin/templates/mediagoblin/admin/user_panel.html +++ /dev/null @@ -1,55 +0,0 @@ -{# -# GNU MediaGoblin -- federated, autonomous media hosting -# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS. -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -#} -{% extends "mediagoblin/base.html" %} - -{% block title -%} - {% trans %}User panel{% endtrans %} — {{ super() }} -{%- endblock %} - -{% block mediagoblin_content %} - -

{% trans %}User panel{% endtrans %}

- -

- {% trans %}Here you can look up users in order to take punitive actions on them.{% endtrans %} -

- -

{% trans %}Active Users{% endtrans %}

- -{% if user_list.count() %} - - - - - - - - {% for user in user_list %} - - - - - - - {% endfor %} -
{% trans %}ID{% endtrans %}{% trans %}Username{% endtrans %}{% trans %}When Joined{% endtrans %}{% trans %}# of Comments Posted{% endtrans %}
{{ user.id }}{{ user.username }}{{ user.created.strftime("%F %R") }}{{ user.posted_comments.count() }}
-{% else %} -

{% trans %}No users found.{% endtrans %}

-{% endif %} -{% endblock %} diff --git a/mediagoblin/templates/mediagoblin/banned.html b/mediagoblin/templates/mediagoblin/banned.html new file mode 100644 index 0000000..4eda054 --- /dev/null +++ b/mediagoblin/templates/mediagoblin/banned.html @@ -0,0 +1,28 @@ +{# +# GNU MediaGoblin -- federated, autonomous media hosting +# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +#} +{% extends "mediagoblin/base.html" %} + +{% block title %}You are Banned.{% endblock %} + +{% block mediagoblin_content %} + {% trans %}Image of goblin stressing out{% endtrans %} +

You have been banned until {{ expiration_date }}

+

{{ reason|safe }}

+
+{% endblock %} diff --git a/mediagoblin/templates/mediagoblin/base.html b/mediagoblin/templates/mediagoblin/base.html index e9a18f2..b52b65e 100644 --- a/mediagoblin/templates/mediagoblin/base.html +++ b/mediagoblin/templates/mediagoblin/base.html @@ -104,9 +104,12 @@ {% if request.user.is_admin %}

Admin powers: - + {%- trans %}Media processing panel{% endtrans -%} + + {%- trans %}User management panel{% endtrans -%} +

{% endif %}
diff --git a/mediagoblin/templates/mediagoblin/moderation/media_panel.html b/mediagoblin/templates/mediagoblin/moderation/media_panel.html new file mode 100644 index 0000000..1c3c866 --- /dev/null +++ b/mediagoblin/templates/mediagoblin/moderation/media_panel.html @@ -0,0 +1,114 @@ +{# +# GNU MediaGoblin -- federated, autonomous media hosting +# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +#} +{% extends "mediagoblin/base.html" %} + +{% block title -%} + {% trans %}Media processing panel{% endtrans %} — {{ super() }} +{%- endblock %} + +{% block mediagoblin_content %} + +

{% trans %}Media processing panel{% endtrans %}

+ +

+ {% trans %}Here you can track the state of media being processed on this instance.{% endtrans %} +

+ +

{% trans %}Media in-processing{% endtrans %}

+ +{% if processing_entries.count() %} + + + + + + + + + {% for media_entry in processing_entries %} + + + + + + {% if media_entry.transcoding_progress %} + + {% else %} + + {% endif %} + + {% endfor %} +
IDUserTitleWhen submittedTranscoding progress
{{ media_entry.id }}{{ media_entry.get_uploader.username }}{{ media_entry.title }}{{ media_entry.created.strftime("%F %R") }}{{ media_entry.transcoding_progress }}%Unknown
+{% else %} +

{% trans %}No media in-processing{% endtrans %}

+{% endif %} + +

{% trans %}These uploads failed to process:{% endtrans %}

+{% if failed_entries.count() %} + + + + + + + + + + + {% for media_entry in failed_entries %} + + + + + + {% if media_entry.get_fail_exception() %} + + + {% else %} + + + {% endif %} + + {% endfor %} +
IDUserTitleWhen submittedReason for failureFailure metadata
{{ media_entry.id }}{{ media_entry.get_uploader.username }}{{ media_entry.title }}{{ media_entry.created.strftime("%F %R") }}{{ media_entry.get_fail_exception().general_message }}{{ media_entry.fail_metadata }}  
+{% else %} +

{% trans %}No failed entries!{% endtrans %}

+{% endif %} +

{% trans %}Last 10 successful uploads{% endtrans %}

+{% if processed_entries.count() %} + + + + + + + + + {% for media_entry in processed_entries %} + + + + + + + {% endfor %} +
IDUserTitleSubmitted
{{ media_entry.id }}{{ media_entry.get_uploader.username }}{{ media_entry.title }}{{ timesince(media_entry.created) }}
+{% else %} +

{% trans %}No processed entries, yet!{% endtrans %}

+{% endif %} +{% endblock %} diff --git a/mediagoblin/templates/mediagoblin/moderation/report.html b/mediagoblin/templates/mediagoblin/moderation/report.html new file mode 100644 index 0000000..6938569 --- /dev/null +++ b/mediagoblin/templates/mediagoblin/moderation/report.html @@ -0,0 +1,137 @@ +{# +# GNU MediaGoblin -- federated, autonomous media hosting +# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +#} +{%- extends "mediagoblin/base.html" %} +{% import "/mediagoblin/utils/wtforms.html" as wtforms_util %} + +{%- block mediagoblin_content %} +{% if not report %} + Sorry, no such report found. +{% else %} +

Report #{{ report.id }}

+ {% if comment %} + Reported comment: + {% set reported_user = comment.get_author %} + + {% elif media_entry %} + +
+ {% endif %} + Reason for report: + + {% if not report.resolved %} + +
+ {{ wtforms_util.render_divs(form) }} + {{ csrf_token }} + +
+ + + + {% else %} +

Status:

+ RESOLVED on {{ report.resolved.strftime("%I:%M%p %Y-%m-%d") }} + {% autoescape False %} +

{{ report.result }}

+ {% endautoescape %} + {% endif %} +{% endif %} +{% endblock %} diff --git a/mediagoblin/templates/mediagoblin/moderation/report_panel.html b/mediagoblin/templates/mediagoblin/moderation/report_panel.html new file mode 100644 index 0000000..126b247 --- /dev/null +++ b/mediagoblin/templates/mediagoblin/moderation/report_panel.html @@ -0,0 +1,106 @@ +{# +# GNU MediaGoblin -- federated, autonomous media hosting +# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +#} +{% extends "mediagoblin/base.html" %} + +{% block title -%} + {% trans %}Report panel{% endtrans %} — {{ super() }} +{%- endblock %} + +{% block mediagoblin_content %} + +

{% trans %}Report panel{% endtrans %}

+ +

+ {% trans %}Here you can look up users in order to take punitive actions on them.{% endtrans %} +

+ +

{% trans %}Reports Filed{% endtrans %}

+ +{% if report_list.count() %} + + + + + + + + + + + {% for report in report_list %} + + + + {% if report.discriminator == "comment_report" %} + + + + + + + {% elif report.discriminator == "media_report" %} + + + + + + + {% endif %} + + {% endfor %} +
IDReport TypeOffenderWhen ReportedReported ByReasonReported Comment or Media Entry
{{ report.id }}Comment Report{{ report.comment.get_author.username }}{{ report.created.strftime("%F %R") }}{{ report.reporter.username }}{{ report.report_content }}{{ report.comment.get_media_entry.title }}Media Report{{ report.media_entry.get_uploader.username }}{{ report.created.strftime("%F %R") }}{{ report.reporter.username }}{{ report.report_content[0:20] }}...{{ report.media_entry.title }}
+{% else %} +

{% trans %}No open reports found.{% endtrans %}

+{% endif %} +

{% trans %}Closed Reports{% endtrans %}

+{% if closed_report_list.count() %} + + + + + + + + + + + {% for report in closed_report_list %} + + {% if report.discriminator == "comment_report" %} + + + + + + + {% elif report.discriminator == "media_report" %} + + + + + + + {% endif %} + {% endfor %} +
IDResolvedOffenderAction TakenReported ByReasonReported Comment or Media Entry
{{ report.id }}{{ report.resolved.strftime("%F %R") }}{{ report.comment.get_author.username }}{{ report.created.strftime("%F %R") }}{{ report.reporter.username }}{{ report.report_content }}{{ report.comment.get_media_entry.title }}{{ report.resolved.strftime("%F %R") }}{{ report.media_entry.get_uploader.username }}{{ report.created.strftime("%F %R") }}{{ report.reporter.username }}{{ report.report_content[0:20] }}...{{ report.media_entry.title }}
+{% else %} +

{% trans %}No closed reports found.{% endtrans %}

+{% endif %} +{% endblock %} diff --git a/mediagoblin/templates/mediagoblin/moderation/user.html b/mediagoblin/templates/mediagoblin/moderation/user.html new file mode 100644 index 0000000..f868aa8 --- /dev/null +++ b/mediagoblin/templates/mediagoblin/moderation/user.html @@ -0,0 +1,155 @@ +{# +# GNU MediaGoblin -- federated, autonomous media hosting +# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +#} +{% extends "mediagoblin/base.html" %} + + +{% block title %} + {%- if user -%} + {%- trans username=user.username -%} + User: {{ username }} + {%- endtrans %} — {{ super() }} + {%- else -%} + {{ super() }} + {%- endif -%} +{% endblock %} + + +{% block mediagoblin_content -%} + {# If no user... #} + {% if not user %} +

{% trans %}Sorry, no such user found.{% endtrans %}

+ + {# User exists, but needs verification #} + {% elif user.status == "needs_email_verification" %} +
+

{% trans %}Email verification needed{% endtrans %}

+ +

+ {% trans -%} + Someone has registered an account with this username, but it still has to be activated. + {%- endtrans %} +

+ +

+ {% trans login_url=request.urlgen('mediagoblin.auth.login') -%} + If you are that person but you've lost your verification email, you can log in and resend it. + {%- endtrans %} +

+
+ + {# Active(?) (or at least verified at some point) user, horray! #} + {% else %} +

+ {%- trans username=user.username %}{{ username }}'s profile{% endtrans -%} +

+ + {% if not user.url and not user.bio %} +
+

+ {% trans -%} + This user hasn't filled in their profile (yet). + {%- endtrans %} +

+ {% else %} +
+ {% include "mediagoblin/utils/profile.html" %} + {% if request.user and + (request.user.id == user.id or request.user.is_admin) %} + + {%- trans %}Edit profile{% endtrans -%} + + {% endif %} + {% endif %} +

+ + {%- trans %}Browse collections{% endtrans -%} + +

+
+ {% endif %} + {% if user %} +

{%- trans %}Active Reports on {% endtrans -%}{{ user.username }}

+ {% if reports.count() %} + + + + + + + {% for report in reports %} + + + + + + + {% endfor %} + +
{%- trans %}Report ID{% endtrans -%}{%- trans %}Reported Content{% endtrans -%}{%- trans %}Description of Report{% endtrans -%}
+ + + {%- trans %}Report #{% endtrans -%}{{ report.id }} + + + {% if report.discriminator == "comment_report" %} + {%- trans %}Reported Comment{% endtrans -%} + {% elif report.discriminator == "media_report" %} + {%- trans %}Reported Media Entry{% endtrans -%} + {% endif %} + {{ report.report_content[:21] }}{% if report.report_content|count >20 %}...{% endif %}{%- trans %}Resolve{% endtrans -%}
+ {% else %} + {%- trans %}No active reports filed on {% endtrans -%} {{ user.username }} + {% endif %} + {{ user.username }}'s report history + +

{{ user.username }}'s Privileges

+ + + + + + {% for privilege in privileges %} + + + + + {% if (not privilege.is_admin_or_moderator() or request.user.is_admin) and not (user.is_admin and not request.user.is_admin) %} + + {% if (not privilege.is_admin_or_moderator() or request.user.is_admin) and not (user.is_admin and not request.user.is_admin) %} + + {{ csrf_token }} + + + {% endfor %} +
{% trans %}Privilege{% endtrans %}{% trans %}User Has Privilege{% endtrans %}
{{ privilege.privilege_name }} + {% if privilege in user.all_privileges %}Yes + + {% endif %} + {% else %}No + + {% endif %} + {% endif %} + +
+ {% endif %} +{% endblock %} diff --git a/mediagoblin/templates/mediagoblin/moderation/user_panel.html b/mediagoblin/templates/mediagoblin/moderation/user_panel.html new file mode 100644 index 0000000..4987707 --- /dev/null +++ b/mediagoblin/templates/mediagoblin/moderation/user_panel.html @@ -0,0 +1,55 @@ +{# +# GNU MediaGoblin -- federated, autonomous media hosting +# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +#} +{% extends "mediagoblin/base.html" %} + +{% block title -%} + {% trans %}User panel{% endtrans %} — {{ super() }} +{%- endblock %} + +{% block mediagoblin_content %} + +

{% trans %}User panel{% endtrans %}

+ +

+ {% trans %}Here you can look up users in order to take punitive actions on them.{% endtrans %} +

+ +

{% trans %}Active Users{% endtrans %}

+ +{% if user_list.count() %} + + + + + + + + {% for user in user_list %} + + + + + + + {% endfor %} +
{% trans %}ID{% endtrans %}{% trans %}Username{% endtrans %}{% trans %}When Joined{% endtrans %}{% trans %}# of Comments Posted{% endtrans %}
{{ user.id }}{{ user.username }}{{ user.created.strftime("%F %R") }}{{ user.posted_comments.count() }}
+{% else %} +

{% trans %}No users found.{% endtrans %}

+{% endif %} +{% endblock %} diff --git a/mediagoblin/tools/response.py b/mediagoblin/tools/response.py index aaf31d0..ecea307 100644 --- a/mediagoblin/tools/response.py +++ b/mediagoblin/tools/response.py @@ -19,6 +19,7 @@ from werkzeug.wrappers import Response as wz_Response from mediagoblin.tools.template import render_template from mediagoblin.tools.translate import (lazy_pass_to_ugettext as _, pass_to_ugettext) +from mediagoblin.db.models import UserBan class Response(wz_Response): """Set default response mimetype to HTML, otherwise we get text/plain""" @@ -62,6 +63,15 @@ def render_404(request): "you're looking for has been moved or deleted.") return render_error(request, 404, err_msg=err_msg) +def render_user_banned(request): + """Renders the page which tells a user they have been banned, for how long + and the reason why they have been banned" + """ + user_ban = UserBan.query.get(request.user.id) + return render_to_response(request, + 'mediagoblin/banned.html', + {'reason':user_ban.reason, + 'expiration_date':user_ban.expiration_date}) def render_http_exception(request, exc, description): """Return Response() given a werkzeug.HTTPException diff --git a/mediagoblin/user_pages/forms.py b/mediagoblin/user_pages/forms.py index 284dd7b..260fe02 100644 --- a/mediagoblin/user_pages/forms.py +++ b/mediagoblin/user_pages/forms.py @@ -51,11 +51,15 @@ class MediaCollectForm(wtforms.Form): Markdown for formatting.""")) class CommentReportForm(wtforms.Form): - report_reason = wtforms.TextAreaField('Reason for Reporting') + report_reason = wtforms.TextAreaField( + _('Reason for Reporting'), + [wtforms.validators.Required()]) comment_id = wtforms.IntegerField() reporter_id = wtforms.IntegerField() class MediaReportForm(wtforms.Form): - report_reason = wtforms.TextAreaField('Reason for Reporting') + report_reason = wtforms.TextAreaField( + _('Reason for Reporting'), + [wtforms.validators.Required()]) media_entry_id = wtforms.IntegerField() reporter_id = wtforms.IntegerField() diff --git a/mediagoblin/user_pages/lib.py b/mediagoblin/user_pages/lib.py index 2558b06..cf7b604 100644 --- a/mediagoblin/user_pages/lib.py +++ b/mediagoblin/user_pages/lib.py @@ -78,7 +78,7 @@ def add_media_to_collection(collection, media, note=None, commit=True): if commit: Session.commit() -def build_report_form(form_dict): +def build_report_table(form_dict): """ This function is used to convert a form dictionary (from a User filing a report) into either a MediaReport or CommentReport object. diff --git a/mediagoblin/user_pages/views.py b/mediagoblin/user_pages/views.py index abf5e5c..c163827 100644 --- a/mediagoblin/user_pages/views.py +++ b/mediagoblin/user_pages/views.py @@ -26,14 +26,14 @@ from mediagoblin.tools.response import render_to_response, render_404, \ from mediagoblin.tools.translate import pass_to_ugettext as _ from mediagoblin.tools.pagination import Pagination from mediagoblin.user_pages import forms as user_forms -from mediagoblin.user_pages.lib import (send_comment_email, build_report_form, +from mediagoblin.user_pages.lib import (send_comment_email, build_report_table, add_media_to_collection) from mediagoblin.decorators import (uses_pagination, get_user_media_entry, get_media_entry_by_id, user_has_privilege, require_active_login, user_may_delete_media, user_may_alter_collection, get_user_collection, get_user_collection_item, active_user_from_url, - get_media_comment_by_id) + get_media_comment_by_id, user_not_banned) from werkzeug.contrib.atom import AtomFeed @@ -41,7 +41,7 @@ from werkzeug.contrib.atom import AtomFeed _log = logging.getLogger(__name__) _log.setLevel(logging.DEBUG) - +@user_not_banned @uses_pagination def user_home(request, page): """'Homepage' of a User()""" @@ -80,7 +80,7 @@ def user_home(request, page): 'media_entries': media_entries, 'pagination': pagination}) - +@user_not_banned @active_user_from_url @uses_pagination def user_gallery(request, page, url_user=None): @@ -114,7 +114,7 @@ def user_gallery(request, page, url_user=None): MEDIA_COMMENTS_PER_PAGE = 50 - +@user_not_banned @get_user_media_entry @uses_pagination def media_home(request, media, page, **kwargs): @@ -190,7 +190,7 @@ def media_post_comment(request, media): return redirect_obj(request, media) - +@user_not_banned @get_media_entry_by_id @require_active_login def media_collect(request, media): @@ -269,6 +269,7 @@ def media_collect(request, media): #TODO: Why does @user_may_delete_media not implicate @require_active_login? +@user_not_banned @get_media_entry_by_id @require_active_login @user_may_delete_media @@ -305,7 +306,7 @@ def media_confirm_delete(request, media): {'media': media, 'form': form}) - +@user_not_banned @active_user_from_url @uses_pagination def user_collection(request, page, url_user=None): @@ -335,7 +336,7 @@ def user_collection(request, page, url_user=None): 'collection_items': collection_items, 'pagination': pagination}) - +@user_not_banned @active_user_from_url def collection_list(request, url_user=None): """A User-defined Collection""" @@ -391,7 +392,7 @@ def collection_item_confirm_remove(request, collection_item): {'collection_item': collection_item, 'form': form}) - +@user_not_banned @get_user_collection @require_active_login @user_may_alter_collection @@ -575,7 +576,7 @@ def collection_atom_feed(request): return feed.get_response() - +@user_not_banned @require_active_login def processing_panel(request): """ @@ -625,8 +626,8 @@ def processing_panel(request): @user_has_privilege(u'reporter') def file_a_report(request, media, comment=None): if request.method == "POST": - report_form = build_report_form(request.form) - report_form.save() + report_table = build_report_table(request.form) + report_table.save() return redirect( request, diff --git a/mediagoblin/views.py b/mediagoblin/views.py index 6acd7e9..595f272 100644 --- a/mediagoblin/views.py +++ b/mediagoblin/views.py @@ -18,10 +18,10 @@ from mediagoblin import mg_globals from mediagoblin.db.models import MediaEntry from mediagoblin.tools.pagination import Pagination from mediagoblin.tools.response import render_to_response -from mediagoblin.decorators import uses_pagination - +from mediagoblin.decorators import uses_pagination, user_not_banned +@user_not_banned @uses_pagination def root_view(request, page): cursor = MediaEntry.query.filter_by(state=u'processed').\ -- 2.1.4