Example #1
0
class SubtitleEditorBase(View):
    def dispatch(self, request, *args, **kwargs):
        self.handle_special_user(request)
        if not request.user.is_authenticated():
            return redirect_to_login(request.build_absolute_uri())
        return super(SubtitleEditorBase,
                     self).dispatch(request, *args, **kwargs)

    def handle_special_user(self, request):
        if 'special_user' not in request.GET:
            return
        try:
            special_user = User.objects.get(
                id=request.session['editor-user-id'])
        except (KeyError, User.DoesNotExist):
            raise PermissionDenied()
        # We use the editor user for this requests, but still don't log them
        # in.  Note that this will also control the auth headers that get sent
        # to the editor, so the API calls will also use this user.
        request.user = special_user

    def get_video_urls(self):
        """Get video URLs to send to the editor."""
        return self.workflow.editor_video_urls(self.language_code)

    def get_redirect_url(self):
        if 'return_url' in self.request.GET:
            return self.request.GET['return_url']
        url = self.editing_language.get_absolute_url()
        if 'team' in self.request.GET:
            url += '?{}'.format(urlencode({'team': self.request.GET['team']}))
        return url

    def get_custom_css(self):
        return ""

    def get_title(self):
        if self.experimental:
            return _('Amara - Experimental')
        else:
            return _('Amara')

    def calc_base_language(self):
        if (self.video.primary_audio_language_code
                and SubtitleVersion.objects.extant().filter(
                    video=self.video,
                    language_code=self.video.primary_audio_language_code).
                exists()):
            self.base_language = self.video.primary_audio_language_code
        else:
            self.base_language = None

    def calc_editing_language(self):
        self.editing_language = self.video.subtitle_language(
            self.language_code)
        if self.editing_language is None:
            self.editing_language = SubtitleLanguage(
                video=self.video, language_code=self.language_code)

    def check_can_writelock(self):
        if not self.editing_language.can_writelock(self.request.user):
            msg = _(
                "Sorry, you cannot edit these subtitles now because they are being edited by another user. Please check back later."
            )
            messages.error(self.request, msg)
            return False
        else:
            return True

    def check_can_edit(self):
        if self.workflow.user_can_edit_subtitles(self.user,
                                                 self.language_code):
            return True
        learn_more_link = u'<a href="{}">{}</a>'.format(
            u'https://support.amara.org/solution/articles/212109-why-do-i-see-a-message-saying-that-i-am-not-permitted-to-edit-subtitles',
            _(u'Learn more'))

        messages.error(
            self.request,
            fmt(_('Sorry, you do not have permission to edit '
                  'these subtitles. (%(learn_more_link)s)'),
                learn_more_link=learn_more_link))
        return False

    def get_editor_data(self):
        editor_data = {
            'canSync':
            bool(self.request.GET.get('canSync', True)),
            'canAddAndRemove':
            bool(self.request.GET.get('canAddAndRemove', True)),
            # front end needs this to be able to set the correct
            # api headers for saving subs
            'authHeaders': {
                'x-api-username': self.request.user.username,
                'x-apikey': self.request.user.get_api_key()
            },
            'username':
            self.request.user.username,
            'user_fullname':
            unicode(self.request.user),
            'video': {
                'id':
                self.video.video_id,
                'title':
                self.video.title,
                'description':
                self.video.description,
                'duration':
                self.video.duration,
                'primaryVideoURLType':
                video_type_registrar.video_type_for_url(
                    self.video.get_video_url()).abbreviation,
                'videoURLs':
                self.get_video_urls(),
                'metadata':
                self.video.get_metadata(),
            },
            'editingVersion': {
                'languageCode':
                self.editing_language.language_code,
                'versionNumber': (self.editing_version.version_number
                                  if self.editing_version else None),
            },
            'baseLanguage':
            self.base_language,
            'languages':
            [self.editor_data_for_language(lang) for lang in self.languages],
            'languageCode':
            self.request.LANGUAGE_CODE,
            'oldEditorURL':
            reverse('subtitles:old-editor',
                    kwargs={
                        'video_id': self.video.video_id,
                        'language_code': self.editing_language.language_code,
                    }),
            'playbackModes':
            self.get_editor_data_for_playback_modes(),
            'preferences': {
                'showTutorial': self.request.user.show_tutorial,
                'playbackModeId': self.request.user.playback_mode
            },
            'staticURL':
            settings.STATIC_URL,
            'notesHeading':
            'Editor Notes',
            'notesEnabled':
            True,
            'redirectUrl':
            self.get_redirect_url(),
            'customCss':
            self.get_custom_css(),
        }

        editor_data.update(
            self.workflow.editor_data(self.user, self.language_code))

        team_attributes = self.get_team_editor_data()
        if team_attributes:
            editor_data['teamAttributes'] = team_attributes

        return editor_data

    def editor_data_for_language(self, language):
        versions_data = []

        if self.workflow.user_can_view_private_subtitles(
                self.user, language.language_code):
            language_qs = language.subtitleversion_set.extant()
        else:
            language_qs = language.subtitleversion_set.public()
        for i, version in enumerate(language_qs):
            version_data = {
                'version_no': version.version_number,
                'visibility': visibility(version),
            }
            if self.editing_version == version:
                version_data.update(_version_data(version))
            elif self.translated_from_version == version:
                version_data.update(_version_data(version))
            elif (language.language_code == self.base_language
                  and i == len(language_qs) - 1):
                version_data.update(_version_data(version))

            versions_data.append(version_data)

        return {
            'translatedFrom': self.translated_from_version and {
                'language_code':
                self.translated_from_version.subtitle_language.language_code,
                'version_number': self.translated_from_version.version_number,
            },
            'editingLanguage': language == self.editing_language,
            'language_code': language.language_code,
            'name': language.get_language_code_display(),
            'pk': language.pk,
            'numVersions': language.num_versions,
            'versions': versions_data,
            'subtitles_complete': language.subtitles_complete,
            'is_rtl': language.is_rtl(),
            'is_original': language.is_primary_audio_language()
        }

    def get_editor_data_for_playback_modes(self):
        return [{
            'id':
            User.PLAYBACK_MODE_MAGIC,
            'idStr':
            'magic',
            'name':
            _('Magic'),
            'desc':
            _('Recommended: magical auto-pause (just keep typing!)')
        }, {
            'id': User.PLAYBACK_MODE_STANDARD,
            'idStr': 'standard',
            'name': _('Standard'),
            'desc': _('Standard: no automatic pausing, use TAB key')
        }, {
            'id': User.PLAYBACK_MODE_BEGINNER,
            'idStr': 'beginner',
            'name': _('Beginner'),
            'desc': _('Beginner: play 4 seconds, then pause')
        }]

    def get_team_editor_data(self):
        if self.team_video:
            team = self.team_video.team
            return dict([('teamName', team.name), ('type', team.workflow_type),
                         ('features', [
                             f.key_name.split('_', 1)[-1]
                             for f in team.settings.features()
                         ]),
                         ('guidelines',
                          dict([(s.key_name.split('_', 1)[-1],
                                 linebreaks(urlize(force_escape(s.data))))
                                for s in team.settings.guidelines()
                                if s.data.strip()]))])
        else:
            return None

    def assign_task_for_editor(self):
        """Try to assign any unassigned tasks to our user.

        If we can't assign the task, return False.
        """
        if self.team_video is None:
            return True
        task_set = self.team_video.task_set.incomplete().filter(
            language=self.language_code)
        tasks = list(task_set[:1])
        if tasks:
            task = tasks[0]
            if task.assignee is None and can_perform_task(self.user, task):
                task.assignee = self.user
                task.set_expiration()
                task.save()

            if task.assignee != self.user:
                msg = fmt(_("Another user is currently performing "
                            "the %(task_type)s task for these subtitles"),
                          task_type=task.get_type_display())
                messages.error(self.request, msg)
                return False
        return True

    def handle_task(self, context, editor_data):
        """Does most of the dirty-work to handle tasks.  """
        context['task'] = None
        if self.team_video is None:
            return

        task = self.team_video.get_task_for_editor(self.language_code)
        if not task:
            return
        context['task'] = task
        editor_data['task_id'] = task.id
        editor_data['savedNotes'] = task.body
        editor_data['task_needs_pane'] = task.get_type_display() in ('Review',
                                                                     'Approve')
        editor_data['team_slug'] = task.team.slug
        editor_data['oldEditorURL'] += '?' + urlencode(
            {
                'mode': Task.TYPE_NAMES[task.type].lower(),
                'task_id': task.id,
            })

    def get(self, request, video_id, language_code):
        self.video = get_object_or_404(Video, video_id=video_id)
        self.team_video = self.video.get_team_video()
        self.language_code = language_code
        self.user = request.user
        self.calc_base_language()
        self.calc_editing_language()
        self.workflow = get_workflow(self.video)

        if (not self.check_can_edit() or not self.check_can_writelock()
                or not self.assign_task_for_editor()):
            if 'team' in self.request.GET:
                qs = '?{}'.format(urlencode({'team':
                                             self.request.GET['team']}))
                return redirect(self.video.get_absolute_url() + qs)
            return redirect(self.video)

        self.editing_language.writelock(self.user, save=True)
        self.editing_version = self.editing_language.get_tip(public=False)
        # we ignore forking because even if it *is* a fork, we still want to
        # show the user the rererence languages:
        self.translated_from_version = self.editing_language.\
            get_translation_source_version(ignore_forking=True)
        self.languages = self.video.newsubtitlelanguage_set.annotate(
            num_versions=Count('subtitleversion'))
        editor_data = self.get_editor_data()
        self.experimental = 'experimental' in request.GET

        context = {
            'title':
            self.get_title(),
            'video':
            self.video,
            'DEBUG':
            settings.DEBUG,
            'language':
            self.editing_language,
            'other_languages':
            self.languages,
            'version':
            self.editing_version,
            'translated_from_version':
            self.translated_from_version,
            'experimental':
            self.experimental,
            'upload_subtitles_form':
            SubtitlesUploadForm(
                request.user,
                self.video,
                initial={'language_code': self.editing_language.language_code},
                allow_all_languages=True),
        }
        self.handle_task(context, editor_data)
        context['editor_data'] = json.dumps(editor_data, indent=4)

        if self.experimental:
            return render(request, "experimental-editor/editor.html", context)
        else:
            return render(request, "editor/editor.html", context)
