trac#687: Add unit tests for `redirect` and `redirect_obj`.
[mediagoblin.git] / mediagoblin / user_pages / views.py
index b6cbcabd4d8d07d02870bee697eab5ada577659f..ba94ec1626ee57e2e2ed2efad4c536d63610240c 100644 (file)
@@ -21,8 +21,9 @@ import json
 import six
 
 from mediagoblin import messages, mg_globals
-from mediagoblin.db.models import (MediaEntry, MediaTag, Collection,
-                                   CollectionItem, User)
+from mediagoblin.db.models import (MediaEntry, MediaTag, Collection, Comment,
+                                   CollectionItem, LocalUser, Activity, \
+                                   GenericModelReference)
 from mediagoblin.tools.response import render_to_response, render_404, \
     redirect, redirect_obj
 from mediagoblin.tools.text import cleaned_markdown_conversion
@@ -53,8 +54,8 @@ _log.setLevel(logging.DEBUG)
 @user_not_banned
 @uses_pagination
 def user_home(request, page):
-    """'Homepage' of a User()"""
-    user = User.query.filter_by(username=request.matchdict['user']).first()
+    """'Homepage' of a LocalUser()"""
+    user = LocalUser.query.filter_by(username=request.matchdict['user']).first()
     if not user:
         return render_404(request)
     elif not user.has_privilege(u'active'):
@@ -64,7 +65,7 @@ def user_home(request, page):
             {'user': user})
 
     cursor = MediaEntry.query.\
-        filter_by(uploader = user.id,
+        filter_by(actor = user.id,
                   state = u'processed').order_by(MediaEntry.created.desc())
 
     pagination = Pagination(page, cursor)
@@ -90,10 +91,10 @@ def user_home(request, page):
 @active_user_from_url
 @uses_pagination
 def user_gallery(request, page, url_user=None):
-    """'Gallery' of a User()"""
+    """'Gallery' of a LocalUser()"""
     tag = request.matchdict.get('tag', None)
     cursor = MediaEntry.query.filter_by(
-        uploader=url_user.id,
+        actor=url_user.id,
         state=u'processed').order_by(MediaEntry.created.desc())
 
     # Filter potentially by tag too:
@@ -178,9 +179,8 @@ def media_post_comment(request, media):
     if not request.method == 'POST':
         raise MethodNotAllowed()
 
-    comment = request.db.MediaComment()
-    comment.media_entry = media.id
-    comment.author = request.user.id
+    comment = request.db.TextComment()
+    comment.actor = request.user.id
     comment.content = six.text_type(request.form['comment_content'])
 
     # Show error message if commenting is disabled.
@@ -195,15 +195,19 @@ def media_post_comment(request, media):
             messages.ERROR,
             _("Oops, your comment was empty."))
     else:
+        create_activity("post", comment, comment.actor, target=media)
+        add_comment_subscription(request.user, media)
         comment.save()
 
+        link = request.db.Comment()
+        link.target = media
+        link.comment = comment
+        link.save()
+
         messages.add_message(
             request, messages.SUCCESS,
             _('Your comment has been posted!'))
-
         trigger_notification(comment, media, request)
-        create_activity("post", comment, comment.author, target=media)
-        add_comment_subscription(request.user, media)
 
     return redirect_obj(request, media)
 
@@ -229,7 +233,9 @@ def media_collect(request, media):
     form = user_forms.MediaCollectForm(request.form)
     # A user's own collections:
     form.collection.query = Collection.query.filter_by(
-        creator = request.user.id).order_by(Collection.title)
+        actor=request.user.id,
+        type=Collection.USER_DEFINED_TYPE
+    ).order_by(Collection.title)
 
     if request.method != 'POST' or not form.validate():
         # No POST submission, or invalid form
@@ -248,44 +254,51 @@ def media_collect(request, media):
     if form.collection_title.data:
         # Make sure this user isn't duplicating an existing collection
         existing_collection = Collection.query.filter_by(
-                                creator=request.user.id,
-                                title=form.collection_title.data).first()
+            actor=request.user.id,
+            title=form.collection_title.data,
+            type=Collection.USER_DEFINED_TYPE
+        ).first()
         if existing_collection:
             messages.add_message(request, messages.ERROR,
                 _('You already have a collection called "%s"!')
                 % existing_collection.title)
             return redirect(request, "mediagoblin.user_pages.media_home",
-                            user=media.get_uploader.username,
+                            user=media.get_actor.username,
                             media=media.slug_or_id)
 
         collection = Collection()
         collection.title = form.collection_title.data
         collection.description = form.collection_description.data
-        collection.creator = request.user.id
+        collection.actor = request.user.id
+        collection.type = Collection.USER_DEFINED_TYPE
         collection.generate_slug()
+        collection.get_public_id(request.urlgen)
+        create_activity("create", collection, collection.actor)
         collection.save()
-        create_activity("create", collection, collection.creator)
 
     # Otherwise, use the collection selected from the drop-down
     else:
         collection = form.collection.data
-        if collection and collection.creator != request.user.id:
+        if collection and collection.actor != request.user.id:
             collection = None
 
     # Make sure the user actually selected a collection
+    item = CollectionItem.query.filter_by(collection=collection.id)
+    item = item.join(CollectionItem.object_helper).filter_by(
+        model_type=media.__tablename__,
+        obj_pk=media.id
+    ).first()
+
     if not collection:
         messages.add_message(
             request, messages.ERROR,
             _('You have to select or add a collection'))
         return redirect(request, "mediagoblin.user_pages.media_collect",
-                    user=media.get_uploader.username,
+                    user=media.get_actor.username,
                     media_id=media.id)
 
-
     # Check whether media already exists in collection
-    elif CollectionItem.query.filter_by(
-        media_entry=media.id,
-        collection=collection.id).first():
+    elif item is not None:
         messages.add_message(request, messages.ERROR,
                              _('"%s" already in collection "%s"')
                              % (media.title, collection.title))
@@ -309,11 +322,17 @@ def media_confirm_delete(request, media):
 
     if request.method == 'POST' and form.validate():
         if form.confirm.data is True:
-            username = media.get_uploader.username
+            username = media.get_actor.username
+
+            # This probably is already filled but just in case it has slipped
+            # through the net somehow, we need to try and make sure the
+            # MediaEntry has a public ID so it gets properly soft-deleted.
+            media.get_public_id(request.urlgen)
 
-            media.get_uploader.uploaded = media.get_uploader.uploaded - \
+            # Decrement the users uploaded quota.
+            media.get_actor.uploaded = media.get_actor.uploaded - \
                 media.file_size
-            media.get_uploader.save()
+            media.get_actor.save()
 
             # Delete MediaEntry and all related files, comments etc.
             media.delete()
@@ -334,7 +353,7 @@ def media_confirm_delete(request, media):
             return redirect_obj(request, media)
 
     if ((request.user.has_privilege(u'admin') and
-         request.user.id != media.uploader)):
+         request.user.id != media.actor)):
         messages.add_message(
             request, messages.WARNING,
             _("You are about to delete another user's media. "
@@ -352,7 +371,7 @@ def media_confirm_delete(request, media):
 def user_collection(request, page, url_user=None):
     """A User-defined Collection"""
     collection = Collection.query.filter_by(
-        get_creator=url_user,
+        get_actor=url_user,
         slug=request.matchdict['collection']).first()
 
     if not collection:
@@ -381,7 +400,7 @@ def user_collection(request, page, url_user=None):
 def collection_list(request, url_user=None):
     """A User-defined Collection"""
     collections = Collection.query.filter_by(
-        get_creator=url_user)
+        get_actor=url_user)
 
     return render_to_response(
         request,
@@ -398,15 +417,15 @@ def collection_item_confirm_remove(request, collection_item):
     form = user_forms.ConfirmCollectionItemRemoveForm(request.form)
 
     if request.method == 'POST' and form.validate():
-        username = collection_item.in_collection.get_creator.username
+        username = collection_item.in_collection.get_actor.username
         collection = collection_item.in_collection
 
         if form.confirm.data is True:
-            entry = collection_item.get_media_entry
-            entry.save()
+            obj = collection_item.get_object()
+            obj.save()
 
             collection_item.delete()
-            collection.items = collection.items - 1
+            collection.num_items = collection.num_items - 1
             collection.save()
 
             messages.add_message(
@@ -419,7 +438,7 @@ def collection_item_confirm_remove(request, collection_item):
         return redirect_obj(request, collection)
 
     if ((request.user.has_privilege(u'admin') and
-         request.user.id != collection_item.in_collection.creator)):
+         request.user.id != collection_item.in_collection.actor)):
         messages.add_message(
             request, messages.WARNING,
             _("You are about to delete an item from another user's collection. "
@@ -441,15 +460,19 @@ def collection_confirm_delete(request, collection):
 
     if request.method == 'POST' and form.validate():
 
-        username = collection.get_creator.username
+        username = collection.get_actor.username
 
         if form.confirm.data is True:
             collection_title = collection.title
 
+            # Firstly like with the MediaEntry delete, lets ensure the
+            # public_id is populated as this is really important!
+            collection.get_public_id(request.urlgen)
+
             # Delete all the associated collection items
             for item in collection.get_collection_items():
-                entry = item.get_media_entry
-                entry.save()
+                obj = item.get_object()
+                obj.save()
                 item.delete()
 
             collection.delete()
@@ -466,7 +489,7 @@ def collection_confirm_delete(request, collection):
             return redirect_obj(request, collection)
 
     if ((request.user.has_privilege(u'admin') and
-         request.user.id != collection.creator)):
+         request.user.id != collection.actor)):
         messages.add_message(
             request, messages.WARNING,
             _("You are about to delete another user's collection. "
@@ -486,13 +509,13 @@ def atom_feed(request):
     """
     generates the atom feed with the newest images
     """
-    user = User.query.filter_by(
+    user = LocalUser.query.filter_by(
         username = request.matchdict['user']).first()
     if not user or not user.has_privilege(u'active'):
         return render_404(request)
 
     cursor = MediaEntry.query.filter_by(
-        uploader = user.id,
+        actor = user.id,
         state = u'processed').\
         order_by(MediaEntry.created.desc()).\
         limit(ATOM_DEFAULT_NR_OF_UPDATED_ITEMS)
@@ -524,15 +547,16 @@ def atom_feed(request):
                links=atomlinks)
 
     for entry in cursor:
-        feed.add(entry.get('title'),
+        feed.add(
+            entry.get('title'),
             entry.description_html,
             id=entry.url_for_self(request.urlgen, qualified=True),
             content_type='html',
             author={
-                'name': entry.get_uploader.username,
+                'name': entry.get_actor.username,
                 'uri': request.urlgen(
                     'mediagoblin.user_pages.user_home',
-                    qualified=True, user=entry.get_uploader.username)},
+                    qualified=True, user=entry.get_actor.username)},
             updated=entry.get('created'),
             links=[{
                 'href': entry.url_for_self(
@@ -548,13 +572,13 @@ def collection_atom_feed(request):
     """
     generates the atom feed with the newest images from a collection
     """
-    user = User.query.filter_by(
+    user = LocalUser.query.filter_by(
         username = request.matchdict['user']).first()
     if not user or not user.has_privilege(u'active'):
         return render_404(request)
 
     collection = Collection.query.filter_by(
-               creator=user.id,
+               actor=user.id,
                slug=request.matchdict['collection']).first()
     if not collection:
         return render_404(request)
@@ -592,19 +616,20 @@ def collection_atom_feed(request):
                 links=atomlinks)
 
     for item in cursor:
-        entry = item.get_media_entry
-        feed.add(entry.get('title'),
+        obj = item.get_object()
+        feed.add(
+            obj.get('title'),
             item.note_html,
-            id=entry.url_for_self(request.urlgen, qualified=True),
+            id=obj.url_for_self(request.urlgen, qualified=True),
             content_type='html',
             author={
-                'name': entry.get_uploader.username,
+                'name': obj.get_actor().username,
                 'uri': request.urlgen(
                     'mediagoblin.user_pages.user_home',
-                    qualified=True, user=entry.get_uploader.username)},
+                    qualified=True, user=obj.get_actor().username)},
             updated=item.get('added'),
             links=[{
-                'href': entry.url_for_self(
+                'href': obj.url_for_self(
                     request.urlgen,
                     qualified=True),
                 'rel': 'alternate',
@@ -618,7 +643,7 @@ def processing_panel(request):
     Show to the user what media is still in conversion/processing...
     and what failed, and why!
     """
-    user = User.query.filter_by(username=request.matchdict['user']).first()
+    user = LocalUser.query.filter_by(username=request.matchdict['user']).first()
     # TODO: XXX: Should this be a decorator?
     #
     # Make sure we have permission to access this user's panel.  Only
@@ -631,18 +656,18 @@ def processing_panel(request):
 
     # Get media entries which are in-processing
     processing_entries = MediaEntry.query.\
-        filter_by(uploader = user.id,
+        filter_by(actor = user.id,
                   state = u'processing').\
         order_by(MediaEntry.created.desc())
 
     # Get media entries which have failed to process
     failed_entries = MediaEntry.query.\
-        filter_by(uploader = user.id,
+        filter_by(actor = user.id,
                   state = u'failed').\
         order_by(MediaEntry.created.desc())
 
     processed_entries = MediaEntry.query.\
-        filter_by(uploader = user.id,
+        filter_by(actor = user.id,
                   state = u'processed').\
         order_by(MediaEntry.created.desc()).\
         limit(10)
@@ -662,15 +687,15 @@ def processing_panel(request):
 @get_optional_media_comment_by_id
 def file_a_report(request, media, comment):
     """
-    This view handles the filing of a MediaReport or a CommentReport.
+    This view handles the filing of a Report.
     """
     if comment is not None:
-        if not comment.get_media_entry.id == media.id:
+        if not comment.target().id == media.id:
             return render_404(request)
 
         form = user_forms.CommentReportForm(request.form)
-        context = {'media': media,
-                   'comment':comment,
+        context = {'media': comment.target(),
+                   'comment':comment.comment(),
                    'form':form}
     else:
         form = user_forms.MediaReportForm(request.form)
@@ -680,9 +705,11 @@ def file_a_report(request, media, comment):
 
 
     if request.method == "POST":
-        report_object = build_report_object(form,
+        report_object = build_report_object(
+            form,
             media_entry=media,
-            comment=comment)
+            comment=comment
+        )
 
         # if the object was built successfully, report_table will not be None
         if report_object:
@@ -696,3 +723,37 @@ def file_a_report(request, media, comment):
         request,
         'mediagoblin/user_pages/report.html',
         context)
+
+@require_active_login
+def activity_view(request):
+    """ /<username>/activity/<id> - Display activity
+
+    This should display a HTML presentation of the activity
+    this is NOT an API endpoint.
+    """
+    # Get the user object.
+    username = request.matchdict["username"]
+    user = LocalUser.query.filter_by(username=username).first()
+
+    activity_id = request.matchdict["id"]
+
+    if request.user is None:
+        return render_404(request)
+
+    activity = Activity.query.filter_by(
+        id=activity_id,
+        author=user.id
+    ).first()
+
+    # There isn't many places to check that the public_id is filled so this
+    # will do, it really should be, lets try and fix that if it isn't.
+    activity.get_public_id(request.urlgen)
+
+    if activity is None:
+        return render_404(request)
+
+    return render_to_response(
+        request,
+        "mediagoblin/api/activity.html",
+        {"activity": activity}
+    )