aboutsummaryrefslogtreecommitdiffstats
path: root/mediagoblin/moderation
diff options
context:
space:
mode:
authortilly-Q <nattilypigeonfowl@gmail.com>2013-07-27 16:44:40 -0400
committertilly-Q <nattilypigeonfowl@gmail.com>2013-07-27 16:44:40 -0400
commit3aa3871b909500ae9198d63814dd79fd28921f93 (patch)
tree02fbde1fcc3c395f19dbb6e5004c6866ee28fb12 /mediagoblin/moderation
parent6bba33d7e6fbb0cedc39f9a11f816fe5bd372ae7 (diff)
downloadmediagoblin-3aa3871b909500ae9198d63814dd79fd28921f93.tar.lz
mediagoblin-3aa3871b909500ae9198d63814dd79fd28921f93.tar.xz
mediagoblin-3aa3871b909500ae9198d63814dd79fd28921f93.zip
This commit had some important milestones in it. The major update is that now I
have mostly completed the moderator punishment and resolution of reports. Along with this, I have also added one last table to the database: one that holds ar- -chived (or resolved) reports. This is some of the primary functionality of my whole update, so this is a big step! The other changes I made this update are primarily organizational. I refactored some of my code into functions and I cl- eaned up many of my templates. --\ mediagoblin/db/models.py --| Created the new ArchivedReport table --| Removed columns from BaseReport table that are only necessary for Archived | reports --\ mediagoblin/db/migrations.py --| Created the new ArchivedReport table --| Removed columns from BaseReport table that are only necessary for Archived | reports --\ mediagoblin/db/util.py --| Created the user_privileges_to_dictionary function. This is useful for | accessing a user's permissions from within a template. --\ mediagoblin/moderation/forms.py --| Expanded the disciplinary actions a moderator can take --| Allowed the moderator to choose more than one disciplinary action at a time | (It's now managed with a list of checkboxes rather than radio buttons) ----| Pulled a MultiCheckBox class from a wtforms tutorial --| Added various other form inputs for details of the moderator's disciplinary | actions --| Tried to ensure that every string is unicode and translated --\ mediagoblin/moderation/tools.py --| Created this file for holding useful moderation tools --| Moved the penalizing code from views to the function take_punitive_actions --| Added many more types of punitive actions --| Added the archiving of old reports --\ mediagoblin/moderation/views.py --| Used the privileges_to_dictionary function for the Users Detail view to | allow for different actions available to a moderator and an admin. --| Added in functionality for ArchivedReports to the reports_detail and | reports_panel views --| Moved the punishments of repots_detail to tools.py (as mentioned above) --\ mediagoblin/static/css/base.css --| Added new styling for the User Detail page --\ mediagoblin/static/images/icon_clipboard_alert.png --| Added this image to represent unresolved reports --\ mediagoblin/templates/mediagoblin/moderation/report.html --| Added 'Return to Reports Panel' button --| Fixed the spacing to be less that 80 columns wide --| Added in display for Archived Reports --\ mediagoblin/templates/mediagoblin/moderation/reports_panel.html --| Changed the placement and columns of the tables --| Fixed the spacing to be less that 80 columns wide --| Added in display for Archived Reports --\ mediagoblin/templates/mediagoblin/moderation/user.html --| Fixed the spacing to be less that 80 columns wide --| Took away the moderator's ability to add and remove privileges at will. | Only the admin has this power now. --\ mediagoblin/templates/mediagoblin/moderation/users_panel.html --| Fixed the spacing to be less that 80 columns wide --\ mediagoblin/tools/response.py --| Added in code to remove a UserBan from a User if that user logs in after | the expiration date
Diffstat (limited to 'mediagoblin/moderation')
-rw-r--r--mediagoblin/moderation/forms.py36
-rw-r--r--mediagoblin/moderation/tools.py134
-rw-r--r--mediagoblin/moderation/views.py85
3 files changed, 180 insertions, 75 deletions
diff --git a/mediagoblin/moderation/forms.py b/mediagoblin/moderation/forms.py
index 0a91b9b4..718cd8fa 100644
--- a/mediagoblin/moderation/forms.py
+++ b/mediagoblin/moderation/forms.py
@@ -17,24 +17,44 @@
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'))]
+ACTION_CHOICES = [(_(u'takeaway'),_(u'Take away privilege')),
+ (_(u'userban'),_(u'Ban the user')),
+ (_(u'sendmessage'),(u'Send the user a message')),
+ (_(u'delete'),_(u'Delete the content'))]
+
+class MultiCheckboxField(wtforms.SelectMultipleField):
+ """
+ A multiple-select, except displays a list of checkboxes.
+
+ Iterating the field will produce subfields, allowing custom rendering of
+ the enclosed checkbox fields.
+
+ code from http://wtforms.simplecodes.com/docs/1.0.4/specific_problems.html
+ """
+ widget = wtforms.widgets.ListWidget(prefix_label=False)
+ option_widget = wtforms.widgets.CheckboxInput()
+
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()],
+ action_to_resolve = MultiCheckboxField(
+ _(u'What action will you take to resolve the report?'),
+ validators=[wtforms.validators.optional()],
choices=ACTION_CHOICES)
targeted_user = wtforms.HiddenField('',
validators=[wtforms.validators.required()])
+ take_away_privileges = wtforms.SelectMultipleField(
+ _(u'What privileges will you take away?'),
+ validators=[wtforms.validators.optional()])
user_banned_until = wtforms.DateField(
- _('User will be banned until:'),
+ _(u'User will be banned until:'),
format='%Y-%m-%d',
validators=[wtforms.validators.optional()])
+ why_user_was_banned = wtforms.TextAreaField(
+ validators=[wtforms.validators.optional()])
+ message_to_user = wtforms.TextAreaField(
+ validators=[wtforms.validators.optional()])
resolution_content = wtforms.TextAreaField()
diff --git a/mediagoblin/moderation/tools.py b/mediagoblin/moderation/tools.py
new file mode 100644
index 00000000..9a3b1c2e
--- /dev/null
+++ b/mediagoblin/moderation/tools.py
@@ -0,0 +1,134 @@
+# 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 <http://www.gnu.org/licenses/>.
+
+from mediagoblin import mg_globals
+from mediagoblin.db.models import User, Privilege, ArchivedReport, UserBan
+from mediagoblin.db.base import Session
+from mediagoblin.tools.mail import send_email
+from mediagoblin.tools.response import redirect
+from datetime import datetime
+from mediagoblin.tools.translate import lazy_pass_to_ugettext as _
+import sys, traceback
+
+def take_punitive_actions(request, form, report, user):
+ message_body =''
+ try:
+
+ # The bulk of this action is running through all of the different
+ # punitive actions that a moderator could take.
+ if u'takeaway' in form.action_to_resolve.data:
+ for privilege_name in form.take_away_privileges.data:
+ privilege = Privilege.one({u'privilege_name':privilege_name})
+ form.resolution_content.data += \
+ u"<br>%s took away %s\'s %s privileges" % (
+ request.user.username,
+ user.username,
+ privilege.privilege_name)
+ user.all_privileges.remove(privilege)
+
+ # If the moderator elects to ban the user, a new instance of user_ban
+ # will be created.
+ if u'userban' in form.action_to_resolve.data:
+ reason = form.resolution_content.data + \
+ "<br>"+request.user.username
+ user_ban = UserBan(
+ user_id=form.targeted_user.data,
+ expiration_date=form.user_banned_until.data,
+ reason= form.why_user_was_banned.data
+ )
+ Session.add(user_ban)
+
+ if form.user_banned_until.data is not None:
+ form.resolution_content.data += \
+ u"<br>%s banned user %s until %s." % (
+ request.user.username,
+ user.username,
+ form.user_banned_until.data)
+ else:
+ form.resolution_content.data += \
+ u"<br>%s banned user %s indefinitely." % (
+ request.user.username,
+ user.username)
+
+ # If the moderator elects to send a warning message. An email will be
+ # sent to the email address given at sign up
+ if u'sendmessage' in form.action_to_resolve.data:
+ message_body = form.message_to_user.data
+ form.resolution_content.data += \
+ u"<br>%s sent a warning email to the offender." % (
+ request.user.username)
+
+ archive = ArchivedReport(
+ reporter_id=report.reporter_id,
+ report_content=report.report_content,
+ reported_user_id=report.reported_user_id,
+ created=report.created,
+ resolved=datetime.now(),
+ resolver_id=request.user.id
+ )
+
+ if u'delete' in form.action_to_resolve.data and \
+ report.is_comment_report():
+ deleted_comment = report.comment
+ Session.delete(deleted_comment)
+ form.resolution_content.data += \
+ u"<br>%s deleted the comment" % (
+ request.user.username)
+ elif u'delete' in form.action_to_resolve.data and \
+ report.is_media_entry_report():
+ deleted_media = report.media_entry
+ Session.delete(deleted_media)
+ form.resolution_content.data += \
+ u"<br>%s deleted the media entry" % (
+ request.user.username)
+
+ # If the moderator didn't delete the content we then attach the
+ # content to the archived report. We also have to actively delete the
+ # old report, since it won't be deleted by cascading.
+ elif report.is_comment_report():
+ archive.comment_id = report.comment_id
+ Session.delete(report)
+ elif report.is_media_entry_report():
+ archive.media_entry_id = report.media_entry.id
+ Session.delete(report)
+
+
+ archive.result=form.resolution_content.data
+# Session.add(archive)
+ Session.commit()
+ if message_body:
+ send_email(
+ mg_globals.app_config['email_sender_address'],
+ [user.email],
+ _('Warning from')+ '- {moderator} '.format(
+ moderator=request.user.username),
+ message_body)
+
+ return redirect(
+ request,
+ 'mediagoblin.moderation.users_detail',
+ user=user.username)
+ except:
+#TODO make a more effective and specific try except statement. To account for
+# incorrect value addition my moderators
+ print sys.exc_info()[0]
+ print sys.exc_info()[1]
+ traceback.print_tb(sys.exc_info()[2])
+ Session.rollback()
+ return redirect(
+ request,
+ 'mediagoblin.moderation.reports_detail',
+ report_id=report.id)
diff --git a/mediagoblin/moderation/views.py b/mediagoblin/moderation/views.py
index 6f6318bc..67928927 100644
--- a/mediagoblin/moderation/views.py
+++ b/mediagoblin/moderation/views.py
@@ -18,11 +18,13 @@ from werkzeug.exceptions import Forbidden
from mediagoblin.db.models import (MediaEntry, User, MediaComment, \
CommentReport, ReportBase, Privilege, \
- UserBan)
+ UserBan, ArchivedReport)
+from mediagoblin.db.util import user_privileges_to_dictionary
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 mediagoblin.moderation.tools import take_punitive_actions
from datetime import datetime
@require_admin_or_moderator_login
@@ -67,17 +69,22 @@ def moderation_users_detail(request):
'''
user = User.query.filter_by(username=request.matchdict['user']).first()
active_reports = user.reports_filed_on.filter(
- ReportBase.resolved==None).limit(5)
+ ReportBase.discriminator!='archived_report').limit(5)
closed_reports = user.reports_filed_on.filter(
- ReportBase.resolved!=None).all()
+ ReportBase.discriminator=='archived_report').all()
privileges = Privilege.query
+ user_banned = UserBan.query.get(user.id)
+ user_privileges = user_privileges_to_dictionary(user.id)
+ requesting_user_privileges = user_privileges_to_dictionary(request.user.id)
return render_to_response(
request,
'mediagoblin/moderation/user.html',
{'user':user,
- 'privileges':privileges,
- 'reports':active_reports})
+ 'privileges': privileges,
+ 'requesting_user_privileges':requesting_user_privileges,
+ 'reports':active_reports,
+ 'user_banned':user_banned})
@require_admin_or_moderator_login
def moderation_reports_panel(request):
@@ -86,10 +93,10 @@ def moderation_reports_panel(request):
media entries for this instance.
'''
report_list = ReportBase.query.filter(
- ReportBase.resolved==None).order_by(
+ ReportBase.discriminator!="archived_report").order_by(
ReportBase.created.desc()).limit(10)
closed_report_list = ReportBase.query.filter(
- ReportBase.resolved!=None).order_by(
+ ReportBase.discriminator=="archived_report").order_by(
ReportBase.created.desc()).limit(10)
# Render to response
@@ -109,66 +116,12 @@ def moderation_reports_detail(request):
form = moderation_forms.ReportResolutionForm(request.form)
report = ReportBase.query.get(request.matchdict['report_id'])
+ form.take_away_privileges.choices = [(s.privilege_name,s.privilege_name.title()) for s in report.reported_user.all_privileges]
+
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"<br>%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"<br>%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 + \
- "<br>"+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"<br>%s banned user %s until %s." % (
- request.user.username,
- user.username,
- form.user_banned_until.data)
- else:
- form.resolution_content.data += \
- u"<br>%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)
+ return take_punitive_actions(request, form, report, user)
- 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
@@ -176,8 +129,6 @@ def moderation_reports_detail(request):
request,
'mediagoblin/moderation/report.html',
{'report':report,
- 'media_entry':media_entry,
- 'comment':comment,
'form':form})
@require_admin_or_moderator_login
@@ -189,7 +140,7 @@ def give_or_take_away_privilege(request, url_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:
+ if privilege in url_user.all_privileges:
url_user.all_privileges.remove(privilege)
else:
url_user.all_privileges.append(privilege)