from mediagoblin.decorators import oauth_required, require_active_login
from mediagoblin.api.decorators import user_has_privilege
-from mediagoblin.db.models import User, LocalUser, MediaEntry, MediaComment, Activity
+from mediagoblin.db.models import User, LocalUser, MediaEntry, Comment, TextComment, Activity
from mediagoblin.tools.federation import create_activity, create_generator
from mediagoblin.tools.routing import extract_url_arguments
from mediagoblin.tools.response import redirect, json_response, json_error, \
status=403
)
- comment = MediaComment(actor=request.user.id)
+ comment = TextComment(actor=request.user.id)
comment.unserialize(data["object"], request)
comment.save()
verb="post",
actor=request.user,
obj=comment,
- target=comment.get_entry,
+ target=comment.get_reply_to(),
generator=generator
)
elif obj.get("objectType", None) == "image":
# Posting an image to the feed
- media_id = int(extract_url_arguments(
+ media_id = extract_url_arguments(
url=data["object"]["id"],
urlmap=request.app.url_map
- )["id"])
+ )["id"]
- media = MediaEntry.query.filter_by(id=media_id).first()
+ # Build public_id
+ public_id = request.urlgen(
+ "mediagoblin.api.object",
+ object_type=obj["objectType"],
+ id=media_id,
+ qualified=True
+ )
+
+ media = MediaEntry.query.filter_by(
+ public_id=public_id
+ ).first()
if media is None:
return json_response(
if "id" not in obj:
return json_error("Object ID has not been specified.")
- obj_id = int(extract_url_arguments(
+ obj_id = extract_url_arguments(
url=obj["id"],
urlmap=request.app.url_map
- )["id"])
+ )["id"]
+
+ public_id = request.urlgen(
+ "mediagoblin.api.object",
+ object_type=obj["objectType"],
+ id=obj_id,
+ qualified=True
+ )
# Now try and find object
if obj["objectType"] == "comment":
status=403
)
- comment = MediaComment.query.filter_by(id=obj_id).first()
+ comment = TextComment.query.filter_by(
+ public_id=public_id
+ ).first()
if comment is None:
return json_error(
"No such 'comment' with id '{0}'.".format(obj_id)
return json_response(activity.serialize(request))
elif obj["objectType"] == "image":
- image = MediaEntry.query.filter_by(id=obj_id).first()
+ image = MediaEntry.query.filter_by(
+ public_id=public_id
+ ).first()
if image is None:
return json_error(
"No such 'image' with the id '{0}'.".format(obj["id"])
return json_error("Object ID has not been specified.")
# Parse out the object ID
- obj_id = int(extract_url_arguments(
+ obj_id = extract_url_arguments(
url=obj["id"],
urlmap=request.app.url_map
- )["id"])
+ )["id"]
+
+ public_id = request.urlgen(
+ "mediagoblin.api.object",
+ object_type=obj["objectType"],
+ id=obj_id,
+ qualified=True
+ )
if obj.get("objectType", None) == "comment":
# Find the comment asked for
- comment = MediaComment.query.filter_by(
- id=obj_id,
+ comment = TextComment.query.filter_by(
+ public_id=public_id,
actor=request.user.id
).first()
if obj.get("objectType", None) == "image":
# Find the image
entry = MediaEntry.query.filter_by(
- id=obj_id,
+ public_id=public_id,
actor=request.user.id
).first()
"No such 'image' with id '{0}'.".format(obj_id)
)
- # Okay lets do our best to ensure there is a public_id for
- # this image, there most likely is but it's important!
- entry.get_public_id(request.urlgen)
-
# Make the delete activity
generator = create_generator(request)
activity = create_activity(
status=404
)
- media = MediaEntry.query.filter_by(id=object_id).first()
+ public_id = request.urlgen(
+ "mediagoblin.api.object",
+ object_type=object_type,
+ id=object_id,
+ qualified=True
+ )
+
+ media = MediaEntry.query.filter_by(public_id=public_id).first()
if media is None:
return json_error(
"Can't find '{0}' with ID '{1}'".format(object_type, object_id),
@oauth_required
def object_comments(request):
""" Looks up for the comments on a object """
- media = MediaEntry.query.filter_by(id=request.matchdict["id"]).first()
+ public_id = request.urlgen(
+ "mediagoblin.api.object",
+ object_type=request.matchdict["object_type"],
+ id=request.matchdict["id"],
+ qualified=True
+ )
+ media = MediaEntry.query.filter_by(public_id=public_id).first()
if media is None:
return json_error("Can't find '{0}' with ID '{1}'".format(
request.matchdict["object_type"],
#
# 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 six
+import copy
+
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import inspect
from sqlalchemy.orm import scoped_session, sessionmaker
Session = scoped_session(sessionmaker())
+class FakeCursor(object):
+
+ def __init__ (self, cursor, mapper, filter=None):
+ self.cursor = cursor
+ self.mapper = mapper
+ self.filter = filter
+
+ def count(self):
+ return self.cursor.count()
+
+ def __copy__(self):
+ # Or whatever the function is named to make
+ # copy.copy happy?
+ return FakeCursor(copy.copy(self.cursor), self.mapper, self.filter)
+
+ def __iter__(self):
+ return six.moves.filter(self.filter, six.moves.map(self.mapper, self.cursor))
+
+ def __getitem__(self, key):
+ return self.mapper(self.cursor[key])
+
+ def slice(self, *args, **kwargs):
+ r = self.cursor.slice(*args, **kwargs)
+ return list(six.moves.filter(self.filter, six.moves.map(self.mapper, r)))
class GMGTableBase(object):
# Deletion types
id=self.actor
).first()
tombstone.object_type = self.object_type
- tombstone.save()
+ tombstone.save(commit=False)
# There will be a lot of places where the GenericForeignKey will point
# to the model, we want to remap those to our tombstone.
from mediagoblin.db.extratypes import JSONEncoded, MutationDict
from mediagoblin.db.migration_tools import (
RegisterMigration, inspect_table, replace_table_hack)
-from mediagoblin.db.models import (MediaEntry, Collection, MediaComment, User,
+from mediagoblin.db.models import (MediaEntry, Collection, Comment, User,
Privilege, Generator, LocalUser, Location,
Client, RequestToken, AccessToken)
from mediagoblin.db.extratypes import JSONEncoded, MutationDict
__tablename__ = 'core__comment_notifications'
id = Column(Integer, ForeignKey(Notification_v0.id), primary_key=True)
- subject_id = Column(Integer, ForeignKey(MediaComment.id))
+ subject_id = Column(Integer, ForeignKey(Comment.id))
class ProcessingNotification_v0(Notification_v0):
id = Column('id',Integer, ForeignKey('core__reports.id'),
primary_key=True)
- comment_id = Column(Integer, ForeignKey(MediaComment.id), nullable=True)
+ comment_id = Column(Integer, ForeignKey(Comment.id), nullable=True)
class MediaReport_v0(ReportBase_v0):
TYPES = {
"user": User,
"media": MediaEntry,
- "comment": MediaComment,
+ "comment": Comment,
"collection": Collection,
}
# Commit this.
db.commit()
+
+class Comment_V0(declarative_base()):
+ __tablename__ = "core__comment_links"
+
+ id = Column(Integer, primary_key=True)
+ target_id = Column(
+ Integer,
+ ForeignKey(GenericModelReference_V0.id),
+ nullable=False
+ )
+ comment_id = Column(
+ Integer,
+ ForeignKey(GenericModelReference_V0.id),
+ nullable=False
+ )
+ added = Column(DateTime, nullable=False, default=datetime.datetime.utcnow)
+
+
+@RegisterMigration(41, MIGRATIONS)
+def federation_comments(db):
+ """
+ This reworks the MediaComent to be a more generic Comment model.
+ """
+ metadata = MetaData(bind=db.bind)
+ textcomment_table = inspect_table(metadata, "core__media_comments")
+ gmr_table = inspect_table(metadata, "core__generic_model_reference")
+
+ # First of all add the public_id field to the TextComment table
+ comment_public_id_column = Column(
+ "public_id",
+ Unicode,
+ unique=True
+ )
+ comment_public_id_column.create(
+ textcomment_table,
+ unique_name="public_id_unique"
+ )
+
+ comment_updated_column = Column(
+ "updated",
+ DateTime,
+ )
+ comment_updated_column.create(textcomment_table)
+
+
+ # First create the Comment link table.
+ Comment_V0.__table__.create(db.bind)
+ db.commit()
+
+ # now look up the comment table
+ comment_table = inspect_table(metadata, "core__comment_links")
+
+ # Itierate over all the comments and add them to the link table.
+ for comment in db.execute(textcomment_table.select()):
+ # Check if there is a GMR to the comment.
+ comment_gmr = db.execute(gmr_table.select().where(and_(
+ gmr_table.c.obj_pk == comment.id,
+ gmr_table.c.model_type == "core__media_comments"
+ ))).first()
+
+ if comment_gmr:
+ comment_gmr = comment_gmr[0]
+ else:
+ comment_gmr = db.execute(gmr_table.insert().values(
+ obj_pk=comment.id,
+ model_type="core__media_comments"
+ )).inserted_primary_key[0]
+
+ # Get or create the GMR for the media entry
+ entry_gmr = db.execute(gmr_table.select().where(and_(
+ gmr_table.c.obj_pk == comment.media_entry,
+ gmr_table.c.model_type == "core__media_entries"
+ ))).first()
+
+ if entry_gmr:
+ entry_gmr = entry_gmr[0]
+ else:
+ entry_gmr = db.execute(gmr_table.insert().values(
+ obj_pk=comment.media_entry,
+ model_type="core__media_entries"
+ )).inserted_primary_key[0]
+
+ # Add the comment link.
+ db.execute(comment_table.insert().values(
+ target_id=entry_gmr,
+ comment_id=comment_gmr,
+ added=datetime.datetime.utcnow()
+ ))
+
+ # Add the data to the updated field
+ db.execute(textcomment_table.update().where(
+ textcomment_table.c.id == comment.id
+ ).values(
+ updated=comment.created
+ ))
+ db.commit()
+
+ # Add not null constraint
+ textcomment_update_column = textcomment_table.columns["updated"]
+ textcomment_update_column.alter(nullable=False)
+
+ # Remove the unused fields on the TextComment model
+ comment_media_entry_column = textcomment_table.columns["media_entry"]
+ comment_media_entry_column.drop()
+ db.commit()
+
+@RegisterMigration(42, MIGRATIONS)
+def consolidate_reports(db):
+ """ Consolidates the report tables into just one """
+ metadata = MetaData(bind=db.bind)
+
+ report_table = inspect_table(metadata, "core__reports")
+ comment_report_table = inspect_table(metadata, "core__reports_on_comments")
+ media_report_table = inspect_table(metadata, "core__reports_on_media")
+ gmr_table = inspect_table(metadata, "core__generic_model_reference")
+
+ # Add the GMR object field onto the base report table
+ report_object_id_column = Column(
+ "object_id",
+ Integer,
+ ForeignKey(GenericModelReference_V0.id),
+ )
+ report_object_id_column.create(report_table)
+ db.commit()
+
+ # Iterate through the reports in the comment table and merge them in.
+ for comment_report in db.execute(comment_report_table.select()):
+ # Find a GMR for this if one exists.
+ crgmr = db.execute(gmr_table.select().where(and_(
+ gmr_table.c.obj_pk == comment_report.comment_id,
+ gmr_table.c.model_type == "core__media_comments"
+ ))).first()
+
+ if crgmr:
+ crgmr = crgmr[0]
+ else:
+ crgmr = db.execute(gmr_table.insert().values(
+ gmr_table.c.obj_pk == comment_report.comment_id,
+ gmr_table.c.model_type == "core__media_comments"
+ )).inserted_primary_key[0]
+
+ # Great now we can save this back onto the (base) report.
+ db.execute(report_table.update().where(
+ report_table.c.id == comment_report.id
+ ).values(
+ object_id=crgmr
+ ))
+
+ # Iterate through the Media Reports and do the save as above.
+ for media_report in db.execute(media_report_table.select()):
+ # Find Mr. GMR :)
+ mrgmr = db.execute(gmr_table.select().where(and_(
+ gmr_table.c.obj_pk == media_report.media_entry_id,
+ gmr_table.c.model_type == "core__media_entries"
+ ))).first()
+
+ if mrgmr:
+ mrgmr = mrgmr[0]
+ else:
+ mrgmr = db.execute(gmr_table.insert().values(
+ obj_pk=media_report.media_entry_id,
+ model_type="core__media_entries"
+ )).inserted_primary_key[0]
+
+ # Save back on to the base.
+ db.execute(report_table.update().where(
+ report_table.c.id == media_report.id
+ ).values(
+ object_id=mrgmr
+ ))
+
+ db.commit()
+
+ # Add the not null constraint
+ report_object_id = report_table.columns["object_id"]
+ report_object_id.alter(nullable=False)
+
+ # Now we can remove the fields we don't need anymore
+ report_type = report_table.columns["type"]
+ report_type.drop()
+
+ # Drop both MediaReports and CommentTable.
+ comment_report_table.drop()
+ media_report_table.drop()
+
+ # Commit we're done.
+ db.commit()
+
+@RegisterMigration(43, MIGRATIONS)
+def consolidate_notification(db):
+ """ Consolidates the notification models into one """
+ metadata = MetaData(bind=db.bind)
+ notification_table = inspect_table(metadata, "core__notifications")
+ cn_table = inspect_table(metadata, "core__comment_notifications")
+ cp_table = inspect_table(metadata, "core__processing_notifications")
+ gmr_table = inspect_table(metadata, "core__generic_model_reference")
+
+ # Add fields needed
+ notification_object_id_column = Column(
+ "object_id",
+ Integer,
+ ForeignKey(GenericModelReference_V0.id)
+ )
+ notification_object_id_column.create(notification_table)
+ db.commit()
+
+ # Iterate over comments and move to notification base table.
+ for comment_notification in db.execute(cn_table.select()):
+ # Find the GMR.
+ cngmr = db.execute(gmr_table.select().where(and_(
+ gmr_table.c.obj_pk == comment_notification.subject_id,
+ gmr_table.c.model_type == "core__media_comments"
+ ))).first()
+
+ if cngmr:
+ cngmr = cngmr[0]
+ else:
+ cngmr = db.execute(gmr_table.insert().values(
+ obj_pk=comment_notification.subject_id,
+ model_type="core__media_comments"
+ )).inserted_primary_key[0]
+
+ # Save back on notification
+ db.execute(notification_table.update().where(
+ notification_table.c.id == comment_notification.id
+ ).values(
+ object_id=cngmr
+ ))
+ db.commit()
+
+ # Do the same for processing notifications
+ for processing_notification in db.execute(cp_table.select()):
+ cpgmr = db.execute(gmr_table.select().where(and_(
+ gmr_table.c.obj_pk == processing_notification.subject_id,
+ gmr_table.c.model_type == "core__processing_notifications"
+ ))).first()
+
+ if cpgmr:
+ cpgmr = cpgmr[0]
+ else:
+ cpgmr = db.execute(gmr_table.insert().values(
+ obj_pk=processing_notification.subject_id,
+ model_type="core__processing_notifications"
+ )).inserted_primary_key[0]
+
+ db.execute(notification_table.update().where(
+ notification_table.c.id == processing_notification.id
+ ).values(
+ object_id=cpgmr
+ ))
+ db.commit()
+
+ # Add the not null constraint
+ notification_object_id = notification_table.columns["object_id"]
+ notification_object_id.alter(nullable=False)
+
+ # Now drop the fields we don't need
+ notification_type_column = notification_table.columns["type"]
+ notification_type_column.drop()
+
+ # Drop the tables we no longer need
+ cp_table.drop()
+ cn_table.drop()
+
+ db.commit()
from mediagoblin.tools.url import slugify
from mediagoblin.tools.translate import pass_to_ugettext as _
+class CommentingMixin(object):
+ """
+ Mixin that gives classes methods to get and add the comments on/to it
+
+ This assumes the model has a "comments" class which is a ForeignKey to the
+ Collection model. This will hold a Collection of comments which are
+ associated to this model. It also assumes the model has an "actor"
+ ForeignKey which points to the creator/publisher/etc. of the model.
+
+ NB: This is NOT the mixin for the Comment Model, this is for
+ other models which support commenting.
+ """
+
+ def get_comment_link(self):
+ # Import here to avoid cyclic imports
+ from mediagoblin.db.models import Comment, GenericModelReference
+
+ gmr = GenericModelReference.query.filter_by(
+ obj_pk=self.id,
+ model_type=self.__tablename__
+ ).first()
+
+ if gmr is None:
+ return None
+
+ link = Comment.query.filter_by(comment_id=gmr.id).first()
+ return link
+
+ def get_reply_to(self):
+ link = self.get_comment_link()
+ if link is None or link.target_id is None:
+ return None
+
+ return link.target()
+
+ def soft_delete(self, *args, **kwargs):
+ link = self.get_comment_link()
+ if link is not None:
+ link.delete()
+ super(CommentingMixin, self).soft_delete(*args, **kwargs)
+
class GeneratePublicIDMixin(object):
"""
Mixin that ensures that a the public_id field is populated.
self.public_id = urlgen(
"mediagoblin.api.object",
object_type=self.object_type,
- id=self.id,
+ id=str(uuid.uuid4()),
qualified=True
)
+ self.save()
return self.public_id
class UserMixin(object):
return exif_short
-class MediaCommentMixin(object):
+class TextCommentMixin(GeneratePublicIDMixin):
object_type = "comment"
@property
actor=self.get_actor,
comment=self.content)
-
class CollectionMixin(GenerateSlugMixin, GeneratePublicIDMixin):
object_type = "collection"
collection=self.slug_or_id,
**extra_args)
+ def add_to_collection(self, obj, content=None, commit=True):
+ """ Adds an object to the collection """
+ # It's here to prevent cyclic imports
+ from mediagoblin.db.models import CollectionItem
+
+ # Need the ID of this collection for this so check we've got one.
+ self.save(commit=False)
+
+ # Create the CollectionItem
+ item = CollectionItem()
+ item.collection = self.id
+ item.get_object = obj
+
+ if content is not None:
+ item.note = content
+
+ self.num_items = self.num_items + 1
+
+ # Save both!
+ self.save(commit=commit)
+ item.save(commit=commit)
+ return item
class CollectionItemMixin(object):
@property
from mediagoblin.db.extratypes import (PathTupleWithSlashes, JSONEncoded,
MutationDict)
-from mediagoblin.db.base import Base, DictReadAttrProxy
+from mediagoblin.db.base import Base, DictReadAttrProxy, FakeCursor
from mediagoblin.db.mixin import UserMixin, MediaEntryMixin, \
- MediaCommentMixin, CollectionMixin, CollectionItemMixin, \
- ActivityMixin
+ CollectionMixin, CollectionItemMixin, ActivityMixin, TextCommentMixin, \
+ CommentingMixin
from mediagoblin.tools.files import delete_media_files
from mediagoblin.tools.common import import_component
from mediagoblin.tools.routing import extract_url_arguments
collection.delete(**kwargs)
# Find all the comments and delete those too
- for comment in MediaComment.query.filter_by(actor=self.id):
+ for comment in TextComment.query.filter_by(actor=self.id):
comment.delete(**kwargs)
# Find all the activities and delete those too
nonce = Column(Unicode, nullable=False, primary_key=True)
timestamp = Column(DateTime, nullable=False, primary_key=True)
-class MediaEntry(Base, MediaEntryMixin):
+class MediaEntry(Base, MediaEntryMixin, CommentingMixin):
"""
TODO: Consider fetching the media_files using join
"""
))
def get_comments(self, ascending=False):
- order_col = MediaComment.created
- if not ascending:
- order_col = desc(order_col)
- return self.all_comments.order_by(order_col)
+ query = Comment.query.join(Comment.target_helper).filter(and_(
+ GenericModelReference.obj_pk == self.id,
+ GenericModelReference.model_type == self.__tablename__
+ ))
+ if ascending:
+ query = query.order_by(Comment.added.asc())
+ else:
+ qury = query.order_by(Comment.added.desc())
+
+ return FakeCursor(query, lambda c:c.comment())
+
def url_to_prev(self, urlgen):
"""get the next 'newer' entry by this user"""
media = MediaEntry.query.filter(
def soft_delete(self, *args, **kwargs):
# Find all of the media comments for this and delete them
- for comment in MediaComment.query.filter_by(media_entry=self.id):
+ for comment in self.get_comments():
comment.delete(*args, **kwargs)
super(MediaEntry, self).soft_delete(*args, **kwargs)
"""A dict like view on this object"""
return DictReadAttrProxy(self)
+class Comment(Base):
+ """
+ Link table between a response and another object that can have replies.
+
+ This acts as a link table between an object and the comments on it, it's
+ done like this so that you can look up all the comments without knowing
+ whhich comments are on an object before hand. Any object can be a comment
+ and more or less any object can accept comments too.
+
+ Important: This is NOT the old MediaComment table.
+ """
+ __tablename__ = "core__comment_links"
+
+ id = Column(Integer, primary_key=True)
+
+ # The GMR to the object the comment is on.
+ target_id = Column(
+ Integer,
+ ForeignKey(GenericModelReference.id),
+ nullable=False
+ )
+ target_helper = relationship(
+ GenericModelReference,
+ foreign_keys=[target_id]
+ )
+ target = association_proxy("target_helper", "get_object",
+ creator=GenericModelReference.find_or_new)
+
+ # The comment object
+ comment_id = Column(
+ Integer,
+ ForeignKey(GenericModelReference.id),
+ nullable=False
+ )
+ comment_helper = relationship(
+ GenericModelReference,
+ foreign_keys=[comment_id]
+ )
+ comment = association_proxy("comment_helper", "get_object",
+ creator=GenericModelReference.find_or_new)
+
+ # When it was added
+ added = Column(DateTime, nullable=False, default=datetime.datetime.utcnow)
+
-class MediaComment(Base, MediaCommentMixin):
+class TextComment(Base, TextCommentMixin, CommentingMixin):
+ """
+ A basic text comment, this is a usually short amount of text and nothing else
+ """
+ # This is a legacy from when Comments where just on MediaEntry objects.
__tablename__ = "core__media_comments"
id = Column(Integer, primary_key=True)
- media_entry = Column(
- Integer, ForeignKey(MediaEntry.id), nullable=False, index=True)
+ public_id = Column(Unicode, unique=True)
actor = Column(Integer, ForeignKey(User.id), nullable=False)
created = Column(DateTime, nullable=False, default=datetime.datetime.utcnow)
+ updated = Column(DateTime, nullable=False, default=datetime.datetime.utcnow)
content = Column(UnicodeText, nullable=False)
location = Column(Integer, ForeignKey("core__locations.id"))
get_location = relationship("Location", lazy="joined")
backref=backref("posted_comments",
lazy="dynamic",
cascade="all, delete-orphan"))
- get_entry = relationship(MediaEntry,
- backref=backref("comments",
- lazy="dynamic",
- cascade="all, delete-orphan"))
-
- # Cascade: Comments are somewhat owned by their MediaEntry.
- # So do the full thing.
- # lazy=dynamic: MediaEntries might have many comments,
- # so make the "all_comments" a query-like thing.
- get_media_entry = relationship(MediaEntry,
- backref=backref("all_comments",
- lazy="dynamic",
- cascade="all, delete-orphan"))
-
deletion_mode = Base.SOFT_DELETE
def serialize(self, request):
""" Unserialize to python dictionary for API """
- href = request.urlgen(
- "mediagoblin.api.object",
- object_type=self.object_type,
- id=self.id,
- qualified=True
- )
- media = MediaEntry.query.filter_by(id=self.media_entry).first()
+ target = self.get_reply_to()
+ # If this is target just.. give them nothing?
+ if target is None:
+ target = {}
+ else:
+ target = target.serialize(request, show_comments=False)
+
+
author = self.get_actor
published = UTC.localize(self.created)
context = {
- "id": href,
+ "id": self.get_public_id(request.urlgen),
"objectType": self.object_type,
"content": self.content,
- "inReplyTo": media.serialize(request, show_comments=False),
+ "inReplyTo": target,
"author": author.serialize(request),
"published": published.isoformat(),
"updated": published.isoformat(),
def unserialize(self, data, request):
""" Takes API objects and unserializes on existing comment """
+ if "content" in data:
+ self.content = data["content"]
+
+ if "location" in data:
+ Location.create(data["location"], self)
+
+
# Handle changing the reply ID
if "inReplyTo" in data:
# Validate that the ID is correct
try:
- media_id = int(extract_url_arguments(
+ id = extract_url_arguments(
url=data["inReplyTo"]["id"],
urlmap=request.app.url_map
- )["id"])
+ )["id"]
except ValueError:
- return False
+ raise False
+
+ public_id = request.urlgen(
+ "mediagoblin.api.object",
+ id=id,
+ object_type=data["inReplyTo"]["objectType"],
+ qualified=True
+ )
- media = MediaEntry.query.filter_by(id=media_id).first()
+ media = MediaEntry.query.filter_by(public_id=public_id).first()
if media is None:
return False
- self.media_entry = media.id
-
- if "content" in data:
- self.content = data["content"]
-
- if "location" in data:
- Location.create(data["location"], self)
+ # We need an ID for this model.
+ self.save(commit=False)
+ # Create the link
+ link = Comment()
+ link.target = media
+ link.comment = self
+ link.save()
+
return True
-
-
-class Collection(Base, CollectionMixin):
+class Collection(Base, CollectionMixin, CommentingMixin):
"""A representation of a collection of objects.
This holds a group/collection of objects that could be a user defined album
OUTBOX_TYPE = "core-outbox"
FOLLOWER_TYPE = "core-followers"
FOLLOWING_TYPE = "core-following"
+ COMMENT_TYPE = "core-comments"
USER_DEFINED_TYPE = "core-user-defined"
def get_collection_items(self, ascending=False):
class Notification(Base):
__tablename__ = 'core__notifications'
id = Column(Integer, primary_key=True)
- type = Column(Unicode)
- created = Column(DateTime, nullable=False, default=datetime.datetime.utcnow)
+ object_id = Column(Integer, ForeignKey(GenericModelReference.id))
+ object_helper = relationship(GenericModelReference)
+ obj = association_proxy("object_helper", "get_object",
+ creator=GenericModelReference.find_or_new)
+ created = Column(DateTime, nullable=False, default=datetime.datetime.utcnow)
user_id = Column(Integer, ForeignKey('core__users.id'), nullable=False,
index=True)
seen = Column(Boolean, default=lambda: False, index=True)
user = relationship(
User,
- backref=backref('notifications', cascade='all, delete-orphan'))
-
- __mapper_args__ = {
- 'polymorphic_identity': 'notification',
- 'polymorphic_on': type
- }
+ backref=backref('notifications', cascade='all, delete-orphan'))
def __repr__(self):
return '<{klass} #{id}: {user}: {subject} ({seen})>'.format(
subject=getattr(self, 'subject', None),
seen='unseen' if not self.seen else 'seen')
-
-class CommentNotification(Notification):
- __tablename__ = 'core__comment_notifications'
- id = Column(Integer, ForeignKey(Notification.id), primary_key=True)
-
- subject_id = Column(Integer, ForeignKey(MediaComment.id))
- subject = relationship(
- MediaComment,
- backref=backref('comment_notifications', cascade='all, delete-orphan'))
-
- __mapper_args__ = {
- 'polymorphic_identity': 'comment_notification'
- }
-
-
-class ProcessingNotification(Notification):
- __tablename__ = 'core__processing_notifications'
-
- id = Column(Integer, ForeignKey(Notification.id), primary_key=True)
-
- subject_id = Column(Integer, ForeignKey(MediaEntry.id))
- subject = relationship(
- MediaEntry,
- backref=backref('processing_notifications',
- cascade='all, delete-orphan'))
-
- __mapper_args__ = {
- 'polymorphic_identity': 'processing_notification'
- }
-
-# the with_polymorphic call has been moved to the bottom above MODELS
-# this is because it causes conflicts with relationship calls.
-
-class ReportBase(Base):
+class Report(Base):
"""
- This is the basic report object which the other reports are based off of.
+ Represents a report that someone might file against Media, Comments, etc.
:keyword reporter_id Holds the id of the user who created
the report, as an Integer column.
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-
resolver's reasons for resolving
the report this way. Some of this
is auto-generated
+ :keyword object_id Holds the ID of the GenericModelReference
+ which points to the reported object.
"""
__tablename__ = 'core__reports'
+
id = Column(Integer, primary_key=True)
reporter_id = Column(Integer, ForeignKey(User.id), nullable=False)
reporter = relationship(
backref=backref("reports_filed_by",
lazy="dynamic",
cascade="all, delete-orphan"),
- primaryjoin="User.id==ReportBase.reporter_id")
+ primaryjoin="User.id==Report.reporter_id")
report_content = Column(UnicodeText)
reported_user_id = Column(Integer, ForeignKey(User.id), nullable=False)
reported_user = relationship(
backref=backref("reports_filed_on",
lazy="dynamic",
cascade="all, delete-orphan"),
- primaryjoin="User.id==ReportBase.reported_user_id")
+ primaryjoin="User.id==Report.reported_user_id")
created = Column(DateTime, nullable=False, default=datetime.datetime.utcnow)
- 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")
+ primaryjoin="User.id==Report.resolver_id")
resolved = Column(DateTime)
result = Column(UnicodeText)
- __mapper_args__ = {'polymorphic_on': discriminator}
+
+ object_id = Column(Integer, ForeignKey(GenericModelReference.id), nullable=False)
+ object_helper = relationship(GenericModelReference)
+ obj = association_proxy("object_helper", "get_object",
+ creator=GenericModelReference.find_or_new)
+
+ def is_archived_report(self):
+ return self.resolved is not None
def is_comment_report(self):
- return self.discriminator=='comment_report'
+ if self.object_id is None:
+ return False
+ return isinstance(self.obj(), TextComment)
def is_media_entry_report(self):
- return self.discriminator=='media_report'
-
- def is_archived_report(self):
- return self.resolved is not None
+ if self.object_id is None:
+ return False
+ return isinstance(self.obj(), MediaEntry)
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
"deleted": self.deleted
}
-with_polymorphic(
- Notification,
- [ProcessingNotification, CommentNotification])
-
MODELS = [
- LocalUser, RemoteUser, User, MediaEntry, Tag, MediaTag, MediaComment,
+ LocalUser, RemoteUser, User, MediaEntry, Tag, MediaTag, Comment, TextComment,
Collection, CollectionItem, MediaFile, FileKeynames, MediaAttachmentFile,
- ProcessingMetaData, Notification, CommentNotification,
- ProcessingNotification, Client, CommentSubscription, ReportBase,
- CommentReport, MediaReport, UserBan, Privilege, PrivilegeUserAssociation,
- RequestToken, AccessToken, NonceTimestamp, Activity, Generator, Location,
- GenericModelReference, Graveyard]
+ ProcessingMetaData, Notification, Client, CommentSubscription, Report,
+ UserBan, Privilege, PrivilegeUserAssociation, RequestToken, AccessToken,
+ NonceTimestamp, Activity, Generator, Location, GenericModelReference, Graveyard]
"""
Foundations are the default rows that are created immediately after the tables
from mediagoblin import mg_globals as mgg
from mediagoblin import messages
-from mediagoblin.db.models import MediaEntry, LocalUser, MediaComment, AccessToken
+from mediagoblin.db.models import MediaEntry, LocalUser, TextComment, \
+ AccessToken, Comment
from mediagoblin.tools.response import (
redirect, render_404,
render_user_banned, json_response)
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.
+ Pass in a Comment based off of a url component. Because of this decor-
+ -ator's use in filing 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
+ the Comment 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.
@wraps(controller)
def wrapper(request, *args, **kwargs):
if 'comment' in request.matchdict:
- comment = MediaComment.query.filter_by(
- id=request.matchdict['comment']).first()
+ comment = Comment.query.filter_by(
+ id=request.matchdict['comment']
+ ).first()
if comment is None:
return render_404(request)
if u'delete' in form.action_to_resolve.data and \
report.is_comment_report():
- deleted_comment = report.comment
+ deleted_comment = report.obj()
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 = report.obj()
deleted_media.delete()
form.resolution_content.data += \
_(u"\n{mod} deleted the media entry.").format(
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-from mediagoblin.db.models import (MediaEntry, User, ReportBase, Privilege,
+from mediagoblin.db.models import (MediaEntry, User, Report, Privilege,
UserBan, LocalUser)
from mediagoblin.decorators import (require_admin_or_moderator_login,
active_user_from_url, user_has_privilege,
LocalUser.username==request.matchdict['user']
).first()
active_reports = user.reports_filed_on.filter(
- ReportBase.resolved==None).limit(5)
+ Report.resolved==None).limit(5)
closed_reports = user.reports_filed_on.filter(
- ReportBase.resolved!=None).all()
+ Report.resolved!=None).all()
privileges = Privilege.query
user_banned = UserBan.query.get(user.id)
ban_form = moderation_forms.BanForm()
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
+ getattr(Report,key)==val
for key,val in filters.viewitems()]
- all_active = ReportBase.query.filter(
- ReportBase.resolved==None).filter(
+ all_active = Report.query.filter(
+ Report.resolved==None).filter(
*filters)
- all_closed = ReportBase.query.filter(
- ReportBase.resolved!=None).filter(
+ all_closed = Report.query.filter(
+ Report.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(
+ Report.created.desc()).offset(
(active_settings['current_page']-1)*10).limit(10)
closed_report_list = all_closed.order_by(
- ReportBase.created.desc()).offset(
+ Report.created.desc()).offset(
(closed_settings['current_page']-1)*10).limit(10)
active_settings['last_page'] = int(ceil(all_active.count()/10.))
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'])
+ report = Report.query.get(request.matchdict['report_id'])
form.take_away_privileges.choices = [
(s.privilege_name,s.privilege_name.title()) \
import logging
-from mediagoblin.db.models import Notification, \
- CommentNotification, CommentSubscription, User
+from mediagoblin.db.models import Notification, CommentSubscription, User, \
+ Comment, GenericModelReference
from mediagoblin.notifications.task import email_notification_task
from mediagoblin.notifications.tools import generate_comment_message
if comment.get_actor == subscription.user:
continue
- cn = CommentNotification(
+ cn = Notification(
user_id=subscription.user_id,
- subject_id=comment.id)
-
+ )
+ cn.obj = comment
cn.save()
if subscription.send_email:
def mark_comment_notification_seen(comment_id, user):
- notification = CommentNotification.query.filter_by(
+ comment = Comment.query.get(comment_id).comment()
+ comment_gmr = GenericModelReference.query.filter_by(
+ obj_pk=comment.id,
+ model_type=comment.__tablename__
+ ).first()
+ notification = Notification.query.filter_by(
user_id=user.id,
- subject_id=comment_id).first()
+ object_id=comment_gmr.id
+ ).first()
_log.debug(u'Marking {0} as seen.'.format(notification))
from celery.task import Task
from mediagoblin.tools.mail import send_email
-from mediagoblin.db.models import CommentNotification
+from mediagoblin.db.models import Notification
_log = logging.getLogger(__name__)
the web server.
'''
def run(self, notification_id, message):
- cn = CommentNotification.query.filter_by(id=notification_id).first()
+ cn = Notification.query.filter_by(id=notification_id).first()
_log.info(u'Sending notification email about {0}'.format(cn))
return send_email(
Marks all comment notifications seen.
"""
for comment in get_notifications(request.user.id):
- mark_comment_notification_seen(comment.subject_id, request.user)
+ mark_comment_notification_seen(
+ comment.obj().get_comment_link().id,
+ request.user
+ )
if request.GET.get('next'):
return redirect(request, location=request.GET.get('next'))
# This will be set later but currently we just don't have enough information
entry.slug = None
+ # This is a MUST.
+ entry.get_public_id(request.urlgen)
+
queue_file = prepare_queue_task(request.app, entry, file_data.filename)
with queue_file:
queue_file.write(request.data)
<h3>{% trans %}New comments{% endtrans %}</h3>
<ul>
{% for notification in notifications %}
- {% set comment = notification.subject %}
+ {% set comment = notification.obj() %}
{% set comment_author = comment.get_actor %}
- {% set media = comment.get_entry %}
+ {% set media = comment.get_reply_to() %}
<li class="comment_wrapper">
<div class="comment_author">
<img src="{{ request.staticdirect('/images/icon_comment.png') }}" />
{% trans %}Return to Reports Panel{% endtrans %}</a>
</div>
<h2>{% trans %}Report{% endtrans %} #{{ report.id }}</h2>
- {% if report.is_comment_report() and report.comment %}
+ {% if report.is_comment_report() and report.object_id %}
{% trans %}Reported comment{% endtrans %}:
- {% set comment = report.comment %}
+ {% set comment = report.obj() %}
+ {% set target = report.obj().get_reply_to() %}
{% set reported_user = comment.get_actor %}
<div id="comment-{{ comment.id }}"
class="comment_wrapper">
<a href="{{ request.urlgen(
'mediagoblin.user_pages.media_home.view_comment',
comment=comment.id,
- user=comment.get_media_entry.get_actor.username,
- media=comment.get_media_entry.slug_or_id) }}#comment"
+ user=target.get_actor.username,
+ media=target.slug_or_id) }}#comment"
class="comment_whenlink">
<span title='{{- comment.created.strftime("%I:%M%p %Y-%m-%d") -}}'>
{%- trans formatted_time=timesince(comment.created) -%}
{% endautoescape %}
</div>
</div>
- {% elif report.is_media_entry_report() and report.media_entry %}
+ {% elif report.is_media_entry_report() and report.object_id %}
- {% set media_entry = report.media_entry %}
+ {% set media_entry = report.obj() %}
<div class="three columns media_thumbnail">
<a href="{{ request.urlgen('mediagoblin.user_pages.media_home',
user=media_entry.get_actor.username,
</tr>
{% for report in report_list %}
<tr>
- {% if report.discriminator == "comment_report" %}
+ {% if report.is_comment_report %}
<td>
<img
src="{{ request.staticdirect(
{% endtrans %}
</a>
</td>
- {% elif report.discriminator == "media_report" %}
+ {% elif report.is_media_entry_report %}
<td>
<img
src="{{ request.staticdirect(
</a>
</td>
<td>
- {% if report.discriminator == "comment_report" %}
+ {% if report.is_comment_report() %}
<a>{%- trans %}Reported Comment{% endtrans -%}</a>
- {% elif report.discriminator == "media_report" %}
+ {% elif report.is_media_entry_report() %}
<a>{%- trans %}Reported Media Entry{% endtrans -%}</a>
{% endif %}
</td>
<p>{{ media.description_html }}</p>
{% endautoescape %}
</div>
- {% if comments and request.user and request.user.has_privilege('commenter') %}
+ {% if request.user and request.user.has_privilege('commenter') %}
<div class="media_comments">
{% if app_config['allow_comments'] %}
<a
from .resources import GOOD_JPG
from mediagoblin import mg_globals
-from mediagoblin.db.models import User, MediaEntry, MediaComment
+from mediagoblin.db.models import User, MediaEntry, TextComment
from mediagoblin.tools.routing import extract_url_arguments
from mediagoblin.tests.tools import fixture_add_user
from mediagoblin.moderation.tools import take_away_privileges
# Lets change the image uploader to be self.other_user, this is easier
# than uploading the image as someone else as the way self.mocked_oauth_required
# and self._upload_image.
- id = int(data["object"]["id"].split("/")[-2])
- media = MediaEntry.query.filter_by(id=id).first()
+ media = MediaEntry.query.filter_by(public_id=data["object"]["id"]).first()
media.actor = self.other_user.id
media.save()
image = json.loads(response.body.decode())["object"]
# Check everything has been set on the media correctly
- id = int(image["id"].split("/")[-2])
- media = MediaEntry.query.filter_by(id=id).first()
+ media = MediaEntry.query.filter_by(public_id=image["id"]).first()
assert media.title == title
assert media.description == description
assert media.license == license
# Check we're being given back everything we should on an update
- assert int(image["id"].split("/")[-2]) == media.id
+ assert image["id"] == media.public_id
assert image["displayName"] == title
assert image["content"] == description
assert image["license"] == license
request = test_app.get(object_uri)
image = json.loads(request.body.decode())
- entry_id = int(image["id"].split("/")[-2])
- entry = MediaEntry.query.filter_by(id=entry_id).first()
+ entry = MediaEntry.query.filter_by(public_id=image["id"]).first()
assert request.status_code == 200
assert response.status_code == 200
# Find the objects in the database
- media_id = int(data["object"]["id"].split("/")[-2])
- media = MediaEntry.query.filter_by(id=media_id).first()
+ media = MediaEntry.query.filter_by(public_id=data["object"]["id"]).first()
comment = media.get_comments()[0]
# Tests that it matches in the database
response, comment_data = self._activity_to_feed(test_app, activity)
# change who uploaded the comment as it's easier than changing
- comment_id = int(comment_data["object"]["id"].split("/")[-2])
- comment = MediaComment.query.filter_by(id=comment_id).first()
+ comment = TextComment.query.filter_by(public_id=comment_data["object"]["id"]).first()
comment.actor = self.other_user.id
comment.save()
response = self._activity_to_feed(test_app, activity)[1]
# Check the media is no longer in the database
- media_id = int(object_id.split("/")[-2])
- media = MediaEntry.query.filter_by(id=media_id).first()
+ media = MediaEntry.query.filter_by(public_id=object_id).first()
assert media is None
delete = self._activity_to_feed(test_app, activity)[1]
# Verify the comment no longer exists
- comment_id = int(comment["object"]["id"].split("/")[-2])
- assert MediaComment.query.filter_by(id=comment_id).first() is None
+ assert TextComment.query.filter_by(public_id=comment["object"]["id"]).first() is None
+ comment_id = comment["object"]["id"]
# Check we've got a delete activity back
assert "id" in delete
comment = self._activity_to_feed(test_app, activity)[1]
# Verify the comment reflects the changes
- comment_id = int(comment["object"]["id"].split("/")[-2])
- model = MediaComment.query.filter_by(id=comment_id).first()
+ model = TextComment.query.filter_by(public_id=comment["object"]["id"]).first()
assert model.content == activity["object"]["content"]
from mediagoblin.media_types import sniff_media
from mediagoblin.submit.lib import new_upload_entry
from mediagoblin.submit.task import collect_garbage
-from mediagoblin.db.models import User, MediaEntry, MediaComment
+from mediagoblin.db.models import User, MediaEntry, TextComment, Comment
from mediagoblin.tests.tools import fixture_add_user, fixture_media_entry
Session.flush()
# Create all 4 possible comments:
- for u_id in (user_a.id, user_b.id):
- for m_id in (media_a.id, media_b.id):
- cmt = MediaComment()
- cmt.media_entry = m_id
- cmt.actor = u_id
+ for u in (user_a, user_b):
+ for m in (media_a, media_b):
+ cmt = TextComment()
+ cmt.actor = u.id
cmt.content = u"Some Comment"
Session.add(cmt)
+ # think i need this to get the command ID
+ Session.flush()
+
+ link = Comment()
+ link.target = m
+ link.comment = cmt
+ Session.add(link)
Session.flush()
usr_cnt1 = User.query.count()
med_cnt1 = MediaEntry.query.count()
- cmt_cnt1 = MediaComment.query.count()
+ cmt_cnt1 = TextComment.query.count()
User.query.get(user_a.id).delete(commit=False)
usr_cnt2 = User.query.count()
med_cnt2 = MediaEntry.query.count()
- cmt_cnt2 = MediaComment.query.count()
+ cmt_cnt2 = TextComment.query.count()
# One user deleted
assert usr_cnt2 == usr_cnt1 - 1
usr_cnt2 = User.query.count()
med_cnt2 = MediaEntry.query.count()
- cmt_cnt2 = MediaComment.query.count()
+ cmt_cnt2 = TextComment.query.count()
# All users gone
assert usr_cnt2 == usr_cnt1 - 2
from mediagoblin.tests.tools import (fixture_add_user,
fixture_add_comment_report, fixture_add_comment)
-from mediagoblin.db.models import User, LocalUser, CommentReport, MediaComment, UserBan
+from mediagoblin.db.models import User, LocalUser, Report, TextComment, \
+ UserBan, GenericModelReference
from mediagoblin.tools import template, mail
from webtest import AppError
# to a reported comment
#----------------------------------------------------------------------
fixture_add_comment_report(reported_user=self.user)
- comment_report = CommentReport.query.filter(
- CommentReport.reported_user==self.user).first()
+ comment_report = Report.query.filter(
+ Report.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()
+ comment_report = Report.query.filter(
+ Report.reported_user==self.user).first()
response, context = self.do_post({'action_to_resolve':[u'takeaway'],
'take_away_privileges':[u'commenter'],
url='/mod/reports/{0}/'.format(comment_report.id))
self.query_for_users()
- comment_report = CommentReport.query.filter(
- CommentReport.reported_user==self.user).first()
+ comment_report = Report.query.filter(
+ Report.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()
+ comment_report = Report.query.filter(
+ Report.reported_user==self.user).first()
# Then, test a moderator sending an email to a user in response to a
# reported comment
url='/mod/reports/{0}/'.format(comment_report.id))
self.query_for_users()
- comment_report = CommentReport.query.filter(
- CommentReport.reported_user==self.user).first()
+ comment_report = Report.query.filter(
+ Report.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\
self.query_for_users()
fixture_add_comment(author=self.user.id,
comment=u'Comment will be removed')
- test_comment = MediaComment.query.filter(
- MediaComment.actor==self.user.id).first()
+ test_comment = TextComment.query.filter(
+ TextComment.actor==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()
+ comment_gmr = GenericModelReference.query.filter_by(
+ obj_pk=test_comment.id,
+ model_type=test_comment.__tablename__
+ ).first()
+ comment_report = Report.query.filter(
+ Report.object_id==comment_gmr.id).filter(
+ Report.resolved==None).first()
response, context = self.do_post(
{'action_to_resolve':[u'userban', u'delete'],
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.actor==self.user.id).first()
+ test_comment = TextComment.query.filter(
+ TextComment.actor==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()
+ comment_report = Report.query.filter(
+ Report.reported_user==self.admin_user).filter(
+ Report.resolved==None).first()
response, context = self.do_post({'action_to_resolve':[u'takeaway'],
'take_away_privileges':[u'active'],
from mediagoblin.tools import template, mail
-from mediagoblin.db.models import Notification, CommentNotification, \
- CommentSubscription
+from mediagoblin.db.models import Notification, CommentSubscription
from mediagoblin.db.base import Session
from mediagoblin.notifications import mark_comment_notification_seen
notification = notifications[0]
- assert type(notification) == CommentNotification
assert notification.seen == False
assert notification.user_id == user.id
- assert notification.subject.get_actor.id == self.test_user.id
- assert notification.subject.content == u'Test comment #42'
+ assert notification.obj().get_actor.id == self.test_user.id
+ assert notification.obj().content == u'Test comment #42'
if wants_email == True:
assert mail.EMAIL_TEST_MBOX_INBOX == [
# Save the ids temporarily because of DetachedInstanceError
notification_id = notification.id
- comment_id = notification.subject.id
+ comment_id = notification.obj().get_comment_link().id
self.logout()
self.login('otherperson', 'nosreprehto')
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, LocalUser,
- MediaComment)
+from mediagoblin.db.models import Report, User, LocalUser, TextComment
class TestReportFiling:
assert response.status == "302 FOUND"
- media_report = MediaReport.query.first()
+ media_report = Report.query.first()
allie_user, natalie_user = self.query_for_users()
assert media_report is not None
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')
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()
+ fixture_add_comment(
+ media_entry=media_entry,
+ author=natalie_user.id
+ )
+ comment = TextComment.query.first()
comment_uri_slug = '/u/{0}/m/{1}/c/{2}/'.format(natalie_user.username,
media_entry.slug,
assert response.status == "302 FOUND"
- comment_report = CommentReport.query.first()
+ comment_report = Report.query.first()
allie_user, natalie_user = self.query_for_users()
assert comment_report is not None
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')
fixture_add_comment(author=allie_user.id,
comment=u'Comment will be removed')
- test_comment = MediaComment.query.filter(
- MediaComment.actor==allie_user.id).first()
+ test_comment = TextComment.query.filter(
+ TextComment.actor==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()
+ comment_report = Report.query.filter(
+ Report.reported_user==allie_user).first()
assert comment_report.report_content == u'Testing Archived Reports #1'
response, context = self.do_post(
assert response.status == "302 FOUND"
allie_user, natalie_user = self.query_for_users()
- archived_report = CommentReport.query.filter(
- CommentReport.reported_user==allie_user).first()
+ archived_report = Report.query.filter(
+ Report.reported_user==allie_user).first()
- assert CommentReport.query.count() != 0
+ assert Report.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.result == u'''This is a test of archiving reports.
natalie banned user allie indefinitely.
natalie deleted the comment.'''
- assert archived_report.discriminator == 'comment_report'
return {'upload_files': [('file', filename)]}
def check_comments(self, request, media_id, count):
- comments = request.db.MediaComment.query.filter_by(media_entry=media_id)
- assert count == len(list(comments))
+ gmr = request.db.GenericModelReference.query.filter_by(
+ obj_pk=media_id,
+ model_type=request.db.MediaEntry.__tablename__
+ ).first()
+ if gmr is None and count <= 0:
+ return # Yerp it's fine.
+ comments = request.db.Comment.query.filter_by(target_id=gmr.id)
+ assert count == comments.count()
def test_missing_fields(self):
# Test blank form
from webtest import TestApp
from mediagoblin import mg_globals
-from mediagoblin.db.models import User, LocalUser, MediaEntry, Collection, MediaComment, \
- CommentSubscription, CommentNotification, Privilege, CommentReport, Client, \
- RequestToken, AccessToken, Activity, Generator
+from mediagoblin.db.models import User, LocalUser, MediaEntry, Collection, TextComment, \
+ CommentSubscription, Notification, Privilege, Report, Client, \
+ RequestToken, AccessToken, Activity, Generator, Comment
from mediagoblin.tools import testing
from mediagoblin.init.config import read_mediagoblin_config
from mediagoblin.db.base import Session
return cs
-def fixture_add_comment_notification(entry_id, subject_id, user_id,
+def fixture_add_comment_notification(entry, subject, user,
seen=False):
- cn = CommentNotification(user_id=user_id,
- seen=seen,
- subject_id=subject_id)
+ cn = Notification(
+ user_id=user,
+ seen=seen,
+ )
+ cn.obj = subject
cn.save()
- cn = CommentNotification.query.filter_by(id=cn.id).first()
+ cn = Notification.query.filter_by(id=cn.id).first()
Session.expunge(cn)
author = fixture_add_user().id
if media_entry is None:
- media_entry = fixture_media_entry().id
+ media_entry = fixture_media_entry()
if comment is None:
comment = \
'Auto-generated test comment by user #{0} on media #{0}'.format(
author, media_entry)
- comment = MediaComment(actor=author,
- media_entry=media_entry,
- content=comment)
+ text_comment = TextComment(
+ actor=author,
+ content=comment
+ )
+ text_comment.save()
- comment.save()
+ comment_link = Comment()
+ comment_link.target = media_entry
+ comment_link.comment = text_comment
+ comment_link.save()
- Session.expunge(comment)
+ Session.expunge(comment_link)
- return comment
+ return text_comment
def fixture_add_comment_report(comment=None, reported_user=None,
reporter=None, created=None, report_content=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 = Report()
+ comment_report.obj = comment
+ comment_report.reported_user = reported_user
+ comment_report.reporter = reporter
+ comment_report.created = created
+ comment_report.report_content = report_content
+ comment_report.obj = comment
comment_report.save()
Session.expunge(comment_report)
from mediagoblin import mg_globals
from mediagoblin.db.base import Session
-from mediagoblin.db.models import (CollectionItem, MediaReport, CommentReport,
- MediaComment, MediaEntry)
+from mediagoblin.db.models import CollectionItem, Report, TextComment, \
+ MediaEntry
from mediagoblin.tools.mail import send_email
from mediagoblin.tools.pluginapi import hook_runall
from mediagoblin.tools.template import render_template
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.
+ report) into a Report.
: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
+ -rted by a Report.
+ :param comment A Comment object. The Comment being
+ reported by a Report.
+
+ :returns A Report object if a valid MediaReportForm is
+ passed as kwarg media_entry. This Report 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.
"""
-
+ report_object = Report()
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(
+ report_object.obj = comment.comment()
+ report_object.reported_user_id = TextComment.query.get(
comment.id).get_actor.id
elif report_form.validate() and media_entry is not None:
- report_object = MediaReport()
- report_object.media_entry_id = media_entry.id
+ report_object.obj = media_entry
report_object.reported_user_id = MediaEntry.query.get(
media_entry.id).get_actor.id
else:
import six
from mediagoblin import messages, mg_globals
-from mediagoblin.db.models import (MediaEntry, MediaTag, Collection,
- CollectionItem, LocalUser, Activity)
+from mediagoblin.db.models import (MediaEntry, MediaTag, Collection, Comment,
+ CollectionItem, LocalUser, Activity, \
+ GenericModelReference)
from mediagoblin.tools.response import render_to_response, render_404, \
redirect, redirect_obj
from mediagoblin.tools.text import cleaned_markdown_conversion
if not request.method == 'POST':
raise MethodNotAllowed()
- comment = request.db.MediaComment()
- comment.media_entry = media.id
+ comment = request.db.TextComment()
comment.actor = request.user.id
comment.content = six.text_type(request.form['comment_content'])
add_comment_subscription(request.user, media)
comment.save()
+ link = request.db.Comment()
+ link.target = media
+ link.comment = comment
+ link.save()
+
messages.add_message(
request, messages.SUCCESS,
_('Your comment has been posted!'))
@get_optional_media_comment_by_id
def file_a_report(request, media, comment):
"""
- This view handles the filing of a MediaReport or a CommentReport.
+ This view handles the filing of a Report.
"""
if comment is not None:
- if not comment.get_media_entry.id == media.id:
+ if not comment.target().id == media.id:
return render_404(request)
form = user_forms.CommentReportForm(request.form)
- context = {'media': media,
- 'comment':comment,
+ context = {'media': comment.target(),
+ 'comment':comment.comment(),
'form':form}
else:
form = user_forms.MediaReportForm(request.form)
if request.method == "POST":
- report_object = build_report_object(form,
+ report_object = build_report_object(
+ form,
media_entry=media,
- comment=comment)
+ comment=comment
+ )
# if the object was built successfully, report_table will not be None
if report_object: