diff --git a/cms/djangoapps/contentstore/views/tests/test_transcript_settings.py b/cms/djangoapps/contentstore/views/tests/test_transcript_settings.py index 05dc62a011..40174725b4 100644 --- a/cms/djangoapps/contentstore/views/tests/test_transcript_settings.py +++ b/cms/djangoapps/contentstore/views/tests/test_transcript_settings.py @@ -5,11 +5,13 @@ from io import BytesIO from mock import Mock, patch, ANY from django.test.testcases import TestCase +from edxval import api from contentstore.tests.utils import CourseTestCase from contentstore.utils import reverse_course_url from contentstore.views.transcript_settings import TranscriptionProviderErrorType, validate_transcript_credentials from openedx.core.djangoapps.profile_images.tests.helpers import make_image_file +from student.roles import CourseStaffRole @ddt.ddt @@ -468,3 +470,112 @@ class TranscriptUploadTest(CourseTestCase): json.loads(response.content)['error'], u'There is a problem with this transcript file. Try to upload a different file.' ) + + +@ddt.ddt +@patch( + 'openedx.core.djangoapps.video_config.models.VideoTranscriptEnabledFlag.feature_enabled', + Mock(return_value=True) +) +class TranscriptUploadTest(CourseTestCase): + """ + Tests for transcript deletion handler. + """ + VIEW_NAME = 'transcript_delete_handler' + + def get_url_for_course_key(self, course_id, **kwargs): + return reverse_course_url(self.VIEW_NAME, course_id, kwargs) + + def test_302_with_anonymous_user(self): + """ + Verify that redirection happens in case of unauthorized request. + """ + self.client.logout() + transcript_delete_url = self.get_url_for_course_key(self.course.id, edx_video_id='test_id', language_code='en') + response = self.client.delete(transcript_delete_url) + self.assertEqual(response.status_code, 302) + + def test_405_with_not_allowed_request_method(self): + """ + Verify that 405 is returned in case of not-allowed request methods. + Allowed request methods include DELETE. + """ + transcript_delete_url = self.get_url_for_course_key(self.course.id, edx_video_id='test_id', language_code='en') + response = self.client.post(transcript_delete_url) + self.assertEqual(response.status_code, 405) + + def test_404_with_feature_disabled(self): + """ + Verify that 404 is returned if the corresponding feature is disabled. + """ + transcript_delete_url = self.get_url_for_course_key(self.course.id, edx_video_id='test_id', language_code='en') + with patch('openedx.core.djangoapps.video_config.models.VideoTranscriptEnabledFlag.feature_enabled') as feature: + feature.return_value = False + response = self.client.delete(transcript_delete_url) + self.assertEqual(response.status_code, 404) + + def test_404_with_non_staff_user(self): + """ + Verify that 404 is returned if the user doesn't have studio write access. + """ + # Making sure that user is not a staff / course's staff. + self.user.is_staff = False + self.user.save() + + # Assert the user's role + self.assertFalse(self.user.is_staff) + self.assertFalse(CourseStaffRole(self.course.id).has_user(self.user)) + + # Now, Make request to deletion handler + transcript_delete_url = self.get_url_for_course_key(self.course.id, edx_video_id='test_id', language_code='en') + response = self.client.delete(transcript_delete_url) + self.assertEqual(response.status_code, 404) + + @ddt.data( + { + 'is_staff': True, + 'is_course_staff': True + }, + { + 'is_staff': False, + 'is_course_staff': True + }, + { + 'is_staff': True, + 'is_course_staff': False + }, + ) + @ddt.unpack + def test_transcript_delete_handler(self, is_staff, is_course_staff): + """ + Tests that transcript delete handler works as expected with combinations of staff and course's staff. + """ + # Setup user's roles + self.user.is_staff = is_staff + self.user.save() + course_staff_role = CourseStaffRole(self.course.id) + if is_course_staff: + course_staff_role.add_users(self.user) + else: + course_staff_role.remove_users(self.user) + + # Assert the user role + self.assertEqual(self.user.is_staff, is_staff) + self.assertEqual(CourseStaffRole(self.course.id).has_user(self.user), is_course_staff) + + video_id, language_code = u'1234', u'en' + # Create a real transcript in VAL. + api.create_or_update_video_transcript( + video_id=video_id, + language_code=language_code, + metadata={'file_format': 'srt'} + ) + + # Make request to transcript deletion handler + response = self.client.delete(self.get_url_for_course_key( + self.course.id, + edx_video_id=video_id, + language_code=language_code + )) + self.assertEqual(response.status_code, 200) + self.assertFalse(api.get_video_transcript_data([video_id], language_code=language_code)) diff --git a/cms/djangoapps/contentstore/views/transcript_settings.py b/cms/djangoapps/contentstore/views/transcript_settings.py index e5457aee9b..6ed21e03b2 100644 --- a/cms/djangoapps/contentstore/views/transcript_settings.py +++ b/cms/djangoapps/contentstore/views/transcript_settings.py @@ -9,9 +9,10 @@ from django.contrib.auth.decorators import login_required from django.core.files.base import ContentFile from django.http import HttpResponseNotFound, HttpResponse from django.utils.translation import ugettext as _ -from django.views.decorators.http import require_POST, require_GET +from django.views.decorators.http import require_http_methods, require_POST, require_GET from edxval.api import ( create_or_update_video_transcript, + delete_video_transcript, get_available_transcript_languages, get_3rd_party_transcription_plans, get_video_transcript_data, @@ -21,12 +22,18 @@ from opaque_keys.edx.keys import CourseKey from openedx.core.djangoapps.video_config.models import VideoTranscriptEnabledFlag from openedx.core.djangoapps.video_pipeline.api import update_3rd_party_transcription_service_credentials +from student.auth import has_studio_write_access from util.json_request import JsonResponse, expect_json from contentstore.views.videos import TranscriptProvider from xmodule.video_module.transcripts_utils import Transcript, TranscriptsGenerationException -__all__ = ['transcript_credentials_handler', 'transcript_download_handler', 'transcript_upload_handler'] +__all__ = [ + 'transcript_credentials_handler', + 'transcript_download_handler', + 'transcript_upload_handler', + 'transcript_delete_handler' +] LOGGER = logging.getLogger(__name__) @@ -254,3 +261,31 @@ def transcript_upload_handler(request, course_key_string): ) return response + + +@login_required +@require_http_methods(["DELETE"]) +def transcript_delete_handler(request, course_key_string, edx_video_id, language_code): + """ + View to delete a transcript file. + + Arguments: + request: A WSGI request object + course_key_string: Course key identifying a course. + edx_video_id: edX video identifier whose transcript need to be deleted. + language_code: transcript's language code. + + Returns + - A 404 if the corresponding feature flag is disabled or user does not have required permisions + - A 200 if transcript is deleted without any error(s) + """ + # Check whether the feature is available for this course. + course_key = CourseKey.from_string(course_key_string) + video_transcripts_enabled = VideoTranscriptEnabledFlag.feature_enabled(course_key) + # User needs to have studio write access for this course. + if not video_transcripts_enabled or not has_studio_write_access(request.user, course_key): + return HttpResponseNotFound() + + delete_video_transcript(video_id=edx_video_id, language_code=language_code) + + return JsonResponse(status=200) diff --git a/cms/djangoapps/contentstore/views/videos.py b/cms/djangoapps/contentstore/views/videos.py index e8ae989c99..315522e0c4 100644 --- a/cms/djangoapps/contentstore/views/videos.py +++ b/cms/djangoapps/contentstore/views/videos.py @@ -597,7 +597,7 @@ def get_all_transcript_languages(): all_languages_dict = dict(settings.ALL_LANGUAGES, **third_party_transcription_languages) # Return combined system settings and 3rd party transcript languages. all_languages = [] - for key, value in sorted(all_languages_dict.iteritems(), key=lambda (k, v): (v, k)): + for key, value in sorted(all_languages_dict.iteritems(), key=lambda (k, v): v): all_languages.append({ 'language_code': key, 'language_text': value @@ -653,6 +653,10 @@ def videos_index_html(course): 'transcript_upload_handler', unicode(course.id) ), + 'transcript_delete_handler_url': reverse_course_url( + 'transcript_delete_handler', + unicode(course.id) + ), 'transcription_plans': get_3rd_party_transcription_plans(), 'trancript_download_file_format': Transcript.SRT } diff --git a/cms/static/js/spec/views/video_transcripts_spec.js b/cms/static/js/spec/views/video_transcripts_spec.js index 36184e842e..4f76d91f37 100644 --- a/cms/static/js/spec/views/video_transcripts_spec.js +++ b/cms/static/js/spec/views/video_transcripts_spec.js @@ -34,11 +34,13 @@ define( TRANSCRIPT_DOWNLOAD_FILE_FORMAT = 'srt', TRANSCRIPT_DOWNLOAD_URL = 'abc.com/transcript_download/course_id', TRANSCRIPT_UPLOAD_URL = 'abc.com/transcript_upload/course_id', + TRANSCRIPT_DELETE_URL = 'abc.com/transcript_delete/course_id', videoSupportedFileFormats = ['.mov', '.mp4'], videoTranscriptSettings = { trancript_download_file_format: TRANSCRIPT_DOWNLOAD_FILE_FORMAT, transcript_download_handler_url: TRANSCRIPT_DOWNLOAD_URL, - transcript_upload_handler_url: TRANSCRIPT_UPLOAD_URL + transcript_upload_handler_url: TRANSCRIPT_UPLOAD_URL, + transcript_delete_handler_url: TRANSCRIPT_DELETE_URL }, videoListView; @@ -119,6 +121,7 @@ define( beforeEach(function() { setFixtures( '
' + + '' + '' ); TemplateHelpers.installTemplate('previous-video-upload-list'); @@ -127,7 +130,7 @@ define( it('renders as expected', function() { // Verify transcript container is present. - expect(videoListView.$el.find('.show-video-transcripts-container')).toExist(); + expect(videoListView.$el.find('.video-transcripts-header')).toExist(); // Veirfy transcript column header is present. expect(videoListView.$el.find('.js-table-head .video-head-col.transcripts-col')).toExist(); // Verify transcript data column is present. @@ -139,7 +142,7 @@ define( it('does not render transcripts view if feature is disabled', function() { renderView(transcripts, false); // Verify transcript container is not present. - expect(videoListView.$el.find('.show-video-transcripts-container')).not.toExist(); + expect(videoListView.$el.find('.video-transcripts-header')).not.toExist(); // Veirfy transcript column header is not present. expect(videoListView.$el.find('.js-table-head .video-head-col.transcripts-col')).not.toExist(); // Verify transcript data column is not present. @@ -150,7 +153,7 @@ define( it('does not show list of transcripts initially', function() { expect( - videoTranscriptsView.$el.find('.show-video-transcripts-wrapper').hasClass('hidden') + videoTranscriptsView.$el.find('.video-transcripts-wrapper').hasClass('hidden') ).toEqual(true); expect(videoTranscriptsView.$el.find('.toggle-show-transcripts-button-text').html().trim()).toEqual( 'Show transcripts (' + transcripts.length + ')' @@ -160,7 +163,7 @@ define( it('shows list of transcripts when clicked on show transcript button', function() { // Verify transcript container is hidden expect( - videoTranscriptsView.$el.find('.show-video-transcripts-wrapper').hasClass('hidden') + videoTranscriptsView.$el.find('.video-transcripts-wrapper').hasClass('hidden') ).toEqual(true); // Verify initial button text @@ -171,7 +174,7 @@ define( // Verify transcript container is not hidden expect( - videoTranscriptsView.$el.find('.show-video-transcripts-wrapper').hasClass('hidden') + videoTranscriptsView.$el.find('.video-transcripts-wrapper').hasClass('hidden') ).toEqual(false); // Verify button text is changed. @@ -191,7 +194,7 @@ define( // Verify transcript container is not hidden expect( - videoTranscriptsView.$el.find('.show-video-transcripts-wrapper').hasClass('hidden') + videoTranscriptsView.$el.find('.video-transcripts-wrapper').hasClass('hidden') ).toEqual(false); videoTranscriptsView.$el.find('.toggle-show-transcripts-button').click(); @@ -203,7 +206,7 @@ define( // Verify transcript container is hidden expect( - videoTranscriptsView.$el.find('.show-video-transcripts-wrapper').hasClass('hidden') + videoTranscriptsView.$el.find('.video-transcripts-wrapper').hasClass('hidden') ).toEqual(true); }); @@ -221,12 +224,12 @@ define( var $transcriptEl; // Show transcripts videoTranscriptsView.$el.find('.toggle-show-transcripts-button').click(); - expect(videoTranscriptsView.$el.find('.show-video-transcript-content').length).toEqual( + expect(videoTranscriptsView.$el.find('.video-transcript-content').length).toEqual( transcripts.length ); _.each(transcripts, function(languageCode) { - $transcriptEl = videoTranscriptsView.$el.find('.show-video-transcript-content[data-language-code="' + languageCode + '"]'); // eslint-disable-line max-len + $transcriptEl = videoTranscriptsView.$el.find('.video-transcript-content[data-language-code="' + languageCode + '"]'); // eslint-disable-line max-len // Verify correct transcript title is set. expect($transcriptEl.find('.transcript-title').html()).toEqual( 'Video client title n_' + languageCode + '.' + TRANSCRIPT_DOWNLOAD_FILE_FORMAT @@ -240,7 +243,7 @@ define( var languageCode = 'en', newLanguageCode = 'ar', requests = AjaxHelpers.requests(this), - $transcriptEl = videoTranscriptsView.$el.find('.show-video-transcript-content[data-language-code="' + languageCode + '"]'); // eslint-disable-line max-len + $transcriptEl = videoTranscriptsView.$el.find('.video-transcript-content[data-language-code="' + languageCode + '"]'); // eslint-disable-line max-len // Verify correct transcript title is set. expect($transcriptEl.find('.transcript-title').html()).toEqual( @@ -255,7 +258,7 @@ define( // Add transcript to upload queue and send POST request to upload transcript. $transcriptEl.find('.upload-transcript-input').fileupload('add', {files: [createFakeTranscriptFile()]}); - // Verify if POST request received for image upload + // Verify if POST request received for transcript upload AjaxHelpers.expectRequest( requests, 'POST', @@ -276,11 +279,97 @@ define( verifyTranscriptStateInfo($transcriptEl, newLanguageCode); }); + it('can delete transcript', function() { + var languageCode = 'en', + requests = AjaxHelpers.requests(this), + $transcriptEl = videoTranscriptsView.$el.find('.video-transcript-content[data-language-code="' + languageCode + '"]'); // eslint-disable-line max-len + + // Verify correct transcript title is set. + expect($transcriptEl.find('.transcript-title').html()).toEqual( + 'Video client title n_' + languageCode + '.' + TRANSCRIPT_DOWNLOAD_FILE_FORMAT + ); + + $transcriptEl.find('.delete-transcript-button').click(); + + // Click remove button on prompt. + $('#page-prompt .action-primary').click(); + + // Verify if DELETE request received for transcript delete + AjaxHelpers.expectRequest( + requests, + 'DELETE', + TRANSCRIPT_DELETE_URL + '/' + edxVideoID + '/' + languageCode + ); + + // Send successful delete response + AjaxHelpers.respondWithJson(requests, {}); + + // Verify English transcript is not present. + expect(videoTranscriptsView.$el.find( + '.video-transcript-content[data-language-code="' + languageCode + '"]' + )).not.toExist(); + + // Verify transcripts view is rendered with transcript deleted for English. + expect(videoTranscriptsView.$el.find('.toggle-show-transcripts-button-text').html().trim()).toEqual( + 'Show transcripts (2)' + ); + }); + + it('should show error message when deleting a transcript in case of server error', function() { + var languageCode = 'en', + requests = AjaxHelpers.requests(this), + $transcriptEl = videoTranscriptsView.$el.find('.video-transcript-content[data-language-code="' + languageCode + '"]'); // eslint-disable-line max-len + + // Verify correct transcript title is set. + expect($transcriptEl.find('.transcript-title').html()).toEqual( + 'Video client title n_' + languageCode + '.' + TRANSCRIPT_DOWNLOAD_FILE_FORMAT + ); + + $transcriptEl.find('.delete-transcript-button').click(); + + // Verify prompt title and description. + + expect($('#page-prompt #prompt-warning-title').html().trim()).toEqual( + 'Are you sure you want to remove this transcript?' + ); + + expect($('#page-prompt #prompt-warning-description').html().trim()).toEqual( + 'If you remove this transcript, the transcript will not be available for any components that use this video.' // eslint-disable-line max-len + ); + + // Click remove button on prompt. + $('#page-prompt .action-primary').click(); + + // Verify if DELETE request received for transcript delete. + AjaxHelpers.expectRequest( + requests, + 'DELETE', + TRANSCRIPT_DELETE_URL + '/' + edxVideoID + '/' + languageCode + ); + + AjaxHelpers.respondWithError(requests, 500); + + // Verify prompt message is shown. + expect($('#page-notification #notification-error-title').html()).toEqual( + "Studio's having trouble saving your work" + ); + + // Verify English transcript container is not removed. + expect(videoTranscriptsView.$el.find( + '.video-transcript-content[data-language-code="' + languageCode + '"]' + )).toExist(); + + // Verify transcripts count is correct. + expect(videoTranscriptsView.$el.find('.toggle-show-transcripts-button-text').html().trim()).toEqual( + 'Show transcripts (3)' + ); + }); + it('shows error state correctly', function() { var languageCode = 'en', requests = AjaxHelpers.requests(this), errorMessage = 'Transcript failed error message', - $transcriptEl = videoTranscriptsView.$el.find('.show-video-transcript-content[data-language-code="' + languageCode + '"]'); // eslint-disable-line max-len + $transcriptEl = videoTranscriptsView.$el.find('.video-transcript-content[data-language-code="' + languageCode + '"]'); // eslint-disable-line max-len $transcriptEl.find('.upload-transcript-button').click(); @@ -306,7 +395,7 @@ define( it('should show error message in case of server error', function() { var languageCode = 'en', requests = AjaxHelpers.requests(this), - $transcriptEl = videoTranscriptsView.$el.find('.show-video-transcript-content[data-language-code="' + languageCode + '"]'); // eslint-disable-line max-len + $transcriptEl = videoTranscriptsView.$el.find('.video-transcript-content[data-language-code="' + languageCode + '"]'); // eslint-disable-line max-len $transcriptEl.find('.upload-transcript-button').click(); @@ -332,7 +421,7 @@ define( var languageCode = 'en', transcriptFileName = 'unsupported-transcript-file-format.txt', errorMessage = 'This file type is not supported. Supported file type is ' + TRANSCRIPT_DOWNLOAD_FILE_FORMAT + '.', // eslint-disable-line max-len - $transcriptEl = videoTranscriptsView.$el.find('.show-video-transcript-content[data-language-code="' + languageCode + '"]'); // eslint-disable-line max-len + $transcriptEl = videoTranscriptsView.$el.find('.video-transcript-content[data-language-code="' + languageCode + '"]'); // eslint-disable-line max-len $transcriptEl.find('.upload-transcript-button').click(); diff --git a/cms/static/js/views/video_transcripts.js b/cms/static/js/views/video_transcripts.js index c034ae224e..3081fb2df7 100644 --- a/cms/static/js/views/video_transcripts.js +++ b/cms/static/js/views/video_transcripts.js @@ -1,8 +1,9 @@ define( ['underscore', 'gettext', 'js/views/baseview', 'common/js/components/views/feedback_prompt', 'edx-ui-toolkit/js/utils/html-utils', 'edx-ui-toolkit/js/utils/string-utils', - 'text!templates/video-transcripts.underscore', 'text!templates/video-transcript-upload-status.underscore'], - function(_, gettext, BaseView, PromptView, HtmlUtils, StringUtils, videoTranscriptsTemplate, + 'common/js/components/utils/view_utils', 'text!templates/video-transcripts.underscore', + 'text!templates/video-transcript-upload-status.underscore'], + function(_, gettext, BaseView, PromptView, HtmlUtils, StringUtils, ViewUtils, videoTranscriptsTemplate, videoTranscriptUploadStatusTemplate) { 'use strict'; @@ -12,10 +13,12 @@ define( events: { 'click .toggle-show-transcripts-button': 'toggleShowTranscripts', 'click .upload-transcript-button': 'chooseFile', + 'click .delete-transcript-button': 'deleteTranscript', 'click .more-details-action': 'showUploadFailureMessage' }, initialize: function(options) { + this.isCollapsed = true; this.transcripts = options.transcripts; this.edxVideoID = options.edxVideoID; this.clientVideoID = options.clientVideoID; @@ -85,33 +88,71 @@ define( ); }, + /* + Returns transcript delete handler url. + */ + getTranscriptDeleteUrl: function(edxVideoID, transcriptLanguageCode, transcriptDeleteHandlerUrl) { + return StringUtils.interpolate( + '{transcriptDeleteHandlerUrl}/{edxVideoID}/{transcriptLanguageCode}', + { + transcriptDeleteHandlerUrl: transcriptDeleteHandlerUrl, + edxVideoID: edxVideoID, + transcriptLanguageCode: transcriptLanguageCode + } + ); + }, + /* Toggles Show/Hide transcript button and transcripts container. */ toggleShowTranscripts: function() { - var $transcriptsWrapperEl = this.$el.find('.show-video-transcripts-wrapper'); + var $transcriptsWrapperEl = this.$el.find('.video-transcripts-wrapper'); - // Toggle show transcript wrapper. - $transcriptsWrapperEl.toggleClass('hidden'); + if ($transcriptsWrapperEl.hasClass('hidden')) { + this.showTranscripts(); + this.isCollapsed = false; + } else { + this.hideTranscripts(); + this.isCollapsed = true; + } + }, - // Toggle button text. + showTranscripts: function() { + // Show transcript wrapper + this.$el.find('.video-transcripts-wrapper').removeClass('hidden'); + + // Update button text. HtmlUtils.setHtml( this.$el.find('.toggle-show-transcripts-button-text'), StringUtils.interpolate( - gettext('{toggleShowTranscriptText} transcripts ({totalTranscripts})'), + gettext('Hide transcripts ({transcriptCount})'), { - toggleShowTranscriptText: $transcriptsWrapperEl.hasClass('hidden') ? gettext('Show') : gettext('Hide'), // eslint-disable-line max-len - totalTranscripts: this.transcripts.length + transcriptCount: this.transcripts.length } ) ); + this.$el.find('.toggle-show-transcripts-icon') + .removeClass('fa-caret-right') + .addClass('fa-caret-down'); + }, - // Toggle icon class. - if ($transcriptsWrapperEl.hasClass('hidden')) { - this.$el.find('.toggle-show-transcripts-icon').removeClass('fa-caret-down').addClass('fa-caret-right'); // eslint-disable-line max-len - } else { - this.$el.find('.toggle-show-transcripts-icon').removeClass('fa-caret-right').addClass('fa-caret-down'); // eslint-disable-line max-len - } + hideTranscripts: function() { + // Hide transcript wrapper + this.$el.find('.video-transcripts-wrapper').addClass('hidden'); + + // Update button text. + HtmlUtils.setHtml( + this.$el.find('.toggle-show-transcripts-button-text'), + StringUtils.interpolate( + gettext('Show transcripts ({transcriptCount})'), + { + transcriptCount: this.transcripts.length + } + ) + ); + this.$el.find('.toggle-show-transcripts-icon') + .removeClass('fa-caret-down') + .addClass('fa-caret-right'); }, validateTranscriptUpload: function(file) { @@ -130,7 +171,7 @@ define( }, chooseFile: function(event) { - var $transcriptContainer = $(event.target).parents('.show-video-transcript-content'), + var $transcriptContainer = $(event.target).parents('.video-transcript-content'), $transcriptUploadEl = $transcriptContainer.find('.upload-transcript-input'); $transcriptUploadEl.fileupload({ @@ -150,7 +191,7 @@ define( transcriptSelected: function(event, data) { var errorMessage, - $transcriptContainer = $(event.target).parents('.show-video-transcript-content'); + $transcriptContainer = $(event.target).parents('.video-transcript-content'); errorMessage = this.validateTranscriptUpload(data.files[0]); if (!errorMessage) { @@ -168,7 +209,7 @@ define( transcriptUploadSucceeded: function(event, data) { var languageCode = data.formData.language_code, newLanguageCode = data.formData.new_language_code, - $transcriptContainer = this.$el.find('.show-video-transcript-content[data-language-code="' + languageCode + '"]'); // eslint-disable-line max-len + $transcriptContainer = this.$el.find('.video-transcript-content[data-language-code="' + languageCode + '"]'); // eslint-disable-line max-len $transcriptContainer.attr('data-language-code', newLanguageCode); $transcriptContainer.find('.download-transcript-button').attr( @@ -197,7 +238,7 @@ define( transcriptUploadFailed: function(event, data) { var errorMessage, languageCode = data.formData.language_code, - $transcriptContainer = this.$el.find('.show-video-transcript-content[data-language-code="' + languageCode + '"]'); // eslint-disable-line max-len + $transcriptContainer = this.$el.find('.video-transcript-content[data-language-code="' + languageCode + '"]'); // eslint-disable-line max-len try { errorMessage = JSON.parse(data.jqXHR.responseText).error; @@ -211,6 +252,39 @@ define( this.renderMessage($transcriptContainer, 'failed', errorMessage); }, + deleteTranscript: function(event) { + var self = this, + $transcriptEl = $(event.target).parents('.video-transcript-content'), + languageCode = $transcriptEl.attr('data-language-code'), + transcriptDeleteUrl = self.getTranscriptDeleteUrl( + self.edxVideoID, + languageCode, + self.videoTranscriptSettings.transcript_delete_handler_url + ); + + ViewUtils.confirmThenRunOperation( + gettext('Are you sure you want to remove this transcript?'), + gettext('If you remove this transcript, the transcript will not be available for any components that use this video.'), // eslint-disable-line max-len + gettext('Remove'), + function() { + ViewUtils.runOperationShowingMessage( + gettext('Removing'), + function() { + return $.ajax({ + url: transcriptDeleteUrl, + type: 'DELETE' + }).done(function() { + // Update transcripts. + self.transcripts = _.without(self.transcripts, languageCode); + // re-render transcripts. + self.render(); + }); + } + ); + } + ); + }, + clearMessage: function() { var $transcriptStatusesEl = this.$el.find('.transcript-upload-status-container'); // Clear all message containers @@ -271,6 +345,12 @@ define( transcriptDownloadHandlerUrl: this.videoTranscriptSettings.transcript_download_handler_url }) ); + + if (this.isCollapsed) { + this.hideTranscripts(); + } else { + this.showTranscripts(); + } return this; } }); diff --git a/cms/static/sass/views/_video-upload.scss b/cms/static/sass/views/_video-upload.scss index 88f6259929..ad02b56a60 100644 --- a/cms/static/sass/views/_video-upload.scss +++ b/cms/static/sass/views/_video-upload.scss @@ -25,7 +25,7 @@ cursor:pointer } - .show-video-transcripts-wrapper { + .video-transcripts-wrapper { display: block; .button-link { @@ -37,7 +37,7 @@ display: none; } - .show-video-transcript-content { + .video-transcript-content { margin-top: ($baseline/2); .transcript-upload-status-container { diff --git a/cms/templates/js/video-transcripts.underscore b/cms/templates/js/video-transcripts.underscore index 8c389c91cc..a5da745ebd 100644 --- a/cms/templates/js/video-transcripts.underscore +++ b/cms/templates/js/video-transcripts.underscore @@ -1,4 +1,4 @@ -