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.decorators import oauth_required, require_active_login
|
||||||
from mediagoblin.api.decorators import user_has_privilege
|
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.federation import create_activity, create_generator
|
||||||
from mediagoblin.tools.routing import extract_url_arguments
|
from mediagoblin.tools.routing import extract_url_arguments
|
||||||
from mediagoblin.tools.response import redirect, json_response, json_error, \
|
from mediagoblin.tools.response import redirect, json_response, json_error, \
|
||||||
@ -268,7 +268,7 @@ def feed_endpoint(request, outbox=None):
|
|||||||
status=403
|
status=403
|
||||||
)
|
)
|
||||||
|
|
||||||
comment = MediaComment(actor=request.user.id)
|
comment = TextComment(actor=request.user.id)
|
||||||
comment.unserialize(data["object"], request)
|
comment.unserialize(data["object"], request)
|
||||||
comment.save()
|
comment.save()
|
||||||
|
|
||||||
@ -278,7 +278,7 @@ def feed_endpoint(request, outbox=None):
|
|||||||
verb="post",
|
verb="post",
|
||||||
actor=request.user,
|
actor=request.user,
|
||||||
obj=comment,
|
obj=comment,
|
||||||
target=comment.get_entry,
|
target=comment.get_reply_to(),
|
||||||
generator=generator
|
generator=generator
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -286,12 +286,22 @@ def feed_endpoint(request, outbox=None):
|
|||||||
|
|
||||||
elif obj.get("objectType", None) == "image":
|
elif obj.get("objectType", None) == "image":
|
||||||
# Posting an image to the feed
|
# Posting an image to the feed
|
||||||
media_id = int(extract_url_arguments(
|
media_id = extract_url_arguments(
|
||||||
url=data["object"]["id"],
|
url=data["object"]["id"],
|
||||||
urlmap=request.app.url_map
|
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:
|
if media is None:
|
||||||
return json_response(
|
return json_response(
|
||||||
@ -345,10 +355,17 @@ def feed_endpoint(request, outbox=None):
|
|||||||
if "id" not in obj:
|
if "id" not in obj:
|
||||||
return json_error("Object ID has not been specified.")
|
return json_error("Object ID has not been specified.")
|
||||||
|
|
||||||
obj_id = int(extract_url_arguments(
|
obj_id = extract_url_arguments(
|
||||||
url=obj["id"],
|
url=obj["id"],
|
||||||
urlmap=request.app.url_map
|
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
|
# Now try and find object
|
||||||
if obj["objectType"] == "comment":
|
if obj["objectType"] == "comment":
|
||||||
@ -358,7 +375,9 @@ def feed_endpoint(request, outbox=None):
|
|||||||
status=403
|
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:
|
if comment is None:
|
||||||
return json_error(
|
return json_error(
|
||||||
"No such 'comment' with id '{0}'.".format(obj_id)
|
"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))
|
return json_response(activity.serialize(request))
|
||||||
|
|
||||||
elif obj["objectType"] == "image":
|
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:
|
if image is None:
|
||||||
return json_error(
|
return json_error(
|
||||||
"No such 'image' with the id '{0}'.".format(obj["id"])
|
"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.")
|
return json_error("Object ID has not been specified.")
|
||||||
|
|
||||||
# Parse out the object ID
|
# Parse out the object ID
|
||||||
obj_id = int(extract_url_arguments(
|
obj_id = extract_url_arguments(
|
||||||
url=obj["id"],
|
url=obj["id"],
|
||||||
urlmap=request.app.url_map
|
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":
|
if obj.get("objectType", None) == "comment":
|
||||||
# Find the comment asked for
|
# Find the comment asked for
|
||||||
comment = MediaComment.query.filter_by(
|
comment = TextComment.query.filter_by(
|
||||||
id=obj_id,
|
public_id=public_id,
|
||||||
actor=request.user.id
|
actor=request.user.id
|
||||||
).first()
|
).first()
|
||||||
|
|
||||||
@ -491,7 +519,7 @@ def feed_endpoint(request, outbox=None):
|
|||||||
if obj.get("objectType", None) == "image":
|
if obj.get("objectType", None) == "image":
|
||||||
# Find the image
|
# Find the image
|
||||||
entry = MediaEntry.query.filter_by(
|
entry = MediaEntry.query.filter_by(
|
||||||
id=obj_id,
|
public_id=public_id,
|
||||||
actor=request.user.id
|
actor=request.user.id
|
||||||
).first()
|
).first()
|
||||||
|
|
||||||
@ -500,10 +528,6 @@ def feed_endpoint(request, outbox=None):
|
|||||||
"No such 'image' with id '{0}'.".format(obj_id)
|
"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
|
# Make the delete activity
|
||||||
generator = create_generator(request)
|
generator = create_generator(request)
|
||||||
activity = create_activity(
|
activity = create_activity(
|
||||||
@ -621,7 +645,14 @@ def object_endpoint(request):
|
|||||||
status=404
|
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:
|
if media is None:
|
||||||
return json_error(
|
return json_error(
|
||||||
"Can't find '{0}' with ID '{1}'".format(object_type, object_id),
|
"Can't find '{0}' with ID '{1}'".format(object_type, object_id),
|
||||||
@ -633,7 +664,13 @@ def object_endpoint(request):
|
|||||||
@oauth_required
|
@oauth_required
|
||||||
def object_comments(request):
|
def object_comments(request):
|
||||||
""" Looks up for the comments on a object """
|
""" 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:
|
if media is None:
|
||||||
return json_error("Can't find '{0}' with ID '{1}'".format(
|
return json_error("Can't find '{0}' with ID '{1}'".format(
|
||||||
request.matchdict["object_type"],
|
request.matchdict["object_type"],
|
||||||
|
@ -13,6 +13,9 @@
|
|||||||
#
|
#
|
||||||
# You should have received a copy of the GNU Affero General Public License
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
import six
|
||||||
|
import copy
|
||||||
|
|
||||||
from sqlalchemy.ext.declarative import declarative_base
|
from sqlalchemy.ext.declarative import declarative_base
|
||||||
from sqlalchemy import inspect
|
from sqlalchemy import inspect
|
||||||
|
|
||||||
@ -22,6 +25,30 @@ if not DISABLE_GLOBALS:
|
|||||||
from sqlalchemy.orm import scoped_session, sessionmaker
|
from sqlalchemy.orm import scoped_session, sessionmaker
|
||||||
Session = 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):
|
class GMGTableBase(object):
|
||||||
# Deletion types
|
# Deletion types
|
||||||
@ -93,7 +120,7 @@ class GMGTableBase(object):
|
|||||||
id=self.actor
|
id=self.actor
|
||||||
).first()
|
).first()
|
||||||
tombstone.object_type = self.object_type
|
tombstone.object_type = self.object_type
|
||||||
tombstone.save()
|
tombstone.save(commit=False)
|
||||||
|
|
||||||
# There will be a lot of places where the GenericForeignKey will point
|
# There will be a lot of places where the GenericForeignKey will point
|
||||||
# to the model, we want to remap those to our tombstone.
|
# 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.extratypes import JSONEncoded, MutationDict
|
||||||
from mediagoblin.db.migration_tools import (
|
from mediagoblin.db.migration_tools import (
|
||||||
RegisterMigration, inspect_table, replace_table_hack)
|
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,
|
Privilege, Generator, LocalUser, Location,
|
||||||
Client, RequestToken, AccessToken)
|
Client, RequestToken, AccessToken)
|
||||||
from mediagoblin.db.extratypes import JSONEncoded, MutationDict
|
from mediagoblin.db.extratypes import JSONEncoded, MutationDict
|
||||||
@ -353,7 +353,7 @@ class CommentNotification_v0(Notification_v0):
|
|||||||
__tablename__ = 'core__comment_notifications'
|
__tablename__ = 'core__comment_notifications'
|
||||||
id = Column(Integer, ForeignKey(Notification_v0.id), primary_key=True)
|
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):
|
class ProcessingNotification_v0(Notification_v0):
|
||||||
@ -542,7 +542,7 @@ class CommentReport_v0(ReportBase_v0):
|
|||||||
|
|
||||||
id = Column('id',Integer, ForeignKey('core__reports.id'),
|
id = Column('id',Integer, ForeignKey('core__reports.id'),
|
||||||
primary_key=True)
|
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):
|
class MediaReport_v0(ReportBase_v0):
|
||||||
@ -917,7 +917,7 @@ class ActivityIntermediator_R0(declarative_base()):
|
|||||||
TYPES = {
|
TYPES = {
|
||||||
"user": User,
|
"user": User,
|
||||||
"media": MediaEntry,
|
"media": MediaEntry,
|
||||||
"comment": MediaComment,
|
"comment": Comment,
|
||||||
"collection": Collection,
|
"collection": Collection,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1875,3 +1875,268 @@ def add_public_id(db):
|
|||||||
|
|
||||||
# Commit this.
|
# Commit this.
|
||||||
db.commit()
|
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.url import slugify
|
||||||
from mediagoblin.tools.translate import pass_to_ugettext as _
|
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):
|
class GeneratePublicIDMixin(object):
|
||||||
"""
|
"""
|
||||||
Mixin that ensures that a the public_id field is populated.
|
Mixin that ensures that a the public_id field is populated.
|
||||||
@ -71,9 +112,10 @@ class GeneratePublicIDMixin(object):
|
|||||||
self.public_id = urlgen(
|
self.public_id = urlgen(
|
||||||
"mediagoblin.api.object",
|
"mediagoblin.api.object",
|
||||||
object_type=self.object_type,
|
object_type=self.object_type,
|
||||||
id=self.id,
|
id=str(uuid.uuid4()),
|
||||||
qualified=True
|
qualified=True
|
||||||
)
|
)
|
||||||
|
self.save()
|
||||||
return self.public_id
|
return self.public_id
|
||||||
|
|
||||||
class UserMixin(object):
|
class UserMixin(object):
|
||||||
@ -342,7 +384,7 @@ class MediaEntryMixin(GenerateSlugMixin, GeneratePublicIDMixin):
|
|||||||
return exif_short
|
return exif_short
|
||||||
|
|
||||||
|
|
||||||
class MediaCommentMixin(object):
|
class TextCommentMixin(GeneratePublicIDMixin):
|
||||||
object_type = "comment"
|
object_type = "comment"
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -367,7 +409,6 @@ class MediaCommentMixin(object):
|
|||||||
actor=self.get_actor,
|
actor=self.get_actor,
|
||||||
comment=self.content)
|
comment=self.content)
|
||||||
|
|
||||||
|
|
||||||
class CollectionMixin(GenerateSlugMixin, GeneratePublicIDMixin):
|
class CollectionMixin(GenerateSlugMixin, GeneratePublicIDMixin):
|
||||||
object_type = "collection"
|
object_type = "collection"
|
||||||
|
|
||||||
@ -404,6 +445,28 @@ class CollectionMixin(GenerateSlugMixin, GeneratePublicIDMixin):
|
|||||||
collection=self.slug_or_id,
|
collection=self.slug_or_id,
|
||||||
**extra_args)
|
**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):
|
class CollectionItemMixin(object):
|
||||||
@property
|
@property
|
||||||
|
@ -36,10 +36,10 @@ from sqlalchemy.util import memoized_property
|
|||||||
|
|
||||||
from mediagoblin.db.extratypes import (PathTupleWithSlashes, JSONEncoded,
|
from mediagoblin.db.extratypes import (PathTupleWithSlashes, JSONEncoded,
|
||||||
MutationDict)
|
MutationDict)
|
||||||
from mediagoblin.db.base import Base, DictReadAttrProxy
|
from mediagoblin.db.base import Base, DictReadAttrProxy, FakeCursor
|
||||||
from mediagoblin.db.mixin import UserMixin, MediaEntryMixin, \
|
from mediagoblin.db.mixin import UserMixin, MediaEntryMixin, \
|
||||||
MediaCommentMixin, CollectionMixin, CollectionItemMixin, \
|
CollectionMixin, CollectionItemMixin, ActivityMixin, TextCommentMixin, \
|
||||||
ActivityMixin
|
CommentingMixin
|
||||||
from mediagoblin.tools.files import delete_media_files
|
from mediagoblin.tools.files import delete_media_files
|
||||||
from mediagoblin.tools.common import import_component
|
from mediagoblin.tools.common import import_component
|
||||||
from mediagoblin.tools.routing import extract_url_arguments
|
from mediagoblin.tools.routing import extract_url_arguments
|
||||||
@ -262,7 +262,7 @@ class User(Base, UserMixin):
|
|||||||
collection.delete(**kwargs)
|
collection.delete(**kwargs)
|
||||||
|
|
||||||
# Find all the comments and delete those too
|
# 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)
|
comment.delete(**kwargs)
|
||||||
|
|
||||||
# Find all the activities and delete those too
|
# Find all the activities and delete those too
|
||||||
@ -509,7 +509,7 @@ class NonceTimestamp(Base):
|
|||||||
nonce = Column(Unicode, nullable=False, primary_key=True)
|
nonce = Column(Unicode, nullable=False, primary_key=True)
|
||||||
timestamp = Column(DateTime, 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
|
TODO: Consider fetching the media_files using join
|
||||||
"""
|
"""
|
||||||
@ -595,11 +595,18 @@ class MediaEntry(Base, MediaEntryMixin):
|
|||||||
))
|
))
|
||||||
|
|
||||||
def get_comments(self, ascending=False):
|
def get_comments(self, ascending=False):
|
||||||
order_col = MediaComment.created
|
query = Comment.query.join(Comment.target_helper).filter(and_(
|
||||||
if not ascending:
|
GenericModelReference.obj_pk == self.id,
|
||||||
order_col = desc(order_col)
|
GenericModelReference.model_type == self.__tablename__
|
||||||
return self.all_comments.order_by(order_col)
|
))
|
||||||
|
|
||||||
|
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):
|
def url_to_prev(self, urlgen):
|
||||||
"""get the next 'newer' entry by this user"""
|
"""get the next 'newer' entry by this user"""
|
||||||
media = MediaEntry.query.filter(
|
media = MediaEntry.query.filter(
|
||||||
@ -689,7 +696,7 @@ class MediaEntry(Base, MediaEntryMixin):
|
|||||||
|
|
||||||
def soft_delete(self, *args, **kwargs):
|
def soft_delete(self, *args, **kwargs):
|
||||||
# Find all of the media comments for this and delete them
|
# 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)
|
comment.delete(*args, **kwargs)
|
||||||
|
|
||||||
super(MediaEntry, self).soft_delete(*args, **kwargs)
|
super(MediaEntry, self).soft_delete(*args, **kwargs)
|
||||||
@ -927,15 +934,63 @@ class MediaTag(Base):
|
|||||||
"""A dict like view on this object"""
|
"""A dict like view on this object"""
|
||||||
return DictReadAttrProxy(self)
|
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"
|
__tablename__ = "core__media_comments"
|
||||||
|
|
||||||
id = Column(Integer, primary_key=True)
|
id = Column(Integer, primary_key=True)
|
||||||
media_entry = Column(
|
public_id = Column(Unicode, unique=True)
|
||||||
Integer, ForeignKey(MediaEntry.id), nullable=False, index=True)
|
|
||||||
actor = Column(Integer, ForeignKey(User.id), nullable=False)
|
actor = Column(Integer, ForeignKey(User.id), nullable=False)
|
||||||
created = Column(DateTime, nullable=False, default=datetime.datetime.utcnow)
|
created = Column(DateTime, nullable=False, default=datetime.datetime.utcnow)
|
||||||
|
updated = Column(DateTime, nullable=False, default=datetime.datetime.utcnow)
|
||||||
content = Column(UnicodeText, nullable=False)
|
content = Column(UnicodeText, nullable=False)
|
||||||
location = Column(Integer, ForeignKey("core__locations.id"))
|
location = Column(Integer, ForeignKey("core__locations.id"))
|
||||||
get_location = relationship("Location", lazy="joined")
|
get_location = relationship("Location", lazy="joined")
|
||||||
@ -947,38 +1002,25 @@ class MediaComment(Base, MediaCommentMixin):
|
|||||||
backref=backref("posted_comments",
|
backref=backref("posted_comments",
|
||||||
lazy="dynamic",
|
lazy="dynamic",
|
||||||
cascade="all, delete-orphan"))
|
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
|
deletion_mode = Base.SOFT_DELETE
|
||||||
|
|
||||||
def serialize(self, request):
|
def serialize(self, request):
|
||||||
""" Unserialize to python dictionary for API """
|
""" Unserialize to python dictionary for API """
|
||||||
href = request.urlgen(
|
target = self.get_reply_to()
|
||||||
"mediagoblin.api.object",
|
# If this is target just.. give them nothing?
|
||||||
object_type=self.object_type,
|
if target is None:
|
||||||
id=self.id,
|
target = {}
|
||||||
qualified=True
|
else:
|
||||||
)
|
target = target.serialize(request, show_comments=False)
|
||||||
media = MediaEntry.query.filter_by(id=self.media_entry).first()
|
|
||||||
|
|
||||||
author = self.get_actor
|
author = self.get_actor
|
||||||
published = UTC.localize(self.created)
|
published = UTC.localize(self.created)
|
||||||
context = {
|
context = {
|
||||||
"id": href,
|
"id": self.get_public_id(request.urlgen),
|
||||||
"objectType": self.object_type,
|
"objectType": self.object_type,
|
||||||
"content": self.content,
|
"content": self.content,
|
||||||
"inReplyTo": media.serialize(request, show_comments=False),
|
"inReplyTo": target,
|
||||||
"author": author.serialize(request),
|
"author": author.serialize(request),
|
||||||
"published": published.isoformat(),
|
"published": published.isoformat(),
|
||||||
"updated": published.isoformat(),
|
"updated": published.isoformat(),
|
||||||
@ -991,34 +1033,47 @@ class MediaComment(Base, MediaCommentMixin):
|
|||||||
|
|
||||||
def unserialize(self, data, request):
|
def unserialize(self, data, request):
|
||||||
""" Takes API objects and unserializes on existing comment """
|
""" 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:
|
if "content" in data:
|
||||||
self.content = data["content"]
|
self.content = data["content"]
|
||||||
|
|
||||||
if "location" in data:
|
if "location" in data:
|
||||||
Location.create(data["location"], self)
|
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
|
return True
|
||||||
|
|
||||||
|
class Collection(Base, CollectionMixin, CommentingMixin):
|
||||||
|
|
||||||
class Collection(Base, CollectionMixin):
|
|
||||||
"""A representation of a collection of objects.
|
"""A representation of a collection of objects.
|
||||||
|
|
||||||
This holds a group/collection of objects that could be a user defined album
|
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"
|
OUTBOX_TYPE = "core-outbox"
|
||||||
FOLLOWER_TYPE = "core-followers"
|
FOLLOWER_TYPE = "core-followers"
|
||||||
FOLLOWING_TYPE = "core-following"
|
FOLLOWING_TYPE = "core-following"
|
||||||
|
COMMENT_TYPE = "core-comments"
|
||||||
USER_DEFINED_TYPE = "core-user-defined"
|
USER_DEFINED_TYPE = "core-user-defined"
|
||||||
|
|
||||||
def get_collection_items(self, ascending=False):
|
def get_collection_items(self, ascending=False):
|
||||||
@ -1201,21 +1257,19 @@ class CommentSubscription(Base):
|
|||||||
class Notification(Base):
|
class Notification(Base):
|
||||||
__tablename__ = 'core__notifications'
|
__tablename__ = 'core__notifications'
|
||||||
id = Column(Integer, primary_key=True)
|
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)
|
created = Column(DateTime, nullable=False, default=datetime.datetime.utcnow)
|
||||||
|
|
||||||
user_id = Column(Integer, ForeignKey('core__users.id'), nullable=False,
|
user_id = Column(Integer, ForeignKey('core__users.id'), nullable=False,
|
||||||
index=True)
|
index=True)
|
||||||
seen = Column(Boolean, default=lambda: False, index=True)
|
seen = Column(Boolean, default=lambda: False, index=True)
|
||||||
user = relationship(
|
user = relationship(
|
||||||
User,
|
User,
|
||||||
backref=backref('notifications', cascade='all, delete-orphan'))
|
backref=backref('notifications', cascade='all, delete-orphan'))
|
||||||
|
|
||||||
__mapper_args__ = {
|
|
||||||
'polymorphic_identity': 'notification',
|
|
||||||
'polymorphic_on': type
|
|
||||||
}
|
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return '<{klass} #{id}: {user}: {subject} ({seen})>'.format(
|
return '<{klass} #{id}: {user}: {subject} ({seen})>'.format(
|
||||||
@ -1233,42 +1287,9 @@ class Notification(Base):
|
|||||||
subject=getattr(self, 'subject', None),
|
subject=getattr(self, 'subject', None),
|
||||||
seen='unseen' if not self.seen else 'seen')
|
seen='unseen' if not self.seen else 'seen')
|
||||||
|
|
||||||
|
class Report(Base):
|
||||||
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):
|
|
||||||
"""
|
"""
|
||||||
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
|
:keyword reporter_id Holds the id of the user who created
|
||||||
the report, as an Integer column.
|
the report, as an Integer column.
|
||||||
@ -1281,8 +1302,6 @@ class ReportBase(Base):
|
|||||||
an Integer column.
|
an Integer column.
|
||||||
:keyword created Holds a datetime column of when the re-
|
:keyword created Holds a datetime column of when the re-
|
||||||
-port was filed.
|
-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
|
:keyword resolver_id Holds the id of the moderator/admin who
|
||||||
resolved the report.
|
resolved the report.
|
||||||
:keyword resolved Holds the DateTime object which descri-
|
:keyword resolved Holds the DateTime object which descri-
|
||||||
@ -1291,8 +1310,11 @@ class ReportBase(Base):
|
|||||||
resolver's reasons for resolving
|
resolver's reasons for resolving
|
||||||
the report this way. Some of this
|
the report this way. Some of this
|
||||||
is auto-generated
|
is auto-generated
|
||||||
|
:keyword object_id Holds the ID of the GenericModelReference
|
||||||
|
which points to the reported object.
|
||||||
"""
|
"""
|
||||||
__tablename__ = 'core__reports'
|
__tablename__ = 'core__reports'
|
||||||
|
|
||||||
id = Column(Integer, primary_key=True)
|
id = Column(Integer, primary_key=True)
|
||||||
reporter_id = Column(Integer, ForeignKey(User.id), nullable=False)
|
reporter_id = Column(Integer, ForeignKey(User.id), nullable=False)
|
||||||
reporter = relationship(
|
reporter = relationship(
|
||||||
@ -1300,7 +1322,7 @@ class ReportBase(Base):
|
|||||||
backref=backref("reports_filed_by",
|
backref=backref("reports_filed_by",
|
||||||
lazy="dynamic",
|
lazy="dynamic",
|
||||||
cascade="all, delete-orphan"),
|
cascade="all, delete-orphan"),
|
||||||
primaryjoin="User.id==ReportBase.reporter_id")
|
primaryjoin="User.id==Report.reporter_id")
|
||||||
report_content = Column(UnicodeText)
|
report_content = Column(UnicodeText)
|
||||||
reported_user_id = Column(Integer, ForeignKey(User.id), nullable=False)
|
reported_user_id = Column(Integer, ForeignKey(User.id), nullable=False)
|
||||||
reported_user = relationship(
|
reported_user = relationship(
|
||||||
@ -1308,69 +1330,42 @@ class ReportBase(Base):
|
|||||||
backref=backref("reports_filed_on",
|
backref=backref("reports_filed_on",
|
||||||
lazy="dynamic",
|
lazy="dynamic",
|
||||||
cascade="all, delete-orphan"),
|
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)
|
created = Column(DateTime, nullable=False, default=datetime.datetime.utcnow)
|
||||||
discriminator = Column('type', Unicode(50))
|
|
||||||
resolver_id = Column(Integer, ForeignKey(User.id))
|
resolver_id = Column(Integer, ForeignKey(User.id))
|
||||||
resolver = relationship(
|
resolver = relationship(
|
||||||
User,
|
User,
|
||||||
backref=backref("reports_resolved_by",
|
backref=backref("reports_resolved_by",
|
||||||
lazy="dynamic",
|
lazy="dynamic",
|
||||||
cascade="all, delete-orphan"),
|
cascade="all, delete-orphan"),
|
||||||
primaryjoin="User.id==ReportBase.resolver_id")
|
primaryjoin="User.id==Report.resolver_id")
|
||||||
|
|
||||||
resolved = Column(DateTime)
|
resolved = Column(DateTime)
|
||||||
result = Column(UnicodeText)
|
result = Column(UnicodeText)
|
||||||
__mapper_args__ = {'polymorphic_on': discriminator}
|
|
||||||
|
object_id = Column(Integer, ForeignKey(GenericModelReference.id), nullable=False)
|
||||||
def is_comment_report(self):
|
object_helper = relationship(GenericModelReference)
|
||||||
return self.discriminator=='comment_report'
|
obj = association_proxy("object_helper", "get_object",
|
||||||
|
creator=GenericModelReference.find_or_new)
|
||||||
def is_media_entry_report(self):
|
|
||||||
return self.discriminator=='media_report'
|
|
||||||
|
|
||||||
def is_archived_report(self):
|
def is_archived_report(self):
|
||||||
return self.resolved is not None
|
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):
|
def archive(self,resolver_id, resolved, result):
|
||||||
self.resolver_id = resolver_id
|
self.resolver_id = resolver_id
|
||||||
self.resolved = resolved
|
self.resolved = resolved
|
||||||
self.result = result
|
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):
|
class UserBan(Base):
|
||||||
"""
|
"""
|
||||||
Holds the information on a specific user's ban-state. As long as one of
|
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
|
"deleted": self.deleted
|
||||||
}
|
}
|
||||||
|
|
||||||
with_polymorphic(
|
|
||||||
Notification,
|
|
||||||
[ProcessingNotification, CommentNotification])
|
|
||||||
|
|
||||||
MODELS = [
|
MODELS = [
|
||||||
LocalUser, RemoteUser, User, MediaEntry, Tag, MediaTag, MediaComment,
|
LocalUser, RemoteUser, User, MediaEntry, Tag, MediaTag, Comment, TextComment,
|
||||||
Collection, CollectionItem, MediaFile, FileKeynames, MediaAttachmentFile,
|
Collection, CollectionItem, MediaFile, FileKeynames, MediaAttachmentFile,
|
||||||
ProcessingMetaData, Notification, CommentNotification,
|
ProcessingMetaData, Notification, Client, CommentSubscription, Report,
|
||||||
ProcessingNotification, Client, CommentSubscription, ReportBase,
|
UserBan, Privilege, PrivilegeUserAssociation, RequestToken, AccessToken,
|
||||||
CommentReport, MediaReport, UserBan, Privilege, PrivilegeUserAssociation,
|
NonceTimestamp, Activity, Generator, Location, GenericModelReference, Graveyard]
|
||||||
RequestToken, AccessToken, NonceTimestamp, Activity, Generator, Location,
|
|
||||||
GenericModelReference, Graveyard]
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Foundations are the default rows that are created immediately after the tables
|
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 mg_globals as mgg
|
||||||
from mediagoblin import messages
|
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 (
|
from mediagoblin.tools.response import (
|
||||||
redirect, render_404,
|
redirect, render_404,
|
||||||
render_user_banned, json_response)
|
render_user_banned, json_response)
|
||||||
@ -325,11 +326,11 @@ def allow_reporting(controller):
|
|||||||
|
|
||||||
def get_optional_media_comment_by_id(controller):
|
def get_optional_media_comment_by_id(controller):
|
||||||
"""
|
"""
|
||||||
Pass in a MediaComment based off of a url component. Because of this decor-
|
Pass in a Comment based off of a url component. Because of this decor-
|
||||||
-ator's use in filing Media or Comment Reports, it has two valid outcomes.
|
-ator's use in filing Reports, it has two valid outcomes.
|
||||||
|
|
||||||
:returns The view function being wrapped with kwarg `comment` set to
|
: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.
|
comment id in the URL and if it is valid.
|
||||||
:returns The view function being wrapped with kwarg `comment` set to
|
:returns The view function being wrapped with kwarg `comment` set to
|
||||||
None. If there is no comment id in the URL.
|
None. If there is no comment id in the URL.
|
||||||
@ -339,8 +340,9 @@ def get_optional_media_comment_by_id(controller):
|
|||||||
@wraps(controller)
|
@wraps(controller)
|
||||||
def wrapper(request, *args, **kwargs):
|
def wrapper(request, *args, **kwargs):
|
||||||
if 'comment' in request.matchdict:
|
if 'comment' in request.matchdict:
|
||||||
comment = MediaComment.query.filter_by(
|
comment = Comment.query.filter_by(
|
||||||
id=request.matchdict['comment']).first()
|
id=request.matchdict['comment']
|
||||||
|
).first()
|
||||||
|
|
||||||
if comment is None:
|
if comment is None:
|
||||||
return render_404(request)
|
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 \
|
if u'delete' in form.action_to_resolve.data and \
|
||||||
report.is_comment_report():
|
report.is_comment_report():
|
||||||
deleted_comment = report.comment
|
deleted_comment = report.obj()
|
||||||
Session.delete(deleted_comment)
|
Session.delete(deleted_comment)
|
||||||
form.resolution_content.data += \
|
form.resolution_content.data += \
|
||||||
_(u"\n{mod} deleted the comment.").format(
|
_(u"\n{mod} deleted the comment.").format(
|
||||||
mod=request.user.username)
|
mod=request.user.username)
|
||||||
elif u'delete' in form.action_to_resolve.data and \
|
elif u'delete' in form.action_to_resolve.data and \
|
||||||
report.is_media_entry_report():
|
report.is_media_entry_report():
|
||||||
deleted_media = report.media_entry
|
deleted_media = report.obj()
|
||||||
deleted_media.delete()
|
deleted_media.delete()
|
||||||
form.resolution_content.data += \
|
form.resolution_content.data += \
|
||||||
_(u"\n{mod} deleted the media entry.").format(
|
_(u"\n{mod} deleted the media entry.").format(
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
from mediagoblin.db.models import (MediaEntry, User, ReportBase, Privilege,
|
from mediagoblin.db.models import (MediaEntry, User, Report, Privilege,
|
||||||
UserBan, LocalUser)
|
UserBan, LocalUser)
|
||||||
from mediagoblin.decorators import (require_admin_or_moderator_login,
|
from mediagoblin.decorators import (require_admin_or_moderator_login,
|
||||||
active_user_from_url, user_has_privilege,
|
active_user_from_url, user_has_privilege,
|
||||||
@ -83,9 +83,9 @@ def moderation_users_detail(request):
|
|||||||
LocalUser.username==request.matchdict['user']
|
LocalUser.username==request.matchdict['user']
|
||||||
).first()
|
).first()
|
||||||
active_reports = user.reports_filed_on.filter(
|
active_reports = user.reports_filed_on.filter(
|
||||||
ReportBase.resolved==None).limit(5)
|
Report.resolved==None).limit(5)
|
||||||
closed_reports = user.reports_filed_on.filter(
|
closed_reports = user.reports_filed_on.filter(
|
||||||
ReportBase.resolved!=None).all()
|
Report.resolved!=None).all()
|
||||||
privileges = Privilege.query
|
privileges = Privilege.query
|
||||||
user_banned = UserBan.query.get(user.id)
|
user_banned = UserBan.query.get(user.id)
|
||||||
ban_form = moderation_forms.BanForm()
|
ban_form = moderation_forms.BanForm()
|
||||||
@ -116,23 +116,23 @@ def moderation_reports_panel(request):
|
|||||||
active_settings['current_page'] = form.active_p.data or 1
|
active_settings['current_page'] = form.active_p.data or 1
|
||||||
closed_settings['current_page'] = form.closed_p.data or 1
|
closed_settings['current_page'] = form.closed_p.data or 1
|
||||||
filters = [
|
filters = [
|
||||||
getattr(ReportBase,key)==val
|
getattr(Report,key)==val
|
||||||
for key,val in filters.viewitems()]
|
for key,val in filters.viewitems()]
|
||||||
|
|
||||||
all_active = ReportBase.query.filter(
|
all_active = Report.query.filter(
|
||||||
ReportBase.resolved==None).filter(
|
Report.resolved==None).filter(
|
||||||
*filters)
|
*filters)
|
||||||
all_closed = ReportBase.query.filter(
|
all_closed = Report.query.filter(
|
||||||
ReportBase.resolved!=None).filter(
|
Report.resolved!=None).filter(
|
||||||
*filters)
|
*filters)
|
||||||
|
|
||||||
# report_list and closed_report_list are the two lists of up to 10
|
# 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
|
# items which are actually passed to the user in this request
|
||||||
report_list = all_active.order_by(
|
report_list = all_active.order_by(
|
||||||
ReportBase.created.desc()).offset(
|
Report.created.desc()).offset(
|
||||||
(active_settings['current_page']-1)*10).limit(10)
|
(active_settings['current_page']-1)*10).limit(10)
|
||||||
closed_report_list = all_closed.order_by(
|
closed_report_list = all_closed.order_by(
|
||||||
ReportBase.created.desc()).offset(
|
Report.created.desc()).offset(
|
||||||
(closed_settings['current_page']-1)*10).limit(10)
|
(closed_settings['current_page']-1)*10).limit(10)
|
||||||
|
|
||||||
active_settings['last_page'] = int(ceil(all_active.count()/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.
|
erator would go to to take an action to resolve a report.
|
||||||
"""
|
"""
|
||||||
form = moderation_forms.ReportResolutionForm(request.form)
|
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 = [
|
form.take_away_privileges.choices = [
|
||||||
(s.privilege_name,s.privilege_name.title()) \
|
(s.privilege_name,s.privilege_name.title()) \
|
||||||
|
@ -16,8 +16,8 @@
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from mediagoblin.db.models import Notification, \
|
from mediagoblin.db.models import Notification, CommentSubscription, User, \
|
||||||
CommentNotification, CommentSubscription, User
|
Comment, GenericModelReference
|
||||||
from mediagoblin.notifications.task import email_notification_task
|
from mediagoblin.notifications.task import email_notification_task
|
||||||
from mediagoblin.notifications.tools import generate_comment_message
|
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:
|
if comment.get_actor == subscription.user:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
cn = CommentNotification(
|
cn = Notification(
|
||||||
user_id=subscription.user_id,
|
user_id=subscription.user_id,
|
||||||
subject_id=comment.id)
|
)
|
||||||
|
cn.obj = comment
|
||||||
cn.save()
|
cn.save()
|
||||||
|
|
||||||
if subscription.send_email:
|
if subscription.send_email:
|
||||||
@ -61,9 +61,15 @@ def mark_notification_seen(notification):
|
|||||||
|
|
||||||
|
|
||||||
def mark_comment_notification_seen(comment_id, user):
|
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,
|
user_id=user.id,
|
||||||
subject_id=comment_id).first()
|
object_id=comment_gmr.id
|
||||||
|
).first()
|
||||||
|
|
||||||
_log.debug(u'Marking {0} as seen.'.format(notification))
|
_log.debug(u'Marking {0} as seen.'.format(notification))
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@ from celery import registry
|
|||||||
from celery.task import Task
|
from celery.task import Task
|
||||||
|
|
||||||
from mediagoblin.tools.mail import send_email
|
from mediagoblin.tools.mail import send_email
|
||||||
from mediagoblin.db.models import CommentNotification
|
from mediagoblin.db.models import Notification
|
||||||
|
|
||||||
|
|
||||||
_log = logging.getLogger(__name__)
|
_log = logging.getLogger(__name__)
|
||||||
@ -34,7 +34,7 @@ class EmailNotificationTask(Task):
|
|||||||
the web server.
|
the web server.
|
||||||
'''
|
'''
|
||||||
def run(self, notification_id, message):
|
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))
|
_log.info(u'Sending notification email about {0}'.format(cn))
|
||||||
|
|
||||||
return send_email(
|
return send_email(
|
||||||
|
@ -57,7 +57,10 @@ def mark_all_comment_notifications_seen(request):
|
|||||||
Marks all comment notifications seen.
|
Marks all comment notifications seen.
|
||||||
"""
|
"""
|
||||||
for comment in get_notifications(request.user.id):
|
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'):
|
if request.GET.get('next'):
|
||||||
return redirect(request, location=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
|
# This will be set later but currently we just don't have enough information
|
||||||
entry.slug = None
|
entry.slug = None
|
||||||
|
|
||||||
|
# This is a MUST.
|
||||||
|
entry.get_public_id(request.urlgen)
|
||||||
|
|
||||||
queue_file = prepare_queue_task(request.app, entry, file_data.filename)
|
queue_file = prepare_queue_task(request.app, entry, file_data.filename)
|
||||||
with queue_file:
|
with queue_file:
|
||||||
queue_file.write(request.data)
|
queue_file.write(request.data)
|
||||||
|
@ -4,9 +4,9 @@
|
|||||||
<h3>{% trans %}New comments{% endtrans %}</h3>
|
<h3>{% trans %}New comments{% endtrans %}</h3>
|
||||||
<ul>
|
<ul>
|
||||||
{% for notification in notifications %}
|
{% for notification in notifications %}
|
||||||
{% set comment = notification.subject %}
|
{% set comment = notification.obj() %}
|
||||||
{% set comment_author = comment.get_actor %}
|
{% set comment_author = comment.get_actor %}
|
||||||
{% set media = comment.get_entry %}
|
{% set media = comment.get_reply_to() %}
|
||||||
<li class="comment_wrapper">
|
<li class="comment_wrapper">
|
||||||
<div class="comment_author">
|
<div class="comment_author">
|
||||||
<img src="{{ request.staticdirect('/images/icon_comment.png') }}" />
|
<img src="{{ request.staticdirect('/images/icon_comment.png') }}" />
|
||||||
|
@ -33,10 +33,11 @@
|
|||||||
{% trans %}Return to Reports Panel{% endtrans %}</a>
|
{% trans %}Return to Reports Panel{% endtrans %}</a>
|
||||||
</div>
|
</div>
|
||||||
<h2>{% trans %}Report{% endtrans %} #{{ report.id }}</h2>
|
<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 %}:
|
{% 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 %}
|
{% set reported_user = comment.get_actor %}
|
||||||
<div id="comment-{{ comment.id }}"
|
<div id="comment-{{ comment.id }}"
|
||||||
class="comment_wrapper">
|
class="comment_wrapper">
|
||||||
@ -50,8 +51,8 @@
|
|||||||
<a href="{{ request.urlgen(
|
<a href="{{ request.urlgen(
|
||||||
'mediagoblin.user_pages.media_home.view_comment',
|
'mediagoblin.user_pages.media_home.view_comment',
|
||||||
comment=comment.id,
|
comment=comment.id,
|
||||||
user=comment.get_media_entry.get_actor.username,
|
user=target.get_actor.username,
|
||||||
media=comment.get_media_entry.slug_or_id) }}#comment"
|
media=target.slug_or_id) }}#comment"
|
||||||
class="comment_whenlink">
|
class="comment_whenlink">
|
||||||
<span title='{{- comment.created.strftime("%I:%M%p %Y-%m-%d") -}}'>
|
<span title='{{- comment.created.strftime("%I:%M%p %Y-%m-%d") -}}'>
|
||||||
{%- trans formatted_time=timesince(comment.created) -%}
|
{%- trans formatted_time=timesince(comment.created) -%}
|
||||||
@ -65,9 +66,9 @@
|
|||||||
{% endautoescape %}
|
{% endautoescape %}
|
||||||
</div>
|
</div>
|
||||||
</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">
|
<div class="three columns media_thumbnail">
|
||||||
<a href="{{ request.urlgen('mediagoblin.user_pages.media_home',
|
<a href="{{ request.urlgen('mediagoblin.user_pages.media_home',
|
||||||
user=media_entry.get_actor.username,
|
user=media_entry.get_actor.username,
|
||||||
|
@ -81,7 +81,7 @@ curr_page !=p %}
|
|||||||
</tr>
|
</tr>
|
||||||
{% for report in report_list %}
|
{% for report in report_list %}
|
||||||
<tr>
|
<tr>
|
||||||
{% if report.discriminator == "comment_report" %}
|
{% if report.is_comment_report %}
|
||||||
<td>
|
<td>
|
||||||
<img
|
<img
|
||||||
src="{{ request.staticdirect(
|
src="{{ request.staticdirect(
|
||||||
@ -97,7 +97,7 @@ curr_page !=p %}
|
|||||||
{% endtrans %}
|
{% endtrans %}
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
{% elif report.discriminator == "media_report" %}
|
{% elif report.is_media_entry_report %}
|
||||||
<td>
|
<td>
|
||||||
<img
|
<img
|
||||||
src="{{ request.staticdirect(
|
src="{{ request.staticdirect(
|
||||||
|
@ -125,9 +125,9 @@
|
|||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{% if report.discriminator == "comment_report" %}
|
{% if report.is_comment_report() %}
|
||||||
<a>{%- trans %}Reported Comment{% endtrans -%}</a>
|
<a>{%- trans %}Reported Comment{% endtrans -%}</a>
|
||||||
{% elif report.discriminator == "media_report" %}
|
{% elif report.is_media_entry_report() %}
|
||||||
<a>{%- trans %}Reported Media Entry{% endtrans -%}</a>
|
<a>{%- trans %}Reported Media Entry{% endtrans -%}</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
|
@ -93,7 +93,7 @@
|
|||||||
<p>{{ media.description_html }}</p>
|
<p>{{ media.description_html }}</p>
|
||||||
{% endautoescape %}
|
{% endautoescape %}
|
||||||
</div>
|
</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">
|
<div class="media_comments">
|
||||||
{% if app_config['allow_comments'] %}
|
{% if app_config['allow_comments'] %}
|
||||||
<a
|
<a
|
||||||
|
@ -25,7 +25,7 @@ from webtest import AppError
|
|||||||
|
|
||||||
from .resources import GOOD_JPG
|
from .resources import GOOD_JPG
|
||||||
from mediagoblin import mg_globals
|
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.tools.routing import extract_url_arguments
|
||||||
from mediagoblin.tests.tools import fixture_add_user
|
from mediagoblin.tests.tools import fixture_add_user
|
||||||
from mediagoblin.moderation.tools import take_away_privileges
|
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
|
# 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
|
# than uploading the image as someone else as the way self.mocked_oauth_required
|
||||||
# and self._upload_image.
|
# and self._upload_image.
|
||||||
id = int(data["object"]["id"].split("/")[-2])
|
media = MediaEntry.query.filter_by(public_id=data["object"]["id"]).first()
|
||||||
media = MediaEntry.query.filter_by(id=id).first()
|
|
||||||
media.actor = self.other_user.id
|
media.actor = self.other_user.id
|
||||||
media.save()
|
media.save()
|
||||||
|
|
||||||
@ -232,14 +231,13 @@ class TestAPI(object):
|
|||||||
image = json.loads(response.body.decode())["object"]
|
image = json.loads(response.body.decode())["object"]
|
||||||
|
|
||||||
# Check everything has been set on the media correctly
|
# Check everything has been set on the media correctly
|
||||||
id = int(image["id"].split("/")[-2])
|
media = MediaEntry.query.filter_by(public_id=image["id"]).first()
|
||||||
media = MediaEntry.query.filter_by(id=id).first()
|
|
||||||
assert media.title == title
|
assert media.title == title
|
||||||
assert media.description == description
|
assert media.description == description
|
||||||
assert media.license == license
|
assert media.license == license
|
||||||
|
|
||||||
# Check we're being given back everything we should on an update
|
# 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["displayName"] == title
|
||||||
assert image["content"] == description
|
assert image["content"] == description
|
||||||
assert image["license"] == license
|
assert image["license"] == license
|
||||||
@ -288,8 +286,7 @@ class TestAPI(object):
|
|||||||
request = test_app.get(object_uri)
|
request = test_app.get(object_uri)
|
||||||
|
|
||||||
image = json.loads(request.body.decode())
|
image = json.loads(request.body.decode())
|
||||||
entry_id = int(image["id"].split("/")[-2])
|
entry = MediaEntry.query.filter_by(public_id=image["id"]).first()
|
||||||
entry = MediaEntry.query.filter_by(id=entry_id).first()
|
|
||||||
|
|
||||||
assert request.status_code == 200
|
assert request.status_code == 200
|
||||||
|
|
||||||
@ -319,8 +316,7 @@ class TestAPI(object):
|
|||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
|
|
||||||
# Find the objects in the database
|
# Find the objects in the database
|
||||||
media_id = int(data["object"]["id"].split("/")[-2])
|
media = MediaEntry.query.filter_by(public_id=data["object"]["id"]).first()
|
||||||
media = MediaEntry.query.filter_by(id=media_id).first()
|
|
||||||
comment = media.get_comments()[0]
|
comment = media.get_comments()[0]
|
||||||
|
|
||||||
# Tests that it matches in the database
|
# Tests that it matches in the database
|
||||||
@ -382,8 +378,7 @@ class TestAPI(object):
|
|||||||
response, comment_data = self._activity_to_feed(test_app, activity)
|
response, comment_data = self._activity_to_feed(test_app, activity)
|
||||||
|
|
||||||
# change who uploaded the comment as it's easier than changing
|
# change who uploaded the comment as it's easier than changing
|
||||||
comment_id = int(comment_data["object"]["id"].split("/")[-2])
|
comment = TextComment.query.filter_by(public_id=comment_data["object"]["id"]).first()
|
||||||
comment = MediaComment.query.filter_by(id=comment_id).first()
|
|
||||||
comment.actor = self.other_user.id
|
comment.actor = self.other_user.id
|
||||||
comment.save()
|
comment.save()
|
||||||
|
|
||||||
@ -510,8 +505,7 @@ class TestAPI(object):
|
|||||||
response = self._activity_to_feed(test_app, activity)[1]
|
response = self._activity_to_feed(test_app, activity)[1]
|
||||||
|
|
||||||
# Check the media is no longer in the database
|
# Check the media is no longer in the database
|
||||||
media_id = int(object_id.split("/")[-2])
|
media = MediaEntry.query.filter_by(public_id=object_id).first()
|
||||||
media = MediaEntry.query.filter_by(id=media_id).first()
|
|
||||||
|
|
||||||
assert media is None
|
assert media is None
|
||||||
|
|
||||||
@ -552,8 +546,8 @@ class TestAPI(object):
|
|||||||
delete = self._activity_to_feed(test_app, activity)[1]
|
delete = self._activity_to_feed(test_app, activity)[1]
|
||||||
|
|
||||||
# Verify the comment no longer exists
|
# Verify the comment no longer exists
|
||||||
comment_id = int(comment["object"]["id"].split("/")[-2])
|
assert TextComment.query.filter_by(public_id=comment["object"]["id"]).first() is None
|
||||||
assert MediaComment.query.filter_by(id=comment_id).first() is None
|
comment_id = comment["object"]["id"]
|
||||||
|
|
||||||
# Check we've got a delete activity back
|
# Check we've got a delete activity back
|
||||||
assert "id" in delete
|
assert "id" in delete
|
||||||
@ -593,7 +587,6 @@ class TestAPI(object):
|
|||||||
comment = self._activity_to_feed(test_app, activity)[1]
|
comment = self._activity_to_feed(test_app, activity)[1]
|
||||||
|
|
||||||
# Verify the comment reflects the changes
|
# Verify the comment reflects the changes
|
||||||
comment_id = int(comment["object"]["id"].split("/")[-2])
|
model = TextComment.query.filter_by(public_id=comment["object"]["id"]).first()
|
||||||
model = MediaComment.query.filter_by(id=comment_id).first()
|
|
||||||
|
|
||||||
assert model.content == activity["object"]["content"]
|
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.media_types import sniff_media
|
||||||
from mediagoblin.submit.lib import new_upload_entry
|
from mediagoblin.submit.lib import new_upload_entry
|
||||||
from mediagoblin.submit.task import collect_garbage
|
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
|
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()
|
Session.flush()
|
||||||
|
|
||||||
# Create all 4 possible comments:
|
# Create all 4 possible comments:
|
||||||
for u_id in (user_a.id, user_b.id):
|
for u in (user_a, user_b):
|
||||||
for m_id in (media_a.id, media_b.id):
|
for m in (media_a, media_b):
|
||||||
cmt = MediaComment()
|
cmt = TextComment()
|
||||||
cmt.media_entry = m_id
|
cmt.actor = u.id
|
||||||
cmt.actor = u_id
|
|
||||||
cmt.content = u"Some Comment"
|
cmt.content = u"Some Comment"
|
||||||
Session.add(cmt)
|
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()
|
Session.flush()
|
||||||
|
|
||||||
usr_cnt1 = User.query.count()
|
usr_cnt1 = User.query.count()
|
||||||
med_cnt1 = MediaEntry.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)
|
User.query.get(user_a.id).delete(commit=False)
|
||||||
|
|
||||||
usr_cnt2 = User.query.count()
|
usr_cnt2 = User.query.count()
|
||||||
med_cnt2 = MediaEntry.query.count()
|
med_cnt2 = MediaEntry.query.count()
|
||||||
cmt_cnt2 = MediaComment.query.count()
|
cmt_cnt2 = TextComment.query.count()
|
||||||
|
|
||||||
# One user deleted
|
# One user deleted
|
||||||
assert usr_cnt2 == usr_cnt1 - 1
|
assert usr_cnt2 == usr_cnt1 - 1
|
||||||
@ -77,7 +83,7 @@ def test_user_deletes_other_comments(test_app):
|
|||||||
|
|
||||||
usr_cnt2 = User.query.count()
|
usr_cnt2 = User.query.count()
|
||||||
med_cnt2 = MediaEntry.query.count()
|
med_cnt2 = MediaEntry.query.count()
|
||||||
cmt_cnt2 = MediaComment.query.count()
|
cmt_cnt2 = TextComment.query.count()
|
||||||
|
|
||||||
# All users gone
|
# All users gone
|
||||||
assert usr_cnt2 == usr_cnt1 - 2
|
assert usr_cnt2 == usr_cnt1 - 2
|
||||||
|
@ -18,7 +18,8 @@ import pytest
|
|||||||
|
|
||||||
from mediagoblin.tests.tools import (fixture_add_user,
|
from mediagoblin.tests.tools import (fixture_add_user,
|
||||||
fixture_add_comment_report, fixture_add_comment)
|
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 mediagoblin.tools import template, mail
|
||||||
from webtest import AppError
|
from webtest import AppError
|
||||||
|
|
||||||
@ -102,15 +103,15 @@ class TestModerationViews:
|
|||||||
# to a reported comment
|
# to a reported comment
|
||||||
#----------------------------------------------------------------------
|
#----------------------------------------------------------------------
|
||||||
fixture_add_comment_report(reported_user=self.user)
|
fixture_add_comment_report(reported_user=self.user)
|
||||||
comment_report = CommentReport.query.filter(
|
comment_report = Report.query.filter(
|
||||||
CommentReport.reported_user==self.user).first()
|
Report.reported_user==self.user).first()
|
||||||
|
|
||||||
response = self.test_app.get('/mod/reports/{0}/'.format(
|
response = self.test_app.get('/mod/reports/{0}/'.format(
|
||||||
comment_report.id))
|
comment_report.id))
|
||||||
assert response.status == '200 OK'
|
assert response.status == '200 OK'
|
||||||
self.query_for_users()
|
self.query_for_users()
|
||||||
comment_report = CommentReport.query.filter(
|
comment_report = Report.query.filter(
|
||||||
CommentReport.reported_user==self.user).first()
|
Report.reported_user==self.user).first()
|
||||||
|
|
||||||
response, context = self.do_post({'action_to_resolve':[u'takeaway'],
|
response, context = self.do_post({'action_to_resolve':[u'takeaway'],
|
||||||
'take_away_privileges':[u'commenter'],
|
'take_away_privileges':[u'commenter'],
|
||||||
@ -118,15 +119,15 @@ class TestModerationViews:
|
|||||||
url='/mod/reports/{0}/'.format(comment_report.id))
|
url='/mod/reports/{0}/'.format(comment_report.id))
|
||||||
|
|
||||||
self.query_for_users()
|
self.query_for_users()
|
||||||
comment_report = CommentReport.query.filter(
|
comment_report = Report.query.filter(
|
||||||
CommentReport.reported_user==self.user).first()
|
Report.reported_user==self.user).first()
|
||||||
assert response.status == '302 FOUND'
|
assert response.status == '302 FOUND'
|
||||||
assert not self.user.has_privilege(u'commenter')
|
assert not self.user.has_privilege(u'commenter')
|
||||||
assert comment_report.is_archived_report() is True
|
assert comment_report.is_archived_report() is True
|
||||||
|
|
||||||
fixture_add_comment_report(reported_user=self.user)
|
fixture_add_comment_report(reported_user=self.user)
|
||||||
comment_report = CommentReport.query.filter(
|
comment_report = Report.query.filter(
|
||||||
CommentReport.reported_user==self.user).first()
|
Report.reported_user==self.user).first()
|
||||||
|
|
||||||
# Then, test a moderator sending an email to a user in response to a
|
# Then, test a moderator sending an email to a user in response to a
|
||||||
# reported comment
|
# reported comment
|
||||||
@ -139,8 +140,8 @@ class TestModerationViews:
|
|||||||
url='/mod/reports/{0}/'.format(comment_report.id))
|
url='/mod/reports/{0}/'.format(comment_report.id))
|
||||||
|
|
||||||
self.query_for_users()
|
self.query_for_users()
|
||||||
comment_report = CommentReport.query.filter(
|
comment_report = Report.query.filter(
|
||||||
CommentReport.reported_user==self.user).first()
|
Report.reported_user==self.user).first()
|
||||||
assert response.status == '302 FOUND'
|
assert response.status == '302 FOUND'
|
||||||
assert mail.EMAIL_TEST_MBOX_INBOX == [{'to': [u'regular@example.com'],
|
assert mail.EMAIL_TEST_MBOX_INBOX == [{'to': [u'regular@example.com'],
|
||||||
'message': 'Content-Type: text/plain; charset="utf-8"\n\
|
'message': 'Content-Type: text/plain; charset="utf-8"\n\
|
||||||
@ -157,13 +158,17 @@ VGhpcyBpcyB5b3VyIGxhc3Qgd2FybmluZywgcmVndWxhci4uLi4=\n',
|
|||||||
self.query_for_users()
|
self.query_for_users()
|
||||||
fixture_add_comment(author=self.user.id,
|
fixture_add_comment(author=self.user.id,
|
||||||
comment=u'Comment will be removed')
|
comment=u'Comment will be removed')
|
||||||
test_comment = MediaComment.query.filter(
|
test_comment = TextComment.query.filter(
|
||||||
MediaComment.actor==self.user.id).first()
|
TextComment.actor==self.user.id).first()
|
||||||
fixture_add_comment_report(comment=test_comment,
|
fixture_add_comment_report(comment=test_comment,
|
||||||
reported_user=self.user)
|
reported_user=self.user)
|
||||||
comment_report = CommentReport.query.filter(
|
comment_gmr = GenericModelReference.query.filter_by(
|
||||||
CommentReport.comment==test_comment).filter(
|
obj_pk=test_comment.id,
|
||||||
CommentReport.resolved==None).first()
|
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(
|
response, context = self.do_post(
|
||||||
{'action_to_resolve':[u'userban', u'delete'],
|
{'action_to_resolve':[u'userban', u'delete'],
|
||||||
@ -176,17 +181,17 @@ VGhpcyBpcyB5b3VyIGxhc3Qgd2FybmluZywgcmVndWxhci4uLi4=\n',
|
|||||||
test_user_ban = UserBan.query.filter(
|
test_user_ban = UserBan.query.filter(
|
||||||
UserBan.user_id == self.user.id).first()
|
UserBan.user_id == self.user.id).first()
|
||||||
assert test_user_ban is not None
|
assert test_user_ban is not None
|
||||||
test_comment = MediaComment.query.filter(
|
test_comment = TextComment.query.filter(
|
||||||
MediaComment.actor==self.user.id).first()
|
TextComment.actor==self.user.id).first()
|
||||||
assert test_comment is None
|
assert test_comment is None
|
||||||
|
|
||||||
# Then, test what happens when a moderator attempts to punish an admin
|
# Then, test what happens when a moderator attempts to punish an admin
|
||||||
# from a reported comment on an admin.
|
# from a reported comment on an admin.
|
||||||
#----------------------------------------------------------------------
|
#----------------------------------------------------------------------
|
||||||
fixture_add_comment_report(reported_user=self.admin_user)
|
fixture_add_comment_report(reported_user=self.admin_user)
|
||||||
comment_report = CommentReport.query.filter(
|
comment_report = Report.query.filter(
|
||||||
CommentReport.reported_user==self.admin_user).filter(
|
Report.reported_user==self.admin_user).filter(
|
||||||
CommentReport.resolved==None).first()
|
Report.resolved==None).first()
|
||||||
|
|
||||||
response, context = self.do_post({'action_to_resolve':[u'takeaway'],
|
response, context = self.do_post({'action_to_resolve':[u'takeaway'],
|
||||||
'take_away_privileges':[u'active'],
|
'take_away_privileges':[u'active'],
|
||||||
|
@ -20,8 +20,7 @@ import six.moves.urllib.parse as urlparse
|
|||||||
|
|
||||||
from mediagoblin.tools import template, mail
|
from mediagoblin.tools import template, mail
|
||||||
|
|
||||||
from mediagoblin.db.models import Notification, CommentNotification, \
|
from mediagoblin.db.models import Notification, CommentSubscription
|
||||||
CommentSubscription
|
|
||||||
from mediagoblin.db.base import Session
|
from mediagoblin.db.base import Session
|
||||||
|
|
||||||
from mediagoblin.notifications import mark_comment_notification_seen
|
from mediagoblin.notifications import mark_comment_notification_seen
|
||||||
@ -109,11 +108,10 @@ class TestNotifications:
|
|||||||
|
|
||||||
notification = notifications[0]
|
notification = notifications[0]
|
||||||
|
|
||||||
assert type(notification) == CommentNotification
|
|
||||||
assert notification.seen == False
|
assert notification.seen == False
|
||||||
assert notification.user_id == user.id
|
assert notification.user_id == user.id
|
||||||
assert notification.subject.get_actor.id == self.test_user.id
|
assert notification.obj().get_actor.id == self.test_user.id
|
||||||
assert notification.subject.content == u'Test comment #42'
|
assert notification.obj().content == u'Test comment #42'
|
||||||
|
|
||||||
if wants_email == True:
|
if wants_email == True:
|
||||||
assert mail.EMAIL_TEST_MBOX_INBOX == [
|
assert mail.EMAIL_TEST_MBOX_INBOX == [
|
||||||
@ -130,7 +128,7 @@ otherperson@example.com\n\nSGkgb3RoZXJwZXJzb24sCmNocmlzIGNvbW1lbnRlZCBvbiB5b3VyI
|
|||||||
|
|
||||||
# Save the ids temporarily because of DetachedInstanceError
|
# Save the ids temporarily because of DetachedInstanceError
|
||||||
notification_id = notification.id
|
notification_id = notification.id
|
||||||
comment_id = notification.subject.id
|
comment_id = notification.obj().get_comment_link().id
|
||||||
|
|
||||||
self.logout()
|
self.logout()
|
||||||
self.login('otherperson', 'nosreprehto')
|
self.login('otherperson', 'nosreprehto')
|
||||||
|
@ -20,8 +20,7 @@ import six
|
|||||||
from mediagoblin.tools import template
|
from mediagoblin.tools import template
|
||||||
from mediagoblin.tests.tools import (fixture_add_user, fixture_media_entry,
|
from mediagoblin.tests.tools import (fixture_add_user, fixture_media_entry,
|
||||||
fixture_add_comment, fixture_add_comment_report)
|
fixture_add_comment, fixture_add_comment_report)
|
||||||
from mediagoblin.db.models import (MediaReport, CommentReport, User, LocalUser,
|
from mediagoblin.db.models import Report, User, LocalUser, TextComment
|
||||||
MediaComment)
|
|
||||||
|
|
||||||
|
|
||||||
class TestReportFiling:
|
class TestReportFiling:
|
||||||
@ -80,7 +79,7 @@ class TestReportFiling:
|
|||||||
|
|
||||||
assert response.status == "302 FOUND"
|
assert response.status == "302 FOUND"
|
||||||
|
|
||||||
media_report = MediaReport.query.first()
|
media_report = Report.query.first()
|
||||||
|
|
||||||
allie_user, natalie_user = self.query_for_users()
|
allie_user, natalie_user = self.query_for_users()
|
||||||
assert media_report is not None
|
assert media_report is not None
|
||||||
@ -88,7 +87,6 @@ class TestReportFiling:
|
|||||||
assert media_report.reporter_id == allie_id
|
assert media_report.reporter_id == allie_id
|
||||||
assert media_report.reported_user_id == natalie_user.id
|
assert media_report.reported_user_id == natalie_user.id
|
||||||
assert media_report.created is not None
|
assert media_report.created is not None
|
||||||
assert media_report.discriminator == 'media_report'
|
|
||||||
|
|
||||||
def testCommentReports(self):
|
def testCommentReports(self):
|
||||||
self.login(u'allie')
|
self.login(u'allie')
|
||||||
@ -98,9 +96,11 @@ class TestReportFiling:
|
|||||||
media_entry = fixture_media_entry(uploader=natalie_user.id,
|
media_entry = fixture_media_entry(uploader=natalie_user.id,
|
||||||
state=u'processed')
|
state=u'processed')
|
||||||
mid = media_entry.id
|
mid = media_entry.id
|
||||||
fixture_add_comment(media_entry=mid,
|
fixture_add_comment(
|
||||||
author=natalie_user.id)
|
media_entry=media_entry,
|
||||||
comment = MediaComment.query.first()
|
author=natalie_user.id
|
||||||
|
)
|
||||||
|
comment = TextComment.query.first()
|
||||||
|
|
||||||
comment_uri_slug = '/u/{0}/m/{1}/c/{2}/'.format(natalie_user.username,
|
comment_uri_slug = '/u/{0}/m/{1}/c/{2}/'.format(natalie_user.username,
|
||||||
media_entry.slug,
|
media_entry.slug,
|
||||||
@ -115,7 +115,7 @@ class TestReportFiling:
|
|||||||
|
|
||||||
assert response.status == "302 FOUND"
|
assert response.status == "302 FOUND"
|
||||||
|
|
||||||
comment_report = CommentReport.query.first()
|
comment_report = Report.query.first()
|
||||||
|
|
||||||
allie_user, natalie_user = self.query_for_users()
|
allie_user, natalie_user = self.query_for_users()
|
||||||
assert comment_report is not None
|
assert comment_report is not None
|
||||||
@ -123,7 +123,6 @@ class TestReportFiling:
|
|||||||
assert comment_report.reporter_id == allie_id
|
assert comment_report.reporter_id == allie_id
|
||||||
assert comment_report.reported_user_id == natalie_user.id
|
assert comment_report.reported_user_id == natalie_user.id
|
||||||
assert comment_report.created is not None
|
assert comment_report.created is not None
|
||||||
assert comment_report.discriminator == 'comment_report'
|
|
||||||
|
|
||||||
def testArchivingReports(self):
|
def testArchivingReports(self):
|
||||||
self.login(u'natalie')
|
self.login(u'natalie')
|
||||||
@ -132,14 +131,14 @@ class TestReportFiling:
|
|||||||
|
|
||||||
fixture_add_comment(author=allie_user.id,
|
fixture_add_comment(author=allie_user.id,
|
||||||
comment=u'Comment will be removed')
|
comment=u'Comment will be removed')
|
||||||
test_comment = MediaComment.query.filter(
|
test_comment = TextComment.query.filter(
|
||||||
MediaComment.actor==allie_user.id).first()
|
TextComment.actor==allie_user.id).first()
|
||||||
fixture_add_comment_report(comment=test_comment,
|
fixture_add_comment_report(comment=test_comment,
|
||||||
reported_user=allie_user,
|
reported_user=allie_user,
|
||||||
report_content=u'Testing Archived Reports #1',
|
report_content=u'Testing Archived Reports #1',
|
||||||
reporter=natalie_user)
|
reporter=natalie_user)
|
||||||
comment_report = CommentReport.query.filter(
|
comment_report = Report.query.filter(
|
||||||
CommentReport.reported_user==allie_user).first()
|
Report.reported_user==allie_user).first()
|
||||||
|
|
||||||
assert comment_report.report_content == u'Testing Archived Reports #1'
|
assert comment_report.report_content == u'Testing Archived Reports #1'
|
||||||
response, context = self.do_post(
|
response, context = self.do_post(
|
||||||
@ -151,10 +150,10 @@ class TestReportFiling:
|
|||||||
assert response.status == "302 FOUND"
|
assert response.status == "302 FOUND"
|
||||||
allie_user, natalie_user = self.query_for_users()
|
allie_user, natalie_user = self.query_for_users()
|
||||||
|
|
||||||
archived_report = CommentReport.query.filter(
|
archived_report = Report.query.filter(
|
||||||
CommentReport.reported_user==allie_user).first()
|
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 is not None
|
||||||
assert archived_report.report_content == u'Testing Archived Reports #1'
|
assert archived_report.report_content == u'Testing Archived Reports #1'
|
||||||
assert archived_report.reporter_id == natalie_id
|
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.
|
assert archived_report.result == u'''This is a test of archiving reports.
|
||||||
natalie banned user allie indefinitely.
|
natalie banned user allie indefinitely.
|
||||||
natalie deleted the comment.'''
|
natalie deleted the comment.'''
|
||||||
assert archived_report.discriminator == 'comment_report'
|
|
||||||
|
@ -99,8 +99,14 @@ class TestSubmission:
|
|||||||
return {'upload_files': [('file', filename)]}
|
return {'upload_files': [('file', filename)]}
|
||||||
|
|
||||||
def check_comments(self, request, media_id, count):
|
def check_comments(self, request, media_id, count):
|
||||||
comments = request.db.MediaComment.query.filter_by(media_entry=media_id)
|
gmr = request.db.GenericModelReference.query.filter_by(
|
||||||
assert count == len(list(comments))
|
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):
|
def test_missing_fields(self):
|
||||||
# Test blank form
|
# Test blank form
|
||||||
|
@ -25,9 +25,9 @@ from paste.deploy import loadapp
|
|||||||
from webtest import TestApp
|
from webtest import TestApp
|
||||||
|
|
||||||
from mediagoblin import mg_globals
|
from mediagoblin import mg_globals
|
||||||
from mediagoblin.db.models import User, LocalUser, MediaEntry, Collection, MediaComment, \
|
from mediagoblin.db.models import User, LocalUser, MediaEntry, Collection, TextComment, \
|
||||||
CommentSubscription, CommentNotification, Privilege, CommentReport, Client, \
|
CommentSubscription, Notification, Privilege, Report, Client, \
|
||||||
RequestToken, AccessToken, Activity, Generator
|
RequestToken, AccessToken, Activity, Generator, Comment
|
||||||
from mediagoblin.tools import testing
|
from mediagoblin.tools import testing
|
||||||
from mediagoblin.init.config import read_mediagoblin_config
|
from mediagoblin.init.config import read_mediagoblin_config
|
||||||
from mediagoblin.db.base import Session
|
from mediagoblin.db.base import Session
|
||||||
@ -222,14 +222,16 @@ def fixture_comment_subscription(entry, notify=True, send_email=None):
|
|||||||
return cs
|
return cs
|
||||||
|
|
||||||
|
|
||||||
def fixture_add_comment_notification(entry_id, subject_id, user_id,
|
def fixture_add_comment_notification(entry, subject, user,
|
||||||
seen=False):
|
seen=False):
|
||||||
cn = CommentNotification(user_id=user_id,
|
cn = Notification(
|
||||||
seen=seen,
|
user_id=user,
|
||||||
subject_id=subject_id)
|
seen=seen,
|
||||||
|
)
|
||||||
|
cn.obj = subject
|
||||||
cn.save()
|
cn.save()
|
||||||
|
|
||||||
cn = CommentNotification.query.filter_by(id=cn.id).first()
|
cn = Notification.query.filter_by(id=cn.id).first()
|
||||||
|
|
||||||
Session.expunge(cn)
|
Session.expunge(cn)
|
||||||
|
|
||||||
@ -309,22 +311,27 @@ def fixture_add_comment(author=None, media_entry=None, comment=None):
|
|||||||
author = fixture_add_user().id
|
author = fixture_add_user().id
|
||||||
|
|
||||||
if media_entry is None:
|
if media_entry is None:
|
||||||
media_entry = fixture_media_entry().id
|
media_entry = fixture_media_entry()
|
||||||
|
|
||||||
if comment is None:
|
if comment is None:
|
||||||
comment = \
|
comment = \
|
||||||
'Auto-generated test comment by user #{0} on media #{0}'.format(
|
'Auto-generated test comment by user #{0} on media #{0}'.format(
|
||||||
author, media_entry)
|
author, media_entry)
|
||||||
|
|
||||||
comment = MediaComment(actor=author,
|
text_comment = TextComment(
|
||||||
media_entry=media_entry,
|
actor=author,
|
||||||
content=comment)
|
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,
|
def fixture_add_comment_report(comment=None, reported_user=None,
|
||||||
reporter=None, created=None, report_content=None):
|
reporter=None, created=None, report_content=None):
|
||||||
@ -344,12 +351,13 @@ def fixture_add_comment_report(comment=None, reported_user=None,
|
|||||||
report_content = \
|
report_content = \
|
||||||
'Auto-generated test report'
|
'Auto-generated test report'
|
||||||
|
|
||||||
comment_report = CommentReport(comment=comment,
|
comment_report = Report()
|
||||||
reported_user = reported_user,
|
comment_report.obj = comment
|
||||||
reporter = reporter,
|
comment_report.reported_user = reported_user
|
||||||
created = created,
|
comment_report.reporter = reporter
|
||||||
report_content=report_content)
|
comment_report.created = created
|
||||||
|
comment_report.report_content = report_content
|
||||||
|
comment_report.obj = comment
|
||||||
comment_report.save()
|
comment_report.save()
|
||||||
|
|
||||||
Session.expunge(comment_report)
|
Session.expunge(comment_report)
|
||||||
|
@ -16,8 +16,8 @@
|
|||||||
|
|
||||||
from mediagoblin import mg_globals
|
from mediagoblin import mg_globals
|
||||||
from mediagoblin.db.base import Session
|
from mediagoblin.db.base import Session
|
||||||
from mediagoblin.db.models import (CollectionItem, MediaReport, CommentReport,
|
from mediagoblin.db.models import CollectionItem, Report, TextComment, \
|
||||||
MediaComment, MediaEntry)
|
MediaEntry
|
||||||
from mediagoblin.tools.mail import send_email
|
from mediagoblin.tools.mail import send_email
|
||||||
from mediagoblin.tools.pluginapi import hook_runall
|
from mediagoblin.tools.pluginapi import hook_runall
|
||||||
from mediagoblin.tools.template import render_template
|
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):
|
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
|
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
|
:param report_form A MediaReportForm or a CommentReportForm object
|
||||||
with valid information from a POST request.
|
with valid information from a POST request.
|
||||||
:param media_entry A MediaEntry object. The MediaEntry being repo-
|
:param media_entry A MediaEntry object. The MediaEntry being repo-
|
||||||
-rted by a MediaReport. In a CommentReport,
|
-rted by a Report.
|
||||||
this will be None.
|
:param comment A Comment object. The Comment being
|
||||||
:param comment A MediaComment object. The MediaComment being
|
reported by a Report.
|
||||||
reported by a CommentReport. In a MediaReport
|
|
||||||
this will be None.
|
|
||||||
|
|
||||||
:returns A MediaReport object if a valid MediaReportForm is
|
:returns A Report object if a valid MediaReportForm is
|
||||||
passed as kwarg media_entry. This MediaReport has
|
passed as kwarg media_entry. This Report has
|
||||||
not been saved.
|
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.
|
:returns None if the form_dict is invalid.
|
||||||
"""
|
"""
|
||||||
|
report_object = Report()
|
||||||
if report_form.validate() and comment is not None:
|
if report_form.validate() and comment is not None:
|
||||||
report_object = CommentReport()
|
report_object.obj = comment.comment()
|
||||||
report_object.comment_id = comment.id
|
report_object.reported_user_id = TextComment.query.get(
|
||||||
report_object.reported_user_id = MediaComment.query.get(
|
|
||||||
comment.id).get_actor.id
|
comment.id).get_actor.id
|
||||||
elif report_form.validate() and media_entry is not None:
|
elif report_form.validate() and media_entry is not None:
|
||||||
report_object = MediaReport()
|
report_object.obj = media_entry
|
||||||
report_object.media_entry_id = media_entry.id
|
|
||||||
report_object.reported_user_id = MediaEntry.query.get(
|
report_object.reported_user_id = MediaEntry.query.get(
|
||||||
media_entry.id).get_actor.id
|
media_entry.id).get_actor.id
|
||||||
else:
|
else:
|
||||||
|
@ -21,8 +21,9 @@ import json
|
|||||||
import six
|
import six
|
||||||
|
|
||||||
from mediagoblin import messages, mg_globals
|
from mediagoblin import messages, mg_globals
|
||||||
from mediagoblin.db.models import (MediaEntry, MediaTag, Collection,
|
from mediagoblin.db.models import (MediaEntry, MediaTag, Collection, Comment,
|
||||||
CollectionItem, LocalUser, Activity)
|
CollectionItem, LocalUser, Activity, \
|
||||||
|
GenericModelReference)
|
||||||
from mediagoblin.tools.response import render_to_response, render_404, \
|
from mediagoblin.tools.response import render_to_response, render_404, \
|
||||||
redirect, redirect_obj
|
redirect, redirect_obj
|
||||||
from mediagoblin.tools.text import cleaned_markdown_conversion
|
from mediagoblin.tools.text import cleaned_markdown_conversion
|
||||||
@ -178,8 +179,7 @@ def media_post_comment(request, media):
|
|||||||
if not request.method == 'POST':
|
if not request.method == 'POST':
|
||||||
raise MethodNotAllowed()
|
raise MethodNotAllowed()
|
||||||
|
|
||||||
comment = request.db.MediaComment()
|
comment = request.db.TextComment()
|
||||||
comment.media_entry = media.id
|
|
||||||
comment.actor = request.user.id
|
comment.actor = request.user.id
|
||||||
comment.content = six.text_type(request.form['comment_content'])
|
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)
|
add_comment_subscription(request.user, media)
|
||||||
comment.save()
|
comment.save()
|
||||||
|
|
||||||
|
link = request.db.Comment()
|
||||||
|
link.target = media
|
||||||
|
link.comment = comment
|
||||||
|
link.save()
|
||||||
|
|
||||||
messages.add_message(
|
messages.add_message(
|
||||||
request, messages.SUCCESS,
|
request, messages.SUCCESS,
|
||||||
_('Your comment has been posted!'))
|
_('Your comment has been posted!'))
|
||||||
@ -682,15 +687,15 @@ def processing_panel(request):
|
|||||||
@get_optional_media_comment_by_id
|
@get_optional_media_comment_by_id
|
||||||
def file_a_report(request, media, comment):
|
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 comment is not None:
|
||||||
if not comment.get_media_entry.id == media.id:
|
if not comment.target().id == media.id:
|
||||||
return render_404(request)
|
return render_404(request)
|
||||||
|
|
||||||
form = user_forms.CommentReportForm(request.form)
|
form = user_forms.CommentReportForm(request.form)
|
||||||
context = {'media': media,
|
context = {'media': comment.target(),
|
||||||
'comment':comment,
|
'comment':comment.comment(),
|
||||||
'form':form}
|
'form':form}
|
||||||
else:
|
else:
|
||||||
form = user_forms.MediaReportForm(request.form)
|
form = user_forms.MediaReportForm(request.form)
|
||||||
@ -700,9 +705,11 @@ def file_a_report(request, media, comment):
|
|||||||
|
|
||||||
|
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
report_object = build_report_object(form,
|
report_object = build_report_object(
|
||||||
|
form,
|
||||||
media_entry=media,
|
media_entry=media,
|
||||||
comment=comment)
|
comment=comment
|
||||||
|
)
|
||||||
|
|
||||||
# if the object was built successfully, report_table will not be None
|
# if the object was built successfully, report_table will not be None
|
||||||
if report_object:
|
if report_object:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user