Merge branch 'comments'
This commit is contained in:
commit
03bb1b7907
@ -22,7 +22,7 @@ from werkzeug.datastructures import FileStorage
|
||||
|
||||
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, \
|
||||
@ -268,7 +268,7 @@ def feed_endpoint(request, outbox=None):
|
||||
status=403
|
||||
)
|
||||
|
||||
comment = MediaComment(actor=request.user.id)
|
||||
comment = TextComment(actor=request.user.id)
|
||||
comment.unserialize(data["object"], request)
|
||||
comment.save()
|
||||
|
||||
@ -278,7 +278,7 @@ def feed_endpoint(request, outbox=None):
|
||||
verb="post",
|
||||
actor=request.user,
|
||||
obj=comment,
|
||||
target=comment.get_entry,
|
||||
target=comment.get_reply_to(),
|
||||
generator=generator
|
||||
)
|
||||
|
||||
@ -286,12 +286,22 @@ def feed_endpoint(request, outbox=None):
|
||||
|
||||
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(
|
||||
@ -345,10 +355,17 @@ def feed_endpoint(request, outbox=None):
|
||||
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":
|
||||
@ -358,7 +375,9 @@ def feed_endpoint(request, outbox=None):
|
||||
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)
|
||||
@ -391,7 +410,9 @@ def feed_endpoint(request, outbox=None):
|
||||
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"])
|
||||
@ -454,15 +475,22 @@ def feed_endpoint(request, outbox=None):
|
||||
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()
|
||||
|
||||
@ -491,7 +519,7 @@ def feed_endpoint(request, outbox=None):
|
||||
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()
|
||||
|
||||
@ -500,10 +528,6 @@ def feed_endpoint(request, outbox=None):
|
||||
"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(
|
||||
@ -621,7 +645,14 @@ def object_endpoint(request):
|
||||
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),
|
||||
@ -633,7 +664,13 @@ def object_endpoint(request):
|
||||
@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"],
|
||||
|
@ -13,6 +13,9 @@
|
||||
#
|
||||
# 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
|
||||
|
||||
@ -22,6 +25,30 @@ if not DISABLE_GLOBALS:
|
||||
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
|
||||
@ -93,7 +120,7 @@ class GMGTableBase(object):
|
||||
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.
|
||||
|
@ -37,7 +37,7 @@ from mediagoblin.tools import crypto
|
||||
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
|
||||
@ -353,7 +353,7 @@ class CommentNotification_v0(Notification_v0):
|
||||
__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):
|
||||
@ -542,7 +542,7 @@ class CommentReport_v0(ReportBase_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):
|
||||
@ -917,7 +917,7 @@ class ActivityIntermediator_R0(declarative_base()):
|
||||
TYPES = {
|
||||
"user": User,
|
||||
"media": MediaEntry,
|
||||
"comment": MediaComment,
|
||||
"comment": Comment,
|
||||
"collection": Collection,
|
||||
}
|
||||
|
||||
@ -1875,3 +1875,268 @@ def add_public_id(db):
|
||||
|
||||
# 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()
|
||||
|
@ -41,6 +41,47 @@ from mediagoblin.tools.text import cleaned_markdown_conversion
|
||||
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.
|
||||
@ -71,9 +112,10 @@ class GeneratePublicIDMixin(object):
|
||||
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):
|
||||
@ -342,7 +384,7 @@ class MediaEntryMixin(GenerateSlugMixin, GeneratePublicIDMixin):
|
||||
return exif_short
|
||||
|
||||
|
||||
class MediaCommentMixin(object):
|
||||
class TextCommentMixin(GeneratePublicIDMixin):
|
||||
object_type = "comment"
|
||||
|
||||
@property
|
||||
@ -367,7 +409,6 @@ class MediaCommentMixin(object):
|
||||
actor=self.get_actor,
|
||||
comment=self.content)
|
||||
|
||||
|
||||
class CollectionMixin(GenerateSlugMixin, GeneratePublicIDMixin):
|
||||
object_type = "collection"
|
||||
|
||||
@ -404,6 +445,28 @@ class CollectionMixin(GenerateSlugMixin, GeneratePublicIDMixin):
|
||||
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
|
||||
|
@ -36,10 +36,10 @@ from sqlalchemy.util import memoized_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
|
||||
@ -262,7 +262,7 @@ class User(Base, UserMixin):
|
||||
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
|
||||
@ -509,7 +509,7 @@ class NonceTimestamp(Base):
|
||||
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
|
||||
"""
|
||||
@ -595,11 +595,18 @@ class MediaEntry(Base, MediaEntryMixin):
|
||||
))
|
||||
|
||||
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(
|
||||
@ -689,7 +696,7 @@ class MediaEntry(Base, MediaEntryMixin):
|
||||
|
||||
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)
|
||||
@ -927,15 +934,63 @@ class MediaTag(Base):
|
||||
"""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.
|
||||
|
||||
class MediaComment(Base, MediaCommentMixin):
|
||||
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 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")
|
||||
@ -947,38 +1002,25 @@ class MediaComment(Base, MediaCommentMixin):
|
||||
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(),
|
||||
@ -991,34 +1033,47 @@ class MediaComment(Base, MediaCommentMixin):
|
||||
|
||||
def unserialize(self, data, request):
|
||||
""" Takes API objects and unserializes on existing comment """
|
||||
# Handle changing the reply ID
|
||||
if "inReplyTo" in data:
|
||||
# Validate that the ID is correct
|
||||
try:
|
||||
media_id = int(extract_url_arguments(
|
||||
url=data["inReplyTo"]["id"],
|
||||
urlmap=request.app.url_map
|
||||
)["id"])
|
||||
except ValueError:
|
||||
return False
|
||||
|
||||
media = MediaEntry.query.filter_by(id=media_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)
|
||||
|
||||
|
||||
# Handle changing the reply ID
|
||||
if "inReplyTo" in data:
|
||||
# Validate that the ID is correct
|
||||
try:
|
||||
id = extract_url_arguments(
|
||||
url=data["inReplyTo"]["id"],
|
||||
urlmap=request.app.url_map
|
||||
)["id"]
|
||||
except ValueError:
|
||||
raise False
|
||||
|
||||
public_id = request.urlgen(
|
||||
"mediagoblin.api.object",
|
||||
id=id,
|
||||
object_type=data["inReplyTo"]["objectType"],
|
||||
qualified=True
|
||||
)
|
||||
|
||||
media = MediaEntry.query.filter_by(public_id=public_id).first()
|
||||
if media is None:
|
||||
return False
|
||||
|
||||
# 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
|
||||
@ -1070,6 +1125,7 @@ class Collection(Base, CollectionMixin):
|
||||
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):
|
||||
@ -1201,21 +1257,19 @@ class CommentSubscription(Base):
|
||||
class Notification(Base):
|
||||
__tablename__ = 'core__notifications'
|
||||
id = Column(Integer, primary_key=True)
|
||||
type = Column(Unicode)
|
||||
|
||||
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(
|
||||
@ -1233,42 +1287,9 @@ class Notification(Base):
|
||||
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.
|
||||
@ -1281,8 +1302,6 @@ class ReportBase(Base):
|
||||
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-
|
||||
@ -1291,8 +1310,11 @@ class ReportBase(Base):
|
||||
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(
|
||||
@ -1300,7 +1322,7 @@ class ReportBase(Base):
|
||||
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(
|
||||
@ -1308,69 +1330,42 @@ class ReportBase(Base):
|
||||
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}
|
||||
|
||||
def is_comment_report(self):
|
||||
return self.discriminator=='comment_report'
|
||||
|
||||
def is_media_entry_report(self):
|
||||
return self.discriminator=='media_report'
|
||||
|
||||
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):
|
||||
if self.object_id is None:
|
||||
return False
|
||||
return isinstance(self.obj(), TextComment)
|
||||
|
||||
def is_media_entry_report(self):
|
||||
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
|
||||
@ -1576,18 +1571,12 @@ class Graveyard(Base):
|
||||
"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
|
||||
|
@ -23,7 +23,8 @@ from six.moves.urllib.parse import urljoin
|
||||
|
||||
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)
|
||||
@ -325,11 +326,11 @@ def allow_reporting(controller):
|
||||
|
||||
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.
|
||||
@ -339,8 +340,9 @@ def get_optional_media_comment_by_id(controller):
|
||||
@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)
|
||||
|
@ -68,14 +68,14 @@ def take_punitive_actions(request, form, report, user):
|
||||
|
||||
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(
|
||||
|
@ -15,7 +15,7 @@
|
||||
# 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,
|
||||
@ -83,9 +83,9 @@ def moderation_users_detail(request):
|
||||
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()
|
||||
@ -116,23 +116,23 @@ def moderation_reports_panel(request):
|
||||
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.))
|
||||
@ -155,7 +155,7 @@ def moderation_reports_detail(request):
|
||||
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()) \
|
||||
|
@ -16,8 +16,8 @@
|
||||
|
||||
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
|
||||
|
||||
@ -37,10 +37,10 @@ def trigger_notification(comment, media_entry, request):
|
||||
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:
|
||||
@ -61,9 +61,15 @@ def mark_notification_seen(notification):
|
||||
|
||||
|
||||
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))
|
||||
|
||||
|
@ -20,7 +20,7 @@ from celery import registry
|
||||
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__)
|
||||
@ -34,7 +34,7 @@ class EmailNotificationTask(Task):
|
||||
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(
|
||||
|
@ -57,7 +57,10 @@ def mark_all_comment_notifications_seen(request):
|
||||
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'))
|
||||
|
@ -281,6 +281,9 @@ def api_upload_request(request, file_data, entry):
|
||||
# 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)
|
||||
|
@ -4,9 +4,9 @@
|
||||
<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') }}" />
|
||||
|
@ -33,10 +33,11 @@
|
||||
{% 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">
|
||||
@ -50,8 +51,8 @@
|
||||
<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) -%}
|
||||
@ -65,9 +66,9 @@
|
||||
{% 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,
|
||||
|
@ -81,7 +81,7 @@ curr_page !=p %}
|
||||
</tr>
|
||||
{% for report in report_list %}
|
||||
<tr>
|
||||
{% if report.discriminator == "comment_report" %}
|
||||
{% if report.is_comment_report %}
|
||||
<td>
|
||||
<img
|
||||
src="{{ request.staticdirect(
|
||||
@ -97,7 +97,7 @@ curr_page !=p %}
|
||||
{% endtrans %}
|
||||
</a>
|
||||
</td>
|
||||
{% elif report.discriminator == "media_report" %}
|
||||
{% elif report.is_media_entry_report %}
|
||||
<td>
|
||||
<img
|
||||
src="{{ request.staticdirect(
|
||||
|
@ -125,9 +125,9 @@
|
||||
</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>
|
||||
|
@ -93,7 +93,7 @@
|
||||
<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
|
||||
|
@ -25,7 +25,7 @@ from webtest import AppError
|
||||
|
||||
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
|
||||
@ -188,8 +188,7 @@ class TestAPI(object):
|
||||
# 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()
|
||||
|
||||
@ -232,14 +231,13 @@ class TestAPI(object):
|
||||
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
|
||||
@ -288,8 +286,7 @@ class TestAPI(object):
|
||||
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
|
||||
|
||||
@ -319,8 +316,7 @@ class TestAPI(object):
|
||||
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
|
||||
@ -382,8 +378,7 @@ class TestAPI(object):
|
||||
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()
|
||||
|
||||
@ -510,8 +505,7 @@ class TestAPI(object):
|
||||
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
|
||||
|
||||
@ -552,8 +546,8 @@ class TestAPI(object):
|
||||
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
|
||||
@ -593,7 +587,6 @@ class TestAPI(object):
|
||||
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"]
|
||||
|
@ -24,7 +24,7 @@ from mediagoblin.db.base import Session
|
||||
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
|
||||
|
||||
|
||||
@ -46,25 +46,31 @@ def test_user_deletes_other_comments(test_app):
|
||||
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
|
||||
@ -77,7 +83,7 @@ def test_user_deletes_other_comments(test_app):
|
||||
|
||||
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
|
||||
|
@ -18,7 +18,8 @@ import pytest
|
||||
|
||||
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
|
||||
|
||||
@ -102,15 +103,15 @@ class TestModerationViews:
|
||||
# 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'],
|
||||
@ -118,15 +119,15 @@ class TestModerationViews:
|
||||
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
|
||||
@ -139,8 +140,8 @@ class TestModerationViews:
|
||||
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\
|
||||
@ -157,13 +158,17 @@ VGhpcyBpcyB5b3VyIGxhc3Qgd2FybmluZywgcmVndWxhci4uLi4=\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'],
|
||||
@ -176,17 +181,17 @@ VGhpcyBpcyB5b3VyIGxhc3Qgd2FybmluZywgcmVndWxhci4uLi4=\n',
|
||||
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'],
|
||||
|
@ -20,8 +20,7 @@ import six.moves.urllib.parse as urlparse
|
||||
|
||||
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
|
||||
@ -109,11 +108,10 @@ class TestNotifications:
|
||||
|
||||
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 == [
|
||||
@ -130,7 +128,7 @@ otherperson@example.com\n\nSGkgb3RoZXJwZXJzb24sCmNocmlzIGNvbW1lbnRlZCBvbiB5b3VyI
|
||||
|
||||
# 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')
|
||||
|
@ -20,8 +20,7 @@ import six
|
||||
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:
|
||||
@ -80,7 +79,7 @@ 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
|
||||
@ -88,7 +87,6 @@ class TestReportFiling:
|
||||
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')
|
||||
@ -98,9 +96,11 @@ class TestReportFiling:
|
||||
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,
|
||||
@ -115,7 +115,7 @@ class TestReportFiling:
|
||||
|
||||
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
|
||||
@ -123,7 +123,6 @@ class TestReportFiling:
|
||||
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')
|
||||
@ -132,14 +131,14 @@ class TestReportFiling:
|
||||
|
||||
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(
|
||||
@ -151,10 +150,10 @@ class TestReportFiling:
|
||||
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
|
||||
@ -164,4 +163,3 @@ class TestReportFiling:
|
||||
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'
|
||||
|
@ -99,8 +99,14 @@ class TestSubmission:
|
||||
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
|
||||
|
@ -25,9 +25,9 @@ from paste.deploy import loadapp
|
||||
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
|
||||
@ -222,14 +222,16 @@ def fixture_comment_subscription(entry, notify=True, send_email=None):
|
||||
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)
|
||||
|
||||
@ -309,22 +311,27 @@ def fixture_add_comment(author=None, media_entry=None, comment=None):
|
||||
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):
|
||||
@ -344,12 +351,13 @@ def fixture_add_comment_report(comment=None, reported_user=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)
|
||||
|
@ -16,8 +16,8 @@
|
||||
|
||||
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
|
||||
@ -82,34 +82,27 @@ def add_media_to_collection(collection, media, note=None, commit=True):
|
||||
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.
|
||||
-rted by a Report.
|
||||
:param comment A Comment object. The Comment being
|
||||
reported by a Report.
|
||||
|
||||
:returns A MediaReport object if a valid MediaReportForm is
|
||||
passed as kwarg media_entry. This MediaReport has
|
||||
: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:
|
||||
|
@ -21,8 +21,9 @@ import json
|
||||
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
|
||||
@ -178,8 +179,7 @@ def media_post_comment(request, media):
|
||||
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'])
|
||||
|
||||
@ -199,6 +199,11 @@ def media_post_comment(request, media):
|
||||
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!'))
|
||||
@ -682,15 +687,15 @@ def processing_panel(request):
|
||||
@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)
|
||||
@ -700,9 +705,11 @@ def file_a_report(request, media, comment):
|
||||
|
||||
|
||||
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:
|
||||
|
Loading…
x
Reference in New Issue
Block a user