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
This commit is contained in:
parent
650a0aa90d
commit
6bba33d7e6
@ -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 <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
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})
|
|
||||||
|
|
||||||
|
|
@ -300,6 +300,7 @@ class ReportBase_v0(declarative_base()):
|
|||||||
reported_user_id = Column(Integer, ForeignKey(User.id), nullable=False)
|
reported_user_id = Column(Integer, ForeignKey(User.id), nullable=False)
|
||||||
created = Column(DateTime, nullable=False, default=datetime.datetime.now)
|
created = Column(DateTime, nullable=False, default=datetime.datetime.now)
|
||||||
resolved = Column(DateTime)
|
resolved = Column(DateTime)
|
||||||
|
result = Column(UnicodeText)
|
||||||
discriminator = Column('type', Unicode(50))
|
discriminator = Column('type', Unicode(50))
|
||||||
__mapper_args__ = {'polymorphic_on': discriminator}
|
__mapper_args__ = {'polymorphic_on': discriminator}
|
||||||
|
|
||||||
|
@ -239,8 +239,8 @@ class MediaEntry(Base, MediaEntryMixin):
|
|||||||
This will *not* automatically delete unused collections, which
|
This will *not* automatically delete unused collections, which
|
||||||
can remain empty...
|
can remain empty...
|
||||||
|
|
||||||
:param del_orphan_tags: True/false if we delete unused Tags too
|
:keyword del_orphan_tags: True/false if we delete unused Tags too
|
||||||
:param commit: True/False if this should end the db transaction"""
|
:keyword commit: True/False if this should end the db transaction"""
|
||||||
# User's CollectionItems are automatically deleted via "cascade".
|
# User's CollectionItems are automatically deleted via "cascade".
|
||||||
# Comments on this Media are deleted by cascade, hopefully.
|
# Comments on this Media are deleted by cascade, hopefully.
|
||||||
|
|
||||||
@ -487,6 +487,14 @@ class ProcessingMetaData(Base):
|
|||||||
|
|
||||||
class ReportBase(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'
|
__tablename__ = 'core__reports'
|
||||||
@ -508,6 +516,7 @@ class ReportBase(Base):
|
|||||||
primaryjoin="User.id==ReportBase.reported_user_id")
|
primaryjoin="User.id==ReportBase.reported_user_id")
|
||||||
created = Column(DateTime, nullable=False, default=datetime.datetime.now())
|
created = Column(DateTime, nullable=False, default=datetime.datetime.now())
|
||||||
resolved = Column(DateTime)
|
resolved = Column(DateTime)
|
||||||
|
result = Column(UnicodeText)
|
||||||
discriminator = Column('type', Unicode(50))
|
discriminator = Column('type', Unicode(50))
|
||||||
__mapper_args__ = {'polymorphic_on': discriminator}
|
__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
|
the reason why they are banned and when (if ever) the ban will be
|
||||||
lifted
|
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
|
attached to. This is a one-to-one
|
||||||
relationship.
|
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
|
If this is null, the ban is permanent
|
||||||
unless a moderator manually lifts it.
|
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'
|
__tablename__ = 'core__user_bans'
|
||||||
|
|
||||||
@ -568,6 +577,17 @@ class UserBan(Base):
|
|||||||
|
|
||||||
|
|
||||||
class Privilege(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'
|
__tablename__ = 'core__privileges'
|
||||||
|
|
||||||
id = Column(Integer, nullable=False, primary_key=True)
|
id = Column(Integer, nullable=False, primary_key=True)
|
||||||
@ -578,12 +598,30 @@ class Privilege(Base):
|
|||||||
secondary="core__privileges_users")
|
secondary="core__privileges_users")
|
||||||
|
|
||||||
def __init__(self, privilege_name):
|
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
|
self.privilege_name = privilege_name
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "<Privilege %s>" % (self.privilege_name)
|
return "<Privilege %s>" % (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):
|
class PrivilegeUserAssociation(Base):
|
||||||
|
'''
|
||||||
|
This table holds the many-to-many relationship between User and Privilege
|
||||||
|
'''
|
||||||
|
|
||||||
__tablename__ = 'core__privileges_users'
|
__tablename__ = 'core__privileges_users'
|
||||||
|
|
||||||
privilege_id = Column(
|
privilege_id = Column(
|
||||||
|
@ -21,8 +21,9 @@ from werkzeug.exceptions import Forbidden, NotFound
|
|||||||
from werkzeug.urls import url_quote
|
from werkzeug.urls import url_quote
|
||||||
|
|
||||||
from mediagoblin import mg_globals as mgg
|
from mediagoblin import mg_globals as mgg
|
||||||
from mediagoblin.db.models import MediaEntry, User, MediaComment, Privilege
|
from mediagoblin.db.models import MediaEntry, User, MediaComment, Privilege, \
|
||||||
from mediagoblin.tools.response import redirect, render_404
|
UserBan
|
||||||
|
from mediagoblin.tools.response import redirect, render_404, render_user_banned
|
||||||
|
|
||||||
|
|
||||||
def require_active_login(controller):
|
def require_active_login(controller):
|
||||||
@ -64,6 +65,7 @@ def active_user_from_url(controller):
|
|||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
def user_has_privilege(privilege_name):
|
def user_has_privilege(privilege_name):
|
||||||
|
|
||||||
def user_has_privilege_decorator(controller):
|
def user_has_privilege_decorator(controller):
|
||||||
@wraps(controller)
|
@wraps(controller)
|
||||||
def wrapper(request, *args, **kwargs):
|
def wrapper(request, *args, **kwargs):
|
||||||
@ -71,7 +73,9 @@ def user_has_privilege(privilege_name):
|
|||||||
privileges_of_user = Privilege.query.filter(
|
privileges_of_user = Privilege.query.filter(
|
||||||
Privilege.all_users.any(
|
Privilege.all_users.any(
|
||||||
User.id==user_id))
|
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():
|
Privilege.privilege_name==privilege_name).count():
|
||||||
raise Forbidden()
|
raise Forbidden()
|
||||||
|
|
||||||
@ -271,14 +275,18 @@ def get_workbench(func):
|
|||||||
|
|
||||||
return new_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)
|
@wraps(controller)
|
||||||
def new_controller_func(request, *args, **kwargs):
|
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 \
|
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()
|
raise Forbidden()
|
||||||
elif not request.user:
|
elif not request.user:
|
||||||
next_url = urljoin(
|
next_url = urljoin(
|
||||||
@ -293,3 +301,18 @@ def require_admin_login(controller):
|
|||||||
|
|
||||||
return new_controller_func
|
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
|
||||||
|
|
||||||
|
@ -55,6 +55,13 @@ def adduser(args):
|
|||||||
entry.pw_hash = auth_lib.bcrypt_gen_password_hash(args.password)
|
entry.pw_hash = auth_lib.bcrypt_gen_password_hash(args.password)
|
||||||
entry.status = u'active'
|
entry.status = u'active'
|
||||||
entry.email_verified = True
|
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()
|
entry.save()
|
||||||
|
|
||||||
print "User created (and email marked as verified)"
|
print "User created (and email marked as verified)"
|
||||||
|
40
mediagoblin/moderation/forms.py
Normal file
40
mediagoblin/moderation/forms.py
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
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()
|
||||||
|
|
35
mediagoblin/moderation/routing.py
Normal file
35
mediagoblin/moderation/routing.py
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
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/<string:user>/',
|
||||||
|
'mediagoblin.moderation.views:moderation_users_detail'),
|
||||||
|
('mediagoblin.moderation.give_or_take_away_privilege',
|
||||||
|
'/users/<string:user>/privilege/',
|
||||||
|
'mediagoblin.moderation.views:give_or_take_away_privilege'),
|
||||||
|
('mediagoblin.moderation.reports_detail',
|
||||||
|
'/reports/<int:report_id>/',
|
||||||
|
'mediagoblin.moderation.views:moderation_reports_detail')]
|
200
mediagoblin/moderation/views.py
Normal file
200
mediagoblin/moderation/views.py
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
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"<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)
|
||||||
|
|
||||||
|
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)
|
@ -18,7 +18,7 @@ import logging
|
|||||||
|
|
||||||
from mediagoblin.tools.routing import add_route, mount, url_map
|
from mediagoblin.tools.routing import add_route, mount, url_map
|
||||||
from mediagoblin.tools.pluginapi import PluginManager
|
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
|
from mediagoblin.auth.routing import auth_routes
|
||||||
|
|
||||||
|
|
||||||
@ -28,7 +28,7 @@ _log = logging.getLogger(__name__)
|
|||||||
def get_url_map():
|
def get_url_map():
|
||||||
add_route('index', '/', 'mediagoblin.views:root_view')
|
add_route('index', '/', 'mediagoblin.views:root_view')
|
||||||
mount('/auth', auth_routes)
|
mount('/auth', auth_routes)
|
||||||
mount('/a', admin_routes)
|
mount('/mod', moderation_routes)
|
||||||
|
|
||||||
import mediagoblin.submit.routing
|
import mediagoblin.submit.routing
|
||||||
import mediagoblin.user_pages.routing
|
import mediagoblin.user_pages.routing
|
||||||
|
@ -402,6 +402,8 @@ a.report_authorlink, a.report_whenlink {
|
|||||||
color: #D486B1;
|
color: #D486B1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ul#action_to_resolve {list-style:none; margin-left:10px;}
|
||||||
|
|
||||||
/* media galleries */
|
/* media galleries */
|
||||||
|
|
||||||
.media_thumbnail {
|
.media_thumbnail {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
{#
|
||||||
# GNU MediaGoblin -- federated, autonomous media hosting
|
# GNU MediaGoblin -- federated, autonomous media hosting
|
||||||
# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
|
# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
|
||||||
#
|
#
|
||||||
@ -13,20 +14,15 @@
|
|||||||
#
|
#
|
||||||
# You should have received a copy of the GNU Affero General Public License
|
# 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/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#}
|
||||||
|
{% extends "mediagoblin/base.html" %}
|
||||||
|
|
||||||
admin_routes = [
|
{% block title %}You are Banned.{% endblock %}
|
||||||
('mediagoblin.admin.media_panel',
|
|
||||||
'/media',
|
{% block mediagoblin_content %}
|
||||||
'mediagoblin.admin.views:admin_media_processing_panel'),
|
<img class="right_align" src="{{ request.staticdirect('/images/404.png') }}"
|
||||||
('mediagoblin.admin.users',
|
alt="{% trans %}Image of goblin stressing out{% endtrans %}" />
|
||||||
'/users',
|
<h1>You have been banned until {{ expiration_date }}</h1>
|
||||||
'mediagoblin.admin.views:admin_users_panel'),
|
<p>{{ reason|safe }}</p>
|
||||||
('mediagoblin.admin.reports',
|
<div class="clear"></div>
|
||||||
'/reports',
|
{% endblock %}
|
||||||
'mediagoblin.admin.views:admin_reports_panel'),
|
|
||||||
('mediagoblin.admin.users_detail',
|
|
||||||
'/users/<string:user>',
|
|
||||||
'mediagoblin.admin.views:admin_users_detail'),
|
|
||||||
('mediagoblin.admin.reports_detail',
|
|
||||||
'/reports/<int:report_id>',
|
|
||||||
'mediagoblin.admin.views:admin_reports_detail')]
|
|
@ -104,9 +104,12 @@
|
|||||||
{% if request.user.is_admin %}
|
{% if request.user.is_admin %}
|
||||||
<p>
|
<p>
|
||||||
<span class="dropdown_title">Admin powers:</span>
|
<span class="dropdown_title">Admin powers:</span>
|
||||||
<a href="{{ request.urlgen('mediagoblin.admin.media_panel') }}">
|
<a href="{{ request.urlgen('mediagoblin.moderation.media_panel') }}">
|
||||||
{%- trans %}Media processing panel{% endtrans -%}
|
{%- trans %}Media processing panel{% endtrans -%}
|
||||||
</a>
|
</a>
|
||||||
|
<a href="{{ request.urlgen('mediagoblin.moderation.users') }}">
|
||||||
|
{%- trans %}User management panel{% endtrans -%}
|
||||||
|
</a>
|
||||||
</p>
|
</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#}
|
#}
|
||||||
{%- extends "mediagoblin/base.html" %}
|
{%- extends "mediagoblin/base.html" %}
|
||||||
|
{% import "/mediagoblin/utils/wtforms.html" as wtforms_util %}
|
||||||
|
|
||||||
{%- block mediagoblin_content %}
|
{%- block mediagoblin_content %}
|
||||||
{% if not report %}
|
{% if not report %}
|
||||||
@ -29,7 +30,7 @@
|
|||||||
class="comment_wrapper">
|
class="comment_wrapper">
|
||||||
<div class="comment_author">
|
<div class="comment_author">
|
||||||
<img src="{{ request.staticdirect('/images/icon_comment.png') }}" />
|
<img src="{{ request.staticdirect('/images/icon_comment.png') }}" />
|
||||||
<a href="{{ request.urlgen('mediagoblin.admin.users_detail',
|
<a href="{{ request.urlgen('mediagoblin.moderation.users_detail',
|
||||||
user=comment.get_author.username) }}"
|
user=comment.get_author.username) }}"
|
||||||
class="comment_authorlink">
|
class="comment_authorlink">
|
||||||
{{- reported_user.username -}}
|
{{- reported_user.username -}}
|
||||||
@ -68,12 +69,12 @@
|
|||||||
<div class="report_author">
|
<div class="report_author">
|
||||||
<img src="{{ request.staticdirect('/images/icon_clipboard.png') }}"
|
<img src="{{ request.staticdirect('/images/icon_clipboard.png') }}"
|
||||||
alt="Under a GNU LGPL v.3 or Creative Commons BY-SA 3.0 license. Distributed by the GNOME project http://www.gnome.org" />
|
alt="Under a GNU LGPL v.3 or Creative Commons BY-SA 3.0 license. Distributed by the GNOME project http://www.gnome.org" />
|
||||||
<a href="{{ request.urlgen('mediagoblin.admin.users_detail',
|
<a href="{{ request.urlgen('mediagoblin.moderation.users_detail',
|
||||||
user=report.reporter.username) }}"
|
user=report.reporter.username) }}"
|
||||||
class="report_authorlink">
|
class="report_authorlink">
|
||||||
{{- report.reporter.username -}}
|
{{- report.reporter.username -}}
|
||||||
</a>
|
</a>
|
||||||
<a href="{{ request.urlgen('mediagoblin.admin.reports_detail',
|
<a href="{{ request.urlgen('mediagoblin.moderation.reports_detail',
|
||||||
report_id=report.id) }}"
|
report_id=report.id) }}"
|
||||||
class="report_whenlink">
|
class="report_whenlink">
|
||||||
<span title='{{- report.created.strftime("%I:%M%p %Y-%m-%d") -}}'>
|
<span title='{{- report.created.strftime("%I:%M%p %Y-%m-%d") -}}'>
|
||||||
@ -87,5 +88,50 @@
|
|||||||
{{ report.report_content }}
|
{{ report.report_content }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{% if not report.resolved %}
|
||||||
|
<input type=button value=Resolve id=open_resolution_form />
|
||||||
|
<form action="" method="POST" id=resolution_form>
|
||||||
|
{{ wtforms_util.render_divs(form) }}
|
||||||
|
{{ csrf_token }}
|
||||||
|
<input type=submit id="submit_this_report" value="Resolve This Report"/>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
|
||||||
|
<script>
|
||||||
|
$(document).ready(function() {
|
||||||
|
$('form#resolution_form').hide()
|
||||||
|
$('#user_banned_until').val("YYYY-MM-DD")
|
||||||
|
$('#open_resolution_form').click(function() {
|
||||||
|
$('form#resolution_form').toggle();
|
||||||
|
$('#user_banned_until').hide();
|
||||||
|
$('label[for=user_banned_until]').hide();
|
||||||
|
});
|
||||||
|
$('#action_to_resolve').change(function() {
|
||||||
|
if ($('ul#action_to_resolve li input:checked').val() == "userban") {
|
||||||
|
$('#user_banned_until').show();
|
||||||
|
$('label[for=user_banned_until]').show();
|
||||||
|
} else {
|
||||||
|
$('#user_banned_until').hide();
|
||||||
|
$('label[for=user_banned_until]').hide();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
$("#user_banned_until").focus(function() {
|
||||||
|
$(this).val("");
|
||||||
|
$(this).unbind('focus');
|
||||||
|
});
|
||||||
|
$("#submit_this_report").click(function(){
|
||||||
|
if ($("#user_banned_until").val() == 'YYYY-MM-DD'){
|
||||||
|
$("#user_banned_until").val("");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{% else %}
|
||||||
|
<h2>Status:</h2>
|
||||||
|
RESOLVED on {{ report.resolved.strftime("%I:%M%p %Y-%m-%d") }}
|
||||||
|
{% autoescape False %}
|
||||||
|
<p>{{ report.result }}</p>
|
||||||
|
{% endautoescape %}
|
||||||
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
@ -29,7 +29,7 @@
|
|||||||
{% trans %}Here you can look up users in order to take punitive actions on them.{% endtrans %}
|
{% trans %}Here you can look up users in order to take punitive actions on them.{% endtrans %}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2>{% trans %}Reports Filed on Comments{% endtrans %}</h2>
|
<h2>{% trans %}Reports Filed{% endtrans %}</h2>
|
||||||
|
|
||||||
{% if report_list.count() %}
|
{% if report_list.count() %}
|
||||||
<table class="admin_panel processing">
|
<table class="admin_panel processing">
|
||||||
@ -44,8 +44,10 @@
|
|||||||
</tr>
|
</tr>
|
||||||
{% for report in report_list %}
|
{% for report in report_list %}
|
||||||
<tr>
|
<tr>
|
||||||
|
|
||||||
|
<td><a href="{{ request.urlgen('mediagoblin.moderation.reports_detail',
|
||||||
|
report_id=report.id) }}">{{ report.id }}</a></td>
|
||||||
{% if report.discriminator == "comment_report" %}
|
{% if report.discriminator == "comment_report" %}
|
||||||
<td>{{ report.id }}</td>
|
|
||||||
<td>Comment Report</td>
|
<td>Comment Report</td>
|
||||||
<td>{{ report.comment.get_author.username }}</td>
|
<td>{{ report.comment.get_author.username }}</td>
|
||||||
<td>{{ report.created.strftime("%F %R") }}</td>
|
<td>{{ report.created.strftime("%F %R") }}</td>
|
||||||
@ -53,7 +55,6 @@
|
|||||||
<td>{{ report.report_content }}</td>
|
<td>{{ report.report_content }}</td>
|
||||||
<td><a href="{{ report.comment.get_media_entry.url_for_self(request.urlgen) }}">{{ report.comment.get_media_entry.title }}</a></td>
|
<td><a href="{{ report.comment.get_media_entry.url_for_self(request.urlgen) }}">{{ report.comment.get_media_entry.title }}</a></td>
|
||||||
{% elif report.discriminator == "media_report" %}
|
{% elif report.discriminator == "media_report" %}
|
||||||
<td>{{ report.id }}</td>
|
|
||||||
<td>Media Report</td>
|
<td>Media Report</td>
|
||||||
<td>{{ report.media_entry.get_uploader.username }}</td>
|
<td>{{ report.media_entry.get_uploader.username }}</td>
|
||||||
<td>{{ report.created.strftime("%F %R") }}</td>
|
<td>{{ report.created.strftime("%F %R") }}</td>
|
||||||
@ -67,26 +68,36 @@
|
|||||||
{% else %}
|
{% else %}
|
||||||
<p><em>{% trans %}No open reports found.{% endtrans %}</em></p>
|
<p><em>{% trans %}No open reports found.{% endtrans %}</em></p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<h2>{% trans %}Closed Reports on Comments{% endtrans %}</h2>
|
<h2>{% trans %}Closed Reports{% endtrans %}</h2>
|
||||||
{% if closed_report_list.count() %}
|
{% if closed_report_list.count() %}
|
||||||
<table class="media_panel processing">
|
<table class="media_panel processing">
|
||||||
<tr>
|
<tr>
|
||||||
<th>ID</th>
|
<th>ID</th>
|
||||||
|
<th>Resolved</th>
|
||||||
<th>Offender</th>
|
<th>Offender</th>
|
||||||
<th>When Reported</th>
|
<th>Action Taken</th>
|
||||||
<th>Reported By</th>
|
<th>Reported By</th>
|
||||||
<th>Reason</th>
|
<th>Reason</th>
|
||||||
<th>Comment Posted On</th>
|
<th>Reported Comment or Media Entry</th>
|
||||||
</tr>
|
</tr>
|
||||||
{% for report in closed_report_list %}
|
{% for report in closed_report_list %}
|
||||||
<tr>
|
<td><a href="{{ request.urlgen('mediagoblin.moderation.reports_detail',
|
||||||
<td>{{ report.id }}</td>
|
report_id=report.id) }}">{{ report.id }}</a></td>
|
||||||
<td>{{ report.comment.get_author.username }}</td>
|
{% if report.discriminator == "comment_report" %}
|
||||||
<td>{{ report.created.strftime("%F %R") }}</td>
|
<td>{{ report.resolved.strftime("%F %R") }}</td>
|
||||||
<td>{{ report.reporter.username }}</td>
|
<td>{{ report.comment.get_author.username }}</td>
|
||||||
<td>{{ report.report_content }}</td>
|
<td>{{ report.created.strftime("%F %R") }}</td>
|
||||||
<td><a href="{{ report.comment.get_media_entry.url_for_self(request.urlgen) }}">{{ report.comment.get_media_entry.title }}</a></td>
|
<td>{{ report.reporter.username }}</td>
|
||||||
</tr>
|
<td>{{ report.report_content }}</td>
|
||||||
|
<td><a href="{{ report.comment.get_media_entry.url_for_self(request.urlgen) }}">{{ report.comment.get_media_entry.title }}</a></td>
|
||||||
|
{% elif report.discriminator == "media_report" %}
|
||||||
|
<td>{{ report.resolved.strftime("%F %R") }}</td>
|
||||||
|
<td>{{ report.media_entry.get_uploader.username }}</td>
|
||||||
|
<td>{{ report.created.strftime("%F %R") }}</td>
|
||||||
|
<td>{{ report.reporter.username }}</td>
|
||||||
|
<td>{{ report.report_content[0:20] }}...</td>
|
||||||
|
<td><a href="{{ report.media_entry.url_for_self(request.urlgen) }}">{{ report.media_entry.title }}</a></td>
|
||||||
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</table>
|
</table>
|
||||||
{% else %}
|
{% else %}
|
@ -85,7 +85,7 @@
|
|||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if user %}
|
{% if user %}
|
||||||
<h2>{%- trans %}Active Reports on{% endtrans -%} {{ user.username }}</h2>
|
<h2>{%- trans %}Active Reports on {% endtrans -%}{{ user.username }}</h2>
|
||||||
{% if reports.count() %}
|
{% if reports.count() %}
|
||||||
<table class="admin_side_panel">
|
<table class="admin_side_panel">
|
||||||
<tr>
|
<tr>
|
||||||
@ -97,7 +97,7 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<img src="{{ request.staticdirect('/images/icon_clipboard.png') }}" />
|
<img src="{{ request.staticdirect('/images/icon_clipboard.png') }}" />
|
||||||
<a href="{{ request.urlgen('mediagoblin.admin.reports_detail',
|
<a href="{{ request.urlgen('mediagoblin.moderation.reports_detail',
|
||||||
report_id=report.id) }}">
|
report_id=report.id) }}">
|
||||||
{%- trans %}Report #{% endtrans -%}{{ report.id }}
|
{%- trans %}Report #{% endtrans -%}{{ report.id }}
|
||||||
</a>
|
</a>
|
||||||
@ -116,7 +116,7 @@
|
|||||||
<tr><td></td><td></td>
|
<tr><td></td><td></td>
|
||||||
</table>
|
</table>
|
||||||
{% else %}
|
{% else %}
|
||||||
{%- trans %}No active reports filed on{% endtrans -%} {{ user.username }}
|
{%- trans %}No active reports filed on {% endtrans -%} {{ user.username }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<a class="right_align">{{ user.username }}'s report history</a>
|
<a class="right_align">{{ user.username }}'s report history</a>
|
||||||
<span class=clear></span>
|
<span class=clear></span>
|
||||||
@ -125,13 +125,31 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<th>{% trans %}Privilege{% endtrans %}</th>
|
<th>{% trans %}Privilege{% endtrans %}</th>
|
||||||
<th>{% trans %}User Has Privilege{% endtrans %}</th>
|
<th>{% trans %}User Has Privilege{% endtrans %}</th>
|
||||||
{% for privilege in privileges %}
|
|
||||||
<tr>
|
|
||||||
<td>{{ privilege.privilege_name }}</td>
|
|
||||||
<td>{% if privilege in user.all_privileges %}Yes{% else %}No{% endif %}</td>
|
|
||||||
<td>{% if privilege in user.all_privileges and privilege.id < request.user.get_highest_privilege().id %}<a>{% trans %}Take Away{% endtrans %}</a>{% else %}<a>{% trans %}Give Privilege{% endtrans %}</a>{% endif %}</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% for privilege in privileges %}
|
||||||
|
<tr>
|
||||||
|
<form action="{{ request.urlgen('mediagoblin.moderation.give_or_take_away_privilege',
|
||||||
|
user=user.username) }}"
|
||||||
|
method=post >
|
||||||
|
<td>{{ privilege.privilege_name }}</td>
|
||||||
|
<td>
|
||||||
|
{% if privilege in user.all_privileges %}Yes</td>
|
||||||
|
{% if (not privilege.is_admin_or_moderator() or request.user.is_admin) and not (user.is_admin and not request.user.is_admin) %}
|
||||||
|
<td><input type=submit value="{% trans %}Take Away{% endtrans %}" />
|
||||||
|
<input type=hidden name=giving_privilege />
|
||||||
|
{% endif %}
|
||||||
|
{% else %}No</td>
|
||||||
|
{% if (not privilege.is_admin_or_moderator() or request.user.is_admin) and not (user.is_admin and not request.user.is_admin) %}
|
||||||
|
<td><input type=submit value="{% trans %}Give Privilege{% endtrans %}" >
|
||||||
|
<input type=hidden name=giving_privilege value=True />
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
<input type=hidden name=privilege_name value="{{ privilege.privilege_name }}" />
|
||||||
|
</td>
|
||||||
|
{{ csrf_token }}
|
||||||
|
</form>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
</table>
|
</table>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
@ -42,7 +42,7 @@
|
|||||||
{% for user in user_list %}
|
{% for user in user_list %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ user.id }}</td>
|
<td>{{ user.id }}</td>
|
||||||
<td><a href="{{ request.urlgen('mediagoblin.admin.users_detail',
|
<td><a href="{{ request.urlgen('mediagoblin.moderation.users_detail',
|
||||||
user= user.username) }}">{{ user.username }}</a></td>
|
user= user.username) }}">{{ user.username }}</a></td>
|
||||||
<td>{{ user.created.strftime("%F %R") }}</td>
|
<td>{{ user.created.strftime("%F %R") }}</td>
|
||||||
<td>{{ user.posted_comments.count() }}</td>
|
<td>{{ user.posted_comments.count() }}</td>
|
@ -19,6 +19,7 @@ from werkzeug.wrappers import Response as wz_Response
|
|||||||
from mediagoblin.tools.template import render_template
|
from mediagoblin.tools.template import render_template
|
||||||
from mediagoblin.tools.translate import (lazy_pass_to_ugettext as _,
|
from mediagoblin.tools.translate import (lazy_pass_to_ugettext as _,
|
||||||
pass_to_ugettext)
|
pass_to_ugettext)
|
||||||
|
from mediagoblin.db.models import UserBan
|
||||||
|
|
||||||
class Response(wz_Response):
|
class Response(wz_Response):
|
||||||
"""Set default response mimetype to HTML, otherwise we get text/plain"""
|
"""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.")
|
"you're looking for has been moved or deleted.")
|
||||||
return render_error(request, 404, err_msg=err_msg)
|
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):
|
def render_http_exception(request, exc, description):
|
||||||
"""Return Response() given a werkzeug.HTTPException
|
"""Return Response() given a werkzeug.HTTPException
|
||||||
|
@ -51,11 +51,15 @@ class MediaCollectForm(wtforms.Form):
|
|||||||
Markdown</a> for formatting."""))
|
Markdown</a> for formatting."""))
|
||||||
|
|
||||||
class CommentReportForm(wtforms.Form):
|
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()
|
comment_id = wtforms.IntegerField()
|
||||||
reporter_id = wtforms.IntegerField()
|
reporter_id = wtforms.IntegerField()
|
||||||
|
|
||||||
class MediaReportForm(wtforms.Form):
|
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()
|
media_entry_id = wtforms.IntegerField()
|
||||||
reporter_id = wtforms.IntegerField()
|
reporter_id = wtforms.IntegerField()
|
||||||
|
@ -78,7 +78,7 @@ def add_media_to_collection(collection, media, note=None, commit=True):
|
|||||||
if commit:
|
if commit:
|
||||||
Session.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
|
This function is used to convert a form dictionary (from a User filing a
|
||||||
report) into either a MediaReport or CommentReport object.
|
report) into either a MediaReport or CommentReport object.
|
||||||
|
@ -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.translate import pass_to_ugettext as _
|
||||||
from mediagoblin.tools.pagination import Pagination
|
from mediagoblin.tools.pagination import Pagination
|
||||||
from mediagoblin.user_pages import forms as user_forms
|
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)
|
add_media_to_collection)
|
||||||
|
|
||||||
from mediagoblin.decorators import (uses_pagination, get_user_media_entry,
|
from mediagoblin.decorators import (uses_pagination, get_user_media_entry,
|
||||||
get_media_entry_by_id, user_has_privilege,
|
get_media_entry_by_id, user_has_privilege,
|
||||||
require_active_login, user_may_delete_media, user_may_alter_collection,
|
require_active_login, user_may_delete_media, user_may_alter_collection,
|
||||||
get_user_collection, get_user_collection_item, active_user_from_url,
|
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
|
from werkzeug.contrib.atom import AtomFeed
|
||||||
|
|
||||||
@ -41,7 +41,7 @@ from werkzeug.contrib.atom import AtomFeed
|
|||||||
_log = logging.getLogger(__name__)
|
_log = logging.getLogger(__name__)
|
||||||
_log.setLevel(logging.DEBUG)
|
_log.setLevel(logging.DEBUG)
|
||||||
|
|
||||||
|
@user_not_banned
|
||||||
@uses_pagination
|
@uses_pagination
|
||||||
def user_home(request, page):
|
def user_home(request, page):
|
||||||
"""'Homepage' of a User()"""
|
"""'Homepage' of a User()"""
|
||||||
@ -80,7 +80,7 @@ def user_home(request, page):
|
|||||||
'media_entries': media_entries,
|
'media_entries': media_entries,
|
||||||
'pagination': pagination})
|
'pagination': pagination})
|
||||||
|
|
||||||
|
@user_not_banned
|
||||||
@active_user_from_url
|
@active_user_from_url
|
||||||
@uses_pagination
|
@uses_pagination
|
||||||
def user_gallery(request, page, url_user=None):
|
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
|
MEDIA_COMMENTS_PER_PAGE = 50
|
||||||
|
|
||||||
|
@user_not_banned
|
||||||
@get_user_media_entry
|
@get_user_media_entry
|
||||||
@uses_pagination
|
@uses_pagination
|
||||||
def media_home(request, media, page, **kwargs):
|
def media_home(request, media, page, **kwargs):
|
||||||
@ -190,7 +190,7 @@ def media_post_comment(request, media):
|
|||||||
|
|
||||||
return redirect_obj(request, media)
|
return redirect_obj(request, media)
|
||||||
|
|
||||||
|
@user_not_banned
|
||||||
@get_media_entry_by_id
|
@get_media_entry_by_id
|
||||||
@require_active_login
|
@require_active_login
|
||||||
def media_collect(request, media):
|
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?
|
#TODO: Why does @user_may_delete_media not implicate @require_active_login?
|
||||||
|
@user_not_banned
|
||||||
@get_media_entry_by_id
|
@get_media_entry_by_id
|
||||||
@require_active_login
|
@require_active_login
|
||||||
@user_may_delete_media
|
@user_may_delete_media
|
||||||
@ -305,7 +306,7 @@ def media_confirm_delete(request, media):
|
|||||||
{'media': media,
|
{'media': media,
|
||||||
'form': form})
|
'form': form})
|
||||||
|
|
||||||
|
@user_not_banned
|
||||||
@active_user_from_url
|
@active_user_from_url
|
||||||
@uses_pagination
|
@uses_pagination
|
||||||
def user_collection(request, page, url_user=None):
|
def user_collection(request, page, url_user=None):
|
||||||
@ -335,7 +336,7 @@ def user_collection(request, page, url_user=None):
|
|||||||
'collection_items': collection_items,
|
'collection_items': collection_items,
|
||||||
'pagination': pagination})
|
'pagination': pagination})
|
||||||
|
|
||||||
|
@user_not_banned
|
||||||
@active_user_from_url
|
@active_user_from_url
|
||||||
def collection_list(request, url_user=None):
|
def collection_list(request, url_user=None):
|
||||||
"""A User-defined Collection"""
|
"""A User-defined Collection"""
|
||||||
@ -391,7 +392,7 @@ def collection_item_confirm_remove(request, collection_item):
|
|||||||
{'collection_item': collection_item,
|
{'collection_item': collection_item,
|
||||||
'form': form})
|
'form': form})
|
||||||
|
|
||||||
|
@user_not_banned
|
||||||
@get_user_collection
|
@get_user_collection
|
||||||
@require_active_login
|
@require_active_login
|
||||||
@user_may_alter_collection
|
@user_may_alter_collection
|
||||||
@ -575,7 +576,7 @@ def collection_atom_feed(request):
|
|||||||
|
|
||||||
return feed.get_response()
|
return feed.get_response()
|
||||||
|
|
||||||
|
@user_not_banned
|
||||||
@require_active_login
|
@require_active_login
|
||||||
def processing_panel(request):
|
def processing_panel(request):
|
||||||
"""
|
"""
|
||||||
@ -625,8 +626,8 @@ def processing_panel(request):
|
|||||||
@user_has_privilege(u'reporter')
|
@user_has_privilege(u'reporter')
|
||||||
def file_a_report(request, media, comment=None):
|
def file_a_report(request, media, comment=None):
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
report_form = build_report_form(request.form)
|
report_table = build_report_table(request.form)
|
||||||
report_form.save()
|
report_table.save()
|
||||||
|
|
||||||
return redirect(
|
return redirect(
|
||||||
request,
|
request,
|
||||||
|
@ -18,10 +18,10 @@ from mediagoblin import mg_globals
|
|||||||
from mediagoblin.db.models import MediaEntry
|
from mediagoblin.db.models import MediaEntry
|
||||||
from mediagoblin.tools.pagination import Pagination
|
from mediagoblin.tools.pagination import Pagination
|
||||||
from mediagoblin.tools.response import render_to_response
|
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
|
@uses_pagination
|
||||||
def root_view(request, page):
|
def root_view(request, page):
|
||||||
cursor = MediaEntry.query.filter_by(state=u'processed').\
|
cursor = MediaEntry.query.filter_by(state=u'processed').\
|
||||||
|
Loading…
x
Reference in New Issue
Block a user