Example #2
0
class SubtitleEditorBase(View):
    def dispatch(self, request, *args, **kwargs):
        self.handle_special_user(request)
        if not request.user.is_authenticated():
            return redirect_to_login(request.build_absolute_uri())
        return super(SubtitleEditorBase, self).dispatch(
            request, *args, **kwargs)

    def handle_special_user(self, request):
        if 'special_user' not in request.GET:
            return
        try:
            special_user = User.objects.get(id=request.session['editor-user-id'])
        except (KeyError, User.DoesNotExist):
            raise PermissionDenied()
        # We use the editor user for this requests, but still don't log them
        # in.  Note that this will also control the auth headers that get sent
        # to the editor, so the API calls will also use this user.
        request.user = special_user

    def get_video_urls(self):
        """Get video URLs to send to the editor."""
        return self.workflow.editor_video_urls(self.language_code)

    def get_redirect_url(self):
        if 'return_url' in self.request.GET:
            return self.request.GET['return_url']
        else:
            return self.video.get_absolute_url()

    def get_custom_css(self):
        return ""

    def get_title(self):
        return _('Amara')

    def get_analytics_additions(self):
        return None

    def calc_base_language(self):
        if (self.video.primary_audio_language_code and 
            SubtitleVersion.objects.extant().filter(
                video=self.video,
                language_code=self.video.primary_audio_language_code)
            .exists()):
            self.base_language = self.video.primary_audio_language_code
        else:
            self.base_language = None

    def calc_editing_language(self):
        self.editing_language = self.video.subtitle_language(self.language_code)
        if self.editing_language is None:
            self.editing_language = SubtitleLanguage(
                video=self.video, language_code=self.language_code)

    def check_can_writelock(self):
        if not self.editing_language.can_writelock(self.request.browser_id):
            msg = _("You can't edit this subtitle because it's locked")
            messages.error(self.request, msg)
            return False
        else:
            return True

    def check_can_edit(self):
        if self.workflow.user_can_edit_subtitles(self.user,
                                                 self.language_code):
            return True
        messages.error(self.request,
                       _('Sorry, these subtitles are privately moderated.'))
        return False

    def get_editor_data(self):
        editor_data = {
            'canSync': bool(self.request.GET.get('canSync', True)),
            'canAddAndRemove': bool(self.request.GET.get('canAddAndRemove', True)),
            # front end needs this to be able to set the correct
            # api headers for saving subs
            'authHeaders': {
                'x-api-username': self.request.user.username,
                'x-apikey': self.request.user.get_api_key()
            },
            'username': self.request.user.username,
            'video': {
                'id': self.video.video_id,
                'title': self.video.title,
                'description': self.video.description,
                'primaryVideoURL': self.video.get_video_url(),
                'videoURLs': self.get_video_urls(),
                'metadata': self.video.get_metadata(),
            },
            'editingVersion': {
                'languageCode': self.editing_language.language_code,
                'versionNumber': (self.editing_version.version_number
                                  if self.editing_version else None),
            },
            'baseLanguage': self.base_language,
            'languages': [self.editor_data_for_language(lang)
                          for lang in self.languages],
            'languageCode': self.request.LANGUAGE_CODE,
            'oldEditorURL': reverse('subtitles:old-editor', kwargs={
                'video_id': self.video.video_id,
                'language_code': self.editing_language.language_code,
            }),
            'preferences': {
                'showTutorial': self.request.user.show_tutorial,
            },
            'staticURL': settings.STATIC_URL,
            'notesHeading': 'Editor Notes',
            'redirectUrl': self.get_redirect_url(),
            'customCss': self.get_custom_css(),
        }

        editor_data.update(self.workflow.editor_data(
            self.user, self.language_code))

        team_attributes = self.get_team_editor_data()
        if team_attributes:
            editor_data['teamAttributes'] = team_attributes

        return editor_data

    def editor_data_for_language(self, language):
        versions_data = []

        if self.workflow.user_can_view_private_subtitles(
            self.user, language.language_code):
            language_qs = language.subtitleversion_set.extant()
        else:
            language_qs = language.subtitleversion_set.public()
        for i, version in enumerate(language_qs):
            version_data = {
                'version_no':version.version_number,
                'visibility': visibility(version),
            }
            if self.editing_version == version:
                version_data.update(_version_data(version))
            elif self.translated_from_version == version:
                version_data.update(_version_data(version))
            elif (language.language_code == self.base_language and
                  i == len(language_qs) - 1):
                version_data.update(_version_data(version))

            versions_data.append(version_data)


        return {
            'translatedFrom': self.translated_from_version and {
                'language_code': self.translated_from_version.subtitle_language.language_code,
                'version_number': self.translated_from_version.version_number,
            },
            'editingLanguage': language == self.editing_language,
            'language_code': language.language_code,
            'name': language.get_language_code_display(),
            'pk': language.pk,
            'numVersions': language.num_versions,
            'versions': versions_data,
            'subtitles_complete': language.subtitles_complete,
            'is_rtl': language.is_rtl(),
            'is_original': language.is_primary_audio_language()
        }

    def get_team_editor_data(self):
        if self.team_video:
            team = self.team_video.team
            return dict([('teamName', team.name), ('type', team.workflow_type), ('guidelines', dict(
                [(s.key_name.split('_', 1)[-1],
                  linebreaks(urlize(force_escape(s.data))))
                 for s in team.settings.guidelines()
                 if s.data.strip()]))])
        else:
            return None

    def assign_task_for_editor(self):
        """Try to assign any unassigned tasks to our user.

        If we can't assign the task, return False.
        """
        if self.team_video is None:
            return True
        task_set = self.team_video.task_set.incomplete().filter(
            language=self.language_code)
        tasks = list(task_set[:1])
        if tasks:
            task = tasks[0]
            if task.assignee is None and can_assign_task(task, self.user):
                task.assignee = self.user
                task.set_expiration()
                task.save()

            if task.assignee != self.user:
                msg = fmt(_("Another user is currently performing "
                            "the %(task_type)s task for these subtitles"),
                          task_type=task.get_type_display())
                messages.error(self.request, msg)
                return False
        return True

    def handle_task(self, context, editor_data):
        """Does most of the dirty-work to handle tasks.  """
        context['task'] = None
        if self.team_video is None:
            return

        task = self.team_video.get_task_for_editor(self.language_code)
        if not task:
            return
        context['task'] = task
        editor_data['task_id'] = task.id
        editor_data['savedNotes'] = task.body
        editor_data['task_needs_pane'] = task.get_type_display() in ('Review', 'Approve')
        editor_data['team_slug'] = task.team.slug
        editor_data['oldEditorURL'] += '?' + urlencode({
            'mode': Task.TYPE_NAMES[task.type].lower(),
            'task_id': task.id,
        })

    def get(self, request, video_id, language_code):
        self.video = get_object_or_404(Video, video_id=video_id)
        self.team_video = self.video.get_team_video()
        self.language_code = language_code
        self.user = request.user
        self.calc_base_language()
        self.calc_editing_language()
        self.workflow = get_workflow(self.video)

        if (not self.check_can_edit() or
            not self.check_can_writelock() or
            not self.assign_task_for_editor()):
            return redirect(self.video)

        self.editing_language.writelock(self.user, self.request.browser_id,
                                        save=True)
        self.editing_version = self.editing_language.get_tip(public=False)
        # we ignore forking because even if it *is* a fork, we still want to
        # show the user the rererence languages:
        self.translated_from_version = self.editing_language.\
            get_translation_source_version(ignore_forking=True)
        self.languages = self.video.newsubtitlelanguage_set.annotate(
            num_versions=Count('subtitleversion'))
        editor_data = self.get_editor_data()

        context = {
            'title': self.get_title(),
            'video': self.video,
            'DEBUG': settings.DEBUG,
            'language': self.editing_language,
            'other_languages': self.languages,
            'version': self.editing_version,
            'translated_from_version': self.translated_from_version,
            'GOOGLE_ANALYTICS_ADDITIONS': self.get_analytics_additions(),
            'upload_subtitles_form': SubtitlesUploadForm(
                request.user, self.video,
                initial={'language_code':
                         self.editing_language.language_code},
                allow_all_languages=True),
        }
        self.handle_task(context, editor_data)
        context['editor_data'] = json.dumps(editor_data, indent=4)

        return render(request, "editor/editor.html", context)
Example #3
0
def subtitle_editor(request, video_id, language_code):
    '''
    Renders the subtitle-editor page, with all data neeeded for the UI
    as a json object on the html document.
    If the language does not exist, it will create one and lock it.
    Also decides what source version should be shown initially (if
    it is a translation).
    '''
    # FIXME: permissions
    video = get_object_or_404(Video, video_id=video_id)

    if (video.primary_audio_language_code
            and SubtitleVersion.objects.extant().filter(
                video=video,
                language_code=video.primary_audio_language_code).exists()):
        base_language = video.primary_audio_language_code
    else:
        base_language = None

    try:
        editing_language = video.newsubtitlelanguage_set.get(
            language_code=language_code)
    except SubtitleLanguage.DoesNotExist:
        editing_language = SubtitleLanguage(video=video,
                                            language_code=language_code)

    if not editing_language.can_writelock(request.browser_id):
        messages.error(request,
                       _("You can't edit this subtitle because it's locked"))
        return redirect(video)

    error_message = assign_task_for_editor(video, language_code, request.user)
    if error_message:
        messages.error(request, error_message)
        return redirect(video)
    task = get_task_for_editor(video, language_code)
    check_result = can_add_version(request.user, video, language_code)
    if not check_result:
        messages.error(request, check_result.message)
        return redirect(video)

    editing_language.writelock(request.user, request.browser_id, save=True)

    # if this language is a translation, show both
    editing_version = editing_language.get_tip(public=False)
    # we ignore forking because even if it *is* a fork, we still want to show
    # the user the rererence languages:
    translated_from_version = editing_language.\
        get_translation_source_version(ignore_forking=True)

    languages = video.newsubtitlelanguage_set.annotate(
        num_versions=Count('subtitleversion'))

    video_urls = []
    for v in video.get_video_urls():
        video_urls.append(v.url)

    editor_data = {
        'canSync':
        bool(request.GET.get('canSync', True)),
        'canAddAndRemove':
        bool(request.GET.get('canAddAndRemove', True)),
        # front end needs this to be able to set the correct
        # api headers for saving subs
        'authHeaders': {
            'x-api-username': request.user.username,
            'x-apikey': request.user.get_api_key()
        },
        'video': {
            'id': video.video_id,
            'title': video.title,
            'description': video.description,
            'primaryVideoURL': video.get_video_url(),
            'videoURLs': video_urls,
            'metadata': video.get_metadata(),
        },
        'editingVersion': {
            'languageCode':
            editing_language.language_code,
            'versionNumber':
            (editing_version.version_number if editing_version else None),
        },
        'baseLanguage':
        base_language,
        'languages': [
            _language_data(lang, editing_version, translated_from_version,
                           base_language) for lang in languages
        ],
        'languageCode':
        request.LANGUAGE_CODE,
        'oldEditorURL':
        reverse('subtitles:old-editor',
                kwargs={
                    'video_id': video.video_id,
                    'language_code': editing_language.language_code,
                }),
        'staticURL':
        settings.STATIC_URL,
    }

    if task:
        editor_data['task_id'] = task.id
        editor_data['savedNotes'] = task.body
        editor_data['task_needs_pane'] = task.get_type_display() in ('Review',
                                                                     'Approve')
        editor_data['team_slug'] = task.team.slug
        editor_data['oldEditorURL'] += '?' + urlencode(
            {
                'mode': Task.TYPE_NAMES[task.type].lower(),
                'task_id': task.id,
            })

    team_attributes = get_team_attributes_for_editor(video)
    if team_attributes:
        editor_data['teamAttributes'] = team_attributes

    return render_to_response("subtitles/subtitle-editor.html", {
        'video': video,
        'DEBUG': settings.DEBUG,
        'language': editing_language,
        'other_languages': languages,
        'version': editing_version,
        'translated_from_version': translated_from_version,
        'task': task,
        'editor_data': json.dumps(editor_data, indent=4)
    },
                              context_instance=RequestContext(request))