Merge pull request #17122 from edx/mrehan/delete-transcripts

Delete transcript
This commit is contained in:
Mushtaq Ali
2018-01-12 16:30:31 +05:00
committed by GitHub
8 changed files with 366 additions and 42 deletions

View File

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

View File

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

View File

@@ -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
}

View File

@@ -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(
'<div id="page-prompt"></div>' +
'<div id="page-notification"></div>' +
'<section class="wrapper-assets"></section>'
);
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();

View File

@@ -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;
}
});

View File

@@ -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 {

View File

@@ -1,4 +1,4 @@
<div class='show-video-transcripts-container'>
<div class='video-transcripts-header'>
<% if (transcripts.length) { %>
<button class="button-link toggle-show-transcripts-button">
<strong>
@@ -11,10 +11,10 @@
<% } else { %>
<span class='transcripts-empty-text'><%- gettext('No transcript uploaded.') %></span>
<% }%>
<div class='show-video-transcripts-wrapper hidden'>
<div class='video-transcripts-wrapper'>
<% _.each(transcripts, function(transcriptLanguageCode){ %>
<% selectedLanguageCodes = _.keys(_.omit(transcripts, transcriptLanguageCode)); %>
<div class='show-video-transcript-content' data-edx-video-id="<%- edxVideoID %>" data-language-code="<%- transcriptLanguageCode %>">
<div class='video-transcript-content' data-edx-video-id="<%- edxVideoID %>" data-language-code="<%- transcriptLanguageCode %>">
<div class='transcript-upload-status-container'></div>
<strong class='transcript-title'><%- StringUtils.interpolate(gettext('{transcriptClientTitle}_{transcriptLanguageCode}.{fileExtension}'), {transcriptClientTitle: transcriptClientTitle, transcriptLanguageCode: transcriptLanguageCode, fileExtension: transcriptFileFormat}) %></strong>
<select class='transcript-language-menu'>
@@ -35,6 +35,8 @@
</a>
<span class='transcript-actions-separator'> | </span>
<button class="button-link upload-transcript-button"><%- gettext('Replace') %></button>
<span class='transcript-actions-separator'> | </span>
<button class="button-link delete-transcript-button"><%- gettext('Delete') %></button>
</div>
</div>
<% }) %>

View File

@@ -143,6 +143,9 @@ urlpatterns = [
contentstore.views.transcript_download_handler, name='transcript_download_handler'),
url(r'^transcript_upload/{}$'.format(settings.COURSE_KEY_PATTERN),
contentstore.views.transcript_upload_handler, name='transcript_upload_handler'),
url(r'^transcript_delete/{}(?:/(?P<edx_video_id>[-\w]+))?(?:/(?P<language_code>[^/]*))?$'.format(
settings.COURSE_KEY_PATTERN
), contentstore.views.transcript_delete_handler, name='transcript_delete_handler'),
url(r'^video_encodings_download/{}$'.format(settings.COURSE_KEY_PATTERN),
contentstore.views.video_encodings_download, name='video_encodings_download'),
url(r'^group_configurations/{}$'.format(settings.COURSE_KEY_PATTERN),