コード例 #1
0
ファイル: video_api.py プロジェクト: meedan/montage
    def video_delete(self, request):
        """
            API Endpoint to delete a project video
        """
        project = self.get_project(request.project_id, assigned_only=True)

        video = get_obj_or_api_404(Video.non_trashed_objects.all(),
                                   project=project,
                                   youtube_id=request.youtube_id)

        if (not self.current_user.is_superuser
                and not project.is_owner_or_admin(self.current_user)
                and self.current_user.pk != video.user_id):
            raise ForbiddenException

        video_id = video.pk
        video.delete(trash=False)

        remove_video_list_cache(project)

        publish_appevent(EventKind.VIDEODELETED,
                         object_id=video_id,
                         video_id=video_id,
                         project_id=project.pk,
                         user=self.current_user)
        return message_types.VoidMessage()
コード例 #2
0
ファイル: videocomment_api.py プロジェクト: meedan/montage
    def delete_reply(self, request):
        """
            Deletes a video comment reply
        """
        project = self.get_project(request.project_id, assigned_only=True)
        video = get_obj_or_api_404(
            Video.objects.select_related('youtube_video'),
            project=project,
            youtube_id=request.youtube_id)

        reply = get_obj_or_api_404(
            TimedVideoComment.objects.select_related('user'),
            tagged_object_id=video.pk,
            pk=request.reply_id)

        if (self.current_user != reply.user and
                not self.current_user.is_superuser):
            raise ForbiddenException

        reply.delete()

        publish_appevent(
            EventKind.TIMEDVIDEOREPLYCOMMENTDELETED,
            object_id=reply.pk,
            video_id=video.pk,
            project_id=project.pk,
            meta=request.comment_id,
            user=self.current_user)

        return message_types.VoidMessage()
コード例 #3
0
    def project_remove_user(self, request):
        """
            Removes a user from the project

            id: The ProjectUser ID
        """
        project = self.get_project(
            request.project_id,
            check_fn=lambda p: p.is_owner_or_admin(self.current_user))

        projectuser = get_obj_or_api_404(ProjectUser, pk=request.id)

        projectuser.delete()

        if projectuser.user_id:
            eventbus.publish_appevent(kind=EventKind.USERREMOVED,
                                      object_id=projectuser.user_id,
                                      project_id=project.pk,
                                      user=self.current_user)
        elif projectuser.pending_user_id:
            eventbus.publish_appevent(kind=EventKind.PENDINGUSERREMOVED,
                                      object_id=projectuser.pending_user_id,
                                      project_id=project.pk,
                                      user=self.current_user)

        return message_types.VoidMessage()
コード例 #4
0
ファイル: videocomment_api.py プロジェクト: meedan/montage
    def create_comment_reply(self, request):
        """
            Creates a reply to a root comment
        """
        project = self.get_project(request.project_id, assigned_only=True)
        video = get_obj_or_api_404(
            Video.objects.select_related('youtube_video'),
            project=project, youtube_id=request.youtube_id)
        comment = get_obj_or_api_404(
            TimedVideoComment.objects.select_related('user'),
            tagged_object_id=video.pk,
            pk=request.comment_id)

        if not comment.is_root():
            raise BadRequestException(
                "Replies can only be added to root comments")

        reply = comment.add_reply(
            request.text,
            self.current_user)

        publish_appevent(
            EventKind.TIMEDVIDEOREPLYCOMMENTCREATED,
            object_id=reply.pk,
            video_id=video.pk,
            project_id=project.pk,
            meta=comment.pk,
            user=self.current_user
        )

        return self.slim_mapper.map(reply, project=project, video=video)
コード例 #5
0
ファイル: videocomment_api.py プロジェクト: meedan/montage
    def create_root_comment(self, request):
        """
            Creates a new root level comment on the video
        """
        project = self.get_project(request.project_id, assigned_only=True)

        video = get_obj_or_api_404(
            Video.objects.select_related('youtube_video'),
            project=project, youtube_id=request.youtube_id)

        comment = TimedVideoComment.add_root(
            video=video,
            start_seconds=request.start_seconds,
            text=request.text,
            user=self.current_user)

        publish_appevent(
            EventKind.TIMEDVIDEOROOTCOMMENTCREATED,
            object_id=comment.pk,
            video_id=video.pk,
            project_id=project.pk,
            user=self.current_user
        )

        return self.mapper.map(comment, project=project, video=video)
コード例 #6
0
ファイル: video_api.py プロジェクト: meedan/montage
    def video_batch_archive(self, request):
        """
            Archive or unarchive a batch of videos

            Pass unarchive=true to unarchive
        """
        project = self.get_project(request.project_id, assigned_only=True)
        is_owner_or_admin = project.is_owner_or_admin(self.current_user)

        qs = Video.archived_objects if request.unarchive else Video.objects

        videos = {
            v.youtube_id: v
            for v in qs.filter(project_id=project.pk,
                               youtube_id__in=request.youtube_ids)
        }

        does_not_exist = [
            make_batch_response_message(yt_id, not_found=True)
            for yt_id in set(request.youtube_ids) - set(videos.keys())
        ]

        video_ids_to_update, permission_denied, success = [], [], []
        for yt_id, video in videos.items():
            if self.current_user.id != video.user_id and not (
                    self.current_user.is_superuser or is_owner_or_admin):
                permission_denied.append(
                    make_batch_response_message(yt_id, forbidden=True))
            else:
                video_ids_to_update.append(video.pk)
                success.append(make_batch_response_message(yt_id,
                                                           success=True))

        (qs.filter(project=project, pk__in=video_ids_to_update).update(
            archived_at=None if request.unarchive else timezone.now()))

        remove_video_list_cache(project)

        for video_id in video_ids_to_update:
            publish_appevent(kind=EventKind.VIDEOUNARCHIVED
                             if request.unarchive else EventKind.VIDEOARCHIVED,
                             object_id=video_id,
                             video_id=video_id,
                             project_id=project.pk,
                             user=self.current_user)

        videos = (Video.all_objects.select_related("youtube_video").filter(
            pk__in=video_ids_to_update) if video_ids_to_update else [])

        return VideoBatchListResponseMessage(
            items=success + does_not_exist + permission_denied,
            videos=map(self.mapper.map, videos),
            is_list=True)
コード例 #7
0
ファイル: onlinecollaborators.py プロジェクト: meedan/montage
    def filter_expired_collaborators(self, collaborators):
        now = datetime.datetime.utcnow()
        for k, v in collaborators.items():
            if (now - v['timestamp']) > self.collaborator_expiry:
                publish_appevent(
                    self.offline_event_kind,
                    object_id=collaborators[k]['id'],
                    project_id=self.object_id,
                    meta=collaborators[k]['id'],
                    user_id=collaborators[k]['id'])

                del collaborators[k]

        return collaborators
コード例 #8
0
ファイル: videocomment_api.py プロジェクト: meedan/montage
    def patch_comment(self, request):
        """
            Patches any video comment
        """
        project = self.get_project(request.project_id, assigned_only=True)
        video = get_obj_or_api_404(
            Video.objects.select_related('youtube_video'),
            project=project, youtube_id=request.youtube_id)

        comment = get_obj_or_api_404(
            TimedVideoComment.objects.select_related('user'),
            tagged_object_id=video.pk,
            pk=request.comment_id)

        if (self.current_user != comment.user and
                not self.current_user.is_superuser):
            raise ForbiddenException

        if request.text is not None:
            comment.text = request.text

        if (request.start_seconds is not None and
                comment.start_seconds != request.start_seconds and
                comment.is_root()):
            # update start_seconds on all children
            comment.start_seconds = request.start_seconds
            reply_ids = list(
                comment.get_children().values_list('pk', flat=True))
            (TimedVideoComment.objects
                .filter(pk__in=reply_ids)
                .update(start_seconds=request.start_seconds))

        if request.text is not None or request.start_seconds is not None:
            comment.save()

            publish_appevent(
                EventKind.TIMEDVIDEOCOMMENTUPDATED,
                object_id=comment.pk,
                video_id=video.pk,
                project_id=project.pk,
                user=self.current_user)

        return self.mapper.map(comment, project=project, video=video)
コード例 #9
0
ファイル: export_views.py プロジェクト: meedan/montage
    def dispatch(self, request, **kwargs):
        """
            Override disptach to validate export format and set user.

            While generally not good practice, we set CSRF as exempt
            because no POST requests handled by this view modify any
            server side data.
        """
        self.user = users.get_current_user()
        self.format = request.POST.get('format', 'csv').lower()
        eventbus.publish_appevent(kind=EventKind.USEREXPORTEDVIDEOS,
                                  object_id=request.user.pk,
                                  project_id=kwargs['project_id'],
                                  user=request.user,
                                  meta=self.format)

        if self.format not in ('csv', 'kml'):
            return HttpResponseBadRequest("Format not valid")

        return super(BaseExportView, self).dispatch(request, **kwargs)
コード例 #10
0
ファイル: video_api.py プロジェクト: meedan/montage
    def video_unarchive(self, request):
        """
            Unarchives the given video
        """
        project = self.get_project(request.project_id, assigned_only=True)

        video = get_obj_or_api_404(Video.archived_objects.all(),
                                   youtube_id=request.youtube_id,
                                   project=project)

        video.unarchive()

        remove_video_list_cache(project)

        publish_appevent(kind=EventKind.VIDEOUNARCHIVED,
                         object_id=video.pk,
                         video_id=video.pk,
                         project_id=project.pk,
                         user=self.current_user)

        return message_types.VoidMessage()
コード例 #11
0
ファイル: video_api.py プロジェクト: meedan/montage
    def video_patch(self, request):
        """
            API Endpoint to patch a project video
        """
        project = self.get_project(request.project_id, assigned_only=True)

        video = get_obj_or_api_404(
            Video.objects.select_related("youtube_video"),
            youtube_id=request.youtube_id,
            project_id=project.pk)

        if request.location_overridden is not None:
            video.location_overridden = request.location_overridden
        if request.recorded_date_overridden is not None:
            video.recorded_date_overridden = request.recorded_date_overridden
        if request.precise_location is not None:
            video.precise_location = request.precise_location

        if video.location_overridden:
            if request.latitude is not None:
                video.latitude = request.latitude
            if request.longitude is not None:
                video.longitude = request.longitude
        else:
            video.latitude = video.longitude = None

        if video.recorded_date_overridden:
            video.recorded_date = request.recorded_date
        else:
            video.recorded_date = None

        video.save()

        publish_appevent(EventKind.VIDEOUPDATED,
                         object_id=video.pk,
                         video_id=video.pk,
                         project_id=project.pk,
                         user=self.current_user)

        return self.mapper.map(video)
コード例 #12
0
ファイル: onlinecollaborators.py プロジェクト: meedan/montage
        def _remove_collaborator():
            collaborators = self.get_collaborators()

            if token in collaborators:
                user_id = collaborators[token]['id']
                del collaborators[token]

                ret = self.client.cas(
                    self.key,
                    collaborators,
                    namespace=self.namespace
                )

                if ret:
                    publish_appevent(
                        self.offline_event_kind,
                        object_id=user_id,
                        project_id=self.object_id,
                        meta=user_id,
                        user_id=user_id)

                return ret
コード例 #13
0
ファイル: onlinecollaborators.py プロジェクト: meedan/montage
        def _add_collaborator():
            collaborators = self.get_collaborators()

            # prevent duplicate collaborator entries if a user has multiple windows open
            for k, v in collaborators.items():
                if v['id'] == user.pk:
                    del collaborators[k]

            collaborators[token] = user_dict

            ret = self.client.cas(
                self.key,
                collaborators,
                namespace=self.namespace
            )

            if ret:
                publish_appevent(
                    self.online_event_kind,
                    object_id=user.pk,
                    project_id=self.object_id,
                    meta=user.pk,
                    user=user)
            return ret
コード例 #14
0
ファイル: video_api.py プロジェクト: meedan/montage
    def video_batch_create(self, request):
        project = self.get_project(request.project_id, assigned_only=True)

        duplicate_videos = {
            v.youtube_video.youtube_id: v
            for v in Video.objects.filter(
                project=project,
                youtube_video__youtube_id__in=request.youtube_ids)
        }

        success, error, videos = [], [], []

        for youtube_id in request.youtube_ids:
            if youtube_id in duplicate_videos:
                video = duplicate_videos[youtube_id]
                error.append(
                    make_batch_response_message(
                        video.youtube_id,
                        error="The video '{0}' already exists in this project".
                        format(youtube_id)))
                videos.append(duplicate_videos[youtube_id])
                continue

        youtube_ids_of_videos_to_add = [
            yt_id for yt_id in request.youtube_ids
            if yt_id not in duplicate_videos
        ]

        youtube_client = YouTubeClient()
        youtube_videos = {
            v.youtube_id: v
            for v in youtube_client.update_or_create_videos(
                youtube_ids_of_videos_to_add)
        }

        for yt_id, yt_video in youtube_videos.items():
            try:
                video = (Video.all_objects.get(youtube_video__youtube_id=yt_id,
                                               project_id=project.pk))
            except Video.DoesNotExist:
                video = Video(
                    project_id=project.pk,
                    youtube_video=yt_video,
                    youtube_id=yt_video.youtube_id,
                    user_id=self.current_user.pk,
                )
            else:
                if not (video.trashed_at or video.archived_at):
                    error.append(
                        make_batch_response_message(
                            None,
                            error="The video '{0}' already exists in "
                            "this project".format(yt_id)))
                else:
                    # if it's trashed or archived then restore it
                    video.archived_at = video.trashed_at = None

            video.save()
            # attach youtube video for mapping response
            video.youtube_video = yt_video
            videos.append(video)

            success.append(
                make_batch_response_message(video.youtube_id, success=True))

            publish_appevent(EventKind.VIDEOCREATED,
                             object_id=video.pk,
                             video_id=video.pk,
                             project_id=project.pk,
                             user=self.current_user)

        remove_video_list_cache(project)
        return VideoBatchListResponseMessage(items=success + error,
                                             videos=map(
                                                 self.mapper.map, videos),
                                             is_list=True)
コード例 #15
0
    def project_add_user(self, request):
        project_users = []
        for email in get_emails(request.email):
            """
            Adds a user to the project
            """
            project = self.get_project(
                request.project_id,
                check_fn=lambda p: p.is_owner_or_admin(self.current_user))

            def send_invite_email():
                send_email(
                    "You've been invited to collaborate on a Montage project",
                    EXISTING_USER_INVITED.format(
                        project_name=project.name,
                        home_link='https://montage.storyful.com'), email)

            User = get_user_model()

            project_user = None
            try:
                user = User.objects.get(email=email)

                project_user = (project.add_admin if request.as_admin else
                                project.add_assigned)(user)

                eventbus.publish_appevent(
                    kind=EventKind.USERINVITEDASPROJECTADMIN if
                    request.as_admin else EventKind.USERINVITEDASPROJECTUSER,
                    object_id=user.pk,
                    project_id=project.pk,
                    user=self.current_user)

                send_invite_email()

            except User.DoesNotExist:
                pending_user, created = User.objects.get_or_create(
                    email=email, username=email)

                project_user = (project.add_admin if request.as_admin else
                                project.add_assigned)(pending_user)

                eventbus.publish_appevent(
                    kind=EventKind.PENDINGUSERINVITEDASPROJECTADMIN
                    if request.as_admin else
                    EventKind.PENDINGUSERINVITEDASPROJECTUSER,
                    object_id=pending_user.pk,
                    project_id=project.pk,
                    meta=pending_user.email,
                    user=self.current_user)

                if created:
                    send_email(
                        "You've been invited to join Montage",
                        NEW_USER_INVITED.format(
                            project_name=project.name,
                            home_link='https://montage.storyful.com'),
                        pending_user.email)
                else:
                    send_invite_email()
            project_users.append(project_user)

        return ProjectUserListResponse(items=map(self.user_mapper.map,
                                                 project_users),
                                       is_list=True)