Merge remote-tracking branch 'gsoc2016/Subtitle-1'
[mediagoblin.git] / mediagoblin / edit / views.py
index 4ab3fe01d49b98e8469fa3e78892d0cd3b7ece30..717241e8b63ef48989c13a1ef36963b904bb4fd5 100644 (file)
@@ -1,4 +1,4 @@
-# GNU MediaGoblin -- federated, autonomous media hosting
+
 # Copyright (C) 2011, 2012 MediaGoblin contributors.  See AUTHORS.
 #
 # This program is free software: you can redistribute it and/or modify
 # You should have received a copy of the GNU Affero General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
+import six
+
 from datetime import datetime
 
 from itsdangerous import BadSignature
 from pyld import jsonld
 from werkzeug.exceptions import Forbidden
 from werkzeug.utils import secure_filename
+from jsonschema import ValidationError, Draft4Validator
 
 from mediagoblin import messages
 from mediagoblin import mg_globals
@@ -31,8 +34,10 @@ from mediagoblin.edit.lib import may_edit_media
 from mediagoblin.decorators import (require_active_login, active_user_from_url,
                             get_media_entry_by_id, user_may_alter_collection,
                             get_user_collection, user_has_privilege,
-                            user_not_banned)
+                            user_not_banned, user_may_delete_media)
 from mediagoblin.tools.crypto import get_timed_signer_url
+from mediagoblin.tools.metadata import (compact_and_validate, DEFAULT_CHECKER,
+                                        DEFAULT_SCHEMA)
 from mediagoblin.tools.mail import email_debug_message
 from mediagoblin.tools.response import (render_to_response,
                                         redirect, redirect_obj, render_404)
@@ -42,7 +47,7 @@ from mediagoblin.tools.text import (
     convert_to_tag_list_of_dicts, media_tags_as_string)
 from mediagoblin.tools.url import slugify
 from mediagoblin.db.util import check_media_slug_used, check_collection_slug_used
-from mediagoblin.db.models import User
+from mediagoblin.db.models import User, LocalUser, Client, AccessToken, Location
 
 import mimetypes
 
@@ -50,6 +55,10 @@ import mimetypes
 @get_media_entry_by_id
 @require_active_login
 def edit_media(request, media):
+    # If media is not processed, return NotFound.
+    if not media.state == u'processed':
+        return render_404(request)
+
     if not may_edit_media(request, media):
         raise Forbidden("User may not edit this media")
 
@@ -61,14 +70,14 @@ def edit_media(request, media):
         license=media.license)
 
     form = forms.EditForm(
-        request.form,
+        request.method=='POST' and request.form or None,
         **defaults)
 
     if request.method == 'POST' and form.validate():
         # Make sure there isn't already a MediaEntry with such a slug
         # and userid.
         slug = slugify(form.slug.data)
-        slug_used = check_media_slug_used(media.uploader, slug, media.id)
+        slug_used = check_media_slug_used(media.actor, slug, media.id)
 
         if slug_used:
             form.slug.errors.append(
@@ -79,17 +88,18 @@ def edit_media(request, media):
             media.tags = convert_to_tag_list_of_dicts(
                                    form.tags.data)
 
-            media.license = unicode(form.license.data) or None
+            media.license = six.text_type(form.license.data) or None
             media.slug = slug
             media.save()
 
             return redirect_obj(request, media)
 
     if request.user.has_privilege(u'admin') \
-            and media.uploader != request.user.id \
+            and media.actor != request.user.id \
             and request.method != 'POST':
         messages.add_message(
-            request, messages.WARNING,
+            request,
+            messages.WARNING,
             _("You are editing another user's media. Proceed with caution."))
 
     return render_to_response(
@@ -109,6 +119,10 @@ UNSAFE_MIMETYPES = [
 @get_media_entry_by_id
 @require_active_login
 def edit_attachments(request, media):
+    # If media is not processed, return NotFound.
+    if not media.state == u'processed':
+        return render_404(request)
+
     if mg_globals.app_config['allow_attachments']:
         form = forms.EditAttachmentsForm()
 
@@ -137,7 +151,7 @@ def edit_attachments(request, media):
 
             attachment_public_filepath \
                 = mg_globals.public_store.get_unique_filepath(
-                ['media_entries', unicode(media.id), 'attachment',
+                ['media_entries', six.text_type(media.id), 'attachment',
                  public_filename])
 
             attachment_public_file = mg_globals.public_store.get_file(
@@ -159,10 +173,11 @@ def edit_attachments(request, media):
             media.save()
 
             messages.add_message(
-                request, messages.SUCCESS,
-                _("You added the attachment %s!") \
-                    % (form.attachment_name.data
-                       or request.files['attachment_file'].filename))
+                request,
+                messages.SUCCESS,
+                _("You added the attachment %s!") %
+                    (form.attachment_name.data or
+                     request.files['attachment_file'].filename))
 
             return redirect(request,
                             location=media.url_for_self(request.urlgen))
@@ -192,24 +207,44 @@ def edit_profile(request, url_user=None):
         # No need to warn again if admin just submitted an edited profile
         if request.method != 'POST':
             messages.add_message(
-                request, messages.WARNING,
+                request,
+                messages.WARNING,
                 _("You are editing a user's profile. Proceed with caution."))
 
     user = url_user
 
-    form = forms.EditProfileForm(request.form,
+    # Get the location name
+    if user.location is None:
+        location = ""
+    else:
+        location = user.get_location.name
+
+    form = forms.EditProfileForm(
+        request.method == 'POST' and request.form or None,
         url=user.url,
-        bio=user.bio)
+        bio=user.bio,
+        location=location)
 
     if request.method == 'POST' and form.validate():
-        user.url = unicode(form.url.data)
-        user.bio = unicode(form.bio.data)
+        user.url = six.text_type(form.url.data)
+        user.bio = six.text_type(form.bio.data)
+
+        # Save location
+        if form.location.data and user.location is None:
+            user.get_location = Location(name=six.text_type(form.location.data))
+        elif form.location.data:
+            location = user.get_location
+            location.name = six.text_type(form.location.data)
+            location.save()
+        else:
+            user.location = None
 
         user.save()
 
-        messages.add_message(request,
-                             messages.SUCCESS,
-                             _("Profile changes saved"))
+        messages.add_message(
+            request,
+            messages.SUCCESS,
+            _("Profile changes saved"))
         return redirect(request,
                        'mediagoblin.user_pages.user_home',
                         user=user.username)
@@ -228,7 +263,8 @@ EMAIL_VERIFICATION_TEMPLATE = (
 @require_active_login
 def edit_account(request):
     user = request.user
-    form = forms.EditAccountForm(request.form,
+    form = forms.EditAccountForm(
+        request.method == 'POST' and request.form or None,
         wants_comment_notification=user.wants_comment_notification,
         license_preference=user.license_preference,
         wants_notifications=user.wants_notifications)
@@ -240,9 +276,10 @@ def edit_account(request):
         user.license_preference = form.license_preference.data
 
         user.save()
-        messages.add_message(request,
-                             messages.SUCCESS,
-                             _("Account settings saved"))
+        messages.add_message(
+            request,
+            messages.SUCCESS,
+            _("Account settings saved"))
         return redirect(request,
                         'mediagoblin.user_pages.user_home',
                         user=user.username)
@@ -253,6 +290,34 @@ def edit_account(request):
         {'user': user,
          'form': form})
 
+@require_active_login
+def deauthorize_applications(request):
+    """ Deauthroize OAuth applications """
+    if request.method == 'POST' and "application" in request.form:
+        token = request.form["application"]
+        access_token = AccessToken.query.filter_by(token=token).first()
+        if access_token is None:
+            messages.add_message(
+                request,
+                messages.ERROR,
+                _("Unknown application, not able to deauthorize")
+            )
+        else:
+            access_token.delete()
+            messages.add_message(
+                request,
+                messages.SUCCESS,
+                _("Application has been deauthorized")
+            )
+
+    access_tokens = AccessToken.query.filter_by(actor=request.user.id)
+    applications = [(a.get_requesttoken, a) for a in access_tokens]
+
+    return render_to_response(
+        request,
+        'mediagoblin/edit/deauthorize_applications.html',
+        {'applications': applications}
+    )
 
 @require_active_login
 def delete_account(request):
@@ -266,7 +331,8 @@ def delete_account(request):
             request.session.delete()
 
             # Delete user account and all related media files etc....
-            request.user.delete()
+            user = User.query.filter(User.id==user.id).first()
+            user.delete()
 
             # We should send a message that the user has been deleted
             # successfully. But we just deleted the session, so we
@@ -275,7 +341,8 @@ def delete_account(request):
 
         else: # Did not check the confirmation box...
             messages.add_message(
-                request, messages.WARNING,
+                request,
+                messages.WARNING,
                 _('You need to confirm the deletion of your account.'))
 
     # No POST submission or not confirmed, just show page
@@ -295,43 +362,46 @@ def edit_collection(request, collection):
         description=collection.description)
 
     form = forms.EditCollectionForm(
-        request.form,
+        request.method == 'POST' and request.form or None,
         **defaults)
 
     if request.method == 'POST' and form.validate():
         # Make sure there isn't already a Collection with such a slug
         # and userid.
-        slug_used = check_collection_slug_used(collection.creator,
+        slug_used = check_collection_slug_used(collection.actor,
                 form.slug.data, collection.id)
 
         # Make sure there isn't already a Collection with this title
         existing_collection = request.db.Collection.query.filter_by(
-                creator=request.user.id,
+                actor=request.user.id,
                 title=form.title.data).first()
 
         if existing_collection and existing_collection.id != collection.id:
             messages.add_message(
-                request, messages.ERROR,
-                _('You already have a collection called "%s"!') % \
+                request,
+                messages.ERROR,
+                _('You already have a collection called "%s"!') %
                     form.title.data)
         elif slug_used:
             form.slug.errors.append(
                 _(u'A collection with that slug already exists for this user.'))
         else:
-            collection.title = unicode(form.title.data)
-            collection.description = unicode(form.description.data)
-            collection.slug = unicode(form.slug.data)
+            collection.title = six.text_type(form.title.data)
+            collection.description = six.text_type(form.description.data)
+            collection.slug = six.text_type(form.slug.data)
 
             collection.save()
 
             return redirect_obj(request, collection)
 
     if request.user.has_privilege(u'admin') \
-            and collection.creator != request.user.id \
+            and collection.actor != request.user.id \
             and request.method != 'POST':
         messages.add_message(
-            request, messages.WARNING,
-            _("You are editing another user's collection. Proceed with caution."))
+            request,
+            messages.WARNING,
+            _("You are editing another user's collection. "
+              "Proceed with caution."))
 
     return render_to_response(
         request,
@@ -385,9 +455,11 @@ def verify_email(request):
         user=user.username)
 
 
+@require_active_login
 def change_email(request):
     """ View to change the user's email """
-    form = forms.ChangeEmailForm(request.form)
+    form = forms.ChangeEmailForm(
+        request.method == 'POST' and request.form or None)
     user = request.user
 
     # If no password authentication, no need to enter a password
@@ -396,8 +468,9 @@ def change_email(request):
 
     if request.method == 'POST' and form.validate():
         new_email = form.new_email.data
-        users_with_email = User.query.filter_by(
-            email=new_email).count()
+        users_with_email = User.query.filter(
+            LocalUser.email==new_email
+        ).count()
 
         if users_with_email:
             form.new_email.errors.append(
@@ -439,46 +512,30 @@ def change_email(request):
 @require_active_login
 @get_media_entry_by_id
 def edit_metadata(request, media):
-    form = forms.EditMetaDataForm(request.form)
+    # If media is not processed, return NotFound.
+    if not media.state == u'processed':
+        return render_404(request)
+
+    form = forms.EditMetaDataForm(
+        request.method == 'POST' and request.form or None)
     if request.method == "POST" and form.validate():
-        context = dict([(row['identifier'],row['value'])
-                            for row in form.context.data])
         metadata_dict = dict([(row['identifier'],row['value'])
                             for row in form.media_metadata.data])
-        # TODO VALIDATE THIS BEFORE WE ENTER IT
-        # validate(metadata_dict)
-        # validate(context)
-        json_ld_metadata = jsonld.compact(metadata_dict, context)
-        # media.media_metadata = json_ld_metadata
-        # media.save()
+        json_ld_metadata = None
+        json_ld_metadata = compact_and_validate(metadata_dict)
+        media.media_metadata = json_ld_metadata
+        media.save()
         return redirect_obj(request, media)
 
-    if media.media_metadata:
-        for row in media.media_metadata.iteritems():
-            if row[0] == "@context": continue
-            identifier = row[0]
-            # TODO Will change when we revert the metadata branch
-            value = row[1]['@value']
+    if len(form.media_metadata) == 0:
+        for identifier, value in six.iteritems(media.media_metadata):
+            if identifier == "@context": continue
             form.media_metadata.append_entry({
                 'identifier':identifier,
                 'value':value})
-        for row in media.media_metadata['@context'].iteritems():
-            identifier, value = row[0:2]
-            form.context.append_entry({
-                'identifier':identifier,
-                'value':value})
-    else:
-        form.media_metadata.append_entry({
-            'identifier':"",
-            'value':""})
-        form.media_metadata.append_entry({
-            'identifier':"",
-            'value':""})
-        form.context.append_entry({
-            'identifier':"",
-            'value':""})
+
     return render_to_response(
         request,
         'mediagoblin/edit/metadata.html',
         {'form':form,
-         'media':media})
+         'media':media})
\ No newline at end of file