diff options
author | tilly-Q <nattilypigeonfowl@gmail.com> | 2013-06-24 16:35:31 -0700 |
---|---|---|
committer | tilly-Q <nattilypigeonfowl@gmail.com> | 2013-06-24 16:35:31 -0700 |
commit | 30a9fe7c1cf128fdf413797a2b2edac2d5439bc2 (patch) | |
tree | ca7f8617751381bcef931bc3ff0cb7f74342eeca | |
parent | 25aad338d4921ec76484c6d2af5e40c97904917d (diff) | |
download | mediagoblin-30a9fe7c1cf128fdf413797a2b2edac2d5439bc2.tar.lz mediagoblin-30a9fe7c1cf128fdf413797a2b2edac2d5439bc2.tar.xz mediagoblin-30a9fe7c1cf128fdf413797a2b2edac2d5439bc2.zip |
This is the first stage of my project of implenting admin/moderator functiona-
lity. At this point, I have finished all the of basic work with the models! I
still need to do some tightening of their documentation, but they seem to be
working well.
Working with Models
========================================
--\ mediagoblin/db/models.py
--| Added in the Report model and table. This model is strictly a parent
----| Added in the CommentReport model which holds information about a report
| filed against a comment. This class inherits from Report.
----| Added in the MediaReport model which holds information about a report f-
| -iled against a media entry. This class inherits from Report.
--| Added in a UserBan model and table. This model is in a one to one relatio-
| -nship with User. This object acts as a marker for whether a user is banned
| or not.
--| Added in a Group model. These objects are in a many-to-many relationship
| with User to explain which privileges a User has.
----| Added in GroupUserAssociation which is a table used to hold this many to
| many relationship between Group & User.
--\ mediagoblin/db/migrations.py
--| Added in the migrations for all of the additions to models
--| Added UserBan_v0
--| Added Report_v0
----| Added CommentReport_v0
----| Added MediaReport_v0
--| Added Group_v0
----| Added GroupUserAssociation_v0
Working with Templates, Views, and Routing
===============================================
>>> Reporting a Comment or a MediaEntry
--\ mediagoblin/user_pages/views.py
--| Added in the function file_a_report to allow user to file reports against
| MediaEntries or Comments. Handles GET and POST requests.
--| Added in the function file_a_comment_report which uses file_a_report but
| also catches appropriate information for comment_ids. I may be able to do
| this more eloquently with decorators.
--\ mediagoblin/user_pages/routing.py
--| Added in route 'mediagoblin.user_pages.media_home.report_media'
| (linked to address /u/<user>/m/<media>/report/ )
--| Added in route ''mediagoblin.user_pages.media_home.report_comment'
| (linked to address /u/<user>/m/<media>/c/<comment>/report/ )
--\ mediagoblin/templates/mediagoblin/user_pages/report.html
--| I created this file to handle the filing of a report.
--\ mediagoblin/templates/mediagoblin/user_pages/media.html
--| Modified this file to add in links allowing users to report either media
| or comments.
--\ mediagoblin/user_pages/lib.py
--| Added in build_report_form which processes data as either a CommentReport or
| a MediaReport depending on which parameters are present
--\ mediagoblin/user_pages/forms.py
--| Added in CommentReportForm
--| Added in MediaReportForm
--| note: ReportForm is vestigial to an earlier strategy I used and I'll remove it
| promptly
--\ mediagoblin/decorators.py
--| Added in 'get_media_comment_by_id' for use in mediagoblin/user_pages/views.py
>>> New Admin Panels
--\ mediagoblin/admin/views.py
--| Added in the function admin_users_panel
--| Added in the function admin_reports_panel
--\ mediagoblin/admin/routing.py
--| Added in route 'mediagoblin.admin.users'
| (linked to address '/a/users')
--| Added in route 'mediagoblin.admin.reports'
| (linked to address '/a/reports/')
--\ mediagoblin/templates/admin/user.html
--| Created this file as a template for monitoring users
--\ mediagoblin/templates/admin/report.html
--| Created this file as a template for monitoring reports filed against media or
| comments
-rw-r--r-- | mediagoblin/admin/routing.py | 8 | ||||
-rw-r--r-- | mediagoblin/admin/views.py | 39 | ||||
-rw-r--r-- | mediagoblin/db/migrations.py | 69 | ||||
-rw-r--r-- | mediagoblin/db/models.py | 88 | ||||
-rw-r--r-- | mediagoblin/decorators.py | 20 | ||||
-rw-r--r-- | mediagoblin/templates/mediagoblin/admin/report.html | 95 | ||||
-rw-r--r-- | mediagoblin/templates/mediagoblin/admin/user.html | 54 | ||||
-rw-r--r-- | mediagoblin/templates/mediagoblin/user_pages/media.html | 22 | ||||
-rw-r--r-- | mediagoblin/templates/mediagoblin/user_pages/report.html | 75 | ||||
-rw-r--r-- | mediagoblin/user_pages/forms.py | 16 | ||||
-rw-r--r-- | mediagoblin/user_pages/lib.py | 31 | ||||
-rw-r--r-- | mediagoblin/user_pages/routing.py | 8 | ||||
-rw-r--r-- | mediagoblin/user_pages/views.py | 33 |
13 files changed, 548 insertions, 10 deletions
diff --git a/mediagoblin/admin/routing.py b/mediagoblin/admin/routing.py index 29515f12..d5edac0f 100644 --- a/mediagoblin/admin/routing.py +++ b/mediagoblin/admin/routing.py @@ -17,4 +17,10 @@ admin_routes = [ ('mediagoblin.admin.panel', '/panel', - 'mediagoblin.admin.views:admin_processing_panel')] + 'mediagoblin.admin.views:admin_processing_panel'), + ('mediagoblin.admin.users', + '/users', + 'mediagoblin.admin.views:admin_users_panel'), + ('mediagoblin.admin.reports', + '/reports', + 'mediagoblin.admin.views:admin_reports_panel')] diff --git a/mediagoblin/admin/views.py b/mediagoblin/admin/views.py index 22ca74a3..faa8603a 100644 --- a/mediagoblin/admin/views.py +++ b/mediagoblin/admin/views.py @@ -16,7 +16,7 @@ from werkzeug.exceptions import Forbidden -from mediagoblin.db.models import MediaEntry +from mediagoblin.db.models import MediaEntry, User, MediaComment, CommentReport, ReportBase from mediagoblin.decorators import require_active_login from mediagoblin.tools.response import render_to_response @@ -46,3 +46,40 @@ def admin_processing_panel(request): {'processing_entries': processing_entries, 'failed_entries': failed_entries, 'processed_entries': processed_entries}) + +@require_active_login +def admin_users_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() + + user_list = User.query + + # Render to response + return render_to_response( + request, + 'mediagoblin/admin/user.html', + {'user_list': user_list}) + +@require_active_login +def admin_reports_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() + + report_list = ReportBase.query.filter(ReportBase.resolved==None).order_by(ReportBase.created.desc()).limit(10) + closed_report_list = ReportBase.query.filter(ReportBase.resolved!=None).order_by(ReportBase.created.desc()).limit(10) + + # Render to response + return render_to_response( + request, + 'mediagoblin/admin/report.html', + {'report_list':report_list, + 'closed_report_list':closed_report_list}) + diff --git a/mediagoblin/db/migrations.py b/mediagoblin/db/migrations.py index 2c553396..110a48d4 100644 --- a/mediagoblin/db/migrations.py +++ b/mediagoblin/db/migrations.py @@ -26,7 +26,7 @@ from sqlalchemy.sql import and_ from migrate.changeset.constraint import UniqueConstraint from mediagoblin.db.migration_tools import RegisterMigration, inspect_table -from mediagoblin.db.models import MediaEntry, Collection, User +from mediagoblin.db.models import MediaEntry, Collection, User, MediaComment, Group MIGRATIONS = {} @@ -287,3 +287,70 @@ def unique_collections_slug(db): constraint.create() 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) + created = Column(DateTime, nullable=False, default=datetime.datetime.now) + resolved = Column(DateTime) + discriminator = Column('type', Unicode(50)) + __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=False) + +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=False) + +@RegisterMigration(11, MIGRATIONS) +def create_report_tables(db): + ReportBase_v0.__table__.create(db.bind) + CommentReport_v0.__table__.create(db.bind) + MediaReport_v0.__table__.create(db.bind) + db.commit() + +class UserBan_v0(declarative_base()): + __tablename__ = 'core__user_bans' + user_id = Column('id',Integer, ForeignKey(User.id), nullable=False, + primary_key=True) + expiration_date = Column(DateTime) + reason = Column(UnicodeText, nullable=False) + +class Group_v0(declarative_base()): + __tablename__ = 'core__groups' + id = Column(Integer, nullable=False, primary_key=True) + group_name = Column(Unicode, nullable=False) + +class GroupUserAssociation_v0(declarative_base()): + __tablename__ = 'core__group_user_associations' + + group_id = Column('core__group_id', Integer, ForeignKey(User.id), primary_key=True) + user_id = Column('core__user_id', Integer, ForeignKey(Group.id), primary_key=True) + + + +@RegisterMigration(12, MIGRATIONS) +def create_banned_and_group_tables(db): + UserBan_v0.__table__.create(db.bind) + Group_v0.__table__.create(db.bind) + GroupUserAssociation_v0.__table__.create(db.bind) + db.commit() + + + diff --git a/mediagoblin/db/models.py b/mediagoblin/db/models.py index 2b925983..f524b220 100644 --- a/mediagoblin/db/models.py +++ b/mediagoblin/db/models.py @@ -29,6 +29,7 @@ from sqlalchemy.orm.collections import attribute_mapped_collection from sqlalchemy.sql.expression import desc from sqlalchemy.ext.associationproxy import association_proxy from sqlalchemy.util import memoized_property +from sqlalchemy.schema import Table from mediagoblin.db.extratypes import PathTupleWithSlashes, JSONEncoded from mediagoblin.db.base import Base, DictReadAttrProxy @@ -484,10 +485,93 @@ class ProcessingMetaData(Base): return DictReadAttrProxy(self) +class ReportBase(Base): + """ + + """ + __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")) + report_content = Column(UnicodeText) + created = Column(DateTime, nullable=False, default=datetime.datetime.now()) + resolved = Column(DateTime) + discriminator = Column('type', Unicode(50)) + __mapper_args__ = {'polymorphic_on': discriminator} + + +class CommentReport(ReportBase): + """ + A class to keep track of reports that have been filed on comments + """ + __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=False) + comment = relationship(MediaComment, backref=backref("reports_filed_on", + lazy="dynamic", + cascade="all, delete-orphan")) + +class MediaReport(ReportBase): + """ + A class to keep track of reports that have been filed on media entries + """ + __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=False) + media_entry = relationship(MediaEntry, backref=backref("reports_filed_on", + lazy="dynamic", + cascade="all, delete-orphan")) + +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 + :param user_id Holds the id of the user this object is attached to. + This should be a one-to-one relationship. + :param expiration_date Holds the date that the ban will be lifted. If this + is null, the ban is permanent unless a moderator + manually lifts it. + :param reason Holds the reason why the user was banned. + """ + __tablename__ = 'core__user_bans' + + user_id = Column('id',Integer, ForeignKey(User.id), nullable=False, + primary_key=True) + expiration_date = Column(DateTime) + reason = Column(UnicodeText, nullable=False) + + +class Group(Base): + __tablename__ = 'core__groups' + + id = Column(Integer, nullable=False, primary_key=True) + group_name = Column(Unicode, nullable=False) + all_users = relationship(User, backref='all_groups', secondary="core__group_user_associations") + + def __repr__(self): + return "<Group %s>" % (self.group_name) + +class GroupUserAssociation(Base): + __tablename__ = 'core__group_user_associations' + + group_id = Column('core__group_id', Integer, ForeignKey(User.id), primary_key=True) + user_id = Column('core__user_id', Integer, ForeignKey(Group.id), primary_key=True) + + + MODELS = [ User, MediaEntry, Tag, MediaTag, MediaComment, Collection, CollectionItem, MediaFile, FileKeynames, - MediaAttachmentFile, ProcessingMetaData] - + MediaAttachmentFile, ProcessingMetaData, CommentReport, MediaReport, UserBan, Group, GroupUserAssociation] ###################################################### # Special, migrations-tracking table diff --git a/mediagoblin/decorators.py b/mediagoblin/decorators.py index f3535fcf..5b55ead7 100644 --- a/mediagoblin/decorators.py +++ b/mediagoblin/decorators.py @@ -21,7 +21,7 @@ from werkzeug.exceptions import Forbidden, NotFound from werkzeug.urls import url_quote from mediagoblin import mg_globals as mgg -from mediagoblin.db.models import MediaEntry, User +from mediagoblin.db.models import MediaEntry, User, MediaComment from mediagoblin.tools.response import redirect, render_404 @@ -226,6 +226,24 @@ def get_media_entry_by_id(controller): return wrapper +def get_media_comment_by_id(controller): + """ + Pass in a MediaComment based off of a url component + """ + @wraps(controller) + def wrapper(request, *args, **kwargs): + comment = MediaComment.query.filter_by( + id=request.matchdict['comment']).first() + # Still no media? Okay, 404. + if not comment: + return render_404(request) + + return controller(request, comment=comment, *args, **kwargs) + + return wrapper + + + def get_workbench(func): """Decorator, passing in a workbench as kwarg which is cleaned up afterwards""" diff --git a/mediagoblin/templates/mediagoblin/admin/report.html b/mediagoblin/templates/mediagoblin/admin/report.html new file mode 100644 index 00000000..ff5cb427 --- /dev/null +++ b/mediagoblin/templates/mediagoblin/admin/report.html @@ -0,0 +1,95 @@ +{# +# 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 %}Report panel{% endtrans %} — {{ super() }} +{%- endblock %} + +{% block mediagoblin_content %} + +<h1>{% trans %}Report panel{% endtrans %}</h1> + +<p> + {% trans %}Here you can look up users in order to take punitive actions on them.{% endtrans %} +</p> + +<h2>{% trans %}Reports Filed on Comments{% endtrans %}</h2> + +{% if report_list.count() %} + <table class="media_panel processing"> + <tr> + <th>ID</th> + <th>Report Type</th> + <th>Offender</th> + <th>When Reported</th> + <th>Reported By</th> + <th>Reason</th> + <th>Reported Comment or Media Entry</th> + </tr> + {% for report in report_list %} + <tr> + {% if report.discriminator == "comment_report" %} + <td>{{ report.id }}</td> + <td>Comment Report</td> + <td>{{ report.comment.get_author.username }}</td> + <td>{{ report.created.strftime("%F %R") }}</td> + <td>{{ report.reporter.username }}</td> + <td>{{ report.report_content }}</td> + <td><a href="{{ report.comment.get_media_entry.url_for_self(request.urlgen) }}">{{ report.comment.get_media_entry.title }}</a></td> + {% elif report.discriminator == "media_report" %} + <td>{{ report.id }}</td> + <td>Media Report</td> + <td>{{ report.media_entry.get_uploader.username }}</td> + <td>{{ report.created.strftime("%F %R") }}</td> + <td>{{ report.reporter.username }}</td> + <td>{{ report.report_content[0:20] }}...</td> + <td><a href="{{ report.media_entry.url_for_self(request.urlgen) }}">{{ report.media_entry.title }}</a></td> + {% endif %} + </tr> + {% endfor %} + </table> +{% else %} + <p><em>{% trans %}No open reports found.{% endtrans %}</em></p> +{% endif %} +<h2>{% trans %}Closed Reports on Comments{% endtrans %}</h2> +{% if closed_report_list.count() %} + <table class="media_panel processing"> + <tr> + <th>ID</th> + <th>Offender</th> + <th>When Reported</th> + <th>Reported By</th> + <th>Reason</th> + <th>Comment Posted On</th> + </tr> + {% for report in closed_report_list %} + <tr> + <td>{{ report.id }}</td> + <td>{{ report.comment.get_author.username }}</td> + <td>{{ report.created.strftime("%F %R") }}</td> + <td>{{ report.reporter.username }}</td> + <td>{{ report.report_content }}</td> + <td><a href="{{ report.comment.get_media_entry.url_for_self(request.urlgen) }}">{{ report.comment.get_media_entry.title }}</a></td> + </tr> + {% endfor %} + </table> +{% else %} + <p><em>{% trans %}No closed reports found.{% endtrans %}</em></p> +{% endif %} +{% endblock %} diff --git a/mediagoblin/templates/mediagoblin/admin/user.html b/mediagoblin/templates/mediagoblin/admin/user.html new file mode 100644 index 00000000..6b6d226a --- /dev/null +++ b/mediagoblin/templates/mediagoblin/admin/user.html @@ -0,0 +1,54 @@ +{# +# 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() %} + <table class="media_panel processing"> + <tr> + <th>ID</th> + <th>Username</th> + <th>When Joined</th> + <th># of Comments Posted</th> + </tr> + {% for user in user_list %} + <tr> + <td>{{ user.id }}</td> + <td>{{ user.username }}</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 %} diff --git a/mediagoblin/templates/mediagoblin/user_pages/media.html b/mediagoblin/templates/mediagoblin/user_pages/media.html index fb892fd7..134a80ad 100644 --- a/mediagoblin/templates/mediagoblin/user_pages/media.html +++ b/mediagoblin/templates/mediagoblin/user_pages/media.html @@ -95,6 +95,17 @@ {% trans %}Add a comment{% endtrans %} </a> {% endif %} + <a + {% if not request.user -%} + href="{{ request.urlgen('mediagoblin.auth.login') }}" + {% else %} + href="{{ request.urlgen('mediagoblin.user_pages.media_home.report_media', + user=media.get_uploader.username, + media=media.slug_or_id) }}" + {% endif %} + class="button_action" id="button_reportmedia" title="Report media"> + {% trans %}Report media{% endtrans %} + </a> {% if request.user %} <form action="{{ request.urlgen('mediagoblin.user_pages.media_post_comment', user= media.get_uploader.username, @@ -139,6 +150,17 @@ {{ comment.content_html }} {%- endautoescape %} </div> + <div> + <a {% if not request.user -%} + href="{{ request.urlgen('mediagoblin.auth.login') }}" + {%- else %} + href="{{ request.urlgen('mediagoblin.user_pages.media_home.report_comment', + user=media.get_uploader.username, + media=media.slug_or_id, + comment=comment.id) }}" + {%- endif %}> + {% trans %} Report {% endtrans %}</a> + </div> </li> {% endfor %} </ul> diff --git a/mediagoblin/templates/mediagoblin/user_pages/report.html b/mediagoblin/templates/mediagoblin/user_pages/report.html new file mode 100644 index 00000000..9431efc0 --- /dev/null +++ b/mediagoblin/templates/mediagoblin/user_pages/report.html @@ -0,0 +1,75 @@ +{# +# 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 mediagoblin_content %} +<h2>File a Report</h2> +<form action="" method=POST > + {% if comment is defined %} + <h4>{% trans %}Reporting this Comment {% endtrans %}</h3> + {% set comment_author = 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.user_pages.user_home', + user=comment_author.username) }}" + class="comment_authorlink"> + {{- comment_author.username -}} + </a> + <a href="{{ request.urlgen('mediagoblin.user_pages.media_home.view_comment', + comment=comment.id, + user=media.get_uploader.username, + media=media.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> + <input type=hidden name=comment_id value="{{ comment.id }}" /> + {% elif media is defined %} + <h3>{% trans %}Reporting this Media Entry {% endtrans %}</h3> + {% trans %}published by {% endtrans %}<a href="{{ request.urlgen('mediagoblin.user_pages.user_home', user=media.get_uploader.username) }}" class="comment_authorlink">{{ media.get_uploader.username }}</a> + <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></td></tr> + <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> + <input type=hidden name=media_entry_id value="{{ media.id }}" /> + {% endif %} + <div class=form_field_input> + <label class=form_field_label >{% trans %}Reason for reporting:{% endtrans %}</label> + <textarea name=report_reason></textarea> + </div> + <input type=hidden name=reporter_id value="{{ request.user.id }}" /> + <input type=submit /> + {{ csrf_token }} +</form> +{% endblock %} diff --git a/mediagoblin/user_pages/forms.py b/mediagoblin/user_pages/forms.py index 9a193680..effe49e1 100644 --- a/mediagoblin/user_pages/forms.py +++ b/mediagoblin/user_pages/forms.py @@ -49,3 +49,19 @@ class MediaCollectForm(wtforms.Form): description=_("""You can use <a href="http://daringfireball.net/projects/markdown/basics"> Markdown</a> for formatting.""")) + +class CommentReportForm(wtforms.Form): + report_reason = wtforms.TextAreaField('Reason for Reporting') + comment_id = wtforms.IntegerField() + reporter_id = wtforms.IntegerField() + +class MediaReportForm(wtforms.Form): + report_reason = wtforms.TextAreaField('Reason for Reporting') + media_entry_id = wtforms.IntegerField() + reporter_id = wtforms.IntegerField() + +class ReportForm(wtforms.Form): + report_reason = wtforms.TextAreaField('Reason for Reporting') + media_entry_id = wtforms.IntegerField() + reporter_id = wtforms.IntegerField() + comment_id = wtforms.IntegerField() diff --git a/mediagoblin/user_pages/lib.py b/mediagoblin/user_pages/lib.py index 2f47e4b1..9c8ddee6 100644 --- a/mediagoblin/user_pages/lib.py +++ b/mediagoblin/user_pages/lib.py @@ -19,7 +19,8 @@ from mediagoblin.tools.template import render_template from mediagoblin.tools.translate import pass_to_ugettext as _ from mediagoblin import mg_globals from mediagoblin.db.base import Session -from mediagoblin.db.models import CollectionItem +from mediagoblin.db.models import CollectionItem, MediaReport, CommentReport +from mediagoblin.user_pages import forms as user_forms def send_comment_email(user, comment, media, request): @@ -75,3 +76,31 @@ def add_media_to_collection(collection, media, note=None, commit=True): if commit: Session.commit() + +def build_report_form(form_dict): + """ + :param form_dict should be an ImmutableMultiDict object which is what is + returned from 'request.form.' The Object should have valid keys + matching the fields in either MediaReportForm or CommentReportForm + + :returns either of MediaReport or a CommentReport object that has not been saved. + In case of an improper form_dict, returns None + """ + if 'comment_id' in form_dict.keys(): + report_form = user_forms.CommentReportForm(form_dict) + elif 'media_entry_id' in form_dict.keys(): + report_form = user_forms.MediaReportForm(form_dict) + else: + return None + if report_form.validate() and 'comment_id' in form_dict.keys(): + report_model = CommentReport() + report_model.comment_id = report_form.comment_id.data + elif report_form.validate() and 'media_entry_id' in form_dict.keys(): + report_model = MediaReport() + report_model.media_entry_id = report_form.media_entry_id.data + else: + return None + report_model.report_content = report_form.report_reason.data or u'' + report_model.reporter_id = report_form.reporter_id.data + return report_model + diff --git a/mediagoblin/user_pages/routing.py b/mediagoblin/user_pages/routing.py index 9cb665b5..adccda9e 100644 --- a/mediagoblin/user_pages/routing.py +++ b/mediagoblin/user_pages/routing.py @@ -23,6 +23,10 @@ add_route('mediagoblin.user_pages.media_home', '/u/<string:user>/m/<string:media>/', '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', '/u/<string:user>/m/<int:media_id>/confirm-delete/', 'mediagoblin.user_pages.views:media_confirm_delete') @@ -40,6 +44,10 @@ add_route('mediagoblin.user_pages.media_home.view_comment', '/u/<string:user>/m/<string:media>/c/<int:comment>/', '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_comment_report') + # User's tags gallery add_route('mediagoblin.user_pages.user_tag_gallery', '/u/<string:user>/tag/<string:tag>/', diff --git a/mediagoblin/user_pages/views.py b/mediagoblin/user_pages/views.py index 738cc054..94cccc66 100644 --- a/mediagoblin/user_pages/views.py +++ b/mediagoblin/user_pages/views.py @@ -19,19 +19,21 @@ import datetime from mediagoblin import messages, mg_globals from mediagoblin.db.models import (MediaEntry, MediaTag, Collection, - CollectionItem, User) + CollectionItem, User, MediaComment, + CommentReport, MediaReport) from mediagoblin.tools.response import render_to_response, render_404, \ redirect, redirect_obj from mediagoblin.tools.translate import pass_to_ugettext as _ from mediagoblin.tools.pagination import Pagination from mediagoblin.user_pages import forms as user_forms -from mediagoblin.user_pages.lib import (send_comment_email, +from mediagoblin.user_pages.lib import (send_comment_email, build_report_form, add_media_to_collection) from mediagoblin.decorators import (uses_pagination, get_user_media_entry, get_media_entry_by_id, require_active_login, user_may_delete_media, user_may_alter_collection, - get_user_collection, get_user_collection_item, active_user_from_url) + get_user_collection, get_user_collection_item, active_user_from_url, + get_media_comment_by_id) from werkzeug.contrib.atom import AtomFeed @@ -616,3 +618,28 @@ def processing_panel(request): 'processing_entries': processing_entries, 'failed_entries': failed_entries, 'processed_entries': processed_entries}) + +@require_active_login +@get_user_media_entry +def file_a_report(request, media, comment=None): + if request.method == "POST": + report_form = build_report_form(request.form) + report_form.save() + return redirect( + request, + 'index') + if comment is not None: + context = {'media': media, + 'comment':comment} + else: + context = {'media': media} + return render_to_response( + request, + 'mediagoblin/user_pages/report.html', + context) + +@require_active_login +@get_user_media_entry +@get_media_comment_by_id +def file_a_comment_report(request, media, comment): + return file_a_report(request, comment=comment) |