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)
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)
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))