Commit ec399645 by Mushtaq Ali Committed by muzaffaryousaf

Course video settings UI - EDUCATOR-1063

parent 9009669c
...@@ -982,7 +982,9 @@ class TranscriptPreferencesTestCase(VideoUploadTestBase, CourseTestCase): ...@@ -982,7 +982,9 @@ class TranscriptPreferencesTestCase(VideoUploadTestBase, CourseTestCase):
@override_settings(AWS_ACCESS_KEY_ID='test_key_id', AWS_SECRET_ACCESS_KEY='test_secret') @override_settings(AWS_ACCESS_KEY_ID='test_key_id', AWS_SECRET_ACCESS_KEY='test_secret')
@patch('boto.s3.key.Key') @patch('boto.s3.key.Key')
@patch('boto.s3.connection.S3Connection') @patch('boto.s3.connection.S3Connection')
def test_transcript_preferences_metadata(self, transcript_preferences, mock_conn, mock_key): @patch('contentstore.views.videos.get_transcript_preferences')
def test_transcript_preferences_metadata(self, transcript_preferences, mock_transcript_preferences,
mock_conn, mock_key):
""" """
Tests that transcript preference metadata is only set if it is transcript Tests that transcript preference metadata is only set if it is transcript
preferences are present in request data. preferences are present in request data.
...@@ -990,8 +992,7 @@ class TranscriptPreferencesTestCase(VideoUploadTestBase, CourseTestCase): ...@@ -990,8 +992,7 @@ class TranscriptPreferencesTestCase(VideoUploadTestBase, CourseTestCase):
file_name = 'test-video.mp4' file_name = 'test-video.mp4'
request_data = {'files': [{'file_name': file_name, 'content_type': 'video/mp4'}]} request_data = {'files': [{'file_name': file_name, 'content_type': 'video/mp4'}]}
if transcript_preferences: mock_transcript_preferences.return_value = transcript_preferences
request_data.update({'transcript_preferences': transcript_preferences})
bucket = Mock() bucket = Mock()
mock_conn.return_value = Mock(get_bucket=Mock(return_value=bucket)) mock_conn.return_value = Mock(get_bucket=Mock(return_value=bucket))
...@@ -1009,10 +1010,12 @@ class TranscriptPreferencesTestCase(VideoUploadTestBase, CourseTestCase): ...@@ -1009,10 +1010,12 @@ class TranscriptPreferencesTestCase(VideoUploadTestBase, CourseTestCase):
# Ensure `transcript_preferences` was set up in Key correctly if sent through request. # Ensure `transcript_preferences` was set up in Key correctly if sent through request.
if transcript_preferences: if transcript_preferences:
mock_key_instance.set_metadata.assert_any_call('transcript_preferences', transcript_preferences) mock_key_instance.set_metadata.assert_any_call('transcript_preferences', json.dumps(transcript_preferences))
else: else:
with self.assertRaises(AssertionError): with self.assertRaises(AssertionError):
mock_key_instance.set_metadata.assert_any_call('transcript_preferences', transcript_preferences) mock_key_instance.set_metadata.assert_any_call(
'transcript_preferences', json.dumps(transcript_preferences)
)
@patch.dict("django.conf.settings.FEATURES", {"ENABLE_VIDEO_UPLOAD_PIPELINE": True}) @patch.dict("django.conf.settings.FEATURES", {"ENABLE_VIDEO_UPLOAD_PIPELINE": True})
......
...@@ -4,6 +4,7 @@ Views related to the video upload feature ...@@ -4,6 +4,7 @@ Views related to the video upload feature
from contextlib import closing from contextlib import closing
import csv import csv
import json
import logging import logging
from datetime import datetime, timedelta from datetime import datetime, timedelta
from uuid import uuid4 from uuid import uuid4
...@@ -340,6 +341,9 @@ def transcript_preferences_handler(request, course_key_string): ...@@ -340,6 +341,9 @@ def transcript_preferences_handler(request, course_key_string):
data = request.json data = request.json
provider = data.get('provider', '') provider = data.get('provider', '')
# TODO: if provider == '': delete course preferences
# i.e call delete api end point like delete_transcript_preferences(course_key_string)
error, preferences = validate_transcript_preferences( error, preferences = validate_transcript_preferences(
provider=provider, provider=provider,
cielo24_fidelity=data.get('cielo24_fidelity', ''), cielo24_fidelity=data.get('cielo24_fidelity', ''),
...@@ -567,7 +571,7 @@ def videos_index_html(course): ...@@ -567,7 +571,7 @@ def videos_index_html(course):
} }
context.update({ context.update({
'third_party_transcript_settings': { 'video_transcript_settings': {
'transcript_preferences_handler_url': reverse_course_url( 'transcript_preferences_handler_url': reverse_course_url(
'transcript_preferences_handler', 'transcript_preferences_handler',
unicode(course.id) unicode(course.id)
...@@ -658,9 +662,9 @@ def videos_post(course, request): ...@@ -658,9 +662,9 @@ def videos_post(course, request):
('course_key', unicode(course.id)), ('course_key', unicode(course.id)),
] ]
transcript_preferences = data.get('transcript_preferences', None) transcript_preferences = get_transcript_preferences(unicode(course.id))
if transcript_preferences is not None: if transcript_preferences is not None:
metadata_list.append(('transcript_preferences', transcript_preferences)) metadata_list.append(('transcript_preferences', json.dumps(transcript_preferences)))
for metadata_name, value in metadata_list: for metadata_name, value in metadata_list:
key.set_metadata(metadata_name, value) key.set_metadata(metadata_name, value)
......
...@@ -64,7 +64,8 @@ ...@@ -64,7 +64,8 @@
contentType: 'application/json; charset=utf-8', contentType: 'application/json; charset=utf-8',
dataType: 'json', dataType: 'json',
data: JSON.stringify(data), data: JSON.stringify(data),
success: callback success: callback,
global: data ? data.global : true // Trigger global AJAX error handler or not
}); });
}; };
$.postJSON = function(url, data, callback) { // eslint-disable-line no-param-reassign $.postJSON = function(url, data, callback) { // eslint-disable-line no-param-reassign
......
...@@ -258,6 +258,7 @@ ...@@ -258,6 +258,7 @@
'js/spec/views/active_video_upload_list_spec', 'js/spec/views/active_video_upload_list_spec',
'js/spec/views/previous_video_upload_spec', 'js/spec/views/previous_video_upload_spec',
'js/spec/views/video_thumbnail_spec', 'js/spec/views/video_thumbnail_spec',
'js/spec/views/course_video_settings_spec',
'js/spec/views/previous_video_upload_list_spec', 'js/spec/views/previous_video_upload_list_spec',
'js/spec/views/assets_spec', 'js/spec/views/assets_spec',
'js/spec/views/baseview_spec', 'js/spec/views/baseview_spec',
......
...@@ -10,19 +10,23 @@ define([ ...@@ -10,19 +10,23 @@ define([
encodingsDownloadUrl, encodingsDownloadUrl,
defaultVideoImageURL, defaultVideoImageURL,
concurrentUploadLimit, concurrentUploadLimit,
uploadButton, courseVideoSettingsButton,
previousUploads, previousUploads,
videoSupportedFileFormats, videoSupportedFileFormats,
videoUploadMaxFileSizeInGB, videoUploadMaxFileSizeInGB,
activeTranscriptPreferences,
videoTranscriptSettings,
videoImageSettings videoImageSettings
) { ) {
var activeView = new ActiveVideoUploadListView({ var activeView = new ActiveVideoUploadListView({
postUrl: videoHandlerUrl, postUrl: videoHandlerUrl,
concurrentUploadLimit: concurrentUploadLimit, concurrentUploadLimit: concurrentUploadLimit,
uploadButton: uploadButton, courseVideoSettingsButton: courseVideoSettingsButton,
videoSupportedFileFormats: videoSupportedFileFormats, videoSupportedFileFormats: videoSupportedFileFormats,
videoUploadMaxFileSizeInGB: videoUploadMaxFileSizeInGB, videoUploadMaxFileSizeInGB: videoUploadMaxFileSizeInGB,
videoImageSettings: videoImageSettings, videoImageSettings: videoImageSettings,
activeTranscriptPreferences: activeTranscriptPreferences,
videoTranscriptSettings: videoTranscriptSettings,
onFileUploadDone: function(activeVideos) { onFileUploadDone: function(activeVideos) {
$.ajax({ $.ajax({
url: videoHandlerUrl, url: videoHandlerUrl,
......
...@@ -32,20 +32,29 @@ define( ...@@ -32,20 +32,29 @@ define(
describe('ActiveVideoUploadListView', function() { describe('ActiveVideoUploadListView', function() {
beforeEach(function() { beforeEach(function() {
setFixtures( setFixtures(
'<div id="page-prompt"></div><div id="page-notification"></div><div id="reader-feedback"></div>' '<div id="page-prompt"></div>' +
'<div id="page-notification"></div>' +
'<div id="reader-feedback"></div>' +
'<div class="video-transcript-settings-wrapper"></div>' +
'<button class="button course-video-settings-button"></button>'
); );
TemplateHelpers.installTemplate('active-video-upload'); TemplateHelpers.installTemplate('active-video-upload');
TemplateHelpers.installTemplate('active-video-upload-list'); TemplateHelpers.installTemplate('active-video-upload-list');
this.postUrl = POST_URL; this.postUrl = POST_URL;
this.uploadButton = $('<button>'); this.courseVideoSettingsButton = $('.course-video-settings-button');
this.videoSupportedFileFormats = ['.mp4', '.mov']; this.videoSupportedFileFormats = ['.mp4', '.mov'];
this.videoUploadMaxFileSizeInGB = 5; this.videoUploadMaxFileSizeInGB = 5;
this.view = new ActiveVideoUploadListView({ this.view = new ActiveVideoUploadListView({
concurrentUploadLimit: concurrentUploadLimit, concurrentUploadLimit: concurrentUploadLimit,
postUrl: this.postUrl, postUrl: this.postUrl,
uploadButton: this.uploadButton, courseVideoSettingsButton: this.courseVideoSettingsButton,
videoSupportedFileFormats: this.videoSupportedFileFormats, videoSupportedFileFormats: this.videoSupportedFileFormats,
videoUploadMaxFileSizeInGB: this.videoUploadMaxFileSizeInGB videoUploadMaxFileSizeInGB: this.videoUploadMaxFileSizeInGB,
activeTranscriptPreferences: {},
videoTranscriptSettings: {
transcript_preferences_handler_url: '',
transcription_plans: {}
}
}); });
this.view.render(); this.view.render();
jasmine.Ajax.install(); jasmine.Ajax.install();
...@@ -55,6 +64,10 @@ define( ...@@ -55,6 +64,10 @@ define(
afterEach(function() { afterEach(function() {
$(window).off('beforeunload'); $(window).off('beforeunload');
jasmine.Ajax.uninstall(); jasmine.Ajax.uninstall();
if (this.view.courseVideoSettingsView) {
this.view.courseVideoSettingsView = null;
}
}); });
it('renders correct text in file drag/drop area', function() { it('renders correct text in file drag/drop area', function() {
...@@ -71,15 +84,19 @@ define( ...@@ -71,15 +84,19 @@ define(
}); });
}); });
it('should trigger file selection when either the upload button or the drop zone is clicked', function() { it('should trigger file selection when the drop zone is clicked', function() {
var clickSpy = jasmine.createSpy(); var clickSpy = jasmine.createSpy();
clickSpy.and.callFake(function(event) { event.preventDefault(); }); clickSpy.and.callFake(function(event) { event.preventDefault(); });
this.view.$('.js-file-input').on('click', clickSpy); this.view.$('.js-file-input').on('click', clickSpy);
this.view.$('.file-drop-area').click(); this.view.$('.file-drop-area').click();
expect(clickSpy).toHaveBeenCalled(); expect(clickSpy).toHaveBeenCalled();
clickSpy.calls.reset(); clickSpy.calls.reset();
this.uploadButton.click(); });
expect(clickSpy).toHaveBeenCalled();
it('shows course video settings pane when course video settings button is clicked', function() {
expect($('.course-video-settings-container')).not.toExist();
this.courseVideoSettingsButton.click();
expect($('.course-video-settings-container')).toExist();
}); });
it('should not show a notification message if there are no active video uploads', function() { it('should not show a notification message if there are no active video uploads', function() {
......
define(
['jquery', 'underscore', 'backbone', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers',
'js/views/course_video_settings', 'common/js/spec_helpers/template_helpers'],
function($, _, Backbone, AjaxHelpers, CourseVideoSettingsView, TemplateHelpers) {
'use strict';
describe('CourseVideoSettingsView', function() {
var $courseVideoSettingsEl,
courseVideoSettingsView,
renderCourseVideoSettingsView,
destroyCourseVideoSettingsView,
verifyPreferanceErrorState,
transcriptPreferencesUrl = '/transcript_preferences/course-v1:edX+DemoX+Demo_Course',
activeTranscriptPreferences = {
provider: 'Cielo24',
cielo24_fidelity: 'PROFESSIONAL',
cielo24_turnaround: 'PRIORITY',
three_play_turnaround: '',
preferred_languages: ['fr', 'en'],
modified: '2017-08-27T12:28:17.421260Z'
},
transcriptionPlans = {
'3PlayMedia': {
languages: {
fr: 'French',
en: 'English'
},
turnaround: {
default: '4-Day/Default',
same_day_service: 'Same Day',
rush_service: '24-hour/Rush',
extended_service: '10-Day/Extended',
expedited_service: '2-Day/Expedited'
},
display_name: '3PlayMedia'
},
Cielo24: {
turnaround: {
PRIORITY: 'Priority, 24h',
STANDARD: 'Standard, 48h'
},
fidelity: {
PROFESSIONAL: {
languages: {
ru: 'Russian',
fr: 'French',
en: 'English'
},
display_name: 'Professional, 99% Accuracy'
},
PREMIUM: {
languages: {
en: 'English'
},
display_name: 'Premium, 95% Accuracy'
},
MECHANICAL: {
languages: {
fr: 'French',
en: 'English',
nl: 'Dutch'
},
display_name: 'Mechanical, 75% Accuracy'
}
},
display_name: 'Cielo24'
}
};
renderCourseVideoSettingsView = function(activeTranscriptPreferencesData, transcriptionPlansData) {
courseVideoSettingsView = new CourseVideoSettingsView({
activeTranscriptPreferences: activeTranscriptPreferencesData || null,
videoTranscriptSettings: {
transcript_preferences_handler_url: transcriptPreferencesUrl,
transcription_plans: transcriptionPlansData || null
}
});
$courseVideoSettingsEl = courseVideoSettingsView.render().$el;
};
destroyCourseVideoSettingsView = function() {
if (courseVideoSettingsView) {
courseVideoSettingsView.closeCourseVideoSettings();
courseVideoSettingsView = null;
}
};
verifyPreferanceErrorState = function($preferanceContainerEl, hasError) {
var $errorIconHtml = hasError ? '<span class="icon fa fa-info-circle" aria-hidden="true"></span>' : '',
requiredText = hasError ? 'Required' : '';
expect($preferanceContainerEl.hasClass('error')).toEqual(hasError);
expect($preferanceContainerEl.find('.error-icon').html()).toEqual($errorIconHtml);
expect($preferanceContainerEl.find('.error-info').html()).toEqual(requiredText);
};
beforeEach(function() {
setFixtures(
'<div class="video-transcript-settings-wrapper"></div>' +
'<button class="button course-video-settings-button"></button>'
);
TemplateHelpers.installTemplate('course-video-settings');
renderCourseVideoSettingsView(activeTranscriptPreferences, transcriptionPlans);
});
afterEach(function() {
destroyCourseVideoSettingsView();
});
it('renders as expected', function() {
expect($courseVideoSettingsEl.find('.course-video-settings-container')).toExist();
});
it('closes course video settings pane when close button is clicked', function() {
expect($courseVideoSettingsEl.find('.course-video-settings-container')).toExist();
$courseVideoSettingsEl.find('.action-close-course-video-settings').click();
expect($courseVideoSettingsEl.find('.course-video-settings-container')).not.toExist();
});
it('closes course video settings pane when clicked outside course video settings pane', function() {
expect($courseVideoSettingsEl.find('.course-video-settings-container')).toExist();
$('body').click();
expect($courseVideoSettingsEl.find('.course-video-settings-container')).not.toExist();
});
it('does not close course video settings pane when clicked inside course video settings pane', function() {
expect($courseVideoSettingsEl.find('.course-video-settings-container')).toExist();
$courseVideoSettingsEl.find('.transcript-provider-group').click();
expect($courseVideoSettingsEl.find('.course-video-settings-container')).toExist();
});
it('does not populate transcription plans if transcription plans are not provided', function() {
// First detroy old referance to the view.
destroyCourseVideoSettingsView();
// Create view with empty data.
renderCourseVideoSettingsView(null, null);
expect($courseVideoSettingsEl.find('.transcript-provider-group').html()).toEqual('');
expect($courseVideoSettingsEl.find('#transcript-turnaround').html()).toEqual('');
expect($courseVideoSettingsEl.find('#transcript-fidelity').html()).toEqual('');
expect($courseVideoSettingsEl.find('#transcript-language').html()).toEqual('');
});
it('populates transcription plans correctly', function() {
// Only check transcript-provider radio buttons for now, because other preferances are based on either
// user selection or activeTranscriptPreferences.
expect($courseVideoSettingsEl.find('.transcript-provider-group').html()).not.toEqual('');
});
it('populates active preferances correctly', function() {
// First check preferance are selected correctly in HTML.
expect($courseVideoSettingsEl.find('.transcript-provider-group input:checked').val()).toEqual(
activeTranscriptPreferences.provider
);
expect($courseVideoSettingsEl.find('#transcript-turnaround').val()).toEqual(
activeTranscriptPreferences.cielo24_turnaround
);
expect($courseVideoSettingsEl.find('#transcript-fidelity').val()).toEqual(
activeTranscriptPreferences.cielo24_fidelity
);
expect($courseVideoSettingsEl.find('.transcript-language-container').length).toEqual(
activeTranscriptPreferences.preferred_languages.length
);
// Now check values are assigned correctly.
expect(courseVideoSettingsView.selectedProvider, activeTranscriptPreferences.provider);
expect(courseVideoSettingsView.selectedTurnaroundPlan, activeTranscriptPreferences.cielo24_turnaround);
expect(courseVideoSettingsView.selectedFidelityPlan, activeTranscriptPreferences.cielo24_fidelity);
expect(courseVideoSettingsView.selectedLanguages, activeTranscriptPreferences.preferred_languages);
});
it('saves transcript settings on update settings button click if preferances are selected', function() {
var requests = AjaxHelpers.requests(this);
$courseVideoSettingsEl.find('.action-update-course-video-settings').click();
AjaxHelpers.expectRequest(
requests,
'POST',
transcriptPreferencesUrl,
JSON.stringify({
provider: activeTranscriptPreferences.provider,
cielo24_fidelity: activeTranscriptPreferences.cielo24_fidelity,
cielo24_turnaround: activeTranscriptPreferences.cielo24_turnaround,
three_play_turnaround: activeTranscriptPreferences.three_play_turnaround,
preferred_languages: activeTranscriptPreferences.preferred_languages,
global: false
})
);
// Send successful upload response.
AjaxHelpers.respondWithJson(requests, {
transcript_preferences: activeTranscriptPreferences
});
// Verify that success message is shown.
expect($courseVideoSettingsEl.find('.course-video-settings-message-wrapper.success').html()).toEqual(
'<div class="course-video-settings-message">' +
'<span class="icon fa fa-check-circle" aria-hidden="true"></span>' +
'<span>Settings updated</span>' +
'</div>'
);
});
it('shows error message if server sends error', function() {
var requests = AjaxHelpers.requests(this);
$courseVideoSettingsEl.find('.action-update-course-video-settings').click();
AjaxHelpers.expectRequest(
requests,
'POST',
transcriptPreferencesUrl,
JSON.stringify({
provider: activeTranscriptPreferences.provider,
cielo24_fidelity: activeTranscriptPreferences.cielo24_fidelity,
cielo24_turnaround: activeTranscriptPreferences.cielo24_turnaround,
three_play_turnaround: activeTranscriptPreferences.three_play_turnaround,
preferred_languages: activeTranscriptPreferences.preferred_languages,
global: false
})
);
// Send error response.
AjaxHelpers.respondWithError(requests, 400, {
error: 'Error message'
});
// Verify that error message is shown.
expect($courseVideoSettingsEl.find('.course-video-settings-message-wrapper.error').html()).toEqual(
'<div class="course-video-settings-message">' +
'<span class="icon fa fa-info-circle" aria-hidden="true"></span>' +
'<span>Error message</span>' +
'</div>'
);
});
it('implies preferences are required if not selected when saving preferances', function() {
// Reset so that no preferance is selected.
courseVideoSettingsView.selectedTurnaroundPlan = '';
courseVideoSettingsView.selectedFidelityPlan = '';
courseVideoSettingsView.selectedLanguages = [];
$courseVideoSettingsEl.find('.action-update-course-video-settings').click();
verifyPreferanceErrorState($courseVideoSettingsEl.find('.transcript-turnaround-wrapper'), true);
verifyPreferanceErrorState($courseVideoSettingsEl.find('.transcript-fidelity-wrapper'), true);
verifyPreferanceErrorState($courseVideoSettingsEl.find('.transcript-languages-wrapper'), true);
});
it('removes error state on preferances if selected', function() {
// Provide values for preferances.
$courseVideoSettingsEl.find('#transcript-turnaround').val('test-value');
$courseVideoSettingsEl.find('#transcript-fidelity').val('test-value');
$courseVideoSettingsEl.find('#transcript-language-menu').val('test-value');
verifyPreferanceErrorState($courseVideoSettingsEl.find('.transcript-turnaround-wrapper'), false);
verifyPreferanceErrorState($courseVideoSettingsEl.find('.transcript-fidelity-wrapper'), false);
verifyPreferanceErrorState($courseVideoSettingsEl.find('.transcript-languages-wrapper'), false);
});
// TODO: Add more tests like clicking on add language, remove and their scenarios and some other tests
// like N/A selected, specific provider selected tests, specific preferance selected tests etc.
});
}
);
...@@ -5,12 +5,13 @@ define([ ...@@ -5,12 +5,13 @@ define([
'js/models/active_video_upload', 'js/models/active_video_upload',
'js/views/baseview', 'js/views/baseview',
'js/views/active_video_upload', 'js/views/active_video_upload',
'js/views/course_video_settings',
'edx-ui-toolkit/js/utils/html-utils', 'edx-ui-toolkit/js/utils/html-utils',
'edx-ui-toolkit/js/utils/string-utils', 'edx-ui-toolkit/js/utils/string-utils',
'text!templates/active-video-upload-list.underscore', 'text!templates/active-video-upload-list.underscore',
'jquery.fileupload' 'jquery.fileupload'
], ],
function($, _, Backbone, ActiveVideoUpload, BaseView, ActiveVideoUploadView, function($, _, Backbone, ActiveVideoUpload, BaseView, ActiveVideoUploadView, CourseVideoSettingsView,
HtmlUtils, StringUtils, activeVideoUploadListTemplate) { HtmlUtils, StringUtils, activeVideoUploadListTemplate) {
'use strict'; 'use strict';
var ActiveVideoUploadListView, var ActiveVideoUploadListView,
...@@ -40,11 +41,13 @@ define([ ...@@ -40,11 +41,13 @@ define([
this.listenTo(this.collection, 'add', this.addUpload); this.listenTo(this.collection, 'add', this.addUpload);
this.concurrentUploadLimit = options.concurrentUploadLimit || 0; this.concurrentUploadLimit = options.concurrentUploadLimit || 0;
this.postUrl = options.postUrl; this.postUrl = options.postUrl;
this.activeTranscriptPreferences = options.activeTranscriptPreferences;
this.videoTranscriptSettings = options.videoTranscriptSettings;
this.videoSupportedFileFormats = options.videoSupportedFileFormats; this.videoSupportedFileFormats = options.videoSupportedFileFormats;
this.videoUploadMaxFileSizeInGB = options.videoUploadMaxFileSizeInGB; this.videoUploadMaxFileSizeInGB = options.videoUploadMaxFileSizeInGB;
this.onFileUploadDone = options.onFileUploadDone; this.onFileUploadDone = options.onFileUploadDone;
if (options.uploadButton) { if (options.courseVideoSettingsButton) {
options.uploadButton.click(this.chooseFile.bind(this)); options.courseVideoSettingsButton.click(this.showCourseVideoSettingsView.bind(this));
} }
this.maxSizeText = StringUtils.interpolate( this.maxSizeText = StringUtils.interpolate(
...@@ -59,6 +62,33 @@ define([ ...@@ -59,6 +62,33 @@ define([
supportedVideoTypes: this.videoSupportedFileFormats.join(', ') supportedVideoTypes: this.videoSupportedFileFormats.join(', ')
} }
); );
this.listenTo(
Backbone,
'coursevideosettings:syncActiveTranscriptPreferences',
this.syncActiveTranscriptPreferences
);
this.listenTo(
Backbone,
'coursevideosettings:destroyCourseVideoSettingsView',
this.destroyCourseVideoSettingsView
);
},
syncActiveTranscriptPreferences: function(activeTranscriptPreferences) {
this.activeTranscriptPreferences = activeTranscriptPreferences;
},
showCourseVideoSettingsView: function(event) {
this.courseVideoSettingsView = new CourseVideoSettingsView({
activeTranscriptPreferences: this.activeTranscriptPreferences,
videoTranscriptSettings: this.videoTranscriptSettings
});
this.courseVideoSettingsView.render();
event.stopPropagation();
},
destroyCourseVideoSettingsView: function() {
this.courseVideoSettingsView = null;
}, },
render: function() { render: function() {
...@@ -98,7 +128,6 @@ define([ ...@@ -98,7 +128,6 @@ define([
$(window).on('drop', preventDefault); $(window).on('drop', preventDefault);
$(window).on('beforeunload', this.onBeforeUnload.bind(this)); $(window).on('beforeunload', this.onBeforeUnload.bind(this));
$(window).on('unload', this.onUnload.bind(this)); $(window).on('unload', this.onUnload.bind(this));
return this; return this;
}, },
......
/**
* CourseVideoSettingsView shows a sidebar containing course wide video settings which we show on Video Uploads page.
*/
define([
'jquery', 'backbone', 'underscore', 'gettext', 'moment',
'edx-ui-toolkit/js/utils/html-utils',
'edx-ui-toolkit/js/utils/string-utils',
'text!templates/course-video-settings.underscore'
],
function($, Backbone, _, gettext, moment, HtmlUtils, StringUtils, TranscriptSettingsTemplate) {
'use strict';
var CourseVideoSettingsView,
CIELO24 = 'Cielo24',
THREE_PLAY_MEDIA = '3PlayMedia';
CourseVideoSettingsView = Backbone.View.extend({
el: 'div.video-transcript-settings-wrapper',
events: {
'change .transcript-provider-group input': 'providerSelected',
'change #transcript-turnaround': 'turnaroundSelected',
'change #transcript-fidelity': 'fidelitySelected',
'click .action-add-language': 'languageSelected',
'click .action-remove-language': 'languageRemoved',
'click .action-update-course-video-settings': 'updateCourseVideoSettings',
'click .action-close-course-video-settings': 'closeCourseVideoSettings'
},
initialize: function(options) {
var videoTranscriptSettings = options.videoTranscriptSettings;
this.activeTranscriptionPlan = options.activeTranscriptPreferences;
this.availableTranscriptionPlans = videoTranscriptSettings.transcription_plans;
this.transcriptHandlerUrl = videoTranscriptSettings.transcript_preferences_handler_url;
this.template = HtmlUtils.template(TranscriptSettingsTemplate);
this.setActiveTranscriptPlanData();
this.selectedLanguages = [];
},
registerCloseClickHandler: function() {
var self = this;
// Preventing any parent handlers from being notified of the event. This is to stop from firing the document
// level click handler to execute on course video settings pane click.
self.$el.click(function(event) {
event.stopPropagation();
});
// Click anywhere outside the course video settings pane would close the pane.
$(document).click(function(event) {
// if the target of the click isn't the container nor a descendant of the contain
if (!self.$el.is(event.target) && self.$el.has(event.target).length === 0) {
self.closeCourseVideoSettings();
}
});
},
resetPlanData: function() {
this.selectedProvider = '';
this.selectedTurnaroundPlan = '';
this.selectedFidelityPlan = '';
this.availableLanguages = [];
this.activeLanguages = [];
this.selectedLanguages = [];
},
setActiveTranscriptPlanData: function() {
if (this.activeTranscriptionPlan) {
this.selectedProvider = this.activeTranscriptionPlan.provider;
this.selectedFidelityPlan = this.activeTranscriptionPlan.cielo24_fidelity;
this.selectedTurnaroundPlan = this.selectedProvider === CIELO24 ?
this.activeTranscriptionPlan.cielo24_turnaround :
this.activeTranscriptionPlan.three_play_turnaround;
this.activeLanguages = this.activeTranscriptionPlan.preferred_languages;
} else {
this.resetPlanData();
}
},
getTurnaroundPlan: function() {
var turnaroundPlan = null;
if (this.selectedProvider) {
turnaroundPlan = this.availableTranscriptionPlans[this.selectedProvider].turnaround;
}
return turnaroundPlan;
},
getFidelityPlan: function() {
var fidelityPlan = null;
if (this.selectedProvider === CIELO24) {
fidelityPlan = this.availableTranscriptionPlans[this.selectedProvider].fidelity;
}
return fidelityPlan;
},
getPlanLanguages: function() {
var selectedPlan = this.availableTranscriptionPlans[this.selectedProvider];
if (this.selectedProvider === CIELO24) {
return selectedPlan.fidelity[this.selectedFidelityPlan].languages;
}
return selectedPlan.languages;
},
fidelitySelected: function(event) {
var $fidelityContainer = this.$el.find('.transcript-fidelity-wrapper');
this.selectedFidelityPlan = event.target.value;
// Remove any error if present already.
this.clearPreferenceErrorState($fidelityContainer);
// Clear active and selected languages.
this.selectedLanguages = this.activeLanguages = [];
this.renderLanguages();
},
turnaroundSelected: function(event) {
var $turnaroundContainer = this.$el.find('.transcript-turnaround-wrapper');
this.selectedTurnaroundPlan = event.target.value;
// Remove any error if present already.
this.clearPreferenceErrorState($turnaroundContainer);
},
providerSelected: function(event) {
this.resetPlanData();
this.selectedProvider = event.target.value;
this.renderPreferences();
},
languageSelected: function(event) {
var $parentEl = $(event.target.parentElement).parent(),
$languagesEl = this.$el.find('.transcript-languages-wrapper'),
selectedLanguage = $parentEl.find('select').val();
// Remove any error if present already.
this.clearPreferenceErrorState($languagesEl);
// Only add if not in the list already.
if (selectedLanguage && _.indexOf(this.selectedLanguages, selectedLanguage) === -1) {
this.selectedLanguages.push(selectedLanguage);
this.addLanguage(selectedLanguage);
// Populate language menu with latest data.
this.populateLanguageMenu();
} else {
this.addErrorState($languagesEl);
}
},
languageRemoved: function(event) {
var selectedLanguage = $(event.target).data('language-code');
$(event.target.parentElement).parent().remove();
// Remove language from selected languages.
this.selectedLanguages = _.without(this.selectedLanguages, selectedLanguage);
// Populate menu again to reflect latest changes.
this.populateLanguageMenu();
},
renderProviders: function() {
var self = this,
providerPlan = self.availableTranscriptionPlans,
$providerEl = self.$el.find('.transcript-provider-group');
if (providerPlan) {
HtmlUtils.setHtml(
$providerEl,
HtmlUtils.interpolateHtml(
HtmlUtils.HTML('<input type="radio" id="transcript-provider-none" name="transcript-provider" value="" {checked}/><label for="transcript-provider-none">{text}</label>'), // eslint-disable-line max-len
{
text: gettext('N/A'),
checked: self.selectedProvider === '' ? 'checked' : ''
}
)
);
_.each(providerPlan, function(providerObject, key) {
var checked = self.selectedProvider === key ? 'checked' : '';
HtmlUtils.append(
$providerEl,
HtmlUtils.interpolateHtml(
HtmlUtils.HTML('<input type="radio" id="transcript-provider-{value}" name="transcript-provider" value="{value}" {checked}/><label for="transcript-provider-{value}">{text}'), // eslint-disable-line max-len
{
text: providerObject.display_name,
value: key,
checked: checked
}
)
);
});
}
},
renderTurnaround: function() {
var self = this,
turnaroundPlan = self.getTurnaroundPlan(),
$turnaroundContainer = self.$el.find('.transcript-turnaround-wrapper'),
$turnaround = self.$el.find('#transcript-turnaround');
// Clear error state if present any.
this.clearPreferenceErrorState($turnaroundContainer);
if (turnaroundPlan) {
HtmlUtils.setHtml(
$turnaround,
HtmlUtils.HTML(new Option(gettext('Select turnaround'), ''))
);
_.each(turnaroundPlan, function(value, key) {
var option = new Option(value, key);
if (self.selectedTurnaroundPlan === key) {
option.selected = true;
}
HtmlUtils.append($turnaround, HtmlUtils.HTML(option));
});
$turnaroundContainer.show();
} else {
$turnaroundContainer.hide();
}
},
renderFidelity: function() {
var self = this,
fidelityPlan = self.getFidelityPlan(),
$fidelityContainer = self.$el.find('.transcript-fidelity-wrapper'),
$fidelity = self.$el.find('#transcript-fidelity');
// Clear error state if present any.
this.clearPreferenceErrorState($fidelityContainer);
// Fidelity dropdown
if (fidelityPlan) {
HtmlUtils.setHtml(
$fidelity,
HtmlUtils.HTML(new Option(gettext('Select fidelity'), ''))
);
_.each(fidelityPlan, function(fidelityObject, key) {
var option = new Option(fidelityObject.display_name, key);
if (self.selectedFidelityPlan === key) {
option.selected = true;
}
HtmlUtils.append($fidelity, HtmlUtils.HTML(option));
});
$fidelityContainer.show();
} else {
$fidelityContainer.hide();
}
},
renderLanguages: function() {
var self = this,
$languagesPreferenceContainer = self.$el.find('.transcript-languages-wrapper'),
$languagesContainer = self.$el.find('.languages-container');
// Clear error state if present any.
this.clearPreferenceErrorState($languagesPreferenceContainer);
$languagesContainer.empty();
// Show language container if provider is 3PlayMedia, else if fidelity is selected.
if (self.selectedProvider === THREE_PLAY_MEDIA || self.selectedFidelityPlan) {
self.availableLanguages = self.getPlanLanguages();
_.each(self.activeLanguages, function(activeLanguage) {
// Only add if not in the list already.
if (_.indexOf(self.selectedLanguages, activeLanguage) === -1) {
self.selectedLanguages.push(activeLanguage);
self.addLanguage(activeLanguage);
}
});
$languagesPreferenceContainer.show();
self.populateLanguageMenu();
} else {
self.availableLanguages = {};
$languagesPreferenceContainer.hide();
}
},
populateLanguageMenu: function() {
var availableLanguages,
$languageMenuEl = this.$el.find('.transcript-language-menu'),
$languageMenuContainerEl = this.$el.find('.transcript-language-menu-container'),
selectOptionEl = new Option(gettext('Select language'), '');
// Omit out selected languages from selecting again.
availableLanguages = _.omit(this.availableLanguages, this.selectedLanguages);
// If no available language is left, then don't even show add language box.
if (_.keys(availableLanguages).length) {
$languageMenuContainerEl.show();
// We need to set id due to a11y aria-labelledby
selectOptionEl.id = 'transcript-language-none';
HtmlUtils.setHtml(
$languageMenuEl,
HtmlUtils.HTML(selectOptionEl)
);
_.each(availableLanguages, function(value, key) {
HtmlUtils.append(
$languageMenuEl,
HtmlUtils.HTML(new Option(value, key))
);
});
} else {
$languageMenuContainerEl.hide();
}
},
renderPreferences: function() {
this.renderProviders();
this.renderTurnaround();
this.renderFidelity();
this.renderLanguages();
},
addLanguage: function(language) {
var $languagesContainer = this.$el.find('.languages-container');
HtmlUtils.append(
$languagesContainer,
HtmlUtils.joinHtml(
HtmlUtils.HTML('<div class="transcript-language-container">'),
HtmlUtils.interpolateHtml(
HtmlUtils.HTML('<span>{languageDisplayName}</span>'),
{
languageDisplayName: this.availableLanguages[language]
}
),
HtmlUtils.interpolateHtml(
HtmlUtils.HTML('<div class="remove-language-action"><button class="button-link action-remove-language" data-language-code="{languageCode}">{text}<span class="sr">{srText}</span></button></div>'), // eslint-disable-line max-len
{
languageCode: language,
text: gettext('Remove'),
srText: gettext('Press Remove to remove language')
}
),
HtmlUtils.HTML('</div>')
)
);
},
clearResponseStatus: function() {
// Remove parent level state.
var $messageWrapperEl = this.$el.find('.course-video-settings-message-wrapper');
$messageWrapperEl.empty();
$messageWrapperEl.removeClass('error');
$messageWrapperEl.removeClass('success');
},
clearPreferenceErrorState: function($PreferenceContainer) {
$PreferenceContainer.removeClass('error');
$PreferenceContainer.find('.error-icon').empty();
$PreferenceContainer.find('.error-info').empty();
// Also clear response status if present already
this.clearResponseStatus();
},
addErrorState: function($PreferenceContainer) {
var requiredText = gettext('Required'),
infoIconHtml = HtmlUtils.HTML('<span class="icon fa fa-info-circle" aria-hidden="true"></span>');
$PreferenceContainer.addClass('error');
HtmlUtils.setHtml(
$PreferenceContainer.find('.error-icon'),
infoIconHtml
);
HtmlUtils.setHtml(
$PreferenceContainer.find('.error-info'),
requiredText
);
},
validateCourseVideoSettings: function() {
var isValid = true,
$turnaroundEl = this.$el.find('.transcript-turnaround-wrapper'),
$fidelityEl = this.$el.find('.transcript-fidelity-wrapper'),
$languagesEl = this.$el.find('.transcript-languages-wrapper');
// Explicit None selected case.
if (this.selectedProvider === '') {
return true;
}
if (!this.selectedTurnaroundPlan) {
isValid = false;
this.addErrorState($turnaroundEl);
} else {
this.clearPreferenceErrorState($turnaroundEl);
}
if (this.selectedProvider === CIELO24 && !this.selectedFidelityPlan) {
isValid = false;
this.addErrorState($fidelityEl);
} else {
this.clearPreferenceErrorState($fidelityEl);
}
if (this.selectedLanguages.length === 0) {
isValid = false;
this.addErrorState($languagesEl);
} else {
this.clearPreferenceErrorState($languagesEl);
}
return isValid;
},
saveTranscriptPreferences: function() {
var self = this,
$messageWrapperEl = self.$el.find('.course-video-settings-message-wrapper');
// First clear response status if present already
this.clearResponseStatus();
$.postJSON(this.transcriptHandlerUrl, {
provider: self.selectedProvider,
cielo24_fidelity: self.selectedFidelityPlan,
cielo24_turnaround: self.selectedProvider === CIELO24 ? self.selectedTurnaroundPlan : '',
three_play_turnaround: self.selectedProvider === THREE_PLAY_MEDIA ? self.selectedTurnaroundPlan : '',
preferred_languages: self.selectedLanguages,
global: false // Do not trigger global AJAX error handler
}, function(data) {
if (data.transcript_preferences) {
$messageWrapperEl.removeClass('error');
$messageWrapperEl.addClass('success');
HtmlUtils.setHtml(
$messageWrapperEl,
HtmlUtils.interpolateHtml(
HtmlUtils.HTML('<div class="course-video-settings-message"><span class="icon fa fa-check-circle" aria-hidden="true"></span><span>{text}</span></div>'), // eslint-disable-line max-len
{
text: gettext('Settings updated')
}
)
);
self.activeTranscriptionPlan = data.transcript_preferences;
// Sync ActiveUploadListView with latest active plan.
Backbone.trigger(
'coursevideosettings:syncActiveTranscriptPreferences',
self.activeTranscriptionPlan
);
}
}).fail(function(jqXHR) {
var errorMessage;
if (jqXHR.responseText) {
// Enclose inside try-catch so that if we get erroneous data, we could still show some error to user
try {
errorMessage = $.parseJSON(jqXHR.responseText).error;
} catch (e) {} // eslint-disable-line no-empty
$messageWrapperEl.removeClass('success');
$messageWrapperEl.addClass('error');
HtmlUtils.setHtml(
$messageWrapperEl,
HtmlUtils.interpolateHtml(
HtmlUtils.HTML('<div class="course-video-settings-message"><span class="icon fa fa-info-circle" aria-hidden="true"></span><span>{text}</span></div>'), // eslint-disable-line max-len
{
text: errorMessage || gettext('Error saving data')
}
)
);
}
});
},
updateCourseVideoSettings: function() {
var $messageWrapperEl = this.$el.find('.course-video-settings-message-wrapper');
if (this.validateCourseVideoSettings()) {
this.saveTranscriptPreferences();
} else {
$messageWrapperEl.empty();
}
},
render: function() {
var dateModified = this.activeTranscriptionPlan ?
moment.utc(this.activeTranscriptionPlan.modified).format('ll') : '';
HtmlUtils.setHtml(
this.$el,
this.template({
dateModified: dateModified
})
);
this.renderPreferences();
this.registerCloseClickHandler();
this.setFixedCourseVideoSettingsPane();
return this;
},
setFixedCourseVideoSettingsPane: function() {
var windowWidth = $(window).width(),
windowHeight = $(window).height(),
$courseVideoSettingsButton = $('.course-video-settings-button'),
$courseVideoSettingsContainer = this.$el.find('.course-video-settings-container'),
initialPositionTop = $courseVideoSettingsContainer.offset().top,
courseVideoSettingsButtonLeft = $courseVideoSettingsButton.offset().left,
fixedOffsetRight = windowWidth -
courseVideoSettingsButtonLeft - $courseVideoSettingsButton.width() - 25;
// set windows total height
$courseVideoSettingsContainer.css('height', windowHeight);
$courseVideoSettingsContainer.css('right', 20);
// Make sticky when scroll reaches top.
$(window).scroll(function() {
// Remove transition when we start scrolling.
// Why we do this? The settings pane come back and forth when it is switched between
// position:fixed and position:absolute, it's right and top position are then being changed wrt to their
// position layout.
$courseVideoSettingsContainer.css('transition', 'none');
if ($(window).scrollTop() >= initialPositionTop) {
$courseVideoSettingsContainer.addClass('fixed-container');
// TODO: Removes these js calculations and try to do through CSS way.
$courseVideoSettingsContainer.css('right', fixedOffsetRight);
} else {
$courseVideoSettingsContainer.removeClass('fixed-container');
$courseVideoSettingsContainer.css('right', 20);
}
});
},
closeCourseVideoSettings: function() {
// Trigger destroy transcript event.
Backbone.trigger('coursevideosettings:destroyCourseVideoSettingsView');
// Unbind any events associated
this.undelegateEvents();
this.stopListening();
// Empty this.$el content from DOM
this.$el.empty();
// Reset everything.
this.resetPlanData();
}
});
return CourseVideoSettingsView;
});
...@@ -83,6 +83,8 @@ $gray-d1: shade($gray, 20%) !default; ...@@ -83,6 +83,8 @@ $gray-d1: shade($gray, 20%) !default;
$gray-d2: shade($gray, 40%) !default; $gray-d2: shade($gray, 40%) !default;
$gray-d3: shade($gray, 60%) !default; $gray-d3: shade($gray, 60%) !default;
$gray-d4: shade($gray, 80%) !default; $gray-d4: shade($gray, 80%) !default;
$gray-u1: #ECF0F1;
// These define button styles similar to LMS // These define button styles similar to LMS
// The goal here is consistency (until we can overhaul all of this...) // The goal here is consistency (until we can overhaul all of this...)
...@@ -302,3 +304,5 @@ $state-warning-border: darken($state-warning-bg, 5%) !default; ...@@ -302,3 +304,5 @@ $state-warning-border: darken($state-warning-bg, 5%) !default;
$state-danger-text: $black !default; $state-danger-text: $black !default;
$state-danger-bg: #f2dede !default; $state-danger-bg: #f2dede !default;
$state-danger-border: darken($state-danger-bg, 5%) !default; $state-danger-border: darken($state-danger-bg, 5%) !default;
$text-dark-black-blue: #2C3E50;
...@@ -12,6 +12,172 @@ ...@@ -12,6 +12,172 @@
} }
} }
.fixed-container {
position: fixed !important;
top: 0 !important;
}
.course-video-settings-container {
position: absolute;
overflow: scroll;
top: 0;
right: -100%;
z-index: 1000;
width: 352px;
transition: all 0.3s ease;
background-color: $white;
-webkit-box-shadow: -3px 0px 3px 0px rgba(153,153,153,0.3);
-moz-box-shadow: -3px 0px 3px 0px rgba(153,153,153,0.3);
box-shadow: -3px 0px 3px 0px rgba(153,153,153,0.3);
.button-link {
background:none;
border:none;
padding:0;
color: $ui-link-color;
cursor:pointer
}
.action-close-wrapper {
.action-close-course-video-settings {
width: 100%;
padding: ($baseline/2) ($baseline*0.8);
background-color: $gray-u1;
border: transparent;
height: ($baseline*2.4);
color: $text-dark-black-blue;
@include font-size(16);
@include text-align(left);
}
}
.course-video-settings-wrapper {
margin-top: ($baseline*1.60);
padding: ($baseline) ($baseline*0.8);
.course-video-settings-title {
color: $black-t4;
margin: ($baseline*1.6) 0 ($baseline*0.8) 0;
font-weight: font-weight(semi-bold);
@include font-size(24);
}
.course-video-settings-message {
padding: ($baseline/2);
margin-bottom: ($baseline*0.8);
max-height: ($baseline*2.4);
color: $black;
@include font-size(16);
.icon {
@include margin-right($baseline/4);
}
}
.course-video-settings-message-wrapper.success .course-video-settings-message {
background-color: $state-success-bg;
border: solid 1px $state-success-border;
}
.course-video-settings-message-wrapper.error .course-video-settings-message {
background-color: $state-danger-bg;
border: solid 1px $state-danger-border;
}
.transcript-preferance-wrapper {
margin-top: ($baseline*1.6);
.icon.fa-info-circle {
@include margin-left($baseline*0.75);
}
}
.transcript-preferance-wrapper.error .transcript-preferance-label {
color: $color-error;
}
.error-info, .error-icon .fa-info-circle {
color: $color-error;
}
.error-info {
@include font-size(16);
}
.transcript-preferance-label {
color: $black-t4;
@include font-size(15);
font-weight: font-weight(semi-bold);
}
.transcript-provider-group, .transcript-turnaround, .transcript-fidelity {
margin-top: ($baseline*0.8);
}
.transcript-provider-group {
input[type=radio] {
margin: 0 ($baseline/2);
}
label {
font-weight: normal;
color: $black-t4;
@include font-size(15);
}
}
.transcript-turnaround-wrapper, .transcript-fidelity-wrapper, .transcript-languages-wrapper {
display: none;
}
.transcript-languages-container .languages-container {
margin-top: ($baseline*0.8);
.transcript-language-container {
padding: ($baseline/4);
background-color: $gray-l6;
border-top: solid 1px $gray-l4;
border-bottom: solid 1px $gray-l4;
.remove-language-action {
display: inline-block;
@include float(right);
}
}
}
.transcript-language-menu-container {
margin-top: ($baseline*0.8);
.transcript-language-menu {
width: 65%;
}
.add-language-action {
display: inline-block;
.action-add-language {
@include margin-left($baseline/4);
}
}
}
}
.course-video-settings-footer {
margin-top: ($baseline*1.6);
.last-updated-text {
@include font-size(12);
@include margin-left($baseline/4);
}
}
.button {
@extend %btn-primary-blue;
@extend %sizing;
.action-button-text {
display: inline-block;
vertical-align: baseline;
}
.icon {
display: inline-block;
vertical-align: baseline;
}
}
}
.file-upload-form { .file-upload-form {
@include clearfix(); @include clearfix();
......
<div class='course-video-settings-container'>
<div class="course-video-settings-header">
<div class="action-close-wrapper">
<button class="action-close-course-video-settings">
<span class="icon fa fa-times" aria-hidden="true"></span>
<%-gettext('Close') %>
<span class='sr'><%-gettext('Press close to hide course video settings') %></span>
</button>
</div>
</div>
<div class='course-video-settings-wrapper'>
<div class='course-video-settings-message-wrapper'></div>
<span class="course-video-settings-title"><%- gettext('Transcript Settings') %></span>
<div class='transcript-preferance-wrapper transcript-provider-wrapper'>
<label class='transcript-preferance-label' for='transcript-provider'><%- gettext('Transcript Provider') %><span class='error-icon' aria-hidden="true"></span></label>
<div class='transcript-provider-group' id='transcript-provider'></div>
<span class='error-info' aria-hidden="true"></span>
</div>
<div class='transcript-preferance-wrapper transcript-turnaround-wrapper'>
<label class='transcript-preferance-label' for='transcript-turnaround'><%- gettext('Transcript Turnaround') %><span class='error-icon' aria-hidden="true"></span></label>
<select id='transcript-turnaround' class='transcript-turnaround'></select>
<span class='error-info' aria-hidden="true"></span>
</div>
<div class='transcript-preferance-wrapper transcript-fidelity-wrapper'>
<label class='transcript-preferance-label' for='transcript-fidelity'><%- gettext('Transcript Fidelity') %><span class='error-icon' aria-hidden="true"></span></label>
<select id='transcript-fidelity' class='transcript-fidelity'></select>
<span class='error-info' aria-hidden="true"></span>
</div>
<div class='transcript-preferance-wrapper transcript-languages-wrapper'>
<span class='transcript-preferance-label'><%- gettext('Transcript Languages') %></span>
<span class='error-icon' aria-hidden="true"></span>
<div class='transcript-languages-container'>
<div class='languages-container'></div>
<div class="transcript-language-menu-container">
<select class="transcript-language-menu" id="transcript-language" aria-labelledby="transcript-language-none"></select>
<div class="add-language-action">
<button class="button-link action-add-language"><%- gettext('Add') %><span class="sr"><%- gettext('Press Add to language') %></span></button>
<span class="error-info" aria-hidden="true"></span>
</div>
</div>
</div>
</div>
<div class='course-video-settings-footer'>
<button class="button button action-update-course-video-settings" aria-describedby='update-button-text'>
<%- gettext('Update Settings') %>
<span id='update-button-text' class='sr'><%-gettext('Press update settings to update course video settings') %></span>
</button>
<%if (dateModified) { %>
<span class='last-updated-text'><%- gettext('Last updated')%> <%- dateModified %></span>
<% } %>
</div>
</div>
</div>
...@@ -34,10 +34,12 @@ ...@@ -34,10 +34,12 @@
"${encodings_download_url | n, js_escaped_string}", "${encodings_download_url | n, js_escaped_string}",
"${default_video_image_url | n, js_escaped_string}", "${default_video_image_url | n, js_escaped_string}",
${concurrent_upload_limit | n, dump_js_escaped_json}, ${concurrent_upload_limit | n, dump_js_escaped_json},
$(".nav-actions .upload-button"), $(".nav-actions .course-video-settings-button"),
$contentWrapper.data("previous-uploads"), $contentWrapper.data("previous-uploads"),
${video_supported_file_formats | n, dump_js_escaped_json}, ${video_supported_file_formats | n, dump_js_escaped_json},
${video_upload_max_file_size | n, dump_js_escaped_json}, ${video_upload_max_file_size | n, dump_js_escaped_json},
${active_transcript_preferences | n, dump_js_escaped_json},
${video_transcript_settings | n, dump_js_escaped_json},
${video_image_settings | n, dump_js_escaped_json} ${video_image_settings | n, dump_js_escaped_json}
); );
}); });
...@@ -46,6 +48,7 @@ ...@@ -46,6 +48,7 @@
<%block name="content"> <%block name="content">
<div class="wrapper-mast wrapper"> <div class="wrapper-mast wrapper">
<div class="video-transcript-settings-wrapper"></div>
<header class="mast has-actions has-subtitle"> <header class="mast has-actions has-subtitle">
<h1 class="page-header"> <h1 class="page-header">
<small class="subtitle">${_("Content")}</small> <small class="subtitle">${_("Content")}</small>
...@@ -54,11 +57,9 @@ ...@@ -54,11 +57,9 @@
<nav class="nav-actions" aria-label="${_('Page Actions')}"> <nav class="nav-actions" aria-label="${_('Page Actions')}">
<h3 class="sr">${_("Page Actions")}</h3> <h3 class="sr">${_("Page Actions")}</h3>
<ul> <div class="nav-item">
<li class="nav-item"> <button class="button course-video-settings-button"><span class="icon fa fa-cog" aria-hidden="true"></span> ${_("Course Video Settings")}</button>
<a href="#" class="button upload-button new-button"><span class="icon fa fa-plus" aria-hidden="true"></span> ${_("Upload New File")}</a> </div>
</li>
</ul>
</nav> </nav>
</header> </header>
</div> </div>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment