Merge remote-tracking branch 'refs/remotes/tilly-q/OPW-Moderation-Update'
Conflicts: mediagoblin/templates/mediagoblin/user_pages/user.html mediagoblin/tests/test_auth.py mediagoblin/tests/test_submission.py
This commit is contained in:
commit
56c4ad89eb
@ -20,6 +20,9 @@ email_debug_mode = true
|
|||||||
# Set to false to disable registrations
|
# Set to false to disable registrations
|
||||||
allow_registration = true
|
allow_registration = true
|
||||||
|
|
||||||
|
# Set to false to disable the ability for users to report offensive content
|
||||||
|
allow_reporting = true
|
||||||
|
|
||||||
## Uncomment this to put some user-overriding templates here
|
## Uncomment this to put some user-overriding templates here
|
||||||
# local_templates = %(here)s/user_dev/templates/
|
# local_templates = %(here)s/user_dev/templates/
|
||||||
|
|
||||||
|
@ -1,48 +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
|
|
||||||
from mediagoblin.decorators import require_active_login
|
|
||||||
from mediagoblin.tools.response import render_to_response
|
|
||||||
|
|
||||||
@require_active_login
|
|
||||||
def admin_processing_panel(request):
|
|
||||||
'''
|
|
||||||
Show the global processing panel for this instance
|
|
||||||
'''
|
|
||||||
# TODO: Why not a "require_admin_login" decorator throwing a 403 exception?
|
|
||||||
if not request.user.is_admin:
|
|
||||||
raise Forbidden()
|
|
||||||
|
|
||||||
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/panel.html',
|
|
||||||
{'processing_entries': processing_entries,
|
|
||||||
'failed_entries': failed_entries,
|
|
||||||
'processed_entries': processed_entries})
|
|
@ -14,12 +14,14 @@
|
|||||||
# 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/>.
|
||||||
|
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import wtforms
|
import wtforms
|
||||||
|
from sqlalchemy import or_
|
||||||
|
|
||||||
from mediagoblin import mg_globals
|
from mediagoblin import mg_globals
|
||||||
from mediagoblin.tools.crypto import get_timed_signer_url
|
from mediagoblin.tools.crypto import get_timed_signer_url
|
||||||
from mediagoblin.db.models import User
|
from mediagoblin.db.models import User, Privilege
|
||||||
from mediagoblin.tools.mail import (normalize_email, send_email,
|
from mediagoblin.tools.mail import (normalize_email, send_email,
|
||||||
email_debug_message)
|
email_debug_message)
|
||||||
from mediagoblin.tools.template import render_template
|
from mediagoblin.tools.template import render_template
|
||||||
@ -129,6 +131,14 @@ def register_user(request, register_form):
|
|||||||
# Create the user
|
# Create the user
|
||||||
user = auth.create_user(register_form)
|
user = auth.create_user(register_form)
|
||||||
|
|
||||||
|
# give the user the default privileges
|
||||||
|
default_privileges = [
|
||||||
|
Privilege.query.filter(Privilege.privilege_name==u'commenter').first(),
|
||||||
|
Privilege.query.filter(Privilege.privilege_name==u'uploader').first(),
|
||||||
|
Privilege.query.filter(Privilege.privilege_name==u'reporter').first()]
|
||||||
|
user.all_privileges += default_privileges
|
||||||
|
user.save()
|
||||||
|
|
||||||
# log the user in
|
# log the user in
|
||||||
request.session['user_id'] = unicode(user.id)
|
request.session['user_id'] = unicode(user.id)
|
||||||
request.session.save()
|
request.session.save()
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
from itsdangerous import BadSignature
|
from itsdangerous import BadSignature
|
||||||
|
|
||||||
from mediagoblin import messages, mg_globals
|
from mediagoblin import messages, mg_globals
|
||||||
from mediagoblin.db.models import User
|
from mediagoblin.db.models import User, Privilege
|
||||||
from mediagoblin.tools.crypto import get_timed_signer_url
|
from mediagoblin.tools.crypto import get_timed_signer_url
|
||||||
from mediagoblin.decorators import auth_enabled, allow_registration
|
from mediagoblin.decorators import auth_enabled, allow_registration
|
||||||
from mediagoblin.tools.response import render_to_response, redirect, render_404
|
from mediagoblin.tools.response import render_to_response, redirect, render_404
|
||||||
@ -147,9 +147,12 @@ def verify_email(request):
|
|||||||
|
|
||||||
user = User.query.filter_by(id=int(token)).first()
|
user = User.query.filter_by(id=int(token)).first()
|
||||||
|
|
||||||
if user and user.email_verified is False:
|
if user and user.has_privilege(u'active') is False:
|
||||||
user.status = u'active'
|
user.verification_key = None
|
||||||
user.email_verified = True
|
user.all_privileges.append(
|
||||||
|
Privilege.query.filter(
|
||||||
|
Privilege.privilege_name==u'active').first())
|
||||||
|
|
||||||
user.save()
|
user.save()
|
||||||
|
|
||||||
messages.add_message(
|
messages.add_message(
|
||||||
@ -183,7 +186,7 @@ def resend_activation(request):
|
|||||||
|
|
||||||
return redirect(request, 'mediagoblin.auth.login')
|
return redirect(request, 'mediagoblin.auth.login')
|
||||||
|
|
||||||
if request.user.email_verified:
|
if request.user.has_privilege(u'active'):
|
||||||
messages.add_message(
|
messages.add_message(
|
||||||
request,
|
request,
|
||||||
messages.ERROR,
|
messages.ERROR,
|
||||||
@ -248,7 +251,7 @@ def forgot_password(request):
|
|||||||
success_message=_("An email has been sent with instructions "
|
success_message=_("An email has been sent with instructions "
|
||||||
"on how to change your password.")
|
"on how to change your password.")
|
||||||
|
|
||||||
if user and not(user.email_verified and user.status == 'active'):
|
if user and not(user.has_privilege(u'active')):
|
||||||
# Don't send reminder because user is inactive or has no verified email
|
# Don't send reminder because user is inactive or has no verified email
|
||||||
messages.add_message(request,
|
messages.add_message(request,
|
||||||
messages.WARNING,
|
messages.WARNING,
|
||||||
@ -304,8 +307,8 @@ def verify_forgot_password(request):
|
|||||||
return redirect(
|
return redirect(
|
||||||
request, 'index')
|
request, 'index')
|
||||||
|
|
||||||
# check if user active and has email verified
|
# check if user active
|
||||||
if user.email_verified and user.status == 'active':
|
if user.has_privilege(u'active'):
|
||||||
|
|
||||||
cp_form = auth_forms.ChangePassForm(formdata_vars)
|
cp_form = auth_forms.ChangePassForm(formdata_vars)
|
||||||
|
|
||||||
@ -325,13 +328,13 @@ def verify_forgot_password(request):
|
|||||||
'mediagoblin/auth/change_fp.html',
|
'mediagoblin/auth/change_fp.html',
|
||||||
{'cp_form': cp_form,})
|
{'cp_form': cp_form,})
|
||||||
|
|
||||||
if not user.email_verified:
|
if not user.has_privilege(u'active'):
|
||||||
messages.add_message(
|
messages.add_message(
|
||||||
request, messages.ERROR,
|
request, messages.ERROR,
|
||||||
_('You need to verify your email before you can reset your'
|
_('You need to verify your email before you can reset your'
|
||||||
' password.'))
|
' password.'))
|
||||||
|
|
||||||
if not user.status == 'active':
|
if not user.has_privilege(u'active'):
|
||||||
messages.add_message(
|
messages.add_message(
|
||||||
request, messages.ERROR,
|
request, messages.ERROR,
|
||||||
_('You are no longer an active user. Please contact the system'
|
_('You are no longer an active user. Please contact the system'
|
||||||
|
@ -42,6 +42,9 @@ allow_comments = boolean(default=True)
|
|||||||
# Whether comments are ascending or descending
|
# Whether comments are ascending or descending
|
||||||
comments_ascending = boolean(default=True)
|
comments_ascending = boolean(default=True)
|
||||||
|
|
||||||
|
# Enable/disable reporting
|
||||||
|
allow_reporting = boolean(default=True)
|
||||||
|
|
||||||
# By default not set, but you might want something like:
|
# By default not set, but you might want something like:
|
||||||
# "%(here)s/user_dev/templates/"
|
# "%(here)s/user_dev/templates/"
|
||||||
local_templates = string()
|
local_templates = string()
|
||||||
|
@ -19,7 +19,7 @@ import uuid
|
|||||||
|
|
||||||
from sqlalchemy import (MetaData, Table, Column, Boolean, SmallInteger,
|
from sqlalchemy import (MetaData, Table, Column, Boolean, SmallInteger,
|
||||||
Integer, Unicode, UnicodeText, DateTime,
|
Integer, Unicode, UnicodeText, DateTime,
|
||||||
ForeignKey)
|
ForeignKey, Date)
|
||||||
from sqlalchemy.exc import ProgrammingError
|
from sqlalchemy.exc import ProgrammingError
|
||||||
from sqlalchemy.ext.declarative import declarative_base
|
from sqlalchemy.ext.declarative import declarative_base
|
||||||
from sqlalchemy.sql import and_
|
from sqlalchemy.sql import and_
|
||||||
@ -28,7 +28,8 @@ from migrate.changeset.constraint import UniqueConstraint
|
|||||||
|
|
||||||
from mediagoblin.db.extratypes import JSONEncoded, MutationDict
|
from mediagoblin.db.extratypes import JSONEncoded, MutationDict
|
||||||
from mediagoblin.db.migration_tools import RegisterMigration, inspect_table
|
from mediagoblin.db.migration_tools import RegisterMigration, inspect_table
|
||||||
from mediagoblin.db.models import MediaEntry, Collection, User, MediaComment
|
from mediagoblin.db.models import (MediaEntry, Collection, MediaComment, User,
|
||||||
|
Privilege)
|
||||||
|
|
||||||
MIGRATIONS = {}
|
MIGRATIONS = {}
|
||||||
|
|
||||||
@ -469,9 +470,204 @@ def wants_notifications(db):
|
|||||||
"""Add a wants_notifications field to User model"""
|
"""Add a wants_notifications field to User model"""
|
||||||
metadata = MetaData(bind=db.bind)
|
metadata = MetaData(bind=db.bind)
|
||||||
user_table = inspect_table(metadata, "core__users")
|
user_table = inspect_table(metadata, "core__users")
|
||||||
|
|
||||||
col = Column('wants_notifications', Boolean, default=True)
|
col = Column('wants_notifications', Boolean, default=True)
|
||||||
col.create(user_table)
|
col.create(user_table)
|
||||||
|
db.commit()
|
||||||
|
|
||||||
|
class ReportBase_v0(declarative_base()):
|
||||||
|
__tablename__ = 'core__reports'
|
||||||
|
id = Column(Integer, primary_key=True)
|
||||||
|
reporter_id = Column(Integer, ForeignKey(User.id), nullable=False)
|
||||||
|
report_content = Column(UnicodeText)
|
||||||
|
reported_user_id = Column(Integer, ForeignKey(User.id), nullable=False)
|
||||||
|
created = Column(DateTime, nullable=False, default=datetime.datetime.now)
|
||||||
|
discriminator = Column('type', Unicode(50))
|
||||||
|
resolver_id = Column(Integer, ForeignKey(User.id))
|
||||||
|
resolved = Column(DateTime)
|
||||||
|
result = Column(UnicodeText)
|
||||||
|
__mapper_args__ = {'polymorphic_on': discriminator}
|
||||||
|
|
||||||
|
class CommentReport_v0(ReportBase_v0):
|
||||||
|
__tablename__ = 'core__reports_on_comments'
|
||||||
|
__mapper_args__ = {'polymorphic_identity': 'comment_report'}
|
||||||
|
|
||||||
|
id = Column('id',Integer, ForeignKey('core__reports.id'),
|
||||||
|
primary_key=True)
|
||||||
|
comment_id = Column(Integer, ForeignKey(MediaComment.id), nullable=True)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class MediaReport_v0(ReportBase_v0):
|
||||||
|
__tablename__ = 'core__reports_on_media'
|
||||||
|
__mapper_args__ = {'polymorphic_identity': 'media_report'}
|
||||||
|
|
||||||
|
id = Column('id',Integer, ForeignKey('core__reports.id'), primary_key=True)
|
||||||
|
media_entry_id = Column(Integer, ForeignKey(MediaEntry.id), nullable=True)
|
||||||
|
|
||||||
|
class UserBan_v0(declarative_base()):
|
||||||
|
__tablename__ = 'core__user_bans'
|
||||||
|
user_id = Column(Integer, ForeignKey(User.id), nullable=False,
|
||||||
|
primary_key=True)
|
||||||
|
expiration_date = Column(Date)
|
||||||
|
reason = Column(UnicodeText, nullable=False)
|
||||||
|
|
||||||
|
class Privilege_v0(declarative_base()):
|
||||||
|
__tablename__ = 'core__privileges'
|
||||||
|
id = Column(Integer, nullable=False, primary_key=True, unique=True)
|
||||||
|
privilege_name = Column(Unicode, nullable=False, unique=True)
|
||||||
|
|
||||||
|
class PrivilegeUserAssociation_v0(declarative_base()):
|
||||||
|
__tablename__ = 'core__privileges_users'
|
||||||
|
privilege_id = Column(
|
||||||
|
'core__privilege_id',
|
||||||
|
Integer,
|
||||||
|
ForeignKey(User.id),
|
||||||
|
primary_key=True)
|
||||||
|
user_id = Column(
|
||||||
|
'core__user_id',
|
||||||
|
Integer,
|
||||||
|
ForeignKey(Privilege.id),
|
||||||
|
primary_key=True)
|
||||||
|
|
||||||
|
PRIVILEGE_FOUNDATIONS_v0 = [{'privilege_name':u'admin'},
|
||||||
|
{'privilege_name':u'moderator'},
|
||||||
|
{'privilege_name':u'uploader'},
|
||||||
|
{'privilege_name':u'reporter'},
|
||||||
|
{'privilege_name':u'commenter'},
|
||||||
|
{'privilege_name':u'active'}]
|
||||||
|
|
||||||
|
|
||||||
|
class User_vR1(declarative_base()):
|
||||||
|
__tablename__ = 'rename__users'
|
||||||
|
id = Column(Integer, primary_key=True)
|
||||||
|
username = Column(Unicode, nullable=False, unique=True)
|
||||||
|
email = Column(Unicode, nullable=False)
|
||||||
|
pw_hash = Column(Unicode)
|
||||||
|
created = Column(DateTime, nullable=False, default=datetime.datetime.now)
|
||||||
|
wants_comment_notification = Column(Boolean, default=True)
|
||||||
|
wants_notifications = Column(Boolean, default=True)
|
||||||
|
license_preference = Column(Unicode)
|
||||||
|
url = Column(Unicode)
|
||||||
|
bio = Column(UnicodeText) # ??
|
||||||
|
|
||||||
|
@RegisterMigration(18, MIGRATIONS)
|
||||||
|
def create_moderation_tables(db):
|
||||||
|
|
||||||
|
# First, we will create the new tables in the database.
|
||||||
|
#--------------------------------------------------------------------------
|
||||||
|
ReportBase_v0.__table__.create(db.bind)
|
||||||
|
CommentReport_v0.__table__.create(db.bind)
|
||||||
|
MediaReport_v0.__table__.create(db.bind)
|
||||||
|
UserBan_v0.__table__.create(db.bind)
|
||||||
|
Privilege_v0.__table__.create(db.bind)
|
||||||
|
PrivilegeUserAssociation_v0.__table__.create(db.bind)
|
||||||
|
|
||||||
|
db.commit()
|
||||||
|
|
||||||
|
# Then initialize the tables that we will later use
|
||||||
|
#--------------------------------------------------------------------------
|
||||||
|
metadata = MetaData(bind=db.bind)
|
||||||
|
privileges_table= inspect_table(metadata, "core__privileges")
|
||||||
|
user_table = inspect_table(metadata, 'core__users')
|
||||||
|
user_privilege_assoc = inspect_table(
|
||||||
|
metadata, 'core__privileges_users')
|
||||||
|
|
||||||
|
# This section initializes the default Privilege foundations, that
|
||||||
|
# would be created through the FOUNDATIONS system in a new instance
|
||||||
|
#--------------------------------------------------------------------------
|
||||||
|
for parameters in PRIVILEGE_FOUNDATIONS_v0:
|
||||||
|
db.execute(privileges_table.insert().values(**parameters))
|
||||||
|
|
||||||
|
db.commit()
|
||||||
|
|
||||||
|
# This next section takes the information from the old is_admin and status
|
||||||
|
# columns and converts those to the new privilege system
|
||||||
|
#--------------------------------------------------------------------------
|
||||||
|
admin_users_ids, active_users_ids, inactive_users_ids = (
|
||||||
|
db.execute(
|
||||||
|
user_table.select().where(
|
||||||
|
user_table.c.is_admin==1)).fetchall(),
|
||||||
|
db.execute(
|
||||||
|
user_table.select().where(
|
||||||
|
user_table.c.is_admin==0).where(
|
||||||
|
user_table.c.status==u"active")).fetchall(),
|
||||||
|
db.execute(
|
||||||
|
user_table.select().where(
|
||||||
|
user_table.c.is_admin==0).where(
|
||||||
|
user_table.c.status!=u"active")).fetchall())
|
||||||
|
|
||||||
|
# Get the ids for each of the privileges so we can reference them ~~~~~~~~~
|
||||||
|
(admin_privilege_id, uploader_privilege_id,
|
||||||
|
reporter_privilege_id, commenter_privilege_id,
|
||||||
|
active_privilege_id) = [
|
||||||
|
db.execute(privileges_table.select().where(
|
||||||
|
privileges_table.c.privilege_name==privilege_name)).first()['id']
|
||||||
|
for privilege_name in
|
||||||
|
[u"admin",u"uploader",u"reporter",u"commenter",u"active"]
|
||||||
|
]
|
||||||
|
|
||||||
|
# Give each user the appopriate privileges depending whether they are an
|
||||||
|
# admin, an active user or an inactivated user ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
for admin_user in admin_users_ids:
|
||||||
|
admin_user_id = admin_user['id']
|
||||||
|
for privilege_id in [admin_privilege_id, uploader_privilege_id, reporter_privilege_id, commenter_privilege_id, active_privilege_id]:
|
||||||
|
db.execute(user_privilege_assoc.insert().values(
|
||||||
|
core__privilege_id=admin_user_id,
|
||||||
|
core__user_id=privilege_id))
|
||||||
|
|
||||||
|
for active_user in active_users_ids:
|
||||||
|
active_user_id = active_user['id']
|
||||||
|
for privilege_id in [uploader_privilege_id, reporter_privilege_id, commenter_privilege_id, active_privilege_id]:
|
||||||
|
db.execute(user_privilege_assoc.insert().values(
|
||||||
|
core__privilege_id=active_user_id,
|
||||||
|
core__user_id=privilege_id))
|
||||||
|
|
||||||
|
for inactive_user in inactive_users_ids:
|
||||||
|
inactive_user_id = inactive_user['id']
|
||||||
|
for privilege_id in [uploader_privilege_id, reporter_privilege_id, commenter_privilege_id]:
|
||||||
|
db.execute(user_privilege_assoc.insert().values(
|
||||||
|
core__privilege_id=inactive_user_id,
|
||||||
|
core__user_id=privilege_id))
|
||||||
|
|
||||||
|
db.commit()
|
||||||
|
|
||||||
|
# And then, once the information is taken from the is_admin & status columns
|
||||||
|
# we drop all of the vestigial columns from the User table.
|
||||||
|
#--------------------------------------------------------------------------
|
||||||
|
if db.bind.url.drivername == 'sqlite':
|
||||||
|
# SQLite has some issues that make it *impossible* to drop boolean
|
||||||
|
# columns. So, the following code is a very hacky workaround which
|
||||||
|
# makes it possible. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
User_vR1.__table__.create(db.bind)
|
||||||
|
db.commit()
|
||||||
|
new_user_table = inspect_table(metadata, 'rename__users')
|
||||||
|
for row in db.execute(user_table.select()):
|
||||||
|
db.execute(new_user_table.insert().values(
|
||||||
|
username=row.username,
|
||||||
|
email=row.email,
|
||||||
|
pw_hash=row.pw_hash,
|
||||||
|
created=row.created,
|
||||||
|
wants_comment_notification=row.wants_comment_notification,
|
||||||
|
wants_notifications=row.wants_notifications,
|
||||||
|
license_preference=row.license_preference,
|
||||||
|
url=row.url,
|
||||||
|
bio=row.bio))
|
||||||
|
|
||||||
|
db.commit()
|
||||||
|
user_table.drop()
|
||||||
|
|
||||||
|
db.commit()
|
||||||
|
new_user_table.rename("core__users")
|
||||||
|
else:
|
||||||
|
# If the db is not SQLite, this process is much simpler ~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
status = user_table.columns['status']
|
||||||
|
email_verified = user_table.columns['email_verified']
|
||||||
|
is_admin = user_table.columns['is_admin']
|
||||||
|
status.drop()
|
||||||
|
email_verified.drop()
|
||||||
|
is_admin.drop()
|
||||||
|
|
||||||
db.commit()
|
db.commit()
|
||||||
|
|
||||||
|
@ -46,7 +46,6 @@ class UserMixin(object):
|
|||||||
def bio_html(self):
|
def bio_html(self):
|
||||||
return cleaned_markdown_conversion(self.bio)
|
return cleaned_markdown_conversion(self.bio)
|
||||||
|
|
||||||
|
|
||||||
class GenerateSlugMixin(object):
|
class GenerateSlugMixin(object):
|
||||||
"""
|
"""
|
||||||
Mixin to add a generate_slug method to objects.
|
Mixin to add a generate_slug method to objects.
|
||||||
|
@ -23,7 +23,7 @@ import datetime
|
|||||||
|
|
||||||
from sqlalchemy import Column, Integer, Unicode, UnicodeText, DateTime, \
|
from sqlalchemy import Column, Integer, Unicode, UnicodeText, DateTime, \
|
||||||
Boolean, ForeignKey, UniqueConstraint, PrimaryKeyConstraint, \
|
Boolean, ForeignKey, UniqueConstraint, PrimaryKeyConstraint, \
|
||||||
SmallInteger
|
SmallInteger, Date
|
||||||
from sqlalchemy.orm import relationship, backref, with_polymorphic
|
from sqlalchemy.orm import relationship, backref, with_polymorphic
|
||||||
from sqlalchemy.orm.collections import attribute_mapped_collection
|
from sqlalchemy.orm.collections import attribute_mapped_collection
|
||||||
from sqlalchemy.sql.expression import desc
|
from sqlalchemy.sql.expression import desc
|
||||||
@ -64,15 +64,12 @@ class User(Base, UserMixin):
|
|||||||
# point.
|
# point.
|
||||||
email = Column(Unicode, nullable=False)
|
email = Column(Unicode, nullable=False)
|
||||||
pw_hash = Column(Unicode)
|
pw_hash = Column(Unicode)
|
||||||
email_verified = Column(Boolean, default=False)
|
|
||||||
created = Column(DateTime, nullable=False, default=datetime.datetime.now)
|
created = Column(DateTime, nullable=False, default=datetime.datetime.now)
|
||||||
status = Column(Unicode, default=u"needs_email_verification", nullable=False)
|
|
||||||
# Intented to be nullable=False, but migrations would not work for it
|
# Intented to be nullable=False, but migrations would not work for it
|
||||||
# set to nullable=True implicitly.
|
# set to nullable=True implicitly.
|
||||||
wants_comment_notification = Column(Boolean, default=True)
|
wants_comment_notification = Column(Boolean, default=True)
|
||||||
wants_notifications = Column(Boolean, default=True)
|
wants_notifications = Column(Boolean, default=True)
|
||||||
license_preference = Column(Unicode)
|
license_preference = Column(Unicode)
|
||||||
is_admin = Column(Boolean, default=False, nullable=False)
|
|
||||||
url = Column(Unicode)
|
url = Column(Unicode)
|
||||||
bio = Column(UnicodeText) # ??
|
bio = Column(UnicodeText) # ??
|
||||||
uploaded = Column(Integer, default=0)
|
uploaded = Column(Integer, default=0)
|
||||||
@ -85,8 +82,8 @@ class User(Base, UserMixin):
|
|||||||
return '<{0} #{1} {2} {3} "{4}">'.format(
|
return '<{0} #{1} {2} {3} "{4}">'.format(
|
||||||
self.__class__.__name__,
|
self.__class__.__name__,
|
||||||
self.id,
|
self.id,
|
||||||
'verified' if self.email_verified else 'non-verified',
|
'verified' if self.has_privilege(u'active') else 'non-verified',
|
||||||
'admin' if self.is_admin else 'user',
|
'admin' if self.has_privilege(u'admin') else 'user',
|
||||||
self.username)
|
self.username)
|
||||||
|
|
||||||
def delete(self, **kwargs):
|
def delete(self, **kwargs):
|
||||||
@ -108,6 +105,36 @@ class User(Base, UserMixin):
|
|||||||
super(User, self).delete(**kwargs)
|
super(User, self).delete(**kwargs)
|
||||||
_log.info('Deleted user "{0}" account'.format(self.username))
|
_log.info('Deleted user "{0}" account'.format(self.username))
|
||||||
|
|
||||||
|
def has_privilege(self,*priv_names):
|
||||||
|
"""
|
||||||
|
This method checks to make sure a user has all the correct privileges
|
||||||
|
to access a piece of content.
|
||||||
|
|
||||||
|
:param priv_names A variable number of unicode objects which rep-
|
||||||
|
-resent the different privileges which may give
|
||||||
|
the user access to this content. If you pass
|
||||||
|
multiple arguments, the user will be granted
|
||||||
|
access if they have ANY of the privileges
|
||||||
|
passed.
|
||||||
|
"""
|
||||||
|
if len(priv_names) == 1:
|
||||||
|
priv = Privilege.query.filter(
|
||||||
|
Privilege.privilege_name==priv_names[0]).one()
|
||||||
|
return (priv in self.all_privileges)
|
||||||
|
elif len(priv_names) > 1:
|
||||||
|
return self.has_privilege(priv_names[0]) or \
|
||||||
|
self.has_privilege(*priv_names[1:])
|
||||||
|
return False
|
||||||
|
|
||||||
|
def is_banned(self):
|
||||||
|
"""
|
||||||
|
Checks if this user is banned.
|
||||||
|
|
||||||
|
:returns True if self is banned
|
||||||
|
:returns False if self is not
|
||||||
|
"""
|
||||||
|
return UserBan.query.get(self.id) is not None
|
||||||
|
|
||||||
|
|
||||||
class Client(Base):
|
class Client(Base):
|
||||||
"""
|
"""
|
||||||
@ -675,16 +702,198 @@ class ProcessingNotification(Notification):
|
|||||||
'polymorphic_identity': 'processing_notification'
|
'polymorphic_identity': 'processing_notification'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
with_polymorphic(
|
with_polymorphic(
|
||||||
Notification,
|
Notification,
|
||||||
[ProcessingNotification, CommentNotification])
|
[ProcessingNotification, CommentNotification])
|
||||||
|
|
||||||
|
class ReportBase(Base):
|
||||||
|
"""
|
||||||
|
This is the basic report object which the other reports are based off of.
|
||||||
|
|
||||||
|
:keyword reporter_id Holds the id of the user who created
|
||||||
|
the report, as an Integer column.
|
||||||
|
:keyword report_content Hold the explanation left by the repor-
|
||||||
|
-ter to indicate why they filed the
|
||||||
|
report in the first place, as a
|
||||||
|
Unicode column.
|
||||||
|
:keyword reported_user_id Holds the id of the user who created
|
||||||
|
the content which was reported, as
|
||||||
|
an Integer column.
|
||||||
|
:keyword created Holds a datetime column of when the re-
|
||||||
|
-port was filed.
|
||||||
|
:keyword discriminator This column distinguishes between the
|
||||||
|
different types of reports.
|
||||||
|
:keyword resolver_id Holds the id of the moderator/admin who
|
||||||
|
resolved the report.
|
||||||
|
:keyword resolved Holds the DateTime object which descri-
|
||||||
|
-bes when this report was resolved
|
||||||
|
:keyword result Holds the UnicodeText column of the
|
||||||
|
resolver's reasons for resolving
|
||||||
|
the report this way. Some of this
|
||||||
|
is auto-generated
|
||||||
|
"""
|
||||||
|
__tablename__ = 'core__reports'
|
||||||
|
id = Column(Integer, primary_key=True)
|
||||||
|
reporter_id = Column(Integer, ForeignKey(User.id), nullable=False)
|
||||||
|
reporter = relationship(
|
||||||
|
User,
|
||||||
|
backref=backref("reports_filed_by",
|
||||||
|
lazy="dynamic",
|
||||||
|
cascade="all, delete-orphan"),
|
||||||
|
primaryjoin="User.id==ReportBase.reporter_id")
|
||||||
|
report_content = Column(UnicodeText)
|
||||||
|
reported_user_id = Column(Integer, ForeignKey(User.id), nullable=False)
|
||||||
|
reported_user = relationship(
|
||||||
|
User,
|
||||||
|
backref=backref("reports_filed_on",
|
||||||
|
lazy="dynamic",
|
||||||
|
cascade="all, delete-orphan"),
|
||||||
|
primaryjoin="User.id==ReportBase.reported_user_id")
|
||||||
|
created = Column(DateTime, nullable=False, default=datetime.datetime.now())
|
||||||
|
discriminator = Column('type', Unicode(50))
|
||||||
|
resolver_id = Column(Integer, ForeignKey(User.id))
|
||||||
|
resolver = relationship(
|
||||||
|
User,
|
||||||
|
backref=backref("reports_resolved_by",
|
||||||
|
lazy="dynamic",
|
||||||
|
cascade="all, delete-orphan"),
|
||||||
|
primaryjoin="User.id==ReportBase.resolver_id")
|
||||||
|
|
||||||
|
resolved = Column(DateTime)
|
||||||
|
result = Column(UnicodeText)
|
||||||
|
__mapper_args__ = {'polymorphic_on': discriminator}
|
||||||
|
|
||||||
|
def is_comment_report(self):
|
||||||
|
return self.discriminator=='comment_report'
|
||||||
|
|
||||||
|
def is_media_entry_report(self):
|
||||||
|
return self.discriminator=='media_report'
|
||||||
|
|
||||||
|
def is_archived_report(self):
|
||||||
|
return self.resolved is not None
|
||||||
|
|
||||||
|
def archive(self,resolver_id, resolved, result):
|
||||||
|
self.resolver_id = resolver_id
|
||||||
|
self.resolved = resolved
|
||||||
|
self.result = result
|
||||||
|
|
||||||
|
|
||||||
|
class CommentReport(ReportBase):
|
||||||
|
"""
|
||||||
|
Reports that have been filed on comments.
|
||||||
|
:keyword comment_id Holds the integer value of the reported
|
||||||
|
comment's ID
|
||||||
|
"""
|
||||||
|
__tablename__ = 'core__reports_on_comments'
|
||||||
|
__mapper_args__ = {'polymorphic_identity': 'comment_report'}
|
||||||
|
|
||||||
|
id = Column('id',Integer, ForeignKey('core__reports.id'),
|
||||||
|
primary_key=True)
|
||||||
|
comment_id = Column(Integer, ForeignKey(MediaComment.id), nullable=True)
|
||||||
|
comment = relationship(
|
||||||
|
MediaComment, backref=backref("reports_filed_on",
|
||||||
|
lazy="dynamic"))
|
||||||
|
|
||||||
|
|
||||||
|
class MediaReport(ReportBase):
|
||||||
|
"""
|
||||||
|
Reports that have been filed on media entries
|
||||||
|
:keyword media_entry_id Holds the integer value of the reported
|
||||||
|
media entry's ID
|
||||||
|
"""
|
||||||
|
__tablename__ = 'core__reports_on_media'
|
||||||
|
__mapper_args__ = {'polymorphic_identity': 'media_report'}
|
||||||
|
|
||||||
|
id = Column('id',Integer, ForeignKey('core__reports.id'),
|
||||||
|
primary_key=True)
|
||||||
|
media_entry_id = Column(Integer, ForeignKey(MediaEntry.id), nullable=True)
|
||||||
|
media_entry = relationship(
|
||||||
|
MediaEntry,
|
||||||
|
backref=backref("reports_filed_on",
|
||||||
|
lazy="dynamic"))
|
||||||
|
|
||||||
|
class UserBan(Base):
|
||||||
|
"""
|
||||||
|
Holds the information on a specific user's ban-state. As long as one of
|
||||||
|
these is attached to a user, they are banned from accessing mediagoblin.
|
||||||
|
When they try to log in, they are greeted with a page that tells them
|
||||||
|
the reason why they are banned and when (if ever) the ban will be
|
||||||
|
lifted
|
||||||
|
|
||||||
|
:keyword user_id Holds the id of the user this object is
|
||||||
|
attached to. This is a one-to-one
|
||||||
|
relationship.
|
||||||
|
: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.
|
||||||
|
:keyword reason Holds the reason why the user was banned.
|
||||||
|
"""
|
||||||
|
__tablename__ = 'core__user_bans'
|
||||||
|
|
||||||
|
user_id = Column(Integer, ForeignKey(User.id), nullable=False,
|
||||||
|
primary_key=True)
|
||||||
|
expiration_date = Column(Date)
|
||||||
|
reason = Column(UnicodeText, nullable=False)
|
||||||
|
|
||||||
|
|
||||||
|
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)
|
||||||
|
privilege_name = Column(Unicode, nullable=False, unique=True)
|
||||||
|
all_users = relationship(
|
||||||
|
User,
|
||||||
|
backref='all_privileges',
|
||||||
|
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 "<Privilege %s>" % (self.privilege_name)
|
||||||
|
|
||||||
|
|
||||||
|
class PrivilegeUserAssociation(Base):
|
||||||
|
'''
|
||||||
|
This table holds the many-to-many relationship between User and Privilege
|
||||||
|
'''
|
||||||
|
|
||||||
|
__tablename__ = 'core__privileges_users'
|
||||||
|
|
||||||
|
privilege_id = Column(
|
||||||
|
'core__privilege_id',
|
||||||
|
Integer,
|
||||||
|
ForeignKey(User.id),
|
||||||
|
primary_key=True)
|
||||||
|
user_id = Column(
|
||||||
|
'core__user_id',
|
||||||
|
Integer,
|
||||||
|
ForeignKey(Privilege.id),
|
||||||
|
primary_key=True)
|
||||||
|
|
||||||
MODELS = [
|
MODELS = [
|
||||||
User, Client, RequestToken, AccessToken, NonceTimestamp, MediaEntry, Tag,
|
User, MediaEntry, Tag, MediaTag, MediaComment, Collection, CollectionItem,
|
||||||
MediaTag, MediaComment, Collection, CollectionItem, MediaFile, FileKeynames,
|
MediaFile, FileKeynames, MediaAttachmentFile, ProcessingMetaData,
|
||||||
MediaAttachmentFile, ProcessingMetaData, Notification, CommentNotification,
|
Notification, CommentNotification, ProcessingNotification, Client,
|
||||||
ProcessingNotification, CommentSubscription]
|
CommentSubscription, ReportBase, CommentReport, MediaReport, UserBan,
|
||||||
|
Privilege, PrivilegeUserAssociation,
|
||||||
|
RequestToken, AccessToken, NonceTimestamp]
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Foundations are the default rows that are created immediately after the tables
|
Foundations are the default rows that are created immediately after the tables
|
||||||
@ -700,7 +909,13 @@ MODELS = [
|
|||||||
|
|
||||||
FOUNDATIONS = {User:user_foundations}
|
FOUNDATIONS = {User:user_foundations}
|
||||||
"""
|
"""
|
||||||
FOUNDATIONS = {}
|
privilege_foundations = [{'privilege_name':u'admin'},
|
||||||
|
{'privilege_name':u'moderator'},
|
||||||
|
{'privilege_name':u'uploader'},
|
||||||
|
{'privilege_name':u'reporter'},
|
||||||
|
{'privilege_name':u'commenter'},
|
||||||
|
{'privilege_name':u'active'}]
|
||||||
|
FOUNDATIONS = {Privilege:privilege_foundations}
|
||||||
|
|
||||||
######################################################
|
######################################################
|
||||||
# Special, migrations-tracking table
|
# Special, migrations-tracking table
|
||||||
|
@ -67,7 +67,6 @@ def check_collection_slug_used(creator_id, slug, ignore_c_id):
|
|||||||
does_exist = Session.query(Collection.id).filter(filt).first() is not None
|
does_exist = Session.query(Collection.id).filter(filt).first() is not None
|
||||||
return does_exist
|
return does_exist
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
from mediagoblin.db.open import setup_connection_and_db_from_config
|
from mediagoblin.db.open import setup_connection_and_db_from_config
|
||||||
|
|
||||||
|
@ -22,25 +22,44 @@ from oauthlib.oauth1 import ResourceEndpoint
|
|||||||
|
|
||||||
from mediagoblin import mg_globals as mgg
|
from mediagoblin import mg_globals as mgg
|
||||||
from mediagoblin import messages
|
from mediagoblin import messages
|
||||||
from mediagoblin.db.models import MediaEntry, User
|
from mediagoblin.db.models import MediaEntry, User, MediaComment
|
||||||
from mediagoblin.tools.response import json_response, redirect, render_404
|
from mediagoblin.tools.response import (redirect, render_404,
|
||||||
|
render_user_banned, json_response)
|
||||||
from mediagoblin.tools.translate import pass_to_ugettext as _
|
from mediagoblin.tools.translate import pass_to_ugettext as _
|
||||||
|
|
||||||
from mediagoblin.oauth.tools.request import decode_authorization_header
|
from mediagoblin.oauth.tools.request import decode_authorization_header
|
||||||
from mediagoblin.oauth.oauth import GMGRequestValidator
|
from mediagoblin.oauth.oauth import GMGRequestValidator
|
||||||
|
|
||||||
def require_active_login(controller):
|
|
||||||
|
def user_not_banned(controller):
|
||||||
"""
|
"""
|
||||||
Require an active login from the user.
|
Requires that the user has not been banned. Otherwise redirects to the page
|
||||||
|
explaining why they have been banned
|
||||||
"""
|
"""
|
||||||
@wraps(controller)
|
@wraps(controller)
|
||||||
|
def wrapper(request, *args, **kwargs):
|
||||||
|
if request.user:
|
||||||
|
if request.user.is_banned():
|
||||||
|
return render_user_banned(request)
|
||||||
|
return controller(request, *args, **kwargs)
|
||||||
|
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
|
def require_active_login(controller):
|
||||||
|
"""
|
||||||
|
Require an active login from the user. If the user is banned, redirects to
|
||||||
|
the "You are Banned" page.
|
||||||
|
"""
|
||||||
|
@wraps(controller)
|
||||||
|
@user_not_banned
|
||||||
def new_controller_func(request, *args, **kwargs):
|
def new_controller_func(request, *args, **kwargs):
|
||||||
if request.user and \
|
if request.user and \
|
||||||
request.user.status == u'needs_email_verification':
|
not request.user.has_privilege(u'active'):
|
||||||
return redirect(
|
return redirect(
|
||||||
request, 'mediagoblin.user_pages.user_home',
|
request, 'mediagoblin.user_pages.user_home',
|
||||||
user=request.user.username)
|
user=request.user.username)
|
||||||
elif not request.user or request.user.status != u'active':
|
elif not request.user or not request.user.has_privilege(u'active'):
|
||||||
next_url = urljoin(
|
next_url = urljoin(
|
||||||
request.urlgen('mediagoblin.auth.login',
|
request.urlgen('mediagoblin.auth.login',
|
||||||
qualified=True),
|
qualified=True),
|
||||||
@ -53,6 +72,34 @@ def require_active_login(controller):
|
|||||||
|
|
||||||
return new_controller_func
|
return new_controller_func
|
||||||
|
|
||||||
|
|
||||||
|
def user_has_privilege(privilege_name):
|
||||||
|
"""
|
||||||
|
Requires that a user have a particular privilege in order to access a page.
|
||||||
|
In order to require that a user have multiple privileges, use this
|
||||||
|
decorator twice on the same view. This decorator also makes sure that the
|
||||||
|
user is not banned, or else it redirects them to the "You are Banned" page.
|
||||||
|
|
||||||
|
:param privilege_name A unicode object that is that represents
|
||||||
|
the privilege object. This object is
|
||||||
|
the name of the privilege, as assigned
|
||||||
|
in the Privilege.privilege_name column
|
||||||
|
"""
|
||||||
|
|
||||||
|
def user_has_privilege_decorator(controller):
|
||||||
|
@wraps(controller)
|
||||||
|
@require_active_login
|
||||||
|
def wrapper(request, *args, **kwargs):
|
||||||
|
user_id = request.user.id
|
||||||
|
if not request.user.has_privilege(privilege_name):
|
||||||
|
raise Forbidden()
|
||||||
|
|
||||||
|
return controller(request, *args, **kwargs)
|
||||||
|
|
||||||
|
return wrapper
|
||||||
|
return user_has_privilege_decorator
|
||||||
|
|
||||||
|
|
||||||
def active_user_from_url(controller):
|
def active_user_from_url(controller):
|
||||||
"""Retrieve User() from <user> URL pattern and pass in as url_user=...
|
"""Retrieve User() from <user> URL pattern and pass in as url_user=...
|
||||||
|
|
||||||
@ -75,7 +122,7 @@ def user_may_delete_media(controller):
|
|||||||
@wraps(controller)
|
@wraps(controller)
|
||||||
def wrapper(request, *args, **kwargs):
|
def wrapper(request, *args, **kwargs):
|
||||||
uploader_id = kwargs['media'].uploader
|
uploader_id = kwargs['media'].uploader
|
||||||
if not (request.user.is_admin or
|
if not (request.user.has_privilege(u'admin') or
|
||||||
request.user.id == uploader_id):
|
request.user.id == uploader_id):
|
||||||
raise Forbidden()
|
raise Forbidden()
|
||||||
|
|
||||||
@ -92,7 +139,7 @@ def user_may_alter_collection(controller):
|
|||||||
def wrapper(request, *args, **kwargs):
|
def wrapper(request, *args, **kwargs):
|
||||||
creator_id = request.db.User.query.filter_by(
|
creator_id = request.db.User.query.filter_by(
|
||||||
username=request.matchdict['user']).first().id
|
username=request.matchdict['user']).first().id
|
||||||
if not (request.user.is_admin or
|
if not (request.user.has_privilege(u'admin') or
|
||||||
request.user.id == creator_id):
|
request.user.id == creator_id):
|
||||||
raise Forbidden()
|
raise Forbidden()
|
||||||
|
|
||||||
@ -256,6 +303,48 @@ def allow_registration(controller):
|
|||||||
|
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
|
def allow_reporting(controller):
|
||||||
|
""" Decorator for if reporting is enabled"""
|
||||||
|
@wraps(controller)
|
||||||
|
def wrapper(request, *args, **kwargs):
|
||||||
|
if not mgg.app_config["allow_reporting"]:
|
||||||
|
messages.add_message(
|
||||||
|
request,
|
||||||
|
messages.WARNING,
|
||||||
|
_('Sorry, reporting is disabled on this instance.'))
|
||||||
|
return redirect(request, 'index')
|
||||||
|
|
||||||
|
return controller(request, *args, **kwargs)
|
||||||
|
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
def get_optional_media_comment_by_id(controller):
|
||||||
|
"""
|
||||||
|
Pass in a MediaComment based off of a url component. Because of this decor-
|
||||||
|
-ator's use in filing Media or Comment Reports, it has two valid outcomes.
|
||||||
|
|
||||||
|
:returns The view function being wrapped with kwarg `comment` set to
|
||||||
|
the MediaComment who's id is in the URL. If there is a
|
||||||
|
comment id in the URL and if it is valid.
|
||||||
|
:returns The view function being wrapped with kwarg `comment` set to
|
||||||
|
None. If there is no comment id in the URL.
|
||||||
|
:returns A 404 Error page, if there is a comment if in the URL and it
|
||||||
|
is invalid.
|
||||||
|
"""
|
||||||
|
@wraps(controller)
|
||||||
|
def wrapper(request, *args, **kwargs):
|
||||||
|
if 'comment' in request.matchdict:
|
||||||
|
comment = MediaComment.query.filter_by(
|
||||||
|
id=request.matchdict['comment']).first()
|
||||||
|
|
||||||
|
if comment is None:
|
||||||
|
return render_404(request)
|
||||||
|
|
||||||
|
return controller(request, comment=comment, *args, **kwargs)
|
||||||
|
else:
|
||||||
|
return controller(request, comment=None, *args, **kwargs)
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
def auth_enabled(controller):
|
def auth_enabled(controller):
|
||||||
"""Decorator for if an auth plugin is enabled"""
|
"""Decorator for if an auth plugin is enabled"""
|
||||||
@ -272,6 +361,31 @@ def auth_enabled(controller):
|
|||||||
|
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
|
def require_admin_or_moderator_login(controller):
|
||||||
|
"""
|
||||||
|
Require a login from an administrator or a moderator.
|
||||||
|
"""
|
||||||
|
@wraps(controller)
|
||||||
|
def new_controller_func(request, *args, **kwargs):
|
||||||
|
if request.user and \
|
||||||
|
not request.user.has_privilege(u'admin',u'moderator'):
|
||||||
|
|
||||||
|
raise Forbidden()
|
||||||
|
elif not request.user:
|
||||||
|
next_url = urljoin(
|
||||||
|
request.urlgen('mediagoblin.auth.login',
|
||||||
|
qualified=True),
|
||||||
|
request.url)
|
||||||
|
|
||||||
|
return redirect(request, 'mediagoblin.auth.login',
|
||||||
|
next=next_url)
|
||||||
|
|
||||||
|
return controller(request, *args, **kwargs)
|
||||||
|
|
||||||
|
return new_controller_func
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def oauth_required(controller):
|
def oauth_required(controller):
|
||||||
""" Used to wrap API endpoints where oauth is required """
|
""" Used to wrap API endpoints where oauth is required """
|
||||||
@wraps(controller)
|
@wraps(controller)
|
||||||
|
@ -19,6 +19,6 @@ def may_edit_media(request, media):
|
|||||||
"""Check, if the request's user may edit the media details"""
|
"""Check, if the request's user may edit the media details"""
|
||||||
if media.uploader == request.user.id:
|
if media.uploader == request.user.id:
|
||||||
return True
|
return True
|
||||||
if request.user.is_admin:
|
if request.user.has_privilege(u'admin'):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
@ -83,7 +83,7 @@ def edit_media(request, media):
|
|||||||
|
|
||||||
return redirect_obj(request, media)
|
return redirect_obj(request, media)
|
||||||
|
|
||||||
if request.user.is_admin \
|
if request.user.has_privilege(u'admin') \
|
||||||
and media.uploader != request.user.id \
|
and media.uploader != request.user.id \
|
||||||
and request.method != 'POST':
|
and request.method != 'POST':
|
||||||
messages.add_message(
|
messages.add_message(
|
||||||
@ -184,7 +184,7 @@ def legacy_edit_profile(request):
|
|||||||
def edit_profile(request, url_user=None):
|
def edit_profile(request, url_user=None):
|
||||||
# admins may edit any user profile
|
# admins may edit any user profile
|
||||||
if request.user.username != url_user.username:
|
if request.user.username != url_user.username:
|
||||||
if not request.user.is_admin:
|
if not request.user.has_privilege(u'admin'):
|
||||||
raise Forbidden(_("You can only edit your own profile."))
|
raise Forbidden(_("You can only edit your own profile."))
|
||||||
|
|
||||||
# No need to warn again if admin just submitted an edited profile
|
# No need to warn again if admin just submitted an edited profile
|
||||||
@ -324,7 +324,7 @@ def edit_collection(request, collection):
|
|||||||
|
|
||||||
return redirect_obj(request, collection)
|
return redirect_obj(request, collection)
|
||||||
|
|
||||||
if request.user.is_admin \
|
if request.user.has_privilege(u'admin') \
|
||||||
and collection.creator != request.user.id \
|
and collection.creator != request.user.id \
|
||||||
and request.method != 'POST':
|
and request.method != 'POST':
|
||||||
messages.add_message(
|
messages.add_message(
|
||||||
|
@ -53,8 +53,17 @@ def adduser(args):
|
|||||||
entry.username = unicode(args.username.lower())
|
entry.username = unicode(args.username.lower())
|
||||||
entry.email = unicode(args.email)
|
entry.email = unicode(args.email)
|
||||||
entry.pw_hash = auth.gen_password_hash(args.password)
|
entry.pw_hash = auth.gen_password_hash(args.password)
|
||||||
entry.status = u'active'
|
default_privileges = [
|
||||||
entry.email_verified = True
|
db.Privilege.query.filter(
|
||||||
|
db.Privilege.privilege_name==u'commenter').one(),
|
||||||
|
db.Privilege.query.filter(
|
||||||
|
db.Privilege.privilege_name==u'uploader').one(),
|
||||||
|
db.Privilege.query.filter(
|
||||||
|
db.Privilege.privilege_name==u'reporter').one(),
|
||||||
|
db.Privilege.query.filter(
|
||||||
|
db.Privilege.privilege_name==u'active').one()
|
||||||
|
]
|
||||||
|
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)"
|
||||||
@ -74,7 +83,10 @@ def makeadmin(args):
|
|||||||
user = db.User.query.filter_by(
|
user = db.User.query.filter_by(
|
||||||
username=unicode(args.username.lower())).one()
|
username=unicode(args.username.lower())).one()
|
||||||
if user:
|
if user:
|
||||||
user.is_admin = True
|
user.all_privileges.append(
|
||||||
|
db.Privilege.query.filter(
|
||||||
|
db.Privilege.privilege_name==u'admin').one()
|
||||||
|
)
|
||||||
user.save()
|
user.save()
|
||||||
print 'The user is now Admin'
|
print 'The user is now Admin'
|
||||||
else:
|
else:
|
||||||
|
148
mediagoblin/moderation/forms.py
Normal file
148
mediagoblin/moderation/forms.py
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
# 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'),_(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()
|
||||||
|
|
||||||
|
|
||||||
|
# ============ Forms for mediagoblin.moderation.user page ================== #
|
||||||
|
|
||||||
|
class PrivilegeAddRemoveForm(wtforms.Form):
|
||||||
|
"""
|
||||||
|
This form is used by an admin to give/take away a privilege directly from
|
||||||
|
their user page.
|
||||||
|
"""
|
||||||
|
privilege_name = wtforms.HiddenField('',[wtforms.validators.required()])
|
||||||
|
|
||||||
|
class BanForm(wtforms.Form):
|
||||||
|
"""
|
||||||
|
This form is used by an admin to ban a user directly from their user page.
|
||||||
|
"""
|
||||||
|
user_banned_until = wtforms.DateField(
|
||||||
|
_(u'User will be banned until:'),
|
||||||
|
format='%Y-%m-%d',
|
||||||
|
validators=[wtforms.validators.optional()])
|
||||||
|
why_user_was_banned = wtforms.TextAreaField(
|
||||||
|
_(u'Why are you banning this User?'),
|
||||||
|
validators=[wtforms.validators.optional()])
|
||||||
|
|
||||||
|
# =========== Forms for mediagoblin.moderation.report page ================= #
|
||||||
|
|
||||||
|
class ReportResolutionForm(wtforms.Form):
|
||||||
|
"""
|
||||||
|
This form carries all the information necessary to take punitive actions
|
||||||
|
against a user who created content that has been reported.
|
||||||
|
|
||||||
|
:param action_to_resolve A list of Unicode objects representing
|
||||||
|
a choice from the ACTION_CHOICES const-
|
||||||
|
-ant. Every choice passed affects what
|
||||||
|
punitive actions will be taken against
|
||||||
|
the user.
|
||||||
|
|
||||||
|
:param targeted_user A HiddenField object that holds the id
|
||||||
|
of the user that was reported.
|
||||||
|
|
||||||
|
:param take_away_privileges A list of Unicode objects which repres-
|
||||||
|
-ent the privileges that are being tak-
|
||||||
|
-en away. This field is optional and
|
||||||
|
only relevant if u'takeaway' is in the
|
||||||
|
`action_to_resolve` list.
|
||||||
|
|
||||||
|
:param user_banned_until A DateField object that holds the date
|
||||||
|
that the user will be unbanned. This
|
||||||
|
field is optional and only relevant if
|
||||||
|
u'userban' is in the action_to_resolve
|
||||||
|
list. If the user is being banned and
|
||||||
|
this field is blank, the user is banned
|
||||||
|
indefinitely.
|
||||||
|
|
||||||
|
:param why_user_was_banned A TextArea object that holds the
|
||||||
|
reason that a user was banned, to disp-
|
||||||
|
-lay to them when they try to log in.
|
||||||
|
This field is optional and only relevant
|
||||||
|
if u'userban' is in the
|
||||||
|
`action_to_resolve` list.
|
||||||
|
|
||||||
|
:param message_to_user A TextArea object that holds a message
|
||||||
|
which will be emailed to the user. This
|
||||||
|
is only relevant if the u'sendmessage'
|
||||||
|
option is in the `action_to_resolve`
|
||||||
|
list.
|
||||||
|
|
||||||
|
:param resolution_content A TextArea object that is required for
|
||||||
|
every report filed. It represents the
|
||||||
|
reasons that the moderator/admin resol-
|
||||||
|
-ved the report in such a way.
|
||||||
|
"""
|
||||||
|
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(
|
||||||
|
_(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()
|
||||||
|
|
||||||
|
# ======== Forms for mediagoblin.moderation.report_panel page ============== #
|
||||||
|
|
||||||
|
class ReportPanelSortingForm(wtforms.Form):
|
||||||
|
"""
|
||||||
|
This form is used for sorting and filtering through different reports in
|
||||||
|
the mediagoblin.moderation.reports_panel view.
|
||||||
|
|
||||||
|
"""
|
||||||
|
active_p = wtforms.IntegerField(
|
||||||
|
validators=[wtforms.validators.optional()])
|
||||||
|
closed_p = wtforms.IntegerField(
|
||||||
|
validators=[wtforms.validators.optional()])
|
||||||
|
reported_user = wtforms.IntegerField(
|
||||||
|
validators=[wtforms.validators.optional()])
|
||||||
|
reporter = wtforms.IntegerField(
|
||||||
|
validators=[wtforms.validators.optional()])
|
||||||
|
|
||||||
|
class UserPanelSortingForm(wtforms.Form):
|
||||||
|
"""
|
||||||
|
This form is used for sorting different reports.
|
||||||
|
"""
|
||||||
|
p = wtforms.IntegerField(
|
||||||
|
validators=[wtforms.validators.optional()])
|
38
mediagoblin/moderation/routing.py
Normal file
38
mediagoblin/moderation/routing.py
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
# 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.ban_or_unban',
|
||||||
|
'/users/<string:user>/ban/',
|
||||||
|
'mediagoblin.moderation.views:ban_or_unban'),
|
||||||
|
('mediagoblin.moderation.reports_detail',
|
||||||
|
'/reports/<int:report_id>/',
|
||||||
|
'mediagoblin.moderation.views:moderation_reports_detail')]
|
217
mediagoblin/moderation/tools.py
Normal file
217
mediagoblin/moderation/tools.py
Normal file
@ -0,0 +1,217 @@
|
|||||||
|
# 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, 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 _
|
||||||
|
|
||||||
|
def take_punitive_actions(request, form, report, user):
|
||||||
|
message_body =''
|
||||||
|
|
||||||
|
# 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:
|
||||||
|
take_away_privileges(user.username, privilege_name)
|
||||||
|
form.resolution_content.data += \
|
||||||
|
u"\n{mod} took away {user}\'s {privilege} privileges.".format(
|
||||||
|
mod=request.user.username,
|
||||||
|
user=user.username,
|
||||||
|
privilege=privilege_name)
|
||||||
|
|
||||||
|
# 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:
|
||||||
|
user_ban = ban_user(form.targeted_user.data,
|
||||||
|
expiration_date=form.user_banned_until.data,
|
||||||
|
reason=form.why_user_was_banned.data)
|
||||||
|
Session.add(user_ban)
|
||||||
|
form.resolution_content.data += \
|
||||||
|
u"\n{mod} banned user {user} {expiration_date}.".format(
|
||||||
|
mod=request.user.username,
|
||||||
|
user=user.username,
|
||||||
|
expiration_date = (
|
||||||
|
"until {date}".format(date=form.user_banned_until.data)
|
||||||
|
if form.user_banned_until.data
|
||||||
|
else "indefinitely"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# 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"\n{mod} sent a warning email to the {user}.".format(
|
||||||
|
mod=request.user.username,
|
||||||
|
user=user.username)
|
||||||
|
|
||||||
|
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"\n{mod} deleted the comment.".format(
|
||||||
|
mod=request.user.username)
|
||||||
|
elif u'delete' in form.action_to_resolve.data and \
|
||||||
|
report.is_media_entry_report():
|
||||||
|
deleted_media = report.media_entry
|
||||||
|
deleted_media.delete()
|
||||||
|
form.resolution_content.data += \
|
||||||
|
u"\n{mod} deleted the media entry.".format(
|
||||||
|
mod=request.user.username)
|
||||||
|
report.archive(
|
||||||
|
resolver_id=request.user.id,
|
||||||
|
resolved=datetime.now(),
|
||||||
|
result=form.resolution_content.data)
|
||||||
|
|
||||||
|
Session.add(report)
|
||||||
|
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)
|
||||||
|
|
||||||
|
|
||||||
|
def take_away_privileges(user,*privileges):
|
||||||
|
"""
|
||||||
|
Take away all of the privileges passed as arguments.
|
||||||
|
|
||||||
|
:param user A Unicode object representing the target user's
|
||||||
|
User.username value.
|
||||||
|
|
||||||
|
:param privileges A variable number of Unicode objects describing
|
||||||
|
the privileges being taken away.
|
||||||
|
|
||||||
|
|
||||||
|
:returns True If ALL of the privileges were taken away
|
||||||
|
successfully.
|
||||||
|
|
||||||
|
:returns False If ANY of the privileges were not taken away
|
||||||
|
successfully. This means the user did not have
|
||||||
|
(one of) the privilege(s) to begin with.
|
||||||
|
"""
|
||||||
|
if len(privileges) == 1:
|
||||||
|
privilege = Privilege.query.filter(
|
||||||
|
Privilege.privilege_name==privileges[0]).first()
|
||||||
|
user = User.query.filter(
|
||||||
|
User.username==user).first()
|
||||||
|
if privilege in user.all_privileges:
|
||||||
|
user.all_privileges.remove(privilege)
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
elif len(privileges) > 1:
|
||||||
|
return (take_away_privileges(user, privileges[0]) and \
|
||||||
|
take_away_privileges(user, *privileges[1:]))
|
||||||
|
|
||||||
|
def give_privileges(user,*privileges):
|
||||||
|
"""
|
||||||
|
Take away all of the privileges passed as arguments.
|
||||||
|
|
||||||
|
:param user A Unicode object representing the target user's
|
||||||
|
User.username value.
|
||||||
|
|
||||||
|
:param privileges A variable number of Unicode objects describing
|
||||||
|
the privileges being granted.
|
||||||
|
|
||||||
|
|
||||||
|
:returns True If ALL of the privileges were granted successf-
|
||||||
|
-ully.
|
||||||
|
|
||||||
|
:returns False If ANY of the privileges were not granted succ-
|
||||||
|
essfully. This means the user already had (one
|
||||||
|
of) the privilege(s) to begin with.
|
||||||
|
"""
|
||||||
|
if len(privileges) == 1:
|
||||||
|
privilege = Privilege.query.filter(
|
||||||
|
Privilege.privilege_name==privileges[0]).first()
|
||||||
|
user = User.query.filter(
|
||||||
|
User.username==user).first()
|
||||||
|
if privilege not in user.all_privileges:
|
||||||
|
user.all_privileges.append(privilege)
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
elif len(privileges) > 1:
|
||||||
|
return (give_privileges(user, privileges[0]) and \
|
||||||
|
give_privileges(user, *privileges[1:]))
|
||||||
|
|
||||||
|
def ban_user(user_id, expiration_date=None, reason=None):
|
||||||
|
"""
|
||||||
|
This function is used to ban a user. If the user is already banned, the
|
||||||
|
function returns False. If the user is not already banned, this function
|
||||||
|
bans the user using the arguments to build a new UserBan object.
|
||||||
|
|
||||||
|
:returns False if the user is already banned and the ban is not updated
|
||||||
|
:returns UserBan object if there is a new ban that was created.
|
||||||
|
"""
|
||||||
|
user_ban =UserBan.query.filter(
|
||||||
|
UserBan.user_id==user_id)
|
||||||
|
if user_ban.count():
|
||||||
|
return False
|
||||||
|
new_user_ban = UserBan(
|
||||||
|
user_id=user_id,
|
||||||
|
expiration_date=expiration_date,
|
||||||
|
reason=reason)
|
||||||
|
return new_user_ban
|
||||||
|
|
||||||
|
def unban_user(user_id):
|
||||||
|
"""
|
||||||
|
This function is used to unban a user. If the user is not currently banned,
|
||||||
|
nothing happens.
|
||||||
|
|
||||||
|
:returns True if the operation was completed successfully and the user
|
||||||
|
has been unbanned
|
||||||
|
:returns False if the user was never banned.
|
||||||
|
"""
|
||||||
|
user_ban = UserBan.query.filter(
|
||||||
|
UserBan.user_id==user_id)
|
||||||
|
if user_ban.count() == 0:
|
||||||
|
return False
|
||||||
|
user_ban.first().delete()
|
||||||
|
return True
|
||||||
|
|
||||||
|
def parse_report_panel_settings(form):
|
||||||
|
"""
|
||||||
|
This function parses the url arguments to which are used to filter reports
|
||||||
|
in the reports panel view. More filters can be added to make a usuable
|
||||||
|
search function.
|
||||||
|
|
||||||
|
:returns A dictionary of sqlalchemy-usable filters.
|
||||||
|
"""
|
||||||
|
filters = {}
|
||||||
|
|
||||||
|
if form.validate():
|
||||||
|
filters['reported_user_id'] = form.reported_user.data
|
||||||
|
filters['reporter_id'] = form.reporter.data
|
||||||
|
|
||||||
|
filters = dict((k, v)
|
||||||
|
for k, v in filters.iteritems() if v)
|
||||||
|
|
||||||
|
return filters
|
219
mediagoblin/moderation/views.py
Normal file
219
mediagoblin/moderation/views.py
Normal file
@ -0,0 +1,219 @@
|
|||||||
|
# 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.db.models import (MediaEntry, User,ReportBase, Privilege,
|
||||||
|
UserBan)
|
||||||
|
from mediagoblin.decorators import (require_admin_or_moderator_login,
|
||||||
|
active_user_from_url, user_has_privilege,
|
||||||
|
allow_reporting)
|
||||||
|
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, \
|
||||||
|
take_away_privileges, give_privileges, ban_user, unban_user, \
|
||||||
|
parse_report_panel_settings)
|
||||||
|
from math import ceil
|
||||||
|
|
||||||
|
@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
|
||||||
|
'''
|
||||||
|
current_page = 1
|
||||||
|
if len(request.args) > 0:
|
||||||
|
form = moderation_forms.UserPanelSortingForm(request.args)
|
||||||
|
if form.validate():
|
||||||
|
current_page = form.p.data or 1
|
||||||
|
|
||||||
|
all_user_list = User.query
|
||||||
|
user_list = all_user_list.order_by(
|
||||||
|
User.created.desc()).offset(
|
||||||
|
(current_page-1)*10).limit(10)
|
||||||
|
last_page = int(ceil(all_user_list.count()/10.))
|
||||||
|
|
||||||
|
return render_to_response(
|
||||||
|
request,
|
||||||
|
'mediagoblin/moderation/user_panel.html',
|
||||||
|
{'user_list': user_list,
|
||||||
|
'current_page':current_page,
|
||||||
|
'last_page':last_page})
|
||||||
|
|
||||||
|
@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
|
||||||
|
user_banned = UserBan.query.get(user.id)
|
||||||
|
ban_form = moderation_forms.BanForm()
|
||||||
|
|
||||||
|
return render_to_response(
|
||||||
|
request,
|
||||||
|
'mediagoblin/moderation/user.html',
|
||||||
|
{'user':user,
|
||||||
|
'privileges': privileges,
|
||||||
|
'reports':active_reports,
|
||||||
|
'user_banned':user_banned,
|
||||||
|
'ban_form':ban_form})
|
||||||
|
|
||||||
|
@require_admin_or_moderator_login
|
||||||
|
@allow_reporting
|
||||||
|
def moderation_reports_panel(request):
|
||||||
|
'''
|
||||||
|
Show the global panel for monitoring reports filed against comments or
|
||||||
|
media entries for this instance.
|
||||||
|
'''
|
||||||
|
filters = []
|
||||||
|
active_settings, closed_settings = {'current_page':1}, {'current_page':1}
|
||||||
|
|
||||||
|
if len(request.args) > 0:
|
||||||
|
form = moderation_forms.ReportPanelSortingForm(request.args)
|
||||||
|
if form.validate():
|
||||||
|
filters = parse_report_panel_settings(form)
|
||||||
|
active_settings['current_page'] = form.active_p.data or 1
|
||||||
|
closed_settings['current_page'] = form.closed_p.data or 1
|
||||||
|
filters = [
|
||||||
|
getattr(ReportBase,key)==val
|
||||||
|
for key,val in filters.viewitems()]
|
||||||
|
|
||||||
|
all_active = ReportBase.query.filter(
|
||||||
|
ReportBase.resolved==None).filter(
|
||||||
|
*filters)
|
||||||
|
all_closed = ReportBase.query.filter(
|
||||||
|
ReportBase.resolved!=None).filter(
|
||||||
|
*filters)
|
||||||
|
|
||||||
|
# report_list and closed_report_list are the two lists of up to 10
|
||||||
|
# items which are actually passed to the user in this request
|
||||||
|
report_list = all_active.order_by(
|
||||||
|
ReportBase.created.desc()).offset(
|
||||||
|
(active_settings['current_page']-1)*10).limit(10)
|
||||||
|
closed_report_list = all_closed.order_by(
|
||||||
|
ReportBase.created.desc()).offset(
|
||||||
|
(closed_settings['current_page']-1)*10).limit(10)
|
||||||
|
|
||||||
|
active_settings['last_page'] = int(ceil(all_active.count()/10.))
|
||||||
|
closed_settings['last_page'] = int(ceil(all_closed.count()/10.))
|
||||||
|
# Render to response
|
||||||
|
return render_to_response(
|
||||||
|
request,
|
||||||
|
'mediagoblin/moderation/report_panel.html',
|
||||||
|
{'report_list':report_list,
|
||||||
|
'closed_report_list':closed_report_list,
|
||||||
|
'active_settings':active_settings,
|
||||||
|
'closed_settings':closed_settings})
|
||||||
|
|
||||||
|
@require_admin_or_moderator_login
|
||||||
|
@allow_reporting
|
||||||
|
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'])
|
||||||
|
|
||||||
|
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() and not (
|
||||||
|
not request.user.has_privilege(u'admin') and
|
||||||
|
report.reported_user.has_privilege(u'admin')):
|
||||||
|
|
||||||
|
user = User.query.get(form.targeted_user.data)
|
||||||
|
return take_punitive_actions(request, form, report, user)
|
||||||
|
|
||||||
|
|
||||||
|
form.targeted_user.data = report.reported_user_id
|
||||||
|
|
||||||
|
return render_to_response(
|
||||||
|
request,
|
||||||
|
'mediagoblin/moderation/report.html',
|
||||||
|
{'report':report,
|
||||||
|
'form':form})
|
||||||
|
|
||||||
|
@user_has_privilege(u'admin')
|
||||||
|
@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.
|
||||||
|
Can only be used by an admin.
|
||||||
|
'''
|
||||||
|
form = moderation_forms.PrivilegeAddRemoveForm(request.form)
|
||||||
|
if request.method == "POST" and form.validate():
|
||||||
|
privilege = Privilege.query.filter(
|
||||||
|
Privilege.privilege_name==form.privilege_name.data).one()
|
||||||
|
if not take_away_privileges(
|
||||||
|
url_user.username, form.privilege_name.data):
|
||||||
|
|
||||||
|
give_privileges(url_user.username, form.privilege_name.data)
|
||||||
|
url_user.save()
|
||||||
|
|
||||||
|
return redirect(
|
||||||
|
request,
|
||||||
|
'mediagoblin.moderation.users_detail',
|
||||||
|
user=url_user.username)
|
||||||
|
|
||||||
|
@user_has_privilege(u'admin')
|
||||||
|
@active_user_from_url
|
||||||
|
def ban_or_unban(request, url_user):
|
||||||
|
"""
|
||||||
|
A page to ban or unban a user. Only can be used by an admin.
|
||||||
|
"""
|
||||||
|
form = moderation_forms.BanForm(request.form)
|
||||||
|
if request.method == "POST" and form.validate():
|
||||||
|
already_banned = unban_user(url_user.id)
|
||||||
|
same_as_requesting_user = (request.user.id == url_user.id)
|
||||||
|
if not already_banned and not same_as_requesting_user:
|
||||||
|
user_ban = ban_user(url_user.id,
|
||||||
|
expiration_date = form.user_banned_until.data,
|
||||||
|
reason = form.why_user_was_banned.data)
|
||||||
|
user_ban.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
|
||||||
|
|
||||||
|
|
||||||
@ -27,8 +27,10 @@ _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')
|
||||||
|
add_route('terms_of_service','/terms_of_service',
|
||||||
|
'mediagoblin.views:terms_of_service')
|
||||||
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
|
||||||
|
@ -156,6 +156,10 @@ a.logo {
|
|||||||
margin: 6px 8px 6px 0;
|
margin: 6px 8px 6px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.fine_print {
|
||||||
|
font-size: 0.8em;
|
||||||
|
}
|
||||||
|
|
||||||
.mediagoblin_content {
|
.mediagoblin_content {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding-bottom: 74px;
|
padding-bottom: 74px;
|
||||||
@ -220,6 +224,7 @@ footer {
|
|||||||
color: #283F35;
|
color: #283F35;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.button_form {
|
.button_form {
|
||||||
min-width: 99px;
|
min-width: 99px;
|
||||||
margin: 10px 0px 10px 15px;
|
margin: 10px 0px 10px 15px;
|
||||||
@ -351,40 +356,40 @@ textarea#description, textarea#bio {
|
|||||||
|
|
||||||
/* comments */
|
/* comments */
|
||||||
|
|
||||||
.comment_wrapper {
|
.comment_wrapper, .report_wrapper {
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.comment_wrapper p {
|
.comment_wrapper p, .report_wrapper p {
|
||||||
margin-bottom: 2px;
|
margin-bottom: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.comment_author {
|
.comment_author, .report_author {
|
||||||
padding-top: 4px;
|
padding-top: 4px;
|
||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
}
|
}
|
||||||
|
|
||||||
a.comment_authorlink {
|
a.comment_authorlink, a.report_authorlink {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
padding-right: 5px;
|
padding-right: 5px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
padding-left: 2px;
|
padding-left: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
a.comment_authorlink:hover {
|
a.comment_authorlink:hover, a.report_authorlink:hover {
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
|
||||||
a.comment_whenlink {
|
a.comment_whenlink, a.report_whenlink {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
a.comment_whenlink:hover {
|
a.comment_whenlink:hover, a.report_whenlink:hover {
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
|
||||||
.comment_content {
|
.comment_content, .report_content {
|
||||||
margin-left: 8px;
|
margin-left: 8px;
|
||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
}
|
}
|
||||||
@ -408,6 +413,13 @@ textarea#comment_content {
|
|||||||
padding-right: 6px;
|
padding-right: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
a.report_authorlink, a.report_whenlink {
|
||||||
|
color: #D486B1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul#action_to_resolve {list-style:none; margin-left:10px;}
|
||||||
|
|
||||||
/* media galleries */
|
/* media galleries */
|
||||||
|
|
||||||
.media_thumbnail {
|
.media_thumbnail {
|
||||||
@ -608,6 +620,38 @@ table.media_panel th {
|
|||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* moderator panels */
|
||||||
|
|
||||||
|
table.admin_panel {
|
||||||
|
width: 100%
|
||||||
|
}
|
||||||
|
|
||||||
|
table.admin_side_panel {
|
||||||
|
width: 60%
|
||||||
|
}
|
||||||
|
|
||||||
|
table.admin_panel th, table.admin_side_panel th {
|
||||||
|
font-weight: bold;
|
||||||
|
padding-bottom: 4px;
|
||||||
|
text-align: left;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
table td.user_with_privilege {
|
||||||
|
font-weight: bold;
|
||||||
|
color: #86D4B1;
|
||||||
|
}
|
||||||
|
|
||||||
|
table td.user_without_privilege {
|
||||||
|
font-weight: bold;
|
||||||
|
color: #D486B1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.return_to_panel {
|
||||||
|
text-align:right;
|
||||||
|
float: right;
|
||||||
|
font-size:1.2em
|
||||||
|
}
|
||||||
|
|
||||||
/* Delete panel */
|
/* Delete panel */
|
||||||
|
|
||||||
@ -616,6 +660,27 @@ table.media_panel th {
|
|||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* code of conduct */
|
||||||
|
|
||||||
|
#code_of_conduct_list {
|
||||||
|
margin-left:25px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
#code_of_conduct_list li {
|
||||||
|
margin:5px 0 15px 25px;
|
||||||
|
}
|
||||||
|
#code_of_conduct_list strong{
|
||||||
|
color:#fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nested_sublist {
|
||||||
|
margin: 5px 0 10px 25px;
|
||||||
|
font-size:80%;
|
||||||
|
}
|
||||||
|
.nested_sublist li {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
/* ASCII art and code */
|
/* ASCII art and code */
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
|
BIN
mediagoblin/static/images/icon_clipboard.png
Normal file
BIN
mediagoblin/static/images/icon_clipboard.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 682 B |
BIN
mediagoblin/static/images/icon_clipboard_alert.png
Normal file
BIN
mediagoblin/static/images/icon_clipboard_alert.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 647 B |
67
mediagoblin/static/js/setup_report_forms.js
Normal file
67
mediagoblin/static/js/setup_report_forms.js
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
/**
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function init_report_resolution_form() {
|
||||||
|
hidden_input_names = {
|
||||||
|
'takeaway':['take_away_privileges'],
|
||||||
|
'userban':['user_banned_until','why_user_was_banned'],
|
||||||
|
'sendmessage':['message_to_user']
|
||||||
|
}
|
||||||
|
init_user_banned_form();
|
||||||
|
$('form#resolution_form').hide()
|
||||||
|
$('#open_resolution_form').click(function() {
|
||||||
|
$('form#resolution_form').toggle();
|
||||||
|
$.each(hidden_input_names, function(key, list){
|
||||||
|
$.each(list, function(index, name){
|
||||||
|
$('label[for='+name+']').hide();
|
||||||
|
$('#'+name).hide();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
$('#action_to_resolve').change(function() {
|
||||||
|
$('ul#action_to_resolve li input:checked').each(function() {
|
||||||
|
$.each(hidden_input_names[$(this).val()], function(index, name){
|
||||||
|
$('label[for='+name+']').show();
|
||||||
|
$('#'+name).show();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
$('ul#action_to_resolve li input:not(:checked)').each(function() {
|
||||||
|
$.each(hidden_input_names[$(this).val()], function(index, name){
|
||||||
|
$('label[for='+name+']').hide();
|
||||||
|
$('#'+name).hide();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
$("#submit_this_report").click(function(){
|
||||||
|
submit_user_banned_form()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function submit_user_banned_form() {
|
||||||
|
if ($("#user_banned_until").val() == 'YYYY-MM-DD'){
|
||||||
|
$("#user_banned_until").val("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function init_user_banned_form() {
|
||||||
|
$('#user_banned_until').val("YYYY-MM-DD")
|
||||||
|
$("#user_banned_until").focus(function() {
|
||||||
|
$(this).val("");
|
||||||
|
$(this).unbind('focus');
|
||||||
|
});
|
||||||
|
}
|
@ -27,7 +27,7 @@ _log = logging.getLogger(__name__)
|
|||||||
from mediagoblin.tools.text import convert_to_tag_list_of_dicts
|
from mediagoblin.tools.text import convert_to_tag_list_of_dicts
|
||||||
from mediagoblin.tools.translate import pass_to_ugettext as _
|
from mediagoblin.tools.translate import pass_to_ugettext as _
|
||||||
from mediagoblin.tools.response import render_to_response, redirect
|
from mediagoblin.tools.response import render_to_response, redirect
|
||||||
from mediagoblin.decorators import require_active_login
|
from mediagoblin.decorators import require_active_login, user_has_privilege
|
||||||
from mediagoblin.submit import forms as submit_forms
|
from mediagoblin.submit import forms as submit_forms
|
||||||
from mediagoblin.messages import add_message, SUCCESS
|
from mediagoblin.messages import add_message, SUCCESS
|
||||||
from mediagoblin.media_types import sniff_media, \
|
from mediagoblin.media_types import sniff_media, \
|
||||||
@ -39,6 +39,7 @@ from mediagoblin.notifications import add_comment_subscription
|
|||||||
|
|
||||||
|
|
||||||
@require_active_login
|
@require_active_login
|
||||||
|
@user_has_privilege(u'uploader')
|
||||||
def submit_start(request):
|
def submit_start(request):
|
||||||
"""
|
"""
|
||||||
First view for submitting a file.
|
First view for submitting a file.
|
||||||
|
35
mediagoblin/templates/mediagoblin/banned.html
Normal file
35
mediagoblin/templates/mediagoblin/banned.html
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/>.
|
||||||
|
#}
|
||||||
|
{% extends "mediagoblin/base.html" %}
|
||||||
|
|
||||||
|
{% block title %}{% trans %}You are Banned.{% endtrans %}{% endblock %}
|
||||||
|
|
||||||
|
{% block mediagoblin_content %}
|
||||||
|
<img class="right_align" src="{{ request.staticdirect('/images/404.png') }}"
|
||||||
|
alt="{% trans %}Image of goblin stressing out{% endtrans %}" />
|
||||||
|
|
||||||
|
<h1>{% trans %}You have been banned{% endtrans %}
|
||||||
|
{% if expiration_date %}
|
||||||
|
{% trans %}until{% endtrans %} {{ expiration_date }}
|
||||||
|
{% else %}
|
||||||
|
{% trans %}indefinitely{% endtrans %}
|
||||||
|
{% endif %}
|
||||||
|
</h2>
|
||||||
|
<p>{{ reason|safe }}</p>
|
||||||
|
<div class="clear"></div>
|
||||||
|
{% endblock %}
|
@ -62,7 +62,7 @@
|
|||||||
{% block mediagoblin_header_title %}{% endblock %}
|
{% block mediagoblin_header_title %}{% endblock %}
|
||||||
<div class="header_right">
|
<div class="header_right">
|
||||||
{%- if request.user %}
|
{%- if request.user %}
|
||||||
{% if request.user and request.user.status == 'active' %}
|
{% if request.user and request.user.has_privilege('active') and not request.user.is_banned() %}
|
||||||
|
|
||||||
{% set notification_count = get_notification_count(request.user.id) %}
|
{% set notification_count = get_notification_count(request.user.id) %}
|
||||||
{% if notification_count %}
|
{% if notification_count %}
|
||||||
@ -71,7 +71,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
<a href="javascript:;" class="button_action header_dropdown_down">▼</a>
|
<a href="javascript:;" class="button_action header_dropdown_down">▼</a>
|
||||||
<a href="javascript:;" class="button_action header_dropdown_up">▲</a>
|
<a href="javascript:;" class="button_action header_dropdown_up">▲</a>
|
||||||
{% elif request.user and request.user.status == "needs_email_verification" %}
|
{% elif request.user and not request.user.has_privilege('active') %}
|
||||||
{# the following link should only appear when verification is needed #}
|
{# the following link should only appear when verification is needed #}
|
||||||
<a href="{{ request.urlgen('mediagoblin.user_pages.user_home',
|
<a href="{{ request.urlgen('mediagoblin.user_pages.user_home',
|
||||||
user=request.user.username) }}"
|
user=request.user.username) }}"
|
||||||
@ -84,6 +84,19 @@
|
|||||||
"javascript:;"
|
"javascript:;"
|
||||||
{% endif %}
|
{% endif %}
|
||||||
>{% trans %}log out{% endtrans %}</a>
|
>{% trans %}log out{% endtrans %}</a>
|
||||||
|
{% elif request.user and request.user.is_banned() %}
|
||||||
|
<a id="logout" href=
|
||||||
|
{% if persona is not defined %}
|
||||||
|
"{{ request.urlgen('mediagoblin.auth.logout') }}"
|
||||||
|
{% else %}
|
||||||
|
"javascript:;"
|
||||||
|
{% endif %}
|
||||||
|
>{% trans %}log out{% endtrans %}</a>
|
||||||
|
<p class="fine_print">
|
||||||
|
<a href="{{ request.urlgen('terms_of_service') }}">
|
||||||
|
{%- trans %}Terms of Service{%- endtrans %}
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{%- elif auth %}
|
{%- elif auth %}
|
||||||
<a href=
|
<a href=
|
||||||
@ -98,7 +111,7 @@
|
|||||||
{%- endif %}
|
{%- endif %}
|
||||||
</div>
|
</div>
|
||||||
<div class="clear"></div>
|
<div class="clear"></div>
|
||||||
{% if request.user and request.user.status == 'active' %}
|
{% if request.user and request.user.has_privilege('active') %}
|
||||||
<div class="header_dropdown">
|
<div class="header_dropdown">
|
||||||
<p>
|
<p>
|
||||||
<span class="dropdown_title">
|
<span class="dropdown_title">
|
||||||
@ -130,14 +143,25 @@
|
|||||||
<a class="button_action" href="{{ request.urlgen('mediagoblin.submit.collection') }}">
|
<a class="button_action" href="{{ request.urlgen('mediagoblin.submit.collection') }}">
|
||||||
{%- trans %}Create new collection{% endtrans -%}
|
{%- trans %}Create new collection{% endtrans -%}
|
||||||
</a>
|
</a>
|
||||||
{% if request.user.is_admin %}
|
{% if request.user.has_privilege('admin','moderator') %}
|
||||||
<p>
|
<p>
|
||||||
<span class="dropdown_title">Admin powers:</span>
|
<span class="dropdown_title">Moderation powers:</span>
|
||||||
<a href="{{ request.urlgen('mediagoblin.admin.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>
|
||||||
|
·
|
||||||
|
<a href="{{ request.urlgen('mediagoblin.moderation.reports') }}">
|
||||||
|
{%- trans %}Report management panel{% endtrans -%}
|
||||||
|
</a>
|
||||||
</p>
|
</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
<p class="fine_print">
|
||||||
|
<a href="{{ request.urlgen('terms_of_service') }}">Terms of Service</a>
|
||||||
|
</p>
|
||||||
{% include 'mediagoblin/fragments/header_notifications.html' %}
|
{% include 'mediagoblin/fragments/header_notifications.html' %}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
{% trans %}Media processing panel{% endtrans %} — {{ super() }}
|
{% trans %}Media processing panel{% endtrans %} — {{ super() }}
|
||||||
{%- endblock %}
|
{%- endblock %}
|
||||||
|
|
||||||
|
|
||||||
{% block mediagoblin_content %}
|
{% block mediagoblin_content %}
|
||||||
|
|
||||||
<h1>{% trans %}Media processing panel{% endtrans %}</h1>
|
<h1>{% trans %}Media processing panel{% endtrans %}</h1>
|
154
mediagoblin/templates/mediagoblin/moderation/report.html
Normal file
154
mediagoblin/templates/mediagoblin/moderation/report.html
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
{#
|
||||||
|
# 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/>.
|
||||||
|
#}
|
||||||
|
{%- extends "mediagoblin/base.html" %}
|
||||||
|
{% import "/mediagoblin/utils/wtforms.html" as wtforms_util %}
|
||||||
|
{%- block mediagoblin_head %}
|
||||||
|
<script src="{{ request.staticdirect('/js/setup_report_forms.js') }}"></script>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{%- block mediagoblin_content %}
|
||||||
|
{% if not report %}
|
||||||
|
Sorry, no such report found.
|
||||||
|
{% else %}
|
||||||
|
<a href="{{ request.urlgen('mediagoblin.moderation.reports') }}"
|
||||||
|
class="return_to_panel button_action"
|
||||||
|
title="Return to Reports Panel">
|
||||||
|
{% trans %}Return to Reports Panel{% endtrans %}</a>
|
||||||
|
<h2>{% trans %}Report{% endtrans %} #{{ report.id }}</h2>
|
||||||
|
{% if report.is_comment_report() and report.comment %}
|
||||||
|
|
||||||
|
{% trans %}Reported comment{% endtrans %}:
|
||||||
|
{% set comment = report.comment %}
|
||||||
|
{% set reported_user = comment.get_author %}
|
||||||
|
<div id="comment-{{ comment.id }}"
|
||||||
|
class="comment_wrapper">
|
||||||
|
<div class="comment_author">
|
||||||
|
<img src="{{ request.staticdirect('/images/icon_comment.png') }}" />
|
||||||
|
<a href="{{ request.urlgen('mediagoblin.moderation.users_detail',
|
||||||
|
user=comment.get_author.username) }}"
|
||||||
|
class="comment_authorlink">
|
||||||
|
{{- reported_user.username -}}
|
||||||
|
</a>
|
||||||
|
<a href="{{ request.urlgen(
|
||||||
|
'mediagoblin.user_pages.media_home.view_comment',
|
||||||
|
comment=comment.id,
|
||||||
|
user=comment.get_media_entry.get_uploader.username,
|
||||||
|
media=comment.get_media_entry.slug_or_id) }}#comment"
|
||||||
|
class="comment_whenlink">
|
||||||
|
<span title='{{- comment.created.strftime("%I:%M%p %Y-%m-%d") -}}'>
|
||||||
|
{%- trans formatted_time=timesince(comment.created) -%}
|
||||||
|
{{ formatted_time }} ago
|
||||||
|
{%- endtrans -%}
|
||||||
|
</span></a>:
|
||||||
|
</div>
|
||||||
|
<div class=comment_content>
|
||||||
|
{% autoescape False %}
|
||||||
|
{{ comment.content_html }}
|
||||||
|
{% endautoescape %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% elif report.is_media_entry_report() and report.media_entry %}
|
||||||
|
|
||||||
|
{% set media_entry = report.media_entry %}
|
||||||
|
<div class="media_thumbnail">
|
||||||
|
<a href="{{ request.urlgen('mediagoblin.user_pages.media_home',
|
||||||
|
user=media_entry.get_uploader.username,
|
||||||
|
media=media_entry.slug_or_id) }}">
|
||||||
|
<img src="{{ media_entry.thumb_url}}"/></a>
|
||||||
|
<a href="{{ request.urlgen('mediagoblin.user_pages.media_home',
|
||||||
|
user=media_entry.get_uploader.username,
|
||||||
|
media=media_entry.slug_or_id) }}" class=thumb_entry_title>
|
||||||
|
{{ media_entry.title }}</a>
|
||||||
|
</div>
|
||||||
|
<div class=clear></div>
|
||||||
|
<p>
|
||||||
|
{% trans user_name=report.reported_user.username,
|
||||||
|
user_url=request.urlgen(
|
||||||
|
'mediagoblin.moderation.users_detail',
|
||||||
|
user=report.reported_user.username) %}
|
||||||
|
❖ Reported media by <a href="{{ user_url }}">{{ user_name }}</a>
|
||||||
|
{% endtrans %}
|
||||||
|
</p>
|
||||||
|
<div class=clear></div>
|
||||||
|
{% else %}
|
||||||
|
<h2>{% trans user_url=request.urlgen(
|
||||||
|
'mediagoblin.moderation.users_detail',
|
||||||
|
user=report.reporter.username),
|
||||||
|
user_name=report.reported_user.username %}
|
||||||
|
CONTENT BY
|
||||||
|
<a href="{{ user_url }}"> {{ user_name }}</a>
|
||||||
|
HAS BEEN DELETED
|
||||||
|
{% endtrans %}
|
||||||
|
</h2>
|
||||||
|
{% endif %}
|
||||||
|
Reason for report:
|
||||||
|
<div id="report-{{ report.id }}"
|
||||||
|
class="report_wrapper">
|
||||||
|
<div class="report_author">
|
||||||
|
<img src="{{ request.staticdirect(
|
||||||
|
'/images/icon_clipboard_alert.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" />
|
||||||
|
<a href="{{ request.urlgen('mediagoblin.moderation.users_detail',
|
||||||
|
user=report.reporter.username) }}"
|
||||||
|
class="report_authorlink">
|
||||||
|
{{- report.reporter.username -}}
|
||||||
|
</a>
|
||||||
|
<a href="{{ request.urlgen('mediagoblin.moderation.reports_detail',
|
||||||
|
report_id=report.id) }}"
|
||||||
|
class="report_whenlink">
|
||||||
|
<span title='{{- report.created.strftime("%I:%M%p %Y-%m-%d") -}}'>
|
||||||
|
{%- trans formatted_time=timesince(report.created) -%}
|
||||||
|
{{ formatted_time }} ago
|
||||||
|
{%- endtrans -%}
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="report_content">
|
||||||
|
{{ report.report_content }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% if not report.is_archived_report() and not (report.reported_user.has_privilege('admin') and not request.user.has_privilege('admin')) %}
|
||||||
|
<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() {
|
||||||
|
init_report_resolution_form();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{% elif report.is_archived_report() %}
|
||||||
|
<h2><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" />
|
||||||
|
{% trans %}Status{% endtrans %}:
|
||||||
|
</h2>
|
||||||
|
<b>{% trans %}RESOLVED{% endtrans %}</b>
|
||||||
|
{{ report.resolved.strftime("%I:%M%p %Y-%m-%d") }}
|
||||||
|
<pre>
|
||||||
|
<p>{{ report.result }}</p>
|
||||||
|
</pre>
|
||||||
|
{% else %}
|
||||||
|
<input type=button disabled=disabled value="Resolve This Report"/>
|
||||||
|
<p>You cannot take action against an administrator</p>
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
{% endblock %}
|
200
mediagoblin/templates/mediagoblin/moderation/report_panel.html
Normal file
200
mediagoblin/templates/mediagoblin/moderation/report_panel.html
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/>.
|
||||||
|
#}
|
||||||
|
{% extends "mediagoblin/base.html" %}
|
||||||
|
{% import "/mediagoblin/utils/wtforms.html" as wtforms_util %}
|
||||||
|
|
||||||
|
{% block title -%}
|
||||||
|
{% trans %}Report panel{% endtrans %} — {{ super() }}
|
||||||
|
{%- endblock %}
|
||||||
|
|
||||||
|
{% block mediagoblin_content %}
|
||||||
|
|
||||||
|
<h1>{% trans %}Report panel{% endtrans %}</h1>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
{% trans %}
|
||||||
|
Here you can look up open reports that have been filed by users.
|
||||||
|
{% endtrans %}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2>{% trans %}Active Reports Filed{% endtrans %}</h2>
|
||||||
|
{% if report_list.count() %}
|
||||||
|
{% if not active_settings.last_page == 1 %}
|
||||||
|
{% if 'active_p='~active_settings.current_page in request.query_string %}
|
||||||
|
{% set query_string = request.query_string %}{% else %}
|
||||||
|
{% set query_string =
|
||||||
|
'active_p='~active_settings.current_page~"&"+request.query_string %}
|
||||||
|
{% endif %}
|
||||||
|
<div class="right_align">
|
||||||
|
{% set first_vis = active_settings.current_page-3 %}
|
||||||
|
{% set last_vis = active_settings.current_page+3 %}
|
||||||
|
{% set curr_page = active_settings.current_page %}
|
||||||
|
{% if 1 == curr_page %}<b>1</b>{% else %}
|
||||||
|
<a href ="?{{ query_string.replace(
|
||||||
|
'active_p='~active_settings.current_page,
|
||||||
|
'active_p='~1) }}">
|
||||||
|
1</a>{% endif %}
|
||||||
|
{% if first_vis > 1 %}...{% endif %}
|
||||||
|
{% for p in range(first_vis,last_vis+1) %}
|
||||||
|
{% if p > 1 and p < active_settings.last_page and
|
||||||
|
curr_page !=p %}
|
||||||
|
<a href="?{{ query_string.replace(
|
||||||
|
'active_p='~active_settings.current_page,
|
||||||
|
'active_p='~p) }}">
|
||||||
|
{{ p }}</a>
|
||||||
|
{% elif p > 1 and p < active_settings.last_page %}
|
||||||
|
<b>{{ p }}</b>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{% if last_vis < active_settings.last_page %}...{% endif %}
|
||||||
|
{% if active_settings.last_page != curr_page %}
|
||||||
|
<a href ="?{{ query_string.replace(
|
||||||
|
'active_p='~active_settings.current_page,
|
||||||
|
'active_p='~active_settings.last_page) }}">
|
||||||
|
{{ active_settings.last_page }}</a>
|
||||||
|
{% else %}<b>{{ active_settings.last_page }}</b>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
<table class="admin_panel processing">
|
||||||
|
<tr>
|
||||||
|
<th></th>
|
||||||
|
<th>{% trans %}Offender{% endtrans %}</th>
|
||||||
|
<th>{% trans %}When Reported{% endtrans %}</th>
|
||||||
|
<th>{% trans %}Reported By{% endtrans %}</th>
|
||||||
|
<th>{% trans %}Reason{% endtrans %}</th>
|
||||||
|
</tr>
|
||||||
|
{% for report in report_list %}
|
||||||
|
<tr>
|
||||||
|
{% if report.discriminator == "comment_report" %}
|
||||||
|
<td>
|
||||||
|
<img
|
||||||
|
src="{{ request.staticdirect('/images/icon_clipboard_alert.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" />
|
||||||
|
<a href="{{ request.urlgen(
|
||||||
|
'mediagoblin.moderation.reports_detail',
|
||||||
|
report_id=report.id) }}">
|
||||||
|
{% trans report_id=report.id %}
|
||||||
|
Comment Report #{{ report_id }}
|
||||||
|
{% endtrans %}
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
{% elif report.discriminator == "media_report" %}
|
||||||
|
<td>
|
||||||
|
<img
|
||||||
|
src="{{ request.staticdirect('/images/icon_clipboard_alert.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" />
|
||||||
|
<a href="{{ request.urlgen(
|
||||||
|
'mediagoblin.moderation.reports_detail',
|
||||||
|
report_id=report.id) }}">
|
||||||
|
{% trans report_id=report.id %}
|
||||||
|
Media Report #{{ report_id }}
|
||||||
|
{% endtrans %}
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
{% endif %}
|
||||||
|
<td>{{ report.reported_user.username }}</td>
|
||||||
|
<td>{{ report.created.strftime("%F %R") }}</td>
|
||||||
|
<td>{{ report.reporter.username }}</td>
|
||||||
|
<td>{{ report.report_content[0:20] }}...</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
{% else %}
|
||||||
|
<p><em>{% trans %}No open reports found.{% endtrans %}</em></p>
|
||||||
|
{% endif %}
|
||||||
|
<h2>{% trans %}Closed Reports{% endtrans %}</h2>
|
||||||
|
{% if closed_report_list.count() %}
|
||||||
|
{% if not closed_settings.last_page == 1 %}
|
||||||
|
{% if 'closed_p='~closed_settings.current_page in request.query_string %}
|
||||||
|
{% set query_string = request.query_string %}{% else %}
|
||||||
|
{% set query_string =
|
||||||
|
'closed_p='~closed_settings.current_page~"&"+request.query_string %}
|
||||||
|
{% endif %}
|
||||||
|
<div class="right_align">
|
||||||
|
{% set first_vis = closed_settings.current_page-3 %}
|
||||||
|
{% set last_vis = closed_settings.current_page+3 %}
|
||||||
|
{% set curr_page = closed_settings.current_page %}
|
||||||
|
{% if not curr_page==1 %}
|
||||||
|
<a href ="?{{ query_string.replace(
|
||||||
|
'closed_p='~closed_settings.current_page,
|
||||||
|
'closed_p='~1) }}">1</a>
|
||||||
|
{% else %}
|
||||||
|
<b>1 </b>
|
||||||
|
{% endif %}
|
||||||
|
{% if first_vis > 1 %}...{% endif %}
|
||||||
|
{% for p in range(first_vis,last_vis+1) %}
|
||||||
|
{% if p > 1 and p < closed_settings.last_page and
|
||||||
|
curr_page !=p %}
|
||||||
|
<a href="?{{ query_string.replace(
|
||||||
|
'closed_p='~closed_settings.current_page,
|
||||||
|
'closed_p='~p) }}">
|
||||||
|
{{ p }}</a>
|
||||||
|
|
||||||
|
{% elif p > 1 and p < closed_settings.last_page %}
|
||||||
|
<b>{{ p }}</b>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{% if last_vis < closed_settings.last_page %}...{% endif %}
|
||||||
|
{% if curr_page != closed_settings.last_page %}
|
||||||
|
<a href ="?{{ query_string.replace(
|
||||||
|
'closed_p='~closed_settings.current_page,
|
||||||
|
'closed_p='~closed_settings.last_page) }}">
|
||||||
|
{{ closed_settings.last_page }}</a>
|
||||||
|
{% else %}<b>{{ closed_settings.last_page }}</b>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
<table class="media_panel processing">
|
||||||
|
<tr>
|
||||||
|
<th></th>
|
||||||
|
<th>{% trans %}Resolved{% endtrans %}</th>
|
||||||
|
<th>{% trans %}Offender{% endtrans %}</th>
|
||||||
|
<th>{% trans %}Action Taken{% endtrans %}</th>
|
||||||
|
<th>{% trans %}Reported By{% endtrans %}</th>
|
||||||
|
<th>{% trans %}Reason{% endtrans %}</th>
|
||||||
|
</tr>
|
||||||
|
{% for report in closed_report_list %}
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<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" />
|
||||||
|
<a href="{{ request.urlgen('mediagoblin.moderation.reports_detail',
|
||||||
|
report_id=report.id) }}">
|
||||||
|
{% trans report_id=report.id %}
|
||||||
|
Closed Report #{{ report_id }}
|
||||||
|
{% endtrans %}
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td>{{ report.resolved.strftime("%F %R") }}</td>
|
||||||
|
<td>{{ report.reported_user.username }}</td>
|
||||||
|
<td>{{ report.created.strftime("%F %R") }}</td>
|
||||||
|
<td>{{ report.reporter.username }}</td>
|
||||||
|
<td>{{ report.report_content[:15] }}...</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
{% else %}
|
||||||
|
<p><em>{% trans %}No closed reports found.{% endtrans %}</em></p>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% endblock %}
|
204
mediagoblin/templates/mediagoblin/moderation/user.html
Normal file
204
mediagoblin/templates/mediagoblin/moderation/user.html
Normal file
@ -0,0 +1,204 @@
|
|||||||
|
{#
|
||||||
|
# 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/>.
|
||||||
|
#}
|
||||||
|
{% extends "mediagoblin/base.html" %}
|
||||||
|
{% import "/mediagoblin/utils/wtforms.html" as wtforms_util %}
|
||||||
|
|
||||||
|
{% block title %}
|
||||||
|
{%- if user -%}
|
||||||
|
{%- trans username=user.username -%}
|
||||||
|
User: {{ username }}
|
||||||
|
{%- endtrans %} — {{ super() }}
|
||||||
|
{%- else -%}
|
||||||
|
{{ super() }}
|
||||||
|
{%- endif -%}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{%- block mediagoblin_head %}
|
||||||
|
<script src="{{ request.staticdirect('/js/setup_report_forms.js') }}"></script>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block mediagoblin_content -%}
|
||||||
|
{# If no user... #}
|
||||||
|
{% if not user %}
|
||||||
|
<p>{% trans %}Sorry, no such user found.{% endtrans %}</p>
|
||||||
|
{# User exists, but needs verification #}
|
||||||
|
{% elif not user.has_privilege('active') %}
|
||||||
|
<div class="profile_sidebar empty_space">
|
||||||
|
<h1>{% trans %}Email verification needed{% endtrans %}</h1>
|
||||||
|
<p>
|
||||||
|
{% trans -%}
|
||||||
|
Someone has registered an account with this username, but it still has
|
||||||
|
to be activated.
|
||||||
|
{%- endtrans %}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{# Active(?) (or at least verified at some point) user, horray! #}
|
||||||
|
{% else %}
|
||||||
|
<a href="{{ request.urlgen('mediagoblin.moderation.users') }}"
|
||||||
|
class="return_to_panel button_action"
|
||||||
|
title="Return to Users Panel">
|
||||||
|
{% trans %}Return to Users Panel{% endtrans %}</a>
|
||||||
|
<h1>
|
||||||
|
{%- trans username=user.username %}{{ username }}'s profile{% endtrans -%}
|
||||||
|
{% if user_banned and user_banned.expiration_date %}
|
||||||
|
— BANNED until {{ user_banned.expiration_date }}
|
||||||
|
{% elif user_banned %}
|
||||||
|
— Banned Indefinitely
|
||||||
|
{% endif %}
|
||||||
|
</h1>
|
||||||
|
{% if not user.url and not user.bio %}
|
||||||
|
<div class="profile_sidebar empty_space">
|
||||||
|
<p>
|
||||||
|
{% trans -%}
|
||||||
|
This user hasn't filled in their profile (yet).
|
||||||
|
{%- endtrans %}
|
||||||
|
</p>
|
||||||
|
{% else %}
|
||||||
|
<div class="profile_sidebar">
|
||||||
|
{% include "mediagoblin/utils/profile.html" %}
|
||||||
|
{% if request.user and
|
||||||
|
(request.user.id == user.id or request.user.has_privilege('admin')) %}
|
||||||
|
<a href="{{ request.urlgen('mediagoblin.edit.profile',
|
||||||
|
user=user.username) }}">
|
||||||
|
{%- trans %}Edit profile{% endtrans -%}
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
<p>
|
||||||
|
<a href="{{ request.urlgen('mediagoblin.user_pages.collection_list',
|
||||||
|
user=user.username) }}">
|
||||||
|
{%- trans %}Browse collections{% endtrans -%}
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% if user %}
|
||||||
|
<h2>{%- trans %}Active Reports on {% endtrans -%}{{ user.username }}</h2>
|
||||||
|
{% if reports.count() %}
|
||||||
|
<table class="admin_side_panel">
|
||||||
|
<tr>
|
||||||
|
<th>{%- trans %}Report ID{% endtrans -%}</th>
|
||||||
|
<th>{%- trans %}Reported Content{% endtrans -%}</th>
|
||||||
|
<th>{%- trans %}Description of Report{% endtrans -%}</th>
|
||||||
|
</tr>
|
||||||
|
{% for report in reports %}
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<img src="{{ request.staticdirect('/images/icon_clipboard.png') }}" />
|
||||||
|
<a href="{{ request.urlgen('mediagoblin.moderation.reports_detail',
|
||||||
|
report_id=report.id) }}">
|
||||||
|
{%- trans %}Report #{% endtrans -%}{{ report.id }}
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{% if report.discriminator == "comment_report" %}
|
||||||
|
<a>{%- trans %}Reported Comment{% endtrans -%}</a>
|
||||||
|
{% elif report.discriminator == "media_report" %}
|
||||||
|
<a>{%- trans %}Reported Media Entry{% endtrans -%}</a>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
<td>{{ report.report_content[:21] }}
|
||||||
|
{% if report.report_content|count >20 %}...{% endif %}</td>
|
||||||
|
<td>{%- trans %}Resolve{% endtrans -%}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
<tr><td></td><td></td>
|
||||||
|
</table>
|
||||||
|
{% else %}
|
||||||
|
{%- trans %}No active reports filed on {% endtrans -%} {{ user.username }}
|
||||||
|
{% endif %}
|
||||||
|
<span class="right_align">
|
||||||
|
<a href="{{ request.urlgen(
|
||||||
|
'mediagoblin.moderation.reports') }}?reported_user={{user.id}}">
|
||||||
|
{%- trans
|
||||||
|
username=user.username %}All reports on {{ username }}{% endtrans %}</a>
|
||||||
|
·
|
||||||
|
<a href="{{ request.urlgen(
|
||||||
|
'mediagoblin.moderation.reports') }}?reporter={{user.id}}">
|
||||||
|
{%- trans
|
||||||
|
username=user.username %}All reports that {{ username }} has filed{% endtrans %}</a>
|
||||||
|
</span>
|
||||||
|
<span class=clear></span>
|
||||||
|
<h2>{{ user.username }}'s Privileges</h2>
|
||||||
|
<form method=POST action="{{ request.urlgen(
|
||||||
|
'mediagoblin.moderation.ban_or_unban',
|
||||||
|
user=user.username) }}" class="right_align">
|
||||||
|
{{ csrf_token }}
|
||||||
|
{% if request.user.has_privilege('admin') and not user_banned and
|
||||||
|
not user.id == request.user.id %}
|
||||||
|
{{ wtforms_util.render_divs(ban_form) }}
|
||||||
|
<input type=submit class="button_action"
|
||||||
|
value="{% trans %}Ban User{% endtrans %}"
|
||||||
|
id="ban_user_submit" />
|
||||||
|
{% elif request.user.has_privilege('admin') and
|
||||||
|
not user.id == request.user.id %}
|
||||||
|
<input type=submit class="button_action right_align"
|
||||||
|
value="{% trans %}UnBan User{% endtrans %}" />
|
||||||
|
{% endif %}
|
||||||
|
</form>
|
||||||
|
<form action="{{ request.urlgen('mediagoblin.moderation.give_or_take_away_privilege',
|
||||||
|
user=user.username) }}"
|
||||||
|
method=post >
|
||||||
|
<table class="admin_side_panel">
|
||||||
|
<tr>
|
||||||
|
<th>{% trans %}Privilege{% endtrans %}</th>
|
||||||
|
<th>{% trans %}User Has Privilege{% endtrans %}</th>
|
||||||
|
</tr>
|
||||||
|
{% for privilege in privileges %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ privilege.privilege_name }}</td>
|
||||||
|
{% if privilege in user.all_privileges %}
|
||||||
|
<td class="user_with_privilege">
|
||||||
|
Yes{% else %}
|
||||||
|
<td class="user_without_privilege">
|
||||||
|
No{% endif %}
|
||||||
|
</td>
|
||||||
|
{% if request.user.has_privilege('admin') %}
|
||||||
|
<td>
|
||||||
|
{% if privilege in user.all_privileges %}
|
||||||
|
<input type=submit id="{{ privilege.privilege_name }}"
|
||||||
|
class="submit_button button_action"
|
||||||
|
value =" -" />
|
||||||
|
{% else %}
|
||||||
|
<input type=submit id="{{ privilege.privilege_name }}"
|
||||||
|
class="submit_button button_action"
|
||||||
|
value ="+" />
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
{% endif %}
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
{{ csrf_token }}
|
||||||
|
<input type=hidden name=privilege_name id=hidden_privilege_name />
|
||||||
|
</form>
|
||||||
|
{% endif %}
|
||||||
|
<script>
|
||||||
|
$(document).ready(function(){
|
||||||
|
$('.submit_button').click(function(){
|
||||||
|
$('#hidden_privilege_name').val($(this).attr('id'));
|
||||||
|
});
|
||||||
|
init_user_banned_form();
|
||||||
|
$('#ban_user_submit').click(function(){
|
||||||
|
submit_user_banned_form()
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
97
mediagoblin/templates/mediagoblin/moderation/user_panel.html
Normal file
97
mediagoblin/templates/mediagoblin/moderation/user_panel.html
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
{#
|
||||||
|
# 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/>.
|
||||||
|
#}
|
||||||
|
{% extends "mediagoblin/base.html" %}
|
||||||
|
|
||||||
|
{% block title -%}
|
||||||
|
{% trans %}User panel{% endtrans %} — {{ super() }}
|
||||||
|
{%- endblock %}
|
||||||
|
|
||||||
|
{% block mediagoblin_content %}
|
||||||
|
|
||||||
|
<h1>{% trans %}User panel{% endtrans %}</h1>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
{% trans %}
|
||||||
|
Here you can look up users in order to take punitive actions on them.
|
||||||
|
{% endtrans %}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2>{% trans %}Active Users{% endtrans %}</h2>
|
||||||
|
|
||||||
|
{% if user_list.count() %}
|
||||||
|
{% if not last_page == 1 %}
|
||||||
|
{% if 'p='~current_page in request.query_string %}
|
||||||
|
{% set query_string = request.query_string %}{% else %}
|
||||||
|
{% set query_string =
|
||||||
|
'p='~current_page~"&"+request.query_string %}
|
||||||
|
{% endif %}
|
||||||
|
<div class="right_align">
|
||||||
|
{% set first_vis = current_page-3 %}
|
||||||
|
{% set last_vis = current_page+3 %}
|
||||||
|
{% if 1 == current_page %}<b>1</b>{% else %}
|
||||||
|
<a href ="?{{ query_string.replace(
|
||||||
|
'p='~current_page,
|
||||||
|
'p='~1) }}">
|
||||||
|
1</a>{% endif %}
|
||||||
|
{% if first_vis > 1 %}...{% endif %}
|
||||||
|
{% for p in range(first_vis,last_vis+1) %}
|
||||||
|
{% if p > 1 and p < last_page and
|
||||||
|
current_page !=p %}
|
||||||
|
<a href="?{{ query_string.replace(
|
||||||
|
'p='~current_page,
|
||||||
|
'p='~p) }}">
|
||||||
|
{{ p }}</a>
|
||||||
|
{% elif p > 1 and p < last_page %}
|
||||||
|
<b>{{ p }}</b>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{% if last_vis < last_page %}...{% endif %}
|
||||||
|
{% if last_page != current_page %}
|
||||||
|
<a href ="?{{ query_string.replace(
|
||||||
|
'p='~current_page,
|
||||||
|
'p='~last_page) }}">
|
||||||
|
{{ last_page }}</a>
|
||||||
|
{% else %}<b>{{ last_page }}</b>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
<table class="admin_panel processing">
|
||||||
|
<tr>
|
||||||
|
<th>{% trans %}ID{% endtrans %}</th>
|
||||||
|
<th>{% trans %}Username{% endtrans %}</th>
|
||||||
|
<th>{% trans %}When Joined{% endtrans %}</th>
|
||||||
|
<th>{% trans %}# of Comments Posted{% endtrans %}</th>
|
||||||
|
</tr>
|
||||||
|
{% for user in user_list %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ user.id }}</td>
|
||||||
|
<td>
|
||||||
|
<a href="{{ request.urlgen('mediagoblin.moderation.users_detail',
|
||||||
|
user= user.username) }}">
|
||||||
|
{{ user.username }}
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td>{{ user.created.strftime("%F %R") }}</td>
|
||||||
|
<td>{{ user.posted_comments.count() }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
{% else %}
|
||||||
|
<p><em>{% trans %}No users found.{% endtrans %}</em></p>
|
||||||
|
{% endif %}
|
||||||
|
{% endblock %}
|
291
mediagoblin/templates/mediagoblin/terms_of_service.html
Normal file
291
mediagoblin/templates/mediagoblin/terms_of_service.html
Normal file
@ -0,0 +1,291 @@
|
|||||||
|
{#
|
||||||
|
# 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/>.
|
||||||
|
#}
|
||||||
|
{% extends "mediagoblin/base.html" %}
|
||||||
|
|
||||||
|
{% block title %}
|
||||||
|
Terms of Service
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block mediagoblin_content -%}
|
||||||
|
{# <h2>The gist</h2>
|
||||||
|
# This is where you might insert your own particular rules, unique to your
|
||||||
|
# own website. Or your own worded summary.
|
||||||
|
#}
|
||||||
|
|
||||||
|
<h2>Terms of Service</h2>
|
||||||
|
|
||||||
|
The following terms and conditions govern all use of the
|
||||||
|
{{ app_config['html_title'] }} website and all content, services and products
|
||||||
|
available at or through the website (taken together, the Website). The Website
|
||||||
|
is owned and operated by Status.net (“Operator”). The Website is offered
|
||||||
|
subject to your acceptance without modification of all of the terms and
|
||||||
|
conditions contained herein and all other operating rules, policies
|
||||||
|
(including, without limitation, Operator’s Privacy Policy) and procedures
|
||||||
|
that may be published from time to time on this Site by Operator (collectively,
|
||||||
|
the “Agreement”).
|
||||||
|
|
||||||
|
Please read this Agreement carefully before accessing or using the Website.
|
||||||
|
By accessing or using any part of the web site, you agree to become bound by
|
||||||
|
the terms and conditions of this agreement. If you do not agree to all the
|
||||||
|
terms and conditions of this agreement, then you may not access the Website
|
||||||
|
or use any services. If these terms and conditions are considered an offer by
|
||||||
|
Operator, acceptance is expressly limited to these terms. The Website is
|
||||||
|
available only to individuals who are at least 13 years old.
|
||||||
|
|
||||||
|
<ol id="code_of_conduct_list">
|
||||||
|
<li><strong>Your {{ app_config['html_title'] }} Account and Site.</strong>
|
||||||
|
If you create a notice stream on the Website, you are responsible for
|
||||||
|
maintaining the security of your account and notice stream, and you are
|
||||||
|
fully responsible for all activities that occur under the account and any
|
||||||
|
other actions taken in connection with the notice stream. You must not
|
||||||
|
describe or assign keywords to your notice stream in a misleading or
|
||||||
|
unlawful manner,
|
||||||
|
including in a manner intended to trade on the name or reputation of
|
||||||
|
others, and Operator may change or remove any description or keyword that
|
||||||
|
it considers inappropriate or unlawful, or otherwise likely to cause
|
||||||
|
Operator liability. You must immediately notify Operator of any
|
||||||
|
unauthorized uses of your notice stream, your account or any other breaches
|
||||||
|
of security. Operator will not be liable for any acts or omissions by You,
|
||||||
|
including any damages of any kind incurred as a result of such acts or
|
||||||
|
omissions.
|
||||||
|
</li>
|
||||||
|
<li><strong>Responsibility of Contributors.</strong> If you operate a notice
|
||||||
|
stream, comment on a notice stream, post material to the Website, post
|
||||||
|
links on the Website, or otherwise make
|
||||||
|
(or allow any third party to make) material
|
||||||
|
available by means of the Website (any such material, “Content”), You are
|
||||||
|
entirely responsible for the content of, and any harm resulting from, that
|
||||||
|
Content. That is the case regardless of whether the Content in question
|
||||||
|
constitutes text, graphics, an audio file, or computer software. By making
|
||||||
|
Content available, you represent and warrant that:
|
||||||
|
<ul class="nested_sublist">
|
||||||
|
<li>the downloading, copying and use of the Content will not infringe
|
||||||
|
the proprietary rights, including but not limited to the copyright,
|
||||||
|
patent, trademark or trade secret rights, of any third party;
|
||||||
|
</li>
|
||||||
|
<li>if your employer has rights to intellectual property you create, you
|
||||||
|
have either (i) received permission from your employer to post or make
|
||||||
|
available the Content, including but not limited to any software, or
|
||||||
|
(ii) secured from your employer a waiver as to all rights in or to the
|
||||||
|
Content;
|
||||||
|
</li>
|
||||||
|
<li>you have fully complied with any third-party licenses relating to the
|
||||||
|
Content, and have done all things necessary to successfully pass
|
||||||
|
through to end users any required terms;
|
||||||
|
</li>
|
||||||
|
<li>the Content does not contain or install any viruses, worms, malware,
|
||||||
|
Trojan horses or other harmful or destructive content;
|
||||||
|
</li>
|
||||||
|
<li>the Content is not spam, and does not contain unethical or unwanted
|
||||||
|
commercial content designed to drive traffic to third party sites or
|
||||||
|
boost the search engine rankings of third party sites, or to further
|
||||||
|
unlawful acts (such as phishing) or mislead recipients as to the
|
||||||
|
source of the material (such as spoofing);
|
||||||
|
</li>
|
||||||
|
<li>if the Content is machine- or randomly-generated, it is for purposes
|
||||||
|
of direct entertainment, information and/or utility for you or other
|
||||||
|
users, and not for spam,
|
||||||
|
</li>
|
||||||
|
<li>the Content is not libelous or defamatory (more info on what that
|
||||||
|
means), does not contain threats or incite violence towards individuals
|
||||||
|
or entities, and does not violate the privacy or publicity rights of
|
||||||
|
any third party;
|
||||||
|
</li>
|
||||||
|
<li>your notice stream is not getting advertised via unwanted electronic
|
||||||
|
messages such as spam links on newsgroups, email lists, other notice
|
||||||
|
streams and web sites, and similar unsolicited promotional methods;
|
||||||
|
</li>
|
||||||
|
<li>your notice stream is not named in a manner that misleads your
|
||||||
|
readers into thinking that you are another person or company. For
|
||||||
|
example, your notice stream’s URL or name is not the name of a person
|
||||||
|
other than yourself or company other than your own; and
|
||||||
|
</li>
|
||||||
|
<li>you have, in the case of Content that includes computer code,
|
||||||
|
accurately categorized and/or described the type, nature, uses and
|
||||||
|
effects of the materials, whether requested to do so by Operator or
|
||||||
|
otherwise.</li>
|
||||||
|
</ul>
|
||||||
|
By submitting Content to Operator for inclusion on your Website, you grant
|
||||||
|
Operator a world-wide, royalty-free, and non-exclusive license to
|
||||||
|
reproduce, modify, adapt and publish the Content solely for the purpose of
|
||||||
|
displaying, distributing and promoting your notice stream.
|
||||||
|
By submitting Content to Operator for inclusion on your Website, you grant
|
||||||
|
all readers the right to use, re-use, modify and/or re-distribute the
|
||||||
|
Content under the terms of the Creative Commons Attribution 3.0.
|
||||||
|
If you delete Content, Operator will use reasonable efforts to remove it
|
||||||
|
from the Website, but you acknowledge that caching or references to the
|
||||||
|
Content may not be made immediately unavailable.
|
||||||
|
Without limiting any of those representations or warranties, Operator has
|
||||||
|
the right (though not the obligation) to, in Operator’s sole discretion
|
||||||
|
(i) refuse or remove any content that, in Operator’s reasonable opinion,
|
||||||
|
violates any Operator policy or is in any way harmful or objectionable, or
|
||||||
|
(ii) terminate or deny access to and use of the Website to any individual
|
||||||
|
or entity for any reason, in Operator’s sole discretion.
|
||||||
|
</li>
|
||||||
|
<li><strong>Responsibility of Website Visitors.</strong> Operator has not
|
||||||
|
reviewed, and cannot
|
||||||
|
review, all of the material, including computer software, posted to the
|
||||||
|
Website, and cannot therefore be responsible for that material’s content,
|
||||||
|
use or effects. By operating the Website, Operator does not represent or
|
||||||
|
imply that it endorses the material there posted, or that it believes such
|
||||||
|
material to be accurate, useful or non-harmful. You are responsible for
|
||||||
|
taking precautions as necessary to protect yourself and your computer
|
||||||
|
systems from viruses, worms, Trojan horses, and other harmful or
|
||||||
|
destructive content. The Website may contain content that is offensive,
|
||||||
|
indecent, or otherwise objectionable, as well as content containing
|
||||||
|
technical inaccuracies, typographical mistakes, and other errors. The
|
||||||
|
Website may also contain material that violates the privacy or publicity
|
||||||
|
rights, or infringes the intellectual property and other proprietary
|
||||||
|
rights, of third parties, or the downloading, copying or use of which is
|
||||||
|
subject to additional terms and conditions, stated or unstated. Operator
|
||||||
|
disclaims any responsibility for any harm resulting from the use by
|
||||||
|
visitors of the Website, or from any downloading by those visitors of
|
||||||
|
content there posted.
|
||||||
|
</li>
|
||||||
|
<li><strong>Content Posted on Other Websites.</strong> We have not reviewed,
|
||||||
|
and cannot
|
||||||
|
review, all of the material, including computer software, made available
|
||||||
|
through the websites and webpages to which {{ app_config['html_title'] }}
|
||||||
|
links, and that link to {{ app_config['html_title'] }}. Operator does not
|
||||||
|
have any control over those external websites and webpages, and is not
|
||||||
|
responsible for their contents or their use. By linking to a external
|
||||||
|
website or webpage, Operator does not represent or imply that it endorses
|
||||||
|
such website or webpage. You are responsible for taking precautions as
|
||||||
|
necessary to protect yourself and your computer systems from viruses,
|
||||||
|
worms, Trojan horses, and other harmful or destructive content. Operator
|
||||||
|
disclaims any responsibility for any harm resulting from your use of
|
||||||
|
external websites and webpages.
|
||||||
|
</li>
|
||||||
|
<li><strong>Copyright Infringement and DMCA Policy.</strong> As Operator asks
|
||||||
|
others to
|
||||||
|
respect its intellectual property rights, it respects the intellectual
|
||||||
|
property rights of others. If you believe that material located on or
|
||||||
|
linked to by {{ app_config['html_title'] }} violates your copyright, you
|
||||||
|
are encouraged to notify Operator in accordance with Operator’s Digital
|
||||||
|
Millennium Copyright Act (”DMCA”) Policy. Operator will respond to all
|
||||||
|
such notices, including as required or appropriate by removing the
|
||||||
|
infringing material or disabling all links to the infringing material. In
|
||||||
|
the case of a visitor who may infringe or repeatedly infringes the
|
||||||
|
copyrights or other intellectual property rights of Operator or others,
|
||||||
|
Operator may, in its discretion, terminate or deny access to and use of
|
||||||
|
the Website. In the case of such termination, Operator will have no
|
||||||
|
obligation to provide a refund of any amounts previously paid to Operator.
|
||||||
|
</li>
|
||||||
|
<li><strong>Intellectual Property.</strong> This Agreement does not transfer
|
||||||
|
from Operator to
|
||||||
|
you any Operator or third party intellectual property, and all right,
|
||||||
|
title and interest in and to such property will remain (as between the
|
||||||
|
parties) solely with Operator. {{ app_config['html_title'] }}, the
|
||||||
|
{{ app_config['html_title'] }} logo, and all other trademarks, service
|
||||||
|
marks, graphics and logos used in connection with
|
||||||
|
{{ app_config['html_title'] }}, or the Website are trademarks or
|
||||||
|
registered trademarks of Operator or Operator’s licensors. Other
|
||||||
|
trademarks, service marks, graphics and logos used in connection with the
|
||||||
|
Website may be the trademarks of other third parties. Your use of the
|
||||||
|
Website grants you no right or license to reproduce or otherwise use any
|
||||||
|
Operator or third-party trademarks.
|
||||||
|
</li>
|
||||||
|
<li><strong>Changes.</strong> Operator reserves the right, at its sole
|
||||||
|
discretion, to modify
|
||||||
|
or replace any part of this Agreement. It is your responsibility to check
|
||||||
|
this Agreement periodically for changes. Your continued use of or access
|
||||||
|
to the Website following the posting of any changes to this Agreement
|
||||||
|
constitutes acceptance of those changes. Operator may also, in the future,
|
||||||
|
offer new services and/or features through the Website (including, the
|
||||||
|
release of new tools and resources). Such new features and/or services
|
||||||
|
shall be subject to the terms and conditions of this Agreement.
|
||||||
|
</li>
|
||||||
|
<li><strong>Termination.</strong> Operator may terminate your access to all
|
||||||
|
or any part of
|
||||||
|
the Website at any time, with or without cause, with or without notice,
|
||||||
|
effective immediately. If you wish to terminate this Agreement or your
|
||||||
|
{{ app_config['html_title'] }} account (if you have one), you may simply
|
||||||
|
discontinue using the Website. All provisions of this Agreement which by
|
||||||
|
their nature should survive termination shall survive termination,
|
||||||
|
including, without limitation, ownership provisions, warranty disclaimers,
|
||||||
|
indemnity and limitations of liability.
|
||||||
|
</li>
|
||||||
|
<li><strong>Disclaimer of Warranties.</strong> The Website is provided
|
||||||
|
“as is”. Operator and
|
||||||
|
its suppliers and licensors hereby disclaim all warranties of any kind,
|
||||||
|
express or implied, including, without limitation, the warranties of
|
||||||
|
merchantability, fitness for a particular purpose and non-infringement.
|
||||||
|
Neither Operator nor its suppliers and licensors, makes any warranty that
|
||||||
|
the Website will be error free or that access thereto will be continuous
|
||||||
|
or uninterrupted. If you’re actually reading this, here’s a treat. You
|
||||||
|
understand that you download from, or otherwise obtain content or services
|
||||||
|
through, the Website at your own discretion and risk.
|
||||||
|
</li>
|
||||||
|
<li><strong>Limitation of Liability.</strong> In no event will Operator, or
|
||||||
|
its suppliers or
|
||||||
|
licensors, be liable with respect to any subject matter of this agreement
|
||||||
|
under any contract, negligence, strict liability or other legal or
|
||||||
|
equitable theory for: (i) any special, incidental or consequential damages;
|
||||||
|
(ii) the cost of procurement or substitute products or services; (iii) for
|
||||||
|
interruption of use or loss or corruption of data; or (iv) for any amounts
|
||||||
|
that exceed the fees paid by you to Operator under this agreement during
|
||||||
|
the twelve (12) month period prior to the cause of action. Operator shall
|
||||||
|
have no liability for any failure or delay due to matters beyond their
|
||||||
|
reasonable control. The foregoing shall not apply to the extent prohibited
|
||||||
|
by applicable law.
|
||||||
|
</li>
|
||||||
|
<li><strong>General Representation and Warranty.</strong> You represent and
|
||||||
|
warrant that (i)
|
||||||
|
your use of the Website will be in strict accordance with the Operator
|
||||||
|
Privacy Policy, with this Agreement and with all applicable laws and
|
||||||
|
regulations (including without limitation any local laws or regulations in
|
||||||
|
your country, state, city, or other governmental area, regarding online
|
||||||
|
conduct and acceptable content, and including all applicable laws regarding
|
||||||
|
the transmission of technical data exported from the United States or the
|
||||||
|
country in which you reside) and (ii) your use of the Website will not
|
||||||
|
infringe or misappropriate the intellectual property rights of any third
|
||||||
|
party.
|
||||||
|
</li>
|
||||||
|
<li><strong>Indemnification.</strong> You agree to indemnify and hold
|
||||||
|
harmless Operator, its
|
||||||
|
contractors, and its licensors, and their respective directors, officers,
|
||||||
|
employees and agents from and against any and all claims and expenses,
|
||||||
|
including attorneys’ fees, arising out of your use of the Website,
|
||||||
|
including but not limited to out of your violation this Agreement.
|
||||||
|
</li>
|
||||||
|
<li><strong>Miscellaneous.</strong> This Agreement constitutes the entire
|
||||||
|
agreement between
|
||||||
|
Operator and you concerning the subject matter hereof, and they may only
|
||||||
|
be modified by a written amendment signed by an authorized executive of
|
||||||
|
Operator, or by the posting by Operator of a revised version. If any part
|
||||||
|
of this Agreement is held invalid or unenforceable, that part will be
|
||||||
|
construed to reflect the parties’ original intent, and the remaining
|
||||||
|
portions will remain in full force and effect. A waiver by either party of
|
||||||
|
any term or condition of this Agreement or any breach thereof, in any one
|
||||||
|
instance, will not waive such term or condition or any subsequent breach
|
||||||
|
thereof. You may assign your rights under this Agreement to any party that
|
||||||
|
consents to, and agrees to be bound by, its terms and conditions; Operator
|
||||||
|
may assign its rights under this Agreement without condition. This
|
||||||
|
Agreement will be binding upon and will inure to the benefit of the
|
||||||
|
parties, their successors and permitted assigns.
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
|
||||||
|
Originally published by Automattic, Inc. as the WordPress.com Terms of Service
|
||||||
|
and made available by them under the Creative Commons Attribution-
|
||||||
|
ShareAlike 3.0 License. Modifications to remove reference to "VIP services",
|
||||||
|
rename "blog" to "notice stream", remove the choice-of-venue clause, and add
|
||||||
|
variables specific to instances of this software made by Control Yourself, Inc.
|
||||||
|
and made available under the terms of the same license.
|
||||||
|
|
||||||
|
{% endblock -%}
|
@ -45,7 +45,7 @@
|
|||||||
{%- endtrans %}
|
{%- endtrans %}
|
||||||
</h1>
|
</h1>
|
||||||
{% if request.user and (collection.creator == request.user.id or
|
{% if request.user and (collection.creator == request.user.id or
|
||||||
request.user.is_admin) %}
|
request.user.has_privilege(u'admin')) %}
|
||||||
{% set edit_url = request.urlgen('mediagoblin.edit.edit_collection',
|
{% set edit_url = request.urlgen('mediagoblin.edit.edit_collection',
|
||||||
user=collection.get_creator.username,
|
user=collection.get_creator.username,
|
||||||
collection=collection.slug) %}
|
collection=collection.slug) %}
|
||||||
|
@ -34,7 +34,7 @@
|
|||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
{% if request.user %}
|
{% if request.user %}
|
||||||
{% if request.user.status == 'active' %}
|
{% if request.user.has_privilege('active') %}
|
||||||
<p>
|
<p>
|
||||||
<a href="{{ request.urlgen('mediagoblin.submit.collection',
|
<a href="{{ request.urlgen('mediagoblin.submit.collection',
|
||||||
user=user.username) }}">
|
user=user.username) }}">
|
||||||
|
@ -72,7 +72,7 @@
|
|||||||
</h2>
|
</h2>
|
||||||
{% if request.user and
|
{% if request.user and
|
||||||
(media.uploader == request.user.id or
|
(media.uploader == request.user.id or
|
||||||
request.user.is_admin) %}
|
request.user.has_privilege('admin')) %}
|
||||||
{% set edit_url = request.urlgen('mediagoblin.edit.edit_media',
|
{% set edit_url = request.urlgen('mediagoblin.edit.edit_media',
|
||||||
user= media.get_uploader.username,
|
user= media.get_uploader.username,
|
||||||
media_id=media.id) %}
|
media_id=media.id) %}
|
||||||
@ -86,7 +86,7 @@
|
|||||||
{% autoescape False %}
|
{% autoescape False %}
|
||||||
<p>{{ media.description_html }}</p>
|
<p>{{ media.description_html }}</p>
|
||||||
{% endautoescape %}
|
{% endautoescape %}
|
||||||
{% if comments %}
|
{% if comments and request.user and request.user.has_privilege('commenter') %}
|
||||||
{% if app_config['allow_comments'] %}
|
{% if app_config['allow_comments'] %}
|
||||||
<a
|
<a
|
||||||
{% if not request.user %}
|
{% if not request.user %}
|
||||||
@ -146,6 +146,15 @@
|
|||||||
{{ comment.content_html }}
|
{{ comment.content_html }}
|
||||||
{%- endautoescape %}
|
{%- endautoescape %}
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
{% if app_config.allow_reporting %}
|
||||||
|
<a href="{{ request.urlgen('mediagoblin.user_pages.media_home.report_comment',
|
||||||
|
user=media.get_uploader.username,
|
||||||
|
media=media.slug_or_id,
|
||||||
|
comment=comment.id) }}">
|
||||||
|
{% trans %}Report{% endtrans %}</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
</li>
|
</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
@ -170,6 +179,10 @@
|
|||||||
|
|
||||||
{% include "mediagoblin/utils/collections.html" %}
|
{% include "mediagoblin/utils/collections.html" %}
|
||||||
|
|
||||||
|
{% if app_config.allow_reporting %}
|
||||||
|
{% include "mediagoblin/utils/report.html" %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% include "mediagoblin/utils/license.html" %}
|
{% include "mediagoblin/utils/license.html" %}
|
||||||
|
|
||||||
{% include "mediagoblin/utils/exif.html" %}
|
{% include "mediagoblin/utils/exif.html" %}
|
||||||
@ -189,7 +202,7 @@
|
|||||||
{%- if app_config['allow_attachments']
|
{%- if app_config['allow_attachments']
|
||||||
and request.user
|
and request.user
|
||||||
and (media.uploader == request.user.id
|
and (media.uploader == request.user.id
|
||||||
or request.user.is_admin) %}
|
or request.user.has_privilege('admin')) %}
|
||||||
{%- if not media.attachment_files|count %}
|
{%- if not media.attachment_files|count %}
|
||||||
<h3>{% trans %}Attachments{% endtrans %}</h3>
|
<h3>{% trans %}Attachments{% endtrans %}</h3>
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
|
83
mediagoblin/templates/mediagoblin/user_pages/report.html
Normal file
83
mediagoblin/templates/mediagoblin/user_pages/report.html
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
{#
|
||||||
|
# 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/>.
|
||||||
|
#}
|
||||||
|
{%- extends "mediagoblin/base.html" %}
|
||||||
|
{%- import "/mediagoblin/utils/wtforms.html" as wtforms_util %}
|
||||||
|
{%- block mediagoblin_content -%}
|
||||||
|
{% trans %}<h2>File a Report</h2>{% endtrans %}
|
||||||
|
<form action="" method=POST >
|
||||||
|
{% if comment is defined %}
|
||||||
|
<h3>{% trans %}Reporting this Comment{% endtrans %}</h3>
|
||||||
|
{%- set comment_author = comment.get_author %}
|
||||||
|
{%- set comment_author_url = request.urlgen(
|
||||||
|
'mediagoblin.user_pages.user_home',
|
||||||
|
user=comment_author.username) %}
|
||||||
|
{%- set comment_url = request.urlgen(
|
||||||
|
'mediagoblin.user_pages.media_home.view_comment',
|
||||||
|
comment=comment.id,
|
||||||
|
user=media.get_uploader.username,
|
||||||
|
media=media.slug_or_id) %}
|
||||||
|
<div id="comment-{{ comment.id }}"
|
||||||
|
class="comment_wrapper">
|
||||||
|
<div class="comment_author">
|
||||||
|
<img
|
||||||
|
src="{{ request.staticdirect('/images/icon_comment.png') }}" />
|
||||||
|
<a href="{{ comment_author_url }}"
|
||||||
|
class="comment_authorlink">
|
||||||
|
{{- comment_author.username -}}
|
||||||
|
</a>
|
||||||
|
<a href="{{ comment_url }}"
|
||||||
|
class="comment_whenlink">
|
||||||
|
<span
|
||||||
|
title='{{- comment.created.strftime("%I:%M%p %Y-%m-%d") -}}'>
|
||||||
|
|
||||||
|
{%- trans formatted_time=timesince(comment.created) -%}
|
||||||
|
{{ formatted_time }} ago
|
||||||
|
{%- endtrans -%}
|
||||||
|
</span></a>:
|
||||||
|
</div>
|
||||||
|
<div class="comment_content">
|
||||||
|
{% autoescape False -%}
|
||||||
|
{{ comment.content_html }}
|
||||||
|
{%- endautoescape %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% elif media is defined %}
|
||||||
|
<h3>{% trans %}Reporting this Media Entry{% endtrans %}</h3>
|
||||||
|
<div class="media_thumbnail">
|
||||||
|
<a href="{{ request.urlgen('mediagoblin.user_pages.media_home',
|
||||||
|
user=media.get_uploader.username,
|
||||||
|
media=media.slug_or_id) }}">
|
||||||
|
<img src="{{ media.thumb_url }}"/></a>
|
||||||
|
<a href="{{ request.urlgen('mediagoblin.user_pages.media_home',
|
||||||
|
user=media.get_uploader.username,
|
||||||
|
media=media.slug_or_id) }}"
|
||||||
|
class=thumb_entry_title>{{ media.title }}</a>
|
||||||
|
</div>
|
||||||
|
<div class=clear></div>
|
||||||
|
{%- trans user_url = request.urlgen('mediagoblin.user_pages.user_home', user=media.get_uploader.username),
|
||||||
|
username = media.get_uploader.username %}
|
||||||
|
❖ Published by <a href="{{ user_url }}"
|
||||||
|
class="comment_authorlink">{{ username }}</a>
|
||||||
|
{% endtrans %}
|
||||||
|
{%- endif %}
|
||||||
|
|
||||||
|
{{- wtforms_util.render_divs(form) }}
|
||||||
|
{{ csrf_token }}
|
||||||
|
<input type=submit value="{% trans %}File Report {% endtrans %}" />
|
||||||
|
</form>
|
||||||
|
{% endblock %}
|
@ -67,7 +67,7 @@
|
|||||||
<div class="profile_sidebar">
|
<div class="profile_sidebar">
|
||||||
{% include "mediagoblin/utils/profile.html" %}
|
{% include "mediagoblin/utils/profile.html" %}
|
||||||
{% if request.user and
|
{% if request.user and
|
||||||
(request.user.id == user.id or request.user.is_admin) %}
|
(request.user.id == user.id or request.user.has_privilege('admin')) %}
|
||||||
<a href="{{ request.urlgen('mediagoblin.edit.profile',
|
<a href="{{ request.urlgen('mediagoblin.edit.profile',
|
||||||
user=user.username) }}">
|
user=user.username) }}">
|
||||||
{%- trans %}Edit profile{% endtrans -%}
|
{%- trans %}Edit profile{% endtrans -%}
|
||||||
|
@ -33,7 +33,7 @@
|
|||||||
|
|
||||||
{% block mediagoblin_content -%}
|
{% block mediagoblin_content -%}
|
||||||
{# User exists, but needs verification #}
|
{# User exists, but needs verification #}
|
||||||
{% if user.status == "needs_email_verification" %}
|
{% if not user.has_privilege('active') %}
|
||||||
{% if user == request.user %}
|
{% if user == request.user %}
|
||||||
{# this should only be visible when you are this user #}
|
{# this should only be visible when you are this user #}
|
||||||
<div class="form_box">
|
<div class="form_box">
|
||||||
|
@ -39,7 +39,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% if request.user and
|
{% if request.user and
|
||||||
(item.in_collection.creator == request.user.id or
|
(item.in_collection.creator == request.user.id or
|
||||||
request.user.is_admin) %}
|
request.user.has_privilege(u'admin')) %}
|
||||||
{%- set remove_url=request.urlgen(
|
{%- set remove_url=request.urlgen(
|
||||||
'mediagoblin.user_pages.collection_item_confirm_remove',
|
'mediagoblin.user_pages.collection_item_confirm_remove',
|
||||||
user=item.in_collection.get_creator.username,
|
user=item.in_collection.get_creator.username,
|
||||||
|
@ -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,8 +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/>.
|
||||||
|
#}
|
||||||
|
|
||||||
admin_routes = [
|
{% block report_content -%}
|
||||||
('mediagoblin.admin.panel',
|
<p>
|
||||||
'/panel',
|
<a href="{{ request.urlgen('mediagoblin.user_pages.media_home.report_media',
|
||||||
'mediagoblin.admin.views:admin_processing_panel')]
|
user=media.get_uploader.username,
|
||||||
|
media=media.slug_or_id) }}"
|
||||||
|
class="button_action" id="button_reportmedia" title="Report media">
|
||||||
|
{% trans %}Report media{% endtrans %}
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
{% endblock %}
|
@ -35,7 +35,8 @@ class TestAPI(object):
|
|||||||
self.db = mg_globals.database
|
self.db = mg_globals.database
|
||||||
|
|
||||||
self.user_password = u'4cc355_70k3N'
|
self.user_password = u'4cc355_70k3N'
|
||||||
self.user = fixture_add_user(u'joapi', self.user_password)
|
self.user = fixture_add_user(u'joapi', self.user_password,
|
||||||
|
privileges=[u'active',u'uploader'])
|
||||||
|
|
||||||
def login(self, test_app):
|
def login(self, test_app):
|
||||||
test_app.post(
|
test_app.post(
|
||||||
|
@ -84,22 +84,26 @@ def test_register_views(test_app):
|
|||||||
template.clear_test_template_context()
|
template.clear_test_template_context()
|
||||||
response = test_app.post(
|
response = test_app.post(
|
||||||
'/auth/register/', {
|
'/auth/register/', {
|
||||||
'username': u'happygirl',
|
'username': u'angrygirl',
|
||||||
'password': 'iamsohappy',
|
'password': 'iamsoangry',
|
||||||
'email': 'happygrrl@example.org'})
|
'email': 'angrygrrl@example.org'})
|
||||||
response.follow()
|
response.follow()
|
||||||
|
|
||||||
## Did we redirect to the proper page? Use the right template?
|
## Did we redirect to the proper page? Use the right template?
|
||||||
assert urlparse.urlsplit(response.location)[2] == '/u/happygirl/'
|
assert urlparse.urlsplit(response.location)[2] == '/u/angrygirl/'
|
||||||
assert 'mediagoblin/user_pages/user_nonactive.html' in template.TEMPLATE_TEST_CONTEXT
|
assert 'mediagoblin/user_pages/user_nonactive.html' in template.TEMPLATE_TEST_CONTEXT
|
||||||
|
|
||||||
## Make sure user is in place
|
## Make sure user is in place
|
||||||
new_user = mg_globals.database.User.query.filter_by(
|
new_user = mg_globals.database.User.query.filter_by(
|
||||||
username=u'happygirl').first()
|
username=u'angrygirl').first()
|
||||||
assert new_user
|
assert new_user
|
||||||
assert new_user.status == u'needs_email_verification'
|
|
||||||
assert new_user.email_verified == False
|
|
||||||
|
|
||||||
|
## Make sure that the proper privileges are granted on registration
|
||||||
|
|
||||||
|
assert new_user.has_privilege(u'commenter')
|
||||||
|
assert new_user.has_privilege(u'uploader')
|
||||||
|
assert new_user.has_privilege(u'reporter')
|
||||||
|
assert not new_user.has_privilege(u'active')
|
||||||
## Make sure user is logged in
|
## Make sure user is logged in
|
||||||
request = template.TEMPLATE_TEST_CONTEXT[
|
request = template.TEMPLATE_TEST_CONTEXT[
|
||||||
'mediagoblin/user_pages/user_nonactive.html']['request']
|
'mediagoblin/user_pages/user_nonactive.html']['request']
|
||||||
@ -108,7 +112,7 @@ def test_register_views(test_app):
|
|||||||
## Make sure we get email confirmation, and try verifying
|
## Make sure we get email confirmation, and try verifying
|
||||||
assert len(mail.EMAIL_TEST_INBOX) == 1
|
assert len(mail.EMAIL_TEST_INBOX) == 1
|
||||||
message = mail.EMAIL_TEST_INBOX.pop()
|
message = mail.EMAIL_TEST_INBOX.pop()
|
||||||
assert message['To'] == 'happygrrl@example.org'
|
assert message['To'] == 'angrygrrl@example.org'
|
||||||
email_context = template.TEMPLATE_TEST_CONTEXT[
|
email_context = template.TEMPLATE_TEST_CONTEXT[
|
||||||
'mediagoblin/auth/verification_email.txt']
|
'mediagoblin/auth/verification_email.txt']
|
||||||
assert email_context['verification_url'] in message.get_payload(decode=True)
|
assert email_context['verification_url'] in message.get_payload(decode=True)
|
||||||
@ -130,10 +134,8 @@ def test_register_views(test_app):
|
|||||||
# assert context['verification_successful'] == True
|
# assert context['verification_successful'] == True
|
||||||
# TODO: Would be good to test messages here when we can do so...
|
# TODO: Would be good to test messages here when we can do so...
|
||||||
new_user = mg_globals.database.User.query.filter_by(
|
new_user = mg_globals.database.User.query.filter_by(
|
||||||
username=u'happygirl').first()
|
username=u'angrygirl').first()
|
||||||
assert new_user
|
assert new_user
|
||||||
assert new_user.status == u'needs_email_verification'
|
|
||||||
assert new_user.email_verified == False
|
|
||||||
|
|
||||||
## Verify the email activation works
|
## Verify the email activation works
|
||||||
template.clear_test_template_context()
|
template.clear_test_template_context()
|
||||||
@ -144,10 +146,8 @@ def test_register_views(test_app):
|
|||||||
# assert context['verification_successful'] == True
|
# assert context['verification_successful'] == True
|
||||||
# TODO: Would be good to test messages here when we can do so...
|
# TODO: Would be good to test messages here when we can do so...
|
||||||
new_user = mg_globals.database.User.query.filter_by(
|
new_user = mg_globals.database.User.query.filter_by(
|
||||||
username=u'happygirl').first()
|
username=u'angrygirl').first()
|
||||||
assert new_user
|
assert new_user
|
||||||
assert new_user.status == u'active'
|
|
||||||
assert new_user.email_verified == True
|
|
||||||
|
|
||||||
# Uniqueness checks
|
# Uniqueness checks
|
||||||
# -----------------
|
# -----------------
|
||||||
@ -155,9 +155,9 @@ def test_register_views(test_app):
|
|||||||
template.clear_test_template_context()
|
template.clear_test_template_context()
|
||||||
response = test_app.post(
|
response = test_app.post(
|
||||||
'/auth/register/', {
|
'/auth/register/', {
|
||||||
'username': u'happygirl',
|
'username': u'angrygirl',
|
||||||
'password': 'iamsohappy2',
|
'password': 'iamsoangry2',
|
||||||
'email': 'happygrrl2@example.org'})
|
'email': 'angrygrrl2@example.org'})
|
||||||
|
|
||||||
context = template.TEMPLATE_TEST_CONTEXT[
|
context = template.TEMPLATE_TEST_CONTEXT[
|
||||||
'mediagoblin/auth/register.html']
|
'mediagoblin/auth/register.html']
|
||||||
@ -172,7 +172,7 @@ def test_register_views(test_app):
|
|||||||
template.clear_test_template_context()
|
template.clear_test_template_context()
|
||||||
response = test_app.post(
|
response = test_app.post(
|
||||||
'/auth/forgot_password/',
|
'/auth/forgot_password/',
|
||||||
{'username': u'happygirl'})
|
{'username': u'angrygirl'})
|
||||||
response.follow()
|
response.follow()
|
||||||
|
|
||||||
## Did we redirect to the proper page? Use the right template?
|
## Did we redirect to the proper page? Use the right template?
|
||||||
@ -182,7 +182,7 @@ def test_register_views(test_app):
|
|||||||
## Make sure link to change password is sent by email
|
## Make sure link to change password is sent by email
|
||||||
assert len(mail.EMAIL_TEST_INBOX) == 1
|
assert len(mail.EMAIL_TEST_INBOX) == 1
|
||||||
message = mail.EMAIL_TEST_INBOX.pop()
|
message = mail.EMAIL_TEST_INBOX.pop()
|
||||||
assert message['To'] == 'happygrrl@example.org'
|
assert message['To'] == 'angrygrrl@example.org'
|
||||||
email_context = template.TEMPLATE_TEST_CONTEXT[
|
email_context = template.TEMPLATE_TEST_CONTEXT[
|
||||||
'mediagoblin/plugins/basic_auth/fp_verification_email.txt']
|
'mediagoblin/plugins/basic_auth/fp_verification_email.txt']
|
||||||
#TODO - change the name of verification_url to something forgot-password-ish
|
#TODO - change the name of verification_url to something forgot-password-ish
|
||||||
@ -212,7 +212,7 @@ def test_register_views(test_app):
|
|||||||
template.clear_test_template_context()
|
template.clear_test_template_context()
|
||||||
response = test_app.post(
|
response = test_app.post(
|
||||||
'/auth/forgot_password/verify/', {
|
'/auth/forgot_password/verify/', {
|
||||||
'password': 'iamveryveryhappy',
|
'password': 'iamveryveryangry',
|
||||||
'token': parsed_get_params['token']})
|
'token': parsed_get_params['token']})
|
||||||
response.follow()
|
response.follow()
|
||||||
assert 'mediagoblin/auth/login.html' in template.TEMPLATE_TEST_CONTEXT
|
assert 'mediagoblin/auth/login.html' in template.TEMPLATE_TEST_CONTEXT
|
||||||
@ -221,8 +221,8 @@ def test_register_views(test_app):
|
|||||||
template.clear_test_template_context()
|
template.clear_test_template_context()
|
||||||
response = test_app.post(
|
response = test_app.post(
|
||||||
'/auth/login/', {
|
'/auth/login/', {
|
||||||
'username': u'happygirl',
|
'username': u'angrygirl',
|
||||||
'password': 'iamveryveryhappy'})
|
'password': 'iamveryveryangry'})
|
||||||
|
|
||||||
# User should be redirected
|
# User should be redirected
|
||||||
response.follow()
|
response.follow()
|
||||||
@ -235,7 +235,7 @@ def test_authentication_views(test_app):
|
|||||||
Test logging in and logging out
|
Test logging in and logging out
|
||||||
"""
|
"""
|
||||||
# Make a new user
|
# Make a new user
|
||||||
test_user = fixture_add_user(active_user=False)
|
test_user = fixture_add_user()
|
||||||
|
|
||||||
|
|
||||||
# Get login
|
# Get login
|
||||||
@ -332,7 +332,6 @@ def test_authentication_views(test_app):
|
|||||||
'next' : '/u/chris/'})
|
'next' : '/u/chris/'})
|
||||||
assert urlparse.urlsplit(response.location)[2] == '/u/chris/'
|
assert urlparse.urlsplit(response.location)[2] == '/u/chris/'
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture()
|
@pytest.fixture()
|
||||||
def authentication_disabled_app(request):
|
def authentication_disabled_app(request):
|
||||||
return get_app(
|
return get_app(
|
||||||
@ -344,6 +343,7 @@ def authentication_disabled_app(request):
|
|||||||
|
|
||||||
def test_authentication_disabled_app(authentication_disabled_app):
|
def test_authentication_disabled_app(authentication_disabled_app):
|
||||||
# app.auth should = false
|
# app.auth should = false
|
||||||
|
assert mg_globals
|
||||||
assert mg_globals.app.auth is False
|
assert mg_globals.app.auth is False
|
||||||
|
|
||||||
# Try to visit register page
|
# Try to visit register page
|
||||||
|
@ -27,7 +27,8 @@ class TestUserEdit(object):
|
|||||||
def setup(self):
|
def setup(self):
|
||||||
# set up new user
|
# set up new user
|
||||||
self.user_password = u'toast'
|
self.user_password = u'toast'
|
||||||
self.user = fixture_add_user(password = self.user_password)
|
self.user = fixture_add_user(password = self.user_password,
|
||||||
|
privileges=[u'active'])
|
||||||
|
|
||||||
def login(self, test_app):
|
def login(self, test_app):
|
||||||
test_app.post(
|
test_app.post(
|
||||||
@ -52,7 +53,8 @@ class TestUserEdit(object):
|
|||||||
# deleted too. Perhaps in submission test?
|
# deleted too. Perhaps in submission test?
|
||||||
|
|
||||||
#Restore user at end of test
|
#Restore user at end of test
|
||||||
self.user = fixture_add_user(password = self.user_password)
|
self.user = fixture_add_user(password = self.user_password,
|
||||||
|
privileges=[u'active'])
|
||||||
self.login(test_app)
|
self.login(test_app)
|
||||||
|
|
||||||
|
|
||||||
@ -80,7 +82,8 @@ class TestUserEdit(object):
|
|||||||
assert test_user.url == u'http://dustycloud.org/'
|
assert test_user.url == u'http://dustycloud.org/'
|
||||||
|
|
||||||
# change a different user than the logged in (should fail with 403)
|
# change a different user than the logged in (should fail with 403)
|
||||||
fixture_add_user(username=u"foo")
|
fixture_add_user(username=u"foo",
|
||||||
|
privileges=[u'active'])
|
||||||
res = test_app.post(
|
res = test_app.post(
|
||||||
'/u/foo/edit/', {
|
'/u/foo/edit/', {
|
||||||
'bio': u'I love toast!',
|
'bio': u'I love toast!',
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
# methods, and so it makes sense to test them here.
|
# methods, and so it makes sense to test them here.
|
||||||
|
|
||||||
from mediagoblin.db.base import Session
|
from mediagoblin.db.base import Session
|
||||||
from mediagoblin.db.models import MediaEntry
|
from mediagoblin.db.models import MediaEntry, User, Privilege
|
||||||
|
|
||||||
from mediagoblin.tests.tools import fixture_add_user
|
from mediagoblin.tests.tools import fixture_add_user
|
||||||
|
|
||||||
@ -151,6 +151,44 @@ class TestMediaEntrySlugs(object):
|
|||||||
qbert_entry.generate_slug()
|
qbert_entry.generate_slug()
|
||||||
assert qbert_entry.slug is None
|
assert qbert_entry.slug is None
|
||||||
|
|
||||||
|
class TestUserHasPrivilege:
|
||||||
|
def _setup(self):
|
||||||
|
fixture_add_user(u'natalie',
|
||||||
|
privileges=[u'admin',u'moderator',u'active'])
|
||||||
|
fixture_add_user(u'aeva',
|
||||||
|
privileges=[u'moderator',u'active'])
|
||||||
|
self.natalie_user = User.query.filter(
|
||||||
|
User.username==u'natalie').first()
|
||||||
|
self.aeva_user = User.query.filter(
|
||||||
|
User.username==u'aeva').first()
|
||||||
|
|
||||||
|
def test_privilege_added_correctly(self, test_app):
|
||||||
|
self._setup()
|
||||||
|
admin = Privilege.query.filter(
|
||||||
|
Privilege.privilege_name == u'admin').one()
|
||||||
|
# first make sure the privileges were added successfully
|
||||||
|
|
||||||
|
assert admin in self.natalie_user.all_privileges
|
||||||
|
assert admin not in self.aeva_user.all_privileges
|
||||||
|
|
||||||
|
def test_user_has_privilege_one(self, test_app):
|
||||||
|
self._setup()
|
||||||
|
|
||||||
|
# then test out the user.has_privilege method for one privilege
|
||||||
|
assert not self.natalie_user.has_privilege(u'commenter')
|
||||||
|
assert self.aeva_user.has_privilege(u'active')
|
||||||
|
|
||||||
|
|
||||||
|
def test_user_has_privileges_multiple(self, test_app):
|
||||||
|
self._setup()
|
||||||
|
|
||||||
|
# when multiple args are passed to has_privilege, the method returns
|
||||||
|
# True if the user has ANY of the privileges
|
||||||
|
assert self.natalie_user.has_privilege(u'admin',u'commenter')
|
||||||
|
assert self.aeva_user.has_privilege(u'moderator',u'active')
|
||||||
|
assert not self.natalie_user.has_privilege(u'commenter',u'uploader')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def test_media_data_init(test_app):
|
def test_media_data_init(test_app):
|
||||||
Session.rollback()
|
Session.rollback()
|
||||||
@ -165,3 +203,4 @@ def test_media_data_init(test_app):
|
|||||||
obj_in_session += 1
|
obj_in_session += 1
|
||||||
print repr(obj)
|
print repr(obj)
|
||||||
assert obj_in_session == 0
|
assert obj_in_session == 0
|
||||||
|
|
||||||
|
242
mediagoblin/tests/test_moderation.py
Normal file
242
mediagoblin/tests/test_moderation.py
Normal file
@ -0,0 +1,242 @@
|
|||||||
|
# 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 pytest
|
||||||
|
|
||||||
|
from mediagoblin.tests.tools import (fixture_add_user,
|
||||||
|
fixture_add_comment_report, fixture_add_comment)
|
||||||
|
from mediagoblin.db.models import User, CommentReport, MediaComment, UserBan
|
||||||
|
from mediagoblin.tools import template, mail
|
||||||
|
from webtest import AppError
|
||||||
|
|
||||||
|
class TestModerationViews:
|
||||||
|
@pytest.fixture(autouse=True)
|
||||||
|
def _setup(self, test_app):
|
||||||
|
self.test_app = test_app
|
||||||
|
|
||||||
|
fixture_add_user(u'admin',
|
||||||
|
privileges=[u'admin',u'active'])
|
||||||
|
fixture_add_user(u'moderator',
|
||||||
|
privileges=[u'moderator',u'active'])
|
||||||
|
fixture_add_user(u'regular',
|
||||||
|
privileges=[u'active',u'commenter'])
|
||||||
|
self.query_for_users()
|
||||||
|
|
||||||
|
def login(self, username):
|
||||||
|
self.test_app.post(
|
||||||
|
'/auth/login/', {
|
||||||
|
'username': username,
|
||||||
|
'password': 'toast'})
|
||||||
|
self.query_for_users()
|
||||||
|
|
||||||
|
def logout(self):
|
||||||
|
self.test_app.get('/auth/logout/')
|
||||||
|
self.query_for_users()
|
||||||
|
|
||||||
|
def query_for_users(self):
|
||||||
|
self.admin_user = User.query.filter(User.username==u'admin').first()
|
||||||
|
self.mod_user = User.query.filter(User.username==u'moderator').first()
|
||||||
|
self.user = User.query.filter(User.username==u'regular').first()
|
||||||
|
|
||||||
|
def do_post(self, data, *context_keys, **kwargs):
|
||||||
|
url = kwargs.pop('url', '/submit/')
|
||||||
|
do_follow = kwargs.pop('do_follow', False)
|
||||||
|
template.clear_test_template_context()
|
||||||
|
response = self.test_app.post(url, data, **kwargs)
|
||||||
|
if do_follow:
|
||||||
|
response.follow()
|
||||||
|
context_data = template.TEMPLATE_TEST_CONTEXT
|
||||||
|
for key in context_keys:
|
||||||
|
context_data = context_data[key]
|
||||||
|
return response, context_data
|
||||||
|
|
||||||
|
def testGiveOrTakeAwayPrivileges(self):
|
||||||
|
self.login(u'admin')
|
||||||
|
# First, test an admin taking away a privilege from a user
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
response, context = self.do_post({'privilege_name':u'commenter'},
|
||||||
|
url='/mod/users/{0}/privilege/'.format(self.user.username))
|
||||||
|
assert response.status == '302 FOUND'
|
||||||
|
self.query_for_users()
|
||||||
|
assert not self.user.has_privilege(u'commenter')
|
||||||
|
|
||||||
|
# Then, test an admin giving a privilege to a user
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
response, context = self.do_post({'privilege_name':u'commenter'},
|
||||||
|
url='/mod/users/{0}/privilege/'.format(self.user.username))
|
||||||
|
assert response.status == '302 FOUND'
|
||||||
|
self.query_for_users()
|
||||||
|
assert self.user.has_privilege(u'commenter')
|
||||||
|
|
||||||
|
# Then, test a mod trying to take away a privilege from a user
|
||||||
|
# they are not allowed to do this, so this will raise an error
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
self.logout()
|
||||||
|
self.login(u'moderator')
|
||||||
|
|
||||||
|
with pytest.raises(AppError) as excinfo:
|
||||||
|
response, context = self.do_post({'privilege_name':u'commenter'},
|
||||||
|
url='/mod/users/{0}/privilege/'.format(self.user.username))
|
||||||
|
assert 'Bad response: 403 FORBIDDEN' in str(excinfo)
|
||||||
|
self.query_for_users()
|
||||||
|
|
||||||
|
assert self.user.has_privilege(u'commenter')
|
||||||
|
|
||||||
|
def testReportResolution(self):
|
||||||
|
self.login(u'moderator')
|
||||||
|
|
||||||
|
# First, test a moderators taking away a user's privilege in response
|
||||||
|
# to a reported comment
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
fixture_add_comment_report(reported_user=self.user)
|
||||||
|
comment_report = CommentReport.query.filter(
|
||||||
|
CommentReport.reported_user==self.user).first()
|
||||||
|
|
||||||
|
response = self.test_app.get('/mod/reports/{0}/'.format(
|
||||||
|
comment_report.id))
|
||||||
|
assert response.status == '200 OK'
|
||||||
|
self.query_for_users()
|
||||||
|
comment_report = CommentReport.query.filter(
|
||||||
|
CommentReport.reported_user==self.user).first()
|
||||||
|
|
||||||
|
response, context = self.do_post({'action_to_resolve':[u'takeaway'],
|
||||||
|
'take_away_privileges':[u'commenter'],
|
||||||
|
'targeted_user':self.user.id},
|
||||||
|
url='/mod/reports/{0}/'.format(comment_report.id))
|
||||||
|
|
||||||
|
self.query_for_users()
|
||||||
|
comment_report = CommentReport.query.filter(
|
||||||
|
CommentReport.reported_user==self.user).first()
|
||||||
|
assert response.status == '302 FOUND'
|
||||||
|
assert not self.user.has_privilege(u'commenter')
|
||||||
|
assert comment_report.is_archived_report() is True
|
||||||
|
|
||||||
|
fixture_add_comment_report(reported_user=self.user)
|
||||||
|
comment_report = CommentReport.query.filter(
|
||||||
|
CommentReport.reported_user==self.user).first()
|
||||||
|
|
||||||
|
# Then, test a moderator sending an email to a user in response to a
|
||||||
|
# reported comment
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
self.query_for_users()
|
||||||
|
|
||||||
|
response, context = self.do_post({'action_to_resolve':[u'sendmessage'],
|
||||||
|
'message_to_user':'This is your last warning, regular....',
|
||||||
|
'targeted_user':self.user.id},
|
||||||
|
url='/mod/reports/{0}/'.format(comment_report.id))
|
||||||
|
|
||||||
|
self.query_for_users()
|
||||||
|
comment_report = CommentReport.query.filter(
|
||||||
|
CommentReport.reported_user==self.user).first()
|
||||||
|
assert response.status == '302 FOUND'
|
||||||
|
assert mail.EMAIL_TEST_MBOX_INBOX == [{'to': [u'regular@example.com'],
|
||||||
|
'message': 'Content-Type: text/plain; charset="utf-8"\n\
|
||||||
|
MIME-Version: 1.0\nContent-Transfer-Encoding: base64\nSubject: Warning from- \
|
||||||
|
moderator \nFrom: notice@mediagoblin.example.org\nTo: regular@example.com\n\n\
|
||||||
|
VGhpcyBpcyB5b3VyIGxhc3Qgd2FybmluZywgcmVndWxhci4uLi4=\n',
|
||||||
|
'from': 'notice@mediagoblin.example.org'}]
|
||||||
|
assert comment_report.is_archived_report() is True
|
||||||
|
|
||||||
|
# Then test a moderator banning a user AND a moderator deleting the
|
||||||
|
# offending comment. This also serves as a test for taking multiple
|
||||||
|
# actions to resolve a report
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
self.query_for_users()
|
||||||
|
fixture_add_comment(author=self.user.id,
|
||||||
|
comment=u'Comment will be removed')
|
||||||
|
test_comment = MediaComment.query.filter(
|
||||||
|
MediaComment.author==self.user.id).first()
|
||||||
|
fixture_add_comment_report(comment=test_comment,
|
||||||
|
reported_user=self.user)
|
||||||
|
comment_report = CommentReport.query.filter(
|
||||||
|
CommentReport.comment==test_comment).filter(
|
||||||
|
CommentReport.resolved==None).first()
|
||||||
|
|
||||||
|
response, context = self.do_post(
|
||||||
|
{'action_to_resolve':[u'userban', u'delete'],
|
||||||
|
'targeted_user':self.user.id,
|
||||||
|
'why_user_was_banned':u'',
|
||||||
|
'user_banned_until':u''},
|
||||||
|
url='/mod/reports/{0}/'.format(comment_report.id))
|
||||||
|
assert response.status == '302 FOUND'
|
||||||
|
self.query_for_users()
|
||||||
|
test_user_ban = UserBan.query.filter(
|
||||||
|
UserBan.user_id == self.user.id).first()
|
||||||
|
assert test_user_ban is not None
|
||||||
|
test_comment = MediaComment.query.filter(
|
||||||
|
MediaComment.author==self.user.id).first()
|
||||||
|
assert test_comment is None
|
||||||
|
|
||||||
|
# Then, test what happens when a moderator attempts to punish an admin
|
||||||
|
# from a reported comment on an admin.
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
fixture_add_comment_report(reported_user=self.admin_user)
|
||||||
|
comment_report = CommentReport.query.filter(
|
||||||
|
CommentReport.reported_user==self.admin_user).filter(
|
||||||
|
CommentReport.resolved==None).first()
|
||||||
|
|
||||||
|
response, context = self.do_post({'action_to_resolve':[u'takeaway'],
|
||||||
|
'take_away_privileges':[u'active'],
|
||||||
|
'targeted_user':self.admin_user.id},
|
||||||
|
url='/mod/reports/{0}/'.format(comment_report.id))
|
||||||
|
self.query_for_users()
|
||||||
|
|
||||||
|
assert response.status == '200 OK'
|
||||||
|
assert self.admin_user.has_privilege(u'active')
|
||||||
|
|
||||||
|
def testAllModerationViews(self):
|
||||||
|
self.login(u'moderator')
|
||||||
|
username = self.user.username
|
||||||
|
self.query_for_users()
|
||||||
|
fixture_add_comment_report(reported_user=self.admin_user)
|
||||||
|
response = self.test_app.get('/mod/reports/')
|
||||||
|
assert response.status == "200 OK"
|
||||||
|
|
||||||
|
response = self.test_app.get('/mod/reports/1/')
|
||||||
|
assert response.status == "200 OK"
|
||||||
|
|
||||||
|
response = self.test_app.get('/mod/users/')
|
||||||
|
assert response.status == "200 OK"
|
||||||
|
|
||||||
|
user_page_url = '/mod/users/{0}/'.format(username)
|
||||||
|
response = self.test_app.get(user_page_url)
|
||||||
|
assert response.status == "200 OK"
|
||||||
|
|
||||||
|
self.test_app.get('/mod/media/')
|
||||||
|
assert response.status == "200 OK"
|
||||||
|
|
||||||
|
def testBanUnBanUser(self):
|
||||||
|
self.login(u'admin')
|
||||||
|
username = self.user.username
|
||||||
|
user_id = self.user.id
|
||||||
|
ban_url = '/mod/users/{0}/ban/'.format(username)
|
||||||
|
response, context = self.do_post({
|
||||||
|
'user_banned_until':u'',
|
||||||
|
'why_user_was_banned':u'Because I said so'},
|
||||||
|
url=ban_url)
|
||||||
|
|
||||||
|
assert response.status == "302 FOUND"
|
||||||
|
user_banned = UserBan.query.filter(UserBan.user_id==user_id).first()
|
||||||
|
assert user_banned is not None
|
||||||
|
assert user_banned.expiration_date is None
|
||||||
|
assert user_banned.reason == u'Because I said so'
|
||||||
|
|
||||||
|
response, context = self.do_post({},
|
||||||
|
url=ban_url)
|
||||||
|
|
||||||
|
assert response.status == "302 FOUND"
|
||||||
|
user_banned = UserBan.query.filter(UserBan.user_id==user_id).first()
|
||||||
|
assert user_banned is None
|
@ -38,7 +38,7 @@ class TestNotifications:
|
|||||||
|
|
||||||
# TODO: Possibly abstract into a decorator like:
|
# TODO: Possibly abstract into a decorator like:
|
||||||
# @as_authenticated_user('chris')
|
# @as_authenticated_user('chris')
|
||||||
self.test_user = fixture_add_user()
|
self.test_user = fixture_add_user(privileges=[u'active',u'commenter'])
|
||||||
|
|
||||||
self.current_user = None
|
self.current_user = None
|
||||||
|
|
||||||
@ -75,7 +75,10 @@ class TestNotifications:
|
|||||||
|
|
||||||
'''
|
'''
|
||||||
user = fixture_add_user('otherperson', password='nosreprehto',
|
user = fixture_add_user('otherperson', password='nosreprehto',
|
||||||
wants_comment_notification=wants_email)
|
wants_comment_notification=wants_email,
|
||||||
|
privileges=[u'active',u'commenter'])
|
||||||
|
|
||||||
|
assert user.wants_comment_notification == wants_email
|
||||||
|
|
||||||
user_id = user.id
|
user_id = user.id
|
||||||
|
|
||||||
@ -124,6 +127,7 @@ otherperson@example.com\n\nSGkgb3RoZXJwZXJzb24sCmNocmlzIGNvbW1lbnRlZCBvbiB5b3VyI
|
|||||||
else:
|
else:
|
||||||
assert mail.EMAIL_TEST_MBOX_INBOX == []
|
assert mail.EMAIL_TEST_MBOX_INBOX == []
|
||||||
|
|
||||||
|
|
||||||
# Save the ids temporarily because of DetachedInstanceError
|
# Save the ids temporarily because of DetachedInstanceError
|
||||||
notification_id = notification.id
|
notification_id = notification.id
|
||||||
comment_id = notification.subject.id
|
comment_id = notification.subject.id
|
||||||
@ -153,7 +157,8 @@ otherperson@example.com\n\nSGkgb3RoZXJwZXJzb24sCmNocmlzIGNvbW1lbnRlZCBvbiB5b3VyI
|
|||||||
def test_mark_all_comment_notifications_seen(self):
|
def test_mark_all_comment_notifications_seen(self):
|
||||||
""" Test that mark_all_comments_seen works"""
|
""" Test that mark_all_comments_seen works"""
|
||||||
|
|
||||||
user = fixture_add_user('otherperson', password='nosreprehto')
|
user = fixture_add_user('otherperson', password='nosreprehto',
|
||||||
|
privileges=[u'active'])
|
||||||
|
|
||||||
media_entry = fixture_media_entry(uploader=user.id, state=u'processed')
|
media_entry = fixture_media_entry(uploader=user.id, state=u'processed')
|
||||||
|
|
||||||
|
@ -38,7 +38,8 @@ class TestOAuth(object):
|
|||||||
self.pman = pluginapi.PluginManager()
|
self.pman = pluginapi.PluginManager()
|
||||||
|
|
||||||
self.user_password = u'4cc355_70k3N'
|
self.user_password = u'4cc355_70k3N'
|
||||||
self.user = fixture_add_user(u'joauth', self.user_password)
|
self.user = fixture_add_user(u'joauth', self.user_password,
|
||||||
|
privileges=[u'active'])
|
||||||
|
|
||||||
self.login()
|
self.login()
|
||||||
|
|
||||||
|
@ -238,7 +238,7 @@ class TestOpenIDPlugin(object):
|
|||||||
def test_add_delete(self, openid_plugin_app):
|
def test_add_delete(self, openid_plugin_app):
|
||||||
"""Test adding and deleting openids"""
|
"""Test adding and deleting openids"""
|
||||||
# Add user
|
# Add user
|
||||||
test_user = fixture_add_user(password='')
|
test_user = fixture_add_user(password='', privileges=[u'active'])
|
||||||
openid = OpenIDUserURL()
|
openid = OpenIDUserURL()
|
||||||
openid.openid_url = 'http://real.myopenid.com'
|
openid.openid_url = 'http://real.myopenid.com'
|
||||||
openid.user_id = test_user.id
|
openid.user_id = test_user.id
|
||||||
|
@ -22,6 +22,7 @@ pytest.importorskip("requests")
|
|||||||
|
|
||||||
from mediagoblin import mg_globals
|
from mediagoblin import mg_globals
|
||||||
from mediagoblin.db.base import Session
|
from mediagoblin.db.base import Session
|
||||||
|
from mediagoblin.db.models import Privilege
|
||||||
from mediagoblin.tests.tools import get_app
|
from mediagoblin.tests.tools import get_app
|
||||||
from mediagoblin.tools import template
|
from mediagoblin.tools import template
|
||||||
|
|
||||||
@ -112,8 +113,9 @@ class TestPersonaPlugin(object):
|
|||||||
# Get user and detach from session
|
# Get user and detach from session
|
||||||
test_user = mg_globals.database.User.query.filter_by(
|
test_user = mg_globals.database.User.query.filter_by(
|
||||||
username=u'chris').first()
|
username=u'chris').first()
|
||||||
test_user.email_verified = True
|
active_privilege = Privilege.query.filter(
|
||||||
test_user.status = u'active'
|
Privilege.privilege_name==u'active').first()
|
||||||
|
test_user.all_privileges.append(active_privilege)
|
||||||
test_user.save()
|
test_user.save()
|
||||||
test_user = mg_globals.database.User.query.filter_by(
|
test_user = mg_globals.database.User.query.filter_by(
|
||||||
username=u'chris').first()
|
username=u'chris').first()
|
||||||
|
205
mediagoblin/tests/test_privileges.py
Normal file
205
mediagoblin/tests/test_privileges.py
Normal file
@ -0,0 +1,205 @@
|
|||||||
|
# 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 pytest
|
||||||
|
from datetime import date, timedelta
|
||||||
|
from webtest import AppError
|
||||||
|
|
||||||
|
from mediagoblin.tests.tools import fixture_add_user, fixture_media_entry
|
||||||
|
|
||||||
|
from mediagoblin.db.models import User, UserBan
|
||||||
|
from mediagoblin.tools import template
|
||||||
|
|
||||||
|
from .resources import GOOD_JPG
|
||||||
|
|
||||||
|
class TestPrivilegeFunctionality:
|
||||||
|
|
||||||
|
@pytest.fixture(autouse=True)
|
||||||
|
def _setup(self, test_app):
|
||||||
|
self.test_app = test_app
|
||||||
|
|
||||||
|
fixture_add_user(u'alex',
|
||||||
|
privileges=[u'admin',u'active'])
|
||||||
|
fixture_add_user(u'meow',
|
||||||
|
privileges=[u'moderator',u'active',u'reporter'])
|
||||||
|
fixture_add_user(u'natalie',
|
||||||
|
privileges=[u'active'])
|
||||||
|
self.query_for_users()
|
||||||
|
|
||||||
|
def login(self, username):
|
||||||
|
self.test_app.post(
|
||||||
|
'/auth/login/', {
|
||||||
|
'username': username,
|
||||||
|
'password': 'toast'})
|
||||||
|
self.query_for_users()
|
||||||
|
|
||||||
|
def logout(self):
|
||||||
|
self.test_app.get('/auth/logout/')
|
||||||
|
self.query_for_users()
|
||||||
|
|
||||||
|
def do_post(self, data, *context_keys, **kwargs):
|
||||||
|
url = kwargs.pop('url', '/submit/')
|
||||||
|
do_follow = kwargs.pop('do_follow', False)
|
||||||
|
template.clear_test_template_context()
|
||||||
|
response = self.test_app.post(url, data, **kwargs)
|
||||||
|
if do_follow:
|
||||||
|
response.follow()
|
||||||
|
context_data = template.TEMPLATE_TEST_CONTEXT
|
||||||
|
for key in context_keys:
|
||||||
|
context_data = context_data[key]
|
||||||
|
return response, context_data
|
||||||
|
|
||||||
|
def query_for_users(self):
|
||||||
|
self.admin_user = User.query.filter(User.username==u'alex').first()
|
||||||
|
self.mod_user = User.query.filter(User.username==u'meow').first()
|
||||||
|
self.user = User.query.filter(User.username==u'natalie').first()
|
||||||
|
|
||||||
|
def testUserBanned(self):
|
||||||
|
self.login(u'natalie')
|
||||||
|
uid = self.user.id
|
||||||
|
# First, test what happens when a user is banned indefinitely
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
user_ban = UserBan(user_id=uid,
|
||||||
|
reason=u'Testing whether user is banned',
|
||||||
|
expiration_date=None)
|
||||||
|
user_ban.save()
|
||||||
|
|
||||||
|
response = self.test_app.get('/')
|
||||||
|
assert response.status == "200 OK"
|
||||||
|
assert "You are Banned" in response.body
|
||||||
|
# Then test what happens when that ban has an expiration date which
|
||||||
|
# hasn't happened yet
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
user_ban = UserBan.query.get(uid)
|
||||||
|
user_ban.delete()
|
||||||
|
user_ban = UserBan(user_id=uid,
|
||||||
|
reason=u'Testing whether user is banned',
|
||||||
|
expiration_date= date.today() + timedelta(days=20))
|
||||||
|
user_ban.save()
|
||||||
|
|
||||||
|
response = self.test_app.get('/')
|
||||||
|
assert response.status == "200 OK"
|
||||||
|
assert "You are Banned" in response.body
|
||||||
|
|
||||||
|
# Then test what happens when that ban has an expiration date which
|
||||||
|
# has already happened
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
user_ban = UserBan.query.get(uid)
|
||||||
|
user_ban.delete()
|
||||||
|
exp_date = date.today() - timedelta(days=20)
|
||||||
|
user_ban = UserBan(user_id=uid,
|
||||||
|
reason=u'Testing whether user is banned',
|
||||||
|
expiration_date= exp_date)
|
||||||
|
user_ban.save()
|
||||||
|
|
||||||
|
response = self.test_app.get('/')
|
||||||
|
assert response.status == "302 FOUND"
|
||||||
|
assert not "You are Banned" in response.body
|
||||||
|
|
||||||
|
def testVariousPrivileges(self):
|
||||||
|
# The various actions that require privileges (ex. reporting,
|
||||||
|
# commenting, moderating...) are tested in other tests. This method
|
||||||
|
# will be used to ensure that those actions are impossible for someone
|
||||||
|
# without the proper privileges.
|
||||||
|
# For other tests that show what happens when a user has the proper
|
||||||
|
# privileges, check out:
|
||||||
|
# tests/test_moderation.py moderator
|
||||||
|
# tests/test_notifications.py commenter
|
||||||
|
# tests/test_reporting.py reporter
|
||||||
|
# tests/test_submission.py uploader
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
self.login(u'natalie')
|
||||||
|
|
||||||
|
# First test the get and post requests of submission/uploading
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
with pytest.raises(AppError) as excinfo:
|
||||||
|
response = self.test_app.get('/submit/')
|
||||||
|
assert 'Bad response: 403 FORBIDDEN' in str(excinfo)
|
||||||
|
|
||||||
|
|
||||||
|
with pytest.raises(AppError) as excinfo:
|
||||||
|
response = self.do_post({'upload_files':[('file',GOOD_JPG)],
|
||||||
|
'title':u'Normal Upload 1'},
|
||||||
|
url='/submit/')
|
||||||
|
assert 'Bad response: 403 FORBIDDEN' in str(excinfo)
|
||||||
|
|
||||||
|
# Test that a user cannot comment without the commenter privilege
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
self.query_for_users()
|
||||||
|
|
||||||
|
media_entry = fixture_media_entry(uploader=self.admin_user.id,
|
||||||
|
state=u'processed')
|
||||||
|
|
||||||
|
media_entry_id = media_entry.id
|
||||||
|
media_uri_id = '/u/{0}/m/{1}/'.format(self.admin_user.username,
|
||||||
|
media_entry.id)
|
||||||
|
media_uri_slug = '/u/{0}/m/{1}/'.format(self.admin_user.username,
|
||||||
|
media_entry.slug)
|
||||||
|
response = self.test_app.get(media_uri_slug)
|
||||||
|
assert not "Add a comment" in response.body
|
||||||
|
|
||||||
|
self.query_for_users()
|
||||||
|
with pytest.raises(AppError) as excinfo:
|
||||||
|
response = self.test_app.post(
|
||||||
|
media_uri_id + 'comment/add/',
|
||||||
|
{'comment_content': u'Test comment #42'})
|
||||||
|
assert 'Bad response: 403 FORBIDDEN' in str(excinfo)
|
||||||
|
|
||||||
|
# Test that a user cannot report without the reporter privilege
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
with pytest.raises(AppError) as excinfo:
|
||||||
|
response = self.test_app.get(media_uri_slug+"report/")
|
||||||
|
assert 'Bad response: 403 FORBIDDEN' in str(excinfo)
|
||||||
|
|
||||||
|
with pytest.raises(AppError) as excinfo:
|
||||||
|
response = self.do_post(
|
||||||
|
{'report_reason':u'Testing Reports #1',
|
||||||
|
'reporter_id':u'3'},
|
||||||
|
url=(media_uri_slug+"report/"))
|
||||||
|
assert 'Bad response: 403 FORBIDDEN' in str(excinfo)
|
||||||
|
|
||||||
|
# Test that a user cannot access the moderation pages w/o moderator
|
||||||
|
# or admin privileges
|
||||||
|
#----------------------------------------------------------------------
|
||||||
|
with pytest.raises(AppError) as excinfo:
|
||||||
|
response = self.test_app.get("/mod/users/")
|
||||||
|
assert 'Bad response: 403 FORBIDDEN' in str(excinfo)
|
||||||
|
|
||||||
|
with pytest.raises(AppError) as excinfo:
|
||||||
|
response = self.test_app.get("/mod/reports/")
|
||||||
|
assert 'Bad response: 403 FORBIDDEN' in str(excinfo)
|
||||||
|
|
||||||
|
with pytest.raises(AppError) as excinfo:
|
||||||
|
response = self.test_app.get("/mod/media/")
|
||||||
|
assert 'Bad response: 403 FORBIDDEN' in str(excinfo)
|
||||||
|
|
||||||
|
with pytest.raises(AppError) as excinfo:
|
||||||
|
response = self.test_app.get("/mod/users/1/")
|
||||||
|
assert 'Bad response: 403 FORBIDDEN' in str(excinfo)
|
||||||
|
|
||||||
|
with pytest.raises(AppError) as excinfo:
|
||||||
|
response = self.test_app.get("/mod/reports/1/")
|
||||||
|
assert 'Bad response: 403 FORBIDDEN' in str(excinfo)
|
||||||
|
|
||||||
|
self.query_for_users()
|
||||||
|
|
||||||
|
with pytest.raises(AppError) as excinfo:
|
||||||
|
response, context = self.do_post({'action_to_resolve':[u'takeaway'],
|
||||||
|
'take_away_privileges':[u'active'],
|
||||||
|
'targeted_user':self.admin_user.id},
|
||||||
|
url='/mod/reports/1/')
|
||||||
|
self.query_for_users()
|
||||||
|
assert 'Bad response: 403 FORBIDDEN' in str(excinfo)
|
167
mediagoblin/tests/test_reporting.py
Normal file
167
mediagoblin/tests/test_reporting.py
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
# 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 pytest
|
||||||
|
|
||||||
|
from mediagoblin.tools import template
|
||||||
|
from mediagoblin.tests.tools import (fixture_add_user, fixture_media_entry,
|
||||||
|
fixture_add_comment, fixture_add_comment_report)
|
||||||
|
from mediagoblin.db.models import (MediaReport, CommentReport, User,
|
||||||
|
MediaComment)
|
||||||
|
|
||||||
|
|
||||||
|
class TestReportFiling:
|
||||||
|
@pytest.fixture(autouse=True)
|
||||||
|
def _setup(self, test_app):
|
||||||
|
self.test_app = test_app
|
||||||
|
|
||||||
|
fixture_add_user(u'allie',
|
||||||
|
privileges=[u'reporter',u'active'])
|
||||||
|
fixture_add_user(u'natalie',
|
||||||
|
privileges=[u'active', u'moderator'])
|
||||||
|
|
||||||
|
def login(self, username):
|
||||||
|
self.test_app.post(
|
||||||
|
'/auth/login/', {
|
||||||
|
'username': username,
|
||||||
|
'password': 'toast'})
|
||||||
|
|
||||||
|
def logout(self):
|
||||||
|
self.test_app.get('/auth/logout/')
|
||||||
|
|
||||||
|
def do_post(self, data, *context_keys, **kwargs):
|
||||||
|
url = kwargs.pop('url', '/submit/')
|
||||||
|
do_follow = kwargs.pop('do_follow', False)
|
||||||
|
template.clear_test_template_context()
|
||||||
|
response = self.test_app.post(url, data, **kwargs)
|
||||||
|
if do_follow:
|
||||||
|
response.follow()
|
||||||
|
context_data = template.TEMPLATE_TEST_CONTEXT
|
||||||
|
for key in context_keys:
|
||||||
|
context_data = context_data[key]
|
||||||
|
return response, context_data
|
||||||
|
|
||||||
|
def query_for_users(self):
|
||||||
|
return (User.query.filter(User.username==u'allie').first(),
|
||||||
|
User.query.filter(User.username==u'natalie').first())
|
||||||
|
|
||||||
|
def testMediaReports(self):
|
||||||
|
self.login(u'allie')
|
||||||
|
allie_user, natalie_user = self.query_for_users()
|
||||||
|
allie_id = allie_user.id
|
||||||
|
|
||||||
|
media_entry = fixture_media_entry(uploader=natalie_user.id,
|
||||||
|
state=u'processed')
|
||||||
|
|
||||||
|
mid = media_entry.id
|
||||||
|
media_uri_slug = '/u/{0}/m/{1}/'.format(natalie_user.username,
|
||||||
|
media_entry.slug)
|
||||||
|
|
||||||
|
response = self.test_app.get(media_uri_slug + "report/")
|
||||||
|
assert response.status == "200 OK"
|
||||||
|
|
||||||
|
response, context = self.do_post(
|
||||||
|
{'report_reason':u'Testing Media Report',
|
||||||
|
'reporter_id':unicode(allie_id)},url= media_uri_slug + "report/")
|
||||||
|
|
||||||
|
assert response.status == "302 FOUND"
|
||||||
|
|
||||||
|
media_report = MediaReport.query.first()
|
||||||
|
|
||||||
|
allie_user, natalie_user = self.query_for_users()
|
||||||
|
assert media_report is not None
|
||||||
|
assert media_report.report_content == u'Testing Media Report'
|
||||||
|
assert media_report.reporter_id == allie_id
|
||||||
|
assert media_report.reported_user_id == natalie_user.id
|
||||||
|
assert media_report.created is not None
|
||||||
|
assert media_report.discriminator == 'media_report'
|
||||||
|
|
||||||
|
def testCommentReports(self):
|
||||||
|
self.login(u'allie')
|
||||||
|
allie_user, natalie_user = self.query_for_users()
|
||||||
|
allie_id = allie_user.id
|
||||||
|
|
||||||
|
media_entry = fixture_media_entry(uploader=natalie_user.id,
|
||||||
|
state=u'processed')
|
||||||
|
mid = media_entry.id
|
||||||
|
fixture_add_comment(media_entry=mid,
|
||||||
|
author=natalie_user.id)
|
||||||
|
comment = MediaComment.query.first()
|
||||||
|
|
||||||
|
comment_uri_slug = '/u/{0}/m/{1}/c/{2}/'.format(natalie_user.username,
|
||||||
|
media_entry.slug,
|
||||||
|
comment.id)
|
||||||
|
|
||||||
|
response = self.test_app.get(comment_uri_slug + "report/")
|
||||||
|
assert response.status == "200 OK"
|
||||||
|
|
||||||
|
response, context = self.do_post({
|
||||||
|
'report_reason':u'Testing Comment Report',
|
||||||
|
'reporter_id':unicode(allie_id)},url= comment_uri_slug + "report/")
|
||||||
|
|
||||||
|
assert response.status == "302 FOUND"
|
||||||
|
|
||||||
|
comment_report = CommentReport.query.first()
|
||||||
|
|
||||||
|
allie_user, natalie_user = self.query_for_users()
|
||||||
|
assert comment_report is not None
|
||||||
|
assert comment_report.report_content == u'Testing Comment Report'
|
||||||
|
assert comment_report.reporter_id == allie_id
|
||||||
|
assert comment_report.reported_user_id == natalie_user.id
|
||||||
|
assert comment_report.created is not None
|
||||||
|
assert comment_report.discriminator == 'comment_report'
|
||||||
|
|
||||||
|
def testArchivingReports(self):
|
||||||
|
self.login(u'natalie')
|
||||||
|
allie_user, natalie_user = self.query_for_users()
|
||||||
|
allie_id, natalie_id = allie_user.id, natalie_user.id
|
||||||
|
|
||||||
|
fixture_add_comment(author=allie_user.id,
|
||||||
|
comment=u'Comment will be removed')
|
||||||
|
test_comment = MediaComment.query.filter(
|
||||||
|
MediaComment.author==allie_user.id).first()
|
||||||
|
fixture_add_comment_report(comment=test_comment,
|
||||||
|
reported_user=allie_user,
|
||||||
|
report_content=u'Testing Archived Reports #1',
|
||||||
|
reporter=natalie_user)
|
||||||
|
comment_report = CommentReport.query.filter(
|
||||||
|
CommentReport.reported_user==allie_user).first()
|
||||||
|
|
||||||
|
assert comment_report.report_content == u'Testing Archived Reports #1'
|
||||||
|
response, context = self.do_post(
|
||||||
|
{'action_to_resolve':[u'userban', u'delete'],
|
||||||
|
'targeted_user':allie_user.id,
|
||||||
|
'resolution_content':u'This is a test of archiving reports.'},
|
||||||
|
url='/mod/reports/{0}/'.format(comment_report.id))
|
||||||
|
|
||||||
|
assert response.status == "302 FOUND"
|
||||||
|
allie_user, natalie_user = self.query_for_users()
|
||||||
|
|
||||||
|
archived_report = CommentReport.query.filter(
|
||||||
|
CommentReport.reported_user==allie_user).first()
|
||||||
|
|
||||||
|
assert CommentReport.query.count() != 0
|
||||||
|
assert archived_report is not None
|
||||||
|
assert archived_report.report_content == u'Testing Archived Reports #1'
|
||||||
|
assert archived_report.reporter_id == natalie_id
|
||||||
|
assert archived_report.reported_user_id == allie_id
|
||||||
|
assert archived_report.created is not None
|
||||||
|
assert archived_report.resolved is not None
|
||||||
|
assert archived_report.result == u'''This is a test of archiving reports.
|
||||||
|
natalie banned user allie indefinitely.
|
||||||
|
natalie deleted the comment.'''
|
||||||
|
assert archived_report.discriminator == 'comment_report'
|
||||||
|
|
@ -47,12 +47,22 @@ class TestSubmission:
|
|||||||
|
|
||||||
# TODO: Possibly abstract into a decorator like:
|
# TODO: Possibly abstract into a decorator like:
|
||||||
# @as_authenticated_user('chris')
|
# @as_authenticated_user('chris')
|
||||||
test_user = fixture_add_user()
|
fixture_add_user(privileges=[u'active',u'uploader', u'commenter'])
|
||||||
|
|
||||||
self.test_user = test_user
|
|
||||||
|
|
||||||
self.login()
|
self.login()
|
||||||
|
|
||||||
|
def our_user(self):
|
||||||
|
"""
|
||||||
|
Fetch the user we're submitting with. Every .get() or .post()
|
||||||
|
invalidates the session; this is a hacky workaround.
|
||||||
|
"""
|
||||||
|
#### FIXME: Pytest collects this as a test and runs this.
|
||||||
|
#### ... it shouldn't. At least it passes, but that's
|
||||||
|
#### totally stupid.
|
||||||
|
#### Also if we found a way to make this run it should be a
|
||||||
|
#### property.
|
||||||
|
return User.query.filter(User.username==u'chris').first()
|
||||||
|
|
||||||
def login(self):
|
def login(self):
|
||||||
self.test_app.post(
|
self.test_app.post(
|
||||||
'/auth/login/', {
|
'/auth/login/', {
|
||||||
@ -98,10 +108,10 @@ class TestSubmission:
|
|||||||
def check_normal_upload(self, title, filename):
|
def check_normal_upload(self, title, filename):
|
||||||
response, context = self.do_post({'title': title}, do_follow=True,
|
response, context = self.do_post({'title': title}, do_follow=True,
|
||||||
**self.upload_data(filename))
|
**self.upload_data(filename))
|
||||||
self.check_url(response, '/u/{0}/'.format(self.test_user.username))
|
self.check_url(response, '/u/{0}/'.format(self.our_user().username))
|
||||||
assert 'mediagoblin/user_pages/user.html' in context
|
assert 'mediagoblin/user_pages/user.html' in context
|
||||||
# Make sure the media view is at least reachable, logged in...
|
# Make sure the media view is at least reachable, logged in...
|
||||||
url = '/u/{0}/m/{1}/'.format(self.test_user.username,
|
url = '/u/{0}/m/{1}/'.format(self.our_user().username,
|
||||||
title.lower().replace(' ', '-'))
|
title.lower().replace(' ', '-'))
|
||||||
self.test_app.get(url)
|
self.test_app.get(url)
|
||||||
# ... and logged out too.
|
# ... and logged out too.
|
||||||
@ -148,7 +158,7 @@ class TestSubmission:
|
|||||||
response, context = self.do_post({'title': u'Normal upload 3 (pdf)'},
|
response, context = self.do_post({'title': u'Normal upload 3 (pdf)'},
|
||||||
do_follow=True,
|
do_follow=True,
|
||||||
**self.upload_data(GOOD_PDF))
|
**self.upload_data(GOOD_PDF))
|
||||||
self.check_url(response, '/u/{0}/'.format(self.test_user.username))
|
self.check_url(response, '/u/{0}/'.format(self.our_user().username))
|
||||||
assert 'mediagoblin/user_pages/user.html' in context
|
assert 'mediagoblin/user_pages/user.html' in context
|
||||||
|
|
||||||
def test_default_upload_limits(self):
|
def test_default_upload_limits(self):
|
||||||
@ -264,7 +274,7 @@ class TestSubmission:
|
|||||||
# render and post to the edit page.
|
# render and post to the edit page.
|
||||||
edit_url = request.urlgen(
|
edit_url = request.urlgen(
|
||||||
'mediagoblin.edit.edit_media',
|
'mediagoblin.edit.edit_media',
|
||||||
user=self.test_user.username, media_id=media_id)
|
user=self.our_user().username, media_id=media_id)
|
||||||
self.test_app.get(edit_url)
|
self.test_app.get(edit_url)
|
||||||
self.test_app.post(edit_url,
|
self.test_app.post(edit_url,
|
||||||
{'title': u'Balanced Goblin',
|
{'title': u'Balanced Goblin',
|
||||||
@ -277,7 +287,7 @@ class TestSubmission:
|
|||||||
self.check_comments(request, media_id, 0)
|
self.check_comments(request, media_id, 0)
|
||||||
comment_url = request.urlgen(
|
comment_url = request.urlgen(
|
||||||
'mediagoblin.user_pages.media_post_comment',
|
'mediagoblin.user_pages.media_post_comment',
|
||||||
user=self.test_user.username, media_id=media_id)
|
user=self.our_user().username, media_id=media_id)
|
||||||
response = self.do_post({'comment_content': 'i love this test'},
|
response = self.do_post({'comment_content': 'i love this test'},
|
||||||
url=comment_url, do_follow=True)[0]
|
url=comment_url, do_follow=True)[0]
|
||||||
self.check_comments(request, media_id, 1)
|
self.check_comments(request, media_id, 1)
|
||||||
@ -286,7 +296,7 @@ class TestSubmission:
|
|||||||
# ---------------------------------------------------
|
# ---------------------------------------------------
|
||||||
delete_url = request.urlgen(
|
delete_url = request.urlgen(
|
||||||
'mediagoblin.user_pages.media_confirm_delete',
|
'mediagoblin.user_pages.media_confirm_delete',
|
||||||
user=self.test_user.username, media_id=media_id)
|
user=self.our_user().username, media_id=media_id)
|
||||||
# Empty data means don't confirm
|
# Empty data means don't confirm
|
||||||
response = self.do_post({}, do_follow=True, url=delete_url)[0]
|
response = self.do_post({}, do_follow=True, url=delete_url)[0]
|
||||||
media = self.check_media(request, {'title': u'Balanced Goblin'}, 1)
|
media = self.check_media(request, {'title': u'Balanced Goblin'}, 1)
|
||||||
@ -359,7 +369,7 @@ class TestSubmission:
|
|||||||
# they'll be caught as failures during the processing step.
|
# they'll be caught as failures during the processing step.
|
||||||
response, context = self.do_post({'title': title}, do_follow=True,
|
response, context = self.do_post({'title': title}, do_follow=True,
|
||||||
**self.upload_data(filename))
|
**self.upload_data(filename))
|
||||||
self.check_url(response, '/u/{0}/'.format(self.test_user.username))
|
self.check_url(response, '/u/{0}/'.format(self.our_user().username))
|
||||||
entry = mg_globals.database.MediaEntry.query.filter_by(title=title).first()
|
entry = mg_globals.database.MediaEntry.query.filter_by(title=title).first()
|
||||||
assert entry.state == 'failed'
|
assert entry.state == 'failed'
|
||||||
assert entry.fail_error == u'mediagoblin.processing:BadMediaFail'
|
assert entry.fail_error == u'mediagoblin.processing:BadMediaFail'
|
||||||
|
@ -25,7 +25,7 @@ from webtest import TestApp
|
|||||||
|
|
||||||
from mediagoblin import mg_globals
|
from mediagoblin import mg_globals
|
||||||
from mediagoblin.db.models import User, MediaEntry, Collection, MediaComment, \
|
from mediagoblin.db.models import User, MediaEntry, Collection, MediaComment, \
|
||||||
CommentSubscription, CommentNotification
|
CommentSubscription, CommentNotification, Privilege, CommentReport
|
||||||
from mediagoblin.tools import testing
|
from mediagoblin.tools import testing
|
||||||
from mediagoblin.init.config import read_mediagoblin_config
|
from mediagoblin.init.config import read_mediagoblin_config
|
||||||
from mediagoblin.db.base import Session
|
from mediagoblin.db.base import Session
|
||||||
@ -33,6 +33,8 @@ from mediagoblin.meddleware import BaseMeddleware
|
|||||||
from mediagoblin.auth import gen_password_hash
|
from mediagoblin.auth import gen_password_hash
|
||||||
from mediagoblin.gmg_commands.dbupdate import run_dbupdate
|
from mediagoblin.gmg_commands.dbupdate import run_dbupdate
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
|
||||||
MEDIAGOBLIN_TEST_DB_NAME = u'__mediagoblin_tests__'
|
MEDIAGOBLIN_TEST_DB_NAME = u'__mediagoblin_tests__'
|
||||||
TEST_SERVER_CONFIG = pkg_resources.resource_filename(
|
TEST_SERVER_CONFIG = pkg_resources.resource_filename(
|
||||||
@ -133,7 +135,6 @@ def get_app(request, paste_config=None, mgoblin_config=None):
|
|||||||
mg_globals.app.meddleware.insert(0, TestingMeddleware(mg_globals.app))
|
mg_globals.app.meddleware.insert(0, TestingMeddleware(mg_globals.app))
|
||||||
|
|
||||||
app = TestApp(test_app)
|
app = TestApp(test_app)
|
||||||
|
|
||||||
return app
|
return app
|
||||||
|
|
||||||
|
|
||||||
@ -170,7 +171,7 @@ def assert_db_meets_expected(db, expected):
|
|||||||
|
|
||||||
|
|
||||||
def fixture_add_user(username=u'chris', password=u'toast',
|
def fixture_add_user(username=u'chris', password=u'toast',
|
||||||
active_user=True, wants_comment_notification=True):
|
privileges=[], wants_comment_notification=True):
|
||||||
# Reuse existing user or create a new one
|
# Reuse existing user or create a new one
|
||||||
test_user = User.query.filter_by(username=username).first()
|
test_user = User.query.filter_by(username=username).first()
|
||||||
if test_user is None:
|
if test_user is None:
|
||||||
@ -179,14 +180,13 @@ def fixture_add_user(username=u'chris', password=u'toast',
|
|||||||
test_user.email = username + u'@example.com'
|
test_user.email = username + u'@example.com'
|
||||||
if password is not None:
|
if password is not None:
|
||||||
test_user.pw_hash = gen_password_hash(password)
|
test_user.pw_hash = gen_password_hash(password)
|
||||||
if active_user:
|
|
||||||
test_user.email_verified = True
|
|
||||||
test_user.status = u'active'
|
|
||||||
|
|
||||||
test_user.wants_comment_notification = wants_comment_notification
|
test_user.wants_comment_notification = wants_comment_notification
|
||||||
|
for privilege in privileges:
|
||||||
|
query = Privilege.query.filter(Privilege.privilege_name==privilege)
|
||||||
|
if query.count():
|
||||||
|
test_user.all_privileges.append(query.one())
|
||||||
|
|
||||||
test_user.save()
|
test_user.save()
|
||||||
|
|
||||||
# Reload
|
# Reload
|
||||||
test_user = User.query.filter_by(username=username).first()
|
test_user = User.query.filter_by(username=username).first()
|
||||||
|
|
||||||
@ -314,3 +314,32 @@ def fixture_add_comment(author=None, media_entry=None, comment=None):
|
|||||||
|
|
||||||
return comment
|
return comment
|
||||||
|
|
||||||
|
def fixture_add_comment_report(comment=None, reported_user=None,
|
||||||
|
reporter=None, created=None, report_content=None):
|
||||||
|
if comment is None:
|
||||||
|
comment = fixture_add_comment()
|
||||||
|
|
||||||
|
if reported_user is None:
|
||||||
|
reported_user = fixture_add_user()
|
||||||
|
|
||||||
|
if reporter is None:
|
||||||
|
reporter = fixture_add_user()
|
||||||
|
|
||||||
|
if created is None:
|
||||||
|
created=datetime.now()
|
||||||
|
|
||||||
|
if report_content is None:
|
||||||
|
report_content = \
|
||||||
|
'Auto-generated test report'
|
||||||
|
|
||||||
|
comment_report = CommentReport(comment=comment,
|
||||||
|
reported_user = reported_user,
|
||||||
|
reporter = reporter,
|
||||||
|
created = created,
|
||||||
|
report_content=report_content)
|
||||||
|
|
||||||
|
comment_report.save()
|
||||||
|
|
||||||
|
Session.expunge(comment_report)
|
||||||
|
|
||||||
|
return comment_report
|
||||||
|
@ -21,6 +21,8 @@ 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, User
|
||||||
|
from datetime import date
|
||||||
|
|
||||||
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"""
|
||||||
@ -50,7 +52,8 @@ def render_400(request, err_msg=None):
|
|||||||
_ = pass_to_ugettext
|
_ = pass_to_ugettext
|
||||||
title = _("Bad Request")
|
title = _("Bad Request")
|
||||||
if err_msg is None:
|
if err_msg is None:
|
||||||
err_msg = _("The request sent to the server is invalid, please double check it")
|
err_msg = _("The request sent to the server is invalid, \
|
||||||
|
please double check it")
|
||||||
|
|
||||||
return render_error(request, 400, title, err_msg)
|
return render_error(request, 400, title, err_msg)
|
||||||
|
|
||||||
@ -71,6 +74,21 @@ 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)
|
||||||
|
if (user_ban.expiration_date is not None and
|
||||||
|
date.today()>user_ban.expiration_date):
|
||||||
|
|
||||||
|
user_ban.delete()
|
||||||
|
return redirect(request,
|
||||||
|
'index')
|
||||||
|
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
|
||||||
|
@ -49,3 +49,15 @@ class MediaCollectForm(wtforms.Form):
|
|||||||
description=_("""You can use
|
description=_("""You can use
|
||||||
<a href="http://daringfireball.net/projects/markdown/basics" target="_blank">
|
<a href="http://daringfireball.net/projects/markdown/basics" target="_blank">
|
||||||
Markdown</a> for formatting."""))
|
Markdown</a> for formatting."""))
|
||||||
|
|
||||||
|
class CommentReportForm(wtforms.Form):
|
||||||
|
report_reason = wtforms.TextAreaField(
|
||||||
|
_('Reason for Reporting'),
|
||||||
|
[wtforms.validators.Required()])
|
||||||
|
reporter_id = wtforms.HiddenField('')
|
||||||
|
|
||||||
|
class MediaReportForm(wtforms.Form):
|
||||||
|
report_reason = wtforms.TextAreaField(
|
||||||
|
_('Reason for Reporting'),
|
||||||
|
[wtforms.validators.Required()])
|
||||||
|
reporter_id = wtforms.HiddenField('')
|
||||||
|
@ -19,7 +19,9 @@ from mediagoblin.tools.template import render_template
|
|||||||
from mediagoblin.tools.translate import pass_to_ugettext as _
|
from mediagoblin.tools.translate import pass_to_ugettext as _
|
||||||
from mediagoblin import mg_globals
|
from mediagoblin import mg_globals
|
||||||
from mediagoblin.db.base import Session
|
from mediagoblin.db.base import Session
|
||||||
from mediagoblin.db.models import CollectionItem
|
from mediagoblin.db.models import (CollectionItem, MediaReport, CommentReport,
|
||||||
|
MediaComment, MediaEntry)
|
||||||
|
from mediagoblin.user_pages import forms as user_forms
|
||||||
|
|
||||||
|
|
||||||
def send_comment_email(user, comment, media, request):
|
def send_comment_email(user, comment, media, request):
|
||||||
@ -75,3 +77,44 @@ def add_media_to_collection(collection, media, note=None, commit=True):
|
|||||||
|
|
||||||
if commit:
|
if commit:
|
||||||
Session.commit()
|
Session.commit()
|
||||||
|
|
||||||
|
def build_report_object(report_form, media_entry=None, comment=None):
|
||||||
|
"""
|
||||||
|
This function is used to convert a form object (from a User filing a
|
||||||
|
report) into either a MediaReport or CommentReport object.
|
||||||
|
|
||||||
|
:param report_form A MediaReportForm or a CommentReportForm object
|
||||||
|
with valid information from a POST request.
|
||||||
|
:param media_entry A MediaEntry object. The MediaEntry being repo-
|
||||||
|
-rted by a MediaReport. In a CommentReport,
|
||||||
|
this will be None.
|
||||||
|
:param comment A MediaComment object. The MediaComment being
|
||||||
|
reported by a CommentReport. In a MediaReport
|
||||||
|
this will be None.
|
||||||
|
|
||||||
|
:returns A MediaReport object if a valid MediaReportForm is
|
||||||
|
passed as kwarg media_entry. This MediaReport has
|
||||||
|
not been saved.
|
||||||
|
:returns A CommentReport object if a valid CommentReportForm
|
||||||
|
is passed as kwarg comment. This CommentReport
|
||||||
|
has not been saved.
|
||||||
|
:returns None if the form_dict is invalid.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if report_form.validate() and comment is not None:
|
||||||
|
report_object = CommentReport()
|
||||||
|
report_object.comment_id = comment.id
|
||||||
|
report_object.reported_user_id = MediaComment.query.get(
|
||||||
|
comment.id).get_author.id
|
||||||
|
elif report_form.validate() and media_entry is not None:
|
||||||
|
report_object = MediaReport()
|
||||||
|
report_object.media_entry_id = media_entry.id
|
||||||
|
report_object.reported_user_id = MediaEntry.query.get(
|
||||||
|
media_entry.id).get_uploader.id
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
report_object.report_content = report_form.report_reason.data
|
||||||
|
report_object.reporter_id = report_form.reporter_id.data
|
||||||
|
return report_object
|
||||||
|
|
||||||
|
@ -23,6 +23,10 @@ add_route('mediagoblin.user_pages.media_home',
|
|||||||
'/u/<string:user>/m/<string:media>/',
|
'/u/<string:user>/m/<string:media>/',
|
||||||
'mediagoblin.user_pages.views:media_home')
|
'mediagoblin.user_pages.views:media_home')
|
||||||
|
|
||||||
|
add_route('mediagoblin.user_pages.media_home.report_media',
|
||||||
|
'/u/<string:user>/m/<string:media>/report/',
|
||||||
|
'mediagoblin.user_pages.views:file_a_report')
|
||||||
|
|
||||||
add_route('mediagoblin.user_pages.media_confirm_delete',
|
add_route('mediagoblin.user_pages.media_confirm_delete',
|
||||||
'/u/<string:user>/m/<int:media_id>/confirm-delete/',
|
'/u/<string:user>/m/<int:media_id>/confirm-delete/',
|
||||||
'mediagoblin.user_pages.views:media_confirm_delete')
|
'mediagoblin.user_pages.views:media_confirm_delete')
|
||||||
@ -44,6 +48,10 @@ add_route('mediagoblin.user_pages.media_home.view_comment',
|
|||||||
'/u/<string:user>/m/<string:media>/c/<int:comment>/',
|
'/u/<string:user>/m/<string:media>/c/<int:comment>/',
|
||||||
'mediagoblin.user_pages.views:media_home')
|
'mediagoblin.user_pages.views:media_home')
|
||||||
|
|
||||||
|
add_route('mediagoblin.user_pages.media_home.report_comment',
|
||||||
|
'/u/<string:user>/m/<string:media>/c/<int:comment>/report/',
|
||||||
|
'mediagoblin.user_pages.views:file_a_report')
|
||||||
|
|
||||||
# User's tags gallery
|
# User's tags gallery
|
||||||
add_route('mediagoblin.user_pages.user_tag_gallery',
|
add_route('mediagoblin.user_pages.user_tag_gallery',
|
||||||
'/u/<string:user>/tag/<string:tag>/',
|
'/u/<string:user>/tag/<string:tag>/',
|
||||||
|
@ -27,13 +27,16 @@ from mediagoblin.tools.text import cleaned_markdown_conversion
|
|||||||
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 add_media_to_collection
|
from mediagoblin.user_pages.lib import (send_comment_email,
|
||||||
|
add_media_to_collection, build_report_object)
|
||||||
from mediagoblin.notifications import trigger_notification, \
|
from mediagoblin.notifications import trigger_notification, \
|
||||||
add_comment_subscription, mark_comment_notification_seen
|
add_comment_subscription, mark_comment_notification_seen
|
||||||
|
|
||||||
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,
|
get_media_entry_by_id, user_has_privilege, user_not_banned,
|
||||||
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_optional_media_comment_by_id, allow_reporting)
|
||||||
|
|
||||||
from werkzeug.contrib.atom import AtomFeed
|
from werkzeug.contrib.atom import AtomFeed
|
||||||
from werkzeug.exceptions import MethodNotAllowed
|
from werkzeug.exceptions import MethodNotAllowed
|
||||||
@ -43,14 +46,14 @@ from werkzeug.wrappers import Response
|
|||||||
_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()"""
|
||||||
user = User.query.filter_by(username=request.matchdict['user']).first()
|
user = User.query.filter_by(username=request.matchdict['user']).first()
|
||||||
if not user:
|
if not user:
|
||||||
return render_404(request)
|
return render_404(request)
|
||||||
elif user.status != u'active':
|
elif not user.has_privilege(u'active'):
|
||||||
return render_to_response(
|
return render_to_response(
|
||||||
request,
|
request,
|
||||||
'mediagoblin/user_pages/user_nonactive.html',
|
'mediagoblin/user_pages/user_nonactive.html',
|
||||||
@ -79,7 +82,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 +117,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):
|
||||||
@ -154,7 +157,7 @@ def media_home(request, media, page, **kwargs):
|
|||||||
|
|
||||||
|
|
||||||
@get_media_entry_by_id
|
@get_media_entry_by_id
|
||||||
@require_active_login
|
@user_has_privilege(u'commenter')
|
||||||
def media_post_comment(request, media):
|
def media_post_comment(request, media):
|
||||||
"""
|
"""
|
||||||
recieves POST from a MediaEntry() comment form, saves the comment.
|
recieves POST from a MediaEntry() comment form, saves the comment.
|
||||||
@ -165,7 +168,6 @@ def media_post_comment(request, media):
|
|||||||
comment = request.db.MediaComment()
|
comment = request.db.MediaComment()
|
||||||
comment.media_entry = media.id
|
comment.media_entry = media.id
|
||||||
comment.author = request.user.id
|
comment.author = request.user.id
|
||||||
print request.form['comment_content']
|
|
||||||
comment.content = unicode(request.form['comment_content'])
|
comment.content = unicode(request.form['comment_content'])
|
||||||
|
|
||||||
# Show error message if commenting is disabled.
|
# Show error message if commenting is disabled.
|
||||||
@ -205,6 +207,7 @@ def media_preview_comment(request):
|
|||||||
|
|
||||||
return Response(json.dumps(cleancomment))
|
return Response(json.dumps(cleancomment))
|
||||||
|
|
||||||
|
@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):
|
||||||
@ -316,7 +319,7 @@ def media_confirm_delete(request, media):
|
|||||||
_("The media was not deleted because you didn't check that you were sure."))
|
_("The media was not deleted because you didn't check that you were sure."))
|
||||||
return redirect_obj(request, media)
|
return redirect_obj(request, media)
|
||||||
|
|
||||||
if ((request.user.is_admin and
|
if ((request.user.has_privilege(u'admin') and
|
||||||
request.user.id != media.uploader)):
|
request.user.id != media.uploader)):
|
||||||
messages.add_message(
|
messages.add_message(
|
||||||
request, messages.WARNING,
|
request, messages.WARNING,
|
||||||
@ -329,7 +332,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):
|
||||||
@ -359,7 +362,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"""
|
||||||
@ -402,7 +405,7 @@ def collection_item_confirm_remove(request, collection_item):
|
|||||||
|
|
||||||
return redirect_obj(request, collection)
|
return redirect_obj(request, collection)
|
||||||
|
|
||||||
if ((request.user.is_admin and
|
if ((request.user.has_privilege(u'admin') and
|
||||||
request.user.id != collection_item.in_collection.creator)):
|
request.user.id != collection_item.in_collection.creator)):
|
||||||
messages.add_message(
|
messages.add_message(
|
||||||
request, messages.WARNING,
|
request, messages.WARNING,
|
||||||
@ -450,7 +453,7 @@ def collection_confirm_delete(request, collection):
|
|||||||
|
|
||||||
return redirect_obj(request, collection)
|
return redirect_obj(request, collection)
|
||||||
|
|
||||||
if ((request.user.is_admin and
|
if ((request.user.has_privilege(u'admin') and
|
||||||
request.user.id != collection.creator)):
|
request.user.id != collection.creator)):
|
||||||
messages.add_message(
|
messages.add_message(
|
||||||
request, messages.WARNING,
|
request, messages.WARNING,
|
||||||
@ -472,9 +475,8 @@ def atom_feed(request):
|
|||||||
generates the atom feed with the newest images
|
generates the atom feed with the newest images
|
||||||
"""
|
"""
|
||||||
user = User.query.filter_by(
|
user = User.query.filter_by(
|
||||||
username = request.matchdict['user'],
|
username = request.matchdict['user']).first()
|
||||||
status = u'active').first()
|
if not user or not user.has_privilege(u'active'):
|
||||||
if not user:
|
|
||||||
return render_404(request)
|
return render_404(request)
|
||||||
|
|
||||||
cursor = MediaEntry.query.filter_by(
|
cursor = MediaEntry.query.filter_by(
|
||||||
@ -535,9 +537,8 @@ def collection_atom_feed(request):
|
|||||||
generates the atom feed with the newest images from a collection
|
generates the atom feed with the newest images from a collection
|
||||||
"""
|
"""
|
||||||
user = User.query.filter_by(
|
user = User.query.filter_by(
|
||||||
username = request.matchdict['user'],
|
username = request.matchdict['user']).first()
|
||||||
status = u'active').first()
|
if not user or not user.has_privilege(u'active'):
|
||||||
if not user:
|
|
||||||
return render_404(request)
|
return render_404(request)
|
||||||
|
|
||||||
collection = Collection.query.filter_by(
|
collection = Collection.query.filter_by(
|
||||||
@ -599,7 +600,6 @@ def collection_atom_feed(request):
|
|||||||
|
|
||||||
return feed.get_response()
|
return feed.get_response()
|
||||||
|
|
||||||
|
|
||||||
@require_active_login
|
@require_active_login
|
||||||
def processing_panel(request):
|
def processing_panel(request):
|
||||||
"""
|
"""
|
||||||
@ -611,7 +611,7 @@ def processing_panel(request):
|
|||||||
#
|
#
|
||||||
# Make sure we have permission to access this user's panel. Only
|
# Make sure we have permission to access this user's panel. Only
|
||||||
# admins and this user herself should be able to do so.
|
# admins and this user herself should be able to do so.
|
||||||
if not (user.id == request.user.id or request.user.is_admin):
|
if not (user.id == request.user.id or request.user.has_privilege(u'admin')):
|
||||||
# No? Simply redirect to this user's homepage.
|
# No? Simply redirect to this user's homepage.
|
||||||
return redirect(
|
return redirect(
|
||||||
request, 'mediagoblin.user_pages.user_home',
|
request, 'mediagoblin.user_pages.user_home',
|
||||||
@ -643,3 +643,44 @@ def processing_panel(request):
|
|||||||
'processing_entries': processing_entries,
|
'processing_entries': processing_entries,
|
||||||
'failed_entries': failed_entries,
|
'failed_entries': failed_entries,
|
||||||
'processed_entries': processed_entries})
|
'processed_entries': processed_entries})
|
||||||
|
|
||||||
|
@allow_reporting
|
||||||
|
@get_user_media_entry
|
||||||
|
@user_has_privilege(u'reporter')
|
||||||
|
@get_optional_media_comment_by_id
|
||||||
|
def file_a_report(request, media, comment):
|
||||||
|
"""
|
||||||
|
This view handles the filing of a MediaReport or a CommentReport.
|
||||||
|
"""
|
||||||
|
if comment is not None:
|
||||||
|
if not comment.get_media_entry.id == media.id:
|
||||||
|
return render_404(request)
|
||||||
|
|
||||||
|
form = user_forms.CommentReportForm(request.form)
|
||||||
|
context = {'media': media,
|
||||||
|
'comment':comment,
|
||||||
|
'form':form}
|
||||||
|
else:
|
||||||
|
form = user_forms.MediaReportForm(request.form)
|
||||||
|
context = {'media': media,
|
||||||
|
'form':form}
|
||||||
|
form.reporter_id.data = request.user.id
|
||||||
|
|
||||||
|
|
||||||
|
if request.method == "POST":
|
||||||
|
report_object = build_report_object(form,
|
||||||
|
media_entry=media,
|
||||||
|
comment=comment)
|
||||||
|
|
||||||
|
# if the object was built successfully, report_table will not be None
|
||||||
|
if report_object:
|
||||||
|
report_object.save()
|
||||||
|
return redirect(
|
||||||
|
request,
|
||||||
|
'index')
|
||||||
|
|
||||||
|
|
||||||
|
return render_to_response(
|
||||||
|
request,
|
||||||
|
'mediagoblin/user_pages/report.html',
|
||||||
|
context)
|
||||||
|
@ -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').\
|
||||||
@ -44,3 +44,7 @@ def simple_template_render(request):
|
|||||||
template_name = request.matchdict['template']
|
template_name = request.matchdict['template']
|
||||||
return render_to_response(
|
return render_to_response(
|
||||||
request, template_name, {})
|
request, template_name, {})
|
||||||
|
|
||||||
|
def terms_of_service(request):
|
||||||
|
return render_to_response(request,
|
||||||
|
'mediagoblin/terms_of_service.html', {})
|
||||||
|
Loading…
x
Reference in New Issue
Block a user